Compare commits

...
Author SHA1 Message Date
Florian Weimer e51a6bf18e malloc: Increase tcache fill count from 16 to 32
This avoids a regression in the omnetpp and xalancbmk benchmarks
of SPEC.  Apparently, these benchmarks are very sensitive to the
fill rate in malloc.

Suggested-by: Wilco Dijkstra <wilco.dijkstra@arm.com>
Tested-by: Wilco Dijkstra <wilco.dijkstra@arm.com>
2026-06-30 09:07:19 +02:00
Florian Weimer b50e24aea3 malloc: Only fill half of the tcache during batch allocation
This leaves room for subsequent frees.
2026-06-30 09:07:19 +02:00
Florian Weimer c0a1c6e06d malloc: Perform batched frees if tcache is full
Batched frees amortize the locking overhead once the tcache is full.
Previously, once the tcache slot was full, each free acquired the
arena lock.  With this change, neighboring tcache allocations in the
same arena re-use an arena lock that already exists.

Pass the tcache pointer to __libc_free_batch so that it is not
necessary to reload the tcache pointer after the _int_free_chunk
calls.
2026-06-30 09:07:19 +02:00
H.J. Lu ccd4cd5324 malloc: Don't call __get_thp_mode/__get_thp_size twice
Both ld.so and malloc track kernel THP mode and THP page size when THP
in ld.so is enabled by

GLIBC_TUNABLES=glibc.elf.thp=1

and THP in malloc is enabled by

GLIBC_TUNABLES=glibc.malloc.hugetlb=1

But DL_MAP_DEFAULT_THP_PAGESIZE and MALLOC_DEFAULT_THP_PAGESIZE may be
different when they are defined in <hugepages.h>.

If THP in ld.so is enabled, change malloc to use kernel THP mode from
ld.so, instead of calling __get_thp_mode, and use THP page size from
ld.so if it came from __get_thp_size.  This avoids calling __get_thp_mode
and __get_thp_size again, which are quite expensive:

1. Initialize mp_.thp_mode and GL(dl_thp_mode) to thp_mode_unknown.
2. Set mp_.thp_mode to GL(dl_thp_mode) if GL(dl_thp_mode) isn't
thp_mode_unknown.  Otherwise call __get_thp_mode to set mp_.thp_mode.
3. GL(dl_elf_thp_pagesize) is set to DL_MAP_DEFAULT_THP_PAGESIZE without
calling __get_thp_size and THP page size for malloc may be different from
THP page size for ld.so.  Set mp_.thp_pagesize to GL(dl_elf_thp_pagesize)
if DL_MAP_DEFAULT_THP_PAGESIZE is defined.  Otherwise call __get_thp_size
to set mp_.thp_pagesize.
4. THP page size in malloc is capped to MAX_THP_PAGESIZE.  If THP page
size is above MAX_THP_PAGESIZE, THP in malloc is disabled.

These result in when glibc.elf.thp is set to 1, malloc uses the actual
kernel THP mode instead of defaulting to madvise mode and madvise_thp
will stop issuing MADV_HUGEPAGE if kernel THP mode is always.

This fixes BZ #34083.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
2026-06-26 10:18:36 +08:00
Jiri Stransky 96773cc77b nis: Fix a memory leak in nis_creategroup when malloc fails
The zo_domain field was missing from the list of fields to be freed.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-06-25 11:21:14 -03:00
Xiang Gao 21103aef12 stdio-common: Fix scanf nan/inf pushback [BZ #34208]
When scanf parses special floating-point inputs like "nan", "inf", it
may read one extra character to check whether the input still matches
the expected word.

If that extra character does not match, the conversion should fail, but
the character has already been read from the stream.  Some error paths
did not push it back before reporting the conversion error, so later input
started at the wrong position.

Fix this by keeping EOF and width errors separate from character mismatch
errors.  Push the character back before calling conv_error for mismatch
errors.

Add narrow and wide scanf tests using the same test body.  The test cases
focus on the paths changed by this patch: mismatches while matching "nan",
"nan(...)", "inf", and "infinity".

Tested on x86_64 Fedora 42.

The following tests passed without regressions:
  make test t=stdio-common/tst-scanf-bz34208
  make test t=stdio-common/tst-wscanf-bz34208
  make subdirs="stdio-common libio" check

Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-25 10:20:24 -03:00
Michael Ford ead80441e6 nss: Remove --enable-static-nss configure option
Since the NSS reorganization in glibc 2.33 the --enable-static-nss
option no longer changes the build: the files and dns services are built
into libc unconditionally, and there is no longer any mechanism to bake
the remaining services into libc.a.  After removing the dead
-DSTATIC_NSS define and the <gnu/lib-names.h> include (commits
82b710c190 and
2169401438), the option's only remaining
effect was to define DO_STATIC_NSS, which suppresses the static link
warnings on the NSS interface functions in libc.a.

With DO_STATIC_NSS gone, nss_interface_function emits the static link
warning unconditionally; static_nss and build-static-nss are removed
along with the configure option.

tst-nss-static, previously built only when build-static-nss was set, is
now added to tests-static unconditionally so the static NSS path retains
test coverage.

Signed-off-by: Michael Ford <fanquake@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-25 10:20:24 -03:00
Adhemerval Zanella 657fe73fb1 manual: Document indirect functions (IFUNC)
Add a new section to the Dynamic Linker chapter describing the
STT_GNU_IFUNC mechanism: the resolver calling convention for each
supported architecture, when resolvers run, and the functionality
supported in resolvers (TLS access, stack protector, cross-object
references) along with the remaining restrictions, reflecting the
recent ordering fixes (BZ 20680, BZ 23240, BZ 28817, BZ 34164,
BZ 34170).  Also cross-reference it from the hardening section.

Reviewed-by: Yury Khrustalev <yury.khrustalev@arm.com>
2026-06-25 09:50:38 -03:00
Sachin Monga c9b5a4a593 ppc64le: Restore optimized memchr for power10 [BZ #34300]
Restore the POWER10 memchr implementation reverted in commit
a7877bb668 [BZ #33059], addressing the
actual cause:
The non-volatile vector register v20 used as the
zero vector (M_VREG_ZERO) is replaced with the volatile register v17.

Additionally, the assembler workaround used earlier for the
instructions lxvp and vextractbm is replaced with actual
instructions.
2026-06-25 00:21:08 -05:00
Shengwen Cheng a014af39fd riscv: Fix RV32 libthread_db THREAD_SELF register layout
The RISC-V libthread_db THREAD_SELF descriptor currently assumes 64-bit
general-register slots:

  REGISTER (64, 64, 4 * 8, ...)

That is correct for RV64, where tp/x4 is at byte offset 4 * 8 in the
general register set.  On RV32, general-register slots are 32-bit wide,
so tp/x4 is at byte offset 4 * 4.

Using the RV64 layout on RV32 makes libthread_db read the wrong thread
pointer value and prevents GDB from enabling pthread-aware thread
debugging.

Use a helper macro to pass the RISC-V register size into the
DB_THREAD_SELF definition.

Signed-off-by: Shengwen Cheng <shengwen1997.tw@gmail.com>
Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
2026-06-24 17:54:00 +00:00
Adhemerval Zanella 16c74b94b9 elf: Fix clang-18 build of ifunc resolver tests
clang-18 errors on the static ifunc resolvers seen as unused.
2026-06-24 13:41:26 -03:00
H.J. Lu 8e5c299980 arm: Xfail 2 static THP tests
Since 2 static THP tests fail on arm:

FAIL: elf/tst-thp-1-no-s-code-static
FAIL: elf/tst-thp-1-static

due to limitations of arm32 kABI:

https://sourceware.org/bugzilla/show_bug.cgi?id=34096

xfail them.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-24 06:18:21 +08:00
H.J. Lu d00085d6d0 thp: Limit THP code padding to 2 * THP page size
Since R_ARC_S25W_PCREL range is limited, 32MB THP code padding leads to
relocation overflow:

.../support/libsupport_nonshared.a(support_test_main.oS):
in function `support_test_main':
.../support/support_test_main.c:396:(.text+0x650): relocation truncated to fit: R_ARC_S25W_PCREL_PLT against symbol `atexit' defined in .text section in
../glibc/libc_nonshared.a(atexit.oS)
.../install/compilers/arc-linux-gnu/arc-glibc-linux-gnu/bin/ld: final link failed
collect2: error: ld returned 1 exit status

Limit THP code padding to 2 * THP page size to avoid R_ARC_S25W_PCREL
relocation overflow.

Tested natively for Linux/x86-64 and Linux/i686 as well as with
build-many-glibcs.py for arc-linux-gnu.

This fixes BZ #34311

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-24 06:17:20 +08:00
Mike FABIAN 98e1e467a8 Update documentation of iconv [BZ #30136]
Reviewed-by: Florian Weimer <fweimer@redhat.com>
2026-06-23 14:50:30 -03:00
Pierre Blanchard 2cce7ec3c8 AArch64: additional mathvec build system cleanups.
Rely on a single list of ISAs and precisions for
source, tests and benchmarks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-23 14:50:30 -03:00
Pierre Blanchard c5a4d7ccb2 AArch64: Update SHARED-FILES with files from Arm Optimized-Routines
Adds lists for math.h and string.h and track file revisions.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-23 14:50:30 -03:00
Pierre Blanchard 5a60281b88 AArch64: Update mathvec directory structure.
The new structure matches that of Arm Optimized-routines,
to facilitate files traceability.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-23 14:50:30 -03:00
John David Anglin f9deb2f50b hppa: Fix missing call to __feraiseexcept (BZ 34306)
The feupdateenv function is supposed to raise exceptions after
installing the environment represented by its envp argument.
This was accidentally missed on hppa.

The failure to raise exceptions was noticed by the failure of
the math/test-narrowing-trap test.

Signed-off-by: John David Anglin <dave.anglin@bell.net>
2026-06-23 13:41:10 -04:00
Sergey Kolosov b61b98e3d0 resolv: Add test for gethostbyname_r unaligned buffer [BZ #18287]
Add a test for the buffer overflow in gethostbyname_r (Bug 18287), which
occurs when alignment padding is not subtracted from the remaining buffer
length.

The test uses binary search to find the exact required buffer size
for a mocked DNS response. It then tests all pointer misalignments
(1-7 bytes) using a custom 0xAA guard region. This guarantees
deterministic detection of out-of-bounds writes, avoiding false
negatives caused by malloc chunk rounding.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
2026-06-23 08:40:41 +02:00
Justus Winter 91eb5057b9 Define _PATH_MOUNTED as "/etc/mtab"
Change the definition of _PATH_MOUNTED to "/etc/mtab". This is the
value used on Linux, and BSD also uses /etc/mtab.

The change is motivated by the fact that on Debian /etc/mtab is a
symbolic link to /proc/mounts. This patch adjusts the macro for
non-linux systems such as Hurd. Changing this using
sysdeps/mach/hurd/paths.h causes build problems because
/usr/include/hurd/paths.h is shadowed by this file. This change is
proposed in the hope that aligning the non-linux targets with the
glibc for Linux is perceived as a good thing while fixing this problem
on Debian/Hurd along the way.
2026-06-22 22:25:38 +02:00
Adhemerval Zanella bc76aa1658 Add advisory text for CVE-2026-6791
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
2026-06-22 14:16:35 -03:00
Adhemerval Zanella 07c24f3539 posix: Fix stack overflow in wordexp tilde expansion (BZ 34091, CVE-2026-6791)
The parse_tilde function previously used strndupa to allocate memory
for the parsed username on the stack, and since the input is
user-defined, this can lead to a stack overflow.

This patch fixes the issue by replacing strndupa with scratch_buffer,
by reusing the buffer used in the __getpwnam_r call.

The new “tst-wordexp-tilde.c” test is a test-container to avoid using
system-defined NSS modules.

Checked on x86_64-linux-gnu and i686-linux-gnu.
2026-06-22 11:11:24 -03:00
60f7247847 riscv: Add RVV memmove for both multiarch and non-multiarch builds
This patch adds an RVV-optimized implementation of memmove for RISC-V and
enables it for both multiarch (IFUNC) and non-multiarch builds.

The implementation integrates Hau Hsu's 2023 RVV work under a unified
ifunc-based framework. A vectorized version (__memmove_vector) is added
alongside the generic fallback (__memmove_generic). The runtime resolver
selects the RVV variant when RISCV_HWPROBE_KEY_IMA_EXT_0 reports vector
support (RVV).

Currently, the resolver still selects the RVV variant even when the RVV
extension is disabled via prctl(). As a consequence, any process that
has RVV disabled via prctl() will receive SIGILL when calling memmove().

Co-authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-authored-by: Jerry Shih <jerry.shih@sifive.com>
Signed-off-by: Yao Zihong <zihong.plct@isrc.iscas.ac.cn>
Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
2026-06-20 22:56:09 -05:00
H.J. Lu 53d909a283 elf: Support THP segment load with madvise enabled THP
The current THP segment load approach works only when THP is enabled
with always in the kernel.  If THP is enabled with madvise in the
kernel, to enable THP segment load in an application, madvise should
be called with MADV_HUGEPAGE on all THP eligible PT_LOAD segments:

1. Define DL_MAP_DEFAULT_THP_PAGESIZE in hugepages.h and default it to 0.
If DL_MAP_DEFAULT_THP_PAGESIZE is defined, assume kernel THP madvise mode.
If kernel THP mode is always or never, there is an extra madvise call
which has no impact.  DL_MAP_DEFAULT_THP_PAGESIZE is defined for x86-64
and 64-bit loongarch.
2. Update _dl_map_segment_align to support madvise THP mode.  This fixes
BZ #34079.
3. Call _dl_executable_postprocess in rtld_setup_main_map for dynamic
executables and in LIBC_START_MAIN for static executables, which calls
madvise with MADV_HUGEPAGE on all THP eligible PT_LOAD segments in
executable.  This fixes BZ #34080 for both dynamic and static executables.
4. Call _dl_postprocess_loadcmd_extra in _dl_postprocess_loadcmd, which
calls madvise with MADV_HUGEPAGE on all THP eligible PT_LOAD segments
when loading an object after they have been mapped in.  This fixes
BZ #34080 for shared objects.
5. Set the maximum page alignment on THP tests to THP page size as the
default maximum page alignment may be smaller than THP page size.
6. Add tests to verify that large executable PT_LOAD segments in
executables are mapped at addresses aligned to THP page size when the
kernel is configured to use THP in "always" mode or "madvise" mode by
inspecting /proc/self/maps to check that the mapping address is aligned
to THP page size reported by the kernel.  Also verify that madvise is
called with MADV_HUGEPAGE when the glibc tunable glibc.elf.thp=1 is used
and madvise isn't called with MADV_HUGEPAGE when the glibc tunable
glibc.elf.thp=0 is used.

Skip these tests if THP page size cannot be determined or if THP is not
enabled in "always" mode nor "madvise" mode.

Quote WANG Rui <wangrui@loongson.cn>:

From benchmarking a clang build of the Linux kernel on x86_64 with
your patch in THP madvise mode, I observed that iTLB misses were
reduced, similar to what we see in THP always mode.

NB: Some THP tests fail on arm due to limitations of arm32 kABI:

https://sourceware.org/bugzilla/show_bug.cgi?id=34096

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-20 05:35:24 +08:00
Carlos O'Donell 9310db5a08 advisories: Update GLIBC-SA-2026-0012 metadata
Update vulnerable versions in descriptive text.
2026-06-19 16:30:41 -04:00
Adhemerval Zanella da11a7d92c Remove unused THREAD_SET_POINTER_GUARD and THREAD_COPY_POINTER_GUARD
Commit a5ec880f80 ("Consolidate pointer
guard to use a relro variable instead of the TCB") moved the pointer
guard to the module-local relro variable __pointer_chk_guard_local and
dropped the THREAD_{SET,COPY}_POINTER_GUARD macros, which are no longer
defined by any target.

Checked on a build for all afftected targets.
2026-06-19 14:23:05 -03:00
Florian Weimer 4ba0b79b95 resolv: Add test case tst-ns_sprintrr (bug 34033, bug 34069)
This test case covers both input buffer overreads and output buffer
overflows.  It should systematically cover these issues.

I used code auto-generation for updating the test expectations for
truncated RDATA in TXT, ISDN records, after writing the rest
of the test by hand.

Assisted-by: LLM
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-06-19 18:22:20 +02:00
Florian Weimer a7b60d23bb resolv: Fix buffer overreads in ns_sprintrrf (CVE-2026-6238)
Check that the RDATA payload does not require more than RDATALEN
bytes while processing it.  The fixes cover A6, LOC records.
(CERT, TKEY, TSIG were fixed before, by switching to the generic
formatter.)

The vulnerable LOC record handling was first introduced before
glibc 2.0, in commit ee188d555b.

CERT, TSIG, TKEY handling came with commit
b43b13ac25, released with glibc 2.2.

A6 record handling was introduced in commit
9163381643 ("* resolv/ns_print.c
(ns_sprintrrf): Handle ns_t_a6 and ns_t_opt."), which went into glibc
2.7.

This fixes bug 34069.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-06-19 18:22:20 +02:00
Florian Weimer ca44a6609c resolv: More types as unknown in ns_sprintrrf (CVE-2026-5435)
Specifically, CERT, TKEY, TSIG, OPT.  This removes the buggy
implementations of TSIG, fixing bug 34033, and partially
fixing bug 34069.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-06-19 18:22:20 +02:00
Florian Weimer cd0db208d5 resolv: Check for inet_ntop failure in ns_sprintrrf
This makes the output more consistent (either failure or complete
output) and helps with systematic testing with varying buffer
sizes.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-06-19 18:22:20 +02:00
Florian Weimer d58415eb17 resolv: Improve formatting of unknown records in ns_sprintrrf
Do not add the "unknown RR type" comment.  After adding the TYPE
prefix, the number is largely redundant.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-06-19 18:22:20 +02:00
Florian Weimer f69b7f95e3 resolv: Fix ns_sprintrrf formatting of class, type values (bug 34289)
The p_class and p_type results could overwrite each other if both
were unknown.  Format unknown values with CLASS and TYPE prefixes,
as in RFC 3597.  Handle A6 separately because it cannot be added
to __p_type_syms for ABI reasons.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-06-19 18:22:20 +02:00
Florian Weimer 360f352c9a resolv: Declare __p_class_syms, __p_type_syms for internal use
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-06-19 18:22:20 +02:00
Florian Weimer 1607beb5f4 Update GLIBC-SA-2026-0012 to mention A6 records
It turns out there is a missing inner length check in it, too.

Also fix the vulnerable commit.  It predates the glibc 2.0 release
because the old stream-based formatting code in resolv/res_debug.c had
the same bug in its LOC handling.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-06-19 18:22:20 +02:00
Wilco Dijkstra cdbefe2f94 string: Improve memchr random test
Add matches before the start to ensure the memchr test checks they do not
result in false matches or missed matches.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-18 19:44:31 +00:00
Dylan Fleming 4e105d9855 AArch64: Vectorise SVE fp64 sin/cos special case
Implemented double precision vector fallback for SVE sin/cos.

Includes rewrite of sin to use trig instructions.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
2026-06-18 14:56:22 +00:00
Dylan Fleming 708185c829 AArch64: Vectorise AdvSIMD fp64 sin/cos special case
Implemented double precision vector fallback for AdvSIMD sin/cos.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
2026-06-18 14:56:22 +00:00
Dylan Fleming e585d88982 AArch64: Vectorise SVE fp32 sin/cos special case
Implemented single precision vector fallback for SVE sinf/cosf.

Includes a rewrite of SVE sinf to use the trig instructions.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
2026-06-18 14:56:22 +00:00
Dylan Fleming faa9ea921a AArch64: Vectorise AdvSIMD fp32 sin/cos special case
Implemented single precision vector fallback for AdvSIMD sinf/cosf.

Includes cleanup of the fast pass to remove old styling and conventions.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
2026-06-18 14:56:22 +00:00
Yury Khrustalev a3b1faddaf news: Describe AArch64-specific changes for 2.44
Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
2026-06-18 14:46:42 +01:00
Adhemerval Zanella 242a1f4b0f elf: Do not scrub AT_RANDOM to a constant when reseeding fails (BZ 34197)
_dl_reseed_random zeroed the AT_RANDOM bytes before refilling them with
getrandom (GRND_NONBLOCK).  That call is best-effort and can write nothing
(e.g. before the kernel entropy pool is initialized early at boot), leaving
the 16 bytes as a constant zero.

Since the value is exposed through getauxval (AT_RANDOM), this traded a
potential guard leak for a guaranteed predictable value.

Drop the memset and overwrite the bytes in place instead.  The generic
version now just drops the pointer (it is not used anywhere).

Checked on x86_64-linux-gnu and i686-linux-gnu.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
2026-06-17 09:52:32 -03:00
Wilco Dijkstra 368438d8b6 manual: Update memchr description [BZ #19406]
Update memchr description to indicate it cannot overread and the size
may be larger than the object passed.  This is required since C11.

Suggested-by: Paul Eggert <eggert@cs.ucla.edu>
Reviewed-by: Paul Eggert <eggert@cs.ucla.edu>
2026-06-17 11:48:50 +00:00
Samuel Thibault 89b4605be8 hurd: Add ETH_TLEN and ETH_FCS_LEN 2026-06-17 01:26:54 +02:00
Samuel Thibault 145b53b22a hurd: Fix ETH_P_PUP
According to IANA, PUP is 0200
2026-06-17 01:26:45 +02:00
Samuel Thibault 8d184c42cc Factorize if_arp.h
They were essentially identical between linux and hurd, and are
OS-independent.
2026-06-17 01:17:21 +02:00
Adhemerval Zanella 337e18d661 elf: Scrub and reseed the AT_RANDOM bytes after deriving the guards (BZ 34197)
Once the pointer and stack guards have been derived from AT_RANDOM, scrub
the bytes and refill them with new random data unrelated to the guards.
On Linux, it uses getrandom syscall (as for tcache_key_initialize), and
fallback to zero the memory if the syscall is not avaiable.

This keeps AT_RANDOM useful to applications while ensuring those bytes no
longer reveal the guards.

The work is done by _dl_reseed_random, called once the guards are in place
and before any ELF constructor can observe AT_RANDOM: in security_init for
the dynamic loader and in __libc_start_main for statically linked programs.

Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-06-16 14:53:48 -03:00
Adhemerval Zanella 604fe0b87b Consolidate dl-osinfo.h into the generic implementation
The generic and the Linux _dl_setup_stack_chk_guard and
_dl_setup_pointer_guard are identical when the kernel provides the
AT_RANDOM auxiliary vector, which is always the case on the supported
Linux kernels; the generic version only adds a fallback for a missing
AT_RANDOM.  Remove the Linux-specific dl-osinfo.h and use the generic
one for all targets.

The removed header pulled in <dl-sysdep.h>, which is what transitively
declared __libc_enable_secure for dl-parse_auxv.h; include <unistd.h>
there directly so it no longer relies on that.  Include <string.h> in
the generic header so it is self-contained.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-06-16 14:53:48 -03:00
Adhemerval Zanella 78f1f0e39c Consolidate the C pointer guard and align the assembly implementations
The per-architecture pointer_guard.h files all implemented some variant
of C PTR_MANGLE/PTR_DEMANGLE: the i386 and x86_64 versions did an
exclusive-or by the guard followed by a rotate left by
2 * sizeof (uintptr_t) + 1 bits (9 on 32-bit, 17 on 64-bit), while the
other targets did the exclusive-or only.  The assembly macros likewise
rotated on i386 and x86_64 but not on the other targets.

Consolidate everything to use XOR + rotate uniformly: collapse the
per-architecture C headers into a single generic implementation, and
add the matching rotate to the assembly implementations that lacked it.

Targets with an assembly rotate instruction (aarch64, arm, loongarch,
powerpc, s390, csky) just add it.  Those without one (alpha, sparc, sh)
synthesize the rotation: alpha and sparc with a shift/shift/or sequence,
which needs an extra scratch register added to the PTR_MANGLE/PTR_DEMANGLE
macros, sh with single-bit rotates, which needs no scratch.  The s390
__longjmp and ____longjmp_chk demangle the return address and stack
pointer in C, so update those too.

Checked with setjmp, ptrguard and longjmp_chk tests for all supported
ABIs that can run under qemu (all but csky), and with builds for all
supported ABIs.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-06-16 14:53:47 -03:00
Adhemerval Zanella c626d764d7 Remove the jmp_buf stack pointer demangle on ABIs that do not mangle it
arc, m68k, microblaze, mips, and riscv store the raw stack pointer in
setjmp, while their jmpbuf-unwind.h applies PTR_DEMANGLE to it in
_jmpbuf_sp.  This is currently harmless because these ABIs use the
generic pointer_guard.h, where the C PTR_DEMANGLE is a no-op.

No functional change.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-06-16 14:53:29 -03:00
Adhemerval Zanella afc90416e9 Split pointer_guard.h into C and assembly headers
Move the assembly PTR_MANGLE/PTR_DEMANGLE definitions out of each
pointer_guard.h into a new sibling pointer_guard-asm.h, guarded by
__ASSEMBLER__.  pointer_guard.h now includes pointer_guard-asm.h and
keeps only the C definitions.

No functional change: the .S and .c files still include pointer_guard.h
and get the same macros, and <pointer_guard-asm.h> resolves per-arch
through the usual sysdeps include path.

Checked with a build for all affected ABIs.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-06-16 14:53:29 -03:00
Adhemerval Zanella d50ae543a4 Enable the pointer guard in the dynamic loader
i386, powerpc, s390, sh and sparc fell back to the generic no-op
pointer_guard.h in ld.so, so the loader's own setjmp/longjmp and mangled
function pointers were left unprotected (the other targets already
mangle in rtld).  ld.so is position-independent and sets up
__pointer_chk_guard_local early in dl_main, so the loader can reach it
the same way a statically linked program does:

  - powerpc, s390, sh, sparc: select __pointer_chk_guard_local for
    IS_IN (rtld) and reuse the existing PIC guard load.
  - i386: load it via @GOTOFF off a GOT pointer set up with LOAD_PIC_REG
    in the caller-provided scratch register
    (ld.so does not keep the GOT in %ebx at the setjmp/longjmp entry).
  - s390: drop the IS_IN (rtld) "#undef PTR_DEMANGLE" in __longjmp.c so
    the loader's longjmp demangles to match its setjmp.

Checked on x86_64-linux-gnu, aarch64-linux-gnu, i686-linux-gnu, and
armv7a-linux-gnueabihf.  I also run elf and setjmp checks on qemu
loongarch64, powerpc64, powerpc64le, and s390x systems.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-06-16 14:53:28 -03:00
Adhemerval Zanella a5ec880f80 Consolidate pointer guard to use a relro variable instead of the TCB
For the targets that still read the pointer guard from the thread
descriptor (i386, x86_64, powerpc, s390, sparc, and sh), mangle and
demangle pointers using the module-local __pointer_chk_guard_local (in
ld.so and static binaries) or the global __pointer_chk_guard provided by
the dynamic loader (in shared objects), matching the scheme already used
by aarch64, alpha, arm, csky and loongarch.

On i386 the guard is no longer reachable as a single %gs-relative memory
operand, so the assembly PTR_MANGLE/PTR_DEMANGLE PIC variants need a
register to hold the GOT pointer and the guard address.

This removes the dependency on the TCB pointer_guard slot: drop
THREAD_{GET,SET,COPY}_POINTER_GUARD and the POINTER_GUARD tcb-offset,
and point the POINTER_CHK_GUARD test macro at the relro variable.  The
tcbhead_t slot is kept (renamed to an unused reserved field, except on
s390 which reused the stack guard) to preserve the ABI.  s390 gains a
distinct pointer guard rather than aliasing the stack guard.

Checked on x86_64-linux-gnu, aarch64-linux-gnu, i686-linux-gnu, and
armv7a-linux-gnueabihf.  I also run elf and setjmp checks on qemu
loongarch64, powerpc64, powerpc64le, and s390x systems.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-06-16 11:35:06 -03:00
Adhemerval Zanella 3785d4c0d0 elf: Propagate the pointer guard to ld.so loaded via static dlopen (BZ 34196)
The static-dlopen does not initialize the pointer guard for ABIs that
define THREAD_SET_POINTER_GUARD.  Besides not properly guard the
pointer if a libc.so symbol is called, this can lead to setjmp
failures (a jmp_buf set up by the loaded libc.so.6 cannot be restored
by the static program's __longjmp, and vice versa).

Seed the just-mapped loader's __pointer_chk_guard from the program's
__pointer_chk_guard_local in __rtld_static_init, next to the other
runtime values copied there.

Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-06-16 08:29:57 -03:00
Adhemerval Zanella a767fbcff0 math: Sync acosh with CORE-MATH 887cab6f
It syncs with CORE-MATH commit 887cab6f.  The new tests are from
the CORE-MATH acosh worst-cases inputs that trigger the FE_TOWARDZERO
issue with the previous implementation, along with the hard-to-round
cases from CORE-MATH commits 72febed6 and ce9e99f6.

Checked on x86_64-linux-gnu, x86_64-linux-gnu with --disable-multi-arch
(so soft FMA implementation is used), i686-linux-gnu (which uses its
own acosh implementation), aarch64-linux-gnu, and
armv7a-linux-gnueabihf.

Reviewed-by: Paul Zimmermann <Paul.Zimmermann@inria.fr>
2026-06-15 17:41:15 +00:00
Michael Ford 82b710c190 inet: Remove unused STATIC_NSS define
The --enable-static-nss configuration adds -DSTATIC_NSS to CFLAGS for
the inet subdirectory, but no source file has referenced the STATIC_NSS
macro for quite some time. So remove the dead define.

Signed-off-by: Michael Ford <fanquake@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-15 15:00:03 +00:00
Michael Ford 2169401438 nss: Remove <gnu/lib-names.h> include from nsswitch.c
nsswitch.c no longer references any of the *_SO macros provided by
<gnu/lib-names.h>. The only consumer, __nss_shlib_revision, moved to
nss_module.c as part of the NSS reorganization in glibc 2.33 (and
nss_module.c includes the header unconditionally), but the guarded
include was left behind.

Signed-off-by: Michael Ford <fanquake@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-15 14:59:41 +00:00
Adhemerval Zanella 1111fbdd3e arm: Save/restore VFP registers in PLT trampolines (BZ 34144, BZ 15792)
_dl_runtime_resolve and _dl_runtime_profile only preserved the integer
argument registers (r0-r3) across the inner call to _dl_fixup /
_dl_profile_fixup.  Two related ABI requirements demand more:

  * Under AAPCS-VFP, d0-d7 hold the caller's double arguments to the
    function being resolved.  Recent GCC emits VFP instructions inside
    the fixup routines, clobbering them, so the resolved function sees
    corrupted arguments (BZ 34144).

  * Per RTABI32, the __aeabi_mem* helpers (and similar runtime helpers
    reachable through the dynamic linker) must only corrupt integer
    core registers.  IFUNC resolvers, audit modules, and interposed
    malloc invoked during symbol resolution may also use VFP, even on
    softfp ABI builds (BZ 15792).

Save all call-clobbered VFP state -- d0-d15 unconditionally, d16-d31
when HWCAP_ARM_VFPD32 is set, and fpscr -- around the inner fixup
call.  Whether VFP is usable is a property of the hardware, not of
the ABI glibc was built with, so the decision is gated on AT_HWCAP at
runtime in both hardfp and softfp builds; hardfp builds will always
find HWCAP_ARM_VFP set, while softfp builds running on a non-VFP CPU
correctly skip the save.

For _dl_runtime_profile the save area is slipped in just before the
bl to _dl_profile_fixup; the outgoing framesizep argument is
recomputed to account for the extra frame, and both the fast path
(no audit framesize) and the slow path (audit wraps with
pltenter/pltexit) traverse the restore before splitting.

Checked on arm-linux-gnueabihf.

Tested-by: Aurelien Jarno <aurelien@aurel32.net>
Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
2026-06-15 14:59:10 +00:00
Adhemerval Zanella 6adb7400fd aarch64: Fix static PIE CPU feature detection (BZ 34205)
ARCH_INIT_CPU_FEATURES is called from __libc_start_main before
_dl_relocate_static_pie, so any code it runs must not rely on
relocations being applied yet.  Two issues break this under static PIE:

  1. The cpu_list table held 'const char *name' members, whose
     addresses are not link-time constants under PIE and thus require
     base-relative relocations that are not yet in place.  Replace the
     array of structs with a flat NUL-separated name string and a
     parallel midr array, removing the pointer relocations.

  2. tunable_strcmp pulls in memcmp, which is an IFUNC and is likewise
     unresolved at this point.  Redirect memcmp to __memcmp_generic in
     the aarch64 dl-symbol-redir-ifunc.h and include it from
     csu/libc-start.c.

Checked on aarch64-linux-gnu.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
2026-06-15 14:58:11 +00:00
Sana Kazi 1cba6073e5 nptl: open threads comm with O_WRONLY|O_CLOEXEC
pthread_setname_np opens the thread's comm file using O_RDWR, but the
function only ever writes to it.  This causes two distinct problems:

1. Missing O_CLOEXEC: the file descriptor is not marked close-on-exec,
   so it remains open across fork+exec.  A child process that audits
   its inherited file-descriptor set will encounter an unexpected /proc
   fd it did not open and may treat this as a security violation and
   abort.

2. Unnecessary O_RDWR: requesting read+write access when only write
   access is needed can cause open() to fail under security policies
   that permit writing to /proc/<tid>/comm but deny reading it.

Fix both issues by replacing O_RDWR with O_WRONLY|O_CLOEXEC

Similarly, updated pthread_getname_np to use O_CLOEXEC.

Bug-Id: 34192[https://sourceware.org/bugzilla/show_bug.cgi?id=34192]

Signed-off-by: Sana Kazi <Sana.Kazi@bmwtechworks.in>
Reviewed-by: Florian Weimer <fweimer@redhat.com>
2026-06-15 16:37:59 +02:00
Yury Khrustalev 9ed3576e61 malloc: aarch64: Add ifuncs for malloc functions
Introduce ifuncs and resolvers for functions pertinent to the
malloc interface on the AArch64 target: malloc, calloc, free,
realloc, memalign, valloc, pvalloc, posix_memalign, aligned_alloc,
free_sized, free_aligned_sized, malloc_usable_size.

A target can define the USE_MULTIARCH_MALLOC macro. In this case
it must provide alternative aliases for the malloc functions that
point to the ifuncs.

This implementation respects the --disable-multi-arch configure
flag. If multi-arch support is disabled, the generic aliases
are used on aarch64.

This patch contains aarch64-specific resolvers. At this moment they
return core implementations but in the future they can be changed
to support for features, e.g. to handle memory tagging.
2026-06-15 13:40:22 +01:00
Fabian Rast df83fa8813 rtld: cache cpuid results on the stack for intel
dl_init_cacheinfo retrieves various information about cache
sizes, using the cpuid instruction on x86.
Previously, the same cpuid leaves were queried multiple times.
This behavior caused intel_check_word to prominently show up in
profiles of dynamic loader startup on the Intel(R) Xeon(R) Gold 6430.
The big performance impact could not be reproduced on other Intel cpus.

This patch reduces the number of cpuid queries on startup
by caching their results on the stack for reuse when searching for a
different cache size value.
This approach does not change the overall design of
the cache enumeration code (repeated calls to handle_* functions).
The values are cached on the stack instead of globally (e.g.
in the cpu_features global) because they are never needed after
early initialization.

The cache is only active for Intel cpus, because it has not yet
been shown through benchmarks that it meaningfully improves performance
for other processors.

Signed-off-by: Fabian Rast <fabian.rast@tum.de>
Reviewed-by: Sunil K Pandey <skpgkp2@gmail.com>
2026-06-11 10:10:38 -07:00
Joseph Myers 8ae37adb21 Fix -Wconstant-logical-operand error for Hurd
Building for Hurd with GCC mainline produces an error in pt-block.c
(here MSG_OPTIONS is a macro that may be defined before pt-block.c is
included by another source file):

In file included from ../sysdeps/mach/htl/pt-block-intr.c:6:
../sysdeps/mach/htl/pt-block.c: In function '__pthread_block_intr':
../sysdeps/mach/htl/pt-block.c:49:42: error: use of logical '&&' with constant operand '1024' [-Werror=constant-logical-operand]
   49 |   if ((MSG_OPTIONS & MACH_RCV_INTERRUPT) && err == MACH_RCV_INTERRUPTED)
      |                                          ^~
../sysdeps/mach/htl/pt-block.c:49:42: note: use '&' for bitwise operation
   49 |   if ((MSG_OPTIONS & MACH_RCV_INTERRUPT) && err == MACH_RCV_INTERRUPTED)
      |                                          ^~
      |                                          &

Fix this, and a similar error in pt-timedblock.c, with an explicit != 0.

Tested with build-many-glibcs.py (compilers and glibcs builds) for
i686-gnu and x86_64-gnu.
2026-06-10 18:49:53 +00:00
Joseph Myers d8fa402f5e Fix -Werror=constant-logical-operand errors in soft-fp
Building for sparc64-linux-gnu with GCC mainline produces a series of
-Werror=constant-logical-operand errors in soft-fp code, relating to
calls to _FP_TO_INT where rsigned is -1 not 0 or 1.  Use explicit != 0
in the soft-fp code to avoid these errors.

Tested with build-many-glibcs.py (compilers and glibcs builds) for
sparc64-linux-gnu with GCC mainline.
2026-06-10 16:33:27 +00:00
Joseph Myers 690472e2a5 Fix -Wconstant-logical-operand error in tst-printf-format-skeleton.c
Building the glibc testsuite with GCC mainline fails with:

In file included from tst-printf-format-skeleton-double.c:33,
                 from tst-printf-format-p-double.c:20:
tst-printf-format-skeleton.c: In function 'do_test':
tst-printf-format-skeleton.c:308:12: error: use of logical '&&' with constant operand '53' [-Werror=constant-logical-operand]
  308 |   if (PREC && printf ("prec:%i\n", PREC) < 0)
      |            ^~
tst-printf-format-skeleton.c:308:12: note: use '&' for bitwise operation
  308 |   if (PREC && printf ("prec:%i\n", PREC) < 0)
      |            ^~
      |            &
cc1: all warnings being treated as errors

Fix this using an explicit "!= 0".  Tested with build-many-glibcs.py
for aarch64-linux-gnu.
2026-06-10 14:01:34 +00:00
Zihong Yao 9cf4dd03bd riscv: Keep __memcmpeq alias with RVV memcmp
Commit 2efc702628 replaces the generic RISC-V memcmp, which also exports
__memcmpeq.

A dedicated RISC-V __memcmpeq implementation is expected in a later patch,
which is still under review. Keep the alias here so this patch does not
temporarily drop the existing __memcmpeq ABI.

Tested on a Muse-Pi board with GCC 15.2.0 using `--with-arch=rv64g_zca_zcd --with-abi=lp64d`
and `CFLAGS="-march=rv64g_zca_zcd -mabi=lp64d -O2"`. Full make check was
run for both multiarch and non-multiarch builds.
This also fixes the string/inl-tester link failure caused by the missing
__memcmpeq symbol.

In both runs, the only FAILs are the unchanged cospi/sinpi/tanpi math test
failures.

Signed-off-by: Zihong Yao <zihong.plct@isrc.iscas.ac.cn>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-09 09:06:07 -03:00
Brian Jorgensen 6f072238e9 fcntl-linux.h : Fix typo in comments. (bug 17119)
This commit fixes incorrect comments in set / get macros.

Signed-off-by: Brian Jorgensen <brjorgen98@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-09 09:05:59 -03:00
Florian Weimer e9325bd7d0 iconv: Suppress intermediate errors with //TRANSLIT (bug 34236)
When tentatively converting characters on behalf of
__gconv_transliterate, do not create a persistent error.  Just
produce a local error, and rely on __gconv_transliterate to
produce the error if all transliteration options are exhausted.

This fixes transliteration of “½” to ASCII, which cannot use the
“ 1⁄2 ” alternative.  Eventually, the “ 1/2 ” alternative is chosen,
but the error sticks.  Therefore, iconv exited with status 1 before
this change.

Adjust iconv/tst-iconv_prog.sh to test both C and en_US.UTF-8 locales.
This requires changing the way the ICONV template is defined, so that
run_program_env is evaluated multiple times.

Fixes commit 9a4b0eaf72 ("iconv: do not
report error exit with transliteration [BZ #32448]"),
commit 6cbf845fcd ("iconv: Preserve
iconv -c error exit on invalid inputs (bug 32046)"), and bug 34236.

Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
2026-06-09 07:28:02 +02:00
Josef Johansson 88099e289d nscd: Typo in comment
Fixes: https://sourceware.org/bugzilla/show_bug.cgi?id=34240

Signed-off-by: Josef Johansson <josef@oderland.se>
Reviewed-by: Florian Weimer <fweimer@redhat.com>
2026-06-08 17:32:27 +02:00
Andreas Schwab 4e22f95057 libio: use _IO_have_wbackup for wide streams
Fix two wrong uses of _IO_have_backup on a wide stream.
2026-06-08 17:01:15 +02:00
Sam James d0cc9bf859 elf: don't clobber ld.so.conf in tst-glibc-hwcaps-prepend-cache [BZ #34210]
dbe5065f21 and ae589cb84d
cater for setups where ld.so.conf{,.d} is required to find runtime support
libraries, but tst-glibc-hwcaps-prepend-cache clobbers the created ld.so.conf
with its own entry.

Fix it to instead use the ld.so.conf.d created in ae589cb84d
to co-exist with existing entries.

Bug: https://bugs.gentoo.org/976773
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31901
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34210
Tested-by: Andreas K. Hüttel <dilfridge@gentoo.org>
Reported-by: Eli Schwartz <eschwartz@gentoo.org>
Reviewed-by: Andreas K. Hüttel <dilfridge@gentoo.org>
2026-06-06 21:22:46 +01:00
Adhemerval Zanella 2b6f6710a1 elf: Remove inhibit_stack_protector from __ifunc_resolver
With 01964c3ec8e fix ifunc resolvers can be fully instrumented with
stack protector.

Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu
built with --enable-stack-protector=all.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-06-05 16:20:20 -03:00
Adhemerval Zanella f9a11a380d elf: Set up TLS slotinfo for dlopen'd modules before relocation (BZ 34170)
An IFUNC resolver in a DSO that is being loaded by dlopen is allowed
to read its own TLS storage during the resolver call.  After
af34b1376a ("elf: Initialize static TLS before relocation processing
(BZ 34164)") that works for the initial-exec model on every supported
architecture.

However, it does not work for the dynamic-TLS path (on both -mtls-dialect mode,
if the ABI supports both).  Both lookup paths index the calling thread's
DTV by the new module's l_tls_modid and, on miss, walk
GL(dl_tls_dtv_slotinfo_list) to discover the module and lazily allocate
its TLS block.  The just-loaded DSO is however not yet in that list when
its resolver fires, so the lookup faults inside dlopen.  This is the
direct dlopen analog of BZ 34164.

The solution is to reorder dl_open_worker_begin so the slotinfo install
happens before the relocation pass.  The new order is:

  1. resize_scopes, resize_tls_slotinfo, add_to_global_resize
     (unchanged, still recoverable).
  2. update_tls_slotinfo: register the new modules in slotinfo, bump
     dl_tls_generation, initialise their static TLS images.
  3. Relocate the new objects.  IFUNC resolvers can now read their
     own DSO's __thread storage via any TLS model.
  4. Demarcation point.
  5. update_scopes, _dl_find_object_update.

Checked on aarch64-linux-gnu and x86_64-linux-gnu.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-06-05 16:20:20 -03:00
Wilco Dijkstra 49244da110 malloc: Simplify _int_free_chunk
Simplify _int_free_chunk() and always lock if needed.  Use
_int_free_merge_chunk() for cases that assume the arena has been locked
instead.  Move the errno save/restore to _int_free_maybe_trim().

Reviewed-by: Yury Khrustalev <yury.khrustalev@arm.com>
2026-06-05 13:56:37 +00:00
c275c424b3 riscv: Add RVV strrchr for both multiarch and non-multiarch builds
This patch adds an RVV-optimized implementation of strrchr for RISC-V and
enables it for both multiarch (IFUNC) and non-multiarch builds.

The implementation integrates Hau Hsu's 2023 RVV work under a unified
ifunc-based framework. A vectorized version (__strrchr_vector) is added
alongside the generic fallback (__strrchr_generic). The runtime resolver
selects the RVV variant when RISCV_HWPROBE_KEY_IMA_EXT_0 reports vector
support (RVV).

Currently, the resolver still selects the RVV variant even when the RVV
extension is disabled via prctl(). As a consequence, any process that
has RVV disabled via prctl() will receive SIGILL when calling strrchr().

Co-authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-authored-by: Jerry Shih <jerry.shih@sifive.com>
Signed-off-by: Yao Zihong <zihong.plct@isrc.iscas.ac.cn>
Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
2026-06-05 00:48:14 +00:00
bbc3867f8d riscv: Add RVV strchr for both multiarch and non-multiarch builds
This patch adds an RVV-optimized implementation of strchr for RISC-V and
enables it for both multiarch (IFUNC) and non-multiarch builds.

The implementation integrates Hau Hsu's 2023 RVV work under a unified
ifunc-based framework. A vectorized version (__strchr_vector) is added
alongside the generic fallback (__strchr_generic). The runtime resolver
selects the RVV variant when RISCV_HWPROBE_KEY_IMA_EXT_0 reports vector
support (RVV).

Currently, the resolver still selects the RVV variant even when the RVV
extension is disabled via prctl(). As a consequence, any process that
has RVV disabled via prctl() will receive SIGILL when calling strchr().

Co-authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-authored-by: Jerry Shih <jerry.shih@sifive.com>
Signed-off-by: Yao Zihong <zihong.plct@isrc.iscas.ac.cn>
Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
2026-06-05 00:48:09 +00:00
512f99e71d riscv: Add RVV memchr for both multiarch and non-multiarch builds
This patch adds an RVV-optimized implementation of memchr for RISC-V and
enables it for both multiarch (IFUNC) and non-multiarch builds.

The implementation integrates Hau Hsu's 2023 RVV work under a unified
ifunc-based framework. A vectorized version (__memchr_vector) is added
alongside the generic fallback (__memchr_generic). The runtime resolver
selects the RVV variant when RISCV_HWPROBE_KEY_IMA_EXT_0 reports vector
support (RVV).

Currently, the resolver still selects the RVV variant even when the RVV
extension is disabled via prctl(). As a consequence, any process that
has RVV disabled via prctl() will receive SIGILL when calling memchr().

Co-authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-authored-by: Jerry Shih <jerry.shih@sifive.com>
Signed-off-by: Yao Zihong <zihong.plct@isrc.iscas.ac.cn>
Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
2026-06-05 00:48:01 +00:00
496ccfe312 riscv: Add RVV memccpy for both multiarch and non-multiarch builds
This patch adds an RVV-optimized implementation of memccpy for RISC-V and
enables it for both multiarch (IFUNC) and non-multiarch builds.

The implementation integrates Hau Hsu's 2023 RVV work under a unified
ifunc-based framework. A vectorized version (__memccpy_vector) is added
alongside the generic fallback (__memccpy_generic). The runtime resolver
selects the RVV variant when RISCV_HWPROBE_KEY_IMA_EXT_0 reports vector
support (RVV).

Currently, the resolver still selects the RVV variant even when the RVV
extension is disabled via prctl(). As a consequence, any process that
has RVV disabled via prctl() will receive SIGILL when calling memccpy().

Co-authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-authored-by: Jerry Shih <jerry.shih@sifive.com>
Signed-off-by: Yao Zihong <zihong.plct@isrc.iscas.ac.cn>
Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
2026-06-05 00:47:51 +00:00
2efc702628 riscv: Add RVV memcmp for both multiarch and non-multiarch builds
This patch adds an RVV-optimized implementation of memcmp for RISC-V and
enables it for both multiarch (IFUNC) and non-multiarch builds.

The implementation integrates Hau Hsu's 2023 RVV work under a unified
ifunc-based framework. A vectorized version (__memcmp_vector) is added
alongside the generic fallback (__memcmp_generic). The runtime resolver
selects the RVV variant when RISCV_HWPROBE_KEY_IMA_EXT_0 reports vector
support (RVV).

Currently, the resolver still selects the RVV variant even when the RVV
extension is disabled via prctl(). As a consequence, any process that
has RVV disabled via prctl() will receive SIGILL when calling memcmp().

Co-authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-authored-by: Jerry Shih <jerry.shih@sifive.com>
Signed-off-by: Yao Zihong <zihong.plct@isrc.iscas.ac.cn>
Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
2026-06-04 19:02:07 -05:00
Wilco Dijkstra 93e6135904 malloc: Reduce maximum arenas
The default maximum arenas is 8 times the number of cores in a 64-bit system.
Since modern CPUs have many cores and big servers have 256 cores, this results
in excessive number of arenas, which wastes memory.  Limit the number of arenas
to max (8, ncores) which is less extreme.  In the future the limit should be
lowered further for large systems.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-04 14:25:37 +00:00
Adam Yi 13dc1040af resolv: reset _u._ext.nscount in __res_iclose [BZ #34154]
__res_iclose, when called with FREE_ADDR=true, frees and NULLs every
statp->_u._ext.nsaddrs[ns] but does not reset statp->_u._ext.nscount.
This breaks the invariant relied on by __res_context_send's cache
validation loop in resolv/res_send.c:293-312: when _u._ext.nscount
is non-zero, every _u._ext.nsaddrs[ns] corresponding to
statp->nsaddr_list[ns].sin_family != 0 is expected to be non-NULL.

res_init unconditionally calls __res_iclose(&_res, true) before
__res_vinit, so any __res_vinit failure (for example,
fopen("/etc/resolv.conf") returning EMFILE under file-descriptor
exhaustion, or any allocation failure in __resolv_conf_load,
__resolv_conf_allocate, or __resolv_conf_attach before
update_from_conf runs) leaves _u._ext.nscount non-zero with all
_u._ext.nsaddrs[] NULL.  The next name lookup walks the validation
loop and dereferences NULL in sock_eq.  A DNS lookup may fail after
a failed res_init, but it should not segfault.

Reset _u._ext.nscount = 0 alongside the existing __resolv_conf_detach
call.  The next __res_context_send call then re-enters its init
block (res_send.c:316-335) and repopulates _u._ext.nsaddrs[] from
statp->nsaddr_list[], which __res_iclose leaves untouched.  This
also lets support/resolv_test.c drop its now-redundant manual reset
after __res_iclose(&_res, true).

Add a regression test that drops RLIMIT_NOFILE so fopen of
/etc/resolv.conf fails with EMFILE inside __res_vinit, then verifies
the subsequent gethostbyname does not crash.

Signed-off-by: Adam Yi <ayi@janestreet.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-03 11:11:09 -03:00
Alejandro Colomar 20fffa2c39 sysdeps/ieee754/ldbl-128ibm-compat/test-printf-chk-ldbl-compat.c: Fix typo
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-03 11:11:06 -03:00
Wilco Dijkstra b7e256c32c malloc: Improve documentation of malloc tunables
Update default for tcache_count tunable.  Remove existing documentation and
mention removal of fastbins in mxfast tunable.  Improve wording of hugetlb
tunable, including default for AArch64.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-06-03 13:10:58 +00:00
Wilco Dijkstra 4a7805d05c malloc: Minor cleanups
Merge request2size into checked_request2size.  Improve interface of
clear_memory.  Fix incorrect use of __glibc_unlikely in __libc_calloc.
Fix missing tabs.

Reviewed-by: Yury Khrustalev <yury.khrustalev@arm.com>
2026-06-03 13:10:06 +00:00
Adhemerval Zanella 0b27186151 support: Also run malloc hugetlb=1 tests when transparent hugepage is 'always'
The glibc.malloc.hugetlb=1 is redundant with kernel is n 'always'
mode, but the madvise does work, and the tunable should not fail.

Suggested-by: Yury Khrustalev <yury.khrustalev@arm.com>
Reviewed-by: Yury Khrustalev <yury.khrustalev@arm.com>
2026-06-03 09:09:27 -03:00
Adhemerval Zanella e951752cd0 arm: Fix main-in-dso with non-BFD linkers (BZ 34098)
Commit c2d6afb4a0 changed the PIC && !SHARED path of start.S (used by
crt1.o and rcrt1.o) to load main via a R_ARM_REL32 data relocation.  When
main is provided by a shared object, BFD ld synthesizes a canonical PLT
entry for it, but other linkers (e.g. mold) do not, producing a broken
binary that crashes at startup.

Follow the aarch64 approach and reference main through a local __wrap_main
that tail-calls it with a branch relocation, which every linker turns into
a PLT entry.

Checked on armv7a-linux-gnueabihf with and without --enable-default-pie.
2026-06-03 09:09:12 -03:00
Adhemerval Zanella 5879990337 elf: Fix tst-ifunc-tls-init with --disable-default-pie
The test failed with --disable-default-pie because its primary check read
the resolver's diagnostic side effect before the resolver had run.

In a non-PIE executable the references to the IFUNC (fptr and ifunc_tls)
are satisfied through a canonical IPLT entry in the executable itself.
Under the default lazy binding that IPLT is resolved on first use, not
during startup relocation, so the resolver had not yet run when
'check_sentinel' inspected that value.  With a PIE executable
(or LD_BIND_NOW=1) the resolver runs eagerly at startup and the check
passed.  The dlopen path was unaffected because dlopen resolves the
data relocation eagerly.

This is a test ordering issue: the resolver always reads the initial-exec
TLS correctly whenever it runs, so the BZ 34164 fix is not involved.
Reorder test_tls_ifunc so that fptr/ifunc_tls force the IFUNC to be
resolved before the last_seen_sentinel value is inspected.

Checked on x86_64-linux-gnu and i686-linux-gnu with --disable-default-pie.

Reported-by: Yury Khrustalev <yury.khrustalev@arm.com>
Reviewed-by: Yury Khrustalev <yury.khrustalev@arm.com>
2026-06-02 15:10:02 -03:00
Sachin Monga 7193069543 powerpc64le: Add optimized __memcmpeq for POWER10
__memcmpeq (added in glibc 2.35) was previously an alias to memcmp on
POWER10 via strong_alias. However, in the multiarch IFUNC path, this
caused __memcmpeq to resolve to the generic C memcmp.c implementation
rather than the optimized POWER10 memcmp.S, leaving a significant
performance gap.

Unlike memcmp, __memcmpeq only needs to return zero or nonzero with
no requirement on the sign or magnitude for unequal inputs, allowing
a simpler and faster implementation.

Performance on POWER10 :

  1) __memcmpeq (generic) -> __memcmpeq_power10
     The primary motivation - __memcmpeq was resolving to generic C
     in the multiarch path.

  - Small data (< 8B to < 512B) : ~52% - 82% improvement.
  - Bulk  (< 16MB to < 256MB)   : ~25% - 32% improvement.
  - Large (1GB)			: ~33% improvement

  2) memcmp_power10 (optimized .S) -> __memcmpeq_power10:
     Comparing dedicated __memcmpeq against the optimized memcmp
     it previously aliased to.

  - Small data (< 8B to < 256B) : No improvement observed.
    Real-world workloads predominantly operate on larger buffers
  - >= 512B			: ~9%  improvement.
  - 16MB - 128MB		: ~25% - 32% improvement.
  - 256MB			: ~3%  improvement.
  - Large (1GB)			: On par.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
2026-06-02 00:51:36 -05:00
Adhemerval Zanella c5278a6428 arm: Redirect memcpy to __memcpy_arm for loader/static init code
After "elf: Initialize TCB and stack-protector before static IFUNC
resolvers (BZ 20680, BZ 27582, BZ 28817)", early loader and static
initialization code may run before the IFUNC resolvers are processed.

The armv7-a supports memcpy, so add a dl-symbol-redir-ifunc.h to
redirect the memcpy to to __memcpy_arm implementation.

Checked on armv7a-linux-gnueabihf.
2026-06-01 13:16:26 -03:00
Yury Khrustalev 0458560b5d misc: Fix typos in comments 2026-06-01 13:29:53 +01:00
Diego Nieto Cid 7d213541cb hurd: define SO_TIMESTAMP in socket.h
* sysdeps/mach/hurd/bits/socket.h: add SO_TIMESTAMP enum entry
  and define corresponding preprocessor macro.
Message-ID: <20260531195342.1633-1-dnietoc@gmail.com>
2026-05-31 21:55:26 +02:00
Yury Khrustalev 70be9819b5 malloc: aarch64: Remove broken memory tagging
Remove the --enable-memory-tagging configure option along with
all associated variables and macros.

Removing the glibc.mem.tagging tunable.

Remove the memory-tagging makefile variable.

Remove the USE_MTAG macro definition and code that is conditionally
compiled when this macro is defined.

As a result, we change 'mtag_mmap_flags' to 'extra_mmap_prot' that
is now always defined. Change of the name due to this being used
as part of PROT options in mmap syscalls rather than part of flags.

Remove 'mtag_enabled' that would become compile-time false. Also
remove any code that would never be compiled when 'mtag_enabled'
is false.

Remove AArch64-specific code pertinent to memory tagging, that is
currently broken, from the core malloc implementation. We keep
the assembly code, since we are going to need it in the future. to
preserve Git history.

Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
2026-05-31 16:11:23 +01:00
Adhemerval Zanella 7052455f0e hesiod: use booleans in parser macro calls
The swallow argument in the INT_FIELD and STRING_FIELD macros is used as a
boolean, change all callers to use false and true instead of 0 and 1.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-29 13:58:58 -03:00
Adhemerval Zanella 41e9457c53 hesiod: fix swapped arguments in service parser
The port number in the service file is a decimal number followed by a
single slash.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-29 13:58:58 -03:00
Adhemerval Zanella 808035fdff math: Fix non-narrowing test build with arg-format conditions
auto-libm-test-in shares inputs between narrowing and non-narrowing
functions, and some carry arg-format conditions (e.g. "arg-ibm128").
When auto-libm-test-out-fma is regenerated, gen-libm-test.py turns these
into TEST_COND_arg_ibm128, which expands via ARG_MANT_DIG to ARG_PREFIX.
ARG_PREFIX is only defined for TEST_NARROW, so the non-narrowing fma test
failed to build with "ARG_PREFIX_MANT_DIG undeclared".

Define TEST_COND_arg_ibm128 to 0 when ARG_FLOAT is not defined, mirroring
the existing guard for TEST_COND_ibm128_libgcc; in the non-narrowing case
there is no separate argument format, so the condition is always false.

Regenerate auto-libm-test-out-fma accordingly.
2026-05-29 13:58:58 -03:00
Muhammad Kamran b9e893b60e aarch64: Use build attributes for asm feature marking
When the compiler defines __ARM_BUILDATTR64_FV, emit AArch64
feature-and-bits build attributes for BTI, PAC, and GCS from sysdep.h
instead of a GNU property note.  Keep the GNU property note as the
fallback for older toolchains.

Mirror the same marking logic in elf/tst-asm-helper.h so custom test
DSOs and assembly tests that cannot include sysdep.h get consistent
feature marking.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-29 13:58:58 -03:00
Martin Coufal bec480cbe6 misc: add a new test for gethostname
Add a simple test that runs in a UTS namespace: checks gethostname
after various sethostname values (including empty and HOST_NAME_MAX),
verifies ENAMETOOLONG when the buffer is too small for a maximal name,
EINVAL when sethostname is given a name longer than HOST_NAME_MAX.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-29 13:58:58 -03:00
Frédéric Bérat 9ef37798fa test: Fix and stabilize tst-wcsmbs-clone-overflow test
The test tst-wcsmbs-clone-overflow was initially added to tests-static.
However, this causes the test to be unstable because gconv modules
dynamically load libc.so. Any discrepancy between the statically linked
version and the dynamically loaded one can lead to a crash.

By removing the test from tests-static, it relies on dynamic linking,
safely bypassing the dlopen crash. Since the test is now dynamically
linked, it cannot use the internal thread-local symbol
_NL_CURRENT_DATA(LC_CTYPE) because _nl_current_LC_CTYPE is hidden in
libc.so, leading to undefined references. Thus, the test now uses
newlocale and uselocale, safely extracting the locale data from the
returned locale_t object.

Furthermore, using newlocale requires the gconv-modules configuration to
be built and available so that the ISO8859-1.so module can be
dynamically loaded. Otherwise, glibc falls back to the built-in C locale
conversions, leaving __shlib_handle as NULL and silently bypassing the
reference counter increment.
A new Makefile fragment, gen-gconv-modules.mk, is introduced to ensure
the gconv-modules are built before the test runs, and an explicit check
for __shlib_handle != NULL is added to the test.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
2026-05-29 11:34:20 +02:00
Adhemerval Zanella 1e7935ae87 math: Fix fma alignment when exponent difference is exactly 64 (BZ 34183)
When d (the exponent difference between z and x*y) is exactly 64,
the alignment path shifts z left by 64 bits via (zhi = nz.m, zlo = 0)
and decrements d to 0, then takes the inner 'if (d < 64)' branch
which evaluates 'rhi << (64 - d)' with d == 0.  A shift by 64 of a
64-bit value is UB in C.

Add the explicit 'if (d == 0)' empty branch (present in the
original musl implementation).

Checked on x86_64-linux-gnu with --disable-multi-arch and
arm-linux-gnueabihf.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-28 10:22:48 -03:00
Adhemerval Zanella b20b94006c support: Skip malloc hugetlb={1,2} variants when kernel cannot honor them
The malloc test variants run with GLIBC_TUNABLES=glibc.malloc.hugetlb=1
exercise transparent huge pages via MADV_HUGEPAGE, which is only
meaningful when /sys/kernel/mm/transparent_hugepage/enabled is set to
'madvise' ('always' makes the madvise redundant and 'never' makes it
ineffective).  The hugetlb=2 variants rely on MAP_HUGETLB, which
requires a positive /proc/sys/vm/nr_hugepages.  On systems that do not
satisfy these prerequisites - including any non-Linux target - those
runs only consume CPU time in this case.

Add support/support_check_hugetlb.{c,h} exposing:

  - support_thp_is_madvise: true iff THP is in 'madvise' mode;
  - support_hugepages_reserved: true iff nr_hugepages > 0;
  - support_check_malloc_hugetlb: inspects GLIBC_TUNABLES and calls
    FAIL_UNSUPPORTED when the requested hugetlb mode cannot be honored.

Gate the check at compile time so only the variant binaries pay for
it.

Reviewed-by: DJ Delorie <dj@redhat.com>
2026-05-27 15:51:21 -03:00
Adhemerval Zanella eceaece54b elf: Re-initialise static TLS after .tdata relocation (BZ 34164)
The af34b1376a (BZ 34164) changed the
TLS setup from:

  relocation loop (applies relocations to .tdata in DSO memory)
  _dl_allocate_tls_init copies relocated .tdata -> main thread TLS

to a new order:

  _dl_allocate_tls_init copies unrelocated .tdata -> main thread TLS
  relocation loop (relocates .tdata in DSO memory, but the TLS block
  has stale copies)

This broke file-scope thread-local initialised with the address of a
function (for instance the cache structs in libmpfr).

Fix it by splitting ELF_DYNAMIC_RELOCATE inside
_dl_relocate_object_no_relro into the non-IRELATIVE and IRELATIVE
sub-passes (similar as done on static-pie startup by b75ad99d45)
and call _dl_init_static_tls between them.  By the time the IFUNC
pass fires, .tdata is fully relocated.

Checked on x86_64-linux-gnu and aarch64-linux-gnu.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-27 10:43:30 -03:00
Wilco Dijkstra b26cc4fbb7 Arch64: Add support for SVE2 ifuncs
Add support for SVE2 in cpu-features. Minor cleanup of init-arch.h.

Reviewed-by: Yury Khrustalev <yury.khrustalev@arm.com>
2026-05-27 13:21:37 +00:00
Diego Nieto Cid c289868492 hurd: adjtime: on error return -1 and set errno
* sysdeps/mach/hurd/adjtime.c: use __hurd_fail to return
    errors back to the caller.
Message-ID: <20260527014103.10791-1-dnietoc@gmail.com>
2026-05-27 14:18:34 +02:00
Avinal Kumar 37318986e7 sunrpc/Makefile: Split and sort tests
This commit splits and sorts the tests in sunrpc/Makefile.

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
2026-05-27 08:15:34 -04:00
Samuel Thibault b507b6d4c6 Hurd: restore some SIOC ioctls
We do not define struct ifreq_short and ifreq_int, but we do
define _IOT_ifreq_short and _IOT_ifreq_int, and need these for
sysdeps/gnu/ifaddrs.c and sysdeps/mach/hurd/if_index.c
2026-05-26 23:12:46 +02:00
Samuel Thibault 7073112263 Hurd: comment ioctls which cannot currently compile
We don't currently have struct ifreq_short, ifreq_int and ifaliasreq, so
don't let applications even try to compile these.
2026-05-26 22:37:34 +02:00
Samuel Thibault ec98b25af5 Hurd: comment PF_ROUTE/AF_ROUTE defines
Comment out the PF_ROUTE and AF_ROUTE defines, since they would be used for
PF_ROUTE setsockopts, which are not available on Hurd.
2026-05-26 22:29:29 +02:00
Pino Toscano 59a0de89ba Hurd: comment PF_LINK/AF_LINK defines
Comment out the PF_LINK and AF_LINK defines, since they are usually associated
with struct sockaddr_dl, which is not available on Hurd.
2026-05-26 22:27:50 +02:00
Stefan Liebler c1b589b21c s390: Enabling lint-makefiles
The s390 specific Makefiles were adjusted to match the required format
for scripts/lint-makefiles.sh / scripts/sort-makefile-lines.py.

Afterwards the lines were sorted by those scripts.
And the testcase lint-makefiles is passing.
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-26 11:05:40 +02:00
Diego Nieto Cid f531cab589 hurd: let the root user raise its priority
Check for task_max_priority RPC

      * config.h.in: add #undef for HAVE_MACH_TASK_MAX_PRIORITY.
      * sysdeps/mach/configure.ac: use mach_RPC_CHECK to check for
        task_max_priority RPC in mach_host.defs.
      * sysdeps/mach/configure: regenerate file.

    Use task_max_priority when setpriority is called by root

      * sysdeps/mach/hurd/setpriority.c: clamp the prio argument
        to the range [-NZERO, NZERO-1] and use task_max_priority
        when called by root.
Message-ID: <8e806c83d8d7b59b2894b4944e4ad82477ecc3cd.1779316637.git.dnietoc@gmail.com>
2026-05-25 23:38:23 +02:00
Etienne Brateau 156141a8f3 hurd: add validations in msync
POSIX specs specify that invalid flags shall return EINVAL and that
ENOMEM shall be returned in case of address outside of address space or
when one or more pages are not mapped.

Signed-off-by: Etienne Brateau <etienne.brateau@gmail.com>
Message-ID: <20260525211142.131508-1-etienne.brateau@gmail.com>
2026-05-25 23:29:13 +02:00
Diego Nieto Cid 58ae7f8f0c hurd: clamp the setpriority prio argument to the range [-NZERO, NZERO-1]
The Open Group Base Specifications Issue 8
getpriority ( https://pubs.opengroup.org/onlinepubs/9799919799/ )

<< The nice value is in the range [0,2*{NZERO} -1], while the
   return value for getpriority() and the third parameter for
   setpriority() are in the range [-{NZERO},{NZERO} -1]. >>

So given that NZERO is defined to 20, we shall use it to clamp to the range
specified by POSIX.

That range is then mapped to something similar to [0, 2*{NZERO}-1], as
specified by POSIX, through the usage of the macro NICE_TO_MACH_PRIORITY
and MACH_PRIORITY_TO_NICE. (i.e. [5, 45] )
2026-05-25 22:56:42 +02:00
Adhemerval Zanella 8560164813 intl: Fix tst-gettext under inherited LC_* environment
The final block of tst-gettext unsets LC_ALL plus LC_MESSAGES, LC_CTYPE,
LC_TIME and LC_NUMERIC, sets LANG=existing-locale, and then expects
setlocale (LC_ALL, "") to resolve every category through LANG.  Any
other LC_* category inherited from the invoking shell (e.g. LC_PAPER,
LC_MONETARY) still takes precedence over LANG, and if it points to a
locale that is not under the test's LOCPATH the setlocale call fails
with ENOENT.

Checked on x86_64-linux-gnu.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
2026-05-25 13:51:20 +00:00
Adhemerval Zanella 8eeed69baf nptl: Skip tst-pthread-gdb-attach{, -static} on env mismatches
The test previously failed with confusing diagnostics in two situations
that are properties of the runtime environment rather than of glibc:

1. find_gdb only checked access(X_OK), which is true for directories
   too.  A 'gdb' directory ahead of /usr/bin in PATH (e.g. one holding
   gdb python helpers) was therefore returned as the gdb executable,
   and the subsequent execl failed with errno != ENOENT, causing the
   test to fail with `numeric comparison failure ... status 256'.

2. The in-tree libthread_db.so.1 is built with -z mark-plt and
   therefore carries a versioned dependency on
   GLIBC_ABI_DT_X86_64_PLT in libc.so (see BZ #33212).  When the
   system gdb is linked against a libc older than 2.41, that version
   is not provided and gdb's dlopen of the in-tree libthread_db.so.1
   fails.  Thread debugging is then disabled, the gdb script's
   `thread 1' / `thread 2' commands fail, gdb exits non-zero, and the
   test reports a generic status mismatch.

Two changes:

* Require S_ISREG in find_gdb so a directory named 'gdb' on PATH is
  skipped, falling through to the next candidate.

* Before running the real gdb scenario, run a minimal probe script
  that triggers libthread_db loading (set debug libthread-db 1; set
  libthread-db-search-path; file /proc/self/exe; start).  If the
  probe output contains `dlopen failed', mark the test UNSUPPORTED
  with a clear message instead of letting the real run fail.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
2026-05-25 13:51:08 +00:00
Matt Turner c056561d31 alpha: fix setrlimit compat symbol for negative rlim values besides -1
Old alpha glibc defined rlim_t as signed long, making RLIM_INFINITY
equal to LONG_MAX (0x7ffffffffffffffful).  The compat symbol
__old_setrlimit64 (setrlimit@GLIBC_2.0 and setrlimit64@GLIBC_2.1) was
introduced in 0d0bc784ca [BZ #22648] to translate this old RLIM_INFINITY
to the kernel's RLIM64_INFINITY (ULONG_MAX) before the prlimit64
syscall, using an exact equality check.

Because old rlim_t was signed, any value a caller treats as negative
(e.g. -2 = 0xfffffffffffffffe unsigned) is also an "infinity or beyond"
value in the old ABI.  Such values are >= OLD_RLIM64_INFINITY and
should be translated to RLIM64_INFINITY; passing them through unchanged
causes prlimit64 to treat them as large finite limits, resulting in
unexpected failures (EPERM or silent truncation).

Change the equality check to >= OLD_RLIM64_INFINITY in
__old_setrlimit64 so that all values the old signed-rlim_t ABI would
interpret as infinity-or-more are correctly mapped to RLIM64_INFINITY.

No change is made to __old_getrlimit64: prlimit64 returns only exact
RLIM64_INFINITY for unlimited resources, so the existing equality
check against RLIM64_INFINITY is correct and mirrors the kernel's own
rlim64_is_infinity() logic.

Fixes: 0d0bc784ca ("Alpha: Add wrappers to get/setrlimit64 to fix
RLIM64_INFINITY constant [BZ #22648]")
Fixes: https://sourceware.org/bugzilla/show_bug.cgi?id=30992
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-05-25 13:51:00 +00:00
Matt Turner b0f985a21e sh: reload r3 after arg evaluation in INTERNAL_SYSCALL [BZ #34167]
r3 is caller-saved. When a function call appears in the args list
(e.g. INTERNAL_SYSCALL_CALL(tgkill, __getpid(), tid, sig)),
SUBSTITUTE_ARGS evaluates __getpid() before r3 is reloaded, leaving
r3=20 (__NR_getpid) instead of __NR_tgkill=270. The trapa then
dispatches the wrong syscall and the signal is silently dropped.

Fix: declare r3 uninitialised, expand SUBSTITUTE_ARGS (all function
calls happen here), then assign the syscall number to r3 with no
intervening calls before the trapa. Applies to both INTERNAL_SYSCALL
and INTERNAL_SYSCALL_NCS.

signal/tst-raise is a regression test for this bug: raise() calls
tgkill(__getpid(), tid, sig), which triggers the clobber.
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-25 13:50:54 +00:00
Adhemerval Zanella b75ad99d45 elf: Initialize TCB and stack-protector before static IFUNC resolvers (BZ 20680, BZ 27582, BZ 28817)
In static linking the IFUNC IPLT (apply_irel for non-PIE, the IRELATIVE
phase inside _dl_relocate_static_pie for static-pie) ran before
__libc_setup_tls and before _dl_setup_stack_chk_guard.  When a resolver
is compiled with -fstack-protector(-all) its prologue loads the canary
from the TCB (TCB-canary ABIs: x86_64, i386, powerpc, s390) or from
__stack_chk_guard (global-var ABIs).  On the former the resolver
crashed reading an unmapped TCB; on the latter it loaded a zero canary
(no crash, but the check is ineffective).  The same applies to a
resolver that reads any thread-local: it crashes on TCB-canary ABIs and
observes a zero-filled slot on the others (BZ 20680).  The pointer
guard has the same problem (e.g. resolvers that register an atexit
handler).

Reorder csu/libc-start.c so that ARCH_SETUP_TLS, the stack-protector
canary and the pointer guard are set up before any IFUNC resolver
runs.  For static-pie this requires splitting the existing
_dl_relocate_static_pie into two phases so the TCB/canary setup can be
interleaved between the non-IRELATIVE and IRELATIVE passes.

The historical ARCH_SETUP_IREL / ARCH_APPLY_IREL split (introduced for
powerpc so its IFUNC resolvers could read TCB fields like hwcap and
at_platform) is no longer required: TLS is now set up before either
macro runs.  ARCH_APPLY_IREL is removed, ARCH_SETUP_IREL does the work
uniformly on every arch, and the powerpc-specific libc-start.h becomes
redundant.

__libc_setup_tls reaches memcpy / mempcpy via _dl_allocate_tls_init in
elf/dl-tls.c, so it requires update ABI specific dl-symbol-redir-ifunc.h
with memcpy/memmove.

Tests added (each fails pre-fix on TCB-canary ABIs with SIGSEGV; the
static-protector variants additionally fail on global-var ABIs with a
"resolver_canary != main_canary" diagnostic):

  elf/tst-ifunc-bz28817                            static-pie + TLS in
                                                   resolver (BZ 28817)
  elf/tst-ifunc-resolver-protector                 dynamic
  elf/tst-ifunc-resolver-protector-static          static-pie
  elf/tst-ifunc-resolver-protector-static-non-pie  non-PIE static

Checked on aarch64-linux-gnu, arm-linux-gnueabihf, x86_64-linux-gnu,
and i686-linux-gnu

I also ran the ELF tests on qemu system for loongarch64-linux-gnuf64,
powerpc-linux-gnu, powerpc-linux-gnu-power4, powerpc-linux-gnu-soft,
powerpc64-linux-gnu, powerpc64le-linux-gnu, riscv64-linux-gnu, and
s390x-linux-gnu.
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-25 12:49:21 +00:00
Adhemerval Zanella af34b1376a elf: Initialize static TLS before relocation processing (BZ 34164)
An IFUNC resolver firing during dynamic linker relocation reads its
DSO's __thread storage from a zero-filled slot: init_tls() allocates
the static TLS block zero-filled, but .tdata is not copied in until
the trailing _dl_allocate_tls_init at the end of dl_main, long after
the per-object phase 2 resolvers from commit 63b31c05a8 have run.
A resolver that *writes* TLS is even worse off -- the write is
clobbered by that same trailing copy.

dl_main (elf/rtld.c): populate the DTV slotinfo, bump
dl_tls_generation, and call _dl_allocate_tls_init right after
init_tls(), before the relocation loop.

_dl_try_allocate_static_tls (elf/dl-reloc.c): drop the
"defer-if-not-relocated" branch and always run _dl_init_static_tls
inline, so a CHECK_STATIC_TLS allocation triggered mid-relocation
initialises the slot before the same object's phase 2 fires.

The new tests cheks some scenarios:

  elf/tst-ifunc-tls-init         resolver reads its DSO's IE TLS.
  elf/tst-ifunc-tls-init-dlopen  same, via dlopen.
  elf/tst-ifunc-tls-write        resolver write to TLS must survive
                                 to main.

Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-25 12:49:14 +00:00
Adhemerval Zanella e563a57105 x86: Revert "x86: Lower non-temporal copy threshold for Hygon"
This version was superseeded by 213ffdfbba.
2026-05-22 10:00:05 -03:00
Wilco Dijkstra 746a560a62 string: Improve test-memchr page cross checks
The tests for memchr attempt to check for accidental overreads that cross a
page.  However they weren't done at the end of a page and don't check for the
case where we match right at the end.  Add buf1_size/buf2_size to make finding
end of buffer easier.

Reviewed-by: Yury Khrustalev <yury.khrustalev@arm.com>
2026-05-21 16:13:07 +00:00
Thomas Daubney a7b6e53462 Vectorise special cases for SVE log1p(f)
This patch adds vectorised special cases for the SVE log functions
log1p and log1pf.

When built with GCC-15 and executed on a Neoverse V2 platform, the
following benchmarking throughput uplifts were measured:

log1pf -> 285% speed-up (4.85 ns/element to 1.26 ns/element)
log1p  -> 117% speed-up (8.25 ns/element to 3.80 ns/element)

Note that the numbers here are for the special case path only and that
the fast path performance has been maintained. These changes have also
maintained the same level of accuracy as before.
2026-05-21 10:05:52 -03:00
Rocket Ma 8ed4c70915 stdio-common: Optimize scanf %ms series array expansion
* stdio-common/vfscanf-internal.c: If user explicitly set the maximum
size of the string, respect it when reading characters. Instead of
always expanding exponentially, try to expand array to the exact size
user requested when `user_size < current_size * 2`.

Signed-off-by: Rocket Ma <marocketbd@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-21 09:08:06 -03:00
Thomas Daubney f7eea1b258 Vectorise special cases for SVE inverse hyperbolics
This patch adds vectorised special cases for the SVE inverse hyperbolic
functions atanh, acosh and asinh for single precision floats. It also
moves the commonly used inf and nan bit values into the sv_log1pf_inline
data struct for resuse.

When built with GCC-15 and executed on a Neoverse V2 platform, the
following benchmarking throughput uplifts were measured:

atanh -> 215% speed-up (5.51 ns/element to 1.75 ns/element)
acosh -> 152% speed-up (4.63 ns/element to 1.84 ns/element)
asinh ->  51% speed-up (5.00 ns/element to 3.31 ns/element)

Note that the numbers here are for the special case path only and that
the fast path performance has been maintained.
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-21 09:07:48 -03:00
zombie12138 54abc8566f x86: Fix non-temporal memset unreachable on AMD Zen 3/4/5
On AMD Zen 3/4/5 with ERMS, the non-temporal memset path is unreachable
because rep_stosb_threshold is set to SIZE_MAX (vectorized loop is faster
than ERMS on these CPUs), but the non-temporal code path is nested inside
the rep_stosb branch.

The existing rescue logic at the Avoid_STOSB check only covers the case
where the CPU lacks ERMS hardware support.  It does not cover AMD Zen 3+
where ERMS is supported but deliberately unused for performance reasons.

Extend the condition to also lower rep_stosb_threshold when:
- The user has not explicitly set x86_rep_stosb_threshold (respect tunables)
- rep_stosb_threshold is higher than memset_non_temporal_threshold (NT gated)

This makes the non-temporal path reachable for large memset operations,
providing ~2x speedup on pre-faulted buffers larger than L3 cache.

Tested on AMD Ryzen 7 8745HS (Zen 4):
- Pre-faulted 64MB memset: 2.02 ms -> 0.94 ms (2.15x faster)
- First-touch 64MB memset: 19.3 ms -> 21.3 ms (11% regression, expected:
  kernel clear_page cache warming bypassed by NT stores)

	* sysdeps/x86/dl-cacheinfo.h (dl_init_cacheinfo): Extend
	rep_stosb_threshold lowering condition to cover AMD Zen 3/4/5
	where ERMS is supported but stosb is disabled via threshold.

Signed-off-by: zombie12138 <zombie12139@gmail.com>
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34129
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-20 17:22:20 -03:00
Xiang Gao 97bd3b3d56 libio: Ignore doallocate for open_memstream and open_wmemstream [BZ #34019]
setvbuf (stream, NULL, _IOFBF, 0) takes a special path in
_IO_setvbuf: if the byte-oriented buffer base is NULL, it calls
_IO_DOALLOCATE and returns without invoking the stream setbuf hook.

For open_wmemstream, the byte-oriented buffer base is NULL although
the wide result buffer has already been initialized in _wide_data.
As a result, this path calls _IO_wdefault_doallocate, which may
replace the wide buffer managed by open_wmemstream.

Install an open_wmemstream-specific doallocate hook that leaves
the growable result buffer unchanged. Add a regression test for this
path.

Install a narrow memstream doallocate hook as well. It keeps both
memstream vtables consistent (generic stdio allocation must not
replace the growable result buffer).

Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-20 17:22:19 -03:00
Xiang Gao b1ab27286e libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019]
open_memstream and open_wmemstream manage an internal growable buffer.
The default setbuf hook can reset that buffer, breaking the assumptions
used by the string stream overflow paths.

Install setbuf hooks that leave the internal buffer unchanged, and add
regression test cases for the narrow and wide cases, based on the
reproducer in BZ #34019.

Checked on x86_64 with no regression in the libio tests.

Reported-by: Rocket Ma <marocketbd@gmail.com>
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-20 17:22:15 -03:00
xiejiamei 213ffdfbba x86: Lower non-temporal copy threshold for Hygon
Benchmarks on Hygon processors show that the default non-temporal
threshold is higher than ideal for large copy workloads.  As a result,
memcpy and memmove may continue to use the temporal copy path for
longer than is beneficial, increasing cache pollution and reducing
throughput for large copies.

Lower the copy non-temporal threshold to 3/8 of the shared cache size
per thread on Hygon.  This allows the non-temporal copy path to be
selected earlier while leaving the memset non-temporal threshold
unchanged.

Signed-off-by: xiejiamei <xiejiamei@hygon.cn>
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-20 17:22:12 -03:00
Daan De Meyer 994a437878 elf: Don't crash in dlsym when tail-called from a constructor [BZ #34156]
If a shared library's constructor calls dlsym and discards the result,
the compiler is free to lower the call to a tail jump.  The dynamic
linker then resolves the apparent caller to ld.so's own link map, which
has no l_scope, and crashes in _dl_lookup_symbol_x dereferencing the
NULL scope pointer.

Tail-call optimization is a legal C transformation and there is no way
for the dynamic linker to recover the real caller from the elided frame.
Detect the situation by its observable effect -- a link map with no
l_scope -- and fall back to the main program's link map, the same
treatment used when the caller's address is otherwise unrecognized.

The check is written against l->l_scope rather than against _dl_rtld_map
directly because dl-sym-post.h is also compiled into libc.so, where
_dl_rtld_map is not visible (it lives only in ld.so).

Add dlfcn/tst-dlsym-ctor exercising the pattern.  Without the fix the
test SIGSEGVs during dlopen; with the fix dlopen returns cleanly.

Signed-off-by: Daan De Meyer <daan@amutable.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-20 17:22:07 -03:00
Avinal Kumar 566135bb77 SHARED-FILES: Update gettext sync record
Update the gettext section to reflect the 2026 sync with GNU
gettext 1.0 (through commit 2ebbdd0e2).  Add intl/eval-plural.h
which was missing from the shared files list.

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-20 17:22:06 -03:00
Avinal Kumar 8aa4a70431 intl: Fix undefined pointer behaviour
In _nl_find_msg (dcigettext.c), outbuf was computed as
freemem + sizeof(size_t) before checking whether freemem_size is
large enough.  When freemem is NULL (initial state), this is
undefined behaviour i.e arithmetic on a null pointer.  Move the
outbuf assignment after the size check where freemem is guaranteed
to be a valid allocation.

In read_alias_file (localealias.c), after realloc the old
string_space pointer is dangling.  The expression
new_pool - string_space subtracts a valid pointer from a dangling
one, which is undefined behaviour per ISO C 23.
Rewrite as new_pool + (map[i].alias - string_space) so both
operands of the subtraction point into the same (old) object
before string_space is reassigned.

Based on GNU gettext commits 695429040 and 2ebbdd0e2.
Original author: Bruno Haible <bruno@clisp.org>

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-20 17:22:03 -03:00
Adhemerval Zanella 209ad6ff9d Fix hurd bootstrap after 4c6f92daea
Hurd bootstrap with build-many-glibc.py fails for i686-gnu since
4c6f92daea:

In file included from zic.c:16:
private.h:849:1: error: static declaration of ‘mempcpy’ follows non-static declaration
  849 | mempcpy(void *restrict s1, void const *restrict s2, size_t n)
      | ^~~~~~~
In file included from ../include/string.h:60,
                 from private.h:222:
../string/string.h:432:14: note: previous declaration of ‘mempcpy’ with type [...]
  432 | extern void *mempcpy (void *__restrict __dest,
      |              ^~~~~~~

The libc-symbols.h already defined some HAVE_*, but timezone files are
built with -D_ISOMAC.  Remove its usage and only define _ and N_
macros if not already defined.

Checked on x86_64-linux-gnu and with a build-many-glibcs.py build for
i686-gnu.
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Paul Eggert <eggert@cs.ucla.edu>
2026-05-20 14:55:00 -03:00
Adhemerval Zanella 999fa30b36 elf: Use dl_scratch_buffer for LD_LIBRARY_PATH copy in _dl_init_paths
_dl_init_paths used strdupa to make a mutable copy of LD_LIBRARY_PATH
for fillin_rpath to tokenize.  The env block is attacker-controllable
and Linux allows individual variables up to MAX_ARG_STRLEN (32 *
PAGE_SIZE = 128 KB), so the strdupa can push tens of KB onto the
loader's startup stack on top of the env block that already sits on
the initial stack.  With a reduced RLIMIT_STACK the doubled copy
overflows before main () is reached.

Replace the strdupa with a dl_scratch_buffer: short paths stay in
the 256-byte inline area, longer ones spill to anonymous mmap (malloc
is not yet available during _dl_init_paths).  Two follow-on changes
make the new scratch lifetime safe against _dl_signal_error:

  * Count entries directly off the const LD_LIBRARY_PATH and allocate
    __rtld_env_path_list.dirs *before* the scratch is live.  That way
    the larger of the two heap allocations the loader controls signals
    its OOM with no scratch to leak.

  * Convert fillin_rpath to return bool instead of calling
    _dl_signal_error internally on per-entry malloc failure.  Its
    only caller in the LLP path now frees the scratch first and then
    signals the error from a clean state.  decompose_rpath, the other
    caller, is updated symmetrically.  This also fixes a pre-existing
    leak in fillin_rpath's OOM path, where the to_free heap copy from
    expand_dynamic_string_token was not released before the
    _dl_signal_error.

Checked on x86_64-linux-gnu, aarch64-linux-gnu, and i686-linux-gnu.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-20 14:50:59 -03:00
Adhemerval Zanella 3215ea5c45 elf: Use dl_scratch_buffer for DST expansion in _dl_map_object_deps
The expand_dst macro in _dl_map_object_deps performs an unbounded
alloca via DL_DST_REQUIRED, which scales with the link map's
l_origin length plus the count of dynamic-string tokens in the
input string.  When a DT_NEEDED entry carries several DSTs and the
link map sits in a deep directory, the resulting allocation grows
to several kilobytes -- enough to overflow a PTHREAD_STACK_MIN
thread that calls dlopen.

Convert the macro to a static function that draws from a caller-
owned dl_scratch_buffer, so oversized expansions land on the heap
(or anonymous mmap during early startup) instead of the stack.
The scratch buffer is reused across DT_NEEDED, DT_AUXILIARY, and
DT_FILTER entries of the same map and freed once dependency
expansion completes.

A new regression test, tst-dst-needed-minstack, builds a wrapper
library that inherits a five-DST SONAME from a leaf module,
deploys it under a deep temporary directory, and dlopens it from
a PTHREAD_STACK_MIN thread.  Without the fix the dlopen overflows
the thread stack and crashes; with the fix the dlopen returns
cleanly (with or without a successful load).

Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-20 11:49:17 -03:00
Adhemerval Zanella 3e3b621910 elf: Replace alloca with dl_scratch_buffer in _dl_load_cache_lookup
The alloca added by commit ccdb048d ("Fix recursive dlopen") to
snapshot the matched cache entry before __strdup runs through
interposable malloc is sized by best_len, which can reach PATH_MAX.
On PTHREAD_STACK_MIN threads that's enough to overflow the stack
mid-dlopen.

Use dl_scratch_buffer with DL_SCRATCH_NO_MALLOC: short entries stay
in the 256-byte inline area, longer ones spill to anonymous mmap
rather than to interposable malloc.  The recursive-dlopen invariant
is preserved.

New container test elf/tst-dl-cache-long-path constructs a ~3.4 KB
deep directory, populates ld.so.cache with that entry, and dlopens
from a PTHREAD_STACK_MIN thread under deliberate stack pressure;
reliably SIGSEGVs against the alloca-based code and passes with the
fix.

Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-20 11:49:12 -03:00
Adhemerval Zanella 26ed1cb8ca elf: Replace alloca/VLA with dl_scratch_buffer in dl-load.c
is_trusted_path_normalize, print_search_path, and open_path used
alloca or a VLA to hold a path scratch buffer sized by user-controlled
inputs (an RPATH directory length, or
max_dirnamelen + max_capstrlen + namelen).  On the worst case that
consumes up to PATH_MAX bytes of stack per call, which can overflow a
PTHREAD_STACK_MIN-sized stack mid-dlopen when combined with the
loader's other on-stack scratch (struct filebuf, etc.).

Replace those allocations with dl_scratch_buffer.  As a small cleanup,
print_search_path now takes the scratch buffer from its caller
(open_path's buffer is already large enough --
max_dirnamelen + max_capstrlen + namelen with namelen >= 1 covers the
max_dirnamelen + max_capstrlen + 1 print_search_path requires), so
LD_DEBUG=libs no longer pays for an extra allocation per open_path
invocation.

A new test elf/tst-dl-path-buf exercises the relevant paths -- dlopen
via DT_RPATH, open_path failure cleanup, dlopen with an over-long
name, dlopen from a PTHREAD_STACK_MIN thread.

Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-20 11:49:11 -03:00
Adhemerval Zanella f86b51dd6a support: Add use_stack_min option to support_small_thread_stack_size
It allows it to return PTHREAD_STACK_MIN if defined.

Checked on x86_64-linux-gnu and with a build for i686-gnu.

Suggested-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-20 11:49:09 -03:00
Adhemerval Zanella 4e3489bd2a elf: Add dl_scratch_buffer, a loader-side scratch buffer
Several loader code paths need a short-lived scratch buffer sized
by attacker-influenced inputs (RPATH entries, ld.so.cache strings,
etc.).  The available primitives are all unsuitable:

  - alloca is unbounded and can overflow PTHREAD_STACK_MIN stacks.

  - <scratch_buffer.h> is unaware of __minimal_malloc: a malloc'd
    spill freed during early loader startup silently leaks because
    __minimal_free only releases the most-recent allocation.

  - A few paths cannot route through the interposable malloc at
    all -- ld.so.cache lookup in particular, because an interposed
    user malloc may recursively call dlopen and __munmap the cache
    mapping mid-copy (commit ccdb048d, "Fix recursive dlopen").

Add a loader-side analogue of <scratch_buffer.h>: a 256-byte inline
area for the common case, with spill to malloc by default or to
anonymous mmap when __minimal_malloc is active or the caller passes
DL_SCRATCH_NO_MALLOC.  Mmap spills are tagged " glibc: loader
scratch" via __set_vma_name for /proc/self/maps visibility.  On OOM
dl_scratch_buffer_allocate raises a loader error via _dl_signal_error
and does not return.  The one-shot contract (no second allocate
without an intervening free) is enforced by an assertion in
_dl_scratch_buffer_allocate.

No functional change in this commit; consumers are added separately.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-20 11:49:08 -03:00
Yury Khrustalev 1233e256f2 malloc: Small fix for code readability
A couple of small fixes for code readability, no functional change.

 - Add missing comments for #endif statements.
 - Move inclusion of string.h from malloc.c to calloc-clear-memory.h
   where it is actually used.
 - Re-order alias definitions for malloc functions.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-05-20 10:38:59 +01:00
Stefan Liebler aadbf5a10a s390: Adjust configure check for static-pie support.
With the previous approach, the configure check fails for lld in version >=19.
While binutils and lld 18 is placing the R_390_IRELATIVE relocation in .rela.plt
and emits DT_JMPREL pointing to it, newer lld versions puts the R_390_IRELATIVE
relocation in .rela.dyn and therefore there is also no DT_JMPREL entry and the
configure check claims that lld does not support static-pie.

The R_390_IRELATIVE relocation is also processed fine in .rela.dyn, thus the
configure check is adjusted.  Now the configure checks that it exists a
R_390_IRELATIVE relocation at all. If the R_390_IRELATIVE relocation lands in
.rela.plt, it ensures that there is DT_JMPREL pointing to it.  Otherwise there
should be a .rela.dyn section.
2026-05-20 10:18:14 +02:00
Andreas Schwab aa56ccb98b nss_files: use booleans in parser macro calls
The swallow argument in the INT_FIELD and STRING_FIELD macros is used as a
boolean, change all callers to use false and true instead of 0 and 1.
2026-05-19 15:20:20 +02:00
Andreas Schwab 66efdda2f8 nss_files: fix swapped arguments in service parser
The port number in the service file is a decimal number followed by a
single slash.
2026-05-19 15:20:20 +02:00
Paul Eggert dee4e2446f Regenerate 'configure'
* configure: Regenerate from configure.ac,
which was updated in the recent commit
'Simplify tzdb-related configuration'.
2026-05-18 14:43:09 -07:00
Weihong Ye 3237d63b84 AArch64: Optimize memcmp for Kunpeng 950 with SVE
Key optimizations:
- Use SVE predication for branch-free handling of short inputs and tails
- Use 4-way loop unrolling to maximize pipeline utilization
- Optimize mismatch detection with early exit logic

Benchmark (bench-memcmp, generic -> this patch):
- Small (0-128B): 15% - 50% speedup
- Medium (129-1024B): 21% - 50% speedup
- Large (2048-4096B): 28% - 50% speedup

Note: regressions may be observed in edge cases where offsets
are near 4K boundaries. These instances are rare and the overall
performance gain remains significantly positive.

Also add IFUNC support for memcmp and correct the first-line
comment in memcpy_kunpeng950.S.
2026-05-18 17:32:54 +00:00
Paul Eggert d420b69b81 Simplify tzdb-related configuration
tzdb 2026b no longer needs -Wno-discarded-qualifiers or
-Wno-unused-variable.  From a suggestion by Joseph Myers in:
https://sourceware.org/pipermail/libc-alpha/2026-May/177312.html
* configure.ac (config-cflags-wno-discarded-qualifiers): Remove.
* timezone/Makefile (CFLAGS-zic.c): Remove -Wno-unused-variable,
$(config-cflags-wno-discarded-qualifiers).

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-18 13:31:00 -03:00
Paul Eggert 4c6f92daea timezone: sync to tzdb 2026b
Sync tzselect, zdump, zic to tzdb 2026b.

This fixes some buffer and integer overflows in zic,
adds new zic options -D, -m and -u inspired by FreeBSD,
and raises zic’s maximum number of abbreviation bytes
per timezone from 50 to 256.

This patch incorporates the following tzdb source code changes:

f9d30685 Output a minimal time zone designation table
37a4d178 Fix zic overflow bug with too-large offsets
4392f2dc zic now checks for signals more often
99a08a66 Fix zic buffer overflow when computing TZ
d63b9287 zic: keep needed last transition to new type
d005045d Pacify clang -Wunterminated-string-initialization
e67b08d3 Port to C23 strchr macro
3d4b4e46 Add zic.c overflow commentary
d9101b88 zic now a bit safer for overflows near 2**63
b23fa8e0 zic now allows more than 50 leap seconds
4ff518d2 Increase TZ_MAX_CHARS from 50 to 256
75d3b73b New -DTZ_RUNTIME_LEAPS=0 build-time option
87343c6e TZ_MAX_TIMES must be at least 310 now
fc8f1b68 Simplify int_fast32_t definition on C89 platforms
24581465 Remove TZDEFRULES ("posixrules") from localtime.c
fc708427 zic now warns about -p
b09a3f23 Port TWOS_COMPLEMENT to signed-magnitude hosts
56b7a24a Make sure 2**31 - 1 is signed
9068ab78 zic no longer generates utoff == -2**31
cb6f9b3b Omit unnecessary L suffixes
c37fbc32 Clarify when ‘__attribute__((pure))’ is a hack
859690a7 Fix some unsequenced/reproducible commentary
9c772ca7 Port to POSIX.1-2001 fflush
10f93018 Omit no-op transitions when Rule+Zone cancel
a0b09b52 Fix unlikely backslash bug in scripts
2cbd3a71 Allow builder to override GRANDPARENTED
c7257626 not used at → used outside
faed4bd3 Clarify <sys/auxv.h> vs getauxval
df08e6a1 Port mode_t (and gid_t, uid_t) to MS-Windows
6127d375 New zic option -u, inspired by FreeBSD
813c9ee0 New zic option -m, inspired by FreeBSD
987ea89c New zic option -D, inspired by FreeBSD
cc377b07 Simplify mkdir situation
cd994a90 Simplify !HAVE_POSIX_DECLS situation
052ddf76 Minor gettext macro improvements
d9018f1c Refactor duplicate duplicate-option code
8d65db97 Prefer fdopen to umask in zic
d7edca6e Omit “'”s from zic usage message
a09ba7a5 getopt returns -1 (not EOF) on failure
e22d410c zic now uses is_digit
f57cadda Always invoke umask at start
242a8338 Fix mode_t issues on MS-Windows
2fecd606 MKDIR_UMASK → MKDIR_PERMS refactoring
90ef088a Move static_assert to top level
41576478 Port better to platforms lacking mempcpy
90a08d3e * private.h: Include stddef.h early enough
aa8b35fe Simplify port to NetBSD struct __state
cd2fddf7 Port to -DHAVE_SYS_STAT_H=0 -DHAVE_POSIX_DECLS=0
8470e759 Pacify GCC 15 -Wunterminated-string-initialization
8817d42f Prefer mempcpy to doing it by hand
87abb113 Tighten security checks on TZ values
c87f0918 Use strnlen
07f7f31a Fix preprocessor indenting
3adf4123 Add offtime_r à la FreeBSD and NetBSD
b807a31e Don’t depend on ‘true’ for tzselect
ddffc800 * zic.c: Fix misspelled comment (thanks to Jonathan Wakely).
7063d08c Fix bug with -d RELATIVE -t ABSOLUTE
e8920e76 Rename emalloc to xmalloc.
e8e1a3d2 NetBSD defines STD_INSPIRED functions
3411494c Define _CRT_DECLARE_NONSTDC_NAMES for MS-Windows
7c909166 Define NOMINMAX for MS-Windows
24a4d97f 'zdump -' now reads from stdin
e6d6bc3e Pacify gcc -Wsuggest-attribute=format sans snprintf in zdump
99557862 TZNAME_MAXIMUM defaults to 254, not 255
fe5be99d Be more consistent about macro true/false vs 1/0
31f483a1 Remove dependency of asctime on strftime
7ef7ed06 Simplify timeoff redefinition
1bd67a4b Move MKTIME_MIGHT_OVERFLOW definition
67f7e8ab Pacify GCC 15ish -Wzero-as-null-pointer-constant
535a4e8b Pacify GCC 15ish -Wleading-whitespace=blanks
0706ef0b Move iinntt definition
ea814e99 strftime %s no longer is limited to time_t range
41e5344e Fix bug near the year 2**31 - 1 - 1900
4e1de249 Pacify gcc -Wsuggest-attribute=const
ebd2ed92 Don’t define _FILE_OFFSET_BITS if _TIME_BITS
26a649a1 Improve zdump overflow checking
9c8221d7 * private.h: Fix timeoff comment.
9db906a0 Switch from RFC 8536 to 9636 for documentation
af54a9e8 Port better to glibc when used internally there

Checked on x86_64-linux-gnu.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-18 13:31:00 -03:00
xiejiamei 08e963f675 x86: Lower non-temporal copy threshold for Hygon
Benchmarks on Hygon processors show that the default non-temporal
threshold is higher than ideal for large copy workloads. As a result,
memcpy and memmove may continue to use the temporal copy path for
longer than is beneficial, increasing cache pollution and reducing
throughput for large copies.

Lower the copy non-temporal threshold to 3/8 of the shared cache size
per thread on Hygon. This allows the non-temporal copy path to be
selected earlier while leaving the memset non-temporal threshold
unchanged.

Signed-off-by: xiejiamei <xiejiamei@hygon.cn>
2026-05-18 13:31:00 -03:00
Rocket Ma 64efa1451b libio: Fix fmemopen_write on appending mode (BZ 34006)
* libio/fmemopen.c: Reference pos the variable instead of c->pos.
On the edge case, one byte should be written at the end of buffer,
instead of returning error.

Signed-off-by: Rocket Ma <marocketbd@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-18 13:31:00 -03:00
Adhemerval Zanella 63b31c05a8 elf: Defer all IRELATIVE relocations until after PLT setup
When a shared library is built with -z lazy and its IFUNC resolver calls
a PLT function, the dynamic linker can crash.  The resolver runs while
the PLT stubs still hold their raw ELF virtual addresses — l_addr has
not yet been added — so the call branches to an unmapped address.

The old code deferred IRELATIVE entries only to the end of the relocation
range currently being processed (via the r2/end2 scan-ahead mechanism in
elf_dynamic_do_Rel).  This was sufficient only when both IRELATIVE and the
JMP_SLOT entries for the PLT functions it needs are in the same section.
On x86-64, aarch64, arm, i386 and most other targets, a file-scope
initialiser of the form

  int (*fptr)(void) = some_ifunc;

causes the linker to place R_*_IRELATIVE in .rela.dyn, while JMP_SLOT
entries for any PLT calls made by the resolver live in .rela.plt.
Processing .rela.dyn before .rela.plt means the resolver fires before the
PLT is usable, regardless of where within .rela.dyn IRELATIVE appears.

Fix this by splitting IRELATIVE processing into a separate, explicitly
deferred pass.  In elf/do-rel.h:

 - Remove the r2/end2 variables and the post-loop IRELATIVE re-scan from
   elf_dynamic_do_Rel.  IRELATIVE entries are now always skipped in the
   non-bootstrap path.

 - Add a new elf_dynamic_do_Rel_irelative function that scans a
   relocation range and calls elf_machine_rel/elf_machine_lazy_rel for
   IRELATIVE and ifunc relocations.

In elf/dynamic-link.h, update _ELF_DYNAMIC_DO_RELOC to use a two-phase
approach for non-bootstrap builds unconditionally (regardless of whether
ranges[1].size is zero):

 Phase 1+2: elf_dynamic_do_Rel over .rela.dyn then .rela.plt — processes
            everything except IRELATIVE/STT_GNU_IFUNC.
 Phase 3+4: elf_dynamic_do_Rel_irelative over .rela.dyn then .rela.plt —
            processes only IRELATIVE, by which point all PLT stubs are
            valid.

This guarantees that IRELATIVE resolvers can call PLT stubs safely
regardless of which section the linker placed R_*_IRELATIVE in.

Add ELF_MACHINE_IRELATIVE to the architectures that were missing it so
the new skip logic in elf_dynamic_do_Rel is compiled for all targets.

This patch addresses the binutils BZ 13302 [1] from the glibc side, and
also fixes the mold-reported issue [2], which shows that IFUNC relocation
placement and processing can work differently across ABIs.

I checked on all ABIs that support IFUNC (x86_64, i686, aarch64, arm,
loongarch, powerpc, riscv, s390, and sparc), some via qemu-system.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=13302
[2] https://github.com/rui314/mold/issues/1550

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-18 13:31:00 -03:00
Wilco Dijkstra 17a79a5120 malloc: Remove dynamic mmap/trim threshold [BZ #30769]
v2: Update documentation

Whenever a large mmap is released the mmap and trim thresholds are updated.
As a result these thresholds grow ever larger which means huge allocations
are always served by arenas rather than mmap.  The thresholds can end up as
large as an arena, which completely stops all trimming of the top block.
Remove the code completely - the default thresholds seem way too low for
modern 64-bit targets, but they can be increased seperately.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-18 13:31:00 -03:00
Avinal Kumar bb91c88af7 intl: Fix memory leak in _nl_find_domain on allocation failure
When _nl_explode_name() returns -1 (out of memory) and the locale was
resolved through an alias, _nl_find_domain() returns immediately
without freeing the locale copy allocated earlier.  Similarly,
when _nl_make_l10nflist() returns NULL, the 'goto out' skips the
alias_value free.

Fix by nesting the _nl_make_l10nflist() call and its result handling
inside 'if (mask != -1)' instead of returning early.  Move the
normalized_codeset free inside the same block.  Both failure paths
now fall through to the unconditional alias_value free at the end.

Imported from GNU gettext commit 10eafd9e5.
Original author: Bruno Haible <bruno@clisp.org>

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-18 13:31:00 -03:00
Avinal Kumar 5ce8201bf5 intl: Remove pre-C99 fallbacks from plural-exp.c
glibc requires C11 since 2022, making pre-C99 compatibility
paths in plural-exp.c dead code:

- init_germanic_plural(): With C99+, GERMANIC_PLURAL is
initialized at compile time and this function is never called.
Remove the function and the INIT_GERMANIC_PLURAL macro.

- HAVE_STRTOUL guard: Protected strtoul() usage with a manual
digit-parsing fallback.  strtoul is in C89 <stdlib.h> and glibc
provides it.  Remove the guard and the fallback loop.

Imported from GNU gettext commits ab5990532 and c1d84d656.
Original author: Bruno Haible <bruno@clisp.org>

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-18 13:31:00 -03:00
Avinal Kumar 9985b162d8 intl: Remove PRI_MACROS_BROKEN from loadmsgcat.c
PRI_MACROS_BROKEN was a workaround for AIX 4, where inttypes.h
did not properly define the PRI* format macros (PRId8, PRIu32, etc.).
glibc has never supported AIX, the macro was always hardcoded to 0
under _LIBC, making it a dead code.

GNU gettext removed this in commit 267f61670 ("Drop portability to
AIX 4"), since no supported system has broken PRI macros post-C99.

Based on GNU gettext commit 267f61670.
Original author: Bruno Haible <bruno@clisp.org>

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-18 13:31:00 -03:00
Avinal Kumar 6f837cdedd intl: Remove IN_LIBGLOCALE dead code
Remove all IN_LIBGLOCALE conditional blocks from intl/.  libglocale
was a proposed API from 2005 that was never completed or shipped.
The macro is never defined in glibc or in current GNU gettext, making
every #ifdef IN_LIBGLOCALE block dead code.

GNU gettext removed these in commits starting from 2023.  Removing
them from glibc reduces noise and eases future syncs with gettext.

Imported from GNU gettext commit d6a6801c1.
Original author: Bruno Haible <bruno@clisp.org>

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-18 13:30:59 -03:00
Muhammad Kamran d093102297 AArch64: simplify __libc_arm_za_disable failure path
The failure tail of __libc_arm_za_disable only leads to
__libc_fatal, so it does not need to preserve call frame state.

Remove the PAC prologue, frame setup, saved cntd value, stack
stores, and associated CFI directives from the fatal path, leaving
only the required SME state shutdown and fatal call.

Add tst-sme-za-disable-fail to exercise the abort path by providing
a TPIDR2 block with non-zero reserved bytes and checking that the
process terminates with SIGABRT and the expected fatal message.
2026-05-18 14:06:58 +00:00
Adhemerval Zanella d9981b5439 stdio-common: Silence clang -Wfortify-source warning in tst-vfscanf-bz34008
clang does not recognize the 'm' scanf specifier and incorrectly warns
that the buf argument may overflow.  Suppress the warning with the
clang-specific DIAG_* macros.
2026-05-15 11:08:31 -03:00
Adhemerval Zanella 1172d0e9cf elf: Batch program-header reads in _dl_map_segments (oversight fix)
The fix for BZ 26577 ("Fix stack overflow in _dl_map_object_from_fd
with large e_phnum") removed the alloca for the program-header table
and introduced a streaming iterator (dl_pt_load_iterator) so segments
could be walked without staging the entire table on the stack.

That patch batched reads correctly in _dl_map_object_scan_phdrs (the
first walk, which collects PT_DYNAMIC/PT_TLS/PT_GNU_* metadata), but
overlooked the second walk in _dl_map_segments:
_dl_pt_load_iterator_next issued one pread64 per program header to
find the next PT_LOAD entry.  For an object with N program headers
this added N redundant per-phdr syscalls on every dlopen / loader
startup -- regardless of whether the table had already been read by
open_verify into struct filebuf.

Unify both walks behind a single batched helper,
_dl_pt_load_iterator_phdr_at:

  - When the program header table fits in the bytes already read by
    open_verify into fbp->buf (the common case for nearly all shared
    objects), all phdr accesses are served from that buffer with no
    syscall at all.

  - Otherwise, up to FILEBUF_SIZE / sizeof(ElfW(Phdr)) program headers
    are read into fbp->buf with a single pread64; subsequent indices
    in the same window hit the buffer.

Both _dl_map_object_scan_phdrs and _dl_pt_load_iterator_next now go
through this helper, eliminating the separate batching logic in
_dl_map_object_scan_phdrs.  struct filebuf moves from dl-load.c to
dl-load.h so the inline iterator in dl-map-segments.h can reach
fbp->buf.

The filebuf size is also bumped to ensure the cached fast path
triggers for all observed binaries.  A survey of an Ubuntu 24.04
installation (scanning /usr) shows:

    Candidate files       : 465834
    ELF files inspected   : 11624
    glibc-linked binaries : 10164
    Minimum e_phnum       : 5
    Maximum e_phnum       : 14
    Average e_phnum       : 11.37
    Median  e_phnum       : 11.0

shows e_phnum capped at 14 (for instance gcc's cc1, lto1, perl,
and gdb).  The previous FILEBUF_SIZE of 832 on 64-bit fit only 13
program headers after the ELF header (64 + 13*56 = 792), so 64-bit
binaries with 14 phdrs missed the cached path.  FILEBUF_SIZE is
bumped from 512/832 to 640/1024 (32-bit / 64-bit) -- enough for at
least 16 program headers on either ABI, leaving headroom over the
observed maximum.

For a typical shared library where open_verify's initial read covers
the program header table, this reduces _dl_map_segments from N
preads to 0.  For a worst-case e_phnum that does not fit in fbp->buf,
reads drop from N to ceil(N / phdrs_per_buf) -- the same cost
_dl_map_object_scan_phdrs already pays.

No functional change.  Tested on x86_64-linux-gnu, aaarch64-linux-gnu,
and i686-linux-gnu.
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-15 10:58:46 -03:00
Adhemerval Zanella 3bd334037e elf: Fix 785a028ab7 for PTHREAD_STACK_MIN platforms
Due a missing ';'.
2026-05-14 19:02:10 -03:00
Adhemerval Zanella 785a028ab7 elf: Fix elf/tst-bz26577-minstack.c on hurd
Hurd does not defined PTHREAD_STACK_MIN, use support_small_thread_stack_size
instead.

Checked on a x86_64-gnu build.
2026-05-14 17:20:02 -03:00
Shamil Abdulaev 389ecf52bc libio: Fix race in _IO_new_file_init_internal initialization order [BZ #33785]
_IO_new_file_init_internal linked the new stream into _IO_list_all
before setting fp->_fileno to -1.  A concurrent thread that walks
_IO_list_all (for example via fflush (NULL)) could observe the stream
with an uninitialized _fileno before initialization completed.

Set _fileno = -1 before _IO_link_in so the stream is fully
initialized when it becomes visible in the global list.

This is the residual concurrency defect noted at the end of commit
b657f72fa3 ("libio: Fix deadlock between freopen, fflush (NULL) and
fclose (bug 24963)").

Add libio/tst-file-init-race exercising concurrent fopen/fclose and
fflush (NULL) to detect regressions.

Signed-off-by: Shamil Abdulaev <ashamil435@gmail.com>
Reviewed-by: Florian Weimer <fweimer@redhat.com>
2026-05-13 07:52:40 +02:00
Frédéric Bérat 1cc165baed test: Add gconv refcount leak test for swscanf
Add a new internal test, `tst-wcsmbs-clone-overflow`, to verify correct
gconv module reference counting. The Makefile is updated to include this
test in the `tests-internal` list and ensure it runs with generated locales.

This test specifically checks that the `__counter` for `gconv_fcts->towc`
does not leak references when `swscanf` is used with a stack-allocated
wide character stream. It ensures that `_IO_wstrfile_fclose_stack`
properly decrements the module reference counter, preventing a module
from staying loaded indefinitely due to unreleased references.

Assisted-by: LLM
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-12 11:09:46 +02:00
Frédéric Bérat 0981c03c27 libio: Fix gconv module reference counter overflow in swscanf
The swscanf family of functions creates a wide-oriented FILE stream
on the stack. Initialization of this stream invokes `_IO_fwide`, which
clones the global locale's gconv transformation steps via
`__wcsmbs_clone_conv`. This increments the reference counter (`__counter`)
of the gconv module.

Because the FILE stream is stack-allocated, `fclose` cannot be called,
and so `__gconv_release_step` is never invoked. The counter leaks,
eventually hitting the 32-bit integer overflow limit and aborting the
process.

To resolve this, we introduce `_IO_wstrfile_fclose_stack`, a dedicated
cleanup function for stack-allocated FILE streams. This function invokes
`_IO_FINISH` and correctly releases the gconv steps via
`__gconv_release_step` without attempting to `free` the FILE pointer.
This cleanup function is then hooked into all variants of swscanf right
before they return.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-12 11:09:34 +02:00
Adhemerval Zanella 18ff9235eb elf: Eliminate alloca for program-header table in the ELF loader
The ELF loader allocates the program-header table on the stack with
alloca(e_phnum * sizeof(ElfW(Phdr))) in two places: once in
open_verify to call elf_machine_reject_phdr_p, and again in
_dl_map_object_from_fd to scan segment types.  Both fall back to
alloca only when the table does not fit in the initial fbp->buf read;
for a crafted ELF with e_phnum == 0x7FFF this means up to ~1.8 MB
(32767 × 56 bytes on a 64-bit host) on the stack in each call, with
no guard against the combination exhausting the available stack space.

A latent variant of this problem exists even for ordinary shared
libraries when dlopen is called from a thread running with
PTHREAD_STACK_MIN stack (16 KB on Linux).  The nptl/tst-minstack-exit
test demonstrates that glibc code paths must operate correctly under
minimum-stack conditions; loading a shared library with even a modest
number of program headers can overflow the remaining stack through the
alloca-based phdr table.

This patch eliminates both allocas by replacing them with a single
_dl_map_object_scan_phdrs function that reads program headers in
fixed-size chunks into the existing fbp->buf scratch buffer (512 B on
32-bit, 832 B on 64-bit) using pread.  When all headers fit within
the bytes already captured by open_verify's initial read() call (the
common case), no extra syscall is needed.  This should be the case for
most of the ELF objects and should not required additional syscalls.

The slow path issues as many pread calls as necessary without any stack
growth proportional to e_phnum.  The elf_machine_reject_phdr_p interface
is redesigned around a new struct dl_machine_phdr_info and on MIPS this
captures the PT_MIPS_ABIFLAGS entry in-flight, so the compatibility check
in elf_machine_reject_phdr_p no longer needs to re-scan the program-header
table.

Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.

NB: this patch depends on https://sourceware.org/pipermail/libc-alpha/2026-May/177239.html
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-11 15:49:41 -03:00
Adhemerval Zanella fdd1684c85 elf: Fix stack overflow in _dl_map_object_from_fd with large e_phnum (BZ 26577)
The _dl_map_object_from_fd uses a VLA (loadcmds[l->l_phnum]) whose size
is proportional to e_phnum.  A crafted ELF with e_phnum == 0x7FFF
allocates ~1.5 MB (32767 × 48 bytes on 64-bit machine) on the stack,
which adds to the previous ~1.75 MB alloca for the phdr table that
precedes it.

This patch follow Florian's suggestion [1] to use a two-pass approach
(collect-then-map) with a single-pass struct dl_pt_load_iterator that
precomputes the metadata needed by _dl_map_segments (p_align_max,
has_holes, first/last segment bounds, nloadcmds) and then yields one
struct loadcmd at a time through _dl_pt_load_iterator_next, holding at
most one loadcmd on the stack at a time.  The same iterator is
threaded through _dl_map_segments in dl-map-segments.h.

The main complex part is the test, which adds python-generated crafted
ET_DYN that has e_phnum == 0x7FFF: one PT_LOAD covering the ELF header
so the loader exercises the full iterator path, and the remaining
headers PT_NULL.  The test runs two subtests under a reduced stack limit
(phdr alloca + 1 MB headroom ≈ 2.75 MB, well below the 3.25 MB the
unfixed VLA code requires).

Checked on aarch64-linux-gnu, x86_64-linux-gnu, and i686-linux-gnu.

[1] https://sourceware.org/pipermail/libc-alpha/2026-February/175136.html
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-11 15:49:41 -03:00
Avinal Kumar ba305d8268 intl: Add tests for plural expression hardening
The first test checks for stack overflow.  It uses a plural expression
nested 5000 levels deep using the !(1-(...)) pattern.  The parser
accepts it (below YYMAXDEPTH=10000), but evaluation exeeds
EVAL_MAXDEPTH=100 and falls back to index 0 instead of crashing with
SIGSEGV.

The second test checks for division by zero in plural expression.  The
expression (n!=1)+1/(n!=1729) triggers 1/0 for n=1729.  msgfmt only
validates 0<= n <= 1000, so the .mo file is accepted.  Evaluation
returns PE_INTDIV and falls back instead of raising SIGFPE.

Adaptations from gettext to glibc:

- gettext's plural-3 embeds the nested expresion as a literal string.
This test uses an AWK script (plural-depth.awk) to generate the same
expression.

- gettext uses LANGUAGE= (empty) with LC_ALL=ll and its own locale
setup.  glibc requires a real locale for setlocale() or else the "C"
locale override in dcigettext.c ignores LANGUAGE entirely.

The tests are derived from GNU gettext's plural-3 (commit 021348871a22)
and plural-4 (commit 429ba6c6b835), adapted to glibc's test framework.

Original author: Bruno Haible <bruno@clisp.org>

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-05-11 15:49:41 -03:00
Avinal Kumar e7f5359db7 intl: Import plural expression hardening from GNU gettext
The plural expression evaluator plural_eval() in eval-plural.h uses
unbounded recursion, which can cause a stack overflow crash with
deeply nested expressions in malicious .mo files.  This is
particularly dangerous on threads with small stacks (musl libc
default: 128 KB, AIX 7 default: 96 KB, glibc after ulimit -s 260:
~3919 recursions max).

Additionally, division by zero in plural expressions triggers
raise(SIGFPE), which is not multithread-safe, catching SIGFPE
requires per-process signal handlers that race with other threads.

Fix both by importing the hardening from GNU gettext:

- Replace unbounded plural_eval() with depth-limited
  plural_eval_recurse() (EVAL_MAXDEPTH=100), returning a
  struct eval_result with status instead of a bare unsigned long.

- Return PE_INTDIV status on division by zero instead of raising
  SIGFPE.  Remove the architecture-specific INTDIV0_RAISES_SIGFPE
  macro and the conditional #include <signal.h>.

- Update plural_lookup() in dcigettext.c to handle the new return
  type, falling back to index 0 on any evaluation failure.

Based on GNU gettext commits ef37a1540 and 726bfb1d1.
Discussed on: https://sourceware.org/pipermail/libc-alpha/2023-October/152010.html

Original author: Bruno Haible <bruno@clisp.org>

Signed-off-by: Avinal Kumar <avinal.xlvii@gmail.com>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
2026-05-11 15:49:40 -03:00
Adhemerval Zanella c2d6afb4a0 arm: Enable static-pie support (BZ 34098)
It requires proper gcc support [1], and without proper compiler support
the arm configure disable static-pie support.

The start.S requires some adjustment to avoid loading main from
the GOT.

Checked on arm-linux-gnueabihf with and without the gcc patch applied.

[1] https://gcc.gnu.org/pipermail/gcc-patches/2022-July/598610.html

Reviewed-by: Yury Khrustalev <yury.khrustalev@arm.com>
Reviewed-by: Sam James <sam@gentoo.org>
2026-05-11 11:15:13 -03:00
Andreas Schwab b28ffb926f riscv: redirect strlen in early startup
__tunables_init calls strlen before ifunc relocations have been set up,
redirect it to __strlen_generic.
2026-05-11 10:49:09 +02:00
Peter Collingbourne c20e60e350 resolv, rt: Change some extern inline functions to static inline
The following functions:

__aio_create_helper_thread
__aio_start_notify_thread
__gai_create_helper_thread
__gai_start_notify_thread

are declared as extern inline, but no translation unit provides their
real definitions. This can lead to a link failure if the functions are
not inlined. Fix it by declaring them as static inline instead.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
2026-05-08 19:53:24 +02:00
9b8ff36d0c riscv: Add RVV strncmp for both multiarch and non-multiarch builds
This patch adds an RVV-optimized implementation of strncmp for RISC-V and
enables it for both multiarch (IFUNC) and non-multiarch builds.

The implementation integrates Hau Hsu's 2023 RVV work under a unified
ifunc-based framework. A vectorized version (__strncmp_vector) is added
alongside the generic fallback (__strncmp_generic). The runtime resolver
selects the RVV variant when RISCV_HWPROBE_KEY_IMA_EXT_0 reports vector
support (RVV).

Currently, the resolver still selects the RVV variant even when the RVV
extension is disabled via prctl(). As a consequence, any process that
has RVV disabled via prctl() will receive SIGILL when calling strncmp().

Co-authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-authored-by: Jerry Shih <jerry.shih@sifive.com>
Signed-off-by: Yao Zihong <zihong.plct@isrc.iscas.ac.cn>
Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
2026-05-08 09:32:11 -05:00
d62f110fd1 riscv: Add RVV strcmp for both multiarch and non-multiarch builds
This patch adds an RVV-optimized implementation of strcmp for RISC-V and
enables it for both multiarch (IFUNC) and non-multiarch builds.

The implementation integrates Hau Hsu's 2023 RVV work under a unified
ifunc-based framework. A vectorized version (__strcmp_vector) is added
alongside the generic fallback (__strcmp_generic). The runtime resolver
selects the RVV variant when RISCV_HWPROBE_KEY_IMA_EXT_0 reports vector
support (RVV).

Currently, the resolver still selects the RVV variant even when the RVV
extension is disabled via prctl(). As a consequence, any process that
has RVV disabled via prctl() will receive SIGILL when calling strcmp().

Co-authored-by: Hau Hsu <hau.hsu@sifive.com>
Co-authored-by: Jerry Shih <jerry.shih@sifive.com>
Signed-off-by: Yao Zihong <zihong.plct@isrc.iscas.ac.cn>
Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
2026-05-08 09:32:11 -05:00
Yury Khrustalev 37ee8868dc support: add support_ptr_after_free
Some tests use pointers after the associated memory has been freed.
On targets that support memory tagging, using such pointers even
for test purposes might be impossible. To work around this, we add
new function that would allow to clear a pointer in a target-specific
way.

We modify 3 relevant malloc tests: tst-malloc-backtrace, tst-tcfree3,
and tst-safe-linking.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
2026-05-08 09:40:40 +01:00
Rocket Ma 8398987772 stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
* stdio-common/vfscanf-internal.c: When enlarging allocated buffer with
format %mc or %mC, glibc allocates one byte less, leading to
user-controlled one byte overflow. This commit fixes BZ #34008, or
CVE-2026-5450.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Signed-off-by: Rocket Ma <marocketbd@gmail.com>
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
2026-05-08 12:27:04 +08:00
Uros Bizjak 1a70b352c3 i386: Replace inline asm rotates in pointer_guard with stdc_rotate_{left,right}
Use the C23 <stdbit.h> rotation helpers instead of inline assembly
for pointer mangling and demangling on i386.

The PTR_MANGLE and PTR_DEMANGLE macros previously used rol/ror
inline asm with a constant rotation of 9. Replace these with
stdc_rotate_left and stdc_rotate_right operating on uintptr_t,
preserving the exact rotation count via 2 * sizeof (uintptr_t) + 1.

This change removes inline assembly, improves portability and
readability and lets the compiler select optimal code generation.

No functional change intended.

Signed-off-by: Uros Bizjak <ubizjak@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-07 20:04:49 +02:00
Uros Bizjak 63545c6d2c x86_64: Replace inline asm rotates in pointer_guard with stdc_rotate_{left,right}
Use the C23 <stdbit.h> rotation helpers instead of inline assembly
for pointer mangling and demangling on x86_64.

The PTR_MANGLE and PTR_DEMANGLE macros previously used rol/ror
inline asm with a constant rotation of 2 * LP_SIZE + 1. Replace
these with stdc_rotate_left and stdc_rotate_right operating on
uintptr_t, preserving the exact rotation count via
2 * sizeof (uintptr_t) + 1.

This change removes inline assembly, improves portability and
readability and lets the compiler select optimal code generation.

No functional change intended.

Signed-off-by: Uros Bizjak <ubizjak@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-07 20:04:24 +02:00
Uros Bizjak f7623844a7 stdlib: Add internal stdc_rotate_left implementation
It follows the C2y N3367 proposed interface, along with some tests
imported from gnulib (and adapted to glibc libsupport).

Signed-off-by: Uros Bizjak <ubizjak@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-07 20:04:06 +02:00
Carlos O'Donell 6b9135f75b Drop "(C) YYYY" from DCO'd contributions.
Contributions made under DCO use a generic statement to indicate that
the file has copyright, but that statement does not need to include a
year. Remove the year to avoid the work required to update that
statement to include future years as such updates are not required.
2026-05-07 10:41:36 -04:00
Rocket Ma ef3bfb5f91 libio: Fix ungetwc operating on byte stream [BZ #33998]
* libio/wgenops.c: When _IO_wdefault_pbackfail attempts to push back one
character, it accidently compare the wchar to push back with the last
char from byte stream, instead of wide stream. Under specific coding,
attacker may exploit this to leak information. This commit fix bug
33998, or CVE-2026-5928.

Signed-off-by: Rocket Ma <marocketbd@gmail.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
2026-05-07 10:15:49 -04:00
Uros Bizjak 79dbb41f15 stdlib: add missing stdc_rotate_right_ull alias when builtin is available
When __builtin_stdc_rotate_right is supported, glibc defines type-specific
aliases for several unsigned integer types (uc, us, ui, ul), but omits the
unsigned long long variant. This leads to an inconsistency between the
builtin-backed path and the generic fallback, where unsigned long long
is handled.

Add the missing stdc_rotate_right_ull macro mapping to
stdc_rotate_right(__x, __n) to complete the set of type-specific helpers
and ensure consistent API coverage across all supported unsigned integer
types.

No functional change for existing users; this only exposes the expected
alias for unsigned long long.

Fixes: 331c7a4cd0 ("stdbit: Fix builtin name used in __glibc_has_builtin check for rotate_right")

Signed-off-by: Uros Bizjak <ubizjak@gmail.com>
2026-05-06 15:21:33 +02:00
Uros Bizjak 331c7a4cd0 stdbit: Fix builtin name used in __glibc_has_builtin check for rotate_right
The __glibc_has_builtin check in include/stdbit.h incorrectly refers to
___builtin_stdc_rotate_right (with three leading underscores) instead of the
correct __builtin_stdc_rotate_right (two leading underscores). As a result,
the builtin is not detected even when supported by the compiler.

Fix the spelling to use __builtin_stdc_rotate_right consistently in both the
feature test and the corresponding comment.

Fixes: 4adae8550a ("stdlib: Add internal stdc_rotate_right implementation")
Signed-off-by: Uros Bizjak <ubizjak@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2026-05-06 14:09:12 +02:00
669 changed files with 18838 additions and 5284 deletions
-20
View File
@@ -154,30 +154,10 @@ passed to 'configure'. For example:
NOTE: '--enable-cet' is only supported on x86_64 and x32.
'--enable-memory-tagging'
Enable memory tagging support if the architecture supports it.
When the GNU C Library is built with this option then the resulting
library will be able to control the use of tagged memory when
hardware support is present by use of the tunable
'glibc.mem.tagging'. This includes the generation of tagged memory
when using the 'malloc' APIs.
At present only AArch64 platforms with MTE provide this
functionality, although the library will still operate (without
memory tagging) on older versions of the architecture.
The default is to disable support for memory tagging.
'--disable-profile'
Don't build libraries with profiling information. You may want to
use this option if you don't plan to do profiling.
'--enable-static-nss'
Compile static versions of the NSS (Name Service Switch) libraries.
This is not recommended because it defeats the purpose of NSS; a
program linked statically with the NSS libraries cannot be
dynamically reconfigured to use a different name database.
'--enable-hardcoded-path-in-tests'
By default, dynamic tests are linked to run with the installed C
library. This option hardcodes the newly built C library path in
+28
View File
@@ -9,6 +9,15 @@ Version 2.44
Major new features:
* A new tunable, glibc.elf.thp, is added to map read-only segments with
Transparent Huge Pages (THP) if THP isn't disable in kernel. When
glibc.elf.thp is set to 1, malloc uses the actual kernel THP mode
instead of defaulting to madvise mode and madvise_thp will stop issuing
MADV_HUGEPAGE if kernel THP mode is always.
* THP page size in malloc is capped to MAX_THP_PAGESIZE. If THP page
size is above MAX_THP_PAGESIZE, THP in malloc is disabled.
* Additional optimized and correctly rounded mathematical functions have
been imported from the CORE-MATH project, in particular cosh, sinh, and
tanh.
@@ -18,6 +27,16 @@ Major new features:
* New locale added: hrx_BR (Hunsrik language spoken in Brazil).
* Static PIE is now supported for arm-*-linux-gnueabi. It requires toolchain
support to correctly set the expected linker options.
* On AArch64 targets that support Guarded Control Stack extension all GCS
operations (including status, write on shadow stack, and push to shadow
stack) are locked after enabling GCS with ENFORCED or OVERRIDE GCS policy.
When a GCS operation is locked, a program cannot change this operation
status via prctl syscall. This prevents disabling or corrupting GCS
shadow stack during runtime.
Deprecated and removed features, and other changes affecting compatibility:
* Although malloc and related functions currently return pointers
@@ -29,6 +48,15 @@ Deprecated and removed features, and other changes affecting compatibility:
* The s390-linux-gnu (31bit) configuration is no longer supported.
* The --enable-memory-tagging configure option has been removed.
The corresponding AArch64-specific functionality that was previously
activated by this flag has been removed as well.
* The --enable-static-nss configure option has been removed. It had no
effect on the build since the NSS reorganization in glibc 2.33; its only
remaining behavior was to suppress the link-time warnings on the NSS
interface functions in libc.a, which are now emitted unconditionally.
Changes to build and runtime requirements:
[Add changes to build and runtime requirements here]
+4 -2
View File
@@ -288,7 +288,8 @@ $(addprefix $(objpfx),$(binaries-malloc-check-tests)): %-malloc-check: %.o \
endif
ifneq "$(strip $(binaries-malloc-hugetlb1-tests))" ""
$(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): %-malloc-hugetlb1: %.o \
$(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): \
%-malloc-hugetlb1: %-malloc-hugetlb1.o \
$(link-extra-libs-tests) \
$(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
$(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
@@ -296,7 +297,8 @@ $(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): %-malloc-hugetlb1: %.o
endif
ifneq "$(strip $(binaries-malloc-hugetlb2-tests))" ""
$(addprefix $(objpfx),$(binaries-malloc-hugetlb2-tests)): %-malloc-hugetlb2: %.o \
$(addprefix $(objpfx),$(binaries-malloc-hugetlb2-tests)): \
%-malloc-hugetlb2: %-malloc-hugetlb2.o \
$(link-extra-libs-tests) \
$(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
$(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
+529 -7
View File
@@ -113,6 +113,7 @@ gnulib:
stdlib/setenv.c
stdlib/strtoll.c
stdlib/strtoul.c
stdlib/tst-stdc_rotate_left.c
stdlib/tst-stdc_rotate_right.c
# Merged from gnulib 2014-6-26, needs merge back
string/memchr.c
@@ -151,9 +152,7 @@ gnulib:
time/strptime.c
time/timegm.c
# The last merge was 2014-12-11 and merged gettext 0.19.3 into glibc with a
# patch submitted to the gettext mailing list for changes that could be merged
# back.
# Synced with GNU gettext 1.0 (2026-05-18), through gettext commit 2ebbdd0e2.
#
# This commit was omitted from the merge as it does not appear to be compatible
# with how glibc expects things to work:
@@ -170,6 +169,7 @@ gettext:
intl/dcngettext.c
intl/dgettext.c
intl/dngettext.c
intl/eval-plural.h
intl/explodename.c
intl/finddomain.c
intl/gettext.c
@@ -211,8 +211,9 @@ unicode:
# The following files are shared with the upstream tzcode project and must be
# updated regularly to stay in sync with the upstream releases.
#
# Currently synced to TZDB 2024b, announced and distributed here:
# https://github.com/eggert/tz/releases/tag/2024b
# Currently synced to TZDB 2026b, distributed at:
# https://github.com/eggert/tz/releases/tag/2026b
# https://data.iana.org/time-zones/releases/tzdb-2026b.tar.lz
tzcode:
timezone/private.h
timezone/tzfile.h
@@ -257,11 +258,11 @@ tzdata:
# FLT_EVAL_METHOD equal to 2 (i386). Additionally, extra optimizations
# are applied to share the internal data table across different
# implementations.
#
#
# The project is distribute here:
# https://gitlab.inria.fr/core-math/core-math/
core-math:
# src/binary64/acosh/acosh.c, revision 1bd85b89
# src/binary64/acosh/acosh.c, revision 887cab6f
sysdeps/ieee754/dbl-64/e_acosh.c
# src/binary64/atanh/atanh.c, revision 532e37dc
sysdeps/ieee754/dbl-64/e_atanh.c
@@ -331,3 +332,524 @@ core-math:
sysdeps/ieee754/flt-32/s_tanhf.c
# src/binary32/tanpi/tanpif.c, revision 3bbf907
sysdeps/ieee754/flt-32/s_tanpif.c
# The following files are imported from the Arm Optimized-Routines project,
# with adjustments made to follow glibc code style and integrate with
# glibc-specific infrastructure where needed.
#
# The project is distributed here:
# https://github.com/ARM-software/optimized-routines/
arm-optimized-routines:
# Math routines
# math/math_config.h, revision 712aa21
sysdeps/ieee754/flt-32/math_config.h
# math/math_config.h, revision 712aa21
sysdeps/ieee754/dbl-64/math_config.h
# math/math_err.c, revision 189dfef
sysdeps/ieee754/dbl-64/math_err.c
# math/math_errf.c, revision 189dfef
sysdeps/ieee754/flt-32/math_errf.c
# math/cosf.c, revision aec783d
sysdeps/ieee754/flt-32/s_cosf.c
# math/exp2f.c, revision 712aa21
sysdeps/ieee754/flt-32/e_exp2f.c
# math/exp2f_data.c, revision 189dfef
sysdeps/ieee754/flt-32/e_exp2f_data.c
# math/expf.c, revision 712aa21
sysdeps/ieee754/flt-32/e_expf.c
# math/log2f.c, revision 712aa21
sysdeps/ieee754/flt-32/e_log2f.c
# math/log2f_data.c, revision 189dfef
sysdeps/ieee754/flt-32/e_log2f_data.c
# math/logf.c, revision 712aa21
sysdeps/ieee754/flt-32/e_logf.c
# math/logf_data.c, revision c0136f1
sysdeps/ieee754/flt-32/e_logf_data.c
# math/powf.c, revision 712aa21
sysdeps/ieee754/flt-32/e_powf.c
# math/powf_log2_data.c, revision 189dfef
sysdeps/ieee754/flt-32/e_powf_log2_data.c
# math/sincosf.c, revision af29d39
sysdeps/ieee754/flt-32/s_sincosf.c
# math/sincosf.h, revision aed553c
sysdeps/ieee754/flt-32/s_sincosf.h
# math/sincosf_data.c, revision 189dfef
sysdeps/ieee754/flt-32/s_sincosf_data.c
# math/sinf.c, revision aec783d
sysdeps/ieee754/flt-32/s_sinf.c
# math/exp.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_exp.c
# math/exp10.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_exp10.c
# math/exp2.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_exp2.c
# math/exp_data.c, revision c013701
sysdeps/ieee754/dbl-64/e_exp_data.c
# math/log.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_log.c
# math/log2.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_log2.c
# math/log2_data.c, revision 189dfef
sysdeps/ieee754/dbl-64/e_log2_data.c
# math/log_data.c, revision 189dfef
sysdeps/ieee754/dbl-64/e_log_data.c
# math/pow.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_pow.c
# math/pow_log_data.c, revision 189dfef
sysdeps/ieee754/dbl-64/e_pow_log_data.c
# Mathvec routines
# math/math_config.h, revision 712aa21
sysdeps/aarch64/fpu/vecmath_config.h
# math/aarch64/v_erf_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/erf_data.c
# math/aarch64/v_erfc_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/erfc_data.c
# math/aarch64/v_erfcf_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/erfcf_data.c
# math/aarch64/v_erff_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/erff_data.c
# math/aarch64/v_exp_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_exp_data.c
# math/aarch64/v_exp_tail_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_exp_tail_data.c
# math/aarch64/v_log10_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_log10_data.c
# math/aarch64/v_log2_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_log2_data.c
# math/aarch64/v_log_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_log_data.c
# math/aarch64/v_pow_exp_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_pow_exp_data.c
# math/aarch64/v_pow_log_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_pow_log_data.c
# math/aarch64/v_powf_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_powf_data.c
# math/aarch64/sve/acos.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acos.c
# math/aarch64/sve/acosf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acosf.c
# math/aarch64/sve/acosh.c, revision 8b665af
sysdeps/aarch64/fpu/sve/acosh.c
# math/aarch64/sve/acoshf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acoshf.c
# math/aarch64/sve/acospi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acospi.c
# math/aarch64/sve/acospif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acospif.c
# math/aarch64/sve/asin.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asin.c
# math/aarch64/sve/asinf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asinf.c
# math/aarch64/sve/asinh.c, revision 9bf4cfc
sysdeps/aarch64/fpu/sve/asinh.c
# math/aarch64/sve/asinhf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asinhf.c
# math/aarch64/sve/asinpi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asinpi.c
# math/aarch64/sve/asinpif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asinpif.c
# math/aarch64/sve/atan.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan.c
# math/aarch64/sve/atan2.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan2.c
# math/aarch64/sve/atan2f.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan2f.c
# math/aarch64/sve/atan2pi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan2pi.c
# math/aarch64/sve/atan2pif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan2pif.c
# math/aarch64/sve/atanf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atanf.c
# math/aarch64/sve/atanh.c, revision 29c7342
sysdeps/aarch64/fpu/sve/atanh.c
# math/aarch64/sve/atanhf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atanhf.c
# math/aarch64/sve/atanpi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atanpi.c
# math/aarch64/sve/atanpif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atanpif.c
# math/aarch64/sve/cbrt.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/cbrt.c
# math/aarch64/sve/cbrtf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/cbrtf.c
# math/aarch64/sve/cos.c, revision 99cbbad
sysdeps/aarch64/fpu/sve/cos.c
# math/aarch64/sve/cosf.c, revision 25aa012
sysdeps/aarch64/fpu/sve/cosf.c
# math/aarch64/sve/cosh.c, revision 242a017
sysdeps/aarch64/fpu/sve/cosh.c
# math/aarch64/sve/coshf.c, revision 2fff60c
sysdeps/aarch64/fpu/sve/coshf.c
# math/aarch64/sve/cospi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/cospi.c
# math/aarch64/sve/cospif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/cospif.c
# math/aarch64/sve/erf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/erf.c
# math/aarch64/sve/erfc.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/erfc.c
# math/aarch64/sve/erfcf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/erfcf.c
# math/aarch64/sve/erff.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/erff.c
# math/aarch64/sve/exp.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp.c
# math/aarch64/sve/exp10.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp10.c
# math/aarch64/sve/exp10f.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp10f.c
# math/aarch64/sve/exp10m1.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp10m1.c
# math/aarch64/sve/exp10m1f.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp10m1f.c
# math/aarch64/sve/exp2.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp2.c
# math/aarch64/sve/exp2f.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp2f.c
# math/aarch64/sve/exp2m1.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp2m1.c
# math/aarch64/sve/exp2m1f.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp2m1f.c
# math/aarch64/sve/expf.c, revision 47044a5
sysdeps/aarch64/fpu/sve/expf.c
# math/aarch64/sve/expm1.c, revision d664b69
sysdeps/aarch64/fpu/sve/expm1.c
# math/aarch64/sve/expm1f.c, revision d664b69
sysdeps/aarch64/fpu/sve/expm1f.c
# math/aarch64/sve/hypot.c, revision 74d699d
sysdeps/aarch64/fpu/sve/hypot.c
# math/aarch64/sve/hypotf.c, revision 74d699d
sysdeps/aarch64/fpu/sve/hypotf.c
# math/aarch64/sve/log.c, revision 91b052e
sysdeps/aarch64/fpu/sve/log.c
# math/aarch64/sve/log10.c, revision 91b052e
sysdeps/aarch64/fpu/sve/log10.c
# math/aarch64/sve/log10f.c, revision 8ca4cc0
sysdeps/aarch64/fpu/sve/log10f.c
# math/aarch64/sve/log10p1.c, revision 166744d
sysdeps/aarch64/fpu/sve/log10p1.c
# math/aarch64/sve/log10p1f.c, revision 8124d30
sysdeps/aarch64/fpu/sve/log10p1f.c
# math/aarch64/sve/log1p.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/log1p.c
# math/aarch64/sve/log1pf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/log1pf.c
# math/aarch64/sve/log2.c, revision 91b052e
sysdeps/aarch64/fpu/sve/log2.c
# math/aarch64/sve/log2f.c, revision 8ca4cc0
sysdeps/aarch64/fpu/sve/log2f.c
# math/aarch64/sve/log2p1.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/log2p1.c
# math/aarch64/sve/log2p1f.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/log2p1f.c
# math/aarch64/sve/logf.c, revision 8ca4cc0
sysdeps/aarch64/fpu/sve/logf.c
# math/aarch64/sve/pow.c, revision 2c38e83
sysdeps/aarch64/fpu/sve/pow.c
# math/aarch64/sve/powf.c, revision 2c38e83
sysdeps/aarch64/fpu/sve/powf.c
# math/aarch64/sve/powr.c, revision 30da949
sysdeps/aarch64/fpu/sve/powr.c
# math/aarch64/sve/powrf.c, revision e2bca42
sysdeps/aarch64/fpu/sve/powrf.c
# math/aarch64/sve/rsqrt.c, revision 4fc7374
sysdeps/aarch64/fpu/sve/rsqrt.c
# math/aarch64/sve/rsqrtf.c, revision b1bc8ba
sysdeps/aarch64/fpu/sve/rsqrtf.c
# math/aarch64/sve/sin.c, revision 99cbbad
sysdeps/aarch64/fpu/sve/sin.c
# math/aarch64/sve/sinf.c, revision 25aa012
sysdeps/aarch64/fpu/sve/sinf.c
# math/aarch64/sve/sinh.c, revision 242a017
sysdeps/aarch64/fpu/sve/sinh.c
# math/aarch64/sve/sinhf.c, revision 2fff60c
sysdeps/aarch64/fpu/sve/sinhf.c
# math/aarch64/sve/sinpi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/sinpi.c
# math/aarch64/sve/sinpif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/sinpif.c
# math/aarch64/sve/tan.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tan.c
# math/aarch64/sve/tanf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tanf.c
# math/aarch64/sve/tanh.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tanh.c
# math/aarch64/sve/tanhf.c, revision 2fff60c
sysdeps/aarch64/fpu/sve/tanhf.c
# math/aarch64/sve/tanpi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tanpi.c
# math/aarch64/sve/tanpif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tanpif.c
# math/aarch64/sve/sv_exp_special_inline.h, revision d664b69
sysdeps/aarch64/fpu/sve/sv_exp_special_inline.h
# math/aarch64/sve/sv_expf_inline.h, revision ba35b32
sysdeps/aarch64/fpu/sve/sv_expf_inline.h
# math/aarch64/sve/sv_expf_special_inline.h, revision d664b69
sysdeps/aarch64/fpu/sve/sv_expf_special_inline.h
# math/aarch64/sve/sv_expm1f_inline.h, revision 78cd003
sysdeps/aarch64/fpu/sve/sv_expm1f_inline.h
# math/aarch64/sve/sv_log1p_inline.h, revision 7d08237
sysdeps/aarch64/fpu/sve/sv_log1p_inline.h
# math/aarch64/sve/sv_log1pf_inline.h, revision a386525
sysdeps/aarch64/fpu/sve/sv_log1pf_inline.h
# math/aarch64/sve/sv_pow_inline.h, revision 30da949
sysdeps/aarch64/fpu/sve/sv_pow_inline.h
# math/aarch64/sve/sv_powf_inline.h, revision 30da949
sysdeps/aarch64/fpu/sve/sv_powf_inline.h
# math/aarch64/sve/sv_poly_f32.h, revision 5d5e6e6
sysdeps/aarch64/fpu/sve/poly_sve_f32.h
# math/aarch64/sve/sv_poly_f64.h, revision 5d5e6e6
sysdeps/aarch64/fpu/sve/poly_sve_f64.h
# math/aarch64/sve/sv_poly_generic.h, revision 5d5e6e6
sysdeps/aarch64/fpu/sve/poly_sve_generic.h
# math/aarch64/sve/sv_math.h, revision 7361ef6
sysdeps/aarch64/fpu/sve/sv_math.h
# math/aarch64/sve/sv_trig_fallback.h, revision 99cbbad
sysdeps/aarch64/fpu/sve/sv_trig_fallback.h
# math/aarch64/sve/sv_trigf_fallback.h, revision 25aa012
sysdeps/aarch64/fpu/sve/sv_trigf_fallback.h
# math/aarch64/advsimd/acos.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/acos.c
# math/aarch64/advsimd/acosf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/acosf.c
# math/aarch64/advsimd/acosh.c, revision 188e75a
sysdeps/aarch64/fpu/advsimd/acosh.c
# math/aarch64/advsimd/acoshf.c, revision af3851e
sysdeps/aarch64/fpu/advsimd/acoshf.c
# math/aarch64/advsimd/acospi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/acospi.c
# math/aarch64/advsimd/acospif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/acospif.c
# math/aarch64/advsimd/asin.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/asin.c
# math/aarch64/advsimd/asinf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/asinf.c
# math/aarch64/advsimd/asinh.c, revision bac1b4f
sysdeps/aarch64/fpu/advsimd/asinh.c
# math/aarch64/advsimd/asinhf.c, revision af3851e
sysdeps/aarch64/fpu/advsimd/asinhf.c
# math/aarch64/advsimd/asinpi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/asinpi.c
# math/aarch64/advsimd/asinpif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/asinpif.c
# math/aarch64/advsimd/atan.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan.c
# math/aarch64/advsimd/atan2.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan2.c
# math/aarch64/advsimd/atan2f.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan2f.c
# math/aarch64/advsimd/atan2pi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan2pi.c
# math/aarch64/advsimd/atan2pif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan2pif.c
# math/aarch64/advsimd/atanf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atanf.c
# math/aarch64/advsimd/atanh.c, revision 281cb3a
sysdeps/aarch64/fpu/advsimd/atanh.c
# math/aarch64/advsimd/atanhf.c, revision d852158
sysdeps/aarch64/fpu/advsimd/atanhf.c
# math/aarch64/advsimd/atanpi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atanpi.c
# math/aarch64/advsimd/atanpif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atanpif.c
# math/aarch64/advsimd/cbrt.c, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/cbrt.c
# math/aarch64/advsimd/cbrtf.c, revision 6c74650
sysdeps/aarch64/fpu/advsimd/cbrtf.c
# math/aarch64/advsimd/cos.c, revision 677a12b
sysdeps/aarch64/fpu/advsimd/cos.c
# math/aarch64/advsimd/cosf.c, revision 15c35d9
sysdeps/aarch64/fpu/advsimd/cosf.c
# math/aarch64/advsimd/cosh.c, revision 447bbaa
sysdeps/aarch64/fpu/advsimd/cosh.c
# math/aarch64/advsimd/coshf.c, revision 45237ba
sysdeps/aarch64/fpu/advsimd/coshf.c
# math/aarch64/advsimd/cospi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/cospi.c
# math/aarch64/advsimd/cospif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/cospif.c
# math/aarch64/advsimd/erf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/erf.c
# math/aarch64/advsimd/erfc.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/erfc.c
# math/aarch64/advsimd/erfcf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/erfcf.c
# math/aarch64/advsimd/erff.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/erff.c
# math/aarch64/advsimd/exp.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp.c
# math/aarch64/advsimd/exp10.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp10.c
# math/aarch64/advsimd/exp10f.c, revision 69d38dd
sysdeps/aarch64/fpu/advsimd/exp10f.c
# math/aarch64/advsimd/exp10m1.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp10m1.c
# math/aarch64/advsimd/exp10m1f.c, revision a5092e8
sysdeps/aarch64/fpu/advsimd/exp10m1f.c
# math/aarch64/advsimd/exp2.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp2.c
# math/aarch64/advsimd/exp2f.c, revision 69d38dd
sysdeps/aarch64/fpu/advsimd/exp2f.c
# math/aarch64/advsimd/exp2m1.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp2m1.c
# math/aarch64/advsimd/exp2m1f.c, revision a5092e8
sysdeps/aarch64/fpu/advsimd/exp2m1f.c
# math/aarch64/advsimd/expf.c, revision 69d38dd
sysdeps/aarch64/fpu/advsimd/expf.c
# math/aarch64/advsimd/expm1.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/expm1.c
# math/aarch64/advsimd/expm1f.c, revision a5092e8
sysdeps/aarch64/fpu/advsimd/expm1f.c
# math/aarch64/advsimd/hypot.c, revision 09f8d05
sysdeps/aarch64/fpu/advsimd/hypot.c
# math/aarch64/advsimd/hypotf.c, revision 99dff4f
sysdeps/aarch64/fpu/advsimd/hypotf.c
# math/aarch64/advsimd/log.c, revision 0345857
sysdeps/aarch64/fpu/advsimd/log.c
# math/aarch64/advsimd/log10.c, revision 77aff35
sysdeps/aarch64/fpu/advsimd/log10.c
# math/aarch64/advsimd/log10f.c, revision ff1596f
sysdeps/aarch64/fpu/advsimd/log10f.c
# math/aarch64/advsimd/log10p1.c, revision 7fc8510
sysdeps/aarch64/fpu/advsimd/log10p1.c
# math/aarch64/advsimd/log10p1f.c, revision 58cd796
sysdeps/aarch64/fpu/advsimd/log10p1f.c
# math/aarch64/advsimd/log1p.c, revision 355a985
sysdeps/aarch64/fpu/advsimd/log1p.c
# math/aarch64/advsimd/log1pf.c, revision 5cf8408
sysdeps/aarch64/fpu/advsimd/log1pf.c
# math/aarch64/advsimd/log2.c, revision b8deb33
sysdeps/aarch64/fpu/advsimd/log2.c
# math/aarch64/advsimd/log2f.c, revision b98f80d
sysdeps/aarch64/fpu/advsimd/log2f.c
# math/aarch64/advsimd/log2p1.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/log2p1.c
# math/aarch64/advsimd/log2p1f.c, revision 58cd796
sysdeps/aarch64/fpu/advsimd/log2p1f.c
# math/aarch64/advsimd/logf.c, revision 1b15b12
sysdeps/aarch64/fpu/advsimd/logf.c
# math/aarch64/advsimd/finite_pow.h, revision 30da949
sysdeps/aarch64/fpu/advsimd/finite_pow.h
# math/pow_common.h, revision 30da949
sysdeps/aarch64/fpu/pow_common.h
# math/powf_common.h, revision 30da949
sysdeps/aarch64/fpu/powf_common.h
# math/aarch64/advsimd/pow.c, revision 30da949
sysdeps/aarch64/fpu/advsimd/pow.c
# math/aarch64/advsimd/powf.c, revision 30da949
sysdeps/aarch64/fpu/advsimd/powf.c
# math/aarch64/advsimd/powr.c, revision 30da949
sysdeps/aarch64/fpu/advsimd/powr.c
# math/aarch64/advsimd/powrf.c, revision e2bca42
sysdeps/aarch64/fpu/advsimd/powrf.c
# math/aarch64/advsimd/rsqrt.c, revision 0945eab
sysdeps/aarch64/fpu/advsimd/rsqrt.c
# math/aarch64/advsimd/rsqrtf.c, revision 88e9cc9
sysdeps/aarch64/fpu/advsimd/rsqrtf.c
# math/aarch64/advsimd/sin.c, revision 677a12b
sysdeps/aarch64/fpu/advsimd/sin.c
# math/aarch64/advsimd/sinf.c, revision 15c35d9
sysdeps/aarch64/fpu/advsimd/sinf.c
# math/aarch64/advsimd/sinh.c, revision 447bbaa
sysdeps/aarch64/fpu/advsimd/sinh.c
# math/aarch64/advsimd/sinhf.c, revision 45237ba
sysdeps/aarch64/fpu/advsimd/sinhf.c
# math/aarch64/advsimd/sinpi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/sinpi.c
# math/aarch64/advsimd/sinpif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/sinpif.c
# math/aarch64/advsimd/tan.c, revision a8bbb87
sysdeps/aarch64/fpu/advsimd/tan.c
# math/aarch64/advsimd/tanf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/tanf.c
# math/aarch64/advsimd/tanh.c, revision 447bbaa
sysdeps/aarch64/fpu/advsimd/tanh.c
# math/aarch64/advsimd/tanhf.c, revision 45237ba
sysdeps/aarch64/fpu/advsimd/tanhf.c
# math/aarch64/advsimd/tanpi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/tanpi.c
# math/aarch64/advsimd/tanpif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/tanpif.c
# math/aarch64/advsimd/v_exp_special_case_inline.h, revision dce5dab
sysdeps/aarch64/fpu/advsimd/v_exp_special_case_inline.h
# math/aarch64/advsimd/v_expf_inline.h, revision 747e0e1
sysdeps/aarch64/fpu/advsimd/v_expf_inline.h
# math/aarch64/advsimd/v_expf_special_inline.h, revision a5092e8
sysdeps/aarch64/fpu/advsimd/v_expf_special_inline.h
# math/aarch64/advsimd/v_expm1_inline.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/v_expm1_inline.h
# math/aarch64/advsimd/v_expm1f_inline.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/v_expm1f_inline.h
# math/aarch64/advsimd/v_log1p_inline.h, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/v_log1p_inline.h
# math/aarch64/advsimd/v_log1pf_inline.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/v_log1pf_inline.h
# math/aarch64/advsimd/v_pow_inline.h, revision efde0c7
sysdeps/aarch64/fpu/advsimd/v_pow_inline.h
# math/aarch64/advsimd/v_powf_inline.h, revision 5b06a16
sysdeps/aarch64/fpu/advsimd/v_powf_inline.h
# math/aarch64/advsimd/v_powrf_inline.h, revision 30da949
sysdeps/aarch64/fpu/advsimd/v_powrf_inline.h
# math/aarch64/advsimd/v_poly_f32.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/poly_advsimd_f32.h
# math/aarch64/advsimd/v_poly_f64.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/poly_advsimd_f64.h
# math/poly_generic.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/poly_generic.h
# math/aarch64/advsimd/v_math.h, revision 1829557
sysdeps/aarch64/fpu/advsimd/v_math.h
# math/aarch64/advsimd/v_trig_fallback.h, revision 677a12b
sysdeps/aarch64/fpu/advsimd/v_trig_fallback.h
# math/aarch64/advsimd/v_trigf_fallback.h, revision 15c35d9
sysdeps/aarch64/fpu/advsimd/v_trigf_fallback.h
# String routines
# string/aarch64/__mtag_tag_region.S, revision 41e5ae5
sysdeps/aarch64/__mtag_tag_region.S
# string/aarch64/__mtag_tag_zero_region.S, revision 41e5ae5
sysdeps/aarch64/__mtag_tag_zero_region.S
# string/aarch64/memchr-mte.S, revision 41e5ae5
sysdeps/aarch64/memchr.S
# string/aarch64/memcmp.S, revision 41e5ae5
sysdeps/aarch64/memcmp.S
# string/aarch64/memcpy-advsimd.S, revision 41e5ae5
sysdeps/aarch64/memcpy.S
# string/aarch64/memcpy-mops.S, revision 41e5ae5
sysdeps/aarch64/multiarch/memcpy_mops.S
# string/aarch64/memcpy-sve.S, revision 71e3640
sysdeps/aarch64/multiarch/memcpy_sve.S
# string/aarch64/memmove-mops.S, revision 41e5ae5
sysdeps/aarch64/multiarch/memmove_mops.S
# string/aarch64/memrchr.S, revision 41e5ae5
sysdeps/aarch64/memrchr.S
# string/aarch64/memset-mops.S, revision 41e5ae5
sysdeps/aarch64/multiarch/memset_mops.S
# string/aarch64/memset-sve.S, revision 0f71101
sysdeps/aarch64/multiarch/memset_sve_zva64.S
# string/aarch64/memset.S, revision 098df8c
sysdeps/aarch64/memset.S
# string/aarch64/stpcpy.S, revision 189dfef
sysdeps/aarch64/stpcpy.S
# string/aarch64/strchr-mte.S, revision 41e5ae5
sysdeps/aarch64/strchr.S
# string/aarch64/strchrnul-mte.S, revision 41e5ae5
sysdeps/aarch64/strchrnul.S
# string/aarch64/strcmp.S, revision 41e5ae5
sysdeps/aarch64/strcmp.S
# string/aarch64/strcpy.S, revision 41e5ae5
sysdeps/aarch64/strcpy.S
# string/aarch64/strlen-mte.S, revision 41e5ae5
sysdeps/aarch64/strlen.S
# string/aarch64/strncmp.S, revision 41e5ae5
sysdeps/aarch64/strncmp.S
# string/aarch64/strnlen.S, revision 41e5ae5
sysdeps/aarch64/strnlen.S
# string/aarch64/strrchr-mte.S, revision 41e5ae5
sysdeps/aarch64/strrchr.S
# string/arm/memchr.S, revision 1eb5d7c
sysdeps/arm/armv6t2/memchr.S
# string/arm/memcpy.S, revision 1eb5d7c
sysdeps/arm/armv7/multiarch/memcpy_impl.S
# string/arm/strcmp.S, revision 1eb5d7c
sysdeps/arm/armv7/strcmp.S
# string/arm/strlen-armv6t2.S, revision 1eb5d7c
sysdeps/arm/armv6t2/strlen.S
+6 -6
View File
@@ -1,11 +1,11 @@
Buffer overread in ns_printrrf with corrupted RDATA field
The deprecated functions ns_printrrf, ns_printrr and fp_nquery in the
GNU C Library version 2.2 and newer fail to validate the RDATA content
against the RDATA length in a DNS response when processing LOC, CERT,
TKEY or TSIG records, which may allow an attacker to craft a DNS
response, causing a target application to crash or read uninitialized
memory.
GNU C Library version 2.0.1 to version 2.43 fail to validate the RDATA
content against the RDATA length in a DNS response when processing A6,
CERT, LOC, TKEY or TSIG records, which may allow an attacker to craft a
DNS response, causing a target application to crash or read
uninitialized memory.
These functions are for application debugging only and hence not in the
path of code executed by the DNS resolver. Further, they have been
@@ -15,4 +15,4 @@ interfaces since they may be removed in future versions.
CVE-Id: CVE-2026-6238
Public-Date: 2026-04-11
Vulnerable-Commit: b43b13ac2544b11f35be301d1589b51a8473e32b (2.2)
Vulnerable-Commit: ee188d555b8c32ad9704a7440cab400af967292f (1.90)
+20
View File
@@ -0,0 +1,20 @@
Potential stack-based buffer clash during tilde expansion in wordexp
Calling wordexp with a tilde (~) followed by an overly long username
in the GNU C Library version 2.2.3 to 2.43 may lead to a stack buffer
clash.
When expanding paths that begin with a tilde (~) followed by a username, the
internal parse_tilde function extracts the username to determine the user's
home directory. The implementation allocates memory for this username directly
on the stack using the strndupa macro. Because the size of this allocation
was determined by the length of the user-supplied input without any bounds
checks, passing an excessively long username e.g. thousands of characters,
forces the thread to exhaust its stack space. Thus if an application passes
untrusted, attacker-controlled input to the wordexp function, an attacker
can trigger a stack clash.
CVE-Id: CVE-2026-6791
Public-Date: 2026-06-22
Vulnerable-Commit: 344af000e1d6e9c7882b9bc48e71cb3f1b5fc03c (2.2.3-114)
Reported-by: storm
+3 -6
View File
@@ -32,9 +32,6 @@
/* Define if _Unwind_Find_FDE should be exported from glibc. */
#undef EXPORT_UNWIND_FIND_FDE
/* Define if static NSS modules are wanted. */
#undef DO_STATIC_NSS
/* Assume that the compiler supports __builtin_expect.
This macro is necessary for proper compilation of code
shared between GNU libc and GNU gettext projects. */
@@ -170,6 +167,9 @@
/* Mach specific: define if the `vm_set_size_limit' RPC is available. */
#undef HAVE_MACH_VM_SET_SIZE_LIMIT
/* Mach specific: define if the `task_max_priority' RPC is available. */
#undef HAVE_MACH_TASK_MAX_PRIORITY
/* Mach/i386 specific: define if the `i386_io_perm_*' RPCs are available. */
#undef HAVE_I386_IO_PERM_MODIFY
@@ -182,9 +182,6 @@
/* Define if inlined system calls are available. */
#undef HAVE_INLINED_SYSCALLS
/* Define if memory tagging support should be enabled. */
#undef USE_MTAG
/* Package description. */
#undef PKGVERSION
-3
View File
@@ -78,12 +78,9 @@ multi-arch = @multi_arch@
mach-interface-list = @mach_interface_list@
memory-tagging = @memory_tagging@
# Configuration options.
build-shared = @shared@
build-profile = @profile@
build-static-nss = @static_nss@
cross-compiling = @cross_compiling@
force-install = @force_install@
build-nscd = @build_nscd@
Vendored
-84
View File
@@ -616,7 +616,6 @@ RELEASE
VERSION
mach_interface_list
DEFINES
static_nss
profile
libc_cv_multidir
test_enable_cet
@@ -703,7 +702,6 @@ INSTALL_PROGRAM
base_machine
build_pt_chown
build_nscd
memory_tagging
enable_werror
force_install
bindnow
@@ -805,13 +803,11 @@ enable_hardcoded_path_in_tests
enable_hidden_plt
enable_bind_now
enable_stack_protector
enable_static_nss
enable_force_install
enable_maintainer_mode
enable_kernel
enable_werror
enable_multi_arch
enable_memory_tagging
enable_systemtap
enable_build_nscd
enable_nscd
@@ -1479,7 +1475,6 @@ Optional Features:
--enable-stack-protector=[yes|no|all|strong]
Use -fstack-protector[-all|-strong] to detect glibc
buffer overflows
--enable-static-nss build static NSS modules [default=no]
--disable-force-install don't force installation of files from this package,
even if they are older than the installed files
--enable-maintainer-mode
@@ -1490,8 +1485,6 @@ Optional Features:
--disable-werror do not build with -Werror
--enable-multi-arch enable single DSO with optimizations for multiple
architectures
--enable-memory-tagging enable memory tagging if supported by the
architecture [default=no]
--enable-systemtap enable systemtap static probe points [default=no]
--disable-build-nscd disable building and installing the nscd daemon
--disable-nscd library functions will not contact the nscd daemon
@@ -4618,21 +4611,6 @@ all|yes|no|strong) ;;
*) as_fn_error $? "Not a valid argument for --enable-stack-protector: \"$enable_stack_protector\"" "$LINENO" 5;;
esac
# Check whether --enable-static-nss was given.
if test ${enable_static_nss+y}
then :
enableval=$enable_static_nss; static_nss=$enableval
else case e in #(
e) static_nss=no ;;
esac
fi
if test x"$static_nss" = xyes || test x"$shared" = xno; then
static_nss=yes
printf "%s\n" "#define DO_STATIC_NSS 1" >>confdefs.h
fi
# Check whether --enable-force-install was given.
if test ${enable_force_install+y}
then :
@@ -4690,26 +4668,6 @@ esac
fi
# Check whether --enable-memory-tagging was given.
if test ${enable_memory_tagging+y}
then :
enableval=$enable_memory_tagging; memory_tagging=$enableval
else case e in #(
e) memory_tagging=no ;;
esac
fi
if test "$memory_tagging" = yes; then
# Only enable this on architectures that support it.
case $host_cpu in
aarch64)
printf "%s\n" "#define USE_MTAG 1" >>confdefs.h
;;
esac
fi
# Check whether --enable-systemtap was given.
if test ${enable_systemtap+y}
then :
@@ -7437,47 +7395,6 @@ printf "%s\n" "$libc_cv_fpie" >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -Wno-discarded-qualifiers" >&5
printf %s "checking for -Wno-discarded-qualifiers... " >&6; }
if test ${libc_cv_cflags_wno_discarded_qualifiers+y}
then :
printf %s "(cached) " >&6
else case e in #(
e) if { ac_try='${CC-cc} -Werror -Wno-discarded-qualifiers -xc /dev/null -S -o /dev/null'
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
(eval $ac_try) 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }
then :
libc_cv_cflags_wno_discarded_qualifiers=-Wno-discarded-qualifiers
else case e in #(
e) libc_cv_cflags_wno_discarded_qualifiers= ;;
esac
fi
if test -z "$libc_cv_cflags_wno_discarded_qualifiers"; then
if { ac_try='${CC-cc} -Werror -Wno-incompatible-pointer-types-discards-qualifiers -xc /dev/null -S -o /dev/null'
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
(eval $ac_try) 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }
then :
libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers=-Wno-incompatible-pointer-types-discards-qualifiers
else case e in #(
e) libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers= ;;
esac
fi
libc_cv_cflags_wno_discarded_qualifiers=$libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers
fi
;;
esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_cflags_wno_discarded_qualifiers" >&5
printf "%s\n" "$libc_cv_cflags_wno_discarded_qualifiers" >&6; }
config_vars="$config_vars
config-cflags-wno-discarded-qualifiers = $libc_cv_cflags_wno_discarded_qualifiers"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GLOB_DAT reloc" >&5
printf %s "checking for GLOB_DAT reloc... " >&6; }
if test ${libc_cv_has_glob_dat+y}
@@ -9660,7 +9577,6 @@ libc_cv_multidir=`${CC-cc} $CFLAGS $CPPFLAGS -print-multi-directory`
VERSION=`sed -n -e 's/^#define VERSION "\([^"]*\)"/\1/p' < $srcdir/version.h`
RELEASE=`sed -n -e 's/^#define RELEASE "\([^"]*\)"/\1/p' < $srcdir/version.h`
-45
View File
@@ -272,19 +272,6 @@ all|yes|no|strong) ;;
*) AC_MSG_ERROR([Not a valid argument for --enable-stack-protector: "$enable_stack_protector"]);;
esac
dnl On some platforms we cannot use dynamic loading. We must provide
dnl static NSS modules.
AC_ARG_ENABLE([static-nss],
AS_HELP_STRING([--enable-static-nss],
[build static NSS modules @<:@default=no@:>@]),
[static_nss=$enableval],
[static_nss=no])
dnl Enable static NSS also if we build no shared objects.
if test x"$static_nss" = xyes || test x"$shared" = xno; then
static_nss=yes
AC_DEFINE(DO_STATIC_NSS)
fi
AC_ARG_ENABLE([force-install],
AS_HELP_STRING([--disable-force-install],
[don't force installation of files from this package, even if they are older than the installed files]),
@@ -328,21 +315,6 @@ AC_ARG_ENABLE([multi-arch],
[multi_arch=$enableval],
[multi_arch=default])
AC_ARG_ENABLE([memory-tagging],
AS_HELP_STRING([--enable-memory-tagging],
[enable memory tagging if supported by the architecture @<:@default=no@:>@]),
[memory_tagging=$enableval],
[memory_tagging=no])
if test "$memory_tagging" = yes; then
# Only enable this on architectures that support it.
case $host_cpu in
aarch64)
AC_DEFINE(USE_MTAG)
;;
esac
fi
AC_SUBST(memory_tagging)
AC_ARG_ENABLE([systemtap],
[AS_HELP_STRING([--enable-systemtap],
[enable systemtap static probe points @<:@default=no@:>@])],
@@ -1371,22 +1343,6 @@ LIBC_TRY_CC_OPTION([-fpie], [libc_cv_fpie=yes], [libc_cv_fpie=no])
AC_SUBST(libc_cv_fpie)
dnl Check how to suppress C23-qualifier-generic const warnings. GCC has
dnl -Wno-discarded-qualifiers, while clang -Wno-incompatible-pointer-types-discards-qualifiers
AC_CACHE_CHECK(for -Wno-discarded-qualifiers, libc_cv_cflags_wno_discarded_qualifiers, [dnl
LIBC_TRY_CC_OPTION([-Werror -Wno-discarded-qualifiers],
[libc_cv_cflags_wno_discarded_qualifiers=-Wno-discarded-qualifiers],
[libc_cv_cflags_wno_discarded_qualifiers=])
if test -z "$libc_cv_cflags_wno_discarded_qualifiers"; then
LIBC_TRY_CC_OPTION([-Werror -Wno-incompatible-pointer-types-discards-qualifiers],
[libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers=-Wno-incompatible-pointer-types-discards-qualifiers],
[libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers=])
libc_cv_cflags_wno_discarded_qualifiers=$libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers
fi
])
LIBC_CONFIG_VAR([config-cflags-wno-discarded-qualifiers],
[$libc_cv_cflags_wno_discarded_qualifiers])
AC_CACHE_CHECK(for GLOB_DAT reloc,
libc_cv_has_glob_dat, [dnl
cat > conftest.c <<EOF
@@ -2231,7 +2187,6 @@ libc_cv_multidir=`${CC-cc} $CFLAGS $CPPFLAGS -print-multi-directory`
AC_SUBST(libc_cv_multidir)
AC_SUBST(profile)
AC_SUBST(static_nss)
AC_SUBST(DEFINES)
+37 -25
View File
@@ -44,21 +44,20 @@ extern void __libc_init_first (int argc, char **argv, char **envp);
#include <tls.h>
#ifndef SHARED
# include <dl-osinfo.h>
# include <dl-reseed-random.h>
# include <dl-symbol-redir-ifunc.h>
# ifndef THREAD_SET_STACK_GUARD
/* Only exported for architectures that don't store the stack guard canary
in thread local area. */
uintptr_t __stack_chk_guard attribute_relro;
# endif
# ifndef THREAD_SET_POINTER_GUARD
/* Only exported for architectures that don't store the pointer guard
value in thread local area. */
uintptr_t __pointer_chk_guard_local attribute_relro attribute_hidden;
# endif
#endif
#ifndef SHARED
# include <link.h>
# include <dl-irel.h>
# include <dl-symbol-redir-ifunc.h>
# ifdef ELF_MACHINE_IRELA
# define IREL_T ElfW(Rela)
@@ -205,6 +204,7 @@ call_fini (void *unused)
#endif /* !SHARED */
#include <libc-start.h>
#include <dl-exec-post.h>
STATIC int LIBC_START_MAIN (int (*main) (int, char **, char **
MAIN_AUXVEC_DECL),
@@ -268,23 +268,22 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
ARCH_INIT_CPU_FEATURES ();
/* Do static pie self relocation after tunables and cpu features
are setup for ifunc resolvers. Before this point relocations
must be avoided. */
/* Do static-pie self relocation for the non-IRELATIVE part after tunables
and cpu features are set up. IFUNC entries are deferred until after the
TCB and the stack-protector canary are usable, so that an instrumented
resolver does not fault. Before this point relocations must be
avoided. */
_dl_relocate_static_pie ();
/* Perform IREL{,A} relocations. */
ARCH_SETUP_IREL ();
/* The stack guard goes into the TCB, so initialize it early. */
/* Set up the TCB so that the IFUNC pass below can fire resolvers
compiled with stack protection, and so that resolvers reading TLS
(errno, __thread variables, powerpc's hwcap / at_platform in the
TCB) observe an initialised slot. */
ARCH_SETUP_TLS ();
/* In some architectures, IREL{,A} relocations happen after TLS setup in
order to let IFUNC resolvers benefit from TCB information, e.g. powerpc's
hwcap and platform fields available in the TCB. */
ARCH_APPLY_IREL ();
/* Set up the stack checker's canary. */
/* Set up the stack checker's canary. Must happen before any IFUNC resolver
runs so a resolver compiled with stack protection loads a defined
canary. */
uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
# ifdef THREAD_SET_STACK_GUARD
THREAD_SET_STACK_GUARD (stack_chk_guard);
@@ -292,18 +291,31 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
__stack_chk_guard = stack_chk_guard;
# endif
/* Initialize libpthread if linked in. */
if (__pthread_initialize_minimal != NULL)
__pthread_initialize_minimal ();
/* Set up the pointer guard value. */
uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random,
stack_chk_guard);
# ifdef THREAD_SET_POINTER_GUARD
THREAD_SET_POINTER_GUARD (pointer_chk_guard);
# else
__pointer_chk_guard_local = pointer_chk_guard;
# endif
/* We do not need the _dl_random value anymore. Scrub the AT_RANDOM
bytes and clear the pointer; on targets with an entropy source, refill
the bytes with fresh random data. */
_dl_reseed_random (&_dl_random);
/* Now that the TCB, canary, and pointer guard are in place, run the
deferred IFUNC relocations. For non-PIE static binaries this is
ARCH_SETUP_IREL (apply_irel); for static-pie it is the IRELATIVE
phase of _dl_relocate_static_pie above. */
_dl_relocate_static_pie_ifunc ();
ARCH_SETUP_IREL ();
/* This must run after the IFUNC relocations: _dl_executable_postprocess
may call IFUNC-resolved routines. */
struct link_map *main_map = _dl_get_dl_main_map ();
_dl_executable_postprocess (main_map, GL(dl_phdr), GL(dl_phnum));
/* Initialize libpthread if linked in. */
if (__pthread_initialize_minimal != NULL)
__pthread_initialize_minimal ();
#endif /* !SHARED */
+5
View File
@@ -23,4 +23,9 @@ void
_dl_relocate_static_pie (void)
{
}
void
_dl_relocate_static_pie_ifunc (void)
{
}
#endif
+4
View File
@@ -67,6 +67,7 @@ tests = \
glrefmain \
tst-dladdr \
tst-dlinfo \
tst-dlsym-ctor \
tst-rec-dlopen \
tstatexit \
tstcxaatexit \
@@ -90,6 +91,7 @@ modules-names = \
modcxaatexit \
moddummy1 \
moddummy2 \
tst-dlsym-ctormod \
# modules-names
failtestmod.so-no-z-defs = yes
@@ -197,3 +199,5 @@ $(objpfx)bug-dl-leaf.out: $(objpfx)bug-dl-leaf-lib-cb.so
$(objpfx)bug-dl-leaf-lib-cb.so: $(objpfx)bug-dl-leaf-lib.so
$(objpfx)tst-rec-dlopen.out: $(objpfx)moddummy1.so $(objpfx)moddummy2.so
$(objpfx)tst-dlsym-ctor.out: $(objpfx)tst-dlsym-ctormod.so
+36
View File
@@ -0,0 +1,36 @@
/* Test that a tail-called dlsym from a constructor works.
Copyright The GNU Toolchain Authors.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <dlfcn.h>
#include <support/check.h>
#include <support/xdlfcn.h>
static int
do_test (void)
{
/* Loading the module runs its constructor, which performs a dlsym
whose result is discarded. Under optimization the compiler lowers
that to a tail call, so dlsym sees a caller address inside the
dynamic linker itself. Before the fix that resolved to the ld.so
link map, which has no l_scope, and the lookup crashed. */
void *h = xdlopen ("tst-dlsym-ctormod.so", RTLD_NOW);
xdlclose (h);
return 0;
}
#include <support/test-driver.c>
+28
View File
@@ -0,0 +1,28 @@
/* Module for tst-dlsym-ctor.
Copyright The GNU Toolchain Authors.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <dlfcn.h>
/* The result is intentionally discarded so the compiler can lower the
dlsym call to a tail call. That is the trigger for the bug -- see
tst-dlsym-ctor.c. */
__attribute__ ((constructor)) static void
ctor (void)
{
(void) dlsym (RTLD_DEFAULT, "tst_dlsym_ctor_no_such_symbol");
}
+153 -2
View File
@@ -78,6 +78,7 @@ dl-routines = \
dl-reloc \
dl-runtime \
dl-scope \
dl-scratch-buffer \
dl-setup_hash \
dl-sort-maps \
dl-thread_gscope_wait \
@@ -269,6 +270,7 @@ tests-static-normal := \
tst-env-setuid-static \
tst-getauxval-static \
tst-linkall-static \
tst-ptrguard-static-dlopen \
tst-single_threaded-pthread-static \
tst-single_threaded-static \
tst-tls-allocation-failure-static \
@@ -277,6 +279,7 @@ tests-static-normal := \
# tests-static-normal
tests-static-internal := \
tst-atrandom-scrub-static \
tst-dl-printf-static \
tst-dl_find_object-static \
tst-env-setuid-tunables \
@@ -288,10 +291,20 @@ tests-static-internal := \
tst-tunables-enable_secure \
# tests-static-internal
ifeq (yes,$(have-gcc-ifunc))
tests-static-internal += \
tst-ifunc-resolver-protector-static \
tst-ifunc-resolver-protector-static-non-pie \
# tests-static-internal
endif
CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
tst-tls1-static-non-pie-no-pie = yes
CRT-tst-ifunc-resolver-protector-static-non-pie := $(csu-objpfx)crt1.o
tst-ifunc-resolver-protector-static-non-pie-no-pie = yes
tests-container := \
tst-dl-cache-long-path \
tst-ldconfig-bad-aux-cache \
tst-ldconfig-ld_so_conf-update \
# tests-container
@@ -404,9 +417,13 @@ tests += \
tst-auxobj \
tst-auxobj-dlopen \
tst-big-note \
tst-bz26577 \
tst-bz26577-minstack \
tst-debug1 \
tst-deep1 \
tst-dl-is_dso \
tst-dl-llp-stack \
tst-dl-path-buf \
tst-dlclose-lazy \
tst-dlmodcount \
tst-dlmopen-dlerror \
@@ -429,6 +446,7 @@ tests += \
tst-dlopenfail-2 \
tst-dlopenrpath \
tst-dlsym-error \
tst-dst-needed-minstack \
tst-filterobj \
tst-filterobj-dlopen \
tst-glibc-hwcaps \
@@ -531,6 +549,7 @@ tests-internal += \
neededtest2 \
neededtest3 \
neededtest4 \
tst-atrandom-scrub \
tst-audit19a \
tst-create_format1 \
tst-dl-hwcaps_split \
@@ -560,6 +579,7 @@ tests-container += \
tst-dlopen-tlsmodid-container \
tst-pldd \
tst-preload-pthread-libc \
tst-ptrguard-static-dlopen \
tst-rootdir \
# tests-container
@@ -700,6 +720,7 @@ ifeq (yes,$(build-shared))
ifneq ($(PERL),no)
tests-special += \
$(objpfx)noload-mem.out \
$(objpfx)tst-dl-path-buf-mem.out \
$(objpfx)tst-leaks1-mem.out \
# tests-special
endif
@@ -736,6 +757,19 @@ test-extras += \
tst-tlsmod17a \
tst-tlsmod18a \
# test-extras
ifeq (yes,$(have-gcc-ifunc))
# The resolver helper needs <tls.h> for the TCB-canary STACK_CHK_GUARD
# macro, so it must be compiled with MODULE_NAME=testsuite_internal.
extra-test-objs += \
tst-ifunc-resolver-protector-static-mod.o \
tst-ifunc-resolver-protector-static-non-pie-mod.o \
# extra-test-objs
test-internal-extras += \
tst-ifunc-resolver-protector-static-mod \
tst-ifunc-resolver-protector-static-non-pie-mod \
# test-internal-extras
endif
modules-names += \
circlemod1 \
circlemod1a \
@@ -909,6 +943,7 @@ modules-names += \
tst-deep1mod1 \
tst-deep1mod2 \
tst-deep1mod3 \
tst-dl-path-buf-mod \
tst-dl_find_object-mod1 \
tst-dl_find_object-mod2 \
tst-dl_find_object-mod3 \
@@ -939,6 +974,8 @@ modules-names += \
tst-dlopenfailmod3 \
tst-dlopenfailnodelmod \
tst-dlopenrpathmod \
tst-dst-needed-leaf-mod \
tst-dst-needed-wrap-mod \
tst-filterobj-aux \
tst-filterobj-filtee \
tst-filterobj-flt \
@@ -980,6 +1017,7 @@ modules-names += \
tst-null-argv-lib \
tst-p_alignmod-base \
tst-p_alignmod3 \
tst-ptrguard-static-dlopen-mod \
tst-recursive-tlsmallocmod \
tst-recursive-tlsmod0 \
tst-recursive-tlsmod1 \
@@ -1223,6 +1261,7 @@ modules-names-nobuild += \
tst-audit24bmod1 \
tst-audit24bmod2 \
tst-big-note-lib \
tst-bz26577-mod \
tst-nodeps1-mod \
tst-nodeps2-mod \
tst-ro-dynamic-mod \
@@ -1244,7 +1283,11 @@ tests-ifuncstatic := \
ifuncmain7static \
# tests-ifuncstatic
ifeq (yes,$(have-gcc-ifunc))
tests-ifuncstatic += ifuncmain9static ifuncmain9picstatic
tests-ifuncstatic += \
ifuncmain9picstatic \
ifuncmain9static \
tst-ifunc-bz28817 \
# tests-ifuncstatic
endif
tests-static += $(tests-ifuncstatic)
tests-internal += $(tests-ifuncstatic)
@@ -1252,6 +1295,15 @@ ifeq (yes,$(build-shared))
tests += \
tst-ifunc-fault-bindnow \
tst-ifunc-fault-lazy \
tst-ifunc-plt \
tst-ifunc-plt-bindnow \
tst-ifunc-plt-dlopen \
tst-ifunc-plt-dlopen-bindnow \
tst-ifunc-resolver-protector \
tst-ifunc-tls-init \
tst-ifunc-tls-init-gd-ld \
tst-ifunc-tls-write \
tst-tls-tdata-reloc \
# tests
# Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
tests-internal += \
@@ -1314,7 +1366,23 @@ modules-names += \
ifuncmod1 \
ifuncmod3 \
ifuncmod6 \
tst-ifunc-plt-dep \
tst-ifunc-plt-lib \
tst-ifunc-resolver-protector-mod \
tst-ifunc-tls-init-gd-global-lib \
tst-ifunc-tls-init-gd-lib \
tst-ifunc-tls-init-ld-lib \
tst-ifunc-tls-init-lib1 \
tst-ifunc-tls-init-lib2 \
tst-ifunc-tls-write-lib \
tst-tls-tdata-reloc-lib \
# modules-names
ifneq (no,$(have-test-mtls-descriptor))
tests += tst-ifunc-tls-init-tlsdesc
modules-names += tst-ifunc-tls-init-tlsdesc-lib
CFLAGS-tst-ifunc-tls-init-tlsdesc-lib.c += \
-mtls-dialect=$(have-test-mtls-descriptor)
endif
ifeq (no,$(with-lld))
modules-names += ifuncmod5
endif
@@ -1721,6 +1789,19 @@ $(objpfx)tst-nodelete-opened.out: $(objpfx)tst-nodelete-opened-lib.so
$(objpfx)tst-tlsalign-extern: $(objpfx)tst-tlsalign-vars.o
$(objpfx)tst-tlsalign-extern-static: $(objpfx)tst-tlsalign-vars.o
# The resolver translation unit must always be compiled with
# -fstack-protector-all so the canary code is emitted regardless of the
# default.
CFLAGS-tst-ifunc-resolver-protector-static-mod.c = -fstack-protector-all
CFLAGS-tst-ifunc-resolver-protector-static-non-pie-mod.c = -fstack-protector-all
CFLAGS-tst-ifunc-resolver-protector-mod.c = -fstack-protector-all
$(objpfx)tst-ifunc-resolver-protector-static: \
$(objpfx)tst-ifunc-resolver-protector-static-mod.o
$(objpfx)tst-ifunc-resolver-protector-static-non-pie: \
$(objpfx)tst-ifunc-resolver-protector-static-non-pie-mod.o
$(objpfx)tst-ifunc-resolver-protector: \
$(objpfx)tst-ifunc-resolver-protector-mod.so
tst-null-argv-ENV = LD_DEBUG=all LD_DEBUG_OUTPUT=$(objpfx)tst-null-argv.debug.out
LDFLAGS-nodel2mod3.so = -Wl,--no-as-needed
LDFLAGS-reldepmod5.so = -Wl,--no-as-needed
@@ -1793,6 +1874,7 @@ unload4mod1.so-no-z-defs = yes
ifuncmod1.so-no-z-defs = yes
ifuncmod5.so-no-z-defs = yes
ifuncmod6.so-no-z-defs = yes
tst-ifunc-plt-lib.so-no-z-defs = yes
tst-auditmod9a.so-no-z-defs = yes
tst-auditmod9b.so-no-z-defs = yes
tst-nodelete-uniquemod.so-no-z-defs = yes
@@ -2267,6 +2349,16 @@ CFLAGS-tst-dlopenrpath.c += -DPFX=\"$(objpfx)\"
LDFLAGS-tst-dlopenrpathmod.so += -Wl,-rpath,\$$ORIGIN/test-subdir
$(objpfx)tst-dlopenrpath.out: $(objpfx)firstobj.so
$(objpfx)tst-dl-path-buf: $(objpfx)tst-dl-path-buf-mod.so $(shared-thread-library)
LDFLAGS-tst-dl-path-buf += -Wl,-rpath,\$$ORIGIN/tst-dl-path-buf-subdir
tst-dl-path-buf-TUNABLES = glibc.mem.decorate_maps=1
tst-dl-path-buf-ENV = MALLOC_TRACE=$(objpfx)tst-dl-path-buf.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
$(objpfx)tst-dl-path-buf-mem.out: $(objpfx)tst-dl-path-buf.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-dl-path-buf.mtrace > $@; \
$(evaluate-test)
$(objpfx)tst-deep1mod2.so: $(objpfx)tst-deep1mod3.so
$(objpfx)tst-deep1: $(objpfx)tst-deep1mod1.so
$(objpfx)tst-deep1.out: $(objpfx)tst-deep1mod2.so
@@ -2330,6 +2422,9 @@ tst-ptrguard1-ARGS = --command "$(host-test-program-cmd) --child"
CFLAGS-tst-ptrguard1-static.c += -DPTRGUARD_LOCAL
tst-ptrguard1-static-ARGS = --command "$(objpfx)tst-ptrguard1-static --child"
# Likewise, the static pointer guard lives in __pointer_chk_guard_local.
CFLAGS-tst-atrandom-scrub-static.c += -DPTRGUARD_LOCAL
$(objpfx)tst-leaks1-mem.out: $(objpfx)tst-leaks1.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-leaks1.mtrace > $@; \
$(evaluate-test)
@@ -2426,6 +2521,39 @@ $(objpfx)tst-ifunc-fault-bindnow.out: $(objpfx)tst-ifunc-fault-bindnow \
$(objpfx)ld.so
$(tst-ifunc-fault-script)
LDFLAGS-tst-ifunc-plt-lib.so = -Wl,-z,lazy
tst-ifunc-plt-bindnow-ENV = LD_BIND_NOW=1
tst-ifunc-plt-dlopen-bindnow-ENV = LD_BIND_NOW=1
$(objpfx)tst-ifunc-plt-lib.so: $(objpfx)tst-ifunc-plt-dep.so
$(objpfx)tst-ifunc-plt: $(objpfx)tst-ifunc-plt-lib.so
$(objpfx)tst-ifunc-plt-bindnow: $(objpfx)tst-ifunc-plt-lib.so
$(objpfx)tst-ifunc-plt-dlopen.out: \
$(objpfx)tst-ifunc-plt-lib.so $(objpfx)tst-ifunc-plt-dep.so
$(objpfx)tst-ifunc-plt-dlopen-bindnow.out: \
$(objpfx)tst-ifunc-plt-lib.so $(objpfx)tst-ifunc-plt-dep.so
$(objpfx)tst-ifunc-tls-init: $(objpfx)tst-ifunc-tls-init-lib1.so
$(objpfx)tst-ifunc-tls-init.out: \
$(objpfx)tst-ifunc-tls-init-lib2.so
$(objpfx)tst-ifunc-tls-write: $(objpfx)tst-ifunc-tls-write-lib.so
$(objpfx)tst-tls-tdata-reloc: $(objpfx)tst-tls-tdata-reloc-lib.so
$(objpfx)tst-ifunc-tls-init-gd-ld: $(shared-thread-library)
$(objpfx)tst-ifunc-tls-init-gd-ld.out: \
$(objpfx)tst-ifunc-tls-init-gd-lib.so \
$(objpfx)tst-ifunc-tls-init-gd-global-lib.so \
$(objpfx)tst-ifunc-tls-init-ld-lib.so
tst-ifunc-tls-init-gd-ld-TUNABLES = glibc.rtld.optional_static_tls=0
ifneq (no,$(have-test-mtls-descriptor))
$(objpfx)tst-ifunc-tls-init-tlsdesc: $(shared-thread-library)
$(objpfx)tst-ifunc-tls-init-tlsdesc.out: \
$(objpfx)tst-ifunc-tls-init-tlsdesc-lib.so
tst-ifunc-tls-init-tlsdesc-TUNABLES = glibc.rtld.optional_static_tls=0
endif # $(have-test-mtls-descriptor)
$(objpfx)tst-unique1.out: $(objpfx)tst-unique1mod1.so \
$(objpfx)tst-unique1mod2.so
@@ -2714,7 +2842,7 @@ $(objpfx)tst-ldconfig-soname.out : tst-ldconfig-soname.sh \
# Test static linking of all the libraries we can possibly link
# together. Note that in some configurations this may be less than the
# complete list of libraries we build but we try to maxmimize this list.
# complete list of libraries we build but we try to maximize this list.
$(objpfx)tst-linkall-static: \
$(common-objpfx)math/libm.a \
$(common-objpfx)resolv/libresolv.a \
@@ -2758,6 +2886,16 @@ $(objpfx)tst-big-note: $(objpfx)tst-big-note-lib.so
$(objpfx)tst-big-note-lib.so: $(objpfx)tst-big-note-lib.o
$(LINK.o) -shared -o $@ $(LDFLAGS.so) $(dt-relr-ldflag) $<
tst-bz26577-ARGS = -- $(host-test-program-cmd)
$(objpfx)tst-bz26577.out: $(objpfx)tst-bz26577-mod.so
$(objpfx)tst-bz26577-mod.so: gen-tst-bz26577-mod.py $(..)/scripts/glibcelf.py \
$(objpfx)ld.so
PYTHONPATH=$(..)scripts $(PYTHON) $< $@ $(objpfx)ld.so
generated += tst-bz26577-mod.so
$(objpfx)tst-bz26577-minstack: $(shared-thread-library)
$(objpfx)tst-bz26577-minstack.out: $(objpfx)tst-bz26577-mod.so
$(objpfx)tst-unwind-ctor: $(objpfx)tst-unwind-ctor-lib.so
LDLIBS-tst-unwind-ctor += $(libunwind)
LDFLAGS-tst-unwind-ctor-lib.so = -Wl,--unresolved-symbols=ignore-all
@@ -2839,6 +2977,16 @@ LDFLAGS-tst-dlopen-nodelete-reloc-mod17.so = -Wl,--no-as-needed
$(objpfx)tst-ldconfig-ld_so_conf-update.out: $(objpfx)tst-ldconfig-ld-mod.so
# Reuses the trivial module already built for tst-dl-path-buf.
$(objpfx)tst-dl-cache-long-path: $(shared-thread-library)
$(objpfx)tst-dl-cache-long-path.out: $(objpfx)tst-dl-path-buf-mod.so
LDFLAGS-tst-dst-needed-leaf-mod.so = \
-Wl,-soname,\$$ORIGIN/\$$ORIGIN/\$$ORIGIN/\$$ORIGIN/\$$ORIGIN/leaf.so
$(objpfx)tst-dst-needed-wrap-mod.so: $(objpfx)tst-dst-needed-leaf-mod.so
$(objpfx)tst-dst-needed-minstack: $(shared-thread-library)
$(objpfx)tst-dst-needed-minstack.out: $(objpfx)tst-dst-needed-wrap-mod.so
LDFLAGS-tst-filterobj-flt.so = -Wl,--filter=$(objpfx)tst-filterobj-filtee.so
$(objpfx)tst-filterobj: $(objpfx)tst-filterobj-flt.so
$(objpfx)tst-filterobj.out: $(objpfx)tst-filterobj-filtee.so
@@ -3061,6 +3209,9 @@ $(objpfx)tst-tls21mod.so: $(tst-tls-many-dynamic-modules:%=$(objpfx)%.so)
$(objpfx)tst-getauxval-static.out: $(objpfx)tst-auxvalmod.so
tst-getauxval-static-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx)
$(objpfx)tst-ptrguard-static-dlopen.out: \
$(objpfx)tst-ptrguard-static-dlopen-mod.so
$(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so
$(objpfx)tst-ro-dynamic: $(objpfx)tst-ro-dynamic-mod.so
+9 -5
View File
@@ -21,6 +21,7 @@
#include <ldsodefs.h>
#include <sys/mman.h>
#include <dl-cache.h>
#include <dl-scratch-buffer.h>
#include <stdint.h>
#include <_itoa.h>
#include <dl-hwcaps.h>
@@ -490,12 +491,15 @@ _dl_load_cache_lookup (const char *name)
/* The double copy is *required* since malloc may be interposed
and call dlopen itself whose completion would unmap the data
we are accessing. Therefore we must make the copy of the
mapping data without using malloc. */
char *temp;
mapping data without using malloc. The DL_SCRATCH_NO_MALLOC
forces any spill to anonymous mmap rather than the malloc. */
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
size_t best_len = strlen (best) + 1;
temp = alloca (best_len);
memcpy (temp, best, best_len);
return __strdup (temp);
dl_scratch_buffer_allocate (&scratch, best_len, DL_SCRATCH_NO_MALLOC);
memcpy (scratch.data, best, best_len);
char *result = __strdup (scratch.data);
dl_scratch_buffer_free (&scratch);
return result;
}
#ifndef MAP_COPY
+1 -1
View File
@@ -485,7 +485,7 @@ _dl_close_worker (struct link_map *map, bool force)
size_t tls_free_end;
tls_free_start = tls_free_end = NO_TLS_OFFSET;
/* Protects global and module specitic TLS state. */
/* Protects global and module specific TLS state. */
__rtld_lock_lock_recursive (GL(dl_load_tls_lock));
/* We modify the list of loaded objects. */
+69 -47
View File
@@ -30,6 +30,7 @@
#include <scratch_buffer.h>
#include <dl-dst.h>
#include <dl-scratch-buffer.h>
/* Whether an shared object references one or more auxiliary objects
is signaled by the AUXTAG entry in l_info. */
@@ -80,47 +81,35 @@ struct list
};
/* Macro to expand DST. It is an macro since we use `alloca'. */
#define expand_dst(l, str, fatal) \
({ \
const char *__str = (str); \
const char *__result = __str; \
size_t __dst_cnt = _dl_dst_count (__str); \
\
if (__dst_cnt != 0) \
{ \
char *__newp; \
\
/* DST must not appear in SUID/SGID programs. */ \
if (__libc_enable_secure) \
_dl_signal_error (0, __str, NULL, N_("\
DST not allowed in SUID/SGID programs")); \
\
__newp = (char *) alloca (DL_DST_REQUIRED (l, __str, strlen (__str), \
__dst_cnt)); \
\
__result = _dl_dst_substitute (l, __str, __newp); \
\
if (*__result == '\0') \
{ \
/* The replacement for the DST is not known. We can't \
processed. */ \
if (fatal) \
_dl_signal_error (0, __str, NULL, N_("\
empty dynamic string token substitution")); \
else \
{ \
/* This is for DT_AUXILIARY. */ \
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) \
_dl_debug_printf (N_("\
cannot load auxiliary `%s' because of empty dynamic string token " \
"substitution\n"), __str); \
continue; \
} \
} \
} \
\
__result; })
/* Expand the dynamic-string-tokens ($ORIGIN / $LIB / $PLATFORM) in INPUT
using L's context. Returns the expanded string -- a pointer either into
SCRATCH->data (when expansion was needed) or back at INPUT (when no DSTs
were present, so no allocation happened). Returns NULL when a DST was
present but could not be resolved.
SCRATCH must be an init'd dl_scratch_buffer the caller will release once
the returned string is no longer needed. This function never returns when
called for a SUID/SGID that contains DSTs: it raises a loader error. */
static const char *
expand_dst (struct link_map *l, const char *input,
struct dl_scratch_buffer *scratch)
{
size_t dst_cnt = _dl_dst_count (input);
if (dst_cnt == 0)
return input;
/* DST must not appear in SUID/SGID programs. */
if (__libc_enable_secure)
_dl_signal_error (0, input, NULL, N_("\
DST not allowed in SUID/SGID programs"));
size_t total = DL_DST_REQUIRED (l, input, strlen (input), dst_cnt);
dl_scratch_buffer_allocate (scratch, total + 1, 0);
const char *result = _dl_dst_substitute (l, input, scratch->data);
if (*result == '\0')
return NULL;
return result;
}
static void
preload (struct list *known, unsigned int *nlist, struct link_map *map)
@@ -224,12 +213,26 @@ _dl_map_object_deps (struct link_map *map,
/* Map in the needed object. */
struct link_map *dep;
/* Recognize DSTs. */
name = expand_dst (l, strtab + d->d_un.d_val, 0);
/* Recognize DSTs. Empty substitution for DT_NEEDED is
non-fatal: log and skip this entry. */
struct dl_scratch_buffer scratch
= dl_scratch_buffer_init ();
name = expand_dst (l, strtab + d->d_un.d_val, &scratch);
if (__glibc_unlikely (name == NULL))
{
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
_dl_debug_printf (N_("\
cannot load auxiliary `%s' because of empty dynamic string token "
"substitution\n"),
strtab + d->d_un.d_val);
dl_scratch_buffer_free (&scratch);
continue;
}
/* Store the tag in the argument structure. */
args.name = name;
int err = _dl_catch_exception (&exception, openaux, &args);
dl_scratch_buffer_free (&scratch);
if (__glibc_unlikely (exception.errstring != NULL))
{
if (err)
@@ -267,9 +270,25 @@ _dl_map_object_deps (struct link_map *map,
{
struct list *newp;
/* Recognize DSTs. */
name = expand_dst (l, strtab + d->d_un.d_val,
d->d_tag == DT_AUXILIARY);
/* Recognize DSTs. DT_AUXILIARY is fatal on unresolved
DST; DT_FILTER is non-fatal and is skipped. */
struct dl_scratch_buffer scratch
= dl_scratch_buffer_init ();
name = expand_dst (l, strtab + d->d_un.d_val, &scratch);
if (__glibc_unlikely (name == NULL))
{
dl_scratch_buffer_free (&scratch);
if (d->d_tag == DT_AUXILIARY)
_dl_signal_error (0, strtab + d->d_un.d_val, NULL,
N_("empty dynamic string token "
"substitution"));
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
_dl_debug_printf (N_("\
cannot load auxiliary `%s' because of empty dynamic string token "
"substitution\n"),
strtab + d->d_un.d_val);
continue;
}
/* Store the tag in the argument structure. */
args.name = name;
@@ -285,6 +304,9 @@ _dl_map_object_deps (struct link_map *map,
object is not available. For filter objects the dependency
must be available. */
int err = _dl_catch_exception (&exception, openaux, &args);
/* NAME is consumed by openaux above; release the DST
scratch buffer regardless of outcome. */
dl_scratch_buffer_free (&scratch);
if (__glibc_unlikely (exception.errstring != NULL))
{
if (d->d_tag == DT_AUXILIARY)
@@ -535,7 +557,7 @@ _dl_map_object_deps (struct link_map *map,
/* Copy the binary into position 0. */
l_initfini[0] = map->l_searchlist.r_list[map_index];
/* Copy the filtees. */
/* Copy the filters. */
for (i = 0; i < map_index; ++i)
l_initfini[i+1] = map->l_searchlist.r_list[i];
+2 -2
View File
@@ -465,7 +465,7 @@ _dl_find_object (void *pc1, struct dl_find_object *result)
}
rtld_hidden_def (_dl_find_object)
/* Subroutine of _dlfo_process_initial to split out noncontigous link
/* Subroutine of _dlfo_process_initial to split out noncontiguous link
maps. NODELETE is the number of used _dlfo_nodelete_mappings
elements. It is incremented as needed, and the new NODELETE value
is returned. */
@@ -586,7 +586,7 @@ _dl_find_object_init (void)
{
/* Non-contiguous main maps are handled in
_dlfo_process_initial. Mark as initialized, but not
coverying any valid PC. */
covering any valid PC. */
_dlfo_main.map_start = -1;
_dlfo_main.map_end = -1;
}
+277 -234
View File
@@ -33,31 +33,7 @@
#include <sys/types.h>
#include <gnu/lib-names.h>
#include <dl-tunables.h>
/* Type for the buffer we put the ELF header and hopefully the program
header. This buffer does not really have to be too large. In most
cases the program header follows the ELF header directly. If this
is not the case all bets are off and we can make the header
arbitrarily large and still won't get it read. This means the only
question is how large are the ELF and program header combined. The
ELF header 32-bit files is 52 bytes long and in 64-bit files is 64
bytes long. Each program header entry is again 32 and 56 bytes
long respectively. I.e., even with a file which has 10 program
header entries we only have to read 372B/624B respectively. Add to
this a bit of margin for program notes and reading 512B and 832B
for 32-bit and 64-bit files respectively is enough. If this
heuristic should really fail for some file the code in
`_dl_map_object_from_fd' knows how to recover. */
struct filebuf
{
ssize_t len;
#if __WORDSIZE == 32
# define FILEBUF_SIZE 512
#else
# define FILEBUF_SIZE 832
#endif
char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
};
#include <dl-scratch-buffer.h>
#include "dynamic-link.h"
#include "get-dynamic-info.h"
@@ -121,7 +97,9 @@ is_trusted_path_normalize (const char *path, size_t len)
if (len == 0)
return false;
char *npath = (char *) alloca (len + 2);
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
dl_scratch_buffer_allocate (&scratch, len + 2, 0);
char *npath = scratch.data;
char *wnp = npath;
while (*path != '\0')
{
@@ -156,19 +134,24 @@ is_trusted_path_normalize (const char *path, size_t len)
if (wnp == npath || wnp[-1] != '/')
*wnp++ = '/';
bool result = false;
const char *trun = system_dirs;
for (size_t idx = 0; idx < nsystem_dirs_len; ++idx)
{
if (wnp - npath >= system_dirs_len[idx]
&& memcmp (trun, npath, system_dirs_len[idx]) == 0)
/* Found it. */
return true;
{
/* Found it. */
result = true;
break;
}
trun += system_dirs_len[idx] + 1;
}
return false;
dl_scratch_buffer_free (&scratch);
return result;
}
/* Given a substring starting at INPUT, just after the DST '$' start
@@ -439,7 +422,10 @@ struct r_search_path_struct __rtld_search_dirs attribute_relro;
static size_t max_dirnamelen;
static struct r_search_path_elem **
/* Tokenize RPATH (in place) and populate RESULT with one entry per non-empty
directory. Returns false if a per-entry allocation fails, leaving the
caller responsible for signaling any error. */
static bool
fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
const char *what, const char *where, struct link_map *l)
{
@@ -507,8 +493,10 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
malloc (sizeof (*dirp) + ncapstr * sizeof (enum r_dir_status)
+ where_len + len + 1);
if (dirp == NULL)
_dl_signal_error (ENOMEM, NULL, NULL,
N_("cannot create cache for search path"));
{
free (to_free);
return false;
}
dirp->dirname = ((char *) dirp + sizeof (*dirp)
+ ncapstr * sizeof (enum r_dir_status));
@@ -545,7 +533,7 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
/* Terminate the array. */
result[nelems] = NULL;
return result;
return true;
}
@@ -626,7 +614,13 @@ decompose_rpath (struct r_search_path_struct *sps,
_dl_signal_error (ENOMEM, NULL, NULL, errstring);
}
fillin_rpath (copy, result, ":", what, where, l);
if (!fillin_rpath (copy, result, ":", what, where, l))
{
free (copy);
free (result);
errstring = N_("cannot create cache for search path");
goto signal_error;
}
/* Free the copied RPATH string. `fillin_rpath' make own copies if
necessary. */
@@ -799,12 +793,13 @@ _dl_init_paths (const char *llp, const char *source,
if (llp != NULL && *llp != '\0')
{
char *llp_tmp = strdupa (llp);
/* Decompose the LD_LIBRARY_PATH contents. First determine how many
elements it has. */
/* Count entries directly off the const LD_LIBRARY_PATH so the
search-path dirs array can be allocated before the scratch buffer is
live; that way an OOM on either of the two heap allocations the
loader controls (the dirs array or the per-entry malloc inside
fillin_rpath) is signalled after the scratch has been released. */
size_t nllp = 1;
for (const char *cp = llp_tmp; *cp != '\0'; ++cp)
for (const char *cp = llp; *cp != '\0'; ++cp)
if (*cp == ':' || *cp == ';')
++nllp;
@@ -816,8 +811,25 @@ _dl_init_paths (const char *llp, const char *source,
goto signal_error;
}
(void) fillin_rpath (llp_tmp, __rtld_env_path_list.dirs, ":;",
source, NULL, l);
/* fillin_rpath needs a mutable copy because __strsep punches NULs
into it as it tokenizes. */
size_t llp_len = strlen (llp);
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
dl_scratch_buffer_allocate (&scratch, llp_len + 1, 0);
char *llp_tmp = memcpy (scratch.data, llp, llp_len + 1);
bool ok = fillin_rpath (llp_tmp, __rtld_env_path_list.dirs, ":;",
source, NULL, l);
dl_scratch_buffer_free (&scratch);
if (!ok)
{
free (__rtld_env_path_list.dirs);
__rtld_env_path_list.dirs = NULL;
errstring = N_("cannot create cache for search path");
goto signal_error;
}
if (__rtld_env_path_list.dirs[0] == NULL)
{
@@ -935,6 +947,167 @@ _dl_notify_new_object (int mode, Lmid_t nsid, struct link_map *l)
#endif
}
/* Initialize the PT_LOAD iterator IT for reading program headers from FD
at file offset PHOFF with PHNUM entries. FBP is used as scratch space
for batched program-header reads; if open_verify's initial read into
FBP->buf already covers the whole phdr table, the iterator runs
entirely from that buffer without any further pread. Zeros all
precomputed fields so the caller's scan loop can fill them in. */
static void
_dl_pt_load_iterator_init (struct dl_pt_load_iterator *it, int fd,
struct filebuf *fbp, ElfW(Off) phoff,
uint16_t phnum)
{
it->fd = fd;
it->fbp = fbp;
it->phoff = phoff;
it->phnum = phnum;
it->idx = 0;
it->pagesize = GLRO (dl_pagesize);
it->p_align_max = 0;
it->nloadcmds = 0;
it->first_mapstart = 0;
it->last_mapstart = 0;
it->last_allocend = 0;
it->cached = (phoff + (ElfW(Off)) phnum * sizeof (ElfW(Phdr))
<= (ElfW(Off)) fbp->len);
it->buf_base = 0;
it->buf_count = it->cached ? phnum : 0;
}
/* Scan all program headers from IT->fd, using the iterator's filebuf as a
scratch buffer for batched reads (skipped entirely if open_verify
already read the whole table). Fills in IT's precomputed PT_LOAD
metadata and collects segment attributes into L. Returns NULL on
success, or an error message string on failure; sets *ERRVALP to errno
for I/O errors, 0 otherwise. */
static const char *
_dl_map_object_scan_phdrs (struct dl_pt_load_iterator *it,
struct link_map *l, int mode,
unsigned int *stack_flagsp, bool *has_holesp,
bool *empty_dynamicp, int *errvalp)
{
ElfW(Addr) prev_mapend = 0;
struct dl_machine_phdr_info minfo;
elf_machine_phdr_info_init (&minfo);
for (ElfW(Half) i = 0; i < it->phnum; i++)
{
const ElfW(Phdr) *ph = _dl_pt_load_iterator_phdr_at (it, i);
if (__glibc_unlikely (ph == NULL))
{
*errvalp = errno;
return N_("cannot read file data");
}
elf_machine_phdr_collect (&minfo, ph);
switch (ph->p_type)
{
case PT_LOAD:
{
if (__glibc_unlikely (((ph->p_vaddr - ph->p_offset)
& (it->pagesize - 1)) != 0))
{
*errvalp = 0;
return N_("ELF load command address/offset not page-aligned");
}
ElfW(Addr) mapstart = ALIGN_DOWN (ph->p_vaddr, it->pagesize);
ElfW(Addr) mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz,
it->pagesize);
ElfW(Off) mapoff = ALIGN_DOWN (ph->p_offset, it->pagesize);
int prot = pf_to_prot (ph->p_flags);
if (powerof2 (ph->p_align) && ph->p_align > it->p_align_max)
it->p_align_max = ph->p_align;
it->p_align_max = _dl_map_segment_align (&(struct loadcmd) {
.mapstart = mapstart,
.mapend = mapend,
.mapoff = mapoff,
.prot = prot },
it->p_align_max);
if (it->nloadcmds > 0 && prev_mapend != mapstart)
*has_holesp = true;
prev_mapend = mapend;
if (it->nloadcmds == 0)
it->first_mapstart = mapstart;
it->last_mapstart = mapstart;
it->last_allocend = ph->p_vaddr + ph->p_memsz;
it->nloadcmds++;
}
break;
/* These entries tell us where to find things once the file's
segments are mapped in. We record the addresses it says
verbatim, and later correct for the run-time load address. */
case PT_DYNAMIC:
if (ph->p_filesz == 0)
*empty_dynamicp = true; /* Usually separate debuginfo. */
else
{
/* Debuginfo only files from "objcopy --only-keep-debug"
contain a PT_DYNAMIC segment with p_filesz == 0. Skip
such a segment to avoid a crash later. */
l->l_ld = (void *) ph->p_vaddr;
l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
l->l_ld_readonly = (ph->p_flags & PF_W) == 0;
}
break;
case PT_PHDR:
l->l_phdr = (void *) ph->p_vaddr;
break;
case PT_TLS:
if (ph->p_memsz == 0)
/* Nothing to do for an empty segment. */
break;
l->l_tls_blocksize = ph->p_memsz;
l->l_tls_align = ph->p_align;
if (ph->p_align == 0)
l->l_tls_firstbyte_offset = 0;
else
l->l_tls_firstbyte_offset = ph->p_vaddr & (ph->p_align - 1);
l->l_tls_initimage_size = ph->p_filesz;
/* Since we don't know the load address yet only store the
offset. We will adjust it later. */
l->l_tls_initimage = (void *) ph->p_vaddr;
/* l->l_tls_modid is assigned below, once there is no
possibility for failure. */
if (l->l_type != lt_library
&& GL(dl_tls_dtv_slotinfo_list) == NULL)
{
#ifdef SHARED
/* We are loading the executable itself when the dynamic
linker was executed directly. The setup will happen
later. */
assert (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0);
#else
assert (false && "TLS not initialized in static application");
#endif
}
break;
case PT_GNU_STACK:
*stack_flagsp = pf_to_prot (ph->p_flags);
break;
case PT_GNU_RELRO:
l->l_relro_addr = ph->p_vaddr;
l->l_relro_size = ph->p_memsz;
break;
}
}
if (__glibc_unlikely (elf_machine_reject_phdr_p (&minfo, l, it->fd)))
{
*errvalp = 0;
return N_("ELF file incompatible with this system");
}
return NULL;
}
/* Map in the shared object NAME, actually located in REALNAME, and already
opened on FD. */
@@ -948,8 +1121,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
const void *stack_endp, Lmid_t nsid)
{
struct link_map *l = NULL;
const ElfW(Ehdr) *header;
const ElfW(Phdr) *phdr;
ElfW(Ehdr) header;
const ElfW(Phdr) *ph;
size_t maplength;
int type;
@@ -1059,8 +1231,10 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
_dl_debug_printf ("file=%s [%lu]; generating link map\n", name, nsid);
/* This is the ELF header. We read it in `open_verify'. */
header = (void *) fbp->buf;
/* The ELF header is already validate in `open_verify', make a local copy
because _dl_map_object_scan_phdrs may overwrite fbp->buf when reading
phdrs via pread in the slow path. */
memcpy (&header, fbp->buf, sizeof header);
/* Enter the new object in the list of loaded objects. */
l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
@@ -1072,26 +1246,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
errstring = N_("cannot create shared object descriptor");
goto lose_errno;
}
/* Extract the remaining details we need from the ELF header
and then read in the program header table. */
l->l_entry = header->e_entry;
type = header->e_type;
l->l_phnum = header->e_phnum;
maplength = header->e_phnum * sizeof (ElfW(Phdr));
if (header->e_phoff + maplength <= (size_t) fbp->len)
phdr = (void *) (fbp->buf + header->e_phoff);
else
{
phdr = alloca (maplength);
if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
header->e_phoff) != maplength)
{
errstring = N_("cannot read file data");
goto lose_errno;
}
}
l->l_entry = header.e_entry;
type = header.e_type;
l->l_phnum = header.e_phnum;
/* On most platforms presume that PT_GNU_STACK is absent and the stack is
* executable. Other platforms default to a nonexecutable stack and don't
@@ -1099,140 +1256,29 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
unsigned int stack_flags = DEFAULT_STACK_PROT_PERMS;
{
/* Scan the program header table, collecting its load commands. */
struct loadcmd loadcmds[l->l_phnum];
size_t nloadcmds = 0;
bool has_holes = false;
/* Single pass over the program header table: initialize the PT_LOAD
iterator (precomputing p_align_max, has_holes, and first/last segment
metadata) and collect all other segment attributes simultaneously.
Program headers are read in chunks into fbp->buf via pread so that
no large stack buffer is needed regardless of e_phnum. */
struct dl_pt_load_iterator it;
bool has_holes;
bool empty_dynamic = false;
ElfW(Addr) p_align_max = 0;
/* The struct is initialized to zero so this is not necessary:
l->l_ld = 0;
l->l_phdr = 0;
l->l_addr = 0; */
for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph)
switch (ph->p_type)
{
/* These entries tell us where to find things once the file's
segments are mapped in. We record the addresses it says
verbatim, and later correct for the run-time load address. */
case PT_DYNAMIC:
if (ph->p_filesz == 0)
empty_dynamic = true; /* Usually separate debuginfo. */
else
{
/* Debuginfo only files from "objcopy --only-keep-debug"
contain a PT_DYNAMIC segment with p_filesz == 0. Skip
such a segment to avoid a crash later. */
l->l_ld = (void *) ph->p_vaddr;
l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
l->l_ld_readonly = (ph->p_flags & PF_W) == 0;
}
break;
_dl_pt_load_iterator_init (&it, fd, fbp, header.e_phoff, l->l_phnum);
has_holes = false;
case PT_PHDR:
l->l_phdr = (void *) ph->p_vaddr;
break;
errstring = _dl_map_object_scan_phdrs (&it, l, mode, &stack_flags,
&has_holes, &empty_dynamic, &errval);
if (__glibc_unlikely (errstring != NULL))
goto lose;
case PT_LOAD:
/* A load command tells us to map in part of the file.
We record the load commands and process them all later. */
if (__glibc_unlikely (((ph->p_vaddr - ph->p_offset)
& (GLRO(dl_pagesize) - 1)) != 0))
{
errstring
= N_("ELF load command address/offset not page-aligned");
goto lose;
}
struct loadcmd *c = &loadcmds[nloadcmds++];
c->mapstart = ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize));
c->mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz, GLRO(dl_pagesize));
c->dataend = ph->p_vaddr + ph->p_filesz;
c->allocend = ph->p_vaddr + ph->p_memsz;
/* Remember the maximum p_align. */
if (powerof2 (ph->p_align) && ph->p_align > p_align_max)
p_align_max = ph->p_align;
c->mapoff = ALIGN_DOWN (ph->p_offset, GLRO(dl_pagesize));
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (11, 0)
/* Suppress invalid GCC warning:
(((char *)loadcmds.113_68 + _933 + 16))[329406144173384849].mapend may be used uninitialized [-Wmaybe-uninitialized]
See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106008
*/
DIAG_IGNORE_NEEDS_COMMENT_GCC (11, "-Wmaybe-uninitialized");
#endif
/* Determine whether there is a gap between the last segment
and this one. */
if (nloadcmds > 1 && c[-1].mapend != c->mapstart)
has_holes = true;
DIAG_POP_NEEDS_COMMENT;
/* Optimize a common case. */
c->prot = pf_to_prot (ph->p_flags);
/* Architecture-specific adjustment of segment alignment. */
p_align_max = _dl_map_segment_align (c, p_align_max);
break;
case PT_TLS:
if (ph->p_memsz == 0)
/* Nothing to do for an empty segment. */
break;
l->l_tls_blocksize = ph->p_memsz;
l->l_tls_align = ph->p_align;
if (ph->p_align == 0)
l->l_tls_firstbyte_offset = 0;
else
l->l_tls_firstbyte_offset = ph->p_vaddr & (ph->p_align - 1);
l->l_tls_initimage_size = ph->p_filesz;
/* Since we don't know the load address yet only store the
offset. We will adjust it later. */
l->l_tls_initimage = (void *) ph->p_vaddr;
/* l->l_tls_modid is assigned below, once there is no
possibility for failure. */
if (l->l_type != lt_library
&& GL(dl_tls_dtv_slotinfo_list) == NULL)
{
#ifdef SHARED
/* We are loading the executable itself when the dynamic
linker was executed directly. The setup will happen
later. */
assert (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0);
#else
assert (false && "TLS not initialized in static application");
#endif
}
break;
case PT_GNU_STACK:
stack_flags = pf_to_prot (ph->p_flags);
break;
case PT_GNU_RELRO:
l->l_relro_addr = ph->p_vaddr;
l->l_relro_size = ph->p_memsz;
break;
}
if (__glibc_unlikely (nloadcmds == 0))
if (__glibc_unlikely (it.nloadcmds == 0))
{
/* This only happens for a bogus object that will be caught with
another error below. But we don't want to go through the
calculations below using NLOADCMDS - 1. */
errstring = N_("object file has no loadable segments");
goto lose;
}
/* Align all PT_LOAD segments to the maximum p_align. */
for (size_t i = 0; i < nloadcmds; i++)
loadcmds[i].mapalign = p_align_max;
/* dlopen of an executable is not valid because it is not possible
to perform proper relocations, handle static TLS, or run the
ELF constructors. For PIE, the check needs the dynamic
@@ -1254,13 +1300,13 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
}
/* Length of the sections to be loaded. */
maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart;
maplength = it.last_allocend - it.first_mapstart;
/* Now process the load commands and map segments into memory.
This is responsible for filling in:
l_map_start, l_map_end, l_addr, l_contiguous, l_phdr
*/
errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds,
errstring = _dl_map_segments (l, fd, &header, type, &it,
maplength, has_holes, loader);
if (__glibc_unlikely (errstring != NULL))
{
@@ -1293,18 +1339,22 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
if (l->l_phdr == NULL)
{
/* The program header is not contained in any of the segments.
We have to allocate memory ourself and copy it over from out
temporary place. */
ElfW(Phdr) *newp = (ElfW(Phdr) *) malloc (header->e_phnum
* sizeof (ElfW(Phdr)));
Allocate memory and read the program header table from the file. */
size_t phdr_size = (size_t) header.e_phnum * sizeof (ElfW(Phdr));
ElfW(Phdr) *newp = (ElfW(Phdr) *) malloc (phdr_size);
if (newp == NULL)
{
errstring = N_("cannot allocate memory for program header");
goto lose_errno;
}
l->l_phdr = memcpy (newp, phdr,
(header->e_phnum * sizeof (ElfW(Phdr))));
if ((size_t) __pread64_nocancel (fd, newp, phdr_size,
header.e_phoff) != phdr_size)
{
free (newp);
errstring = N_("cannot read file data");
goto lose_errno;
}
l->l_phdr = newp;
l->l_phdr_allocated = 1;
}
else
@@ -1457,12 +1507,17 @@ cannot enable executable stack as shared object requires");
return l;
}
/* Print search path. */
/* Print search path. BUF is a scratch buffer provided by the caller;
it must be large enough to hold the longest "<dirname><capstr>" plus
a trailing NUL byte -- i.e. at least
max_dirnamelen + max_capstrlen + 1 bytes. open_path's path buffer
(max_dirnamelen + max_capstrlen + namelen, namelen >= 1) is reused
here so that enabling LD_DEBUG=libs does not require an extra mmap
per call. */
static void
print_search_path (struct r_search_path_elem **list,
const char *what, const char *name)
const char *what, const char *name, char *buf)
{
char buf[max_dirnamelen + max_capstrlen];
int first = 1;
_dl_debug_printf (" search path=");
@@ -1564,8 +1619,6 @@ open_verify (const char *name, int fd,
if (fd != -1)
{
ElfW(Ehdr) *ehdr;
ElfW(Phdr) *phdr;
size_t maplength;
/* We successfully opened the file. Now verify it is a file
we can use. */
@@ -1691,32 +1744,6 @@ open_verify (const char *name, int fd,
goto lose;
}
maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
if (ehdr->e_phoff + maplength <= (size_t) fbp->len)
phdr = (void *) (fbp->buf + ehdr->e_phoff);
else
{
phdr = alloca (maplength);
if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
ehdr->e_phoff) != maplength)
{
errval = errno;
errstring = N_("cannot read file data");
goto lose;
}
}
if (__glibc_unlikely (elf_machine_reject_phdr_p
(phdr, ehdr->e_phnum, fbp->buf, fbp->len,
loader, fd)))
{
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
_dl_debug_printf (" (incompatible ELF headers with the host)\n");
__close_nocancel (fd);
__set_errno (ENOENT);
return -1;
}
}
else
{
@@ -1740,7 +1767,6 @@ open_path (const char *name, size_t namelen, int mode,
bool *found_other_class)
{
struct r_search_path_elem **dirs = sps->dirs;
char *buf;
int fd = -1;
const char *current_what = NULL;
int any = 0;
@@ -1750,7 +1776,18 @@ open_path (const char *name, size_t namelen, int mode,
given on the command line when rtld is run directly. */
return -1;
buf = alloca (max_dirnamelen + max_capstrlen + namelen);
/* The scratch buffer below is sized to satisfy both this function's
candidate-path construction (max_dirnamelen + max_capstrlen + namelen)
and print_search_path's buffer precondition
(max_dirnamelen + max_capstrlen + 1). An empty NAME would under-size the
buffer for the latter and would also produce a meaningless lookup (the
loader rejects empty names well before reaching here). */
assert (namelen >= 1);
size_t bufsize = max_dirnamelen + max_capstrlen + namelen;
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
dl_scratch_buffer_allocate (&scratch, bufsize, 0);
char *buf = scratch.data;
do
{
struct r_search_path_elem *this_dir = *dirs;
@@ -1765,7 +1802,7 @@ open_path (const char *name, size_t namelen, int mode,
&& current_what != this_dir->what)
{
current_what = this_dir->what;
print_search_path (dirs, current_what, this_dir->where);
print_search_path (dirs, current_what, this_dir->where, buf);
}
edp = (char *) __mempcpy (buf, this_dir->dirname, this_dir->dirnamelen);
@@ -1853,6 +1890,7 @@ open_path (const char *name, size_t namelen, int mode,
if (*realname != NULL)
{
memcpy (*realname, buf, buflen);
dl_scratch_buffer_free (&scratch);
return fd;
}
else
@@ -1860,6 +1898,7 @@ open_path (const char *name, size_t namelen, int mode,
/* No memory for the name, we certainly won't be able
to load and link it. */
__close_nocancel (fd);
dl_scratch_buffer_free (&scratch);
return -1;
}
}
@@ -1869,7 +1908,10 @@ open_path (const char *name, size_t namelen, int mode,
directory (for instance, if the component is a existing file meaning
essentially that the pathname is invalid - ENOTDIR). */
if (here_any && errno != ENOENT && errno != EACCES && errno != ENOTDIR)
return -1;
{
dl_scratch_buffer_free (&scratch);
return -1;
}
/* Remember whether we found anything. */
any |= here_any;
@@ -1890,6 +1932,7 @@ open_path (const char *name, size_t namelen, int mode,
sps->dirs = (void *) -1;
}
dl_scratch_buffer_free (&scratch);
return -1;
}
+124 -2
View File
@@ -21,7 +21,39 @@
#define _DL_LOAD_H 1
#include <link.h>
#include <stddef.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <libc-pointer-arith.h>
#include <stackinfo.h>
#include <not-cancel.h>
/* Type for the buffer we put the ELF header and hopefully the program
header. This buffer does not really have to be too large. In most
cases the program header follows the ELF header directly. If this
is not the case all bets are off and we can make the header
arbitrarily large and still won't get it read. This means the only
question is how large are the ELF and program header combined. The
ELF header for 32-bit files is 52 bytes long and for 64-bit files
64 bytes long. Each program header entry is 32 and 56 bytes long
respectively.
Size for at least 16 entries (with a little margin for program notes)
needs 52 + 16*32 = 564 bytes on 32-bit and 64 + 16*56 = 960 bytes on
64-bit; round up to 640 and 1024 respectively. If this heuristic
should still fail for some file the code in
`_dl_map_object_from_fd' knows how to recover. */
struct filebuf
{
ssize_t len;
#if __WORDSIZE == 32
# define FILEBUF_SIZE 640
#else
# define FILEBUF_SIZE 1024
#endif
char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
};
/* On some systems, no flag bits are given to specify file mapping. */
@@ -80,6 +112,95 @@ struct loadcmd
int prot; /* PROT_* bits. */
};
#include <dl-load-post.h>
/* Iterator for program header segments. Initialize with
_dl_pt_load_iterator_init, then either walk PT_LOAD segments via
_dl_pt_load_iterator_next or do random access via
_dl_pt_load_iterator_phdr_at. A scratch buffer (fbp->buf) is used to
batch-read program headers; if the entire program header table was
already loaded by open_verify's initial read no pread is issued. */
struct dl_pt_load_iterator
{
int fd; /* File descriptor for pread. */
struct filebuf *fbp; /* Scratch buffer for batched phdr reads. */
ElfW(Off) phoff; /* Program header table file offset. */
ElfW(Half) phnum; /* Total number of program headers. */
ElfW(Half) idx; /* Index of next header to read. */
ElfW(Half) buf_base; /* Index of phdr at start of fbp->buf
(chunked mode only). */
ElfW(Half) buf_count; /* Number of phdrs currently in fbp->buf. */
bool cached; /* True iff entire phdr table is already
resident in fbp->buf from open_verify. */
ElfW(Addr) p_align_max; /* Maximum p_align over all PT_LOAD segments. */
ElfW(Addr) pagesize; /* System page size (GLRO(dl_pagesize)). */
/* Fields below are precomputed by _dl_map_object_scan_phdrs and are
intended for use by _dl_map_segments. */
ElfW(Addr) first_mapstart; /* mapstart of the first PT_LOAD segment. */
ElfW(Addr) last_mapstart; /* mapstart of the last PT_LOAD segment. */
ElfW(Addr) last_allocend; /* allocend of the last PT_LOAD segment. */
size_t nloadcmds; /* Number of PT_LOAD segments found. */
};
/* Return a pointer to the program header at INDEX. If the entire phdr
table is already cached in fbp->buf (from open_verify), it is served
directly with no syscall; otherwise a batch of up to FILEBUF_SIZE /
sizeof(ElfW(Phdr)) entries is read into fbp->buf via a single pread.
Subsequent calls within the same batch hit the buffer. Returns NULL on
read failure (errno set by pread). */
static __always_inline const ElfW(Phdr) *
_dl_pt_load_iterator_phdr_at (struct dl_pt_load_iterator *it, ElfW(Half) idx)
{
if (__glibc_likely (it->cached))
return (const ElfW(Phdr) *) (it->fbp->buf + it->phoff) + idx;
if (idx < it->buf_base || idx >= it->buf_base + it->buf_count)
{
const ElfW(Half) phdrs_per_buf
= sizeof (it->fbp->buf) / sizeof (ElfW(Phdr));
ElfW(Half) batch = it->phnum - idx;
if (batch > phdrs_per_buf)
batch = phdrs_per_buf;
size_t bytes = (size_t) batch * sizeof (ElfW(Phdr));
ElfW(Off) off = it->phoff + (ElfW(Off)) idx * sizeof (ElfW(Phdr));
if (__pread64_nocancel (it->fd, it->fbp->buf, bytes, off)
!= (ssize_t) bytes)
return NULL;
it->buf_base = idx;
it->buf_count = batch;
}
return (const ElfW(Phdr) *) it->fbp->buf + (idx - it->buf_base);
}
/* Advance iterator IT to the next PT_LOAD segment and fill C with its
decoded load command. Returns true when a segment was found, false
when the end of the program header table has been reached or a read
error occurs. */
static __always_inline bool
_dl_pt_load_iterator_next (struct dl_pt_load_iterator *it, struct loadcmd *c)
{
while (it->idx < it->phnum)
{
const ElfW(Phdr) *ph = _dl_pt_load_iterator_phdr_at (it, it->idx);
it->idx++;
if (__glibc_unlikely (ph == NULL))
return false;
if (ph->p_type != PT_LOAD)
continue;
c->mapstart = ALIGN_DOWN (ph->p_vaddr, it->pagesize);
c->mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz, it->pagesize);
c->dataend = ph->p_vaddr + ph->p_filesz;
c->allocend = ph->p_vaddr + ph->p_memsz;
c->mapoff = ALIGN_DOWN (ph->p_offset, it->pagesize);
c->prot = pf_to_prot (ph->p_flags);
c->mapalign = it->p_align_max;
return true;
}
return false;
}
/* This is a subroutine of _dl_map_segments. It should be called for each
load command, some time after L->l_addr has been set correctly. It is
@@ -95,6 +216,8 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
/* Found the program header in this segment. */
l->l_phdr = (void *) (uintptr_t) (c->mapstart + header->e_phoff
- c->mapoff);
_dl_postprocess_loadcmd_extra (l, c);
}
@@ -113,8 +236,7 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
static const char *_dl_map_segments (struct link_map *l, int fd,
const ElfW(Ehdr) *header, int type,
const struct loadcmd loadcmds[],
size_t nloadcmds,
struct dl_pt_load_iterator *it,
const size_t maplength,
bool has_holes,
struct link_map *loader); */
+29 -5
View File
@@ -21,12 +21,36 @@
#include <stdbool.h>
/* Return true iff ELF program headers are incompatible with the running
host. */
/* Machine-specific data collected during the program-header scan for use
by elf_machine_reject_phdr_p. Ports that override elf_machine_reject_phdr_p
must define their own layout; this generic version carries no data. */
struct dl_machine_phdr_info
{
};
/* Initialize INFO before the program-header scan begins. */
static inline void
elf_machine_phdr_info_init (struct dl_machine_phdr_info *info
__attribute__ ((__unused__)))
{
}
/* Called once per ELF program header PH during the scan. Records any
machine-specific data from PH that elf_machine_reject_phdr_p needs. */
static inline void
elf_machine_phdr_collect (struct dl_machine_phdr_info *info
__attribute__ ((__unused__)),
const ElfW(Phdr) *ph __attribute__ ((__unused__)))
{
}
/* Return true iff the program-header data collected in INFO is incompatible
with the running host. */
static inline bool
elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, unsigned int phnum,
const char *buf, size_t len, struct link_map *map,
int fd)
elf_machine_reject_phdr_p (const struct dl_machine_phdr_info *info
__attribute__ ((__unused__)),
struct link_map *map __attribute__ ((__unused__)),
int fd __attribute__ ((__unused__)))
{
return false;
}
+29 -28
View File
@@ -75,11 +75,14 @@ _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref,
static __always_inline const char *
_dl_map_segments (struct link_map *l, int fd,
const ElfW(Ehdr) *header, int type,
const struct loadcmd loadcmds[], size_t nloadcmds,
struct dl_pt_load_iterator *it,
const size_t maplength, bool has_holes,
struct link_map *loader)
{
const struct loadcmd *c = loadcmds;
/* Fetch the first PT_LOAD segment. _dl_pt_load_iterator_init already
verified nloadcmds > 0, so this call always succeeds. */
struct loadcmd c = { 0 };
_dl_pt_load_iterator_next (it, &c);
if (__glibc_likely (type == ET_DYN))
{
@@ -95,16 +98,16 @@ _dl_map_segments (struct link_map *l, int fd,
prefer to map such objects at; but this is only a preference,
the OS can do whatever it likes. */
ElfW(Addr) mappref
= (ELF_PREFERRED_ADDRESS (loader, maplength, c->mapstart)
= (ELF_PREFERRED_ADDRESS (loader, maplength, c.mapstart)
- MAP_BASE_ADDR (l));
/* Remember which part of the address space this object uses. */
l->l_map_start = _dl_map_segment (c, mappref, maplength, fd);
l->l_map_start = _dl_map_segment (&c, mappref, maplength, fd);
if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
l->l_map_end = l->l_map_start + maplength;
l->l_addr = l->l_map_start - c->mapstart;
l->l_addr = l->l_map_start - c.mapstart;
if (has_holes)
{
@@ -113,12 +116,11 @@ _dl_map_segments (struct link_map *l, int fd,
unallocated. Then jump into the normal segment-mapping loop to
handle the portion of the segment past the end of the file
mapping. */
if (__glibc_unlikely (loadcmds[nloadcmds - 1].mapstart <
c->mapend))
return N_("ELF load command address/offset not page-aligned");
if (__glibc_unlikely (it->last_mapstart < c.mapend))
return N_("ELF load command address/offset not page-aligned");
if (__glibc_unlikely
(__mprotect ((caddr_t) (l->l_addr + c->mapend),
loadcmds[nloadcmds - 1].mapstart - c->mapend,
(__mprotect ((caddr_t) (l->l_addr + c.mapend),
it->last_mapstart - c.mapend,
PROT_NONE) < 0))
return DL_MAP_SEGMENTS_ERROR_MPROTECT;
}
@@ -129,32 +131,32 @@ _dl_map_segments (struct link_map *l, int fd,
}
/* Remember which part of the address space this object uses. */
l->l_map_start = c->mapstart + l->l_addr;
l->l_map_start = c.mapstart + l->l_addr;
l->l_map_end = l->l_map_start + maplength;
l->l_contiguous = !has_holes;
while (c < &loadcmds[nloadcmds])
do
{
if (c->mapend > c->mapstart
if (c.mapend > c.mapstart
/* Map the segment contents from the file. */
&& (__mmap ((void *) (l->l_addr + c->mapstart),
c->mapend - c->mapstart, c->prot,
&& (__mmap ((void *) (l->l_addr + c.mapstart),
c.mapend - c.mapstart, c.prot,
MAP_FIXED|MAP_COPY|MAP_FILE,
fd, c->mapoff)
fd, c.mapoff)
== MAP_FAILED))
return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
postmap:
_dl_postprocess_loadcmd (l, header, c);
_dl_postprocess_loadcmd (l, header, &c);
if (c->allocend > c->dataend)
if (c.allocend > c.dataend)
{
/* Extra zero pages should appear at the end of this segment,
after the data mapped from the file. */
ElfW(Addr) zero, zeroend, zeropage;
zero = l->l_addr + c->dataend;
zeroend = l->l_addr + c->allocend;
zero = l->l_addr + c.dataend;
zeroend = l->l_addr + c.allocend;
zeropage = ((zero + GLRO(dl_pagesize) - 1)
& ~(GLRO(dl_pagesize) - 1));
@@ -166,18 +168,18 @@ _dl_map_segments (struct link_map *l, int fd,
if (zeropage > zero)
{
/* Zero the final part of the last page of the segment. */
if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
if (__glibc_unlikely ((c.prot & PROT_WRITE) == 0))
{
/* Dag nab it. */
if (__mprotect ((caddr_t) (zero
& ~(GLRO(dl_pagesize) - 1)),
GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0)
GLRO(dl_pagesize), c.prot|PROT_WRITE) < 0)
return DL_MAP_SEGMENTS_ERROR_MPROTECT;
}
memset ((void *) zero, '\0', zeropage - zero);
if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
if (__glibc_unlikely ((c.prot & PROT_WRITE) == 0))
__mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)),
GLRO(dl_pagesize), c->prot);
GLRO(dl_pagesize), c.prot);
}
if (zeroend > zeropage)
@@ -187,7 +189,7 @@ _dl_map_segments (struct link_map *l, int fd,
caddr_t mapat;
mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
c.prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
-1, 0);
if (__glibc_unlikely (mapat == MAP_FAILED))
return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL;
@@ -220,13 +222,12 @@ _dl_map_segments (struct link_map *l, int fd,
}
}
}
++c;
}
while (_dl_pt_load_iterator_next (it, &c));
/* Notify ELF_PREFERRED_ADDRESS that we have to load this one
fixed. */
ELF_FIXED_ADDRESS (loader, c->mapstart);
ELF_FIXED_ADDRESS (loader, it->last_mapstart);
return NULL;
}
+26 -27
View File
@@ -658,6 +658,31 @@ dl_open_worker_begin (void *a)
bool relocation_in_progress = false;
/* This only performs the memory allocations. The actual update of
the scopes happens below, after failure is impossible. */
resize_scopes (new);
/* Increase the size of the GL (dl_tls_dtv_slotinfo_list) data
structure. */
bool any_tls = resize_tls_slotinfo (new);
/* Perform the necessary allocations for adding new global objects
to the global scope below. */
if (mode & RTLD_GLOBAL)
add_to_global_resize (new);
/* Install the new modules in the DTV slotinfo and initialise their
static TLS *before* relocation, so an IFUNC resolver firing during
the relocation loop below can reach its DSO's __thread storage via
__tls_get_addr / TLSDESC. Without this, the resolver's TLS access
for a just-loaded module would index into an unallocated DTV slot
and crash. If relocation later fails, the subsequent _dl_close_worker
cleans up these slotinfo entries via remove_slotinfo. */
if (any_tls)
/* FIXME: This calls _dl_update_slotinfo, which aborts the process
on memory allocation failure. See bug 16134. */
update_tls_slotinfo (new);
/* Perform relocation. This can trigger lazy binding in IFUNC
resolvers. For NODELETE mappings, these dependencies are not
recorded because the flag has not been applied to the newly
@@ -682,19 +707,6 @@ dl_open_worker_begin (void *a)
_dl_open_relocate_one_object (args, r, new->l_initfini[i], reloc_mode,
&relocation_in_progress);
/* This only performs the memory allocations. The actual update of
the scopes happens below, after failure is impossible. */
resize_scopes (new);
/* Increase the size of the GL (dl_tls_dtv_slotinfo_list) data
structure. */
bool any_tls = resize_tls_slotinfo (new);
/* Perform the necessary allocations for adding new global objects
to the global scope below. */
if (mode & RTLD_GLOBAL)
add_to_global_resize (new);
/* Demarcation point: After this, no recoverable errors are allowed.
All memory allocations for new objects must have happened
before. */
@@ -716,19 +728,6 @@ dl_open_worker_begin (void *a)
_dl_signal_error (ENOMEM, new->l_libname->name, NULL,
N_ ("cannot allocate address lookup data"));
/* FIXME: It is unclear whether the order here is correct.
Shouldn't new objects be made available for binding (and thus
execution) only after there TLS data has been set up fully?
Fixing bug 16134 will likely make this distinction less
important. */
/* Second stage after resize_tls_slotinfo: Update the slotinfo data
structures. */
if (any_tls)
/* FIXME: This calls _dl_update_slotinfo, which aborts the process
on memory allocation failure. See bug 16134. */
update_tls_slotinfo (new);
/* Notify the debugger all new objects have been relocated. */
if (relocation_in_progress)
LIBC_PROBE (reloc_complete, 3, args->nsid, r, new);
@@ -777,7 +776,7 @@ dl_open_worker (void *a)
#ifdef SHARED
if (was_not_consistent)
/* Avoid redudant/recursive signalling. */
/* Avoid redundant/recursive signalling. */
_dl_audit_activity_nsid (nsid, LA_ACT_CONSISTENT);
#endif
}
+18 -6
View File
@@ -31,8 +31,10 @@
#include "dynamic-link.h"
#include "get-dynamic-info.h"
/* Relocate static executable with PIE. */
/* Phase 1: relocate static PIE - non-IRELATIVE pass. IFUNC resolvers are
deferred to _dl_relocate_static_pie_ifunc so that csu/libc-start.c can
initialise the TCB (and write the stack-protector canary into it) between
the two passes. */
void
_dl_relocate_static_pie (void)
{
@@ -76,10 +78,9 @@ _dl_relocate_static_pie (void)
ELF_MACHINE_BEFORE_RTLD_RELOC (main_map, main_map->l_info);
# endif
/* Relocate ourselves so we can do normal function calls and
data access using the global offset table. */
ELF_DYNAMIC_RELOCATE (main_map, NULL, 0, 0, 0);
main_map->l_relocated = 1;
/* Relocate ourselves so we can do normal function calls and data access
using the global offset table. IRELATIVE entries are deferred. */
ELF_DYNAMIC_RELOCATE_NOIFUNC (main_map, NULL, 0, 0);
/* Initialize _r_debug_extended. */
struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE);
@@ -89,4 +90,15 @@ _dl_relocate_static_pie (void)
time. */
elf_setup_debug_entry (main_map, r);
}
/* Phase 2: run the deferred IRELATIVE entries for the static-pie main map.
Must be called after the TCB is set up and the stack-protector canary is
written, so that an instrumented IFUNC resolver does not fault. */
void
_dl_relocate_static_pie_ifunc (void)
{
struct link_map *main_map = _dl_get_dl_main_map ();
ELF_DYNAMIC_RELOCATE_IFUNC (main_map, NULL, 0, 0);
main_map->l_relocated = 1;
}
#endif
+35 -16
View File
@@ -107,22 +107,21 @@ _dl_try_allocate_static_tls (struct link_map *map, bool optional)
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
#endif
/* If the object is not yet relocated we cannot initialize the
static TLS region. Delay it. */
if (map->l_real->l_relocated)
{
/* Initialise the static TLS region, the map may not yet be l_relocated (a
TLS reloc inside the relocation loop triggered the allocation), but
_dl_init_static_tls only writes .tdata into the static TLS slot, which is
independent of relocation state.
Doing this inline ensures any IFUNC resolver that fires later in the same
object's relocation pass sees an initialised TLS slot, and the
post-relocation TLS init loop in dl_open_worker_begin becomes a no-op for
this map. */
#ifdef SHARED
/* Update the DTV of the current thread. Note: GL(dl_load_tls_lock)
is held here so normal load of the generation counter is valid. */
if (__builtin_expect (THREAD_DTV()[0].counter != GL(dl_tls_generation),
0))
(void) _dl_update_slotinfo (map->l_tls_modid, GL(dl_tls_generation));
/* Update the DTV of the current thread. Note: GL(dl_load_tls_lock)
is held here so normal load of the generation counter is valid. */
if (__glibc_unlikely (THREAD_DTV()[0].counter != GL(dl_tls_generation)))
_dl_update_slotinfo (map->l_tls_modid, GL(dl_tls_generation));
#endif
_dl_init_static_tls (map);
}
else
map->l_need_tls_init = 1;
_dl_init_static_tls (map);
return 0;
}
@@ -270,9 +269,29 @@ _dl_relocate_object_no_relro (struct link_map *l, struct r_scope_elem *scope[],
}
{
/* Do the actual relocation of the object's GOT and other data. */
/* Do the actual relocation of the object's GOT and other data.
ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
Process the non-IRELATIVE pass first so .tdata is fully relocated
(including R_*_RELATIVE / R_*_64 fixups for TLS initialisers, e.g. a
file-scope thread-local initialised with the address of a function),
then refresh the static TLS slot before the IRELATIVE pass runs the
IFUNC resolvers. Without this, a resolver would see the unrelocated
initialiser bytes that were placed into the slot by the early
_dl_allocate_tls_init. */
ELF_DYNAMIC_RELOCATE_NOIFUNC (l, scope, lazy, consider_profiling);
#ifdef SHARED
/* Re-initialise the static TLS slot with the .tdata so the IRELATIVE
pass observes a fully-relocated initialiser image. Skipped for objects
without static TLS or before the main thread TCB has been set up. */
if (l->l_tls_blocksize != 0
&& __rtld_tls_init_tp_called
&& l->l_tls_offset != NO_TLS_OFFSET
&& l->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET)
_dl_init_static_tls (l);
#endif
ELF_DYNAMIC_RELOCATE_IFUNC (l, scope, lazy, skip_ifunc);
if ((consider_profiling || consider_symbind)
&& l->l_info[DT_PLTRELSZ] != NULL)
+90
View File
@@ -0,0 +1,90 @@
/* Loader-internal scratch buffer.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <dl-scratch-buffer.h>
#include <assert.h>
#include <errno.h>
#include <ldsodefs.h>
#include <libc-pointer-arith.h>
#include <libintl.h>
#include <setvmaname.h>
#include <stdlib.h>
#include <sys/mman.h>
void
_dl_scratch_buffer_allocate (struct dl_scratch_buffer *b, size_t size,
unsigned int flags)
{
/* Enforce the one-shot contract. */
assert (b->backend == DL_SCRATCH_INLINE);
bool use_malloc = !(flags & DL_SCRATCH_NO_MALLOC);
#ifdef SHARED
/* While __minimal_malloc is the active allocator, __minimal_free
only releases the most-recent block; route through mmap instead so
dl_scratch_buffer_free can symmetrically release the spill. */
if (!__rtld_malloc_is_complete ())
use_malloc = false;
#endif
if (use_malloc)
{
void *p = malloc (size);
if (__glibc_unlikely (p == NULL))
_dl_signal_error (ENOMEM, NULL, NULL,
N_("cannot allocate loader scratch buffer"));
b->data = p;
b->size = size;
b->backend = DL_SCRATCH_MALLOC;
return;
}
size_t map_size = ALIGN_UP (size, GLRO(dl_pagesize));
void *p = __mmap (NULL, map_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (__glibc_unlikely (p == MAP_FAILED))
_dl_signal_error (ENOMEM, NULL, NULL,
N_("cannot allocate loader scratch buffer"));
__set_vma_name (p, map_size, " glibc: loader scratch");
b->data = p;
b->size = map_size;
b->backend = DL_SCRATCH_MMAP;
}
rtld_hidden_def (_dl_scratch_buffer_allocate)
void
_dl_scratch_buffer_free (struct dl_scratch_buffer *b)
{
switch (b->backend)
{
case DL_SCRATCH_MALLOC:
free (b->data);
break;
case DL_SCRATCH_MMAP:
__munmap (b->data, b->size);
break;
case DL_SCRATCH_INLINE:
/* Unreachable in normal use; guarded by the inline wrapper. */
break;
}
b->data = b->inline_data;
b->size = sizeof b->inline_data;
b->backend = DL_SCRATCH_INLINE;
}
rtld_hidden_def (_dl_scratch_buffer_free)
+145
View File
@@ -0,0 +1,145 @@
/* Loader-internal scratch buffer.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This is the loader-side analogue of <scratch_buffer.h>. It exists
because the loader has two constraints that <scratch_buffer.h> does
not address:
1. While the active allocator is __minimal_malloc (early startup,
before __rtld_malloc_init_real has switched in libc's malloc),
__minimal_free only releases the most-recent allocation -- a
malloc'd spill would silently leak.
2. Some loader code paths cannot route a spill through the
interposable malloc at all because the user malloc may
recursively re-enter the loader and invalidate state we are
copying from (the canonical example is _dl_load_cache_lookup
copying out of the file-backed ld.so.cache mapping).
The buffer starts in a stack-resident inline area; if the caller
needs more bytes, the spill is to anonymous mmap (always safe,
tagged for /proc/self/maps visibility) or to malloc (cheaper, only
chosen when both the active allocator is real malloc and the
caller does not pass DL_SCRATCH_NO_MALLOC).
Typical usage:
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
dl_scratch_buffer_allocate (&scratch, needed, 0);
... use scratch.data ...
dl_scratch_buffer_free (&scratch);
The interface is one-shot: every consumer knows the required size
upfront and calls dl_scratch_buffer_allocate exactly once, so there
is no incremental-growth model. A second allocate without an
intervening free is a programming error and is checked by an
assertion in _dl_scratch_buffer_allocate.
On allocation failure dl_scratch_buffer_allocate does not return;
it raises a loader ENOMEM via _dl_signal_error. Callers may
therefore treat scratch.data as valid after a successful return. */
#ifndef _DL_SCRATCH_BUFFER_H
#define _DL_SCRATCH_BUFFER_H 1
#include <stdbool.h>
#include <stddef.h>
#include <sys/cdefs.h>
/* Size of the inline area. Tuned to cover typical ld.so.cache
entries (well under 256 bytes) so that the common case stays
entirely on-stack with no syscall and no malloc. */
enum { DL_SCRATCH_BUFFER_INLINE_SIZE = 256 };
enum dl_scratch_backend
{
DL_SCRATCH_INLINE,
DL_SCRATCH_MMAP,
DL_SCRATCH_MALLOC,
};
struct dl_scratch_buffer
{
void *data;
size_t size;
enum dl_scratch_backend backend;
char inline_data[DL_SCRATCH_BUFFER_INLINE_SIZE]
__attribute__ ((aligned (__alignof__ (max_align_t))));
};
enum
{
/* Forbid the malloc backend for spill allocations -- the spill must
come from anonymous mmap so that interposed user malloc cannot
recursively re-enter the loader and invalidate state the caller
is copying from. See _dl_load_cache_lookup. */
DL_SCRATCH_NO_MALLOC = 1 << 0,
};
/* Return a freshly-initialized scratch buffer suitable for use as a
stack-resident initializer. */
static __always_inline __attribute_warn_unused_result__
struct dl_scratch_buffer
dl_scratch_buffer_init (void)
{
return (struct dl_scratch_buffer) {
.data = NULL,
.size = sizeof ((struct dl_scratch_buffer *) 0)->inline_data,
.backend = DL_SCRATCH_INLINE,
};
}
extern void _dl_scratch_buffer_allocate (struct dl_scratch_buffer *b,
size_t size, unsigned int flags)
__nonnull ((1)) attribute_hidden;
rtld_hidden_proto (_dl_scratch_buffer_allocate)
extern void _dl_scratch_buffer_free (struct dl_scratch_buffer *b)
__nonnull ((1)) attribute_hidden;
rtld_hidden_proto (_dl_scratch_buffer_free)
/* Ensure B->data points to a buffer of at least SIZE bytes; updates
B->size and B->backend accordingly. Intended to be called exactly
once per buffer lifetime (callers know the required size upfront --
there is no incremental growth model). Raises a loader ENOMEM
error via _dl_signal_error on failure -- does not return NULL. */
static __always_inline __nonnull ((1)) void
dl_scratch_buffer_allocate (struct dl_scratch_buffer *b, size_t size,
unsigned int flags)
{
/* First call after dl_scratch_buffer_init: point .data at the
caller's inline area now that its address is in scope. */
if (__glibc_unlikely (b->data == NULL))
b->data = b->inline_data;
if (__glibc_likely (size <= b->size))
return;
_dl_scratch_buffer_allocate (b, size, flags);
}
/* Release any out-of-line allocation held by B and restore the
inline state. Safe to call multiple times (and on an already-freed
or freshly-initialized buffer). */
static __always_inline __nonnull ((1)) void
dl_scratch_buffer_free (struct dl_scratch_buffer *b)
{
if (__glibc_likely (b->backend == DL_SCRATCH_INLINE))
return;
_dl_scratch_buffer_free (b);
}
#endif /* dl-scratch-buffer.h */
+6
View File
@@ -179,6 +179,12 @@ int _dl_stack_cache_lock;
#endif
struct dl_scope_free_list *_dl_scope_free_list;
#ifdef HAVE_THP
enum dl_elf_thp_control_t _dl_elf_thp_control;
enum thp_mode_t _dl_thp_mode;
size_t _dl_elf_thp_pagesize;
#endif
#ifdef NEED_DL_SYSINFO
/* Needed for improved syscall handling on at least x86/Linux. NB: Don't
initialize it here to avoid RELATIVE relocation in static PIE. */
+9 -5
View File
@@ -22,12 +22,16 @@ static struct link_map *
_dl_sym_find_caller_link_map (ElfW(Addr) caller)
{
struct link_map *l = _dl_find_dso_for_object (caller);
if (l != NULL)
/* A constructor that tail-calls dlsym makes the caller address point
into the dynamic linker itself. The ld.so link map has no l_scope
set, so using it for a symbol lookup would dereference NULL. Treat
that like an unknown caller. */
if (l != NULL && l->l_scope != NULL)
return l;
else
/* If the address is not recognized the call comes from the main
program (we hope). */
return GL(dl_ns)[LM_ID_BASE]._ns_loaded;
/* The address does not belong to any loaded object (e.g. it is in
JIT-generated code or in the main program). Fall back to the main
program's link map. */
return GL(dl_ns)[LM_ID_BASE]._ns_loaded;
}
/* Translates RESULT, *REF, VALUE into a symbol address from the point
+3 -3
View File
@@ -339,7 +339,7 @@ _dl_determine_tlsoffset (void)
/* Extra TLS block for internal usage to append at the end of the TLS blocks
(in allocation order). The address at which the block is allocated must
be aligned to 'extra_tls_align'. The size of the block as returned by
'_dl_extra_tls_get_size ()' is always a multiple of the aligment.
'_dl_extra_tls_get_size ()' is always a multiple of the alignment.
On Linux systems this is where the rseq area will be allocated. On other
systems it is currently unused and both values will be '0'. */
@@ -419,7 +419,7 @@ _dl_determine_tlsoffset (void)
/* Extra TLS block for internal usage to append at the end of the TLS blocks
(in allocation order). The address at which the block is allocated must
be aligned to 'extra_tls_align'. The size of the block as returned by
'_dl_extra_tls_get_size ()' is always a multiple of the aligment.
'_dl_extra_tls_get_size ()' is always a multiple of the alignment.
On Linux systems this is where the rseq area will be allocated. On other
systems it is currently unused and both values will be '0'. */
@@ -699,7 +699,7 @@ _dl_allocate_tls_init (void *result, bool main_thread)
initialization because it would already be set by the
audit setup, which uses the dlopen code and already
clears l_need_tls_init. Calls with !main_thread from
pthread_create need to initialze TLS for the current
pthread_create need to initialize TLS for the current
thread regardless of namespace. */
if (map->l_ns != LM_ID_BASE && main_thread)
continue;
-5
View File
@@ -116,11 +116,6 @@ glibc {
}
mem {
tagging {
type: INT_32
minval: 0
maxval: 255
}
decorate_maps {
type: INT_32
minval: 0
+138 -96
View File
@@ -23,6 +23,9 @@
#ifdef DO_RELA
# define elf_dynamic_do_Rel elf_dynamic_do_Rela
# define elf_dynamic_do_Rel_irelative elf_dynamic_do_Rela_irelative
# define elf_dynamic_is_Rel_irelative elf_dynamic_is_Rela_irelative
# define elf_dynamic_Rel_audit_symbind elf_dynamic_Rela_audit_symbind
# define Rel Rela
# define elf_machine_rel elf_machine_rela
# define elf_machine_rel_relative elf_machine_rela_relative
@@ -34,16 +37,53 @@
(void *) (l_addr + relative->r_offset))
#endif
static __always_inline bool
elf_dynamic_is_Rel_irelative (const ElfW(Rel) *reloc, const ElfW(Sym) *sym)
{
#ifdef ELF_MACHINE_IRELATIVE
const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
return ((sym != NULL
&& ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC
&& sym->st_shndx != SHN_UNDEF)
|| r_type == ELF_MACHINE_IRELATIVE);
#else
return false;
#endif
}
static __always_inline void
elf_dynamic_Rel_audit_symbind (struct link_map *map,
struct r_scope_elem *scope[],
const ElfW(Rel) *reloc, const ElfW(Sym) *sym,
const struct r_found_version *rversion,
void *r_addr_arg)
{
#if defined SHARED
if (ELFW(R_TYPE) (reloc->r_info) == ELF_MACHINE_JMP_SLOT
&& GLRO(dl_naudit) > 0)
{
struct link_map *sym_map
= RESOLVE_MAP (map, scope, &sym, rversion, ELF_MACHINE_JMP_SLOT);
if (sym != NULL)
_dl_audit_symbind (map, NULL, reloc, sym, r_addr_arg, sym_map, false);
}
#endif
}
/* Perform the relocations in MAP on the running program image as specified
by RELTAG, SZTAG. If LAZY is nonzero, this is the first pass on PLT
relocations; they should be set up to call _dl_runtime_resolve, rather
than fully resolved now. */
than fully resolved now.
IRELATIVE entries are always skipped (non-bootstrap); they are handled
separately by elf_dynamic_do_Rel_irelative after all other relocations
for both .rel.dyn and .rel.plt have been processed. */
static inline void __attribute__ ((always_inline))
elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
ElfW(Addr) reladdr, ElfW(Addr) relsize,
__typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative,
int lazy, int skip_ifunc)
int lazy)
{
const ElfW(Rel) *relative = (const void *) reladdr;
const ElfW(Rel) *r = relative + nrelative;
@@ -65,14 +105,9 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
const struct r_found_version *rversion = &map->l_versions[ndx];
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, skip_ifunc);
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
}
#else /* !RTLD_BOOTSTRAP */
# if defined ELF_MACHINE_IRELATIVE
const ElfW(Rel) *r2 = NULL;
const ElfW(Rel) *end2 = NULL;
# endif
#if !defined DO_RELA || !defined ELF_MACHINE_PLT_REL
/* We never bind lazily during ld.so bootstrap. Unfortunately gcc is
not clever enough to see through all the function calls to realize
@@ -81,23 +116,12 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
{
/* Doing lazy PLT relocations; they need very little info. */
for (; r < end; ++r)
# ifdef ELF_MACHINE_IRELATIVE
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
{
if (r2 == NULL)
r2 = r;
end2 = r;
}
else
# endif
elf_machine_lazy_rel (map, scope, l_addr, r, skip_ifunc);
# ifdef ELF_MACHINE_IRELATIVE
if (r2 != NULL)
for (; r2 <= end2; ++r2)
if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
elf_machine_lazy_rel (map, scope, l_addr, r2, skip_ifunc);
# endif
{
const ElfW (Sym) *sym = &symtab[ELFW (R_SYM) (r->r_info)];
if (elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_lazy_rel (map, scope, l_addr, r, 0);
}
}
else
#endif
@@ -125,46 +149,13 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
const struct r_found_version *rversion = &map->l_versions[ndx];
#if defined ELF_MACHINE_IRELATIVE
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
{
if (r2 == NULL)
r2 = r;
end2 = r;
continue;
}
#endif
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg,
skip_ifunc);
#if defined SHARED
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
&& GLRO(dl_naudit) > 0)
{
struct link_map *sym_map
= RESOLVE_MAP (map, scope, &sym, rversion,
ELF_MACHINE_JMP_SLOT);
if (sym != NULL)
_dl_audit_symbind (map, NULL, r, sym, r_addr_arg, sym_map,
false);
}
#endif
if (elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
elf_dynamic_Rel_audit_symbind (map, scope, r, sym, rversion,
r_addr_arg);
}
#if defined ELF_MACHINE_IRELATIVE
if (r2 != NULL)
for (; r2 <= end2; ++r2)
if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
{
ElfW(Half) ndx
= version[ELFW(R_SYM) (r2->r_info)] & 0x7fff;
elf_machine_rel (map, scope, r2,
&symtab[ELFW(R_SYM) (r2->r_info)],
&map->l_versions[ndx],
(void *) (l_addr + r2->r_offset),
skip_ifunc);
}
#endif
}
else
{
@@ -172,46 +163,97 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
{
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
# ifdef ELF_MACHINE_IRELATIVE
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
{
if (r2 == NULL)
r2 = r;
end2 = r;
continue;
}
# endif
elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg,
skip_ifunc);
# if defined SHARED
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
&& GLRO(dl_naudit) > 0)
{
struct link_map *sym_map
= RESOLVE_MAP (map, scope, &sym,
(struct r_found_version *) NULL,
ELF_MACHINE_JMP_SLOT);
if (sym != NULL)
_dl_audit_symbind (map, NULL, r, sym,r_addr_arg, sym_map,
false);
}
# endif
}
# ifdef ELF_MACHINE_IRELATIVE
if (r2 != NULL)
for (; r2 <= end2; ++r2)
if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
elf_machine_rel (map, scope, r2, &symtab[ELFW(R_SYM) (r2->r_info)],
NULL, (void *) (l_addr + r2->r_offset),
skip_ifunc);
# endif
if (elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg, 0);
elf_dynamic_Rel_audit_symbind (map, scope, r, sym, NULL,
r_addr_arg);
}
}
}
#endif /* !RTLD_BOOTSTRAP */
}
/* Process only IRELATIVE entries (and other relocations targeting a defined
STT_GNU_IFUNC symbol) in the relocation range [reladdr, reladdr+relsize).
The first NRELATIVE entries are R_*_RELATIVE and are skipped without
inspection. When lazy is non-zero the PLT lazy-binding path
(elf_machine_lazy_rel) is used, otherwise the full non-lazy path
(elf_machine_rel) is used.
Called by _ELF_DYNAMIC_DO_RELOC after all non-IRELATIVE relocations have
been processed for both .rela.dyn and .rela.plt, so that IRELATIVE
resolvers may call PLT stubs safely regardless of which section the linker
placed R_*_IRELATIVE in. */
static __always_inline void
elf_dynamic_do_Rel_irelative (struct link_map *map,
struct r_scope_elem *scope[],
ElfW(Addr) reladdr, ElfW(Addr) relsize,
__typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative,
int lazy, int skip_ifunc)
{
# ifdef ELF_MACHINE_IRELATIVE
const ElfW(Rel) *r = (const ElfW(Rel) *) reladdr + nrelative;
const ElfW(Rel) *end = (const void *) (reladdr + relsize);
ElfW(Addr) l_addr = map->l_addr;
const ElfW(Sym) *const symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
if (lazy)
{
for (; r < end; ++r)
{
const ElfW (Sym) *sym = &symtab[ELFW (R_SYM) (r->r_info)];
if (!elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_lazy_rel (map, scope, l_addr, r, skip_ifunc);
}
}
else
{
if (map->l_info[VERSYMIDX (DT_VERSYM)])
{
const ElfW(Half) *const version =
(const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
for (; r < end; ++r)
{
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
if (!elf_dynamic_is_Rel_irelative (r, sym))
continue;
ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
const struct r_found_version *rversion = &map->l_versions[ndx];
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg,
skip_ifunc);
elf_dynamic_Rel_audit_symbind (map, scope, r, sym, rversion,
r_addr_arg);
}
}
else
{
for (; r < end; ++r)
{
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
if (!elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg,
skip_ifunc);
elf_dynamic_Rel_audit_symbind (map, scope, r, sym, NULL,
r_addr_arg);
}
}
}
# endif
}
#undef elf_dynamic_do_Rel
#undef elf_dynamic_do_Rel_irelative
#undef elf_dynamic_is_Rel_irelative
#undef elf_dynamic_Rel_audit_symbind
#undef Rel
#undef elf_machine_rel
#undef elf_machine_rel_relative
+84 -10
View File
@@ -78,7 +78,18 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
consumes precisely the very end of the DT_REL*, or DT_JMPREL and DT_REL*
are completely separate and there is a gap between them. */
# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, test_rel) \
/* This controls which sub-passes _ELF_DYNAMIC_DO_RELOC runs. Used to
interleave TLS / stack-protector setup between the two passes so IFUNC
resolvers see a fully-initialised TCB. */
enum elf_dynamic_reloc_phase
{
DL_RELOC_BOTH = 0, /* Non-IRELATIVE pass then IRELATIVE pass. */
DL_RELOC_NOIFUNC = 1, /* Non-IRELATIVE pass only. */
DL_RELOC_IFUNC = 2, /* IRELATIVE pass only. */
};
# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, \
test_rel, phase) \
do { \
struct { ElfW(Addr) start, size; \
__typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative; int lazy; } \
@@ -118,13 +129,36 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
} \
} \
\
for (int ranges_index = 0; ranges_index < 2; ++ranges_index) \
elf_dynamic_do_##reloc ((map), scope, \
ranges[ranges_index].start, \
ranges[ranges_index].size, \
ranges[ranges_index].nrelative, \
ranges[ranges_index].lazy, \
skip_ifunc); \
/* Defer all IRELATIVE relocations until after all non-IRELATIVE \
relocations (including PLT lazy-binding setup) have been processed \
for both sections. This ensures IRELATIVE resolvers can call PLT \
stubs safely regardless of which section R_*_IRELATIVE was placed in \
by the linker. */ \
if (!DO_RTLD_BOOTSTRAP) \
{ \
if ((phase) != DL_RELOC_IFUNC) \
for (int ranges_index = 0; ranges_index < 2; ++ranges_index) \
elf_dynamic_do_##reloc ((map), scope, \
ranges[ranges_index].start, \
ranges[ranges_index].size, \
ranges[ranges_index].nrelative, \
ranges[ranges_index].lazy); \
if ((phase) != DL_RELOC_NOIFUNC) \
for (int ranges_index = 0; ranges_index < 2; ++ranges_index) \
elf_dynamic_do_##reloc##_irelative ((map), scope, \
ranges[ranges_index].start, \
ranges[ranges_index].size, \
ranges[ranges_index].nrelative,\
ranges[ranges_index].lazy, \
skip_ifunc); \
} \
else \
for (int ranges_index = 0; ranges_index < 2; ++ranges_index) \
elf_dynamic_do_##reloc ((map), scope, \
ranges[ranges_index].start, \
ranges[ranges_index].size, \
ranges[ranges_index].nrelative, \
ranges[ranges_index].lazy); \
} while (0)
# if ELF_MACHINE_NO_REL || ELF_MACHINE_NO_RELA
@@ -136,18 +170,36 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
# if ! ELF_MACHINE_NO_REL
# include "do-rel.h"
# define ELF_DYNAMIC_DO_REL(map, scope, lazy, skip_ifunc) \
_ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc, _ELF_CHECK_REL)
_ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc, \
_ELF_CHECK_REL, DL_RELOC_BOTH)
# define ELF_DYNAMIC_DO_REL_NOIFUNC(map, scope, lazy) \
_ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, 0, \
_ELF_CHECK_REL, DL_RELOC_NOIFUNC)
# define ELF_DYNAMIC_DO_REL_IFUNCONLY(map, scope, lazy, skip_ifunc) \
_ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc, \
_ELF_CHECK_REL, DL_RELOC_IFUNC)
# else
# define ELF_DYNAMIC_DO_REL(map, scope, lazy, skip_ifunc) /* Nothing to do. */
# define ELF_DYNAMIC_DO_REL_NOIFUNC(map, scope, lazy) /* Nothing to do. */
# define ELF_DYNAMIC_DO_REL_IFUNCONLY(map, scope, lazy, skip_ifunc) /* Nothing. */
# endif
# if ! ELF_MACHINE_NO_RELA
# define DO_RELA
# include "do-rel.h"
# define ELF_DYNAMIC_DO_RELA(map, scope, lazy, skip_ifunc) \
_ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc, _ELF_CHECK_REL)
_ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc, \
_ELF_CHECK_REL, DL_RELOC_BOTH)
# define ELF_DYNAMIC_DO_RELA_NOIFUNC(map, scope, lazy) \
_ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, 0, \
_ELF_CHECK_REL, DL_RELOC_NOIFUNC)
# define ELF_DYNAMIC_DO_RELA_IFUNCONLY(map, scope, lazy, skip_ifunc) \
_ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc, \
_ELF_CHECK_REL, DL_RELOC_IFUNC)
# else
# define ELF_DYNAMIC_DO_RELA(map, scope, lazy, skip_ifunc) /* Nothing to do. */
# define ELF_DYNAMIC_DO_RELA_NOIFUNC(map, scope, lazy) /* Nothing to do. */
# define ELF_DYNAMIC_DO_RELA_IFUNCONLY(map, scope, lazy, skip_ifunc) /* Nothing. */
# endif
# define ELF_DYNAMIC_DO_RELR(map) \
@@ -199,4 +251,26 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
ELF_DYNAMIC_AFTER_RELOC ((map), (edr_lazy)); \
} while (0)
/* Like ELF_DYNAMIC_RELOCATE but only processes the non-IRELATIVE pass.
The IRELATIVE pass must be completed later via ELF_DYNAMIC_RELOCATE_IFUNC.
Used by the static-pie startup so the TCB and stack-protector canary can
be initialised between the two passes. */
# define ELF_DYNAMIC_RELOCATE_NOIFUNC(map, scope, lazy, consider_profile) \
do { \
int edr_lazy = elf_machine_runtime_setup ((map), (scope), (lazy), \
(consider_profile)); \
if (!is_rtld_link_map (map) || DO_RTLD_BOOTSTRAP) \
ELF_DYNAMIC_DO_RELR (map); \
ELF_DYNAMIC_DO_REL_NOIFUNC ((map), (scope), edr_lazy); \
ELF_DYNAMIC_DO_RELA_NOIFUNC ((map), (scope), edr_lazy); \
ELF_DYNAMIC_AFTER_RELOC ((map), (edr_lazy)); \
} while (0)
/* IRELATIVE-only companion to ELF_DYNAMIC_RELOCATE_NOIFUNC. */
# define ELF_DYNAMIC_RELOCATE_IFUNC(map, scope, lazy, skip_ifunc) \
do { \
ELF_DYNAMIC_DO_REL_IFUNCONLY ((map), (scope), (lazy), skip_ifunc); \
ELF_DYNAMIC_DO_RELA_IFUNCONLY ((map), (scope), (lazy), skip_ifunc); \
} while (0)
#endif
+153
View File
@@ -0,0 +1,153 @@
#!/usr/bin/python3
# Generate a crafted ELF with a large number of PT_NULL program headers
# for tst-bz26577.
# Copyright (C) 2026 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
#
# The GNU C Library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# The GNU C Library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with the GNU C Library; if not, see
# <https://www.gnu.org/licenses/>.
# The generated file is an ET_DYN ELF with EVIL_PHNUM (0x7FFF) program
# headers. The first header is a PT_LOAD covering the ELF header itself so
# the dynamic linker actually attempts to map the object and exercises the
# iterator path that replaced the old loadcmd VLA. The remaining headers
# are PT_NULL. The object has no PT_DYNAMIC, so dlopen and LD_PRELOAD will
# both fail gracefully after loading.
import os
import struct
import sys
import glibcelf
# Must match the definition in tst-bz26577.c.
EVIL_PHNUM = 0x7FFF
def main():
if len(sys.argv) != 3:
print('usage: gen-tst-bz26577-mod.py OUTPUT REF-ELF', file=sys.stderr)
sys.exit(1)
output_path = sys.argv[1]
ref_elf_path = sys.argv[2]
# Read EI_CLASS, EI_DATA and e_machine from a target ELF (ld.so) so
# the generated file matches the target ABI, not the host Python.
ref = glibcelf.Image.readfile(ref_elf_path)
ei_class = ref.ehdr.e_ident.ei_class # ElfClass.ELFCLASS32 or ELFCLASS64
ei_data = ref.ehdr.e_ident.ei_data # ElfData.ELFDATA2LSB or ELFDATA2MSB
e_machine = ref.ehdr.e_machine # Machine.*
endian = '<' if ei_data == glibcelf.ElfData.ELFDATA2LSB else '>'
is64 = (ei_class == glibcelf.ElfClass.ELFCLASS64)
ehdr_size = glibcelf.Ehdr.layouts[(ei_class, ei_data)].size
phdr_size = glibcelf.Phdr.layouts[(ei_class, ei_data)].size
# File must hold the ELF header plus the full program header table so
# that pread() in open_verify and _dl_map_object_from_fd can read all
# EVIL_PHNUM entries without a short read.
total = ehdr_size + EVIL_PHNUM * phdr_size
# Assume workable value, the binary should be reject by the loader anyway.
pagesize = 4096
total = (total + pagesize - 1) & ~(pagesize - 1)
buf = bytearray(total)
# ELF Header:
buf[0:4] = b'\x7fELF'
buf[4] = ei_class.value
buf[5] = ei_data.value
buf[6] = 1 # EV_CURRENT
buf[7] = 0 # ELFOSABI_SYSV
# bytes 8..15 remain zero (padding)
# Pack the ELF header fields that follow e_ident.
# ELF64 Ehdr layout (after e_ident[16]):
# e_type(H) e_machine(H) e_version(I)
# e_entry(Q) e_phoff(Q) e_shoff(Q)
# e_flags(I) e_ehsize(H) e_phentsize(H)
# e_phnum(H) e_shentsize(H) e_shnum(H) e_shstrndx(H)
# ELF32 Ehdr layout (after e_ident[16]):
# e_type(H) e_machine(H) e_version(I)
# e_entry(I) e_phoff(I) e_shoff(I)
# e_flags(I) e_ehsize(H) e_phentsize(H)
# e_phnum(H) e_shentsize(H) e_shnum(H) e_shstrndx(H)
if is64:
fmt = endian + '2HI3QI6H'
else:
fmt = endian + '2H5I6H'
phoff = ehdr_size # program header table immediately follows Ehdr
fields = (
glibcelf.Et.ET_DYN.value, # e_type
e_machine.value, # e_machine
1, # e_version (EV_CURRENT)
0, # e_entry
phoff, # e_phoff
0, # e_shoff
0, # e_flags
ehdr_size, # e_ehsize
phdr_size, # e_phentsize
EVIL_PHNUM, # e_phnum
0, # e_shentsize
0, # e_shnum
0, # e_shstrndx
)
struct.pack_into(fmt, buf, 16, *fields)
# Write the first program header as PT_LOAD covering the ELF header
# (p_offset=0, p_filesz=ehdr_size, p_memsz=ehdr_size, PF_R). This
# ensures the dynamic linker actually maps the segment and exercises
# the PT_LOAD iterator path rather than aborting early with
# "no loadable segments". The remaining EVIL_PHNUM-1 headers stay
# zero (PT_NULL).
#
# ELF64 Phdr field order (layout '2I6Q'):
# p_type(I) p_flags(I) p_offset(Q) p_vaddr(Q) p_paddr(Q)
# p_filesz(Q) p_memsz(Q) p_align(Q)
# ELF32 Phdr field order (layout '8I'):
# p_type(I) p_offset(I) p_vaddr(I) p_paddr(I)
# p_filesz(I) p_memsz(I) p_flags(I) p_align(I)
if is64:
phdr_fmt = endian + '2I6Q'
phdr_fields = (
glibcelf.Pt.PT_LOAD.value, # p_type
glibcelf.Pf.PF_R.value, # p_flags
0, # p_offset
0, # p_vaddr
0, # p_paddr
ehdr_size, # p_filesz
ehdr_size, # p_memsz
pagesize, # p_align
)
else:
phdr_fmt = endian + '8I'
phdr_fields = (
glibcelf.Pt.PT_LOAD.value, # p_type
0, # p_offset
0, # p_vaddr
0, # p_paddr
ehdr_size, # p_filesz
ehdr_size, # p_memsz
glibcelf.Pf.PF_R.value, # p_flags
pagesize, # p_align
)
struct.pack_into(phdr_fmt, buf, ehdr_size, *phdr_fields)
with open(output_path, 'wb') as f:
f.write(buf)
if __name__ == '__main__':
main()
+32 -29
View File
@@ -35,6 +35,7 @@
#include <unsecvars.h>
#include <dl-cache.h>
#include <dl-osinfo.h>
#include <dl-reseed-random.h>
#include <dl-prop.h>
#include <dl-vdso.h>
#include <dl-vdso-setup.h>
@@ -52,6 +53,7 @@
#include <dl-find_object.h>
#include <dl-audit-check.h>
#include <dl-call_tls_init_tp.h>
#include <dl-exec-post.h>
#include <assert.h>
@@ -166,12 +168,8 @@ rtld_hidden_data_def (_dl_argv)
uintptr_t __stack_chk_guard attribute_relro;
#endif
/* Only exported for architectures that don't store the pointer guard
value in thread local area. */
uintptr_t __pointer_chk_guard_local attribute_relro attribute_hidden;
#ifndef THREAD_SET_POINTER_GUARD
strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
#endif
/* Check that AT_SECURE=0, or that the passed name does not contain
directories and is not overly long. Reject empty names
@@ -827,15 +825,12 @@ security_init (void)
/* Set up the pointer guard as well, if necessary. */
uintptr_t pointer_chk_guard
= _dl_setup_pointer_guard (_dl_random, stack_chk_guard);
#ifdef THREAD_SET_POINTER_GUARD
THREAD_SET_POINTER_GUARD (pointer_chk_guard);
#endif
__pointer_chk_guard_local = pointer_chk_guard;
/* We do not need the _dl_random value anymore. The less
information we leave behind, the better, so clear the
variable. */
_dl_random = NULL;
/* We do not need the _dl_random value anymore. Scrub the AT_RANDOM
bytes and clear the pointer; on targets with an entropy source, refill
the bytes with fresh random data. */
_dl_reseed_random (&_dl_random);
}
#include <setup-vdso.h>
@@ -1209,14 +1204,8 @@ rtld_setup_main_map (struct link_map *main_map)
main_map->l_relro_size = ph->p_memsz;
break;
}
/* Process program headers again, but scan them backwards since
PT_GNU_PROPERTY is close to the end of program headers. */
for (const ElfW(Phdr) *ph = &phdr[phnum]; ph != phdr; --ph)
if (ph[-1].p_type == PT_GNU_PROPERTY)
{
_dl_process_pt_gnu_property (main_map, -1, &ph[-1]);
break;
}
_dl_executable_postprocess (main_map, phdr, phnum);
/* Adjust the address of the TLS initialization image in case
the executable is actually an ET_DYN object. */
@@ -1589,6 +1578,9 @@ dl_main (const ElfW(Phdr) *phdr,
{
RTLD_TIMING_VAR (start);
rtld_timer_start (&start);
#ifdef HAVE_THP
_dl_get_thp_config ();
#endif
_dl_map_object (NULL, rtld_progname, lt_executable, 0,
__RTLD_OPENEXEC, LM_ID_BASE);
rtld_timer_stop (&load_time, start);
@@ -2263,6 +2255,26 @@ dl_main (const ElfW(Phdr) *phdr,
rtld_timer_accum (&relocate_time, start);
}
/* Populate the DTV slotinfo and copy each TLS module's into thestatic TLS
block *before* the relocation loop. IFUNC resolvers fired during phase 2
of the per-object two-phase scheme therefore observe initialised TLS. */
if (__rtld_tls_init_tp_called)
{
unsigned int i = main_map->l_searchlist.r_nlist;
while (i-- > 0)
{
struct link_map *l = main_map->l_initfini[i];
if (l->l_tls_blocksize != 0)
_dl_add_to_slotinfo (l, true);
}
/* _dl_add_to_slotinfo records gen = dl_tls_generation + 1, and
_dl_allocate_tls_init asserts gen <= dl_tls_generation, so bump
the generation before init. */
if (GL(dl_tls_max_dtv_idx) > 0)
++GL(dl_tls_generation);
_dl_allocate_tls_init (tcbp, true);
}
RTLD_TIMING_VAR (start);
rtld_timer_start (&start);
{
@@ -2286,10 +2298,6 @@ dl_main (const ElfW(Phdr) *phdr,
_dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0,
consider_profiling);
/* Add object to slot information data if necessary. */
if (l->l_tls_blocksize != 0 && __rtld_tls_init_tp_called)
_dl_add_to_slotinfo (l, true);
}
}
rtld_timer_stop (&relocate_time, start);
@@ -2310,12 +2318,7 @@ dl_main (const ElfW(Phdr) *phdr,
|| count_modids != _dl_count_modids ())
++GL(dl_tls_generation);
/* Now that we have completed relocation, the initializer data
for the TLS blocks has its final values and we can copy them
into the main thread's TLS area, which we allocated above.
Note: thread-local variables must only be accessed after completing
the next step. */
_dl_allocate_tls_init (tcbp, true);
/* TLS .tdata copy moved before the relocation loop above. */
/* And finally install it for the main thread. */
if (! __rtld_tls_init_tp_called)
+10
View File
@@ -81,5 +81,15 @@ __rtld_static_init (struct link_map *map)
dl->_dl_find_object = _dl_find_object;
dl->_dl_readonly_area = _dl_readonly_area;
extern uintptr_t __pointer_chk_guard_local attribute_hidden;
const ElfW(Sym) *guard_sym
= _dl_lookup_direct (map, "__pointer_chk_guard",
0x69f99cab, /* _dl_new_hash output. */
"GLIBC_PRIVATE",
0x0963cf85); /* _dl_elf_hash output. */
assert (guard_sym != NULL);
*(uintptr_t *) DL_SYMBOL_ADDRESS (map, guard_sym)
= __pointer_chk_guard_local;
__rtld_static_init_arch (map, dl);
}
+30 -7
View File
@@ -1,6 +1,5 @@
/* Test header that defines macros for GNU properties that need to be
used in some test assembly files where sysdep.h cannot be included
for some reason.
/* Test header that defines feature-marking macros used in some test
assembly files where sysdep.h cannot be included for some reason.
Copyright (C) 2024-2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
@@ -27,7 +26,6 @@
#define FEATURE_1_PAC 2
#define FEATURE_1_GCS 4
/* Add a NT_GNU_PROPERTY_TYPE_0 note. */
#define GNU_PROPERTY(type, value) \
.section .note.gnu.property, "a"; \
.p2align 3; \
@@ -41,7 +39,32 @@
.word 0; \
.text
/* Add GNU property note with the supported features to all asm code
where sysdep.h is included. */
GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_PAC|FEATURE_1_GCS)
#ifdef __ARM_BUILDATTR64_FV
/* Add AArch64 feature bits build attributes. */
# define FEATURE_1_AND_MARK(value) \
.aeabi_subsection aeabi_feature_and_bits, optional, ULEB128; \
.if ((value) & FEATURE_1_BTI); \
.aeabi_attribute Tag_Feature_BTI, 1; \
.else; \
.aeabi_attribute Tag_Feature_BTI, 0; \
.endif; \
.if ((value) & FEATURE_1_GCS); \
.aeabi_attribute Tag_Feature_GCS, 1; \
.else; \
.aeabi_attribute Tag_Feature_GCS, 0; \
.endif; \
.if ((value) & FEATURE_1_PAC); \
.aeabi_attribute Tag_Feature_PAC, 1; \
.else; \
.aeabi_attribute Tag_Feature_PAC, 0; \
.endif; \
.text
#else
/* Add a NT_GNU_PROPERTY_TYPE_0 note. */
# define FEATURE_1_AND_MARK(value) GNU_PROPERTY (FEATURE_1_AND, value)
#endif /* __ARM_BUILDATTR64_FV */
/* Add marking with the supported features to all asm code where this header
is included. */
FEATURE_1_AND_MARK (FEATURE_1_BTI | FEATURE_1_PAC | FEATURE_1_GCS)
#endif
+1
View File
@@ -0,0 +1 @@
#include "tst-atrandom-scrub.c"
+89
View File
@@ -0,0 +1,89 @@
/* Verify the AT_RANDOM bytes do not reveal the guards after startup.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* The loader (security_init) and the static startup code (__libc_start_main)
derive the stack and pointer guards from the AT_RANDOM bytes, scrub those
bytes, and refill them with fresh entropy unrelated to the guards. The
AT_RANDOM entry is kept, so getauxval (AT_RANDOM) keeps returning 16 random
bytes, but they no longer reveal the guards. Check that neither guard can
be reconstructed from AT_RANDOM and that no auxiliary vector entry holds a
guard value. */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/auxv.h>
#include <stackguard-macros.h>
#include <tls.h>
#include <support/check.h>
static int
do_test (void)
{
uintptr_t stack_guard = STACK_CHK_GUARD;
uintptr_t pointer_guard = POINTER_CHK_GUARD;
unsigned char *random = (unsigned char *) getauxval (AT_RANDOM);
if (random == NULL)
FAIL_UNSUPPORTED ("the kernel did not provide AT_RANDOM");
printf ("debug: stack guard = %0*jx\n",
(int) (2 * sizeof (uintptr_t)), (uintmax_t) stack_guard);
printf ("debug: pointer guard = %0*jx\n",
(int) (2 * sizeof (uintptr_t)), (uintmax_t) pointer_guard);
printf ("debug: AT_RANDOM = ");
for (int i = 0; i < 16; i++)
printf ("%02x", random[i]);
printf ("\n");
/* Sanity check: the guards and AT_RANDOM must all have been populated
(not all-zero), otherwise we cannot tell scrub-and-reseed apart from
"never set up". */
TEST_VERIFY (stack_guard != 0);
TEST_VERIFY (pointer_guard != 0);
bool random_all_zero = true;
for (int i = 0; i < 16; i++)
if (random[i] != 0)
{
random_all_zero = false;
break;
}
TEST_VERIFY (!random_all_zero);
/* Reconstruct the guards from the (reseeded) AT_RANDOM bytes the way the
loader does and check that they no longer match the live guards. */
uintptr_t recovered_stack;
memcpy (&recovered_stack, random, sizeof (recovered_stack));
#if __BYTE_ORDER == __LITTLE_ENDIAN
recovered_stack &= ~(uintptr_t) 0xff;
#else
recovered_stack &= ~((uintptr_t) 0xff << (8 * (sizeof (recovered_stack) - 1)));
#endif
TEST_VERIFY (recovered_stack != stack_guard);
uintptr_t recovered_pointer;
memcpy (&recovered_pointer, random + sizeof (uintptr_t),
sizeof (recovered_pointer));
TEST_VERIFY (recovered_pointer != pointer_guard);
return 0;
}
#include <support/test-driver.c>
+67
View File
@@ -0,0 +1,67 @@
/* Test that dlopen on a large-e_phnum DSO does not overflow the stack when
called from a thread with PTHREAD_STACK_MIN stack size (BZ #26577).
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Before the fix for BZ #26577, _dl_map_object_from_fd and open_verify both
used alloca(e_phnum * sizeof(ElfW(Phdr))) when the program-header table did
not fit in the initial filebuf read. Calling dlopen from a thread created
with PTHREAD_STACK_MIN stack size (typically 16 KB on Linux) would overflow
the stack even for moderate e_phnum values, and catastrophically for the
0x7FFF-header DSO used here.
The crafted DSO (tst-bz26577-mod.so, generated by gen-tst-bz26577-mod.py)
has e_phnum == 0x7FFF: one PT_LOAD covering the ELF header and the rest
PT_NULL. dlopen on it must return NULL (no PT_DYNAMIC) without triggering
a stack overflow. */
#include <dlfcn.h>
#include <limits.h>
#include <stdlib.h>
#include <support/check.h>
#include <support/support.h>
#include <support/xthread.h>
static void *
dlopen_thread (void *arg)
{
/* Attempt to load the large-phnum module from a PTHREAD_STACK_MIN thread.
With the old alloca-based code this overflows the stack; with the fix the
load fails gracefully because the module has no PT_DYNAMIC. */
void *h = dlopen ((const char *) arg, RTLD_LAZY);
TEST_VERIFY (h == NULL);
return NULL;
}
static int
do_test (void)
{
char *path = xasprintf ("%s/elf/tst-bz26577-mod.so", support_objdir_root);
size_t stacksize = support_small_thread_stack_size (true);
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, stacksize);
pthread_t thr = xpthread_create (&attr, dlopen_thread, path);
xpthread_join (thr);
xpthread_attr_destroy (&attr);
free (path);
return 0;
}
#include <support/test-driver.c>
+169
View File
@@ -0,0 +1,169 @@
/* Tests for BZ #26577: stack overflow when loading a crafted ELF with
large e_phnum in _dl_map_object_from_fd.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* The crafted ELF is generated at build time by gen-tst-bz26577-mod.py
(elf/tst-bz26577-mod.so) with an ET_DYN with e_phnum = 0x7FFF, one PT_LOAD
segment covering the ELF header and the remaining headers PT_NULL.
This test exercises the loader against that crafted module via two
subtests, each run as a fresh exec with a reduced stack limit:
1. dlopen() subtest (--restart dlopen <path>): call dlopen on the
crafted .so.
2. LD_PRELOAD startup subtest (--restart, with LD_PRELOAD set): the
dynamic linker attempts to load the crafted .so at startup.
In both cases loader should fail to load the DSO, but without triggering
errors like SEGFAULT. */
#include <dlfcn.h>
#include <elf.h>
#include <getopt.h>
#include <link.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>
#include <support/check.h>
#include <support/subprocess.h>
#include <support/support.h>
/* Number of program headers in the crafted ELF. Must match EVIL_PHNUM
in gen-tst-bz26577-mod.py. */
#define EVIL_PHNUM UINT16_C (0x7FFF)
static int restart;
#define CMDLINE_OPTIONS \
{ "restart", no_argument, &restart, 1 },
static const char *test_binary;
/* Reduced stack size used in subprocess tests. The 1 MB headroom should
cover the loader required call chain. */
static size_t
evil_stack_size (void)
{
return (size_t) EVIL_PHNUM * sizeof (ElfW(Phdr)) + 1024 * 1024;
}
struct subtest_args
{
const char *mod_path;
const char *subtest;
};
static void
run_with_limited_stack (void *closure)
{
const struct subtest_args *args = closure;
struct rlimit rl;
TEST_VERIFY_EXIT (getrlimit (RLIMIT_STACK, &rl) == 0);
rl.rlim_cur = evil_stack_size ();
TEST_VERIFY_EXIT (setrlimit (RLIMIT_STACK, &rl) == 0);
if (args->subtest == NULL)
setenv ("LD_PRELOAD", args->mod_path, 1);
char *spawn_argv[6];
int i = 0;
spawn_argv[i++] = (char *) test_binary;
spawn_argv[i++] = (char *) "--direct";
spawn_argv[i++] = (char *) "--restart";
if (args->subtest != NULL)
{
spawn_argv[i++] = (char *) args->subtest;
spawn_argv[i++] = (char *) args->mod_path;
}
spawn_argv[i] = NULL;
struct support_spawn_wrapped *w
= support_spawn_wrap (test_binary, spawn_argv, NULL, 0);
execve (w->path, (char *const *) w->argv, (char *const *) w->envp);
_exit (127);
}
/* Fork a child with a reduced stack limit and exec this binary to call
dlopen on MOD_PATH. */
static void
test_dlopen_large_phnum (const char *mod_path)
{
struct subtest_args args = { mod_path, "dlopen" };
struct support_subprocess proc
= support_subprocess (run_with_limited_stack, &args);
int status = support_process_wait (&proc);
if (WIFSIGNALED (status) && WTERMSIG (status) == SIGSEGV)
FAIL_EXIT1 ("dlopen test: child killed by SIGSEGV"
" (stack overflow from unfixed loadcmd VLA)");
}
/* Fork a child with a reduced stack limit and exec this binary with
LD_PRELOAD set to MOD_PATH. */
static void
test_startup_large_phnum (const char *mod_path)
{
struct subtest_args args = { mod_path, NULL };
struct support_subprocess proc
= support_subprocess (run_with_limited_stack, &args);
int status = support_process_wait (&proc);
if (WIFSIGNALED (status) && WTERMSIG (status) == SIGSEGV)
FAIL_EXIT1 ("startup test: child killed by SIGSEGV"
" (stack overflow from unfixed loadcmd VLA)");
}
static int
do_test (int argc, char *argv[])
{
if (restart)
{
/* dlopen subtest: argv[1] == "dlopen", argv[2] == module path. */
if (argc > 1 && strcmp (argv[1], "dlopen") == 0)
{
TEST_VERIFY (argc == 3);
void *h = dlopen (argv[2], RTLD_LAZY);
TEST_VERIFY (h == NULL);
}
/* LD_PRELOAD subtest: no extra args; loader already exercised the
code during startup before main() was reached. */
return 0;
}
/* We must have either one argument (hardcoded paths) or four arguments
(ld.so, --library-path, lib-path, binary) after the program name. */
TEST_VERIFY_EXIT (argc == 2 || argc == 5);
test_binary = argv[argc - 1];
char *mod_path = xasprintf ("%s/elf/tst-bz26577-mod.so",
support_objdir_root);
test_dlopen_large_phnum (mod_path);
test_startup_large_phnum (mod_path);
free (mod_path);
return 0;
}
#define TEST_FUNCTION_ARGV do_test
#include <support/test-driver.c>
+1 -1
View File
@@ -128,7 +128,7 @@ do_test_threads (bool set_guard)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
size_t stacksize = support_small_thread_stack_size ();
size_t stacksize = support_small_thread_stack_size (false);
void *stack = xmmap (0,
stacksize,
PROT_READ | PROT_WRITE,
+174
View File
@@ -0,0 +1,174 @@
/* Test dlopen through ld.so.cache with a cache entry longer than the
dl_scratch_buffer inline area.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This test populates the cache with a single library sitting in a directory
whose absolute path is far larger than the dl_scratch_buffer inline area
(256 bytes) and most of the way to PATH_MAX; the loader's cache lookup
therefore exercises the anonymous-mmap spill. The dlopen is also repeated
from a PTHREAD_STACK_MIN thread to demonstrate that the path no longer
consumes too much caller's stack. */
#include <dlfcn.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/xdlfcn.h>
#include <support/xstdio.h>
#include <support/xthread.h>
#include <support/xunistd.h>
/* ldconfig only indexes filenames starting with "lib", so the module is
deployed under the lib-prefixed name (MOD_DEPLOYED) in the deep directory
and dlopened by that name. */
#define MOD_BUILT "tst-dl-path-buf-mod.so"
#define MOD_DEPLOYED "libtst-dl-path-buf-mod.so"
#define MOD_SYMBOL "tst_dl_path_buf_mod_value"
#define MOD_EXPECTED 0xaabbccddu
/* Final absolute path of the deep directory holding the module; filled in by
setup (). Kept around so dlopen_module can sanity-print it on failure. */
static char *deep_dir;
static void
run_ldconfig (void *x)
{
char *prog = xasprintf ("%s/ldconfig", support_install_rootsbindir);
char *args[] = { prog, NULL };
execv (args[0], args);
FAIL_EXIT1 ("execv (%s): %m", prog);
}
/* Build /tst-dl-cache-long-path/d.../d.../d... with several long components,
totalling well past dl_scratch_buffer's inline area
(DL_SCRATCH_BUFFER_INLINE_SIZE = 256 bytes) and close to PATH_MAX. */
static char *
build_deep_directory (void)
{
enum { component_len = 250, components = 15 };
/* 14 * (1 + 240) = 3374 bytes of nesting, plus the base. */
char component[component_len + 1];
memset (component, 'd', component_len);
component[component_len] = '\0';
const char *base = "/tst-dl-cache-long-path";
size_t cap = strlen (base) + components * (1 + component_len) + 1;
char *path = xmalloc (cap);
strcpy (path, base);
xmkdirp (path, 0777);
add_temp_file (path);
for (int i = 0; i < components; ++i)
{
strcat (path, "/");
strcat (path, component);
xmkdirp (path, 0777);
add_temp_file (path);
}
return path;
}
static void
do_prepare (int argc, char **argv)
{
deep_dir = build_deep_directory ();
TEST_VERIFY (strlen (deep_dir) > 256);
char *src = xasprintf ("%s/elf/" MOD_BUILT, support_objdir_root);
char *dst = xasprintf ("%s/" MOD_DEPLOYED, deep_dir);
support_copy_file (src, dst);
add_temp_file (dst);
free (src);
free (dst);
char *conf = xasprintf ("%s/ld.so.conf", support_sysconfdir_prefix);
FILE *fp = xfopen (conf, "w");
fprintf (fp, "%s\n", deep_dir);
xfclose (fp);
free (conf);
xmkdirp ("/var/cache/ldconfig", 0777);
struct support_capture_subprocess r
= support_capture_subprocess (run_ldconfig, NULL);
support_capture_subprocess_check (&r, "ldconfig", 0, sc_allow_none);
support_capture_subprocess_free (&r);
}
#define PREPARE do_prepare
static void
__attribute_noinline__
dlopen_via_cache (volatile char *pressure)
{
if (pressure != NULL)
(void) *pressure;
void *h = xdlopen (MOD_DEPLOYED, RTLD_NOW | RTLD_LOCAL);
unsigned int (*fn) (void) = xdlsym (h, MOD_SYMBOL);
TEST_COMPARE (fn (), MOD_EXPECTED);
xdlclose (h);
}
/* Reduce the stack budget available to the dlopen call chain by
STACK_PRESSURE bytes. */
enum { STACK_PRESSURE = 5 * 1024 };
static void
__attribute_noinline__
dlopen_via_cache_under_pressure (void)
{
char filler[STACK_PRESSURE];
dlopen_via_cache (&filler[0]);
}
static void *
minstack_thread (void *closure)
{
dlopen_via_cache_under_pressure ();
return NULL;
}
static int
do_test (void)
{
/* Sanity: from the main thread (no stack pressure needed). */
dlopen_via_cache (NULL);
/* The motivating scenario: from a PTHREAD_STACK_MIN thread. Before
_dl_load_cache_lookup was converted to dl_scratch_buffer this would
have alloca'd ~3 KB mid-dlopen and risked overflowing. */
size_t stacksize = support_small_thread_stack_size (true);
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, stacksize);
pthread_t thr = xpthread_create (&attr, minstack_thread, NULL);
xpthread_join (thr);
xpthread_attr_destroy (&attr);
free (deep_dir);
return 0;
}
#include <support/test-driver.c>
+152
View File
@@ -0,0 +1,152 @@
/* Test that a long search path does not overflow loader startup stack.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This test reduces RLIMIT_STACK to a value that just covers regular loader
startup, then spawns a child whose dynamic-linker search path is
artificially long (16 pages of synthetic colon-separated entries).
On exec the kernel places argv+envp at the top of the initial stack and
rounds the reservation to page granularity, which would otherwise eat the
loader's entire budget on architectures with large pages (e.g. 64 KB-page
aarch64).
Some kernels enforce that exec's argv+envp does not exceed RLIMIT_STACK/4;
the constraints here (envp+envp_copy must exceed RLIMIT_STACK) mean the
scenario cannot be set up on such kernels, in which case posix_spawn
returns E2BIG and the test is marked UNSUPPORTED. */
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <spawn.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>
#include <support/check.h>
#include <support/subprocess.h>
#include <support/support.h>
#include <support/xunistd.h>
static int restart;
#define CMDLINE_OPTIONS \
{ "restart", no_argument, &restart, 1 },
enum { llp_entries = 16 };
/* Return a newly malloc'd string of the form "PREFIX[:/ddd...]{16}"
where each synthetic entry is ENTRY_LEN bytes long. PREFIX is the
ld.so --library-path value from support_spawn_wrap and supplies the
real build directories so the rtld can still resolve libc. */
static char *
build_long_library_path (const char *prefix, size_t entry_len)
{
size_t prefix_len = strlen (prefix);
size_t junk_len = (size_t) llp_entries * (1 + entry_len);
char *out = xmalloc (prefix_len + junk_len + 1);
char *p = stpcpy (out, prefix);
for (int i = 0; i < llp_entries; i++)
{
*p++ = ':';
*p++ = '/';
memset (p, 'd', entry_len - 1);
p += entry_len - 1;
}
*p = '\0';
return out;
}
static int
do_test (void)
{
if (restart)
return 0;
char *binary = xasprintf ("%s/elf/tst-dl-llp-stack", support_objdir_root);
char *child_argv_in[] = {
binary, (char *) "--direct", (char *) "--restart", NULL
};
struct support_spawn_wrapped *w
= support_spawn_wrap (binary, child_argv_in, NULL,
support_spawn_wrap_force);
/* Scale envp and stack rlimit with PAGE_SIZE to handle kernels with
different page sizes. */
long page_size = sysconf (_SC_PAGESIZE);
TEST_VERIFY_EXIT (page_size > 0);
size_t entry_len = (size_t) page_size - 1;
size_t stack_limit = (size_t) 24 * page_size;
/* Extend the wrapped --library-path value with the synthetic junk.
Build a fresh argv whose slot 2 points at our long-paths string;
all other slots alias into the wrapped argv, which keeps ownership
of those strings. */
TEST_VERIFY_EXIT (w->argv[0] != NULL && w->argv[1] != NULL
&& w->argv[2] != NULL
&& strcmp (w->argv[1], "--library-path") == 0);
char *long_paths = build_long_library_path (w->argv[2], entry_len);
size_t nargs;
for (nargs = 0; w->argv[nargs] != NULL; nargs++)
;
char **child_argv = xcalloc (nargs + 1, sizeof (*child_argv));
for (size_t i = 0; i < nargs; i++)
child_argv[i] = (i == 2) ? long_paths : (char *) w->argv[i];
child_argv[nargs] = NULL;
/* Reduce the stack rlimit; the posix_spawn'd child inherits it. */
struct rlimit rl_save, rl_small;
TEST_VERIFY_EXIT (getrlimit (RLIMIT_STACK, &rl_save) == 0);
rl_small.rlim_cur = (rlim_t) stack_limit;
rl_small.rlim_max = rl_save.rlim_max;
TEST_VERIFY_EXIT (setrlimit (RLIMIT_STACK, &rl_small) == 0);
pid_t pid;
int spawn_ret = posix_spawn (&pid, w->path, NULL, NULL, child_argv,
(char *const *) w->envp);
TEST_VERIFY_EXIT (setrlimit (RLIMIT_STACK, &rl_save) == 0);
if (spawn_ret == E2BIG)
FAIL_UNSUPPORTED ("posix_spawn returned E2BIG: this kernel enforces "
"argv+envp <= RLIMIT_STACK/4");
if (spawn_ret != 0)
{
errno = spawn_ret;
FAIL_EXIT1 ("posix_spawn: %m");
}
int status;
TEST_COMPARE (xwaitpid (pid, &status, 0), pid);
if (WIFSIGNALED (status))
FAIL_EXIT1 ("child killed by signal %d", WTERMSIG (status));
TEST_VERIFY_EXIT (WIFEXITED (status));
TEST_COMPARE (WEXITSTATUS (status), 0);
free (child_argv);
free (long_paths);
support_spawn_wrapped_free (w);
free (binary);
return 0;
}
#include <support/test-driver.c>
+23
View File
@@ -0,0 +1,23 @@
/* Trivial DSO used by tst-dl-path-buf.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
unsigned int
tst_dl_path_buf_mod_value (void)
{
return 0xaabbccddu;
}
+220
View File
@@ -0,0 +1,220 @@
/* Exercise the mmap-backed path scratch buffer in elf/dl-load.c.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* open_path()'s scratch buffer comes from dl_scratch_buffer, which uses
anonymous mmap while __minimal_malloc is active (during loader startup)
and libc malloc afterwards. Mappings from the mmap backend are tagged
with the VMA name " glibc: loader scratch". This test exercises the
relevant code paths -- search via DT_RPATH, open_path failure cleanup,
dlopen with an over-long name, dlopen from a minimal-stack thread,
and per-backend leak checks -- to verify each path properly
alloc/frees the scratch buffer. */
#include <dlfcn.h>
#include <errno.h>
#include <limits.h>
#include <mcheck.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <support/check.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/xdlfcn.h>
#include <support/xstdio.h>
#include <support/xthread.h>
#include <support/xunistd.h>
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif
/* Must match LDFLAGS-tst-dl-path-buf in elf/Makefile. The test binary's
DT_RPATH resolves to $ORIGIN of the binary plus this subdirectory. */
#define MOD_SUBDIR "tst-dl-path-buf-subdir"
#define MOD_NAME "tst-dl-path-buf-mod.so"
/* Tag installed by _dl_scratch_buffer_allocate via __set_vma_name. Mappings
in /proc/self/maps annotated with this string belong to a live scratch
buffer; after a successful dlopen/dlclose cycle there must be zero of
them. */
#define SCRATCH_VMA_TAG "[anon: glibc: loader scratch]"
/* Open MOD_NAME via DT_RPATH. Returns the handle; the caller closes it. */
static void
dlopen_module (void)
{
void *h = xdlopen (MOD_NAME, RTLD_NOW | RTLD_LOCAL);
unsigned int (*fn) (void) = xdlsym (h, "tst_dl_path_buf_mod_value");
TEST_COMPARE (fn (), 0xaabbccddu);
xdlclose (h);
}
/* Subtest 1: basic dlopen/dlclose via DT_RPATH search. */
static void
test_basic (void)
{
dlopen_module ();
}
/* Subtest 2: non-existent name: open_path is exercised on every search list
(DT_RPATH then cache then __rtld_search_dirs), failing each time. Each
failure path must release its scratch buffer. */
static void
test_nonexistent (void)
{
void *h = dlopen ("tst-dl-path-buf-does-not-exist.so",
RTLD_NOW | RTLD_LOCAL);
TEST_VERIFY (h == NULL);
}
/* Subtest 3: a name whose resolved length far exceeds PATH_MAX cannot refer
to a real file: open_path will allocate a large scratch buffer, build
candidate paths, and have every open() return ENAMETOOLONG. dlopen must
therefore fail cleanly (and without leaking the scratch buffer on the
failure paths). */
static void
test_overlong_name (void)
{
char *huge = xmalloc (PATH_MAX + 64);
memset (huge, 'a', PATH_MAX + 32);
memcpy (huge + PATH_MAX, ".so", 4);
void *h = dlopen (huge, RTLD_NOW | RTLD_LOCAL);
TEST_VERIFY (h == NULL);
free (huge);
}
/* Count anonymous mappings in /proc/self/maps annotated with SCRATCH_VMA_TAG.
Used by the mmap-backend leak subtest below. */
static unsigned int
count_scratch_mappings (void)
{
FILE *f = xfopen ("/proc/self/maps", "r");
unsigned int n = 0;
char *line = NULL;
size_t line_len = 0;
while (xgetline (&line, &line_len, f))
if (strstr (line, SCRATCH_VMA_TAG) != NULL)
++n;
free (line);
xfclose (f);
return n;
}
/* Subtest 4a (mmap backend). dl_scratch_buffer's mmap backend is used while
__rtld_malloc_is_complete returns false -- that window covers the entire
loader-startup phase, during which the loader resolves the test binary's
DT_NEEDED dependencies via open_path() (and so allocates and frees scratch
buffers). */
static void
test_no_leak_mmap (void)
{
if (!support_set_vma_name_supported ())
{
printf ("info: skipping mmap-backend leak subtest:"
" kernel does not support PR_SET_VMA_ANON_NAME\n");
return;
}
unsigned int residual = count_scratch_mappings ();
if (residual != 0)
FAIL_EXIT1 ("%u leaked loader scratch mapping(s) survived loader"
" startup -- _dl_scratch_buffer_free's mmap backend"
" is broken", residual);
}
/* Subtest 4b (malloc backend). Once libc malloc is active,
dl_scratch_buffer_allocate routes through malloc and the mapping VMA tag is
no longer used. Drive enough dlopen success+failure cycles to exercise
every path in dl_scratch_buffer_free. */
static void
test_no_leak_malloc (void)
{
mtrace ();
enum { iterations = 10 };
for (unsigned int i = 0; i < iterations; ++i)
{
dlopen_module ();
void *nh = dlopen ("tst-dl-path-buf-does-not-exist.so",
RTLD_NOW | RTLD_LOCAL);
TEST_VERIFY (nh == NULL);
}
}
/* Subtest 5. Run the success path from a PTHREAD_STACK_MIN thread. */
static void *
minstack_thread (void *closure)
{
dlopen_module ();
void *nh = dlopen ("tst-dl-path-buf-does-not-exist.so",
RTLD_NOW | RTLD_LOCAL);
TEST_VERIFY (nh == NULL);
return NULL;
}
static void
test_minstack (void)
{
size_t stacksize = support_small_thread_stack_size (true);
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, stacksize);
pthread_t thr = xpthread_create (&attr, minstack_thread, NULL);
xpthread_join (thr);
xpthread_attr_destroy (&attr);
}
static int
do_test (void)
{
support_need_proc ("/proc/self/maps is read for the leak subtest.");
char *subdir = xasprintf ("%s/elf/" MOD_SUBDIR, support_objdir_root);
xmkdirp (subdir, 0777);
add_temp_file (subdir);
char *src = xasprintf ("%s/elf/" MOD_NAME, support_objdir_root);
char *dst = xasprintf ("%s/" MOD_NAME, subdir);
support_copy_file (src, dst);
add_temp_file (dst);
/* Check the mmap backend's startup behavior first, before any
subtest can perturb /proc/self/maps with its own allocations. */
test_no_leak_mmap ();
test_basic ();
test_nonexistent ();
test_overlong_name ();
test_no_leak_malloc ();
test_minstack ();
free (src);
free (dst);
free (subdir);
return 0;
}
#include <support/test-driver.c>
+20
View File
@@ -0,0 +1,20 @@
/* Leaf DSO whose SONAME contains several DST tokens. Used by
tst-dst-needed-minstack via tst-dst-needed-wrap-mod.so.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
int tst_dst_needed_leaf_dummy;
+122
View File
@@ -0,0 +1,122 @@
/* Test that dlopen of a library whose DT_NEEDED string carries
several dynamic-string tokens does not overflow a
PTHREAD_STACK_MIN-sized thread.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* The leaf library is linked with a SONAME containing five $DST tokens; the
wrapper library links against the leaf so its DT_NEEDED inherits that
string. This test deploys the wrapper in a ~3 KB deep directory (so the
wrapper's l_origin matches), then dlopens it from a PTHREAD_STACK_MIN
thread. The leaf is not actually reachable through the (impossible)
expanded path, so the dlopen is expected to fail -- the regression
assertion is that the failure occurs without a stack overflow. */
#include <dlfcn.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <support/check.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/xstdio.h>
#include <support/xthread.h>
#include <support/xunistd.h>
#define WRAP_MOD "tst-dst-needed-wrap-mod.so"
/* Set by do_prepare; the absolute path of the wrapper as deployed in the
deep directory. */
static char *deep_wrap_path;
/* Build <temp>/<long components>/ and return the final path; intermediates
are registered with add_temp_file so cleanup is automatic. */
static char *
build_deep_directory (void)
{
enum { component_len = 240, components = 14 };
char component[component_len + 1];
memset (component, 'd', component_len);
component[component_len] = '\0';
char *base = support_create_temp_directory ("tst-dst-needed-");
size_t cap = strlen (base) + components * (1 + component_len) + 1;
char *path = xmalloc (cap);
strcpy (path, base);
free (base);
for (int i = 0; i < components; ++i)
{
strcat (path, "/");
strcat (path, component);
xmkdirp (path, 0777);
add_temp_file (path);
}
return path;
}
static void
do_prepare (int argc, char **argv)
{
char *deep_dir = build_deep_directory ();
/* Deep enough that l_origin alone is well over PTHREAD_STACK_MIN. */
TEST_VERIFY (strlen (deep_dir) > 256);
char *src = xasprintf ("%s/elf/" WRAP_MOD, support_objdir_root);
deep_wrap_path = xasprintf ("%s/" WRAP_MOD, deep_dir);
support_copy_file (src, deep_wrap_path);
add_temp_file (deep_wrap_path);
free (src);
free (deep_dir);
}
#define PREPARE do_prepare
static void *
minstack_thread (void *closure)
{
/* Trigger DT_NEEDED expansion on the deep wrapper. The leaf's five-$DST
SONAME, expanded against the wrapper's deep l_origin, produces a buffer
of several KB inside _dl_map_object_deps. We do not care whether the
leaf is actually findable -- the test passes if and only if the dlopen
returns without a stack overflow. */
void *h = dlopen (deep_wrap_path, RTLD_NOW);
TEST_VERIFY_EXIT (h == NULL);
return NULL;
}
static int
do_test (void)
{
size_t stacksize = support_small_thread_stack_size (true);
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, stacksize);
pthread_t thr = xpthread_create (&attr, minstack_thread, NULL);
xpthread_join (thr);
xpthread_attr_destroy (&attr);
free (deep_wrap_path);
return 0;
}
#include <support/test-driver.c>
+21
View File
@@ -0,0 +1,21 @@
/* Wrapper DSO whose DT_NEEDED string inherits the DST-laden SONAME
of tst-dst-needed-leaf-mod.so. Loading this from a
PTHREAD_STACK_MIN thread is what tst-dst-needed-minstack exercises.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
int tst_dst_needed_wrap_dummy;
+1 -1
View File
@@ -46,7 +46,7 @@ do_test (void)
{
/* Install the default implementation of libmarkermod1.so. */
char *conf_path = xasprintf ("%s/ld.so.conf", support_sysconfdir_prefix);
char *conf_path = xasprintf ("%s/ld.so.conf.d/hwcaps.conf", support_sysconfdir_prefix);
xmkdirp (support_sysconfdir_prefix, 0777);
support_write_file_string (conf_path, "/glibc-test/lib\n");
free (conf_path);
+61
View File
@@ -0,0 +1,61 @@
/* BZ 28817: TLS read from a static-pie IFUNC resolver.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <stdlib.h>
#include <support/check.h>
__thread int bar;
extern __thread int bar_gd asm ("bar")
__attribute__ ((tls_model("global-dynamic")));
static int *bar_ptr;
int foo (void);
static void
init_foo (void)
{
bar_ptr = &bar_gd;
}
static int
my_foo (void)
{
return bar_ptr != NULL;
}
static __typeof (foo) *
inhibit_stack_protector
__attribute_used__
foo_ifunc (void)
{
init_foo ();
__typeof (foo) *res = my_foo;
return res;
};
__typeof (foo) foo __attribute__ ((ifunc ("foo" "_ifunc")));
static int
do_test (void)
{
TEST_VERIFY (foo ());
TEST_VERIFY (&bar == bar_ptr);
return 0;
}
#include <support/test-driver.c>
+24
View File
@@ -0,0 +1,24 @@
/* Test that IRELATIVE resolvers may call PLT functions with LD_BIND_NOW=1.
Copyright (C) 2019-2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Same as tst-ifunc-plt, but the test is run with LD_BIND_NOW=1 so the
library is processed eagerly. Under eager binding the JMP_SLOT entries
in .rela.plt are resolved by elf_machine_rel during relocation; this
exercises the eager path of the deferred IRELATIVE processing. */
#include "tst-ifunc-plt.c"
+23
View File
@@ -0,0 +1,23 @@
/* Dependency library for tst-ifunc-plt.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
int
get_value (void)
{
return 42;
}
+45
View File
@@ -0,0 +1,45 @@
/* Test that IRELATIVE resolvers may call PLT functions via dlopen + RTLD_NOW.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Same as tst-ifunc-plt-dlopen, but uses RTLD_NOW to force eager
relocation of the loaded library. This exercises the dlopen entry
point along the eager-binding path. */
#include <support/xdlfcn.h>
#include <support/check.h>
typedef int (*fn_t) (void);
static int
do_test (void)
{
void *handle = xdlopen ("tst-ifunc-plt-lib.so", RTLD_NOW | RTLD_LOCAL);
fn_t compute_a = (fn_t) xdlsym (handle, "compute_a");
TEST_COMPARE (compute_a (), 1);
fn_t compute_b = (fn_t) xdlsym (handle, "compute_b");
TEST_COMPARE (compute_b (), 2);
xdlclose (handle);
return 0;
}
#include <support/test-driver.c>
+46
View File
@@ -0,0 +1,46 @@
/* Test that IRELATIVE resolvers may call PLT functions on a dlopen'd library.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* dlopen tst-ifunc-plt-lib.so with RTLD_LAZY and verify that the IFUNC
resolver (which calls get_value() via PLT) ran successfully. This
exercises the same _dl_relocate_object code path as startup loading
but via the dlopen entry point. */
#include <support/xdlfcn.h>
#include <support/check.h>
typedef int (*fn_t) (void);
static int
do_test (void)
{
void *handle = xdlopen ("tst-ifunc-plt-lib.so", RTLD_LAZY | RTLD_LOCAL);
fn_t compute_a = (fn_t) xdlsym (handle, "compute_a");
TEST_COMPARE (compute_a (), 1);
fn_t compute_b = (fn_t) xdlsym (handle, "compute_b");
TEST_COMPARE (compute_b (), 2);
xdlclose (handle);
return 0;
}
#include <support/test-driver.c>
+59
View File
@@ -0,0 +1,59 @@
/* Shared library for tst-ifunc-plt.
Two static IFUNCs whose resolvers both call get_value() via PLT.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Both resolvers call get_value() via PLT (one JUMP_SLOT entry in
.rel{a}.plt). This verifies that every IRELATIVE entry is deferred
until after .rela.plt has been processed, not just the first one. */
#include <sys/cdefs.h>
#include <stddef.h>
extern int get_value (void);
static int
impl_a (void)
{
return 1;
}
static int
impl_b (void)
{
return 2;
}
static typeof (impl_a) *
__attribute_used__
resolve_a (void)
{
return get_value () == 42 ? impl_a : NULL;
}
static typeof (impl_b) *
__attribute_used__
resolve_b (void)
{
return get_value () == 42 ? impl_b : NULL;
}
/* The test is only built for $(have-ifunc), so we can assume HAVE_GCC_IFUNC
here. */
int compute_a (void) __attribute__ ((ifunc ("resolve_a")));
int compute_b (void) __attribute__ ((ifunc ("resolve_b")));
+38
View File
@@ -0,0 +1,38 @@
/* Test that IRELATIVE resolvers may call PLT functions during startup.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* tst-ifunc-plt-multi-lib.so defines two static IFUNCs (compute_a and
compute_b), each producing an R_*_IRELATIVE in .rel{a}.dyn, with both
resolvers calling get_value() via PLT. The test verifies that both
IRELATIVEs are deferred until after .rel{a}.plt is processed. */
#include <support/check.h>
extern int compute_a (void);
extern int compute_b (void);
static int
do_test (void)
{
TEST_COMPARE (compute_a (), 1);
TEST_COMPARE (compute_b (), 2);
return 0;
}
#include <support/test-driver.c>
+64
View File
@@ -0,0 +1,64 @@
/* Stack-protector-instrumented IFUNC resolver in a shared library, used
by tst-ifunc-resolver-protector. BZ #27582.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Built with -fstack-protector-all so the resolver's prologue/epilogue
carries the full canary check. Reaching the return statement means the
canary load did not fault (TCB-canary ABIs) and the canary compare did not
call __stack_chk_fail. The dynamic linker calls security_init() before the
main relocation loop, so resolvers fired from startup or dlopen should
always observe an initialised canary; this test guards against any future
reordering that would break that invariant. */
#include <sys/cdefs.h>
#define SENTINEL 0x5A5A1234
static volatile int resolver_ran;
int
get_resolver_ran (void)
{
return resolver_ran;
}
static int
impl_ok (int x)
{
return x + SENTINEL;
}
typedef int (*fn_t) (int);
static fn_t
__attribute_used__
resolver (void)
{
/* Buffer + zero-fill force -fstack-protector-all canary code. */
volatile char buf[32];
for (unsigned i = 0; i < sizeof (buf); ++i)
buf[i] = 0;
resolver_ran = 1;
return impl_ok;
}
int compute (int) __attribute__ ((ifunc ("resolver")));
/* Address taken in DSO data to force an R_*_IRELATIVE in .rela.dyn (a
non-PLT relocation against the IFUNC). */
int (*fptr) (int) = compute;
@@ -0,0 +1,69 @@
/* Stack-protector-instrumented IFUNC resolver used by
tst-ifunc-resolver-protector-static. BZ #34164.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This translation unit is built with -fstack-protector-all so that the
compiler instruments the resolver below. The resolver's prologue loads
the canary from the TCB (TCB-canary ABIs) or from __stack_chk_guard
(global-var ABIs); the epilogue compares it against the on-stack copy.
Pre-fix the prologue load either faulted (TCB-canary) or loaded zero
(global-var).
The resolver also records the canary value via STACK_CHK_GUARD so the
test can verify it matches what main observes. */
#include <stackguard-macros.h>
#include <stdint.h>
#include <tls.h>
#define SENTINEL 0x5A5A1234
static volatile uintptr_t resolver_canary;
uintptr_t
get_resolver_canary (void)
{
return resolver_canary;
}
static int
impl_ok (int x)
{
return x + SENTINEL;
}
typedef int (*fn_t) (int);
static fn_t
__attribute_used__
resolver (void)
{
/* Buffer forces -fstack-protector-all to emit canary code even with
no other reason to. */
volatile char buf[32];
for (unsigned i = 0; i < sizeof (buf); ++i)
buf[i] = 0;
resolver_canary = STACK_CHK_GUARD;
return impl_ok;
}
int compute (int) __attribute__ ((ifunc ("resolver")));
int (*fptr) (int) = compute;
@@ -0,0 +1,2 @@
/* Companion module for tst-ifunc-resolver-protector-static-non-pie. */
#include "tst-ifunc-resolver-protector-static-mod.c"
@@ -0,0 +1,5 @@
/* Same coverage as tst-ifunc-resolver-protector-static, but linked as non-PIE
static. Exercises the apply_irel / __rela_iplt_start path instead of the
static-pie _dl_relocate_static_pie_ifunc path. */
#include "tst-ifunc-resolver-protector-static.c"
+61
View File
@@ -0,0 +1,61 @@
/* Check that a stack-protector-instrumented IFUNC resolver works in a
static binary. BZ #34164.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* When a static binary's IFUNC resolver is compiled with stack protector, the
resolver prologue loads the canary from the TCB (TCB-canary ABIs: x86_64,
i386, powerpc, s390) or from the global __stack_chk_guard (other ABIs).
The test checks if TCB-canary or the global-var is properly initialized:
1. The resolver runs without SIGSEGV (TCB-canary) or SIGABRT (canary
mismatch).
2. The resolver records the canary value it observed via STACK_CHK_GUARD,
and main asserts it equals the value visible from user code -- catching
the "silent zero canary" variant on global-var ABIs. */
#include <stackguard-macros.h>
#include <stdint.h>
#include <tls.h>
#include <support/check.h>
#define SENTINEL 0x5A5A1234
extern int compute (int);
extern uintptr_t get_resolver_canary (void);
static int
do_test (void)
{
/* compute() returns its argument + SENTINEL iff the resolver picked
impl_ok, which it does whenever the canary check at the resolver
prologue / epilogue did not abort. */
TEST_COMPARE (compute (1), 1 + SENTINEL);
/* Silent-variant check: the canary the resolver loaded must match the
canary do_test reads. On global-var ABIs, it checks if the stack
protector cookie is properly initialised. */
uintptr_t resolver_canary = get_resolver_canary ();
uintptr_t main_canary = STACK_CHK_GUARD;
TEST_VERIFY (resolver_canary != 0);
TEST_COMPARE (resolver_canary, main_canary);
return 0;
}
#include <support/test-driver.c>
+42
View File
@@ -0,0 +1,42 @@
/* Check that a stack-protector-instrumented IFUNC resolver works in a
dynamically-linked binary. BZ #27582.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Dynamic counterpart to tst-ifunc-resolver-protector-static.
The dynamic linker calls security_init() before the main relocation
loop, so a stack-protected resolver fired from startup should observe
an initialised canary -- but having a regression test guards against
any future reordering that would break that invariant. */
#include <support/check.h>
#define SENTINEL 0x5A5A1234
extern int compute (int);
extern int get_resolver_ran (void);
static int
do_test (void)
{
TEST_COMPARE (compute (1), 1 + SENTINEL);
TEST_VERIFY (get_resolver_ran () != 0);
return 0;
}
#include <support/test-driver.c>
+55
View File
@@ -0,0 +1,55 @@
/* Shared library for tst-ifunc-tls-init-dlopen.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Same as tst-ifunc-tls-init-lib.c, but exercises the dlopen path. */
#define SENTINEL 0x5A5A1234
static volatile __thread int sentinel
__attribute__ ((tls_model ("initial-exec"))) = SENTINEL;
static volatile int last_seen_sentinel;
static int
impl_ok (void)
{
return SENTINEL;
}
static int
impl_bad (void)
{
return 0;
}
int
get_last_seen_sentinel (void)
{
return last_seen_sentinel;
}
static int (*resolver (void)) (void)
{
int s = sentinel;
last_seen_sentinel = s;
return s == SENTINEL ? impl_ok : impl_bad;
}
int ifunc_tls (void) __attribute__ ((ifunc ("resolver")));
/* Force a non-PLT relocation against the IFUNC symbol. */
int (*fptr) (void) = ifunc_tls;
+3
View File
@@ -0,0 +1,3 @@
#define TLS_MODEL "global-dynamic"
#define SENTINEL_STORAGE /* empty */
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
@@ -0,0 +1,73 @@
/* Shared-library skeleton for tst-ifunc-tls-init-gd-ld.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Unlike tst-ifunc-tls-init-lib-skeleton.c, which uses initial-exec and
resolves via a single TP-relative load, this skeleton's TLS_MODEL is
either "global-dynamic" or "local-dynamic" so the resolver's read of
'sentinel' must traverse __tls_get_addr (or the architecture's
TLSDESC equivalent). That dynamic-TLS access can lazily allocate a
per-module TLS block, which is the path being exercised. */
#include <sys/cdefs.h>
#ifndef TLS_MODEL
# error "tst-ifunc-tls-init-gd-ld-lib-skeleton.c needs TLS_MODEL defined"
#endif
/* Without static the relocation against the SENTINEL goes through the regular
global-symbol lookup path; combined with TLS_MODEL="global-dynamic" this
exercises the "global GD" variant rather than the file-local one. */
#ifndef SENTINEL_STORAGE
# define SENTINEL_STORAGE static
#endif
#define SENTINEL 0x5A5A1234
SENTINEL_STORAGE volatile __thread int sentinel
__attribute__ ((tls_model (TLS_MODEL))) = SENTINEL;
static volatile int last_seen_sentinel;
static int
impl_ok (void)
{
return SENTINEL;
}
static int
impl_bad (void)
{
return 0;
}
int
get_last_seen_sentinel (void)
{
return last_seen_sentinel;
}
static typeof (impl_ok) *
__attribute_used__
resolver (void)
{
int s = sentinel;
last_seen_sentinel = s;
return s == SENTINEL ? impl_ok : impl_bad;
}
int ifunc_tls (void) __attribute__ ((ifunc ("resolver")));
int (*fptr) (void) = ifunc_tls;
+89
View File
@@ -0,0 +1,89 @@
/* Check if dynamic-TLS variables (global-dynamic, local-dynamic) are
correctly initialised in IFUNC resolvers reached via dlopen.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This test dlopens three modules covering the dynamic-TLS access variants:
gd-lib file-local 'sentinel' + tls_model("global-dynamic")
ld-lib file-local 'sentinel' + tls_model("local-dynamic")
gd-global-lib external 'sentinel' + tls_model("global-dynamic")
In each case the IFUNC resolver's read of 'sentinel' must traverse the
dynamic-TLS resolution path at the moment the resolver fires during
dlopen-time relocation.
Each variant additionally calls the resolved IFUNC from a thread spawned
after dlopen, to verify per-thread DTV propagation for the newly-loaded
module. */
#include <support/check.h>
#include <support/xdlfcn.h>
#include <support/xthread.h>
#define SENTINEL 0x5A5A1234
struct ifunc_handles
{
int (*get_last_seen_sentinel) (void);
int (**fptr) (void);
int (*ifunc_tls) (void);
};
static void *
ifunc_caller (void *arg)
{
struct ifunc_handles *h = arg;
TEST_COMPARE ((*h->fptr) (), SENTINEL);
TEST_COMPARE (h->ifunc_tls (), SENTINEL);
return NULL;
}
static void
test_lib (const char *soname)
{
void *handle = xdlopen (soname, RTLD_LAZY | RTLD_LOCAL);
struct ifunc_handles h;
h.get_last_seen_sentinel = xdlsym (handle, "get_last_seen_sentinel");
h.fptr = xdlsym (handle, "fptr");
h.ifunc_tls = xdlsym (handle, "ifunc_tls");
TEST_COMPARE (h.get_last_seen_sentinel (), SENTINEL);
TEST_VERIFY (*h.fptr != NULL);
TEST_COMPARE ((*h.fptr) (), SENTINEL);
TEST_COMPARE (h.ifunc_tls (), SENTINEL);
/* From a thread spawned *after* the dlopen, which exercises DTV propagation
for the new module into a fresh TCB. */
pthread_t consumer = xpthread_create (NULL, ifunc_caller, &h);
xpthread_join (consumer);
xdlclose (handle);
}
static int
do_test (void)
{
test_lib ("tst-ifunc-tls-init-gd-lib.so");
test_lib ("tst-ifunc-tls-init-ld-lib.so");
test_lib ("tst-ifunc-tls-init-gd-global-lib.so");
return 0;
}
#include <support/test-driver.c>
+2
View File
@@ -0,0 +1,2 @@
#define TLS_MODEL "global-dynamic"
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
+2
View File
@@ -0,0 +1,2 @@
#define TLS_MODEL "local-dynamic"
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
+61
View File
@@ -0,0 +1,61 @@
/* Test that static-TLS initialisation works correctly with IFUNC resolvers.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* The initial-exec TLS model keeps the access path to a single TP-relative
load, so the test is sensitive to whether the static TLS block has been
populated rather than to any __tls_get_addr / DTV-update timing. */
#include <sys/cdefs.h>
#define SENTINEL 0x5A5A1234
/* The 'volatile' avoids constant fold optimization in impl_ok. */
static volatile __thread int sentinel
__attribute__ ((tls_model ("initial-exec"))) = SENTINEL;
static volatile int last_seen_sentinel;
static int
impl_ok (void)
{
return SENTINEL;
}
static int
impl_bad (void)
{
return 0;
}
int
get_last_seen_sentinel (void)
{
return last_seen_sentinel;
}
static typeof (impl_ok) *
__attribute_used__
resolver (void)
{
int s = sentinel;
last_seen_sentinel = s;
return s == SENTINEL ? impl_ok : impl_bad;
}
int ifunc_tls (void) __attribute__ ((ifunc ("resolver")));
/* Force a non-PLT relocation against the IFUNC symbol. */
int (*fptr) (void) = ifunc_tls;
+1
View File
@@ -0,0 +1 @@
#include "tst-ifunc-tls-init-lib-skeleton.c"
+1
View File
@@ -0,0 +1 @@
#include "tst-ifunc-tls-init-lib-skeleton.c"
+3
View File
@@ -0,0 +1,3 @@
#define TLS_MODEL "global-dynamic"
#define SENTINEL_STORAGE /* empty */
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
+69
View File
@@ -0,0 +1,69 @@
/* Check that an IFUNC resolver in a dlopen'd DSO can read .tdata-initialised
__thread storage when the TLS access is compiled as a TLSDESC sequence.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Sibling of tst-ifunc-tls-init-gd-ld, but the TLS access in the loaded
DSO is compiled with the TLSDESC dialect (x86 gnu2, aarch64 desc). */
#include <support/check.h>
#include <support/xdlfcn.h>
#include <support/xthread.h>
#define SENTINEL 0x5A5A1234
struct ifunc_handles
{
int (*get_last_seen_sentinel) (void);
int (**fptr) (void);
int (*ifunc_tls) (void);
};
static void *
ifunc_caller (void *arg)
{
struct ifunc_handles *h = arg;
TEST_COMPARE ((*h->fptr) (), SENTINEL);
TEST_COMPARE (h->ifunc_tls (), SENTINEL);
return NULL;
}
static int
do_test (void)
{
void *handle = xdlopen ("tst-ifunc-tls-init-tlsdesc-lib.so",
RTLD_LAZY | RTLD_LOCAL);
struct ifunc_handles h;
h.get_last_seen_sentinel = xdlsym (handle, "get_last_seen_sentinel");
h.fptr = xdlsym (handle, "fptr");
h.ifunc_tls = xdlsym (handle, "ifunc_tls");
TEST_COMPARE (h.get_last_seen_sentinel (), SENTINEL);
TEST_VERIFY (*h.fptr != NULL);
TEST_COMPARE ((*h.fptr) (), SENTINEL);
TEST_COMPARE (h.ifunc_tls (), SENTINEL);
pthread_t consumer = xpthread_create (NULL, ifunc_caller, &h);
xpthread_join (consumer);
xdlclose (handle);
return 0;
}
#include <support/test-driver.c>
+85
View File
@@ -0,0 +1,85 @@
/* Check if static-TLS variables are correctly intialized in IFUNC resolvers.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Checks if a IFUNC resolver sees if a TLS blocker is properly initialized.
The tst-ifunc-tls-init-lib.so carries:
- a thread-local 'sentinel' initialised to SENTINEL.
- an IFUNC 'ifunc_tls' whose resolver picks impl_ok when sentinel reads
as SENTINEL and impl_bad (returning 0) otherwise.
- an IFUNC-backed function-pointer global 'fptr' whose initialiser
produces a non-PLT relocation. */
#include <support/check.h>
#include <support/xdlfcn.h>
#define SENTINEL 0x5A5A1234
extern int ifunc_tls (void);
extern int (*fptr) (void);
extern int get_last_seen_sentinel (void);
static void
test_tls_ifunc (int (*check_sentinel)(void),
int (*check_fptr)(void),
int (*check_ifunc_tls)(void))
{
/* Force the IFUNC to be resolved before inspecting what the resolver
observed. In a non-PIE executable the references to the IFUNC are
satisfied through a canonical IPLT entry in the executable, which under
lazy binding is resolved on first use rather than during startup
relocation. Calling fptr/ifunc_tls here guarantees the resolver has run
regardless of PIE-ness or binding mode. */
TEST_VERIFY (check_fptr != NULL);
TEST_COMPARE (check_fptr (), SENTINEL);
/* Issue the ifunc directly as well. */
TEST_COMPARE (check_ifunc_tls (), SENTINEL);
/* Primary check: 'get_last_seen_sentinel' returns the value of the DSO's
thread-local 'sentinel' as observed by the resolver at the moment it ran
for the IFUNC reloc that initialised fptr. The getter is a regular
PLT-resolved function in the DSO, so the read of the diagnostic global
does NOT go through a COPY relocation that could overwrite the resolver's
write. */
TEST_COMPARE (check_sentinel (), SENTINEL);
}
static int
do_test (void)
{
test_tls_ifunc (get_last_seen_sentinel, fptr, ifunc_tls);
/* Same as before, but check the dlopen path. */
void *handle = xdlopen ("tst-ifunc-tls-init-lib2.so",
RTLD_LAZY | RTLD_LOCAL);
int (*get_last_seen_sentinel_dlopen) (void)
= xdlsym (handle, "get_last_seen_sentinel");
int (**fptr_dlopen) (void) = xdlsym (handle, "fptr");
int (*ifunc_tls_dlopen) (void) = xdlsym (handle, "ifunc_tls");
test_tls_ifunc (get_last_seen_sentinel_dlopen, *fptr_dlopen,
ifunc_tls_dlopen);
xdlclose (handle);
return 0;
}
#include <support/test-driver.c>
+58
View File
@@ -0,0 +1,58 @@
/* Shared library for tst-ifunc-tls-write.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* The DSO defines an initial-exec thread-local 'counter' initialised to
SENTINEL, the IFUNC resolver (via the non-PLT IFUNC reloc on 'fptr')
overwrites counter with MARKER, and the test then reads counter back
through a getter. */
#include <sys/cdefs.h>
#define SENTINEL 0x5A5A1234u
#define MARKER 0x32125A5Au
/* The 'volatile' avoids constant fold optimization in impl_ok. */
static volatile __thread unsigned int counter
__attribute__ ((tls_model ("initial-exec"))) = SENTINEL;
static unsigned int
impl (void)
{
return 0;
}
static typeof (impl) *
__attribute_used__
resolver (void)
{
counter = MARKER;
return impl;
}
unsigned int ifunc_write (void) __attribute__ ((ifunc ("resolver")));
/* Force the resolver rather than lazy bind on first call, which would
re-write counter after the test reads it). Using a COPY'd fptr also lets
the test verify the resolver ran without making a PLT call that would
itself fire the resolver again. */
unsigned int (*fptr) (void) = ifunc_write;
unsigned int
get_counter (void)
{
return counter;
}
@@ -1,5 +1,5 @@
/* PowerPC definitions for libc main startup.
Copyright (C) 2017-2026 Free Software Foundation, Inc.
/* Check if static-TLS variables are correctly intialized in IFUNC resolvers.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -16,15 +16,19 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef _LIBC_START_H
#define _LIBC_START_H
#include <support/check.h>
#ifndef SHARED
/* IREL{,A} must happen after TCB initialization in order to allow IFUNC
resolvers to read TCB fields, e.g. hwcap and at_platform. */
#define ARCH_SETUP_IREL()
#define ARCH_SETUP_TLS() __libc_setup_tls ()
#define ARCH_APPLY_IREL() apply_irel ()
#endif /* ! SHARED */
#define MARKER 0x32125A5Au
#endif /* _LIBC_START_H */
extern unsigned int (*fptr) (void);
extern unsigned int get_counter (void);
static int
do_test (void)
{
TEST_VERIFY (fptr != NULL);
TEST_COMPARE (get_counter (), MARKER);
return 0;
}
#include <support/test-driver.c>
+29
View File
@@ -0,0 +1,29 @@
/* Shared object for the static-dlopen pointer guard test.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <setjmp.h>
static jmp_buf jb;
void (*do_longjmp) (jmp_buf);
void
foo (void)
{
if (setjmp (jb) == 0)
do_longjmp (jb);
}
+51
View File
@@ -0,0 +1,51 @@
/* Test that the pointer guard is propagated to ld.so via static dlopen.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* A statically linked program dlopens a shared object; the object's setjmp
uses the just-mapped libc.so's pointer guard while the longjmp below uses
the program's guard. Unless __rtld_static_init propagates the guard to
the loaded loader the two differ, and the setjmp/longjmp round-trip jumps
to a corrupt address and crashes. */
#include <setjmp.h>
#include <support/check.h>
#include <support/xdlfcn.h>
static void
call_longjmp (jmp_buf jb)
{
longjmp (jb, 1);
}
static int
do_test (void)
{
void *h = xdlopen ("tst-ptrguard-static-dlopen-mod.so", RTLD_NOW);
void (*foo) (void) = xdlsym (h, "foo");
void (**do_longjmp) (jmp_buf) = xdlsym (h, "do_longjmp");
*do_longjmp = call_longjmp;
/* foo () sets the jump buffer and calls back into call_longjmp; a
mismatched guard makes the return jump fault. */
foo ();
xdlclose (h);
return 0;
}
#include <support/test-driver.c>
@@ -0,0 +1 @@
cp $B/elf/tst-ptrguard-static-dlopen-mod.so $L/tst-ptrguard-static-dlopen-mod.so
+42
View File
@@ -0,0 +1,42 @@
/* Shared library for tst-tls-tdata-reloc.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#define EXPECTED 0x12345678
static int
callee (void)
{
return EXPECTED;
}
typedef struct
{
int (*func) (void);
} cache_t;
/* General-dynamic TLS: the initialiser of '.func' produces an
R_*_RELATIVE on .tdata. */
__thread cache_t cache = { .func = callee };
int
call_through_tls (void)
{
if (cache.func == 0)
return -1;
return cache.func ();
}
+32
View File
@@ -0,0 +1,32 @@
/* Check that .tdata relocations are applied to the static TLS slot.
Copyright (C) 2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <support/check.h>
#define EXPECTED 0x12345678
extern int call_through_tls (void);
static int
do_test (void)
{
TEST_COMPARE (call_through_tls (), EXPECTED);
return 0;
}
#include <support/test-driver.c>
+6
View File
@@ -0,0 +1,6 @@
# defines target $(gen-gconv-modules) that ensures gconv-modules are available
gen-gconv-modules := $(common-objpfx)iconvdata/gconv-modules
$(gen-gconv-modules):
$(MAKE) -C ../iconvdata subdir=iconvdata $@
+2 -2
View File
@@ -39,8 +39,8 @@ struct protoent_data {};
#include <nss/nss_files/files-parse.c>
LINE_PARSER
("#",
STRING_FIELD (result->p_name, isspace, 1);
INT_FIELD (result->p_proto, isspace, 1, 10,);
STRING_FIELD (result->p_name, isspace, true);
INT_FIELD (result->p_proto, isspace, true, 10,);
)
enum nss_status
+3 -3
View File
@@ -39,9 +39,9 @@ struct servent_data {};
#define ISSC_OR_SPACE(c) ((c) == ';' || isspace (c))
LINE_PARSER
("#",
STRING_FIELD (result->s_name, ISSC_OR_SPACE, 1);
STRING_FIELD (result->s_proto, ISSC_OR_SPACE, 1);
INT_FIELD (result->s_port, ISSC_OR_SPACE, 10, 0, htons);
STRING_FIELD (result->s_name, ISSC_OR_SPACE, true);
STRING_FIELD (result->s_proto, ISSC_OR_SPACE, true);
INT_FIELD (result->s_port, ISSC_OR_SPACE, false, 10, htons);
)
enum nss_status
+2 -1
View File
@@ -139,7 +139,8 @@ $(objpfx)test-iconvconfig.out: $(objpfx)iconvconfig
rm -f $$tmp) > $@; \
$(evaluate-test)
$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog
$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog \
$(gen-locales)
$(BASH) $< $(common-objdir) '$(test-wrapper-env)' \
'$(run-program-env)' '$(rpath-link)' > $@; \
$(evaluate-test)
+4 -2
View File
@@ -144,8 +144,10 @@
if (irreversible == NULL) \
{ \
/* This means we are in call from __gconv_transliterate. In this \
case we are not doing any error recovery ourselves. */ \
result = __gconv_mark_illegal_input (step_data); \
case we are not doing any error recovery ourselves. Do not create \
a persistent error state. If __gconv_transliterate exhausts all \
alternatives, it will call __gconv_mark_illegal_input itself. */ \
result = __GCONV_ILLEGAL_INPUT; \
break; \
} \
\
+20 -10
View File
@@ -29,10 +29,10 @@ LIBPATH=$codir:$codir/iconvdata
# How the start the iconv(1) program. $from is not defined/expanded yet.
ICONV='
$test_wrapper_env $run_program_env
$codir/elf/ld.so --library-path $library_path:$LIBPATH --inhibit-rpath ${from}.so
$codir/iconv/iconv_prog
'
ICONV="$test_wrapper_env $run_program_env $ICONV"
TIMEOUTFACTOR=${TIMEOUTFACTOR:-1}
@@ -220,6 +220,7 @@ testarray=(
"\x00\x00;;INVALID;UTF-8;1"
"\x00\x00;;UTF-8;INVALID;1"
"\xc3\xa9;;UTF-8;ASCII//TRANSLIT;0"
"X\xc2\xbdY;;UTF-8;ASCII//TRANSLIT;0"
)
# Requires $twobyte input, $c flag, $from, and $to to be set; sets $ret
@@ -280,12 +281,21 @@ check_errtest_result ()
fi
}
for testcommand in "${testarray[@]}"; do
twobyte="$(echo "$testcommand" | cut -d";" -f 1)"
c="$(echo "$testcommand" | cut -d";" -f 2)"
from="$(echo "$testcommand" | cut -d";" -f 3)"
to="$(echo "$testcommand" | cut -d";" -f 4)"
eret="$(echo "$testcommand" | cut -d";" -f 5)"
execute_test
check_errtest_result
done
run_test_array ()
{
for testcommand in "${testarray[@]}"; do
twobyte="$(echo "$testcommand" | cut -d";" -f 1)"
c="$(echo "$testcommand" | cut -d";" -f 2)"
from="$(echo "$testcommand" | cut -d";" -f 3)"
to="$(echo "$testcommand" | cut -d";" -f 4)"
eret="$(echo "$testcommand" | cut -d";" -f 5)"
execute_test
check_errtest_result
done
}
echo "info: testing C locale"
run_test_array
echo "info: testing en_US.UTF-8 locale"
run_program_env="$run_program_env LC_ALL=en_US.UTF-8"
run_test_array
+2 -1
View File
@@ -113,6 +113,7 @@
#define HAVE_LIBINTL_H 1
#define HAVE_WCTYPE_H 1
#define HAVE_ISWCTYPE 1
#define HAVE_MEMPCPY 1
#define ENABLE_NLS 1
/* The symbols in all the user (non-_) macros are C symbols. */
@@ -670,7 +671,7 @@ for linking")
/* Helper / base macros for indirect function symbols. */
#define __ifunc_resolver(type_name, name, expr, init, classifier, ...) \
classifier inhibit_stack_protector \
classifier \
__typeof (type_name) *name##_ifunc (__VA_ARGS__) \
{ \
init (); \
+5
View File
@@ -70,6 +70,11 @@ libc_hidden_proto (__libc_res_nameinquery)
extern __typeof (__res_queriesmatch) __libc_res_queriesmatch;
libc_hidden_proto (__libc_res_queriesmatch)
extern const struct res_sym __p_class_syms[];
libresolv_hidden_proto (__p_class_syms)
extern const struct res_sym __p_type_syms[];
libresolv_hidden_proto (__p_type_syms)
/* Variant of res_hnok which operates on binary (but uncompressed) names. */
bool __res_binary_hnok (const unsigned char *dn) attribute_hidden;
+65 -4
View File
@@ -5,12 +5,73 @@
#ifndef _ISOMAC
# include <stdint.h>
# if __glibc_has_builtin (___builtin_stdc_rotate_right)
# define stdc_rotate_right(__x, __n) (___builtin_stdc_rotate_right (__x, __n))
# if __glibc_has_builtin (__builtin_stdc_rotate_left)
# define stdc_rotate_left(__x, __n) (__builtin_stdc_rotate_left (__x, __n))
# define stdc_rotate_left_uc(__x, __n) (stdc_rotate_left (__x, __n))
# define stdc_rotate_left_us(__x, __n) (stdc_rotate_left (__x, __n))
# define stdc_rotate_left_ui(__x, __n) (stdc_rotate_left (__x, __n))
# define stdc_rotate_left_ul(__x, __n) (stdc_rotate_left (__x, __n))
# define stdc_rotate_left_ull(__x, __n) (stdc_rotate_left (__x, __n))
# else
# if __WORDSIZE == 64
# define __ROL_UL_GENERIC __rol64_inline
# else
# define __ROL_UL_GENERIC __rol32_inline
# endif
# define stdc_rotate_left(__x, __n) \
_Generic((__x), \
unsigned char: __rol8_inline, \
unsigned short: __rol16_inline, \
unsigned int: __rol32_inline, \
unsigned long: __ROL_UL_GENERIC, \
unsigned long long: __rol64_inline \
)(__x, __n)
# define __rol_generic(__v, __n) \
((__v << (__n & (sizeof __v * 8 - 1))) \
| (__v >> ((-__n) & (sizeof __v * 8 - 1))))
static __always_inline uint8_t
__rol8_inline (uint8_t __x, unsigned int __n)
{
return __rol_generic (__x, __n);
}
static __always_inline uint16_t
__rol16_inline (uint16_t __x, unsigned int __n)
{
return __rol_generic (__x, __n);
}
static __always_inline uint32_t
__rol32_inline (uint32_t __x, unsigned int __n)
{
return __rol_generic (__x, __n);
}
static __always_inline uint64_t
__rol64_inline (uint64_t __x, unsigned int __n)
{
return __rol_generic (__x, __n);
}
# define stdc_rotate_left_uc(__x, __n) (__rol8_inline (__x, __n))
# define stdc_rotate_left_us(__x, __n) (__rol16_inline (__x, __n))
# define stdc_rotate_left_ui(__x, __n) (__rol32_inline (__x, __n))
# if __WORDSIZE == 64
# define stdc_rotate_left_ul(__x, __n) (__rol64_inline (__x, __n))
# else
# define stdc_rotate_left_ul(__x, __n) (__rol32_inline (__x, __n))
# endif
# define stdc_rotate_left_ull(__x, __n) (__rol64_inline (__x, __n))
# endif /* __glibc_has_builtin (__builtin_stdc_rotate_left) */
# if __glibc_has_builtin (__builtin_stdc_rotate_right)
# define stdc_rotate_right(__x, __n) (__builtin_stdc_rotate_right (__x, __n))
# define stdc_rotate_right_uc(__x, __n) (stdc_rotate_right (__x, __n))
# define stdc_rotate_right_us(__x, __n) (stdc_rotate_right (__x, __n))
# define stdc_rotate_right_ui(__x, __n) (stdc_rotate_right (__x, __n))
# define stdc_rotate_right_ul(__x, __n) (stdc_rotate_right (__x, __n))
# define stdc_rotate_right_ull(__x, __n) (stdc_rotate_right (__x, __n))
# else
# if __WORDSIZE == 64
# define __ROR_UL_GENERIC __ror64_inline
@@ -48,7 +109,7 @@ __ror32_inline (uint32_t __x, unsigned int __n)
return __ror_generic (__x, __n);
}
static __always_inline uint64_t
static __always_inline uint64_t
__ror64_inline (uint64_t __x, unsigned int __n)
{
return __ror_generic (__x, __n);
@@ -62,7 +123,7 @@ __ror64_inline (uint64_t __x, unsigned int __n)
# define stdc_rotate_right_ul(__x, __n) (__ror32_inline (__x, __n))
# endif
# define stdc_rotate_right_ull(__x, __n) (__ror64_inline (__x, __n))
# endif /* __glibc_has_builtin (___builtin_stdc_rotate_right) */
# endif /* __glibc_has_builtin (__builtin_stdc_rotate_right) */
# endif /* _ISOMAC */
-4
View File
@@ -134,10 +134,6 @@ endif
$(inst_sysconfdir)/rpc: etc.rpc $(+force)
$(do-install)
ifeq ($(build-static-nss),yes)
CFLAGS += -DSTATIC_NSS
endif
ifeq ($(have-test-clang),yes)
CFLAGS-tst-deadline.c += -Wno-ignored-attributes
endif
+26 -6
View File
@@ -22,13 +22,21 @@ subdir = intl
include ../Makeconfig
headers = libintl.h
routines = bindtextdom dcgettext dgettext gettext \
routines = bindtextdom dcgettext dgettext gettext \
dcigettext dcngettext dngettext ngettext \
finddomain loadmsgcat localealias textdomain
aux = l10nflist explodename plural plural-exp hash-string
aux = l10nflist explodename plural plural-exp hash-string
multithread-test-srcs := tst-gettext4 tst-gettext5 tst-gettext6
test-srcs := tst-gettext tst-translit tst-gettext2 tst-codeset tst-gettext3
test-srcs := \
tst-gettext \
tst-translit \
tst-gettext2 \
tst-codeset \
tst-gettext3 \
tst-plural-eval
# test-srcs
ifeq ($(have-thread-library),yes)
test-srcs += $(multithread-test-srcs)
endif
@@ -53,9 +61,15 @@ $(objpfx)plural.o: $(objpfx)plural.c
ifeq ($(run-built-tests),yes)
ifeq (yes,$(build-shared))
ifneq ($(strip $(MSGFMT)),:)
tests-special += $(objpfx)tst-translit.out $(objpfx)tst-gettext.out \
$(objpfx)tst-gettext2.out $(objpfx)tst-codeset.out \
$(objpfx)tst-gettext3.out
tests-special += \
$(objpfx)tst-translit.out \
$(objpfx)tst-gettext.out \
$(objpfx)tst-gettext2.out \
$(objpfx)tst-codeset.out \
$(objpfx)tst-gettext3.out \
$(objpfx)tst-plural-eval.out
# tests-special
ifeq ($(have-thread-library),yes)
tests-special += $(objpfx)tst-gettext4.out $(objpfx)tst-gettext5.out \
$(objpfx)tst-gettext6.out
@@ -103,6 +117,10 @@ $(objpfx)tst-gettext4.out: tst-gettext4.sh $(objpfx)tst-gettext4
$(objpfx)tst-gettext6.out: tst-gettext6.sh $(objpfx)tst-gettext6
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)' $(common-objpfx)intl/; \
$(evaluate-test)
$(objpfx)tst-plural-eval.out: tst-plural-eval.sh $(objpfx)tst-plural-eval
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)' \
$(common-objpfx)intl/; \
$(evaluate-test)
$(objpfx)tst-codeset.out: $(codeset_mo)
$(objpfx)tst-gettext3.out: $(codeset_mo)
@@ -140,6 +158,7 @@ CFLAGS-tst-gettext3.c += -DOBJPFX=\"$(objpfx)\"
CFLAGS-tst-gettext4.c += -DOBJPFX=\"$(objpfx)\"
CFLAGS-tst-gettext5.c += -DOBJPFX=\"$(objpfx)\"
CFLAGS-tst-gettext6.c += -DOBJPFX=\"$(objpfx)\"
CFLAGS-tst-plural-eval.c += -DOBJPFX=\"$(objpfx)\"
ifeq ($(have-thread-library),yes)
ifeq (yes,$(build-shared))
@@ -156,6 +175,7 @@ $(objpfx)tst-gettext3.out: $(objpfx)tst-gettext.out
$(objpfx)tst-gettext4.out: $(objpfx)tst-gettext.out
$(objpfx)tst-gettext5.out: $(objpfx)tst-gettext.out
$(objpfx)tst-gettext6.out: $(objpfx)tst-gettext.out
$(objpfx)tst-plural-eval.out: $(objpfx)tst-gettext.out
CPPFLAGS += -D'LOCALEDIR="$(localedir)"' \
-D'LOCALE_ALIAS_PATH="$(localedir)"' \
+28 -130
View File
@@ -68,20 +68,6 @@ extern int errno;
#include <locale.h>
#ifdef _LIBC
/* Guess whether integer division by zero raises signal SIGFPE.
Set to 1 only if you know for sure. In case of doubt, set to 0. */
# if defined __alpha__ || defined __arm__ || defined __i386__ \
|| defined __m68k__ || defined __s390x__
# define INTDIV0_RAISES_SIGFPE 1
# else
# define INTDIV0_RAISES_SIGFPE 0
# endif
#endif
#if !INTDIV0_RAISES_SIGFPE
# include <signal.h>
#endif
#if defined HAVE_SYS_PARAM_H || defined _LIBC
# include <sys/param.h>
#endif
@@ -95,9 +81,6 @@ extern int errno;
#ifdef _LIBC
# include <libintl.h>
#else
# ifdef IN_LIBGLOCALE
# include <libintl.h>
# endif
# include "libgnuintl.h"
#endif
#include "hash-string.h"
@@ -228,7 +211,7 @@ static void *mempcpy (void *dest, const void *src, size_t n);
#endif
/* Whether to support different locales in different threads. */
#if defined _LIBC || HAVE_USELOCALE || defined IN_LIBGLOCALE
#if defined _LIBC || HAVE_USELOCALE
# define HAVE_PER_THREAD_LOCALE
#endif
@@ -247,11 +230,6 @@ struct known_translation_t
const char *localename;
#endif
#ifdef IN_LIBGLOCALE
/* The character encoding. */
const char *encoding;
#endif
/* State of the catalog counter at the point the string was found. */
int counter;
@@ -299,10 +277,6 @@ transcmp (const void *p1, const void *p2)
if (result == 0)
#endif
{
#ifdef IN_LIBGLOCALE
result = strcmp (s1->encoding, s2->encoding);
if (result == 0)
#endif
/* We compare the category last (though this is the cheapest
operation) since it is hopefully always the same (namely
LC_MESSAGES). */
@@ -318,11 +292,9 @@ transcmp (const void *p1, const void *p2)
textdomain(3). The default value for this is "messages". */
const char _nl_default_default_domain[] attribute_hidden = "messages";
#ifndef IN_LIBGLOCALE
/* Value used as the default domain for gettext(3). */
const char *_nl_current_default_domain attribute_hidden
= _nl_default_default_domain;
#endif
/* Contains the default location of the message catalogs. */
#if defined __EMX__
@@ -338,25 +310,17 @@ libc_hidden_data_def (_nl_default_dirname)
# endif
#endif
#ifndef IN_LIBGLOCALE
/* List with bindings of specific domains created by bindtextdomain()
calls. */
struct binding *_nl_domain_bindings;
#endif
/* Prototypes for local functions. */
static char *plural_lookup (struct loaded_l10nfile *domain,
unsigned long int n,
const char *translation, size_t translation_len);
#ifdef IN_LIBGLOCALE
static const char *guess_category_value (int category,
const char *categoryname,
const char *localename);
#else
static const char *guess_category_value (int category,
const char *categoryname);
#endif
#ifdef _LIBC
# include "../locale/localeinfo.h"
@@ -364,7 +328,7 @@ static const char *guess_category_value (int category,
#else
static const char *category_to_name (int category);
#endif
#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
#if (defined _LIBC || HAVE_ICONV)
static const char *get_output_charset (struct binding *domainbinding);
#endif
@@ -443,18 +407,9 @@ static int enable_secure;
/* Look up MSGID in the DOMAINNAME message catalog for the current
CATEGORY locale and, if PLURAL is nonzero, search over string
depending on the plural form determined by N. */
#ifdef IN_LIBGLOCALE
char *
gl_dcigettext (const char *domainname,
const char *msgid1, const char *msgid2,
int plural, unsigned long int n,
int category,
const char *localename, const char *encoding)
#else
char *
DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
int plural, unsigned long int n, int category)
#endif
{
struct loaded_l10nfile *domain;
struct binding *binding;
@@ -469,7 +424,7 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
int saved_errno;
struct known_translation_t search;
struct known_translation_t **foundp = NULL;
#if defined HAVE_PER_THREAD_LOCALE && !defined IN_LIBGLOCALE
#if defined HAVE_PER_THREAD_LOCALE
const char *localename;
#endif
size_t domainname_len;
@@ -516,21 +471,16 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
search.domainname = domainname;
search.category = category;
#ifdef HAVE_PER_THREAD_LOCALE
# ifndef IN_LIBGLOCALE
# ifdef _LIBC
# ifdef _LIBC
localename = __current_locale_name (category);
# else
# else
categoryname = category_to_name (category);
# define CATEGORYNAME_INITIALIZED
# define CATEGORYNAME_INITIALIZED
localename = _nl_locale_name_thread_unsafe (category, categoryname);
if (localename == NULL)
localename = "";
# endif
# endif
search.localename = localename;
# ifdef IN_LIBGLOCALE
search.encoding = encoding;
# endif
/* Since tfind/tsearch manage a balanced tree, concurrent tfind and
tsearch calls can be fatal. */
@@ -562,12 +512,6 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
DETERMINE_SECURE;
/* First find matching binding. */
#ifdef IN_LIBGLOCALE
/* We can use a trivial binding, since _nl_find_msg will ignore it anyway,
and _nl_load_domain and _nl_find_domain just pass it through. */
binding = NULL;
dirname = bindtextdomain (domainname, NULL);
#else
for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
{
int compare = strcmp (domainname, binding->domainname);
@@ -587,7 +531,6 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
else
{
dirname = binding->dirname;
#endif
if (!IS_ABSOLUTE_PATH (dirname))
{
/* We have a relative path. Make it absolute now. */
@@ -603,20 +546,13 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
goto return_untranslated;
dirname = xdirname;
}
#ifndef IN_LIBGLOCALE
}
#endif
/* Now determine the symbolic name of CATEGORY and its value. */
#ifndef CATEGORYNAME_INITIALIZED
categoryname = category_to_name (category);
#endif
#ifdef IN_LIBGLOCALE
categoryvalue = guess_category_value (category, categoryname, localename);
#else
categoryvalue = guess_category_value (category, categoryname);
#endif
domainname_len = strlen (domainname);
xdomainname = (char *) malloc (strlen (categoryname)
+ domainname_len + 5);
@@ -675,11 +611,7 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
if (domain != NULL)
{
#if defined IN_LIBGLOCALE
retval = _nl_find_msg (domain, binding, encoding, msgid1, &retlen);
#else
retval = _nl_find_msg (domain, binding, msgid1, 1, &retlen);
#endif
if (retval == NULL)
{
@@ -687,13 +619,8 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
{
#if defined IN_LIBGLOCALE
retval = _nl_find_msg (domain->successor[cnt], binding,
encoding, msgid1, &retlen);
#else
retval = _nl_find_msg (domain->successor[cnt], binding,
msgid1, 1, &retlen);
#endif
/* Resource problems are not fatal, instead we return no
translation. */
@@ -754,9 +681,6 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
newp->category = category;
#ifdef HAVE_PER_THREAD_LOCALE
newp->localename = new_localename;
#endif
#ifdef IN_LIBGLOCALE
newp->encoding = encoding;
#endif
newp->counter = _nl_msg_cat_cntr;
newp->domain = domain;
@@ -837,17 +761,10 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
in case of a memory allocation failure during conversion (only if
ENCODING != NULL resp. CONVERT == true). */
char *
#ifdef IN_LIBGLOCALE
_nl_find_msg (struct loaded_l10nfile *domain_file,
struct binding *domainbinding, const char *encoding,
const char *msgid,
size_t *lengthp)
#else
_nl_find_msg (struct loaded_l10nfile *domain_file,
struct binding *domainbinding,
const char *msgid, int convert,
size_t *lengthp)
#endif
{
struct loaded_domain *domain;
nls_uint32 nstrings;
@@ -953,16 +870,10 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
}
#if defined _LIBC || HAVE_ICONV
# ifdef IN_LIBGLOCALE
if (encoding != NULL)
# else
if (convert)
# endif
{
/* We are supposed to do a conversion. */
# ifndef IN_LIBGLOCALE
const char *encoding = get_output_charset (domainbinding);
# endif
size_t nconversions;
struct converted_domain *convd;
size_t i;
@@ -1056,12 +967,7 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
reallocate domain->conversions because we pass
encoding = NULL or convert = 0, respectively. */
nullentry =
# ifdef IN_LIBGLOCALE
_nl_find_msg (domain_file, domainbinding, NULL, "",
&nullentrylen);
# else
_nl_find_msg (domain_file, domainbinding, "", 0, &nullentrylen);
# endif
/* Resource problems are fatal. If we continue onwards we will
only attempt to calloc a new conv_tab and fail later. */
@@ -1230,7 +1136,6 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
not_translated_yet:
inbuf = (const unsigned char *) result;
outbuf = freemem + sizeof (size_t);
# ifndef _LIBC
transmem_list = NULL;
# endif
@@ -1239,13 +1144,16 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
while (1)
{
transmem_block_t *newmem;
# ifdef _LIBC
size_t non_reversible;
int res;
if (freemem_size < sizeof (size_t))
goto resize_freemem;
outbuf = freemem + sizeof (size_t);
# ifdef _LIBC
size_t non_reversible;
int res;
res = __gconv (convd->conv,
&inbuf, inbuf + resultlen,
&outbuf,
@@ -1271,9 +1179,6 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
char *outptr = (char *) outbuf;
size_t outleft;
if (freemem_size < sizeof (size_t))
goto resize_freemem;
outleft = freemem_size - sizeof (size_t);
if (iconv (convd->conv,
(ICONV_CONST char **) &inptr, &inleft,
@@ -1342,8 +1247,6 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
transmem_list = newmem;
freemem = newmem;
# endif
outbuf = freemem + sizeof (size_t);
}
/* We have now in our buffer a converted string. Put this
@@ -1381,14 +1284,19 @@ plural_lookup (struct loaded_l10nfile *domain, unsigned long int n,
const char *translation, size_t translation_len)
{
struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
struct eval_result result;
unsigned long int index;
const char *p;
index = plural_eval (domaindata->plural, n);
if (index >= domaindata->nplurals)
/* This should never happen. It means the plural expression and the
given maximum value do not match. */
result = plural_eval (domaindata->plural, n);
if (result.status != PE_OK)
/* The plural expression evaluation failed. */
index = 0;
else if (result.value >= domaindata->nplurals)
/* The plural expression and the given maximum value do not match. */
index = 0;
else
index = result.value;
/* Skip INDEX strings at TRANSLATION. */
p = translation;
@@ -1470,21 +1378,13 @@ category_to_name (int category)
/* Guess value of current locale from value of the environment variables
or system-dependent defaults. */
static const char *
#ifdef IN_LIBGLOCALE
guess_category_value (int category, const char *categoryname,
const char *locale)
#else
guess_category_value (int category, const char *categoryname)
#endif
{
const char *language;
#ifndef IN_LIBGLOCALE
const char *locale;
# ifndef _LIBC
#ifndef _LIBC
const char *language_default;
int locale_defaulted;
# endif
#endif
/* We use the settings in the following order:
@@ -1501,18 +1401,17 @@ guess_category_value (int category, const char *categoryname)
- If the system provides both a list of languages and a default locale,
the former is used. */
#ifndef IN_LIBGLOCALE
/* Fetch the locale name, through the POSIX method of looking to `LC_ALL',
`LC_xxx', and `LANG'. On some systems this can be done by the
`setlocale' function itself. */
# ifdef _LIBC
#ifdef _LIBC
locale = __current_locale_name (category);
# else
#else
locale_defaulted = 0;
# if HAVE_USELOCALE
# if HAVE_USELOCALE
locale = _nl_locale_name_thread_unsafe (category, categoryname);
if (locale == NULL)
# endif
# endif
{
locale = _nl_locale_name_posix (category, categoryname);
if (locale == NULL)
@@ -1521,7 +1420,6 @@ guess_category_value (int category, const char *categoryname)
locale_defaulted = 1;
}
}
# endif
#endif
/* Ignore LANGUAGE and its system-dependent analogon if the locale is set
@@ -1547,7 +1445,7 @@ guess_category_value (int category, const char *categoryname)
language = getenv ("LANGUAGE");
if (language != NULL && language[0] != '\0')
return language;
#if !defined IN_LIBGLOCALE && !defined _LIBC
#ifndef _LIBC
/* The next priority value is the locale name, if not defaulted. */
if (locale_defaulted)
{
@@ -1561,7 +1459,7 @@ guess_category_value (int category, const char *categoryname)
return locale;
}
#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
#if (defined _LIBC || HAVE_ICONV)
/* Returns the output charset. */
static const char *
get_output_charset (struct binding *domainbinding)
+104 -34
View File
@@ -14,24 +14,59 @@
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* Written by Ulrich Drepper and Bruno Haible. */
#ifndef STATIC
#define STATIC static
#endif
/* Evaluate the plural expression and return an index value. */
STATIC
unsigned long int
plural_eval (const struct expression *pexp, unsigned long int n)
/* While the bison parser is able to support expressions of a maximum depth
of YYMAXDEPTH = 10000, the runtime evaluation of a parsed plural expression
has a smaller maximum recursion depth.
If we did not limit the recursion depth, a program that just invokes
ngettext() on a thread other than the main thread could get a crash by
stack overflow in the following circumstances:
- On systems with glibc, after the stack size has been reduced,
e.g. on x86_64 systems after "ulimit -s 260".
This stack size is only sufficient for ca. 3919 recursions.
Cf. <https://unix.stackexchange.com/questions/620720/>
- On systems with musl libc, because there the thread stack size (for a
thread other than the main thread) by default is only 128 KB, see
<https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size>.
- On AIX 7 systems, because there the thread stack size (for a thread
other than the main thread) by default is only 96 KB, see
<https://www.ibm.com/docs/en/aix/7.1?topic=programming-threads-library-options>.
This stack size is only sufficient for between 887 and 1363 recursions,
depending on the compiler and compiler optimization options.
A maximum depth of 100 is a large enough for all practical needs
and also small enough to avoid stack overflow even with small thread stack
sizes. */
#ifndef EVAL_MAXDEPTH
# define EVAL_MAXDEPTH 100
#endif
/* A shorthand that denotes a successful evaluation result with a value V. */
#define OK(v) (struct eval_result) { .status = PE_OK, .value = (v) }
/* Evaluates a plural expression PEXP for n=N, up to ALLOWED_DEPTH. */
static struct eval_result
plural_eval_recurse (const struct expression *pexp, unsigned long int n,
unsigned int allowed_depth)
{
if (allowed_depth == 0)
/* The allowed recursion depth is exhausted. */
return (struct eval_result) { .status = PE_STACKOVF };
allowed_depth--;
switch (pexp->nargs)
{
case 0:
switch (pexp->operation)
{
case var:
return n;
return OK (n);
case num:
return pexp->val.num;
return OK (pexp->val.num);
default:
break;
}
@@ -40,52 +75,73 @@ plural_eval (const struct expression *pexp, unsigned long int n)
case 1:
{
/* pexp->operation must be lnot. */
unsigned long int arg = plural_eval (pexp->val.args[0], n);
return ! arg;
struct eval_result arg =
plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
if (arg.status != PE_OK)
return arg;
return OK (! arg.value);
}
case 2:
{
unsigned long int leftarg = plural_eval (pexp->val.args[0], n);
struct eval_result leftarg =
plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
if (leftarg.status != PE_OK)
return leftarg;
if (pexp->operation == lor)
return leftarg || plural_eval (pexp->val.args[1], n);
{
if (leftarg.value)
return OK (1);
struct eval_result rightarg =
plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
if (rightarg.status != PE_OK)
return rightarg;
return OK (rightarg.value ? 1 : 0);
}
else if (pexp->operation == land)
return leftarg && plural_eval (pexp->val.args[1], n);
{
if (!leftarg.value)
return OK (0);
struct eval_result rightarg =
plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
if (rightarg.status != PE_OK)
return rightarg;
return OK (rightarg.value ? 1 : 0);
}
else
{
unsigned long int rightarg = plural_eval (pexp->val.args[1], n);
struct eval_result rightarg =
plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
if (rightarg.status != PE_OK)
return rightarg;
switch (pexp->operation)
{
case mult:
return leftarg * rightarg;
return OK (leftarg.value * rightarg.value);
case divide:
#if !INTDIV0_RAISES_SIGFPE
if (rightarg == 0)
raise (SIGFPE);
#endif
return leftarg / rightarg;
if (rightarg.value == 0)
return (struct eval_result) { .status = PE_INTDIV };
return OK (leftarg.value / rightarg.value);
case module:
#if !INTDIV0_RAISES_SIGFPE
if (rightarg == 0)
raise (SIGFPE);
#endif
return leftarg % rightarg;
if (rightarg.value == 0)
return (struct eval_result) { .status = PE_INTDIV };
return OK (leftarg.value % rightarg.value);
case plus:
return leftarg + rightarg;
return OK (leftarg.value + rightarg.value);
case minus:
return leftarg - rightarg;
return OK (leftarg.value - rightarg.value);
case less_than:
return leftarg < rightarg;
return OK (leftarg.value < rightarg.value);
case greater_than:
return leftarg > rightarg;
return OK (leftarg.value > rightarg.value);
case less_or_equal:
return leftarg <= rightarg;
return OK (leftarg.value <= rightarg.value);
case greater_or_equal:
return leftarg >= rightarg;
return OK (leftarg.value >= rightarg.value);
case equal:
return leftarg == rightarg;
return OK (leftarg.value == rightarg.value);
case not_equal:
return leftarg != rightarg;
return OK (leftarg.value != rightarg.value);
default:
break;
}
@@ -96,10 +152,24 @@ plural_eval (const struct expression *pexp, unsigned long int n)
case 3:
{
/* pexp->operation must be qmop. */
unsigned long int boolarg = plural_eval (pexp->val.args[0], n);
return plural_eval (pexp->val.args[boolarg ? 1 : 2], n);
struct eval_result boolarg =
plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
if (boolarg.status != PE_OK)
return boolarg;
return plural_eval_recurse (pexp->val.args[boolarg.value ? 1 : 2], n,
allowed_depth);
}
}
/* NOTREACHED */
return 0;
return (struct eval_result) { .status = PE_ASSERT };
}
/* Evaluates a plural expression PEXP for n=N. */
STATIC
struct eval_result
plural_eval (const struct expression *pexp, unsigned long int n)
{
return plural_eval_recurse (pexp, n, EVAL_MAXDEPTH);
}
#undef OK
+34 -38
View File
@@ -50,7 +50,6 @@
/* List of already loaded domains. */
static struct loaded_l10nfile *_nl_loaded_domains;
/* Return a data structure describing the message catalog described by
the DOMAINNAME and CATEGORY parameters with respect to the currently
established bindings. */
@@ -133,55 +132,52 @@ _nl_find_domain (const char *dirname, char *locale,
/* Now we determine the single parts of the locale name. First
look for the language. Termination symbols are `_', '.', and `@'. */
mask = _nl_explode_name (locale, &language, &modifier, &territory,
&codeset, &normalized_codeset);
if (mask == -1)
/* This means we are out of core. */
return NULL;
/* We need to protect modifying the _NL_LOADED_DOMAINS data. */
gl_rwlock_wrlock (lock);
/* Create all possible locale entries which might be interested in
generalization. */
retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
strlen (dirname) + 1, mask, language, territory,
codeset, normalized_codeset, modifier,
domainname, 1);
gl_rwlock_unlock (lock);
if (retval == NULL)
/* This means we are out of core. */
goto out;
if (retval->decided <= 0)
_nl_load_domain (retval, domainbinding);
if (retval->data == NULL)
mask = _nl_explode_name (locale, &language, &modifier, &territory, &codeset,
&normalized_codeset);
if (mask != -1) /* Not out of memory? */
{
int cnt;
for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
/* We need to protect modifying the _NL_LOADED_DOMAINS data. */
gl_rwlock_wrlock (lock);
/* Create all possible locale entries which might be interested in
generalization. */
retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
strlen (dirname) + 1, mask, language,
territory, codeset, normalized_codeset,
modifier, domainname, 1);
gl_rwlock_unlock (lock);
if (retval != NULL) /* Not out of memory? */
{
if (retval->successor[cnt]->decided <= 0)
_nl_load_domain (retval->successor[cnt], domainbinding);
if (retval->successor[cnt]->data != NULL)
break;
if (retval->decided <= 0)
_nl_load_domain (retval, domainbinding);
if (retval->data == NULL)
{
int cnt;
for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
{
if (retval->successor[cnt]->decided <= 0)
_nl_load_domain (retval->successor[cnt], domainbinding);
if (retval->successor[cnt]->data != NULL)
break;
}
}
}
/* The space for normalized_codeset is dynamically allocated.
Free it. */
if (mask & XPG_NORM_CODESET)
free ((void *) normalized_codeset);
}
/* The room for an alias was dynamically allocated. Free it now. */
if (alias_value != NULL)
free (locale);
out:
/* The space for normalized_codeset is dynamically allocated. Free it. */
if (mask & XPG_NORM_CODESET)
free ((void *) normalized_codeset);
return retval;
}
#ifdef _LIBC
/* This is called from iconv/gconv_db.c's free_mem, as locales must
be freed before freeing gconv steps arrays. */
-21
View File
@@ -70,18 +70,10 @@ extern void _nl_unload_domain (struct loaded_domain *__domain)
# undef _INTL_REDIRECT_MACROS
# define _INTL_REDIRECT_MACROS
# include "libgnuintl.h"
# ifdef IN_LIBGLOCALE
extern char *gl_dcigettext (const char *__domainname,
const char *__msgid1, const char *__msgid2,
int __plural, unsigned long int __n,
int __category,
const char *__localename, const char *__encoding);
# else
extern char *libintl_dcigettext (const char *__domainname,
const char *__msgid1, const char *__msgid2,
int __plural, unsigned long int __n,
int __category);
# endif
#endif
#include "loadinfo.h"
@@ -216,12 +208,7 @@ struct binding
/* A counter which is incremented each time some previous translations
become invalid.
This variable is part of the external ABI of the GNU libintl. */
#ifdef IN_LIBGLOCALE
# include <glocale/config.h>
extern LIBGLOCALE_DLL_EXPORTED int _nl_msg_cat_cntr;
#else
extern LIBINTL_DLL_EXPORTED int _nl_msg_cat_cntr;
#endif
#ifndef _LIBC
extern const char *_nl_language_preferences_default (void);
@@ -258,18 +245,10 @@ void _nl_load_domain (struct loaded_l10nfile *__domain,
struct binding *__domainbinding)
attribute_hidden;
#ifdef IN_LIBGLOCALE
char *_nl_find_msg (struct loaded_l10nfile *domain_file,
struct binding *domainbinding, const char *encoding,
const char *msgid,
size_t *lengthp)
attribute_hidden;
#else
char *_nl_find_msg (struct loaded_l10nfile *domain_file,
struct binding *domainbinding, const char *msgid,
int convert, size_t *lengthp)
attribute_hidden;
#endif
/* The internal variables in the standalone libintl.a must have different
names than the internal variables in GNU libc, otherwise programs
+84 -93
View File
@@ -77,361 +77,357 @@
# include "lock.h"
#endif
#ifdef _LIBC
# define PRI_MACROS_BROKEN 0
#endif
/* Provide fallback values for macros that ought to be defined in <inttypes.h>.
Note that our fallback values need not be literal strings, because we don't
use them with preprocessor string concatenation. */
#if !defined PRId8 || PRI_MACROS_BROKEN
#if !defined PRId8
# undef PRId8
# define PRId8 "d"
#endif
#if !defined PRIi8 || PRI_MACROS_BROKEN
#if !defined PRIi8
# undef PRIi8
# define PRIi8 "i"
#endif
#if !defined PRIo8 || PRI_MACROS_BROKEN
#if !defined PRIo8
# undef PRIo8
# define PRIo8 "o"
#endif
#if !defined PRIu8 || PRI_MACROS_BROKEN
#if !defined PRIu8
# undef PRIu8
# define PRIu8 "u"
#endif
#if !defined PRIx8 || PRI_MACROS_BROKEN
#if !defined PRIx8
# undef PRIx8
# define PRIx8 "x"
#endif
#if !defined PRIX8 || PRI_MACROS_BROKEN
#if !defined PRIX8
# undef PRIX8
# define PRIX8 "X"
#endif
#if !defined PRId16 || PRI_MACROS_BROKEN
#if !defined PRId16
# undef PRId16
# define PRId16 "d"
#endif
#if !defined PRIi16 || PRI_MACROS_BROKEN
#if !defined PRIi16
# undef PRIi16
# define PRIi16 "i"
#endif
#if !defined PRIo16 || PRI_MACROS_BROKEN
#if !defined PRIo16
# undef PRIo16
# define PRIo16 "o"
#endif
#if !defined PRIu16 || PRI_MACROS_BROKEN
#if !defined PRIu16
# undef PRIu16
# define PRIu16 "u"
#endif
#if !defined PRIx16 || PRI_MACROS_BROKEN
#if !defined PRIx16
# undef PRIx16
# define PRIx16 "x"
#endif
#if !defined PRIX16 || PRI_MACROS_BROKEN
#if !defined PRIX16
# undef PRIX16
# define PRIX16 "X"
#endif
#if !defined PRId32 || PRI_MACROS_BROKEN
#if !defined PRId32
# undef PRId32
# define PRId32 "d"
#endif
#if !defined PRIi32 || PRI_MACROS_BROKEN
#if !defined PRIi32
# undef PRIi32
# define PRIi32 "i"
#endif
#if !defined PRIo32 || PRI_MACROS_BROKEN
#if !defined PRIo32
# undef PRIo32
# define PRIo32 "o"
#endif
#if !defined PRIu32 || PRI_MACROS_BROKEN
#if !defined PRIu32
# undef PRIu32
# define PRIu32 "u"
#endif
#if !defined PRIx32 || PRI_MACROS_BROKEN
#if !defined PRIx32
# undef PRIx32
# define PRIx32 "x"
#endif
#if !defined PRIX32 || PRI_MACROS_BROKEN
#if !defined PRIX32
# undef PRIX32
# define PRIX32 "X"
#endif
#if !defined PRId64 || PRI_MACROS_BROKEN
#if !defined PRId64
# undef PRId64
# define PRId64 (sizeof (long) == 8 ? "ld" : "lld")
#endif
#if !defined PRIi64 || PRI_MACROS_BROKEN
#if !defined PRIi64
# undef PRIi64
# define PRIi64 (sizeof (long) == 8 ? "li" : "lli")
#endif
#if !defined PRIo64 || PRI_MACROS_BROKEN
#if !defined PRIo64
# undef PRIo64
# define PRIo64 (sizeof (long) == 8 ? "lo" : "llo")
#endif
#if !defined PRIu64 || PRI_MACROS_BROKEN
#if !defined PRIu64
# undef PRIu64
# define PRIu64 (sizeof (long) == 8 ? "lu" : "llu")
#endif
#if !defined PRIx64 || PRI_MACROS_BROKEN
#if !defined PRIx64
# undef PRIx64
# define PRIx64 (sizeof (long) == 8 ? "lx" : "llx")
#endif
#if !defined PRIX64 || PRI_MACROS_BROKEN
#if !defined PRIX64
# undef PRIX64
# define PRIX64 (sizeof (long) == 8 ? "lX" : "llX")
#endif
#if !defined PRIdLEAST8 || PRI_MACROS_BROKEN
#if !defined PRIdLEAST8
# undef PRIdLEAST8
# define PRIdLEAST8 "d"
#endif
#if !defined PRIiLEAST8 || PRI_MACROS_BROKEN
#if !defined PRIiLEAST8
# undef PRIiLEAST8
# define PRIiLEAST8 "i"
#endif
#if !defined PRIoLEAST8 || PRI_MACROS_BROKEN
#if !defined PRIoLEAST8
# undef PRIoLEAST8
# define PRIoLEAST8 "o"
#endif
#if !defined PRIuLEAST8 || PRI_MACROS_BROKEN
#if !defined PRIuLEAST8
# undef PRIuLEAST8
# define PRIuLEAST8 "u"
#endif
#if !defined PRIxLEAST8 || PRI_MACROS_BROKEN
#if !defined PRIxLEAST8
# undef PRIxLEAST8
# define PRIxLEAST8 "x"
#endif
#if !defined PRIXLEAST8 || PRI_MACROS_BROKEN
#if !defined PRIXLEAST8
# undef PRIXLEAST8
# define PRIXLEAST8 "X"
#endif
#if !defined PRIdLEAST16 || PRI_MACROS_BROKEN
#if !defined PRIdLEAST16
# undef PRIdLEAST16
# define PRIdLEAST16 "d"
#endif
#if !defined PRIiLEAST16 || PRI_MACROS_BROKEN
#if !defined PRIiLEAST16
# undef PRIiLEAST16
# define PRIiLEAST16 "i"
#endif
#if !defined PRIoLEAST16 || PRI_MACROS_BROKEN
#if !defined PRIoLEAST16
# undef PRIoLEAST16
# define PRIoLEAST16 "o"
#endif
#if !defined PRIuLEAST16 || PRI_MACROS_BROKEN
#if !defined PRIuLEAST16
# undef PRIuLEAST16
# define PRIuLEAST16 "u"
#endif
#if !defined PRIxLEAST16 || PRI_MACROS_BROKEN
#if !defined PRIxLEAST16
# undef PRIxLEAST16
# define PRIxLEAST16 "x"
#endif
#if !defined PRIXLEAST16 || PRI_MACROS_BROKEN
#if !defined PRIXLEAST16
# undef PRIXLEAST16
# define PRIXLEAST16 "X"
#endif
#if !defined PRIdLEAST32 || PRI_MACROS_BROKEN
#if !defined PRIdLEAST32
# undef PRIdLEAST32
# define PRIdLEAST32 "d"
#endif
#if !defined PRIiLEAST32 || PRI_MACROS_BROKEN
#if !defined PRIiLEAST32
# undef PRIiLEAST32
# define PRIiLEAST32 "i"
#endif
#if !defined PRIoLEAST32 || PRI_MACROS_BROKEN
#if !defined PRIoLEAST32
# undef PRIoLEAST32
# define PRIoLEAST32 "o"
#endif
#if !defined PRIuLEAST32 || PRI_MACROS_BROKEN
#if !defined PRIuLEAST32
# undef PRIuLEAST32
# define PRIuLEAST32 "u"
#endif
#if !defined PRIxLEAST32 || PRI_MACROS_BROKEN
#if !defined PRIxLEAST32
# undef PRIxLEAST32
# define PRIxLEAST32 "x"
#endif
#if !defined PRIXLEAST32 || PRI_MACROS_BROKEN
#if !defined PRIXLEAST32
# undef PRIXLEAST32
# define PRIXLEAST32 "X"
#endif
#if !defined PRIdLEAST64 || PRI_MACROS_BROKEN
#if !defined PRIdLEAST64
# undef PRIdLEAST64
# define PRIdLEAST64 PRId64
#endif
#if !defined PRIiLEAST64 || PRI_MACROS_BROKEN
#if !defined PRIiLEAST64
# undef PRIiLEAST64
# define PRIiLEAST64 PRIi64
#endif
#if !defined PRIoLEAST64 || PRI_MACROS_BROKEN
#if !defined PRIoLEAST64
# undef PRIoLEAST64
# define PRIoLEAST64 PRIo64
#endif
#if !defined PRIuLEAST64 || PRI_MACROS_BROKEN
#if !defined PRIuLEAST64
# undef PRIuLEAST64
# define PRIuLEAST64 PRIu64
#endif
#if !defined PRIxLEAST64 || PRI_MACROS_BROKEN
#if !defined PRIxLEAST64
# undef PRIxLEAST64
# define PRIxLEAST64 PRIx64
#endif
#if !defined PRIXLEAST64 || PRI_MACROS_BROKEN
#if !defined PRIXLEAST64
# undef PRIXLEAST64
# define PRIXLEAST64 PRIX64
#endif
#if !defined PRIdFAST8 || PRI_MACROS_BROKEN
#if !defined PRIdFAST8
# undef PRIdFAST8
# define PRIdFAST8 "d"
#endif
#if !defined PRIiFAST8 || PRI_MACROS_BROKEN
#if !defined PRIiFAST8
# undef PRIiFAST8
# define PRIiFAST8 "i"
#endif
#if !defined PRIoFAST8 || PRI_MACROS_BROKEN
#if !defined PRIoFAST8
# undef PRIoFAST8
# define PRIoFAST8 "o"
#endif
#if !defined PRIuFAST8 || PRI_MACROS_BROKEN
#if !defined PRIuFAST8
# undef PRIuFAST8
# define PRIuFAST8 "u"
#endif
#if !defined PRIxFAST8 || PRI_MACROS_BROKEN
#if !defined PRIxFAST8
# undef PRIxFAST8
# define PRIxFAST8 "x"
#endif
#if !defined PRIXFAST8 || PRI_MACROS_BROKEN
#if !defined PRIXFAST8
# undef PRIXFAST8
# define PRIXFAST8 "X"
#endif
#if !defined PRIdFAST16 || PRI_MACROS_BROKEN
#if !defined PRIdFAST16
# undef PRIdFAST16
# define PRIdFAST16 "d"
#endif
#if !defined PRIiFAST16 || PRI_MACROS_BROKEN
#if !defined PRIiFAST16
# undef PRIiFAST16
# define PRIiFAST16 "i"
#endif
#if !defined PRIoFAST16 || PRI_MACROS_BROKEN
#if !defined PRIoFAST16
# undef PRIoFAST16
# define PRIoFAST16 "o"
#endif
#if !defined PRIuFAST16 || PRI_MACROS_BROKEN
#if !defined PRIuFAST16
# undef PRIuFAST16
# define PRIuFAST16 "u"
#endif
#if !defined PRIxFAST16 || PRI_MACROS_BROKEN
#if !defined PRIxFAST16
# undef PRIxFAST16
# define PRIxFAST16 "x"
#endif
#if !defined PRIXFAST16 || PRI_MACROS_BROKEN
#if !defined PRIXFAST16
# undef PRIXFAST16
# define PRIXFAST16 "X"
#endif
#if !defined PRIdFAST32 || PRI_MACROS_BROKEN
#if !defined PRIdFAST32
# undef PRIdFAST32
# define PRIdFAST32 "d"
#endif
#if !defined PRIiFAST32 || PRI_MACROS_BROKEN
#if !defined PRIiFAST32
# undef PRIiFAST32
# define PRIiFAST32 "i"
#endif
#if !defined PRIoFAST32 || PRI_MACROS_BROKEN
#if !defined PRIoFAST32
# undef PRIoFAST32
# define PRIoFAST32 "o"
#endif
#if !defined PRIuFAST32 || PRI_MACROS_BROKEN
#if !defined PRIuFAST32
# undef PRIuFAST32
# define PRIuFAST32 "u"
#endif
#if !defined PRIxFAST32 || PRI_MACROS_BROKEN
#if !defined PRIxFAST32
# undef PRIxFAST32
# define PRIxFAST32 "x"
#endif
#if !defined PRIXFAST32 || PRI_MACROS_BROKEN
#if !defined PRIXFAST32
# undef PRIXFAST32
# define PRIXFAST32 "X"
#endif
#if !defined PRIdFAST64 || PRI_MACROS_BROKEN
#if !defined PRIdFAST64
# undef PRIdFAST64
# define PRIdFAST64 PRId64
#endif
#if !defined PRIiFAST64 || PRI_MACROS_BROKEN
#if !defined PRIiFAST64
# undef PRIiFAST64
# define PRIiFAST64 PRIi64
#endif
#if !defined PRIoFAST64 || PRI_MACROS_BROKEN
#if !defined PRIoFAST64
# undef PRIoFAST64
# define PRIoFAST64 PRIo64
#endif
#if !defined PRIuFAST64 || PRI_MACROS_BROKEN
#if !defined PRIuFAST64
# undef PRIuFAST64
# define PRIuFAST64 PRIu64
#endif
#if !defined PRIxFAST64 || PRI_MACROS_BROKEN
#if !defined PRIxFAST64
# undef PRIxFAST64
# define PRIxFAST64 PRIx64
#endif
#if !defined PRIXFAST64 || PRI_MACROS_BROKEN
#if !defined PRIXFAST64
# undef PRIXFAST64
# define PRIXFAST64 PRIX64
#endif
#if !defined PRIdMAX || PRI_MACROS_BROKEN
#if !defined PRIdMAX
# undef PRIdMAX
# define PRIdMAX (sizeof (uintmax_t) == sizeof (long) ? "ld" : "lld")
#endif
#if !defined PRIiMAX || PRI_MACROS_BROKEN
#if !defined PRIiMAX
# undef PRIiMAX
# define PRIiMAX (sizeof (uintmax_t) == sizeof (long) ? "li" : "lli")
#endif
#if !defined PRIoMAX || PRI_MACROS_BROKEN
#if !defined PRIoMAX
# undef PRIoMAX
# define PRIoMAX (sizeof (uintmax_t) == sizeof (long) ? "lo" : "llo")
#endif
#if !defined PRIuMAX || PRI_MACROS_BROKEN
#if !defined PRIuMAX
# undef PRIuMAX
# define PRIuMAX (sizeof (uintmax_t) == sizeof (long) ? "lu" : "llu")
#endif
#if !defined PRIxMAX || PRI_MACROS_BROKEN
#if !defined PRIxMAX
# undef PRIxMAX
# define PRIxMAX (sizeof (uintmax_t) == sizeof (long) ? "lx" : "llx")
#endif
#if !defined PRIXMAX || PRI_MACROS_BROKEN
#if !defined PRIXMAX
# undef PRIXMAX
# define PRIXMAX (sizeof (uintmax_t) == sizeof (long) ? "lX" : "llX")
#endif
#if !defined PRIdPTR || PRI_MACROS_BROKEN
#if !defined PRIdPTR
# undef PRIdPTR
# define PRIdPTR \
(sizeof (void *) == sizeof (long) ? "ld" : \
sizeof (void *) == sizeof (int) ? "d" : \
"lld")
#endif
#if !defined PRIiPTR || PRI_MACROS_BROKEN
#if !defined PRIiPTR
# undef PRIiPTR
# define PRIiPTR \
(sizeof (void *) == sizeof (long) ? "li" : \
sizeof (void *) == sizeof (int) ? "i" : \
"lli")
#endif
#if !defined PRIoPTR || PRI_MACROS_BROKEN
#if !defined PRIoPTR
# undef PRIoPTR
# define PRIoPTR \
(sizeof (void *) == sizeof (long) ? "lo" : \
sizeof (void *) == sizeof (int) ? "o" : \
"llo")
#endif
#if !defined PRIuPTR || PRI_MACROS_BROKEN
#if !defined PRIuPTR
# undef PRIuPTR
# define PRIuPTR \
(sizeof (void *) == sizeof (long) ? "lu" : \
sizeof (void *) == sizeof (int) ? "u" : \
"llu")
#endif
#if !defined PRIxPTR || PRI_MACROS_BROKEN
#if !defined PRIxPTR
# undef PRIxPTR
# define PRIxPTR \
(sizeof (void *) == sizeof (long) ? "lx" : \
sizeof (void *) == sizeof (int) ? "x" : \
"llx")
#endif
#if !defined PRIXPTR || PRI_MACROS_BROKEN
#if !defined PRIXPTR
# undef PRIXPTR
# define PRIXPTR \
(sizeof (void *) == sizeof (long) ? "lX" : \
@@ -1256,12 +1252,7 @@ _nl_load_domain (struct loaded_l10nfile *domain_file,
#endif
/* Get the header entry and look for a plural specification. */
#ifdef IN_LIBGLOCALE
nullentry =
_nl_find_msg (domain_file, domainbinding, NULL, "", &nullentrylen);
#else
nullentry = _nl_find_msg (domain_file, domainbinding, "", 0, &nullentrylen);
#endif
if (__builtin_expect (nullentry == (char *) -1, 0))
{
#ifdef _LIBC

Some files were not shown because too many files have changed in this diff Show More