Compare commits

...
Author SHA1 Message Date
Hemanth Kumar M D 6c453bb606 nptl: Skip pretty-printer tests without python3 [BZ #34507]
The tests-printers-out rule in Rules wraps $(PYTHON) through
$(test-wrapper-env).  Unlike ordinary tests, which wrap a freshly built
target binary, this wraps python3, a build-host tool.  When cross-testing
with test-wrapper set (e.g. via scripts/cross-test-ssh.sh) the whole
command is forwarded to the target; if the target lacks python3 the shell
returns 127 and evaluate-test.sh reports the six nptl pretty-printer
tests as FAIL instead of UNSUPPORTED.

scripts/test_printers_common.py already exits UNSUPPORTED (77) when its
dependencies are missing, but that is unreachable when python3 itself is
absent.

Guard the invocation with a "command -v" check so the recipe exits 77
(UNSUPPORTED) when python3 is not found.  Native builds are unaffected,
as configure requires python3.

Signed-off-by: Hemanth Kumar M D <Hemanth.KumarMD@windriver.com>
Suggested-by: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit c958d789db)
2026-09-08 10:53:54 -03:00
Adhemerval Zanella d407ace6e1 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>
(cherry picked from commit 7052455f0e)
2026-09-08 10:53:14 -03:00
Adhemerval Zanella 5e45e86fed 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>
(cherry picked from commit 41e9457c53)
2026-09-08 10:53:08 -03:00
Andreas Schwab a27376f0dc 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.

(cherry picked from commit aa56ccb98b)
2026-09-08 10:52:05 -03:00
Andreas Schwab bc76f2aa9b nss_files: fix swapped arguments in service parser
The port number in the service file is a decimal number followed by a
single slash.

(cherry picked from commit 66efdda2f8)
2026-09-08 10:51:56 -03:00
Shamil Abdulaev 552849c43c libio: Add test for fopen with an empty ", ccs=" value [BZ #34574]
This goes on top of the fix for CVE-2026-18374.  The test runs the
reproducer from the bug report, plus "w,ccs=" and "w,ccs=,", and
expects NULL with errno set to EINVAL.

Signed-off-by: Shamil Abdulaev <ashamil435@gmail.com>
Reviewed-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit cca93e5d88)
2026-09-05 00:25:50 +02:00
Dongkyun Son 2ba6f4c063 libio: Fix CVE-2026-18374 heap buffer overflow in ccs= handling
When fopen() is called with a ,ccs= parameter whose value becomes empty
after strip(), the code must reject it with EINVAL instead of attempting
to use it.  The original upstr() fallback could read past the ',' delimiter
and cause a heap buffer overflow.

The fix checks if the charset specification is empty after strip() and
returns EINVAL immediately, preventing the overflow and following the
approach described in BZ #34574.

CVE-2026-18374 - CVSS 4.9 (AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L)

Reported-by: AISLE in partnership with Red Hat
Signed-off-by: Dongkyun Son <dongkyun.s@samsung.com>
Reviewed-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit 9765a538eb)
2026-09-05 00:25:49 +02:00
Florian Weimer 0afd4d5feb iconvdata: Test case for bug 34556, bug 34568
Assisted-by: LLM
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit 35efcffa97)
2026-08-29 14:26:27 +02:00
Florian Weimer 87c2795cf6 iconvdata: EUC_JISX0213 decoding lacks pending character reset (CVE-2026-80489)
This fixes bug 34568.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit 4dafa087ff)
2026-08-29 14:26:27 +02:00
Florian Weimer 67db60ee15 iconvdata: SHIFT_JISX0213 decoding lacks pending character reset (CVE-2026-77117)
This fixes bug 34556.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit 68d94bbe50)
2026-08-29 14:26:27 +02:00
Florian Weimer 6ad255db1d stdlib: Fix right-justification in strfmon (bug 34510, CVE-2026-19499)
The memmove call did not take into account that __printf_buffer_pad
updated the buffer pointers.

Fixes commit e88b9f0e5c
("stdio-common: Convert vfprintf and related functions to buffers"),
which went into glibc 2.37.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit b090cf226f)
2026-08-28 21:24:13 +02:00
Adhemerval Zanella 893379d4ed posix: Remove unnecessary overflow check in wordexp (BZ 34090)
The WRDE_APPEND path duplicates the caller's we_wordv array, which
already holds we_offs + we_wordc + 1 pointers.  Follow-up to commit
e2cefe16c3.

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

(cherry picked from commit 53ec26f173)
2026-08-28 21:15:48 +02:00
Florian Weimer 2ea357280d misc: Fix out-of-bounds array write in tdelete (bug 34506)
Allocate the maximum array sizes directly, instead of resizing
the arrays as needed.  This eliminates alloca usage from the
function, and fixes the out-of-bounds accesses.  The asserts
guard against the bug coming back if the balancing of the tree
turns out not to work correctly.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
(cherry picked from commit e2789c46e3)
2026-08-28 21:04:28 +02:00
Adhemerval Zanella 8be3551ccb posix: Fix wordexp WRDE_APPEND to preserve state on non-NOSPACE errors (BZ 34090, CVE-2026-6368)
The previous implementation saved a copy of the wordexp_t struct at
entry and blindly restored it on error via (*pwordexp = old_word).
This is incorrect when WRDE_APPEND is set because w_addword may have
called realloc on we_wordv during partial processing before the error
was detected.  If realloc relocated the buffer, the saved we_wordv
pointer is dangling; restoring it causes a use-after-free in the
caller (e.g. via wordfree), and the relocated buffer is leaked.

Fix this by duplicating the we_wordv pointer array at entry when
WRDE_APPEND is set, so that all subsequent realloc calls inside
w_addword operate on the copy.

This change also fixes a POSIX conformance issue: if the WRDE_APPEND
flag is specified, pwordexp->we_wordc and pwordexp->we_wordv shall
not be modified.

Also fix two pre-existing error return paths in the '"' and '\'' cases
that returned directly from w_addword failures instead of going through
do_error, which would leak the saved array (and previously would also
skip the word cleanup).

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

Reviewed-by: DJ Delorie <dj@redhat.com>
(cherry picked from commit e2cefe16c3)
2026-08-14 12:54:47 +02:00
Adhemerval Zanella 7414631f8a 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.

(cherry picked from commit 07c24f3539)
2026-08-14 12:54:47 +02:00
Florian Weimer 296fb7f4a2 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>
(cherry picked from commit 4ba0b79b95)
2026-08-14 12:54:47 +02:00
Florian Weimer cb4c624480 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>
(cherry picked from commit a7b60d23bb)
2026-08-14 12:54:47 +02:00
Florian Weimer 299e1d25c3 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>
(cherry picked from commit ca44a6609c)
2026-08-14 12:54:47 +02:00
Florian Weimer 05dc6da0b4 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>
(cherry picked from commit cd0db208d5)
2026-08-14 12:54:46 +02:00
Florian Weimer 509d819cea 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>
(cherry picked from commit d58415eb17)
2026-08-14 12:54:46 +02:00
Florian Weimer 3c27e5170c 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>
(cherry picked from commit f69b7f95e3)
2026-08-14 12:54:46 +02:00
Florian Weimer f2f55eac9e 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>
(cherry picked from commit 360f352c9a)
2026-08-14 12:54:46 +02:00
Fabian Rast 5492954033 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>
(cherry picked from commit df83fa8813)
2026-07-13 18:00:25 -07:00
John David Anglin 0be5a6a72a 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:45:52 -04:00
Adhemerval Zanella f6713070c6 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>
(cherry picked from commit 1111fbdd3e)
2026-06-18 06:30:17 +02:00
Florian Weimer f671746f6c 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>
(cherry picked from commit e9325bd7d0)
2026-06-17 21:35:56 +02:00
Sam James 748699d938 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>
(cherry picked from commit d0cc9bf859)
2026-06-08 15:40:36 +01:00
DJ Delorie 6cebb0b80f stdio-common: Allow partially-filled %mc buffers [BZ #12701]
This is a backwards-compatible alternative to the main solution to
the %mc part of 12701.  The allocated buffer is expanded to the
requested size and NUL padded, but truncated reads are allowed.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
2026-06-01 13:32:45 -04:00
Maciej W. Rozycki 97926e9017 stdio-common: Reject insufficient character data in scanf [BZ #12701]
Reject invalid formatted scanf character data with the 'c' conversion
where there is not enough input available to satisfy the field width
requested.  It is required by ISO C that this conversion matches a
sequence of characters of exactly the number specified by the field
width and it is also already documented as such in our own manual:

"It reads precisely the next N characters, and fails if it cannot get
that many."

Currently a matching success is instead incorrectly produced where the
EOF condition is encountered before the required number of characters
has been retrieved, and the characters actually obtained are stored in
the buffer provided.

Add test cases accordingly and remove placeholders from 'c' conversion
input data for the existing scanf tests.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>

[This is a modified version of commit 2b16c76609, which tests for the
old behavior and only includes the test cases, for older branches
and downstream backports - DJ]

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
2026-06-01 13:32:29 -04:00
Maciej W. Rozycki b866ef2977 support: Implement 'xfmemopen' for seamless 'fmemopen' use
Add 'xfmemopen' wrapper for seamless 'fmemopen' use in tests, following
'xfopen', 'xfclose', etc., and providing a standardized error reporting
facility.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit fe709cc245)

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
2026-06-01 13:32:12 -04:00
Rocket Ma 4ebd33dd77 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>
(cherry picked from commit 8398987772)
2026-05-09 11:20:29 +02:00
Rocket Ma b4bca35ab9 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>
(cherry picked from commit ef3bfb5f91)
2026-05-09 11:20:28 +02:00
H.J. Lu 3e87cf9be9 abilist.awk: Handle weak unversioned defined symbols
After

commit f685e3953f9a38a41bbd0a597f9882870cee13d5
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Wed Oct 29 09:49:57 2025 +0800

    elf: Don't set its DT_VERSYM entry for unversioned symbol

ld no longer assigns version index 1 to unversioned defined symbol.
For libmachuser.so, "objdump --dynamic-syms" reports:

0000dd30  w   DF .text	000000f8              processor_start

instead of

0000dd30  w   DF .text	000000f8 (Base)       processor_start

Also allow NF == 6 for weak unversioned dynamic symbols.  This fixes BZ
33650.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Sam James <sam@gentoo.org>
(cherry picked from commit ee5d1db2a8)
2026-05-03 06:55:48 +01:00
Florian Weimer 3ecfa68561 Linux: Only define OPEN_TREE_* macros in <sys/mount.h> if undefined (bug 33921)
There is a conditional inclusion of <linux/mount.h> earlier in the file.
If that defines the macros, do not redefine them.  This addresses build
problems as the token sequence used by the UAPI macro definitions
changes between Linux versions.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit d12b017cdd)
2026-05-02 12:55:50 +02:00
DJ Delorie 12feedaf67 include: isolate __O_CLOEXEC flag for sys/mount.h and fcntl.h
Including sys/mount.h should not implicitly include fcntl.h
as that causes namespace pollution and conflicts with kernel
headers.  It only needs O_CLOEXEC for OPEN_TREE_CLOEXEC
(although it shouldn't need that, but it's defined that way)
so we provide that define (via a private version) separately.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
Tested-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit 419245719c)
2026-05-02 12:55:50 +02:00
Florian Weimer f13c1bb0f9 Use pending character state in IBM1390, IBM1399 character sets (CVE-2026-4046)
Follow the example in iso-2022-jp-3.c and use the __count state
variable to store the pending character.  This avoids restarting
the conversion if the output buffer ends between two 4-byte UCS-4
code points, so that the assert reported in the bug can no longer
happen.

Even though the fix is applied to ibm1364.c, the change is only
effective for the two HAS_COMBINED codecs for IBM1390, IBM1399.

The test case was mostly auto-generated using
claude-4.6-opus-high-thinking, and composer-2-fast shows up in the
log as well.  During review, gpt-5.4-xhigh flagged that the original
version of the test case was not exercising the new character
flush logic.

This fixes bug 33980.

Assisted-by: LLM
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit d6f08d1cf0)
2026-04-19 12:57:49 +02:00
Michael Jeanson a56a2943d2 tests: fix tst-rseq with Linux 7.0
A sub-test of tst-rseq is to validate the return code and errno of the
rseq syscall when attempting to register the exact same rseq area as was
done in the dynamic loader.

This involves finding the rseq area address by adding the
'__rseq_offset' to the thread pointer and calculating the area size from
the AT_RSEQ_FEATURE_SIZE auxiliary vector. However the test currently
calculates the size of the rseq area allocation in the TLS block which
must be a multiple of AT_RSEQ_ALIGN.

Up until now that happened to be the same value since the feature size
and alignment exposed by the kernel were below the minimum ABI size of
32. Starting with Linux 7.0 the feature size has reached 33 while the
alignment is now 64.

This results in the test trying to re-register the rseq area with a
different size and thus not getting the expected errno value.

Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
(cherry picked from commit 67f303b47d)
2026-03-29 12:47:57 +02:00
Xi Ruoyao 68099ccc94 elf: parse /proc/self/maps as the last resort to find the gap for tst-link-map-contiguous-ldso
The initialization process of libc.so calls mmap() several times and the
kernel may lay the maps into the gap.  If all pages in the gap are
occupied, the test would not be able to find the gap with mmap() and the
test would fail.

The failure reproduces most frequently on LoongArch because with the
commonly used page size (16 KiB) the gap only contains 4 pages and the
probability they are all occupied is not near to zero.

With the changes in the patch, a test run may output:

    info: ld.so link map is not contiguous
    info: object "/dev/zero" found at 0x7ffff1fe0000 - 0x7ffff1fe4000
    info: anonymous mapping found at 0x7ffff1fe4000 - 0x7ffff1fec000

Also take the chance to fix a mistake in the "object found at" message
which has puzzled me during the initial debug session.

Signed-off-by: Xi Ruoyao <xry111@xry111.site>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit aed8390a6a)
2026-03-28 17:43:25 +01:00
Carlos O'Donell 426378547e resolv: Check hostname for validity (CVE-2026-4438)
The processed hostname in getanswer_ptr should be correctly checked to
avoid invalid characters from being allowed, including shell
metacharacters. It is a security issue to fail to check the returned
hostname for validity.

A regression test is added for invalid metacharacters and other cases
of invalid or valid characters.

No regressions on x86_64-linux-gnu.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit e10977481f)
2026-03-27 19:59:16 +01:00
Carlos O'Donell 8e863fb1c9 resolv: Count records correctly (CVE-2026-4437)
The answer section boundary was previously ignored, and the code in
getanswer_ptr would iterate past the last resource record, but not
beyond the end of the returned data.  This could lead to subsequent data
being interpreted as answer records, thus violating the DNS
specification.  Such resource records could be maliciously crafted and
hidden from other tooling, but processed by the glibc stub resolver and
acted upon by the application.  While we trust the data returned by the
configured recursive resolvers, we should not trust its format and
should validate it as required.  It is a security issue to incorrectly
process the DNS protocol.

A regression test is added for response section crossing.

No regressions on x86_64-linux-gnu.

Reviewed-by: Collin Funk <collin.funk1@gmail.com>
(cherry picked from commit 9f5f18aab4)
2026-03-25 21:15:22 +01:00
Florian Weimer 1a19d5a507 posix: Run tst-wordexp-reuse-mem test
The test was not properly scheduled for execution with a Makefile
dependency.

Fixes commit 80cc58ea2d ("posix: Reset
wordexp_t fields with WRDE_REUSE (CVE-2025-15281 / BZ 33814").

(cherry picked from commit bed2db02f3)
2026-03-23 15:24:45 +01:00
Florian Weimer 9cd9c90544 iconvdata: Fix invalid pointer arithmetic in ANSI_X3.110 module
The expression inptr + 1 can technically be invalid: if inptr == inend,
inptr may point one element past the end of an array.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit e98bd0c54d)
2026-03-23 15:24:45 +01:00
Florian Weimer ebd45473f5 nss: Missing checks in __nss_configure_lookup, __nss_database_get (bug 28940)
This avoids a null pointer dereference in the
nss_database_check_reload_and_get function, and assertion failures.

Reviewed-by: Sam James <sam@gentoo.org>
(cherry picked from commit 5b713b4944)
2026-02-18 08:59:56 +01:00
Florian Weimer 937ef7aaf3 Linux: In getlogin_r, use utmp fallback only for specific errors
Most importantly, if getwpuid_r fails, it does not make sense to retry
via utmp because the user ID obtained from there is less reliable than
the one from /proc/self/loginuid.

Reviewed-by: Sam James <sam@gentoo.org>
(cherry picked from commit 28660f4b45)
2026-02-18 08:59:56 +01:00
Florian Weimer 3989780591 nss: Introduce dedicated struct nss_database_for_fork type
The initialized field in struct nss_database_data is rather confusing
because it is not used by the regular NSS code, only by the fork
state synchronization code.  Introduce a separate type and place
the initialized field there.

Reviewed-by: Sam James <sam@gentoo.org>
(cherry picked from commit 7bb859f419)
2026-02-18 08:59:56 +01:00
Florian Weimer 912d89a766 Switch currency symbol for the bg_BG locale to euro
Bulgaria joined the eurozone on 2026-01-01.

Suggested-by: Йордан Гигов <jgigov@abv.bg>
Reviewed-by: Collin Funk <collin.funk1@gmail.com>
(cherry picked from commit 78fdb2d6b1)
2026-01-24 13:25:07 +01:00
Adhemerval Zanella cbf39c26b2 posix: Reset wordexp_t fields with WRDE_REUSE (CVE-2025-15281 / BZ 33814)
The wordexp fails to properly initialize the input wordexp_t when
WRDE_REUSE is used. The wordexp_t struct is properly freed, but
reuses the old wc_wordc value and updates the we_wordv in the
wrong position.  A later wordfree will then call free with an
invalid pointer.

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

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit 80cc58ea2d)
2026-01-20 12:34:07 -03:00
Carlos O'Donell 453e6b8dba resolv: Fix NSS DNS backend for getnetbyaddr (CVE-2026-0915)
The default network value of zero for net was never tested for and
results in a DNS query constructed from uninitialized stack bytes.
The solution is to provide a default query for the case where net
is zero.

Adding a test case for this was straight forward given the existence of
tst-resolv-network and if the test is added without the fix you observe
this failure:

FAIL: resolv/tst-resolv-network
original exit status 1
error: tst-resolv-network.c:174: invalid QNAME: \146\218\129\128
error: 1 test failures

With a random QNAME resulting from the use of uninitialized stack bytes.

After the fix the test passes.

Additionally verified using wireshark before and after to ensure
on-the-wire bytes for the DNS query were as expected.

No regressions on x86_64.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit e56ff82d50)
2026-01-16 09:31:22 -05:00
Siddhesh Poyarekar b0ec8fb689 memalign: reinstate alignment overflow check (CVE-2026-0861)
The change to cap valid sizes to PTRDIFF_MAX inadvertently dropped the
overflow check for alignment in memalign functions, _mid_memalign and
_int_memalign.  Reinstate the overflow check in _int_memalign, aligned
with the PTRDIFF_MAX change since that is directly responsible for the
CVE.  The missing _mid_memalign check is not relevant (and does not have
a security impact) and may need a different approach to fully resolve,
so it has been omitted.

CVE-Id: CVE-2026-0861
Vulnerable-Commit: 9bf8e29ca1
Reported-by: Igor Morgenstern, Aisle Research
Fixes: BZ #33796
Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
(cherry picked from commit c9188d3337)
2026-01-15 16:42:00 -05:00
Sunil K Pandey f122d0b4d1 nptl: Optimize trylock for high cache contention workloads (BZ #33704)
Check lock availability before acquisition to reduce cache line
bouncing.  Significantly improves trylock throughput on multi-core
systems under heavy contention.

Tested on x86_64.

Fixes BZ #33704.

Co-authored-by: Alex M Wells <alex.m.wells@intel.com>
Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
(cherry picked from commit 63716823db)
2025-12-30 13:57:56 -08:00
Florian Weimer a1d3294a5b support: Exit on consistency check failure in resolv_response_add_name
Using TEST_VERIFY (crname_target != crname) instructs some analysis
tools that crname_target == crname might hold.  Under this assumption,
they report a use-after-free for crname_target->offset below, caused
by the previous free (crname).

Reviewed-by: Collin Funk <collin.funk1@gmail.com>
(cherry picked from commit b64335ff11)
2025-12-23 18:48:18 +01:00
Florian Weimer 8dfb84ad4e support: Fix FILE * leak in check_for_unshare_hints in test-container
The file opened via fopen is never closed.

(cherry picked from commit 20a2a75608)
2025-12-23 18:48:18 +01:00
Collin Funk 2a0873aa81 sprof: fix -Wformat warnings on 32-bit hosts
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
(cherry picked from commit 9681f645ba)
2025-12-23 18:48:18 +01:00
DJ Delorie efdf4c0c87 sprof: check pread size and offset for overflow
Add a bit of descriptive paranoia to the values we read from
the ELF headers and use to access data.

Reviewed-by: Collin Funk <collin.funk1@gmail.com>
(cherry picked from commit 324084649b)
2025-12-23 18:48:18 +01:00
Yury Khrustalev b11411fe2e posix: Fix invalid flags test for p{write,read}v2
Two tests fail from time to time when a new flag is added for the
p{write,read}v2 functions in a new Linux kernel:

 - misc/tst-preadvwritev2
 - misc/tst-preadvwritev64v2

This disrupts when testing Glibc on a system with a newer kernel
and it seems we can try improve testing for invalid flags setting
all the bits that are not supposed to be supported (rather than
setting only the next unsupported bit).

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit 58a31b4316)
2025-12-11 21:59:25 +01:00
Sachin Monga 8aaf4b732d ppc64le: Power 10 rawmemchr clobbers v20 (bug #33091)
Replace non-volatile(v20) by volatile(v17)
since v20 is not restored

Reviewed-by: Peter Bergner <bergner@tenstorrent.com>
(cherry picked from commit b59799f14f)
2025-11-27 03:28:24 -05:00
Sachin Monga 2dbf973fe0 ppc64le: Restore optimized strncmp for power10
This patch addresses the actual cause of CVE-2025-5745

The vector non-volatile registers are not used anymore for
32 byte load and comparison operation

Additionally, the assembler workaround used earlier for the
instruction lxvp is replaced with actual instruction.

Signed-off-by: Sachin Monga <smonga@linux.ibm.com>
Co-authored-by: Paul Murphy <paumurph@redhat.com>
(cherry picked from commit 2ea943f7d4)
2025-11-21 00:31:01 -05:00
Sachin Monga 6b2957cfe8 ppc64le: Restore optimized strcmp for power10
This patch addresses the actual cause of CVE-2025-5702

The vector non-volatile registers are not used anymore for
32 byte load and comparison operation

Additionally, the assembler workaround used earlier for the
instruction lxvp is replaced with actual instruction.

Signed-off-by: Sachin Monga <smonga@linux.ibm.com>
Co-authored-by: Paul Murphy <paumurph@redhat.com>
(cherry picked from commit 9a40b1cda5)
2025-11-21 00:30:15 -05:00
Pierre Blanchard 828b8d23f3 AArch64: Fix and improve SVE pow(f) special cases
powf:

Update scalar special case function to best use new interface.

pow:

Make specialcase NOINLINE to prevent str/ldr leaking in fast path.
Remove depency in sv_call2, as new callback impl is not a
performance gain.
Replace with vectorised specialcase since structure of scalar
routine is fairly simple.

Throughput gain of about 5-10% on V1 for large values and 25% for subnormal `x`.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
(cherry picked from commit bb6519de1e)
2025-11-18 16:07:21 +00:00
Pierre Blanchard 710d7a2e83 AArch64: fix SVE tanpi(f) [BZ #33642]
Fixed svld1rq using incorrect predicates (BZ #33642).
Next to no performance variations (tested on V1).

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
(cherry picked from commit e889160273)
2025-11-18 16:07:21 +00:00
Joe Ramsay 0c9430ed97 AArch64: Fix instability in AdvSIMD sinh
Previously presence of special-cases in one lane could affect the
results in other lanes due to unconditional scalar fallback. The old
WANT_SIMD_EXCEPT option (which has never been enabled in libmvec) has
been removed from AOR, making it easier to spot and fix
this. No measured change in performance. This patch applies cleanly as
far back as 2.41, however there are conflicts with 2.40 where sinh was
first introduced.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
(cherry picked from commit e45af510bc)
2025-11-18 16:07:21 +00:00
Joe Ramsay ec041b1f53 AArch64: Fix instability in AdvSIMD tan
Previously presence of special-cases in one lane could affect the
results in other lanes due to unconditional scalar fallback. The old
WANT_SIMD_EXCEPT option (which has never been enabled in libmvec) has
been removed from AOR, making it easier to spot and fix this. 4%
improvement in throughput with GCC 14 on Neoverse V1. This bug is
present as far back as 2.39 (where tan was first introduced).

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
(cherry picked from commit 6c22823da5)
2025-11-18 16:07:21 +00:00
Joe Ramsay 97297120ce AArch64: Optimise SVE scalar callbacks
Instead of using SVE instructions to marshall special results into the
correct lane, just write the entire vector (and the predicate) to
memory, then use cheaper scalar operations.

Geomean speedup of 16% in special intervals on Neoverse with GCC 14.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
(cherry picked from commit 5b82fb1882)
2025-11-18 16:07:21 +00:00
Yury Khrustalev 17c3eab387 aarch64: fix includes in SME tests
Use the correct include for the SIGCHLD macro: signal.h

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
(cherry picked from commit a9c426bcca)
2025-11-13 13:06:49 +00:00
Yury Khrustalev de1fe81f47 aarch64: fix cfi directives around __libc_arm_za_disable
Incorrect CFI directive corrupted call stack information
and prevented debuggers from correctly displaying call
stack information.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit 2f77aec043)
2025-11-10 10:02:58 +00:00
Jiamei Xie bf499c2a49 x86: fix wmemset ifunc stray '!' (bug 33542)
The ifunc selector for wmemset had a stray '!' in the
X86_ISA_CPU_FEATURES_ARCH_P(...) check:

  if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX2)
      && X86_ISA_CPU_FEATURES_ARCH_P (cpu_features,
                                      AVX_Fast_Unaligned_Load, !))

This effectively negated the predicate and caused the AVX2/AVX512
paths to be skipped, making the dispatcher fall back to the SSE2
implementation even on CPUs where AVX2/AVX512 are available. The
regression leads to noticeable throughput loss for wmemset.

Remove the stray '!' so the AVX_Fast_Unaligned_Load capability is
tested as intended and the correct AVX2/EVEX variants are selected.

Impact:
- On AVX2/AVX512-capable x86_64, wmemset no longer incorrectly
  falls back to SSE2; perf now shows __wmemset_evex/avx2 variants.

Testing:
- benchtests/bench-wmemset shows improved bandwidth across sizes.
- perf confirm the selected symbol is no longer SSE2.

Signed-off-by: xiejiamei <xiejiamei@hygon.com>
Signed-off-by: Li jing <lijing@hygon.cn>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit 4d86b6cdd8)
2025-11-04 12:22:09 +00:00
Yury Khrustalev 71874f167a aarch64: tests for SME
This commit adds tests for the following use cases relevant to handing of
the SME state:

 - fork() and vfork()
 - clone() and clone3()
 - signal handler

While most cases are trivial, the case of clone3() is more complicated since
the clone3() symbol is not public in Glibc.

To avoid having to check all possible ways clone3() may be called via other
public functions (e.g. vfork() or pthread_create()), we put together a test
that links directly with clone3.o. All the existing functions that have calls
to clone3() may not actually use it, in which case the outcome of such tests
would be unexpected. Having a direct call to the clone3() symbol in the test
allows to check precisely what we need to test: that the __arm_za_disable()
function is indeed called and has the desired effect.

Linking to clone3.o also requires linking to __arm_za_disable.o that in
turn requires the _dl_hwcap2 hidden symbol which to provide in the test
and initialise it before using.

Co-authored-by: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit ecb0fc2f0f)
2025-10-20 13:10:22 +01:00
Yury Khrustalev 256030b984 aarch64: clear ZA state of SME before clone and clone3 syscalls
This change adds a call to the __arm_za_disable() function immediately
before the SVC instruction inside clone() and clone3() wrappers. It also
adds a macro for inline clone() used in fork() and adds the same call to
the vfork implementation. This sets the ZA state of SME to "off" on return
from these functions (for both the child and the parent).

The __arm_za_disable() function is described in [1] (8.1.3). Note that
the internal Glibc name for this function is __libc_arm_za_disable().

When this change was originally proposed [2,3], it generated a long
discussion where several questions and concerns were raised. Here we
will address these concerns and explain why this change is useful and,
in fact, necessary.

In a nutshell, a C library that conforms to the AAPCS64 spec [1] (pertinent
to this change, mainly, the chapters 6.2 and 6.6), should have a call to the
__arm_za_disable() function in clone() and clone3() wrappers. The following
explains in detail why this is the case.

When we consider using the __arm_za_disable() function inside the clone()
and clone3() libc wrappers, we talk about the C library subroutines clone()
and clone3() rather than the syscalls with similar names. In the current
version of Glibc, clone() is public and clone3() is private, but it being
private is not pertinent to this discussion.

We will begin with stating that this change is NOT a bug fix for something
in the kernel. The requirement to call __arm_za_disable() does NOT come from
the kernel. It also is NOT needed to satisfy a contract between the kernel
and userspace. This is why it is not for the kernel documentation to describe
this requirement. This requirement is instead needed to satisfy a pure userspace
scheme outlined in [1] and to make sure that software that uses Glibc (or any
other C library that has correct handling of SME states (see below)) conforms
to [1] without having to unnecessarily become SME-aware thus losing portability.

To recap (see [1] (6.2)), SME extension defines SME state which is part of
processor state. Part of this SME state is ZA state that is necessary to
manage ZA storage register in the context of the ZA lazy saving scheme [1]
(6.6). This scheme exists because it would be challenging to handle ZA
storage of SME in either callee-saved or caller-saved manner.

There are 3 kinds of ZA state that are defined in terms of the PSTATE.ZA
bit and the TPIDR2_EL0 register (see [1] (6.6.3)):

- "off":       PSTATE.ZA == 0
- "active":    PSTATE.ZA == 1 TPIDR2_EL0 == null
- "dormant":   PSTATE.ZA == 1 TPIDR2_EL0 != null

As [1] (6.7.2) outlines, every subroutine has exactly one SME-interface
depending on the permitted ZA-states on entry and on normal return from
a call to this subroutine. Callers of a subroutine must know and respect
the ZA-interface of the subroutines they are using. Using a subroutine
in a way that is not permitted by its ZA-interface is undefined behaviour.

In particular, clone() and clone3() (the C library functions) have the
ZA-private interface. This means that the permitted ZA-states on entry
are "off" and "dormant" and that the permitted states on return are "off"
or "dormant" (but if and only if it was "dormant" on entry).

This means that both functions in question should correctly handle both
"off" and "dormant" ZA-states on entry. The conforming states on return
are "off" and "dormant" (if inbound state was already "dormant").

This change ensures that the ZA-state on return is always "off". Note,
that, in the context of clone() and clone3(), "on return" means a point
when execution resumes at certain address after transferring from clone()
or clone3(). For the caller (we may refer to it as "parent") this is the
return address in the link register where the RET instruction jumps. For
the "child", this is the target branch address.

So, the "off" state on return is permitted and conformant. Why can't we
retain the "dormant" state? In theory, we can, but we shouldn't, here is
why.

Every subroutine with a private-ZA interface, including clone() and clone3(),
must comply with the lazy saving scheme [1] (6.7.2). This puts additional
responsibility on a subroutine if ZA-state on return is "dormant" because
this state has special meaning. The "caller" (that is the place in code
where execution is transferred to, so this include both "parent" and "child")
may check the ZA-state and use it as per the spec of the "dormant" state that
is outlined in [1] (6.6.6 and 6.6.7).

Conforming to this would require more code inside of clone() and clone3()
which hardly is desirable.

For the return to "parent" this could be achieved in theory, but given that
neither clone() nor clone3() are supposed to be used in the middle of an
SME operation, if wouldn't be useful. For the "return" to "child" this
would be particularly difficult to achieve given the complexity of these
functions and their interfaces. Most importantly, it would be illegal
and somewhat meaningless to allow a "child" to start execution in the
"dormant" ZA-state because the very essence of the "dormant" state implies
that there is a place to return and that there is some outer context that
we are allowed to interact with.

To sum up, calling __arm_za_disable() to ensure the "off" ZA-state when the
execution resumes after a call to clone() or clone3() is correct and also
the most simple way to conform to [1].

Can there be situations when we can avoid calling __arm_za_disable()?

Calling __arm_za_disable() implies certain (sufficiently small) overhead,
so one might rightly ponder avoiding making a call to this function when
we can afford not to. The most trivial cases like this (e.g. when the
calling thread doesn't have access to SME or to the TPIDR2_EL0 register)
are already handled by this function (see [1] (8.1.3 and 8.1.2)). Reasoning
about other possible use cases would require making code inside clone() and
clone3() more complicated and it would defeat the point of trying to make
an optimisation of not calling __arm_za_disable().

Why can't the kernel do this instead?

The handling of SME state by the kernel is described in [4]. In short,
kernel must not impose a specific ZA-interface onto a userspace function.
Interaction with the kernel happens (among other thing) via system calls.
In Glibc many of the system calls (notably, including SYS_clone and
SYS_clone3) are used via wrappers, and the kernel has no control of them
and, moreover, it cannot dictate how these wrappers should behave because
it is simply outside of the kernel's remit.

However, in certain cases, the kernel may ensure that a "child" doesn't
start in an incorrect state. This is what is done by the recent change
included in 6.16 kernel [5]. This is not enough to ensure that code that
uses clone() and clone3() function conforms to [1] when it runs on a
system that provides SME, hence this change.

[1]: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
[2]: https://inbox.sourceware.org/libc-alpha/20250522114828.2291047-1-yury.khrustalev@arm.com
[3]: https://inbox.sourceware.org/libc-alpha/20250609121407.3316070-1-yury.khrustalev@arm.com
[4]: https://www.kernel.org/doc/html/v6.16/arch/arm64/sme.html
[5]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=cde5c32db55740659fca6d56c09b88800d88fd29

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit 27effb3d50)
2025-10-20 13:10:16 +01:00
Yury Khrustalev 6de12fc9ad aarch64: define macro for calling __libc_arm_za_disable
A common sequence of instructions is used in several places
in assembly files, so define it in one place as an assembly
macro.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit b4b713bd89)
2025-10-20 13:10:09 +01:00
Sunil K Pandey ab8c1b5d62 x86: Detect Intel Nova Lake Processor
Detect Intel Nova Lake Processor and tune it similar to Intel Panther
Lake.  https://cdrdv2.intel.com/v1/dl/getContent/671368 Section 1.2.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
(cherry picked from commit a114e29ddd)
2025-10-08 10:56:42 -07:00
Sunil K Pandey bf48b17a28 x86: Detect Intel Wildcat Lake Processor
Detect Intel Wildcat Lake Processor and tune it similar to Intel Panther
Lake.  https://cdrdv2.intel.com/v1/dl/getContent/671368 Section 1.2.

Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
(cherry picked from commit f8dd52901b)
2025-10-08 10:56:28 -07:00
Adhemerval Zanella 18fd689cdc nptl: Fix MADV_GUARD_INSTALL logic for thread without guard page (BZ 33356)
The main issue is that setup_stack_prot fails to account for cases where
the cached thread stack lacks a guard page, which can cause madvise to
fail. Update the logic to also handle whether MADV_GUARD_INSTALL is
supported when resizing the guard page.

Checked on x86_64-linux-gnu with 6.8.0 and 6.15 kernels.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit 855bfa2566)
2025-09-23 10:33:49 -03:00
Florian Weimer 46b4e37c9e nss: Group merge does not react to ERANGE during merge (bug 33361)
The break statement in CHECK_MERGE is expected to exit the surrounding
while loop, not the do-while loop with in the macro.  Remove the
do-while loop from the macro.  It is not needed to turn the macro
expansion into a single statement due to the way CHECK_MERGE is used
(and the statement expression would cover this anyway).

Reviewed-by: Collin Funk <collin.funk1@gmail.com>
(cherry picked from commit 0fceed2545)
2025-09-19 09:19:17 +02:00
Florian Weimer 1166170d95 libio: Define AT_RENAME_* with the same tokens as Linux
Linux uses different expressions for the RENAME_* and AT_RENAME_*
constants.  Mirror that in <stdio.h>, so that the macro redefinitions
do not result in preprocessor warnings.

Reviewed-by: Collin Funk <collin.funk1@gmail.com>
(cherry picked from commit b173557da9)
2025-09-19 09:18:18 +02:00
Pierre Blanchard e34453cd6a AArch64: Fix SVE powf routine [BZ #33299]
Fix a bug in predicate logic introduced in last change.
A slight performance improvement from relying on all true
predicates during conversion from single to double.
This fixes BZ #33299.

Reviewed-by: Wilco Dijkstra  <Wilco.Dijkstra@arm.com>
(cherry picked from commit aac077645a)
2025-09-03 13:21:19 +00:00
H.J. Lu e87844ec42 i386: Also add GLIBC_ABI_GNU2_TLS version [BZ #33129]
Since the GNU2 TLS run-time bug:

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

affects both i386 and x86-64, also add GLIBC_ABI_GNU2_TLS version to i386
to indicate the working GNU2 TLS run-time.  For x86-64, the additional
GNU2 TLS run-time bug fix is needed for

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

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Sam James <sam@gentoo.org>
(cherry picked from commit bd4628f3f1)
2025-08-19 05:16:06 -07:00
H.J. Lu 7a8f3c6ee4 x86-64: Add GLIBC_ABI_DT_X86_64_PLT [BZ #33212]
When the linker -z mark-plt option is used to add DT_X86_64_PLT,
DT_X86_64_PLTSZ and DT_X86_64_PLTENT, the r_addend field of the
R_X86_64_JUMP_SLOT relocation stores the offset of the indirect
branch instruction.  However, glibc versions without the commit:

commit f8587a6189
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri May 20 19:21:48 2022 -0700

    x86-64: Ignore r_addend for R_X86_64_GLOB_DAT/R_X86_64_JUMP_SLOT

    According to x86-64 psABI, r_addend should be ignored for R_X86_64_GLOB_DAT
    and R_X86_64_JUMP_SLOT.  Since linkers always set their r_addends to 0, we
    can ignore their r_addends.

    Reviewed-by: Fangrui Song <maskray@google.com>

won't ignore the r_addend value in the R_X86_64_JUMP_SLOT relocation.
Such programs and shared libraries will fail at run-time randomly.

Add GLIBC_ABI_DT_X86_64_PLT version to indicate that glibc is compatible
with DT_X86_64_PLT.

The linker can add the glibc GLIBC_ABI_DT_X86_64_PLT version dependency
whenever -z mark-plt is passed to the linker.  The resulting programs and
shared libraries will fail to load at run-time against libc.so without the
GLIBC_ABI_DT_X86_64_PLT version, instead of fail randomly.

This fixes BZ #33212.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Sam James <sam@gentoo.org>
(cherry picked from commit 399384e0c8)
2025-08-15 22:17:02 +01:00
H.J. Lu 3970785beb x86-64: Add GLIBC_ABI_GNU2_TLS version [BZ #33129]
Programs and shared libraries compiled with -mtls-dialect=gnu2 may fail
silently at run-time against glibc without the GNU2 TLS run-time fix
for:

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

Add GLIBC_ABI_GNU2_TLS version to indicate that glibc has the working
GNU2 TLS run-time.  Linker can add the GLIBC_ABI_GNU2_TLS version to
binaries which depend on the working GNU2 TLS run-time:

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

so that such programs and shared libraries will fail to load and run at
run-time against libc.so without the GLIBC_ABI_GNU2_TLS version, instead
of fail silently at random.

This fixes BZ #33129.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Sam James <sam@gentoo.org>
(cherry picked from commit 9df8fa397d)
2025-08-15 22:16:48 +01:00
H.J. Lu d0f72b96f2 i386: Add GLIBC_ABI_GNU_TLS version [BZ #33221]
On i386, programs and shared libraries with __thread usage may fail
silently at run-time against glibc without the TLS run-time fix for:

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

Add GLIBC_ABI_GNU_TLS version to indicate that glibc has the working
GNU TLS run-time.  Linker can add the GLIBC_ABI_GNU_TLS version to
binaries which depend on the working TLS run-time so that such programs
and shared libraries will fail to load and run at run-time against
libc.so without the GLIBC_ABI_GNU_TLS version, instead of fail silently
at random.

This fixes BZ #33221.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Sam James <sam@gentoo.org>
(cherry picked from commit ed1b7a5a48)
2025-08-15 22:15:49 +01:00
Jens Remus 8dbaecbe92 Use TLS initial-exec model for __libc_tsd_CTYPE_* thread variables [BZ #33234]
Commit 10a66a8e42 ("Remove <libc-tsd.h>") removed the TLS initial-exec
(IE) model attribute from the __libc_tsd_CTYPE_* thread variable declarations
and definitions.  Commit a894f04d87 ("Optimize __libc_tsd_* thread
variable access") restored it on declarations.

Restore the TLS initial-exec model attribute on __libc_tsd_CTYPE_* thread
variable definitions.

This resolves test tst-locale1 failure on s390 32-bit, when using a
GNU linker without the fix from GNU binutils commit aefebe82dc89
("IBM zSystems: Fix offset relative to static TLS").

Reviewed-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit e5363e6f46)
2025-08-14 12:06:14 +02:00
Samuel Thibault d7274d718e malloc: Fix checking for small negative values of tcache_key
tcache_key is unsigned so we should turn it explicitly to signed before
taking its absolute value.

(cherry picked from commit 8543577b04)
2025-08-11 14:33:39 +00:00
Samuel Thibault a96a82c4a5 malloc: Make sure tcache_key is odd enough
We want tcache_key not to be a commonly-occurring value in memory, so ensure
a minimum amount of one and zero bits.

And we need it non-zero, otherwise even if tcache_double_free_verify sets
e->key to 0 before calling __libc_free, it gets called again by __libc_free,
thus looping indefinitely.

Fixes: c968fe5062 ("malloc: Use tailcalls in __libc_free")
(cherry picked from commit 2536c4f858)
2025-08-11 14:33:30 +00:00
Wilco Dijkstra c491dabd8a malloc: Fix MAX_TCACHE_SMALL_SIZE
MAX_TCACHE_SMALL_SIZE should use chunk size since it is used after
checked_request2size.  Increase limit of tcache_max_bytes by 1 since all
comparisons use '<'.  As a result, the last tcache entry is now used as
expected.

Reviewed-by: DJ Delorie <dj@redhat.com>
(cherry picked from commit ad4caba414)
2025-08-11 14:33:16 +00:00
Wilco Dijkstra 8a726b6304 malloc: Remove redundant NULL check
Remove a redundant NULL check from tcache_get_n.

Reviewed-by: Cupertino Miranda <cupertino.miranda@oracle.com>
(cherry picked from commit 089b4fb90f)
2025-08-11 14:33:01 +00:00
Samuel Thibault c5476b7907 hurd: support: Fix running SGID tests
Secure mode is enabled only if SGID actually provides a new privilege,
so we have to drop it before gaining it again.

Fixes commit 3a3fb2ed83
("Fix error reporting (false negatives) in SGID tests")

(cherry picked from commit ad4589e2d8)
2025-08-06 11:56:44 +02:00
H.J. Lu e5754399b5 Revert "tst-freopen4-main.c: Call support_capture_subprocess with chroot"
Revert commit 6463d4a7b2 to fix

FAIL: stdio-common/tst-freopen4-mem
FAIL: stdio-common/tst-freopen64-4-mem

This fixes BZ #33254.

Reviewed-by: Sam James <sam@gentoo.org>
(cherry picked from commit adec0bf05b)
2025-08-05 21:55:09 +01:00
H.J. Lu c090b0cb1c tst-env-setuid: Delete LD_DEBUG_OUTPUT output
Update tst-env-setuid.c to delete LD_DEBUG_OUTPUT output, instead of
leaving it behind.

This partially fixes BZ #33182.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit 5d23dfb289)
2025-08-05 13:53:13 +02:00
H.J. Lu 41a77b78cf tst-freopen4-main.c: Call support_capture_subprocess with chroot
Update tst-freopen4-main.c to call support_capture_subprocess with chroot,
which makes temporary files inaccessible, so that temporary files can be
deleted.

This partially fixes BZ #33182.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit 6463d4a7b2)
2025-08-05 13:52:19 +02:00
H.J. Lu 9ec7a532ff tst-fopen-threaded.c: Delete temporary file
Update tst-fopen-threaded.c to call support_create_temp_directory to
create a temporary directory and open "file" in the temporary directory,
instead of using /tmp/openclosetest and leaving it behind.

This partially fixes BZ #33182.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit e7db515060)
2025-08-05 13:50:57 +02:00
H.J. Lu 9d5bf9c17d Delete temporary files in support_subprocess
Call support_delete_temp_files to delete temporary files before exit in
support_subprocess.

This partially fixes BZ #33182.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit d27b1a71cd)
2025-08-05 13:49:25 +02:00
Adhemerval Zanella b38f3f60d5 nptl: Fix SYSCALL_CANCEL for return values larger than INT_MAX (BZ 33245)
The SYSCALL_CANCEL calls __syscall_cancel, which in turn
calls __internal_syscall_cancel with an 'int' return instead of the
expected 'long int'.  This causes issues with syscalls that return
values larger than INT_MAX, such as copy_file_range [1].

Checked on x86_64-linux-gnu.

[1] https://debbugs.gnu.org/cgi/bugreport.cgi?bug=79139

Reviewed-by: Andreas K. Huettel <dilfridge@gentoo.org>
(cherry picked from commit 7107bebf19)
2025-08-01 17:44:39 -03:00
Florian Weimer 5e298d2d93 elf: Handle ld.so with LOAD segment gaps in _dl_find_object (bug 31943)
Detect if ld.so not contiguous and handle that case in _dl_find_object.
Set l_find_object_processed even for initially loaded link maps,
otherwise dlopen of an initially loaded object adds it to
_dlfo_loaded_mappings (where maps are expected to be contiguous),
in addition to _dlfo_nodelete_mappings.

Test elf/tst-link-map-contiguous-ldso iterates over the loader
image, reading every word to make sure memory is actually mapped.
It only does that if the l_contiguous flag is set for the link map.
Otherwise, it finds gaps with mmap and checks that _dl_find_object
does not return the ld.so mapping for them.

The test elf/tst-link-map-contiguous-main does the same thing for
the libc.so shared object.  This only works if the kernel loaded
the main program because the glibc dynamic loader may fill
the gaps with PROT_NONE mappings in some cases, making it contiguous,
but accesses to individual words may still fault.

Test elf/tst-link-map-contiguous-libc is again slightly different
because the dynamic loader always fills the gaps with PROT_NONE
mappings, so a different form of probing has to be used.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit 20681be149)
2025-08-01 19:27:35 +02:00
Florian Weimer 2fadee5301 elf: Extract rtld_setup_phdr function from dl_main
Remove historic binutils reference from comment and update
how this data is used by applications.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
(cherry picked from commit 2cac9559e0)
2025-08-01 19:27:04 +02:00
Davide Cavalca fd18059c0f stdlib: resolve a double lock init issue after fork [BZ #32994]
The __abort_fork_reset_child (introduced in
d40ac01cbb) call resets the lock after the
fork. This causes a DRD regression in valgrind
(https://bugs.kde.org/show_bug.cgi?id=503668), as it's effectively a
double initialization, despite it being actually ok in this case. As
suggested in https://sourceware.org/bugzilla/show_bug.cgi?id=32994#c2
we replace it here with a memcpy of another initialized lock instead,
which makes valgrind happy.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit d9a348d092)
2025-08-01 13:30:28 +02:00
Sam James bc13db7393 inet-fortified: fix namespace violation (bug 33227)
We need to use __sz, not sz, as we do elsewhere.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
(cherry picked from commit 87afbd7a1a)
2025-07-29 11:50:56 +01:00
Andreas K. Hüttel 3ec4dd77f6 NEWS: add new section
Signed-off-by: Andreas K. Hüttel <dilfridge@gentoo.org>
2025-07-28 23:39:48 +02:00
Andreas K. Hüttel bdea6c3719 Replace advisories directory with pointer file
Signed-off-by: Andreas K. Hüttel <dilfridge@gentoo.org>
2025-07-28 20:35:38 +02:00
174 changed files with 5393 additions and 1200 deletions
+2
View File
@@ -0,0 +1,2 @@
For the GNU C Library Security Advisories, see the git master branch:
https://sourceware.org/git/?p=glibc.git;a=tree;f=advisories;hb=HEAD
+16
View File
@@ -5,6 +5,22 @@ See the end for copying conditions.
Please send GNU C library bug reports via <https://sourceware.org/bugzilla/>
using `glibc' in the "product" field.
Version 2.42.1
The following bugs were resolved with this release:
[31943] _dl_find_object can fail if ld.so contains gaps between load segments
[32994] stdlib: resolve a double lock init issue after fork
[33234] Use TLS initial-exec model for __libc_tsd_CTYPE_* thread variables
[33245] nptl: nptl: error in internal cancellation syscall handling
[33356] nptl: creating thread stack with guardsize 0 can erroneously
conclude MADV_GUARD_INSTALL is available
[33361] nss: Group merge does not react to ERANGE during merge
[33814] glob: wordexp with WRDE_REUSE and WRDE_APPEND may return
uninitialized memory
[34507] nptl: Pretty-printer tests FAIL instead of UNSUPPORTED when
cross-testing without python3 on target
Version 2.42
Major new features:
+3 -2
View File
@@ -423,8 +423,9 @@ py-env := PYTHONPATH=$(py-const-dir):$(..)scripts:$${PYTHONPATH}
# The pretty printer files and test_common_printers.py must be present for all.
$(tests-printers-out): $(objpfx)%.out: $(objpfx)% %.py %.c $(pretty-printers) \
$(..)scripts/test_printers_common.py
$(test-wrapper-env) $(py-env) \
$(PYTHON) $*.py $*.c $(objpfx)$* $(pretty-printers) > $@; \
$(test-wrapper-env) $(py-env) sh -c \
'command -v $(firstword $(PYTHON)) > /dev/null 2>&1 || exit 77; \
exec $(PYTHON) $*.py $*.c $(objpfx)$* $(pretty-printers)' > $@; \
$(evaluate-test)
endif
-14
View File
@@ -1,14 +0,0 @@
printf: incorrect output for integers with thousands separator and width field
When the printf family of functions is called with a format specifier
that uses an <apostrophe> (enable grouping) and a minimum width
specifier, the resulting output could be larger than reasonably expected
by a caller that computed a tight bound on the buffer size. The
resulting larger than expected output could result in a buffer overflow
in the printf family of functions.
CVE-Id: CVE-2023-25139
Public-Date: 2023-02-02
Vulnerable-Commit: e88b9f0e5cc50cab57a299dc7efe1a4eb385161d (2.37)
Fix-Commit: c980549cc6a1c03c23cc2fe3e7b0fe626a0364b0 (2.38)
Fix-Commit: 07b9521fc6369d000216b96562ff7c0ed32a16c4 (2.37-4)
-15
View File
@@ -1,15 +0,0 @@
getaddrinfo: Stack read overflow in no-aaaa mode
If the system is configured in no-aaaa mode via /etc/resolv.conf,
getaddrinfo is called for the AF_UNSPEC address family, and a DNS
response is received over TCP that is larger than 2048 bytes,
getaddrinfo may potentially disclose stack contents via the returned
address data, or crash.
CVE-Id: CVE-2023-4527
Public-Date: 2023-09-12
Vulnerable-Commit: f282cdbe7f436c75864e5640a409a10485e9abb2 (2.36)
Fix-Commit: bd77dd7e73e3530203be1c52c8a29d08270cb25d (2.39)
Fix-Commit: 4ea972b7edd7e36610e8cde18bf7a8149d7bac4f (2.36-113)
Fix-Commit: b7529346025a130fee483d42178b5c118da971bb (2.37-38)
Fix-Commit: b25508dd774b617f99419bdc3cf2ace4560cd2d6 (2.38-19)
-15
View File
@@ -1,15 +0,0 @@
getaddrinfo: Potential use-after-free
When an NSS plugin only implements the _gethostbyname2_r and
_getcanonname_r callbacks, getaddrinfo could use memory that was freed
during buffer resizing, potentially causing a crash or read or write to
arbitrary memory.
CVE-Id: CVE-2023-4806
Public-Date: 2023-09-12
Fix-Commit: 973fe93a5675c42798b2161c6f29c01b0e243994 (2.39)
Fix-Commit: e09ee267c03e3150c2c9ba28625ab130705a485e (2.34-420)
Fix-Commit: e3ccb230a961b4797510e6a1f5f21fd9021853e7 (2.35-270)
Fix-Commit: a9728f798ec7f05454c95637ee6581afaa9b487d (2.36-115)
Fix-Commit: 6529a7466c935f36e9006b854d6f4e1d4876f942 (2.37-39)
Fix-Commit: 00ae4f10b504bc4564e9f22f00907093f1ab9338 (2.38-20)
-16
View File
@@ -1,16 +0,0 @@
tunables: local privilege escalation through buffer overflow
If a tunable of the form NAME=NAME=VAL is passed in the environment of a
setuid program and NAME is valid, it may result in a buffer overflow,
which could be exploited to achieve escalated privileges. This flaw was
introduced in glibc 2.34.
CVE-Id: CVE-2023-4911
Public-Date: 2023-10-03
Vulnerable-Commit: 2ed18c5b534d9e92fc006202a5af0df6b72e7aca (2.34)
Fix-Commit: 1056e5b4c3f2d90ed2b4a55f96add28da2f4c8fa (2.39)
Fix-Commit: dcc367f148bc92e7f3778a125f7a416b093964d9 (2.34-423)
Fix-Commit: c84018a05aec80f5ee6f682db0da1130b0196aef (2.35-274)
Fix-Commit: 22955ad85186ee05834e47e665056148ca07699c (2.36-118)
Fix-Commit: b4e23c75aea756b4bddc4abcf27a1c6dca8b6bd3 (2.37-45)
Fix-Commit: 750a45a783906a19591fb8ff6b7841470f1f5701 (2.38-27)
-18
View File
@@ -1,18 +0,0 @@
getaddrinfo: DoS due to memory leak
The fix for CVE-2023-4806 introduced a memory leak when an application
calls getaddrinfo for AF_INET6 with AI_CANONNAME, AI_ALL and AI_V4MAPPED
flags set.
CVE-Id: CVE-2023-5156
Public-Date: 2023-09-25
Vulnerable-Commit: e09ee267c03e3150c2c9ba28625ab130705a485e (2.34-420)
Vulnerable-Commit: e3ccb230a961b4797510e6a1f5f21fd9021853e7 (2.35-270)
Vulnerable-Commit: a9728f798ec7f05454c95637ee6581afaa9b487d (2.36-115)
Vulnerable-Commit: 6529a7466c935f36e9006b854d6f4e1d4876f942 (2.37-39)
Vulnerable-Commit: 00ae4f10b504bc4564e9f22f00907093f1ab9338 (2.38-20)
Fix-Commit: 8006457ab7e1cd556b919f477348a96fe88f2e49 (2.34-421)
Fix-Commit: 17092c0311f954e6f3c010f73ce3a78c24ac279a (2.35-272)
Fix-Commit: 856bac55f98dc840e7c27cfa82262b933385de90 (2.36-116)
Fix-Commit: 4473d1b87d04b25cdd0e0354814eeaa421328268 (2.37-42)
Fix-Commit: 5ee59ca371b99984232d7584fe2b1a758b4421d3 (2.38-24)
-15
View File
@@ -1,15 +0,0 @@
syslog: Heap buffer overflow in __vsyslog_internal
__vsyslog_internal did not handle a case where printing a SYSLOG_HEADER
containing a long program name failed to update the required buffer
size, leading to the allocation and overflow of a too-small buffer on
the heap.
CVE-Id: CVE-2023-6246
Public-Date: 2024-01-30
Vulnerable-Commit: 52a5be0df411ef3ff45c10c7c308cb92993d15b1 (2.37)
Fix-Commit: 6bd0e4efcc78f3c0115e5ea9739a1642807450da (2.39)
Fix-Commit: 23514c72b780f3da097ecf33a793b7ba9c2070d2 (2.38-42)
Fix-Commit: 97a4292aa4a2642e251472b878d0ec4c46a0e59a (2.37-57)
Vulnerable-Commit: b0e7888d1fa2dbd2d9e1645ec8c796abf78880b9 (2.36-16)
Fix-Commit: d1a83b6767f68b3cb5b4b4ea2617254acd040c82 (2.36-126)
-15
View File
@@ -1,15 +0,0 @@
syslog: Heap buffer overflow in __vsyslog_internal
__vsyslog_internal used the return value of snprintf/vsnprintf to
calculate buffer sizes for memory allocation. If these functions (for
any reason) failed and returned -1, the resulting buffer would be too
small to hold output.
CVE-Id: CVE-2023-6779
Public-Date: 2024-01-30
Vulnerable-Commit: 52a5be0df411ef3ff45c10c7c308cb92993d15b1 (2.37)
Fix-Commit: 7e5a0c286da33159d47d0122007aac016f3e02cd (2.39)
Fix-Commit: d0338312aace5bbfef85e03055e1212dd0e49578 (2.38-43)
Fix-Commit: 67062eccd9a65d7fda9976a56aeaaf6c25a80214 (2.37-58)
Vulnerable-Commit: b0e7888d1fa2dbd2d9e1645ec8c796abf78880b9 (2.36-16)
Fix-Commit: 2bc9d7c002bdac38b5c2a3f11b78e309d7765b83 (2.36-127)
-13
View File
@@ -1,13 +0,0 @@
syslog: Integer overflow in __vsyslog_internal
__vsyslog_internal calculated a buffer size by adding two integers, but
did not first check if the addition would overflow.
CVE-Id: CVE-2023-6780
Public-Date: 2024-01-30
Vulnerable-Commit: 52a5be0df411ef3ff45c10c7c308cb92993d15b1 (2.37)
Fix-Commit: ddf542da94caf97ff43cc2875c88749880b7259b (2.39)
Fix-Commit: d37c2b20a4787463d192b32041c3406c2bd91de0 (2.38-44)
Fix-Commit: 2b58cba076e912961ceaa5fa58588e4b10f791c0 (2.37-59)
Vulnerable-Commit: b0e7888d1fa2dbd2d9e1645ec8c796abf78880b9 (2.36-16)
Fix-Commit: b9b7d6a27aa0632f334352fa400771115b3c69b7 (2.36-128)
-28
View File
@@ -1,28 +0,0 @@
ISO-2022-CN-EXT: fix out-of-bound writes when writing escape sequence
The iconv() function in the GNU C Library versions 2.39 and older may
overflow the output buffer passed to it by up to 4 bytes when converting
strings to the ISO-2022-CN-EXT character set, which may be used to
crash an application or overwrite a neighbouring variable.
ISO-2022-CN-EXT uses escape sequences to indicate character set changes
(as specified by RFC 1922). While the SOdesignation has the expected
bounds checks, neither SS2designation nor SS3designation have its;
allowing a write overflow of 1, 2, or 3 bytes with fixed values:
'$+I', '$+J', '$+K', '$+L', '$+M', or '$*H'.
CVE-Id: CVE-2024-2961
Public-Date: 2024-04-17
Vulnerable-Commit: 755104edc75c53f4a0e7440334e944ad3c6b32fc (2.1.93-169)
Fix-Commit: f9dc609e06b1136bb0408be9605ce7973a767ada (2.40)
Fix-Commit: 31da30f23cddd36db29d5b6a1c7619361b271fb4 (2.39-31)
Fix-Commit: e1135387deded5d73924f6ca20c72a35dc8e1bda (2.38-66)
Fix-Commit: 89ce64b269a897a7780e4c73a7412016381c6ecf (2.37-89)
Fix-Commit: 4ed98540a7fd19f458287e783ae59c41e64df7b5 (2.36-164)
Fix-Commit: 36280d1ce5e245aabefb877fe4d3c6cff95dabfa (2.35-315)
Fix-Commit: a8b0561db4b9847ebfbfec20075697d5492a363c (2.34-459)
Fix-Commit: ed4f16ff6bed3037266f1fa682ebd32a18fce29c (2.33-263)
Fix-Commit: 682ad4c8623e611a971839990ceef00346289cc9 (2.32-140)
Fix-Commit: 3703c32a8d304c1ee12126134ce69be965f38000 (2.31-154)
Reported-By: Charles Fol
-22
View File
@@ -1,22 +0,0 @@
nscd: Stack-based buffer overflow in netgroup cache
If the Name Service Cache Daemon's (nscd) fixed size cache is exhausted
by client requests then a subsequent client request for netgroup data
may result in a stack-based buffer overflow. This flaw was introduced
in glibc 2.15 when the cache was added to nscd.
This vulnerability is only present in the nscd binary.
CVE-Id: CVE-2024-33599
Public-Date: 2024-04-23
Vulnerable-Commit: 684ae515993269277448150a1ca70db3b94aa5bd (2.15)
Fix-Commit: 69c58d5ef9f584ea198bd00f7964d364d0e6b921 (2.31-155)
Fix-Commit: a77064893bfe8a701770e2f53a4d33805bc47a5a (2.32-141)
Fix-Commit: 5c75001a96abcd50cbdb74df24c3f013188d076e (2.33-264)
Fix-Commit: 52f73e5c4e29b14e79167272297977f360ae1e97 (2.34-460)
Fix-Commit: 7a95873543ce225376faf13bb71c43dea6d24f86 (2.35-316)
Fix-Commit: caa3151ca460bdd9330adeedd68c3112d97bffe4 (2.36-165)
Fix-Commit: f75c298e747b2b8b41b1c2f551c011a52c41bfd1 (2.37-91)
Fix-Commit: 5968aebb86164034b8f8421b4abab2f837a5bdaf (2.38-72)
Fix-Commit: 1263d583d2e28afb8be53f8d6922f0842036f35d (2.39-35)
Fix-Commit: 87801a8fd06db1d654eea3e4f7626ff476a9bdaa (2.40)
-32
View File
@@ -1,32 +0,0 @@
nscd: Null pointer crash after notfound response
If the Name Service Cache Daemon's (nscd) cache fails to add a not-found
netgroup response to the cache, the client request can result in a null
pointer dereference. This flaw was introduced in glibc 2.15 when the
cache was added to nscd.
This vulnerability is only present in the nscd binary.
CVE-Id: CVE-2024-33600
Public-Date: 2024-04-24
Vulnerable-Commit: 684ae515993269277448150a1ca70db3b94aa5bd (2.15)
Fix-Commit: b048a482f088e53144d26a61c390bed0210f49f2 (2.40)
Fix-Commit: 7835b00dbce53c3c87bbbb1754a95fb5e58187aa (2.40)
Fix-Commit: c99f886de54446cd4447db6b44be93dabbdc2f8b (2.39-37)
Fix-Commit: 5a508e0b508c8ad53bd0d2fb48fd71b242626341 (2.39-36)
Fix-Commit: 2ae9446c1b7a3064743b4a51c0bbae668ee43e4c (2.38-74)
Fix-Commit: 541ea5172aa658c4bd5c6c6d6fd13903c3d5bb0a (2.38-73)
Fix-Commit: a8070b31043c7585c36ba68a74298c4f7af075c3 (2.37-93)
Fix-Commit: 5eea50c4402e39588de98aa1d4469a79774703d4 (2.37-92)
Fix-Commit: f205b3af56740e3b014915b1bd3b162afe3407ef (2.36-167)
Fix-Commit: c34f470a615b136170abd16142da5dd0c024f7d1 (2.36-166)
Fix-Commit: bafadc589fbe21ae330e8c2af74db9da44a17660 (2.35-318)
Fix-Commit: 4370bef52b0f3f3652c6aa13d7a9bb3ac079746d (2.35-317)
Fix-Commit: 1f94122289a9bf7dba573f5d60327aaa2b85cf2e (2.34-462)
Fix-Commit: 966d6ac9e40222b84bb21674cc4f83c8d72a5a26 (2.34-461)
Fix-Commit: e3eef1b8fbdd3a7917af466ca9c4b7477251ca79 (2.33-266)
Fix-Commit: f20a8d696b13c6261b52a6434899121f8b19d5a7 (2.33-265)
Fix-Commit: be602180146de37582a3da3a0caa4b719645de9c (2.32-143)
Fix-Commit: 394eae338199078b7961b051c191539870742d7b (2.32-142)
Fix-Commit: 8d7949183760170c61e55def723c1d8050187874 (2.31-157)
Fix-Commit: 304ce5fe466c4762b21b36c26926a4657b59b53e (2.31-156)
-28
View File
@@ -1,28 +0,0 @@
nscd: netgroup cache may terminate daemon on memory allocation failure
The Name Service Cache Daemon's (nscd) netgroup cache uses xmalloc or
xrealloc and these functions may terminate the process due to a memory
allocation failure resulting in a denial of service to the clients. The
flaw was introduced in glibc 2.15 when the cache was added to nscd.
This vulnerability is only present in the nscd binary.
Subsequent refactoring of the netgroup cache only added more uses of
xmalloc and xrealloc. Uses of xmalloc and xrealloc in other parts of
nscd only occur during startup of the daemon and so are not affected by
client requests that could trigger an out of memory followed by
termination.
CVE-Id: CVE-2024-33601
Public-Date: 2024-04-24
Vulnerable-Commit: 684ae515993269277448150a1ca70db3b94aa5bd (2.15)
Fix-Commit: c04a21e050d64a1193a6daab872bca2528bda44b (2.40)
Fix-Commit: a9a8d3eebb145779a18d90e3966009a1daa63cd8 (2.39-38)
Fix-Commit: 71af8ca864345d39b746d5cee84b94b430fad5db (2.38-75)
Fix-Commit: 6e106dc214d6a033a4e945d1c6cf58061f1c5f1f (2.37-94)
Fix-Commit: b6742463694b1dfdd5120b91ee21cf05d15ec2e2 (2.36-168)
Fix-Commit: 7a5864cac60e06000394128a5a2817b03542f5a3 (2.35-319)
Fix-Commit: 86f1d5f4129c373ac6fb6df5bcf38273838843cb (2.34-463)
Fix-Commit: 4d27d4b9a188786fc6a56745506cec2acfc51f83 (2.33-267)
Fix-Commit: 3ed195a8ec89da281e3c4bf887a13d281b72d8f4 (2.32-144)
Fix-Commit: bbf5a58ccb55679217f94de706164d15372fbbc0 (2.31-158)
-26
View File
@@ -1,26 +0,0 @@
nscd: netgroup cache assumes NSS callback uses in-buffer strings
The Name Service Cache Daemon's (nscd) netgroup cache can corrupt memory
when the NSS callback does not store all strings in the provided buffer.
The flaw was introduced in glibc 2.15 when the cache was added to nscd.
This vulnerability is only present in the nscd binary.
There is no guarantee from the NSS callback API that the returned
strings are all within the buffer. However, the netgroup cache code
assumes that the NSS callback uses in-buffer strings and if it doesn't
the buffer resizing logic could lead to potential memory corruption.
CVE-Id: CVE-2024-33602
Public-Date: 2024-04-24
Vulnerable-Commit: 684ae515993269277448150a1ca70db3b94aa5bd (2.15)
Fix-Commit: c04a21e050d64a1193a6daab872bca2528bda44b (2.40)
Fix-Commit: a9a8d3eebb145779a18d90e3966009a1daa63cd8 (2.39-38)
Fix-Commit: 71af8ca864345d39b746d5cee84b94b430fad5db (2.38-75)
Fix-Commit: 6e106dc214d6a033a4e945d1c6cf58061f1c5f1f (2.37-94)
Fix-Commit: b6742463694b1dfdd5120b91ee21cf05d15ec2e2 (2.36-168)
Fix-Commit: 7a5864cac60e06000394128a5a2817b03542f5a3 (2.35-319)
Fix-Commit: 86f1d5f4129c373ac6fb6df5bcf38273838843cb (2.34-463)
Fix-Commit: 4d27d4b9a188786fc6a56745506cec2acfc51f83 (2.33-267)
Fix-Commit: 3ed195a8ec89da281e3c4bf887a13d281b72d8f4 (2.32-144)
Fix-Commit: bbf5a58ccb55679217f94de706164d15372fbbc0 (2.31-158)
-40
View File
@@ -1,40 +0,0 @@
assert: Buffer overflow when printing assertion failure message
When the assert() function fails, it does not allocate enough space for the
assertion failure message string and size information, which may lead to a
buffer overflow if the message string size aligns to page size.
This bug can be triggered when an assertion in a program fails. The assertion
failure message is allocated to allow developers to see this failure in core
dumps and it typically includes, in addition to the invariant assertion
string and function name, the name of the program. If the name of the failing
program is user controlled, for example on a local system, this could allow an
attacker to control the assertion failure to trigger this buffer overflow.
The only viable vector for exploitation of this bug is local, if a setuid
program exists that has an existing bug that results in an assertion failure.
No such program has been discovered at the time of publishing this advisory,
but the presence of custom setuid programs, although strongly discouraged as a
security practice, cannot be discounted.
CVE-Id: CVE-2025-0395
Public-Date: 2025-01-22
Vulnerable-Commit: f8a3b5bf8fa1d0c43d2458e03cc109a04fdef194 (2.13-175)
Fix-Commit: 68ee0f704cb81e9ad0a78c644a83e1e9cd2ee578 (2.41)
Fix-Commit: cdb9ba84191ce72e86346fb8b1d906e7cd930ea2 (2.42)
Fix-Commit: 69fda28279b497bd405fdd442a6d8e4d3d5f681b (2.41-7)
Fix-Commit: 7d4b6bcae91f29d7b4daf15bab06b66cf1d2217c (2.40-66)
Fix-Commit: d6c156c326999f144cb5b73d29982108d549ad8a (2.40-71)
Fix-Commit: 808a84a8b81468b517a4d721fdc62069cb8c211f (2.39-146)
Fix-Commit: f6d48470aef9264d2d56f4c4533eb76db7f9c2e4 (2.39-150)
Fix-Commit: c32fd59314c343db88c3ea4a203870481d33c3d2 (2.38-122)
Fix-Commit: f984e2d7e8299726891a1a497a3c36cd5542a0bf (2.38-124)
Fix-Commit: a3d7865b098a3a67c44f7812208d9ce4718873ba (2.37-143)
Fix-Commit: b989519fe1683c204ac24ec92830e3fe3bfaccad (2.37-146)
Fix-Commit: 7971add7ee4171fdd8dfd17e7c04c4ed77a18845 (2.36-216)
Fix-Commit: 0487893d5c5bc6710d83d7c3152d888a0339559e (2.36-219)
Fix-Commit: 8b5d4be762419c4f6176261c6fea40ac559b88dc (2.35-370)
Fix-Commit: 8b3d09dc0d350191985f9d291cc30ce96f034b49 (2.35-373)
Fix-Commit: df4e1f4a5096b385c9bcc94424cf2eaa227b3761 (2.34-500)
Fix-Commit: 31eb872cb21449832ab47ad5db83281d240e1d03 (2.34-503)
Reported-By: Qualys Security Advisory
-23
View File
@@ -1,23 +0,0 @@
elf: static setuid binary dlopen may incorrectly search LD_LIBRARY_PATH
A statically linked setuid binary that calls dlopen (including internal
dlopen calls after setlocale or calls to NSS functions such as getaddrinfo)
may incorrectly search LD_LIBRARY_PATH to determine which library to load,
leading to the execution of library code that is attacker controlled.
The only viable vector for exploitation of this bug is local, if a static
setuid program exists, and that program calls dlopen, then it may search
LD_LIBRARY_PATH to locate the SONAME to load. No such program has been
discovered at the time of publishing this advisory, but the presence of
custom setuid programs, although strongly discouraged as a security
practice, cannot be discounted.
CVE-Id: CVE-2025-4802
Public-Date: 2025-05-16
Vulnerable-Commit: 10e93d968716ab82931d593bada121c17c0a4b93 (2.27)
Fix-Commit: 5451fa962cd0a90a0e2ec1d8910a559ace02bba0 (2.39)
Fix-Commit: 3be3728df2f1912c80abd3288bc6e3a25ad679e4 (2.38-132)
Fix-Commit: 7403ede2d7752e59e0c47d5d33d73c2bf850e7be (2.37-154)
Fix-Commit: 2ef7850279b2931caf6d6d6743ebaa91839e1cf7 (2.36-227)
Fix-Commit: 621c65ccf12ddd415ceeb2234423bd1acd0fabb3 (2.35-387)
Fix-Commit: 35018c0fd20eac9ceaf60060fed2745b3177359d (2.34-517)
-30
View File
@@ -1,30 +0,0 @@
power10: strcmp fails to save and restore nonvolatile vector registers
The Power 10 implementation of strcmp in
sysdeps/powerpc/powerpc64/le/power10/strcmp.S failed to save/restore
nonvolatile vector registers in the 32-byte aligned loop path. This
results in callers reading content from those registers in a different
context, potentially altering program logic.
There could be a program context where a user controlled string could
leak through strcmp into program code, thus altering its logic. There
is also a potential for sensitive strings passed into strcmp leaking
through the clobbered registers into parts of the calling program that
should otherwise not have had access to those strings.
The impact of this flaw is limited to applications running on Power 10
hardware that use the nonvolatile vector registers, i.e. v20 to v31
assuming that they have been treated in accordance with the OpenPower
psABI. It is possible to work around the issue for those specific
applications by setting the glibc.cpu.hwcaps tunable to "-arch_3_1" like
so:
export GLIBC_TUNABLES=glibc.cpu.hwcaps=-arch_3_1
CVE-Id: CVE-2025-5702
Public-Date: 2025-06-04
Vulnerable-Commit: 3367d8e180848030d1646f088759f02b8dfe0d6f (2.39)
Fix-Commit: 15808c77b35319e67ee0dc8f984a9a1a434701bc (2.42)
Fix-Commit: 0c76c951620f9e12df2a89b2c684878b55bb6795 (2.41-60)
Fix-Commit: 7e12550b8e3a11764a4a9090ce6bd3fc23fc8a8e (2.40-139)
Fix-Commit: 06a70769fd0b2e1f2a3085ad50ab620282bd77b3 (2.39-209)
-29
View File
@@ -1,29 +0,0 @@
power10: strncmp fails to save and restore nonvolatile vector registers
The Power 10 implementation of strncmp in
sysdeps/powerpc/powerpc64/le/power10/strncmp.S failed to save/restore
nonvolatile vector registers in the 32-byte aligned loop path. This
results in callers reading content from those registers in a different
context, potentially altering program logic.
There could be a program context where a user controlled string could
leak through strncmp into program code, thus altering its logic. There
is also a potential for sensitive strings passed into strncmp leaking
through the clobbered registers into parts of the calling program that
should otherwise not have had access to those strings.
The impact of this flaw is limited to applications running on Power 10
hardware that use the nonvolatile vector registers, i.e. v20 to v31
assuming that they have been treated in accordance with the OpenPower
psABI. It is possible to work around the issue for those specific
applications by setting the glibc.cpu.hwcaps tunable to "-arch_3_1" like
so:
export GLIBC_TUNABLES=glibc.cpu.hwcaps=-arch_3_1
CVE-Id: CVE-2025-5745
Public-Date: 2025-06-05
Vulnerable-Commit: 23f0d81608d0ca6379894ef81670cf30af7fd081 (2.40)
Fix-Commit: 63c60101ce7c5eac42be90f698ba02099b41b965 (2.42)
Fix-Commit: 84bdbf8a6f2fdafd3661489dbb7f79835a52da82 (2.41-57)
Fix-Commit: 42a5a940c974d02540c8da26d6374c744d148cb9 (2.40-136)
-14
View File
@@ -1,14 +0,0 @@
posix: Fix double-free after allocation failure in regcomp
The regcomp function in the GNU C library version from 2.4 to 2.41 is
subject to a double free if some previous allocation fails. It can be
accomplished either by a malloc failure or by using an interposed
malloc that injects random malloc failures. The double free can allow
buffer manipulation depending of how the regex is constructed.
This issue affects all architectures and ABIs supported by the GNU C
library.
CVE-Id: CVE-2025-8058
Public-Date: 2025-07-22
Vulnerable-Commit: 963d8d782fc98fb6dc3a66f0068795f9920c269d (2.3.3-1596)
Fix-Commit: 7ea06e994093fa0bcca0d0ee2c1db271d8d7885d (2.42)
-77
View File
@@ -1,77 +0,0 @@
GNU C Library Security Advisory Format
======================================
Security advisories in this directory follow a simple git commit log
format, with a heading and free-format description augmented with tags
to allow parsing key information. References to code changes are
specific to the glibc repository and follow a specific format:
Tag-name: <commit-ref> (release-version)
The <commit-ref> indicates a specific commit in the repository. The
release-version indicates the publicly consumable release in which this
commit is known to exist. The release-version is derived from the
git-describe format, (i.e. stripped out from glibc-2.34.NNN-gxxxx) and
is of the form 2.34-NNN. If the -NNN suffix is absent, it means that
the change is in that release tarball, otherwise the change is on the
release/2.YY/master branch and not in any released tarball.
The following tags are currently being used:
CVE-Id:
This is the CVE-Id assigned under the CVE Program
(https://www.cve.org/).
Public-Date:
The date this issue became publicly known.
Vulnerable-Commit:
The commit that introduced this vulnerability. There could be multiple
entries, one for each release branch in the glibc repository; the
release-version portion of this tag should tell you which branch this is
on.
Fix-Commit:
The commit that fixed this vulnerability. There could be multiple
entries for each release branch in the glibc repository, indicating that
all of those commits contributed to fixing that issue in each of those
branches.
Reported-By:
The entity that reported this issue. There could be multiple entries, one for
each reporter.
Adding an Advisory
------------------
An advisory for a CVE needs to be added on the master branch in two steps:
1. Add the text of the advisory without any Fix-Commit tags along with
the fix for the CVE. Add the Vulnerable-Commit tag, if applicable.
The advisories directory does not exist in release branches, so keep
the advisory text commit distinct from the code changes, to ease
backports. Ask for the GLIBC-SA advisory number from the security
team.
2. Finish all backports on release branches and then back on the msater
branch, add all commit refs to the advisory using the Fix-Commit
tags. Don't bother adding the release-version subscript since the
next step will overwrite it.
3. Run the process-advisories.sh script in the scripts directory on the
advisory:
scripts/process-advisories.sh update GLIBC-SA-YYYY-NNNN
(replace YYYY-NNNN with the actual advisory number).
4. Verify the updated advisory and push the result.
Getting a NEWS snippet from advisories
--------------------------------------
Run:
scripts/process-advisories.sh news
and copy the content into the NEWS file.
+3 -3
View File
@@ -24,11 +24,11 @@
__ctype_init before user code runs, but this does not happen for
threads in secondary namespaces. With the initializers, secondary
namespaces at least get locale data from the C locale. */
__thread const uint16_t * __libc_tsd_CTYPE_B
__thread const uint16_t * __libc_tsd_CTYPE_B attribute_tls_model_ie
= (const uint16_t *) _nl_C_LC_CTYPE_class + 128;
__thread const int32_t * __libc_tsd_CTYPE_TOLOWER
__thread const int32_t * __libc_tsd_CTYPE_TOLOWER attribute_tls_model_ie
= (const int32_t *) _nl_C_LC_CTYPE_tolower + 128;
__thread const int32_t * __libc_tsd_CTYPE_TOUPPER
__thread const int32_t * __libc_tsd_CTYPE_TOUPPER attribute_tls_model_ie
= (const int32_t *) _nl_C_LC_CTYPE_toupper + 128;
+6
View File
@@ -543,6 +543,8 @@ tests-internal += \
tst-dl_find_object-threads \
tst-dlmopen2 \
tst-hash-collision3 \
tst-link-map-contiguous-ldso \
tst-link-map-contiguous-libc \
tst-ptrguard1 \
tst-stackguard1 \
tst-tls-surplus \
@@ -554,6 +556,10 @@ tests-internal += \
unload2 \
# tests-internal
ifeq ($(build-hardcoded-path-in-tests),yes)
tests-internal += tst-link-map-contiguous-main
endif
tests-container += \
tst-dlopen-self-container \
tst-dlopen-tlsmodid-container \
+45 -29
View File
@@ -465,6 +465,37 @@ _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
maps. NODELETE is the number of used _dlfo_nodelete_mappings
elements. It is incremented as needed, and the new NODELETE value
is returned. */
static size_t
_dlfo_process_initial_noncontiguous_map (struct link_map *map,
size_t nodelete)
{
struct dl_find_object_internal dlfo;
_dl_find_object_from_map (map, &dlfo);
/* PT_LOAD segments for a non-contiguous link map are added to the
non-closeable mappings. */
const ElfW(Phdr) *ph = map->l_phdr;
const ElfW(Phdr) *ph_end = map->l_phdr + map->l_phnum;
for (; ph < ph_end; ++ph)
if (ph->p_type == PT_LOAD)
{
if (_dlfo_nodelete_mappings != NULL)
{
/* Second pass only. */
_dlfo_nodelete_mappings[nodelete] = dlfo;
ElfW(Addr) start = ph->p_vaddr + map->l_addr;
_dlfo_nodelete_mappings[nodelete].map_start = start;
_dlfo_nodelete_mappings[nodelete].map_end = start + ph->p_memsz;
}
++nodelete;
}
return nodelete;
}
/* _dlfo_process_initial is called twice. First to compute the array
sizes from the initial loaded mappings. Second to fill in the
bases and infos arrays with the (still unsorted) data. Returns the
@@ -476,29 +507,8 @@ _dlfo_process_initial (void)
size_t nodelete = 0;
if (!main_map->l_contiguous)
{
struct dl_find_object_internal dlfo;
_dl_find_object_from_map (main_map, &dlfo);
/* PT_LOAD segments for a non-contiguous are added to the
non-closeable mappings. */
for (const ElfW(Phdr) *ph = main_map->l_phdr,
*ph_end = main_map->l_phdr + main_map->l_phnum;
ph < ph_end; ++ph)
if (ph->p_type == PT_LOAD)
{
if (_dlfo_nodelete_mappings != NULL)
{
/* Second pass only. */
_dlfo_nodelete_mappings[nodelete] = dlfo;
_dlfo_nodelete_mappings[nodelete].map_start
= ph->p_vaddr + main_map->l_addr;
_dlfo_nodelete_mappings[nodelete].map_end
= _dlfo_nodelete_mappings[nodelete].map_start + ph->p_memsz;
}
++nodelete;
}
}
/* Contiguous case already handled in _dl_find_object_init. */
nodelete = _dlfo_process_initial_noncontiguous_map (main_map, nodelete);
size_t loaded = 0;
for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
@@ -510,11 +520,18 @@ _dlfo_process_initial (void)
/* lt_library link maps are implicitly NODELETE. */
if (l->l_type == lt_library || l->l_nodelete_active)
{
if (_dlfo_nodelete_mappings != NULL)
/* Second pass only. */
_dl_find_object_from_map
(l, _dlfo_nodelete_mappings + nodelete);
++nodelete;
/* The kernel may have loaded ld.so with gaps. */
if (!l->l_contiguous && is_rtld_link_map (l))
nodelete
= _dlfo_process_initial_noncontiguous_map (l, nodelete);
else
{
if (_dlfo_nodelete_mappings != NULL)
/* Second pass only. */
_dl_find_object_from_map
(l, _dlfo_nodelete_mappings + nodelete);
++nodelete;
}
}
else if (l->l_type == lt_loaded)
{
@@ -764,7 +781,6 @@ _dl_find_object_update_1 (struct link_map **loaded, size_t count)
/* Prefer newly loaded link map. */
assert (loaded_index1 > 0);
_dl_find_object_from_map (loaded[loaded_index1 - 1], dlfo);
loaded[loaded_index1 - 1]->l_find_object_processed = 1;
--loaded_index1;
}
+5 -2
View File
@@ -94,7 +94,7 @@ _dl_find_object_to_external (struct dl_find_object_internal *internal,
}
/* Extract the object location data from a link map and writes it to
*RESULT using relaxed MO stores. */
*RESULT using relaxed MO stores. Set L->l_find_object_processed. */
static void __attribute__ ((unused))
_dl_find_object_from_map (struct link_map *l,
struct dl_find_object_internal *result)
@@ -141,8 +141,11 @@ _dl_find_object_from_map (struct link_map *l,
break;
}
if (read_seg == 3)
return;
goto done;
}
done:
l->l_find_object_processed = 1;
}
/* Called by the dynamic linker to set up the data structures for the
+55 -27
View File
@@ -1239,6 +1239,60 @@ rtld_setup_main_map (struct link_map *main_map)
return has_interp;
}
/* Set up the program header information for the dynamic linker
itself. It can be accessed via _r_debug and dl_iterate_phdr
callbacks, and it is used by _dl_find_object. */
static void
rtld_setup_phdr (void)
{
/* Starting from binutils-2.23, the linker will define the magic
symbol __ehdr_start to point to our own ELF header if it is
visible in a segment that also includes the phdrs. */
const ElfW(Ehdr) *rtld_ehdr = &__ehdr_start;
assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));
const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;
_dl_rtld_map.l_phdr = rtld_phdr;
_dl_rtld_map.l_phnum = rtld_ehdr->e_phnum;
_dl_rtld_map.l_contiguous = 1;
/* The linker may not have produced a contiguous object. The kernel
will load the object with actual gaps (unlike the glibc loader
for shared objects, which always produces a contiguous mapping).
See similar logic in rtld_setup_main_map above. */
{
ElfW(Addr) expected_load_address = 0;
for (const ElfW(Phdr) *ph = rtld_phdr; ph < &rtld_phdr[rtld_ehdr->e_phnum];
++ph)
if (ph->p_type == PT_LOAD)
{
ElfW(Addr) mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1);
if (_dl_rtld_map.l_contiguous && expected_load_address != 0
&& expected_load_address != mapstart)
_dl_rtld_map.l_contiguous = 0;
ElfW(Addr) allocend = ph->p_vaddr + ph->p_memsz;
/* The next expected address is the page following this load
segment. */
expected_load_address = ((allocend + GLRO(dl_pagesize) - 1)
& ~(GLRO(dl_pagesize) - 1));
}
}
/* PT_GNU_RELRO is usually the last phdr. */
size_t cnt = rtld_ehdr->e_phnum;
while (cnt-- > 0)
if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
{
_dl_rtld_map.l_relro_addr = rtld_phdr[cnt].p_vaddr;
_dl_rtld_map.l_relro_size = rtld_phdr[cnt].p_memsz;
break;
}
}
/* Adjusts the contents of the stack and related globals for the user
entry point. The ld.so processed skip_args arguments and bumped
_dl_argv and _dl_argc accordingly. Those arguments are removed from
@@ -1705,33 +1759,7 @@ dl_main (const ElfW(Phdr) *phdr,
++GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
++GL(dl_load_adds);
/* Starting from binutils-2.23, the linker will define the magic symbol
__ehdr_start to point to our own ELF header if it is visible in a
segment that also includes the phdrs. If that's not available, we use
the old method that assumes the beginning of the file is part of the
lowest-addressed PT_LOAD segment. */
/* Set up the program header information for the dynamic linker
itself. It is needed in the dl_iterate_phdr callbacks. */
const ElfW(Ehdr) *rtld_ehdr = &__ehdr_start;
assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));
const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;
_dl_rtld_map.l_phdr = rtld_phdr;
_dl_rtld_map.l_phnum = rtld_ehdr->e_phnum;
/* PT_GNU_RELRO is usually the last phdr. */
size_t cnt = rtld_ehdr->e_phnum;
while (cnt-- > 0)
if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
{
_dl_rtld_map.l_relro_addr = rtld_phdr[cnt].p_vaddr;
_dl_rtld_map.l_relro_size = rtld_phdr[cnt].p_memsz;
break;
}
rtld_setup_phdr ();
/* Add the dynamic linker to the TLS list if it also uses TLS. */
if (_dl_rtld_map.l_tls_blocksize != 0)
+40 -1
View File
@@ -38,6 +38,7 @@
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <intprops.h>
/* Get libc version number. */
#include "../version.h"
@@ -410,6 +411,7 @@ load_shobj (const char *name)
int fd;
ElfW(Shdr) *shdr;
size_t pagesize = getpagesize ();
struct stat st;
/* Since we use dlopen() we must be prepared to work around the sometimes
strange lookup rules for the shared objects. If we have a file foo.so
@@ -550,14 +552,39 @@ load_shobj (const char *name)
error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"),
map->l_name);
if (fstat (fd, &st) < 0)
error (EXIT_FAILURE, errno, _("stat(%s) failure"), map->l_name);
/* We're depending on data that's being read from the file, so be a
bit paranoid here and make sure the requests are reasonable -
i.e. both size and offset are nonnegative and smaller than the
file size, as well as the offset of the end of the data. PREAD
would have failed anyway, but this is more robust and explains
what happened better. Note that SZ must be unsigned and OFF may
be signed or unsigned. */
#define PCHECK(sz1,off1) { \
size_t sz = sz1, end_off; \
off_t off = off1; \
if (sz > st.st_size \
|| off < 0 || off > st.st_size \
|| INT_ADD_WRAPV (sz, off, &end_off) \
|| end_off > st.st_size) \
error (EXIT_FAILURE, ERANGE, \
_("read outside of file extents %zu + %jd > %jd"), \
sz, (intmax_t) off, (intmax_t) st.st_size); \
}
/* Map the section header. */
size_t size = ehdr->e_shnum * sizeof (ElfW(Shdr));
shdr = (ElfW(Shdr) *) alloca (size);
PCHECK (size, ehdr->e_shoff);
if (pread (fd, shdr, size, ehdr->e_shoff) != size)
error (EXIT_FAILURE, errno, _("reading of section headers failed"));
/* Get the section header string table. */
char *shstrtab = (char *) alloca (shdr[ehdr->e_shstrndx].sh_size);
PCHECK (shdr[ehdr->e_shstrndx].sh_size,
shdr[ehdr->e_shstrndx].sh_offset);
if (pread (fd, shstrtab, shdr[ehdr->e_shstrndx].sh_size,
shdr[ehdr->e_shstrndx].sh_offset)
!= shdr[ehdr->e_shstrndx].sh_size)
@@ -585,6 +612,7 @@ load_shobj (const char *name)
size_t size = debuglink_entry->sh_size;
char *debuginfo_fname = (char *) alloca (size + 1);
debuginfo_fname[size] = '\0';
PCHECK (size, debuglink_entry->sh_offset);
if (pread (fd, debuginfo_fname, size, debuglink_entry->sh_offset)
!= size)
{
@@ -638,21 +666,32 @@ load_shobj (const char *name)
if (fd2 != -1)
{
ElfW(Ehdr) ehdr2;
struct stat st;
if (fstat (fd2, &st) < 0)
error (EXIT_FAILURE, errno, _("stat(%s) failure"), workbuf);
/* Read the ELF header. */
PCHECK (sizeof (ehdr2), 0);
if (pread (fd2, &ehdr2, sizeof (ehdr2), 0) != sizeof (ehdr2))
error (EXIT_FAILURE, errno,
_("reading of ELF header failed"));
/* Map the section header. */
size_t size = ehdr2.e_shnum * sizeof (ElfW(Shdr));
size_t size;
if (INT_MULTIPLY_WRAPV (ehdr2.e_shnum, sizeof (ElfW(Shdr)), &size))
error (EXIT_FAILURE, errno, _("too many section headers"));
ElfW(Shdr) *shdr2 = (ElfW(Shdr) *) alloca (size);
PCHECK (size, ehdr2.e_shoff);
if (pread (fd2, shdr2, size, ehdr2.e_shoff) != size)
error (EXIT_FAILURE, errno,
_("reading of section headers failed"));
/* Get the section header string table. */
shstrtab = (char *) alloca (shdr2[ehdr2.e_shstrndx].sh_size);
PCHECK (shdr2[ehdr2.e_shstrndx].sh_size,
shdr2[ehdr2.e_shstrndx].sh_offset);
if (pread (fd2, shstrtab, shdr2[ehdr2.e_shstrndx].sh_size,
shdr2[ehdr2.e_shstrndx].sh_offset)
!= shdr2[ehdr2.e_shstrndx].sh_size)
+22 -4
View File
@@ -40,6 +40,8 @@ static char SETGID_CHILD[] = "setgid-child";
# define PROFILE_LIB "tst-sonamemove-runmod2.so"
#endif
#define LD_DEBUG_OUTPUT "/tmp/some-file"
struct envvar_t
{
const char *env;
@@ -61,7 +63,7 @@ static const struct envvar_t filtered_envvars[] =
{ "MALLOC_TRIM_THRESHOLD_", FILTERED_VALUE },
{ "RES_OPTIONS", FILTERED_VALUE },
{ "LD_DEBUG", "all" },
{ "LD_DEBUG_OUTPUT", "/tmp/some-file" },
{ "LD_DEBUG_OUTPUT", LD_DEBUG_OUTPUT },
{ "LD_WARN", FILTERED_VALUE },
{ "LD_VERBOSE", FILTERED_VALUE },
{ "LD_BIND_NOW", "0" },
@@ -74,6 +76,14 @@ static const struct envvar_t unfiltered_envvars[] =
{ "LD_ASSUME_KERNEL", UNFILTERED_VALUE },
};
static void
unlink_ld_debug_output (pid_t pid)
{
char *output = xasprintf ("%s.%d", LD_DEBUG_OUTPUT, pid);
unlink (output);
free (output);
}
static int
test_child (void)
{
@@ -138,13 +148,21 @@ do_test (int argc, char **argv)
/* Setgid child process. */
if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0)
{
pid_t ppid = getppid ();
if (getgid () == getegid ())
/* This can happen if the file system is mounted nosuid. */
FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
(intmax_t) getgid ());
{
/* This can happen if the file system is mounted nosuid. */
unlink_ld_debug_output (ppid);
FAIL_UNSUPPORTED ("SGID failed: GID and EGID match (%jd)\n",
(intmax_t) getgid ());
}
int ret = test_child ();
unlink_ld_debug_output (ppid);
if (ret != 0)
exit (1);
return 0;
+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);
+158
View File
@@ -0,0 +1,158 @@
/* Check that _dl_find_object behavior matches up with gaps.
Copyright (C) 2025 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 <dlfcn.h>
#include <gnu/lib-names.h>
#include <inttypes.h>
#include <link.h>
#include <stdbool.h>
#include <stdio.h>
#include <support/check.h>
#include <support/support.h>
#include <support/xdlfcn.h>
#include <support/xunistd.h>
#include <support/xstdio.h>
#include <sys/mman.h>
#include <unistd.h>
/* Slow path in case we cannot find a gap with mmap (when the runtime has
mapped all the pages in the gap for some reason). */
static bool
find_gap_with_proc_self_map (const struct link_map *l)
{
int pagesize = getpagesize ();
support_need_proc ("Reads /proc/self/maps to find gap in ld.so mapping");
/* Parse /proc/self/maps and find all the mappings in the ld.so range
but not from ld.so. */
FILE *f = xfopen ("/proc/self/maps", "r");
char *line = NULL, *path_ldso = NULL;
size_t len;
bool found = false;
while (xgetline (&line, &len, f))
{
uintptr_t from, to;
char *path = NULL;
int r = sscanf (line, "%" SCNxPTR "-%" SCNxPTR "%*s%*s%*s%*s%ms",
&from, &to, &path);
TEST_VERIFY (r == 2 || r == 3);
TEST_COMPARE (from % pagesize, 0);
TEST_COMPARE (to % pagesize, 0);
if (path_ldso == NULL && l->l_map_start == from)
{
TEST_COMPARE (r, 3);
path_ldso = path;
continue;
}
if (from > l->l_map_start && to < l->l_map_end
&& (r == 2 || (path_ldso != NULL && strcmp (path, path_ldso))))
{
if (r == 2)
printf ("info: anonymous mapping found at 0x%" PRIxPTR " - 0x%"
PRIxPTR "\n", from, to);
else
printf ("info: object \"%s\" found at 0x%" PRIxPTR " - 0x%"
PRIxPTR "\n", path, from, to);
found = true;
}
free (path);
}
free (path_ldso);
free (line);
xfclose (f);
return found;
}
static int
do_test (void)
{
struct link_map *l = xdlopen (LD_SO, RTLD_NOW);
if (!l->l_contiguous)
{
puts ("info: ld.so link map is not contiguous");
/* Try to find holes by probing with mmap. */
int pagesize = getpagesize ();
bool gap_found = false;
ElfW(Addr) addr = l->l_map_start;
TEST_COMPARE (addr % pagesize, 0);
while (addr < l->l_map_end)
{
void *expected = (void *) addr;
void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1);
struct dl_find_object dlfo;
int dlfo_ret = _dl_find_object (expected, &dlfo);
if (ptr == expected)
{
if (dlfo_ret < 0)
{
TEST_COMPARE (dlfo_ret, -1);
printf ("info: hole without mapping data found at %p\n", ptr);
}
else
FAIL ("object \"%s\" found in gap at %p",
dlfo.dlfo_link_map->l_name, ptr);
gap_found = true;
}
else if (dlfo_ret == 0)
{
if ((void *) dlfo.dlfo_link_map != (void *) l)
{
printf ("info: object \"%s\" found at %p\n",
dlfo.dlfo_link_map->l_name, expected);
gap_found = true;
}
}
else
TEST_COMPARE (dlfo_ret, -1);
xmunmap (ptr, 1);
addr += pagesize;
}
if (!gap_found && !find_gap_with_proc_self_map (l))
FAIL ("no ld.so gap found");
}
else
{
puts ("info: ld.so link map is contiguous");
/* Assert that ld.so is truly contiguous in memory. */
volatile long int *p = (volatile long int *) l->l_map_start;
volatile long int *end = (volatile long int *) l->l_map_end;
while (p < end)
{
*p;
++p;
}
}
xdlclose (l);
return 0;
}
#include <support/test-driver.c>
+57
View File
@@ -0,0 +1,57 @@
/* Check that the entire libc.so program image is readable if contiguous.
Copyright (C) 2025 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 <gnu/lib-names.h>
#include <link.h>
#include <support/check.h>
#include <support/xdlfcn.h>
#include <support/xunistd.h>
#include <sys/mman.h>
#include <unistd.h>
static int
do_test (void)
{
struct link_map *l = xdlopen (LIBC_SO, RTLD_NOW);
/* The dynamic loader fills holes with PROT_NONE mappings. */
if (!l->l_contiguous)
FAIL_EXIT1 ("libc.so link map is not contiguous");
/* Direct probing does not work because not everything is readable
due to PROT_NONE mappings. */
int pagesize = getpagesize ();
ElfW(Addr) addr = l->l_map_start;
TEST_COMPARE (addr % pagesize, 0);
while (addr < l->l_map_end)
{
void *expected = (void *) addr;
void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1);
if (ptr == expected)
FAIL ("hole in libc.so memory image after %lu bytes",
(unsigned long int) (addr - l->l_map_start));
xmunmap (ptr, 1);
addr += pagesize;
}
xdlclose (l);
return 0;
}
#include <support/test-driver.c>
+45
View File
@@ -0,0 +1,45 @@
/* Check that the entire main program image is readable if contiguous.
Copyright (C) 2025 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 <link.h>
#include <support/check.h>
#include <support/xdlfcn.h>
static int
do_test (void)
{
struct link_map *l = xdlopen ("", RTLD_NOW);
if (!l->l_contiguous)
FAIL_UNSUPPORTED ("main link map is not contiguous");
/* This check only works if the kernel loaded the main program. The
dynamic loader replaces gaps with PROT_NONE mappings, resulting
in faults. */
volatile long int *p = (volatile long int *) l->l_map_start;
volatile long int *end = (volatile long int *) l->l_map_end;
while (p < end)
{
*p;
++p;
}
xdlclose (l);
return 0;
}
#include <support/test-driver.c>
+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
@@ -138,7 +138,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)' > $@; \
$(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
@@ -27,10 +27,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 $LIBPATH --inhibit-rpath ${from}.so
$codir/iconv/iconv_prog
'
ICONV="$test_wrapper_env $run_program_env $ICONV"
TIMEOUTFACTOR=${TIMEOUTFACTOR:-1}
@@ -218,6 +218,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
@@ -278,12 +279,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
+6 -1
View File
@@ -76,7 +76,8 @@ tests = bug-iconv1 bug-iconv2 tst-loading tst-e2big tst-iconv4 bug-iconv4 \
tst-iconv6 bug-iconv5 bug-iconv6 tst-iconv7 bug-iconv8 bug-iconv9 \
bug-iconv10 bug-iconv11 bug-iconv12 tst-iconv-big5-hkscs-to-2ucs4 \
bug-iconv13 bug-iconv14 bug-iconv15 \
tst-iconv-iso-2022-cn-ext
tst-iconv-iso-2022-cn-ext tst-bug33980 \
tst-jisx0213-progress
ifeq ($(have-thread-library),yes)
tests += bug-iconv3
endif
@@ -333,6 +334,10 @@ $(objpfx)bug-iconv15.out: $(addprefix $(objpfx), $(gconv-modules)) \
$(addprefix $(objpfx),$(modules.so))
$(objpfx)tst-iconv-iso-2022-cn-ext.out: $(addprefix $(objpfx), $(gconv-modules)) \
$(addprefix $(objpfx),$(modules.so))
$(objpfx)tst-bug33980.out: $(addprefix $(objpfx), $(gconv-modules)) \
$(addprefix $(objpfx),$(modules.so))
$(objpfx)tst-jisx0213-progress.out: \
$(addprefix $(objpfx), $(gconv-modules)) $(addprefix $(objpfx),$(modules.so))
$(objpfx)iconv-test.out: run-iconv-test.sh \
$(addprefix $(objpfx), $(gconv-modules)) \
+1 -1
View File
@@ -407,7 +407,7 @@ static const char from_ucs4[][2] =
is also available. */ \
uint32_t ch2; \
\
if (inptr + 1 >= inend) \
if (inend - inptr <= 1) \
{ \
/* The second character is not available. */ \
result = __GCONV_INCOMPLETE_INPUT; \
+3
View File
@@ -224,6 +224,9 @@
STANDARD_FROM_LOOP_ERR_HANDLER (1); \
} \
} \
else \
/* There was a pending character. Clear it. */ \
*statep = 0; \
\
put32 (outptr, ch); \
outptr += 4; \
+55 -15
View File
@@ -67,12 +67,29 @@
/* Since this is a stateful encoding we have to provide code which resets
the output state to the initial state. This has to be done during the
flushing. */
flushing. For the to-internal direction (FROM_DIRECTION is true),
there may be a pending character that needs flushing. */
#define EMIT_SHIFT_TO_INIT \
if ((data->__statep->__count & ~7) != sb) \
{ \
if (FROM_DIRECTION) \
data->__statep->__count &= 7; \
{ \
uint32_t ch = data->__statep->__count >> 7; \
if (__glibc_unlikely (ch != 0)) \
{ \
if (__glibc_unlikely (outend - outbuf < 4)) \
status = __GCONV_FULL_OUTPUT; \
else \
{ \
put32 (outbuf, ch); \
outbuf += 4; \
/* Clear character and db bit. */ \
data->__statep->__count &= 7; \
} \
} \
else \
data->__statep->__count &= 7; \
} \
else \
{ \
/* We are not in the initial state. To switch back we have \
@@ -99,11 +116,13 @@
*curcsp = save_curcs
/* Current codeset type. */
/* Current codeset type. The bit is stored in the __count variable of
the conversion state. If the db bit is set, bit 7 and above store
a pending UCS-4 code point if non-zero. */
enum
{
sb = 0,
db = 64
sb = 0, /* Single byte mode. */
db = 64 /* Double byte mode. */
};
@@ -119,21 +138,29 @@ enum
} \
else \
{ \
/* This is a combined character. Make sure we have room. */ \
if (__glibc_unlikely (outptr + 8 > outend)) \
{ \
result = __GCONV_FULL_OUTPUT; \
break; \
} \
\
const struct divide *cmbp \
= &DB_TO_UCS4_COMB[ch - __TO_UCS4_COMBINED_MIN]; \
assert (cmbp->res1 != 0 && cmbp->res2 != 0); \
\
put32 (outptr, cmbp->res1); \
outptr += 4; \
put32 (outptr, cmbp->res2); \
outptr += 4; \
\
/* See whether we have room for the second character. */ \
if (outend - outptr >= 4) \
{ \
put32 (outptr, cmbp->res2); \
outptr += 4; \
} \
else \
{ \
/* Otherwise store only the first character now, and \
put the second one into the queue. */ \
curcs |= cmbp->res2 << 7; \
inptr += 2; \
/* Tell the caller why we terminate the loop. */ \
result = __GCONV_FULL_OUTPUT; \
break; \
} \
} \
}
#else
@@ -153,7 +180,20 @@ enum
#define LOOPFCT FROM_LOOP
#define BODY \
{ \
uint32_t ch = *inptr; \
uint32_t ch; \
\
ch = curcs >> 7; \
if (__glibc_unlikely (ch != 0)) \
{ \
put32 (outptr, ch); \
outptr += 4; \
/* Remove the pending character, but preserve state bits. */ \
curcs &= (1 << 7) - 1; \
continue; \
} \
\
/* Otherwise read the next input byte. */ \
ch = *inptr; \
\
if (__builtin_expect (ch, 0) == SO) \
{ \
+3
View File
@@ -226,6 +226,9 @@
STANDARD_FROM_LOOP_ERR_HANDLER (1); \
} \
} \
else \
/* There was a pending character. Clear it. */ \
*statep = 0; \
\
put32 (outptr, ch); \
outptr += 4; \
+153
View File
@@ -0,0 +1,153 @@
/* Test for bug 33980: combining characters in IBM1390/IBM1399.
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 <alloc_buffer.h>
#include <errno.h>
#include <iconv.h>
#include <stdbool.h>
#include <string.h>
#include <support/check.h>
#include <support/next_to_fault.h>
#include <support/support.h>
/* Run iconv in a loop with a small output buffer of OUTBUFSIZE bytes
starting at OUTBUF. OUTBUF should be right before an unmapped page
so that writing past the end will fault. Skip SHIFT bytes at the
start of the input and output, to exercise different buffer
alignment. TRUNCATE indicates skipped bytes at the end of
input (0 and 1 a valid). */
static void
test_one (const char *encoding, unsigned int shift, unsigned int truncate,
char *outbuf, size_t outbufsize)
{
/* In IBM1390 and IBM1399, the DBCS code 0xECB5 expands to two
Unicode code points when translated. */
static char input[] =
{
/* 8 letters X. */
0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
/* SO, 0xECB5, SI: shift to DBCS, special character, shift back. */
0x0e, 0xec, 0xb5, 0x0f
};
/* Expected output after UTF-8 conversion. */
static char expected[] =
{
'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X',
/* U+304B (HIRAGANA LETTER KA). */
0xe3, 0x81, 0x8b,
/* U+309A (COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK). */
0xe3, 0x82, 0x9a
};
iconv_t cd = iconv_open ("UTF-8", encoding);
TEST_VERIFY_EXIT (cd != (iconv_t) -1);
char result_storage[64];
struct alloc_buffer result_buf
= alloc_buffer_create (result_storage, sizeof (result_storage));
char *inptr = &input[shift];
size_t inleft = sizeof (input) - shift - truncate;
while (inleft > 0)
{
char *outptr = outbuf;
size_t outleft = outbufsize;
size_t inleft_before = inleft;
size_t ret = iconv (cd, &inptr, &inleft, &outptr, &outleft);
size_t produced = outptr - outbuf;
alloc_buffer_copy_bytes (&result_buf, outbuf, produced);
if (ret == (size_t) -1 && errno == E2BIG)
{
if (produced == 0 && inleft == inleft_before)
{
/* Output buffer too small to make progress. This is
expected for very small output buffer sizes. */
TEST_VERIFY_EXIT (outbufsize < 3);
break;
}
continue;
}
if (ret == (size_t) -1)
FAIL_EXIT1 ("%s (outbufsize %zu): iconv: %m", encoding, outbufsize);
break;
}
/* Flush any pending state (e.g. a buffered combined character).
With outbufsize < 3, we could not store the first character, so
the second character did not become pending, and there is nothing
to flush. */
{
char *outptr = outbuf;
size_t outleft = outbufsize;
size_t ret = iconv (cd, NULL, NULL, &outptr, &outleft);
TEST_VERIFY_EXIT (ret == 0);
size_t produced = outptr - outbuf;
alloc_buffer_copy_bytes (&result_buf, outbuf, produced);
/* Second flush does not provide more data. */
outptr = outbuf;
outleft = outbufsize;
ret = iconv (cd, NULL, NULL, &outptr, &outleft);
TEST_VERIFY_EXIT (ret == 0);
TEST_VERIFY (outptr == outbuf);
}
TEST_VERIFY_EXIT (!alloc_buffer_has_failed (&result_buf));
size_t result_used
= sizeof (result_storage) - alloc_buffer_size (&result_buf);
if (outbufsize >= 3)
{
TEST_COMPARE (inleft, 0);
TEST_COMPARE (result_used, sizeof (expected) - shift);
TEST_COMPARE_BLOB (result_storage, result_used,
&expected[shift], sizeof (expected) - shift);
}
else
/* If the buffer is too small, only the leading X could be converted. */
TEST_COMPARE (result_used, 8 - shift);
TEST_VERIFY_EXIT (iconv_close (cd) == 0);
}
static int
do_test (void)
{
struct support_next_to_fault ntf
= support_next_to_fault_allocate (8);
for (int shift = 0; shift <= 8; ++shift)
for (int truncate = 0; truncate < 2; ++truncate)
for (size_t outbufsize = 1; outbufsize <= 8; outbufsize++)
{
char *outbuf = ntf.buffer + ntf.length - outbufsize;
test_one ("IBM1390", shift, truncate, outbuf, outbufsize);
test_one ("IBM1399", shift, truncate, outbuf, outbufsize);
}
support_next_to_fault_free (&ntf);
return 0;
}
#include <support/test-driver.c>
+124
View File
@@ -0,0 +1,124 @@
/* Test JISX0213 combining character conversion progress (bug 34556, bug 34568).
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/>. */
/* Certain JISX0213 byte sequences map to a combining sequence, for
example U+304B (HIRAGANA LETTER KA) followed by U+309A (COMBINING
SEMI-VOICED SOUND MARK). When converting to internal encoding
(actually UTF-32) with a small output buffer, the first code point
is emitted and the second is queued in the converter state. This
test verifies that the queued code point is consumed exactly once
on retry, so that the conversion makes progress and terminates. */
#include <errno.h>
#include <iconv.h>
#include <stdio.h>
#include <string.h>
#include <support/check.h>
#include <support/support.h>
static void
test_one (const char *charset, const char *input, size_t outbufsize)
{
printf ("info: %s: testing output buffer size %zu\n", charset, outbufsize);
/* Expected UTF-32 output. */
static const wchar_t expected[] = { 0x304b, 0x309a, 'A' };
/* Use WCHAR_T encoding to avoid the BOM. */
iconv_t cd = iconv_open ("WCHAR_T", charset);
TEST_VERIFY_EXIT (cd != (iconv_t) -1);
char result[64];
size_t result_len = 0;
char *inptr = (char *) input;
size_t inleft = strlen (input);
char outbuf[64];
int iterations = 0;
while (inleft > 0)
{
char *outptr = outbuf;
size_t outleft = outbufsize;
size_t inleft_before = inleft;
size_t ret = iconv (cd, &inptr, &inleft, &outptr, &outleft);
size_t produced = outptr - outbuf;
TEST_VERIFY_EXIT (result_len + produced <= sizeof (result));
memcpy (result + result_len, outbuf, produced);
result_len += produced;
if (ret == (size_t) -1 && errno == E2BIG)
{
if (produced == 0 && inleft == inleft_before)
{
/* Output buffer too small for a single code point. */
TEST_VERIFY_EXIT (outbufsize < 4);
break;
}
/* Bound iterations to detect non-progress bugs. */
if (++iterations < 10)
continue;
else
{
FAIL ("%s: no progress", charset);
goto out;
}
}
if (ret == (size_t) -1)
FAIL_EXIT1 ("outbufsize %zu: iconv: %m", outbufsize);
break;
}
/* Flush pending converter state. */
{
char *outptr = outbuf;
size_t outleft = outbufsize;
size_t ret = iconv (cd, NULL, NULL, &outptr, &outleft);
TEST_VERIFY (ret == 0);
size_t produced = outptr - outbuf;
memcpy (result + result_len, outbuf, produced);
result_len += produced;
}
if (outbufsize >= 4)
{
TEST_COMPARE (inleft, 0);
TEST_COMPARE_BLOB (result, result_len,
expected, sizeof (expected));
}
out:
TEST_VERIFY_EXIT (iconv_close (cd) == 0);
}
static int
do_test (void)
{
for (size_t outbufsize = 1; outbufsize <= 16; outbufsize++)
{
test_one ("EUC-JISX0213", "\244\367A", outbufsize);
test_one ("SHIFT_JISX0213", "\202\365A", outbufsize);
}
return 0;
}
#include <support/test-driver.c>
+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;
+4 -4
View File
@@ -45,15 +45,15 @@ __NTH (inet_pton (int __af,
__fortify_clang_warning_only_if_bos0_lt
(4, __dst, "inet_pton called with destination buffer size less than 4")
{
size_t sz = 0;
size_t __sz = 0;
if (__af == AF_INET)
sz = sizeof (struct in_addr);
__sz = sizeof (struct in_addr);
else if (__af == AF_INET6)
sz = sizeof (struct in6_addr);
__sz = sizeof (struct in6_addr);
else
return __inet_pton_alias (__af, __src, __dst);
return __glibc_fortify (inet_pton, sz, sizeof (char),
return __glibc_fortify (inet_pton, __sz, sizeof (char),
__glibc_objsize (__dst),
__af, __src, __dst);
};
+4
View File
@@ -18,6 +18,10 @@
#include <errno.h>
#include <fcntl.h>
#ifndef __O_CLOEXEC
# error __O_CLOEXEC not defined by fcntl.h/cloexec.h
#endif
/* Perform file control operations on FD. */
int
__fcntl (int fd, int cmd, ...)
+2
View File
@@ -83,6 +83,7 @@ tests = \
bug-ungetwc1 \
bug-ungetwc2 \
bug-wfflush \
bug-wgenops-bz33998 \
bug-wmemstream1 \
bug-wsetpos \
test-fmemopen \
@@ -106,6 +107,7 @@ tests = \
tst-fgetc-after-eof \
tst-fgetwc \
tst-fgetws \
tst-fopen-ccs-empty \
tst-fopenloc2 \
tst-fputws \
tst-freopen \
+54
View File
@@ -0,0 +1,54 @@
/* Regression test for ungetwc operating on byte stream (BZ #33998)
Copyright (C) 2026 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 "support/temp_file.h"
#include "support/xstdio.h"
#include "support/xunistd.h"
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <wchar.h>
#include <support/check.h>
static int
do_test (void)
{
char *filename;
int fd = create_temp_file ("tst-bz33998-", &filename);
TEST_VERIFY (fd != -1);
xwrite (fd, "A", sizeof ("A")); // write "A\0" by design
xclose (fd);
FILE *fp = xfopen (filename, "r+");
TEST_COMPARE (getwc (fp), L'A');
/* If the bug is fixed, then ungetwc should not touch byte stream.
If the bug is not fixed, ungetwc firstly match last read char, L'A',
failed, then the pbackfail branch, matching last read char in byte
stream, that is, '\0' (initialized when setup wide stream). */
char *old_read_ptr = fp->_IO_read_ptr;
TEST_COMPARE (ungetwc (L'\0', fp), L'\0');
TEST_VERIFY (fp->_IO_read_ptr == old_read_ptr);
xfclose (fp);
free (filename);
return 0;
}
#include <support/test-driver.c>
+7 -5
View File
@@ -339,12 +339,14 @@ _IO_new_file_fopen (FILE *fp, const char *filename, const char *mode,
*((char *) __mempcpy (ccs, cs + 5, endp - (cs + 5))) = '\0';
strip (ccs, ccs);
if (__wcsmbs_named_conv (&fcts, ccs[2] == '\0'
? upstr (ccs, cs + 5) : ccs) != 0)
/* After stripping, ccs[2] == '\0' means the charset name is empty.
This is not a valid charset and would cause problems downstream.
Reject it with EINVAL (BZ #34574, CVE-2026-18374). */
if (ccs[2] == '\0' || __wcsmbs_named_conv (&fcts, ccs) != 0)
{
/* Something went wrong, we cannot load the conversion modules.
This means we cannot proceed since the user explicitly asked
for these. */
/* Either the charset name is empty after strip(), or conversion
modules cannot be loaded. This means we cannot proceed since
the user explicitly asked for character conversion. */
(void) _IO_file_close_it (fp);
free (ccs);
__set_errno (EINVAL);
+3 -3
View File
@@ -168,11 +168,11 @@ extern int renameat (int __oldfd, const char *__old, int __newfd,
#ifdef __USE_GNU
/* Flags for renameat2. */
# define RENAME_NOREPLACE (1 << 0)
# define AT_RENAME_NOREPLACE RENAME_NOREPLACE
# define AT_RENAME_NOREPLACE 0x0001
# define RENAME_EXCHANGE (1 << 1)
# define AT_RENAME_EXCHANGE RENAME_EXCHANGE
# define AT_RENAME_EXCHANGE 0x0002
# define RENAME_WHITEOUT (1 << 2)
# define AT_RENAME_WHITEOUT RENAME_WHITEOUT
# define AT_RENAME_WHITEOUT 0x0004
/* Rename file OLD relative to OLDFD to NEW relative to NEWFD, with
additional flags. */
+62
View File
@@ -0,0 +1,62 @@
/* Test fopen with an empty ",ccs=" value in the mode string (bug 34574).
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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <support/check.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/xunistd.h>
static void
check_fopen_fails (const char *path, const char *mode)
{
errno = 0;
FILE *fp = fopen (path, mode);
TEST_VERIFY (fp == NULL);
TEST_COMPARE (errno, EINVAL);
if (fp != NULL)
fclose (fp);
}
static int
do_test (void)
{
char *path;
xclose (create_temp_file ("tst-fopen-ccs-empty", &path));
/* The value is blank and the mode string continues well past it. */
enum { size = 1024 * 1024 };
char *mode = xmalloc (size);
memset (mode, 'X', size);
mode[size - 1] = '\0';
static const char prefix[] = "w,ccs= ,";
memcpy (mode, prefix, sizeof (prefix) - 1);
check_fopen_fails (path, mode);
free (mode);
check_fopen_fails (path, "w,ccs=");
check_fopen_fails (path, "w,ccs=,");
free (path);
return 0;
}
#include <support/test-driver.c>
+2 -2
View File
@@ -108,8 +108,8 @@ _IO_wdefault_pbackfail (FILE *fp, wint_t c)
{
if (fp->_wide_data->_IO_read_ptr > fp->_wide_data->_IO_read_base
&& !_IO_in_backup (fp)
&& (wint_t) fp->_IO_read_ptr[-1] == c)
--fp->_IO_read_ptr;
&& (wint_t) fp->_wide_data->_IO_read_ptr[-1] == c)
--fp->_wide_data->_IO_read_ptr;
else
{
/* Need to handle a filebuf in write mode (switch to read mode). FIXME!*/
+2
View File
@@ -236,6 +236,8 @@ tests = \
bug-iconv-trans \
bug-setlocale1 \
bug-usesetlocale \
tst-bz12701-lc \
tst-bz12701-lc2 \
tst-bz13988 \
tst-c-utf8-consistency \
tst-digits \
+2 -2
View File
@@ -248,8 +248,8 @@ reorder-end
END LC_COLLATE
LC_MONETARY
int_curr_symbol "BGN "
currency_symbol "лв."
int_curr_symbol "EUR "
currency_symbol ""
mon_decimal_point ","
mon_thousands_sep ""
mon_grouping 3
+218
View File
@@ -0,0 +1,218 @@
/* Verify scanf field width handling with the 'lc' conversion (BZ #12701).
Copyright (C) 2025-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 <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <libc-diag.h>
#include <support/check.h>
#include <support/next_to_fault.h>
#include <support/xstdio.h>
/* Compare character-wise the initial part of the wide character object
pointed to by WS corresponding to wide characters obtained by the
conversion of first N bytes of the multibyte character object pointed
to by S. */
static int
tst_bz12701_lc_memcmp (const wchar_t *ds, const char *s, size_t n)
{
size_t nc = mbsnrtowcs (NULL, &s, n, 0, NULL);
struct support_next_to_fault ntf;
ntf = support_next_to_fault_allocate (nc * sizeof (wchar_t));
wchar_t *ss = (wchar_t *) ntf.buffer;
mbsnrtowcs (ss, &s, n, nc, NULL);
int r = wmemcmp (ds, ss, nc);
support_next_to_fault_free (&ntf);
return r;
}
/* Verify various aspects of field width handling, including the data
obtained, the number of bytes consumed, and the stream position. */
static int
do_test (void)
{
if (setlocale (LC_ALL, "pl_PL.UTF-8") == NULL)
FAIL_EXIT1 ("setlocale (LC_ALL, \"pl_PL.UTF-8\")");
/* Part of a tongue-twister in Polish, which says:
"On a rainy morning cuckoos and warblers, rather than starting
on earthworms, stuffed themselves fasted with the flesh of cress." */
static const char s[126] = "Dżdżystym rankiem gżegżółki i piegże, "
"zamiast wziąć się za dżdżownice, "
"nażarły się na czczo miąższu rzeżuchy";
const char *sp = s;
size_t nc;
TEST_VERIFY_EXIT ((nc = mbsnrtowcs (NULL, &sp, sizeof (s), 0, NULL)) == 108);
struct support_next_to_fault ntfo, ntfi;
ntfo = support_next_to_fault_allocate (nc * sizeof (wchar_t));
ntfi = support_next_to_fault_allocate (sizeof (s));
wchar_t *e = (wchar_t *) ntfo.buffer + nc;
char *b = ntfi.buffer;
wchar_t *c;
FILE *f;
int ic;
int n;
int i;
memcpy (ntfi.buffer, s, sizeof (s));
ic = i = 0;
f = xfmemopen (b, sizeof (s), "r");
c = e - 1;
TEST_VERIFY_EXIT (ftell (f) == i);
/* Avoid: "warning: zero width in gnu_scanf format [-Werror=format=]". */
DIAG_PUSH_NEEDS_COMMENT;
DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
TEST_VERIFY_EXIT (fscanf (f, "%0lc%n", c, &n) == 1);
DIAG_POP_NEEDS_COMMENT;
TEST_VERIFY_EXIT (n == 1);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 1;
i += n;
c = e - 1;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 2);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 1;
i += n;
c = e - 1;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%1lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 1);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 1;
i += n;
c = e - 2;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 3);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 2;
i += n;
c = e - 4;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%4lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 4);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 4;
i += n;
c = e - 8;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%8lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 8);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 8;
i += n;
c = e - 16;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%16lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 20);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 16;
i += n;
c = e - 32;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%32lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 38);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 32;
i += n;
c = e - (nc - ic);
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_COMPARE (fscanf (f, "%64lc%n", c, &n), 1);
TEST_COMPARE (n , 49);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, sizeof (s) - i) == 0);
TEST_VERIFY_EXIT (ftell (f) == sizeof (s));
TEST_VERIFY_EXIT (feof (f) != 0);
xfclose (f);
ic = i = 0;
f = xfmemopen (b, 3, "r");
c = e - 2;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 3);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 2;
i += n;
c = e - (nc - ic);
TEST_VERIFY_EXIT (feof (f) == 0);
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == EOF);
TEST_VERIFY_EXIT (n == 3);
TEST_VERIFY_EXIT (ftell (f) == 3);
TEST_VERIFY_EXIT (feof (f) != 0);
xfclose (f);
ic = i = 0;
f = xfmemopen (b, 3, "r");
c = e - 1;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 1);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0);
ic += 1;
i += n;
c = e - (nc - ic);
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 2);
TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, 3 - i) == 0);
TEST_VERIFY_EXIT (ftell (f) == 3);
TEST_VERIFY_EXIT (feof (f) != 0);
xfclose (f);
support_next_to_fault_free (&ntfi);
support_next_to_fault_free (&ntfo);
return 0;
}
#include <support/test-driver.c>
+47
View File
@@ -0,0 +1,47 @@
/* Verify scanf memory handling with the 'c' conversion (BZ #12701).
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 <malloc.h>
#include <string.h>
#include <libc-diag.h>
#include <support/check.h>
#include <support/next_to_fault.h>
#include <support/xstdio.h>
static int
do_test (void)
{
wchar_t *c = NULL;
int i;
TEST_VERIFY (sscanf ("1234", "%30mlc", &c) == 1);
TEST_VERIFY (c != NULL);
TEST_COMPARE_BLOB (c, 5 * sizeof (wchar_t),
L"1234\0", 5 * sizeof (wchar_t));
for (i = 5; i < 30; i ++)
TEST_VERIFY (c[i] == L'\0');
TEST_VERIFY (malloc_usable_size (c) >= 30 * sizeof(wchar_t));
return 0;
}
#include <support/test-driver.c>
+32 -16
View File
@@ -230,6 +230,9 @@
/* For uintptr_t. */
#include <stdint.h>
/* For stdc_count_ones. */
#include <stdbit.h>
/* For va_arg, va_start, va_end. */
#include <stdarg.h>
@@ -294,9 +297,9 @@
# define TCACHE_SMALL_BINS 64
# define TCACHE_LARGE_BINS 12 /* Up to 4M chunks */
# define TCACHE_MAX_BINS (TCACHE_SMALL_BINS + TCACHE_LARGE_BINS)
# define MAX_TCACHE_SMALL_SIZE tidx2usize (TCACHE_SMALL_BINS-1)
# define MAX_TCACHE_SMALL_SIZE tidx2csize (TCACHE_SMALL_BINS-1)
/* Only used to pre-fill the tunables. */
# define tidx2csize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE)
# define tidx2usize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE - SIZE_SZ)
/* When "x" is from chunksize(). */
@@ -1932,7 +1935,7 @@ static struct malloc_par mp_ =
,
.tcache_count = TCACHE_FILL_COUNT,
.tcache_small_bins = TCACHE_SMALL_BINS,
.tcache_max_bytes = MAX_TCACHE_SMALL_SIZE,
.tcache_max_bytes = MAX_TCACHE_SMALL_SIZE + 1,
.tcache_unsorted_limit = 0 /* No limit. */
#endif
};
@@ -3152,6 +3155,19 @@ tcache_key_initialize (void)
if (__getrandom_nocancel_nostatus_direct (&tcache_key, sizeof(tcache_key),
GRND_NONBLOCK)
!= sizeof (tcache_key))
tcache_key = 0;
/* We need tcache_key to be non-zero (otherwise tcache_double_free_verify's
clearing of e->key would go unnoticed and it would loop getting called
through __libc_free), and we want tcache_key not to be a
commonly-occurring value in memory, so ensure a minimum amount of one and
zero bits. */
int minimum_bits = __WORDSIZE / 4;
int maximum_bits = __WORDSIZE - minimum_bits;
while (labs ((intptr_t) tcache_key) <= 0x1000000
|| stdc_count_ones (tcache_key) < minimum_bits
|| stdc_count_ones (tcache_key) > maximum_bits)
{
tcache_key = random_bits ();
#if __WORDSIZE == 64
@@ -3208,11 +3224,10 @@ tcache_get_n (size_t tc_idx, tcache_entry **ep, bool mangled)
if (__glibc_unlikely (misaligned_mem (e)))
malloc_printerr ("malloc(): unaligned tcache chunk detected");
void *ne = e == NULL ? NULL : REVEAL_PTR (e->next);
if (!mangled)
*ep = ne;
*ep = REVEAL_PTR (e->next);
else
*ep = PROTECT_PTR (ep, ne);
*ep = PROTECT_PTR (ep, REVEAL_PTR (e->next));
++(tcache->num_slots[tc_idx]);
e->key = 0;
@@ -3229,7 +3244,7 @@ tcache_put (mchunkptr chunk, size_t tc_idx)
static __always_inline void *
tcache_get (size_t tc_idx)
{
return tcache_get_n (tc_idx, & tcache->entries[tc_idx], false);
return tcache_get_n (tc_idx, &tcache->entries[tc_idx], false);
}
static __always_inline tcache_entry **
@@ -5152,7 +5167,7 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
INTERNAL_SIZE_T size;
nb = checked_request2size (bytes);
if (nb == 0)
if (nb == 0 || alignment > PTRDIFF_MAX)
{
__set_errno (ENOMEM);
return NULL;
@@ -5168,7 +5183,10 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
we don't find anything in those bins, the common malloc code will
scan starting at 2x. */
/* Call malloc with worst case padding to hit alignment. */
/* Call malloc with worst case padding to hit alignment. ALIGNMENT is a
power of 2, so it tops out at (PTRDIFF_MAX >> 1) + 1, leaving plenty of
space to add MINSIZE and whatever checked_request2size adds to BYTES to
get NB. Consequently, total below also does not overflow. */
m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
if (m == NULL)
@@ -5587,15 +5605,13 @@ do_set_arena_max (size_t value)
static __always_inline int
do_set_tcache_max (size_t value)
{
if (value > PTRDIFF_MAX)
return 0;
size_t nb = request2size (value);
size_t tc_idx = csize2tidx (nb);
/* To check that value is not too big and request2size does not return an
overflown value. */
if (value > nb)
return 0;
if (nb > MAX_TCACHE_SMALL_SIZE)
if (tc_idx >= TCACHE_SMALL_BINS)
tc_idx = large_csize2tidx (nb);
LIBC_PROBE (memory_tunable_tcache_max_bytes, 2, value, mp_.tcache_max_bytes);
@@ -5604,7 +5620,7 @@ do_set_tcache_max (size_t value)
{
if (tc_idx < TCACHE_SMALL_BINS)
mp_.tcache_small_bins = tc_idx + 1;
mp_.tcache_max_bytes = nb;
mp_.tcache_max_bytes = nb + 1;
return 1;
}
+2 -8
View File
@@ -152,7 +152,6 @@ test_large_allocations (size_t size)
}
static long pagesize;
/* This function tests the following aligned memory allocation functions
using several valid alignments and precedes each allocation test with a
@@ -171,8 +170,8 @@ test_large_aligned_allocations (size_t size)
/* All aligned memory allocation functions expect an alignment that is a
power of 2. Given this, we test each of them with every valid
alignment from 1 thru PAGESIZE. */
for (align = 1; align <= pagesize; align *= 2)
alignment for the type of ALIGN, i.e. until it wraps to 0. */
for (align = 1; align > 0; align <<= 1)
{
test_setup ();
#if __GNUC_PREREQ (7, 0)
@@ -265,11 +264,6 @@ do_test (void)
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
/* Aligned memory allocation functions need to be tested up to alignment
size equivalent to page size, which should be a power of 2. */
pagesize = sysconf (_SC_PAGESIZE);
TEST_VERIFY_EXIT (powerof2 (pagesize));
/* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
+11 -20
View File
@@ -85,6 +85,7 @@
#include <assert.h>
#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <search.h>
@@ -406,12 +407,13 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
int cmp;
node *rootp = (node *) vrootp;
node root, unchained;
/* Stack of nodes so we remember the parents without recursion. It's
_very_ unlikely that there are paths longer than 40 nodes. The tree
would need to have around 250.000 nodes. */
int stacksize = 40;
/* Stack of nodes so we remember the parents without recursion. The
stack size is a conservative approximation of the maximum height
of a red-black tree, based on size of the address space.
Actual numbers are closer to 57 (32 bit) and 117 (63 bit). */
enum { stacksize = 2 * UINTPTR_WIDTH };
int sp = 0;
node **nodestack = alloca (sizeof (node *) * stacksize);
node *nodestack[stacksize];
if (rootp == NULL)
return NULL;
@@ -424,14 +426,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
root = DEREFNODEPTR(rootp);
while ((cmp = (*compar) (key, root->key)) != 0)
{
if (sp == stacksize)
{
node **newstack;
stacksize += 20;
newstack = alloca (sizeof (node *) * stacksize);
nodestack = memcpy (newstack, nodestack, sp * sizeof (node *));
}
assert (sp < stacksize);
nodestack[sp++] = rootp;
p = DEREFNODEPTR(rootp);
if (cmp < 0)
@@ -470,13 +465,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
node upn;
for (;;)
{
if (sp == stacksize)
{
node **newstack;
stacksize += 20;
newstack = alloca (sizeof (node *) * stacksize);
nodestack = memcpy (newstack, nodestack, sp * sizeof (node *));
}
assert (sp < stacksize);
nodestack[sp++] = parentp;
parentp = up;
upn = DEREFNODEPTR(up);
@@ -541,6 +530,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
SETNODEPTR(pp,q);
/* Make sure pp is right if the case below tries to use
it. */
assert (sp < stacksize);
nodestack[sp++] = pp = LEFTPTR(q);
q = RIGHT(p);
}
@@ -625,6 +615,7 @@ __tdelete (const void *key, void **vrootp, __compar_fn_t compar)
SETLEFT(p,RIGHT(q));
SETRIGHT(q,p);
SETNODEPTR(pp,q);
assert (sp < stacksize);
nodestack[sp++] = pp = RIGHTPTR(q);
q = LEFT(p);
}
+2 -3
View File
@@ -109,9 +109,8 @@ do_test_with_invalid_iov (void)
static void
do_test_with_invalid_flags (void)
{
/* Set the next bit from the mask of all supported flags. */
int invalid_flag = RWF_SUPPORTED != 0 ? __builtin_clz (RWF_SUPPORTED) : 2;
invalid_flag = 0x1 << ((sizeof (int) * CHAR_BIT) - invalid_flag);
/* Set all the bits that are not used by the supported flags. */
int invalid_flag = ~RWF_SUPPORTED;
char buf[32];
const struct iovec vec = { .iov_base = buf, .iov_len = sizeof (buf) };
+17 -5
View File
@@ -240,7 +240,7 @@ setup_stack_prot (char *mem, size_t size, struct pthread *pd,
/* Update the guard area of the thread stack MEM of size SIZE with the new
GUARDISZE. It uses the method defined by PD stack_mode. */
static inline bool
adjust_stack_prot (char *mem, size_t size, const struct pthread *pd,
adjust_stack_prot (char *mem, size_t size, struct pthread *pd,
size_t guardsize, size_t pagesize_m1)
{
/* The required guard area is larger than the current one. For
@@ -258,11 +258,23 @@ adjust_stack_prot (char *mem, size_t size, const struct pthread *pd,
so use the new guard placement with the new size. */
if (guardsize > pd->guardsize)
{
/* There was no need to previously setup a guard page, so we need
to check whether the kernel supports guard advise. */
char *guard = guard_position (mem, size, guardsize, pd, pagesize_m1);
if (pd->stack_mode == ALLOCATE_GUARD_MADV_GUARD)
return __madvise (guard, guardsize, MADV_GUARD_INSTALL) == 0;
else if (pd->stack_mode == ALLOCATE_GUARD_PROT_NONE)
return __mprotect (guard, guardsize, PROT_NONE) == 0;
if (atomic_load_relaxed (&allocate_stack_mode)
== ALLOCATE_GUARD_MADV_GUARD)
{
if (__madvise (guard, guardsize, MADV_GUARD_INSTALL) == 0)
{
pd->stack_mode = ALLOCATE_GUARD_MADV_GUARD;
return true;
}
atomic_store_relaxed (&allocate_stack_mode,
ALLOCATE_GUARD_PROT_NONE);
}
pd->stack_mode = ALLOCATE_GUARD_PROT_NONE;
return __mprotect (guard, guardsize, PROT_NONE) == 0;
}
/* The current guard area is larger than the required one. For
_STACK_GROWS_DOWN is means change the guard as:
+2 -2
View File
@@ -72,8 +72,8 @@ __syscall_cancel (__syscall_arg_t a1, __syscall_arg_t a2,
__syscall_arg_t a5, __syscall_arg_t a6,
__SYSCALL_CANCEL7_ARG_DEF __syscall_arg_t nr)
{
int r = __internal_syscall_cancel (a1, a2, a3, a4, a5, a6,
__SYSCALL_CANCEL7_ARG nr);
long int r = __internal_syscall_cancel (a1, a2, a3, a4, a5, a6,
__SYSCALL_CANCEL7_ARG nr);
return __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (r))
? SYSCALL_ERROR_LABEL (INTERNAL_SYSCALL_ERRNO (r))
: r;
+6 -2
View File
@@ -48,7 +48,8 @@ ___pthread_mutex_trylock (pthread_mutex_t *mutex)
return 0;
}
if (lll_trylock (mutex->__data.__lock) == 0)
if (atomic_load_relaxed (&(mutex->__data.__lock)) == 0
&& lll_trylock (mutex->__data.__lock) == 0)
{
/* Record the ownership. */
mutex->__data.__owner = id;
@@ -71,7 +72,10 @@ ___pthread_mutex_trylock (pthread_mutex_t *mutex)
/*FALL THROUGH*/
case PTHREAD_MUTEX_ADAPTIVE_NP:
case PTHREAD_MUTEX_ERRORCHECK_NP:
if (lll_trylock (mutex->__data.__lock) != 0)
/* Mutex type is already loaded, lock check overhead should
be minimal. */
if (atomic_load_relaxed (&(mutex->__data.__lock)) != 0
|| lll_trylock (mutex->__data.__lock) != 0)
break;
/* Record the ownership. */
+40 -20
View File
@@ -21,6 +21,7 @@
#include <setjmp.h>
#include <stackinfo.h>
#include <stdio.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
#include <support/test-driver.h>
#include <support/xsignal.h>
@@ -202,7 +203,7 @@ tf (void *closure)
/* Test 1: caller provided stack without guard. */
static void
do_test1 (void)
do_test1 (void *closure)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
@@ -227,7 +228,7 @@ do_test1 (void)
/* Test 2: same as 1., but with a guard area. */
static void
do_test2 (void)
do_test2 (void *closure)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
@@ -250,18 +251,9 @@ do_test2 (void)
xmunmap (stack, stacksize);
}
/* Test 3: pthread_create with default values. */
/* Test 3: pthread_create without a guard area. */
static void
do_test3 (void)
{
pthread_t t = xpthread_create (NULL, tf, NULL);
void *status = xpthread_join (t);
TEST_VERIFY (status == 0);
}
/* Test 4: pthread_create without a guard area. */
static void
do_test4 (void)
do_test3 (void *closure)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
@@ -277,9 +269,18 @@ do_test4 (void)
xpthread_attr_destroy (&attr);
}
/* Test 4: pthread_create with default values. */
static void
do_test4 (void *closure)
{
pthread_t t = xpthread_create (NULL, tf, NULL);
void *status = xpthread_join (t);
TEST_VERIFY (status == 0);
}
/* Test 5: pthread_create with non default stack and guard size value. */
static void
do_test5 (void)
do_test5 (void *closure)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
@@ -299,7 +300,7 @@ do_test5 (void)
test 3, but with a larger guard area. The pthread_create will need to
increase the guard area. */
static void
do_test6 (void)
do_test6 (void *closure)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
@@ -320,7 +321,7 @@ do_test6 (void)
pthread_create should use the cached stack from previous tests, but it
would require to reduce the guard area. */
static void
do_test7 (void)
do_test7 (void *closure)
{
pthread_t t = xpthread_create (NULL, tf, NULL);
void *status = xpthread_join (t);
@@ -346,21 +347,40 @@ do_test (void)
static const struct {
const char *descr;
void (*test)(void);
void (*test) (void *);
} tests[] = {
{ "user provided stack without guard", do_test1 },
{ "user provided stack with guard", do_test2 },
{ "default attribute", do_test3 },
{ "default attribute without guard", do_test4 },
/* N.B: do_test3 should be before do_test4 to check if a new thread
that uses the thread stack previously allocated without a guard
page correctly sets up the guard pages even on a kernel without
MADV_GUARD_INSTALL support (BZ 33356). */
{ "default attribute without guard", do_test3 },
{ "default attribute", do_test4 },
/* Also checks if the guard is correctly removed from the cache thread
stack. */
{ "default attribute without guard", do_test3 },
{ "non default stack and guard sizes", do_test5 },
{ "reused stack with larger guard", do_test6 },
{ "reused stack with smaller guard", do_test7 },
};
/* Run each test with a clean state. */
for (int i = 0; i < array_length (tests); i++)
{
printf ("debug: fork: test%01d: %s\n", i, tests[i].descr);
struct support_capture_subprocess result =
support_capture_subprocess (tests[i].test, NULL);
support_capture_subprocess_check (&result, tests[i].descr, 0,
sc_allow_none);
support_capture_subprocess_free (&result);
}
/* And now run the same tests along with the thread stack cache. */
for (int i = 0; i < array_length (tests); i++)
{
printf ("debug: test%01d: %s\n", i, tests[i].descr);
tests[i].test();
tests[i].test ( NULL);
}
return 0;
+1
View File
@@ -326,6 +326,7 @@ tests := \
tst-gshadow \
tst-nss-getpwent \
tst-nss-hash \
tst-nss-malloc-failure-getlogin_r \
tst-nss-test1 \
tst-nss-test2 \
tst-nss-test4 \
+7 -11
View File
@@ -157,19 +157,15 @@ __merge_einval (LOOKUP_TYPE *a,
#define CHECK_MERGE(err, status) \
({ \
do \
if (err) \
{ \
if (err) \
{ \
__set_errno (err); \
if (err == ERANGE) \
status = NSS_STATUS_TRYAGAIN; \
else \
status = NSS_STATUS_UNAVAIL; \
break; \
} \
__set_errno (err); \
if (err == ERANGE) \
status = NSS_STATUS_TRYAGAIN; \
else \
status = NSS_STATUS_UNAVAIL; \
break; \
} \
while (0); \
})
/* Type of the lookup function we need here. */
+13 -8
View File
@@ -56,7 +56,6 @@ global_state_allocate (void *closure)
{
result->data.nsswitch_conf.size = -1; /* Force reload. */
memset (result->data.services, 0, sizeof (result->data.services));
result->data.initialized = true;
result->data.reload_disabled = false;
__libc_lock_init (result->lock);
result->root_ino = 0;
@@ -251,9 +250,12 @@ __nss_configure_lookup (const char *dbname, const char *service_line)
/* Force any load/cache/read whatever to happen, so we can override
it. */
__nss_database_get (db, &result);
if (!__nss_database_get (db, &result))
return -1;
local = nss_database_state_get ();
if (local == NULL)
return -1;
result = __nss_action_parse (service_line);
if (result == NULL)
@@ -451,8 +453,8 @@ nss_database_check_reload_and_get (struct nss_database_state *local,
/* Avoid overwriting the global configuration until we have loaded
everything successfully. Otherwise, if the file change
information changes back to what is in the global configuration,
the lookups would use the partially-written configuration. */
struct nss_database_data staging = { .initialized = true, };
the lookups would use the partially-written configuration. */
struct nss_database_data staging = { };
bool ok = nss_database_reload (&staging, &initial);
@@ -478,6 +480,8 @@ bool
__nss_database_get (enum nss_database db, nss_action_list *actions)
{
struct nss_database_state *local = nss_database_state_get ();
if (local == NULL)
return false;
return nss_database_check_reload_and_get (local, actions, db);
}
libc_hidden_def (__nss_database_get)
@@ -503,7 +507,7 @@ __nss_database_freeres (void)
}
void
__nss_database_fork_prepare_parent (struct nss_database_data *data)
__nss_database_fork_prepare_parent (struct nss_database_for_fork *data)
{
/* Do not use allocate_once to trigger loading unnecessarily. */
struct nss_database_state *local = atomic_load_acquire (&global_database_state);
@@ -515,20 +519,21 @@ __nss_database_fork_prepare_parent (struct nss_database_data *data)
because it avoids acquiring the lock during the actual
fork. */
__libc_lock_lock (local->lock);
*data = local->data;
data->data = local->data;
__libc_lock_unlock (local->lock);
data->initialized = true;
}
}
void
__nss_database_fork_subprocess (struct nss_database_data *data)
__nss_database_fork_subprocess (struct nss_database_for_fork *data)
{
struct nss_database_state *local = atomic_load_acquire (&global_database_state);
if (data->initialized)
{
/* Restore the state at the point of the fork. */
assert (local != NULL);
local->data = *data;
local->data = data->data;
__libc_lock_init (local->lock);
}
else if (local != NULL)
+9 -3
View File
@@ -70,15 +70,21 @@ struct nss_database_data
struct file_change_detection nsswitch_conf;
nss_action_list services[NSS_DATABASE_COUNT];
int reload_disabled; /* Actually bool; int for atomic access. */
bool initialized;
};
/* Use to store a consistent state snapshot across fork. */
struct nss_database_for_fork
{
bool initialized; /* Set to true if the data field below is initialized. */
struct nss_database_data data;
};
/* Called by fork in the parent process, before forking. */
void __nss_database_fork_prepare_parent (struct nss_database_data *data)
void __nss_database_fork_prepare_parent (struct nss_database_for_fork *)
attribute_hidden;
/* Called by fork in the new subprocess, after forking. */
void __nss_database_fork_subprocess (struct nss_database_data *data)
void __nss_database_fork_subprocess (struct nss_database_for_fork *)
attribute_hidden;
#endif /* _NSS_DATABASE_H */
+3 -3
View File
@@ -36,16 +36,16 @@ LINE_PARSER
unsigned int number;
if (cnt < 5)
INT_FIELD (number, ISCOLON , 0, 16, (unsigned int))
INT_FIELD (number, ISCOLON , false, 16, (unsigned int))
else
INT_FIELD (number, isspace, 1, 16, (unsigned int))
INT_FIELD (number, isspace, true, 16, (unsigned int))
if (number > 0xff)
return 0;
result->e_addr.ether_addr_octet[cnt] = number;
}
};
STRING_FIELD (result->e_name, isspace, 1);
STRING_FIELD (result->e_name, isspace, true);
)
+2 -2
View File
@@ -53,7 +53,7 @@ LINE_PARSER
{
char *addr;
STRING_FIELD (addr, isspace, 1);
STRING_FIELD (addr, isspace, true);
/* Parse address. */
if (__inet_pton (af == AF_UNSPEC ? AF_INET : af, addr, entdata->host_addr)
@@ -96,7 +96,7 @@ LINE_PARSER
entdata->h_addr_ptrs[1] = NULL;
result->h_addr_list = entdata->h_addr_ptrs;
STRING_FIELD (result->h_name, isspace, 1);
STRING_FIELD (result->h_name, isspace, true);
})
#define EXTRA_ARGS_VALUE , AF_INET, 0
+2 -2
View File
@@ -38,9 +38,9 @@ LINE_PARSER
char *cp;
int n = 1;
STRING_FIELD (result->n_name, isspace, 1);
STRING_FIELD (result->n_name, isspace, true);
STRING_FIELD (addr, isspace, 1);
STRING_FIELD (addr, isspace, true);
/* 'inet_network' does not add zeroes at the end if the network number
does not contain four byte values. We shift result ourselves if
necessary. */
+1
View File
@@ -20,6 +20,7 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <nss_files.h>
+2 -2
View File
@@ -29,8 +29,8 @@ struct protoent_data {};
#include "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,);
)
#include GENERIC
+2 -2
View File
@@ -29,8 +29,8 @@ struct rpcent_data {};
#include "files-parse.c"
LINE_PARSER
("#",
STRING_FIELD (result->r_name, isspace, 1);
INT_FIELD (result->r_number, isspace, 1, 10,);
STRING_FIELD (result->r_name, isspace, true);
INT_FIELD (result->r_number, isspace, true, 10,);
)
#include GENERIC
+3 -3
View File
@@ -31,9 +31,9 @@ struct servent_data {};
#define ISSLASH(c) ((c) == '/')
LINE_PARSER
("#",
STRING_FIELD (result->s_name, isspace, 1);
INT_FIELD (result->s_port, ISSLASH, 10, 0, htons);
STRING_FIELD (result->s_proto, isspace, 1);
STRING_FIELD (result->s_name, isspace, true);
INT_FIELD (result->s_port, ISSLASH, false, 10, htons);
STRING_FIELD (result->s_proto, isspace, true);
)
#include GENERIC
+345
View File
@@ -0,0 +1,345 @@
/* Test NSS/getlogin_r with injected allocation failures (bug 28940).
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 <errno.h>
#include <getopt.h>
#include <malloc.h>
#include <netdb.h>
#include <nss.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <support/check.h>
#include <support/namespace.h>
#include <support/support.h>
#include <support/xstdio.h>
#include <unistd.h>
/* This test calls getpwuid_r via getlogin_r (on Linux).
This test uses the NSS system configuration to exercise that code
path. It means that it can fail (crash) if malloc failure is not
handled by NSS modules for the passwd database. */
/* Data structure allocated via MAP_SHARED, so that writes from the
subprocess are visible. */
struct shared_data
{
/* Number of tracked allocations performed so far. */
volatile unsigned int allocation_count;
/* If this number is reached, one allocation fails. */
volatile unsigned int failing_allocation;
/* The number of allocations performed during initialization
(before the actual getlogin_r call). */
volatile unsigned int init_allocation_count;
/* Error code of an expected getlogin_r failure. */
volatile int expected_failure;
/* The subprocess stores the expected name here. */
char name[100];
};
/* Allocation count in shared mapping. */
static struct shared_data *shared;
/* Returns true if a failure should be injected for this allocation. */
static bool
fail_this_allocation (void)
{
if (shared != NULL)
{
unsigned int count = shared->allocation_count;
shared->allocation_count = count + 1;
return count == shared->failing_allocation;
}
else
return false;
}
/* Failure-injecting wrappers for allocation functions used by glibc. */
void *
malloc (size_t size)
{
if (fail_this_allocation ())
{
errno = ENOMEM;
return NULL;
}
extern __typeof (malloc) __libc_malloc;
return __libc_malloc (size);
}
void *
calloc (size_t a, size_t b)
{
if (fail_this_allocation ())
{
errno = ENOMEM;
return NULL;
}
extern __typeof (calloc) __libc_calloc;
return __libc_calloc (a, b);
}
void *
realloc (void *ptr, size_t size)
{
if (fail_this_allocation ())
{
errno = ENOMEM;
return NULL;
}
extern __typeof (realloc) __libc_realloc;
return __libc_realloc (ptr, size);
}
/* No-op subprocess to verify that support_isolate_in_subprocess does
not perform any heap allocations. */
static void
no_op (void *ignored)
{
}
/* Perform a getlogin_r call in a subprocess, to obtain the number of
allocations used and the expected result of a successful call. */
static void
initialize (void *configure_lookup)
{
shared->init_allocation_count = 0;
if (configure_lookup != NULL)
{
TEST_COMPARE (__nss_configure_lookup ("passwd", configure_lookup), 0);
shared->init_allocation_count = shared->allocation_count;
}
shared->name[0] = '\0';
int ret = getlogin_r (shared->name, sizeof (shared->name));
if (ret != 0)
{
printf ("info: getlogin_r failed: %s (%d)\n",
strerrorname_np (ret), ret);
shared->expected_failure = ret;
}
else
{
shared->expected_failure = 0;
if (shared->name[0] == '\0')
FAIL ("error: getlogin_r succeeded without result\n");
else
printf ("info: getlogin_r: \"%s\"\n", shared->name);
}
}
/* Perform getlogin_r in a subprocess with fault injection. */
static void
test_in_subprocess (void *configure_lookup)
{
if (configure_lookup != NULL
&& __nss_configure_lookup ("passwd", configure_lookup) < 0)
{
printf ("info: __nss_configure_lookup failed: %s (%d)\n",
strerrorname_np (errno), errno);
TEST_COMPARE (errno, ENOMEM);
TEST_VERIFY (shared->allocation_count <= shared->init_allocation_count);
return;
}
unsigned int inject_at = shared->failing_allocation;
char name[sizeof (shared->name)] = "name not set";
int ret = getlogin_r (name, sizeof (name));
shared->failing_allocation = ~0U;
if (ret == 0)
{
TEST_COMPARE (shared->expected_failure, 0);
TEST_COMPARE_STRING (name, shared->name);
}
else
{
printf ("info: allocation %u failure results in error %s (%d)\n",
inject_at, strerrorname_np (ret), ret);
if (ret != ENOMEM)
{
if (shared->expected_failure != 0)
TEST_COMPARE (ret, shared->expected_failure);
else if (configure_lookup == NULL)
/* The ENOENT failure can happen due to an issue related
to bug 22041: dlopen failure does not result in ENOMEM. */
TEST_COMPARE (ret, ENOENT);
else
FAIL ("unexpected getlogin_r error");
}
}
if (shared->expected_failure == 0)
{
/* The second call should succeed. */
puts ("info: about to perform second getlogin_r call");
ret = getlogin_r (name, sizeof (name));
if (configure_lookup == NULL)
{
/* This check can fail due to bug 22041 if the malloc error
injection causes a failure internally in dlopen. */
if (ret != 0)
{
printf ("warning: second getlogin_r call failed with %s (%d)\n",
strerrorname_np (ret), ret);
TEST_COMPARE (ret, ENOENT);
}
}
else
/* If __nss_configure_lookup has been called, the error caching
bug does not happen because nss_files is built-in, and the
second getlogin_r is expected to succeed. */
TEST_COMPARE (ret, 0);
if (ret == 0)
TEST_COMPARE_STRING (name, shared->name);
}
}
/* Set by the --failing-allocation command line option. Together with
--direct, this can be used to trigger an allocation failure in the
original process, which may help with debugging. */
static int option_failing_allocation = -1;
/* Set by --override, to be used with --failing-allocation. Turns on
the __nss_configure_lookup call for passwd/files, which is disabled
by default. */
static int option_override = 0;
static int
do_test (void)
{
char files[] = "files";
if (option_failing_allocation >= 0)
{
/* The test was invoked with --failing-allocation. Perform just
one test, using the original nsswitch.conf. This is a
condensed version of the probing/testing loop below. */
printf ("info: testing with failing allocation %d\n",
option_failing_allocation);
shared = support_shared_allocate (sizeof (*shared));
shared->failing_allocation = ~0U;
char *configure_lookup = option_override ? files : NULL;
support_isolate_in_subprocess (initialize, configure_lookup);
shared->allocation_count = 0;
shared->failing_allocation = option_failing_allocation;
test_in_subprocess (configure_lookup); /* No subprocess. */
support_shared_free (shared);
shared = NULL;
return 0;
}
bool any_success = false;
for (int do_configure_lookup = 0; do_configure_lookup < 2;
++do_configure_lookup)
{
if (do_configure_lookup)
puts ("info: testing with nsswitch.conf override");
else
puts ("info: testing with original nsswitch.conf");
char *configure_lookup = do_configure_lookup ? files : NULL;
shared = support_shared_allocate (sizeof (*shared));
/* Disable fault injection. */
shared->failing_allocation = ~0U;
support_isolate_in_subprocess (no_op, NULL);
TEST_COMPARE (shared->allocation_count, 0);
support_isolate_in_subprocess (initialize, configure_lookup);
if (shared->name[0] != '\0')
any_success = true;
/* The number of allocations in the successful case. Once the
number of expected allocations is exceeded, injecting further
failures does not make a difference (assuming that the number
of malloc calls is deterministic). */
unsigned int maximum_allocation_count = shared->allocation_count;
printf ("info: initial getlogin_r performed %u allocations\n",
maximum_allocation_count);
for (unsigned int inject_at = 0; inject_at <= maximum_allocation_count;
++inject_at)
{
printf ("info: running fault injection at allocation %u\n",
inject_at);
shared->allocation_count = 0;
shared->failing_allocation = inject_at;
support_isolate_in_subprocess (test_in_subprocess, configure_lookup);
}
support_shared_free (shared);
shared = NULL;
}
{
FILE *fp = fopen (_PATH_NSSWITCH_CONF, "r");
if (fp == NULL)
printf ("info: no %s file\n", _PATH_NSSWITCH_CONF);
else
{
printf ("info: %s contents follows\n", _PATH_NSSWITCH_CONF);
int last_ch = '\n';
while (true)
{
int ch = fgetc (fp);
if (ch == EOF)
break;
putchar (ch);
last_ch = ch;
}
if (last_ch != '\n')
putchar ('\n');
printf ("(end of %s contents)\n", _PATH_NSSWITCH_CONF);
xfclose (fp);
}
}
support_record_failure_barrier ();
if (!any_success)
FAIL_UNSUPPORTED ("no successful getlogin_r calls");
return 0;
}
static void
cmdline_process (int c)
{
if (c == 'F')
option_failing_allocation = atoi (optarg);
}
#define CMDLINE_OPTIONS \
{ "failing-allocation", required_argument, NULL, 'F' }, \
{ "override", no_argument, &option_override, 1 },
#define CMDLINE_PROCESS cmdline_process
#include <support/test-driver.c>
+13
View File
@@ -326,7 +326,9 @@ tests := \
tst-wait3 \
tst-wait4 \
tst-waitid \
tst-wordexp-append \
tst-wordexp-nocmd \
tst-wordexp-reuse \
tstgetopt \
# tests
@@ -355,6 +357,7 @@ tests-internal := \
tests-container := \
bug-ga2 \
tst-vfork3 \
tst-wordexp-tilde \
# tests-container
tests-time64 := \
@@ -457,6 +460,8 @@ generated += \
tst-rxspencer-no-utf8.mtrace \
tst-vfork3-mem.out \
tst-vfork3.mtrace \
tst-wordexp-reuse-mem.out \
tst-wordexp-reuse.mtrace \
# generated
endif
endif
@@ -492,6 +497,7 @@ tests-special += \
$(objpfx)tst-pcre-mem.out \
$(objpfx)tst-rxspencer-no-utf8-mem.out \
$(objpfx)tst-vfork3-mem.out \
$(objpfx)tst-wordexp-reuse-mem.out \
# tests-special
endif
endif
@@ -775,3 +781,10 @@ $(objpfx)posix-conf-vars-def.h: $(..)scripts/gen-posix-conf-vars.awk \
$(make-target-directory)
$(AWK) -f $(filter-out Makefile, $^) > $@.tmp
mv -f $@.tmp $@
tst-wordexp-reuse-ENV += MALLOC_TRACE=$(objpfx)tst-wordexp-reuse.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
$(objpfx)tst-wordexp-reuse-mem.out: $(objpfx)tst-wordexp-reuse.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-wordexp-reuse.mtrace > $@; \
$(evaluate-test)
+1 -1
View File
@@ -50,7 +50,7 @@ __libc_fork (void)
lastrun = __run_prefork_handlers (multiple_threads);
struct nss_database_data nss_database_data;
struct nss_database_for_fork nss_database_data;
/* If we are not running multiple threads, we do not have to
preserve lock state. If fork runs from a signal handler, only
+393
View File
@@ -0,0 +1,393 @@
/* Test for wordexp with WRDE_APPEND flag.
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 <wordexp.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <support/check.h>
#include <support/support.h>
static unsigned int relocating_reallocs;
/* w_addword grows we_wordv with realloc, make every call guaranteed to
relocate the block. This makes BZ 34090 regression more deterministic. */
void *
realloc (void *ptr, size_t size)
{
if (ptr == NULL)
return malloc (size);
if (size == 0)
{
free (ptr);
return NULL;
}
void *new = malloc (size);
if (new == NULL)
return NULL;
/* Copy only what is valid in the old block to avoid reading past it. */
size_t old = malloc_usable_size (ptr);
memcpy (new, ptr, old < size ? old : size);
/* Clobber the old block so that a stale we_wordv pointer restored on the
error path reads garbage instead of the old contents, which might
otherwise survive intact and mask the bug. */
memset (ptr, 0x5a, old);
free (ptr);
relocating_reallocs++;
return new;
}
/* Verify that all words in we match the expected NULL-terminated
array. */
static void
check_words (const wordexp_t *we, const char *const *expected)
{
size_t i;
for (i = 0; expected[i] != NULL; i++)
{
TEST_VERIFY (i < we->we_wordc);
TEST_COMPARE_STRING (we->we_wordv[we->we_offs + i], expected[i]);
}
TEST_COMPARE (we->we_wordc, i);
}
#define CHECK_WORDS(we, ...) \
do { \
const char *const expected_[] = { __VA_ARGS__, NULL }; \
check_words (we, expected_); \
} while (0)
/* Test 1: WRDE_APPEND + WRDE_BADCHAR preserves we_wordc. */
static void
test_append_badchar_preserves_count (void)
{
printf ("info: test_append_badchar_preserves_count\n");
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("one two three", &we, 0), 0);
TEST_COMPARE (we.we_wordc, 3);
size_t saved_count = we.we_wordc;
/* ')' triggers WRDE_BADCHAR and "extra" would be a new word if the
expansion succeeded, exercising the w_addword path before the error
is detected. */
TEST_COMPARE (wordexp ("extra )", &we, WRDE_APPEND), WRDE_BADCHAR);
TEST_COMPARE (we.we_wordc, saved_count);
wordfree (&we);
}
/* Test 2: WRDE_APPEND + WRDE_BADCHAR preserves the we_wordv pointer even
when internal realloc would move the buffer. */
static void
test_append_badchar_preserves_pointer (void)
{
printf ("info: test_append_badchar_preserves_pointer\n");
wordexp_t we = { 0 };
/* Use many words so that the initial we_wordv allocation is
non-trivial and a later realloc is more likely to move it. */
TEST_COMPARE (wordexp ("a b c d e f g h", &we, 0), 0);
TEST_COMPARE (we.we_wordc, 8);
char **saved_wordv = we.we_wordv;
size_t saved_count = we.we_wordc;
unsigned int saved_reallocs = relocating_reallocs;
/* The interposed realloc guarantees the internal we_wordv buffer moves
during parsing, so the pointer-stability check below is meaningful. */
TEST_COMPARE (wordexp ("append )", &we, WRDE_APPEND), WRDE_BADCHAR);
/* Verify that a relocating realloc actually happened during the failed
call, otherwise the pointer-stability check is vacuous. */
TEST_VERIFY (relocating_reallocs > saved_reallocs);
TEST_COMPARE (we.we_wordc, saved_count);
TEST_VERIFY (we.we_wordv == saved_wordv);
wordfree (&we);
}
/* Test 3: After a failed WRDE_APPEND the original words are still accessible
and correct. */
static void
test_append_badchar_words_intact (void)
{
printf ("info: test_append_badchar_words_intact\n");
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("alpha beta gamma", &we, 0), 0);
CHECK_WORDS (&we, "alpha", "beta", "gamma");
TEST_COMPARE (wordexp ("delta )", &we, WRDE_APPEND), WRDE_BADCHAR);
/* Words must still be intact. */
CHECK_WORDS (&we, "alpha", "beta", "gamma");
/* The NULL terminator must still be present. */
TEST_VERIFY (we.we_wordv[we.we_offs + we.we_wordc] == NULL);
wordfree (&we);
}
/* Test 4: Successful WRDE_APPEND still works (regression test). */
static void
test_append_success (void)
{
printf ("info: test_append_success\n");
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("hello", &we, 0), 0);
TEST_COMPARE (we.we_wordc, 1);
char **saved_wordv = we.we_wordv;
TEST_COMPARE (wordexp ("world", &we, WRDE_APPEND), 0);
TEST_COMPARE (we.we_wordc, 2);
/* A successful append works on a fresh copy of the array, so the
caller-visible pointer must have changed. */
TEST_VERIFY (we.we_wordv != saved_wordv);
CHECK_WORDS (&we, "hello", "world");
wordfree (&we);
}
/* Test 5: Successful append after a failed append — the implementation must
recover and allow further use of the wordexp_t. */
static void
test_append_success_after_failure (void)
{
printf ("info: test_append_success_after_failure\n");
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("first", &we, 0), 0);
CHECK_WORDS (&we, "first");
TEST_COMPARE (wordexp ("bad |", &we, WRDE_APPEND), WRDE_BADCHAR);
/* State must be exactly as before the failed call. */
CHECK_WORDS (&we, "first");
/* A subsequent successful append must work. */
TEST_COMPARE (wordexp ("second third", &we, WRDE_APPEND), 0);
CHECK_WORDS (&we, "first", "second", "third");
wordfree (&we);
}
/* Test 6: Multiple consecutive failed appends do not corrupt state. */
static void
test_append_multiple_failures (void)
{
printf ("info: test_append_multiple_failures\n");
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("keep this", &we, 0), 0);
CHECK_WORDS (&we, "keep", "this");
size_t saved_count = we.we_wordc;
char **saved_wordv = we.we_wordv;
/* Each of these bad characters must leave the state unchanged. */
TEST_COMPARE (wordexp ("x )", &we, WRDE_APPEND), WRDE_BADCHAR);
TEST_COMPARE (wordexp ("x |", &we, WRDE_APPEND), WRDE_BADCHAR);
TEST_COMPARE (wordexp ("x ;", &we, WRDE_APPEND), WRDE_BADCHAR);
TEST_COMPARE (wordexp ("x &", &we, WRDE_APPEND), WRDE_BADCHAR);
TEST_COMPARE (wordexp ("x <", &we, WRDE_APPEND), WRDE_BADCHAR);
TEST_COMPARE (wordexp ("x >", &we, WRDE_APPEND), WRDE_BADCHAR);
TEST_COMPARE (we.we_wordc, saved_count);
TEST_VERIFY (we.we_wordv == saved_wordv);
CHECK_WORDS (&we, "keep", "this");
wordfree (&we);
}
/* Test 7: WRDE_APPEND with WRDE_SYNTAX error (unterminated quote) also
preserves state. */
static void
test_append_syntax_error (void)
{
printf ("info: test_append_syntax_error\n");
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("original", &we, 0), 0);
CHECK_WORDS (&we, "original");
char **saved_wordv = we.we_wordv;
size_t saved_count = we.we_wordc;
/* Unterminated double quote triggers WRDE_SYNTAX. */
TEST_COMPARE (wordexp ("\"unterminated", &we, WRDE_APPEND), WRDE_SYNTAX);
TEST_COMPARE (we.we_wordc, saved_count);
TEST_VERIFY (we.we_wordv == saved_wordv);
CHECK_WORDS (&we, "original");
wordfree (&we);
}
/* Test 8: Error without WRDE_APPEND still works (regression test for the
non-APPEND code path in do_error). */
static void
test_no_append_error (void)
{
printf ("info: test_no_append_error\n");
wordexp_t we = { 0 };
/* Simple failure without WRDE_APPEND. */
TEST_COMPARE (wordexp ("bad |", &we, 0), WRDE_BADCHAR);
/* After failure without WRDE_APPEND the struct should be safe to
reuse start fresh. */
TEST_COMPARE (wordexp ("ok", &we, 0), 0);
CHECK_WORDS (&we, "ok");
wordfree (&we);
}
/* Test 9: WRDE_BADCHAR on the very first character (no partial words added
before the error). */
static void
test_append_badchar_immediate (void)
{
printf ("info: test_append_badchar_immediate\n");
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("hello world", &we, 0), 0);
CHECK_WORDS (&we, "hello", "world");
char **saved_wordv = we.we_wordv;
size_t saved_count = we.we_wordc;
/* The bad character is the very first byte — no w_addword call happens
before the error. */
TEST_COMPARE (wordexp ("|", &we, WRDE_APPEND), WRDE_BADCHAR);
TEST_COMPARE (we.we_wordc, saved_count);
TEST_VERIFY (we.we_wordv == saved_wordv);
wordfree (&we);
}
/* Test 10: WRDE_APPEND into an empty wordexp_t (initial call uses WRDE_APPEND
with a zeroed struct unusual but allowed). */
static void
test_append_into_empty (void)
{
printf ("info: test_append_into_empty\n");
wordexp_t we = { 0 };
/* First call with WRDE_APPEND on a zeroed struct. The implementation
must handle we_wordv == NULL gracefully. */
TEST_COMPARE (wordexp ("solo", &we, WRDE_APPEND), 0);
TEST_COMPARE (we.we_wordc, 1);
CHECK_WORDS (&we, "solo");
wordfree (&we);
}
/* Verify that the leading we_offs slots are all NULL. */
static void
check_offs_null (const wordexp_t *we)
{
for (size_t i = 0; i < we->we_offs; i++)
TEST_VERIFY (we->we_wordv[i] == NULL);
}
/* Test 11: successful WRDE_APPEND with WRDE_DOOFFS and a non-zero we_offs.
The leading offset slots must stay NULL and words must land at
we_wordv[we_offs + i] across both the initial and the appended call. */
static void
test_dooffs_append_success (void)
{
printf ("info: test_dooffs_append_success\n");
wordexp_t we = { 0 };
we.we_offs = 2;
TEST_COMPARE (wordexp ("one two", &we, WRDE_DOOFFS), 0);
TEST_COMPARE (we.we_offs, 2);
check_offs_null (&we);
CHECK_WORDS (&we, "one", "two");
TEST_COMPARE (wordexp ("three", &we, WRDE_APPEND | WRDE_DOOFFS), 0);
TEST_COMPARE (we.we_offs, 2);
check_offs_null (&we);
CHECK_WORDS (&we, "one", "two", "three");
/* The NULL terminator must sit right after the last word. */
TEST_VERIFY (we.we_wordv[we.we_offs + we.we_wordc] == NULL);
wordfree (&we);
}
/* Test 12: failed WRDE_APPEND with WRDE_DOOFFS preserves we_wordc, the
we_wordv pointer, the words and the leading NULL offset slots. This
exercises the we_offs arithmetic in the array duplication and in the
error-path cleanup (we_wordv[we_offs + --we_wordc]). */
static void
test_dooffs_append_error_preserves_state (void)
{
printf ("info: test_dooffs_append_error_preserves_state\n");
wordexp_t we = { 0 };
we.we_offs = 3;
TEST_COMPARE (wordexp ("alpha beta", &we, WRDE_DOOFFS), 0);
check_offs_null (&we);
CHECK_WORDS (&we, "alpha", "beta");
char **saved_wordv = we.we_wordv;
size_t saved_count = we.we_wordc;
unsigned int saved_reallocs = relocating_reallocs;
/* "gamma" is a partial word added via w_addword (forcing a relocating
realloc of we_wordv) before ')' triggers WRDE_BADCHAR. */
TEST_COMPARE (wordexp ("gamma )", &we, WRDE_APPEND | WRDE_DOOFFS),
WRDE_BADCHAR);
TEST_VERIFY (relocating_reallocs > saved_reallocs);
TEST_COMPARE (we.we_offs, 3);
TEST_COMPARE (we.we_wordc, saved_count);
TEST_VERIFY (we.we_wordv == saved_wordv);
check_offs_null (&we);
CHECK_WORDS (&we, "alpha", "beta");
TEST_VERIFY (we.we_wordv[we.we_offs + we.we_wordc] == NULL);
wordfree (&we);
}
static int
do_test (void)
{
test_append_badchar_preserves_count ();
test_append_badchar_preserves_pointer ();
test_append_badchar_words_intact ();
test_append_success ();
test_append_success_after_failure ();
test_append_multiple_failures ();
test_append_syntax_error ();
test_no_append_error ();
test_append_badchar_immediate ();
test_append_into_empty ();
test_dooffs_append_success ();
test_dooffs_append_error_preserves_state ();
return 0;
}
#include <support/test-driver.c>
+89
View File
@@ -0,0 +1,89 @@
/* Test for wordexp with WRDE_REUSE flag.
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 <wordexp.h>
#include <mcheck.h>
#include <support/check.h>
static int
do_test (void)
{
mtrace ();
{
wordexp_t p = { 0 };
TEST_COMPARE (wordexp ("one", &p, 0), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[0], "one");
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[0], "two");
wordfree (&p);
}
{
wordexp_t p = { .we_offs = 2 };
TEST_COMPARE (wordexp ("one", &p, 0), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[0], "one");
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE | WRDE_DOOFFS), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two");
wordfree (&p);
}
{
wordexp_t p = { 0 };
TEST_COMPARE (wordexp ("one", &p, 0), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[0], "one");
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE | WRDE_APPEND), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[0], "two");
wordfree (&p);
}
{
wordexp_t p = { .we_offs = 2 };
TEST_COMPARE (wordexp ("one", &p, WRDE_DOOFFS), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "one");
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE
| WRDE_DOOFFS), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two");
wordfree (&p);
}
{
wordexp_t p = { .we_offs = 2 };
TEST_COMPARE (wordexp ("one", &p, WRDE_DOOFFS), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "one");
TEST_COMPARE (wordexp ("two", &p, WRDE_REUSE
| WRDE_DOOFFS | WRDE_APPEND), 0);
TEST_COMPARE (p.we_wordc, 1);
TEST_COMPARE_STRING (p.we_wordv[p.we_offs + 0], "two");
wordfree (&p);
}
return 0;
}
#include <support/test-driver.c>
+244
View File
@@ -0,0 +1,244 @@
/* Test wordexp tilde expansion with large usernames (BZ 34091).
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 <pwd.h>
#include <stdio.h>
#include <string.h>
#include <wordexp.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <support/check.h>
#include <support/support.h>
#include <support/xunistd.h>
#include <support/namespace.h>
typedef void (*func_callback_t)(void);
static void
subprocess_small_stack (void *closure)
{
struct rlimit rl;
TEST_COMPARE (getrlimit (RLIMIT_STACK, &rl), 0);
rl.rlim_cur = 512 * 1024;
TEST_COMPARE (setrlimit (RLIMIT_STACK, &rl), 0);
func_callback_t func_test = closure;
func_test ();
}
/* Build a string "~<padding>/tail" where <padding> is LEN bytes of the
character CH. The caller must free the result. */
static char *
make_tilde_input (char ch, size_t len, const char *tail)
{
/* ~ + len + / + tail + \0 */
size_t taillen = tail != NULL ? strlen (tail) : 0;
size_t total = 1 + len + 1 + taillen + 1;
char *buf = xmalloc (total);
buf[0] = '~';
memset (buf + 1, ch, len);
buf[1 + len] = '/';
if (tail != NULL)
memcpy (buf + 1 + len + 1, tail, taillen);
buf[total - 1] = '\0';
return buf;
}
/* Test 1: A very long username must not crash. The username will not match
any real user, so wordexp returns ~<long>/rest. */
static void
test_long_username (void)
{
printf ("info: test_long_username_no_crash\n");
static const char REST[] = "rest";
/* 1 MiB username — well beyond any reasonable stack frame. */
const size_t long_len = 1024 * 1024;
char *input = make_tilde_input ('A', long_len, REST);
wordexp_t we = { 0 };
int ret = wordexp (input, &we, 0);
/* The (non-existent) username is invalid, so wordexp falls back to
literal output: ~AAA/rest. */
TEST_COMPARE (ret, 0);
TEST_COMPARE (we.we_wordc, 1);
/* Verify prefix: '~' followed by long_len 'A's. */
const char *result = we.we_wordv[0];
TEST_COMPARE (result[0], '~');
TEST_COMPARE (strlen (result),
1 /* ~ */ + long_len + sizeof (REST));
for (size_t j = 1; j <= long_len; j++)
if (result[j] != 'A')
{
printf (" mismatch at position %zu: expected 'A', got '%c'\n",
j, result[j]);
support_record_failure ();
break;
}
/* Verify the tail after the username. */
TEST_COMPARE_STRING (result + 1 + long_len, "/rest");
wordfree (&we);
free (input);
}
/* Test 2: A username that just exceeds the default scratch_buffer inline
size (1024 bytes) exercises the scratch_buffer_set_array_size growth path
without being excessively large. */
static void
test_scratch_buffer_growth (void)
{
printf ("info: test_scratch_buffer_growth\n");
const size_t len = 2048;
char *input = make_tilde_input ('x', len, NULL);
wordexp_t we = { 0 };
int ret = wordexp (input, &we, 0);
TEST_COMPARE (ret, 0);
TEST_COMPARE (we.we_wordc, 1);
/* ~xxx…/ — the trailing slash makes a separate empty component, but
wordexp merges it into the single token ~xxx/. */
const char *result = we.we_wordv[0];
TEST_COMPARE (result[0], '~');
for (size_t j = 1; j <= len; j++)
if (result[j] != 'x')
{
printf (" mismatch at position %zu\n", j);
support_record_failure ();
break;
}
TEST_COMPARE (result[1 + len], '/');
wordfree (&we);
free (input);
}
/* Test 3: ~root still resolves to the correct home directory through the
__getpwnam_r path. */
static void
test_known_user (void)
{
printf ("info: test_known_user\n");
/* Look up root's home directory for comparison. */
struct passwd *pw = getpwnam ("root");
if (pw == NULL || pw->pw_dir == NULL)
{
printf (" SKIP: cannot look up root\n");
return;
}
char *expected = xasprintf ("%s/file", pw->pw_dir);
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("~root/file", &we, 0), 0);
TEST_COMPARE (we.we_wordc, 1);
TEST_COMPARE_STRING (we.we_wordv[0], expected);
wordfree (&we);
free (expected);
}
/* Test 4: Bare tilde expands to $HOME. */
static void
test_bare_tilde (void)
{
printf ("info: test_bare_tilde\n");
const char *home = getenv ("HOME");
if (home == NULL)
{
printf (" SKIP: HOME is not set\n");
return;
}
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("~", &we, 0), 0);
TEST_COMPARE (we.we_wordc, 1);
TEST_COMPARE_STRING (we.we_wordv[0], home);
wordfree (&we);
}
/* Test 5: Short non-existent username falls back to literal ~username output,
exercising the invalid-login-name path. */
static void
test_unknown_user (void)
{
printf ("info: test_unknown_user\n");
/* Pick a username that is extremely unlikely to exist. */
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("~no_such_user_xyzzy42", &we, 0), 0);
TEST_COMPARE (we.we_wordc, 1);
TEST_COMPARE_STRING (we.we_wordv[0], "~no_such_user_xyzzy42");
wordfree (&we);
}
/* Test 6: Tilde with username and WRDE_APPEND — exercises parse_tilde's
interaction with the WRDE_APPEND word list. */
static void
test_tilde_with_append (void)
{
printf ("info: test_tilde_with_append\n");
const char *home = getenv ("HOME");
if (home == NULL)
{
printf (" SKIP: HOME is not set\n");
return;
}
wordexp_t we = { 0 };
TEST_COMPARE (wordexp ("first", &we, 0), 0);
TEST_COMPARE (wordexp ("~/path", &we, WRDE_APPEND), 0);
TEST_COMPARE (we.we_wordc, 2);
TEST_COMPARE_STRING (we.we_wordv[0], "first");
char *expected = xasprintf ("%s/path", home);
TEST_COMPARE_STRING (we.we_wordv[1], expected);
wordfree (&we);
free (expected);
}
static int
do_test (void)
{
test_known_user ();
test_bare_tilde ();
test_unknown_user ();
test_tilde_with_append ();
support_isolate_in_subprocess (subprocess_small_stack,
test_long_username);
support_isolate_in_subprocess (subprocess_small_stack,
test_scratch_buffer_growth);
return 0;
}
#include <support/test-driver.c>
+1
View File
@@ -0,0 +1 @@
root:x:0:
@@ -0,0 +1,3 @@
passwd: files
group: files
shadow: files
+1
View File
@@ -0,0 +1 @@
root:x:0:0:root:/root:/bin/sh
+77 -15
View File
@@ -335,17 +335,29 @@ parse_tilde (char **word, size_t *word_length, size_t *max_length,
else
{
/* Look up user name in database to get home directory */
char *user = strndupa (&words[1 + *offset], i - (1 + *offset));
struct passwd pwd, *tpwd;
int result;
size_t userlen = i - (1 + *offset);
/* tmpbuf contains both the user and the __getpwnam_r working area. */
struct scratch_buffer tmpbuf;
scratch_buffer_init (&tmpbuf);
if (!scratch_buffer_set_array_size (&tmpbuf, userlen + 1, 1))
return WRDE_NOSPACE;
char *user = tmpbuf.data;
memcpy (user, &words[1 + *offset], userlen);
user[userlen] = '\0';
while ((result = __getpwnam_r (user, &pwd, tmpbuf.data, tmpbuf.length,
struct passwd pwd, *tpwd;
int result;
while ((result = __getpwnam_r (user,
&pwd,
tmpbuf.data + userlen + 1,
tmpbuf.length - userlen - 1,
&tpwd)) != 0
&& errno == ERANGE)
if (!scratch_buffer_grow (&tmpbuf))
return WRDE_NOSPACE;
{
if (!scratch_buffer_grow_preserve (&tmpbuf))
return WRDE_NOSPACE;
user = tmpbuf.data;
}
if (result == 0 && tpwd != NULL && pwd.pw_dir)
*word = w_addstr (*word, word_length, max_length, pwd.pw_dir);
@@ -2212,11 +2224,19 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
char ifs_white[4];
wordexp_t old_word = *pwordexp;
/* When WRDE_APPEND is set we work on a copy of the we_wordv array so that
the caller's original pointer is never invalidated by realloc inside
w_addword. The saved_wordv keeps the original; on success we free it,
on non-NOSPACE error we free the working copy and restore the original. */
char **saved_wordv = NULL;
if (flags & WRDE_REUSE)
{
/* Minimal implementation of WRDE_REUSE for now */
wordfree (pwordexp);
old_word.we_wordc = 0;
old_word.we_wordv = NULL;
pwordexp->we_wordc = 0;
}
if ((flags & WRDE_APPEND) == 0)
@@ -2244,6 +2264,21 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
pwordexp->we_offs = 0;
}
}
else if (pwordexp->we_wordv != NULL)
{
/* WRDE_APPEND with an existing word list: duplicate the array so that
realloc during parsing does not invalidate the caller's pointer. The
strings themselves are shared an the array already holds
'we_offs + we_wordc + 1 pointers' (so the size computation cannot
overflow). */
size_t num_p = pwordexp->we_offs + pwordexp->we_wordc + 1;
char **dup = malloc (num_p * sizeof (char *));
if (dup == NULL)
return WRDE_NOSPACE;
memcpy (dup, pwordexp->we_wordv, num_p * sizeof (char *));
saved_wordv = pwordexp->we_wordv;
pwordexp->we_wordv = dup;
}
/* Find out what the field separators are.
* There are two types: whitespace and non-whitespace.
@@ -2324,7 +2359,7 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
error = w_addword (pwordexp, NULL);
if (error)
return error;
goto do_error;
}
break;
@@ -2342,7 +2377,7 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
error = w_addword (pwordexp, NULL);
if (error)
return error;
goto do_error;
}
break;
@@ -2408,10 +2443,18 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
/* There was a word separator at the end */
if (word == NULL) /* i.e. w_newword */
return 0;
{
free (saved_wordv);
return 0;
}
/* There was no field separator at the end */
return w_addword (pwordexp, word);
/* There was no field separator at the end. The only possible error
from w_addword is WRDE_NOSPACE. */
error = w_addword (pwordexp, word);
if (error != 0)
goto do_error;
free (saved_wordv);
return 0;
do_error:
/* Error:
@@ -2422,11 +2465,30 @@ do_error:
free (word);
if (error == WRDE_NOSPACE)
return WRDE_NOSPACE;
{
/* we_wordc and we_wordv are updated to reflect any words that were
successfully expanded. The old array is obsolete. */
free (saved_wordv);
return WRDE_NOSPACE;
}
if ((flags & WRDE_APPEND) == 0)
wordfree (pwordexp);
if (flags & WRDE_APPEND)
{
/* POSIX 2024 states that for in other error cases, if the WRDE_APPEND
flag was specified, we_wordc and we_wordv shall not be modified.
Free strings appended during this call, discard the working copy of
we_wordv, and restore the caller's original pointer. */
while (pwordexp->we_wordc > old_word.we_wordc)
free (pwordexp->we_wordv[pwordexp->we_offs + --pwordexp->we_wordc]);
free (pwordexp->we_wordv);
pwordexp->we_wordv = saved_wordv;
}
else
{
wordfree (pwordexp);
*pwordexp = old_word;
}
*pwordexp = old_word;
return error;
}
+9
View File
@@ -98,14 +98,17 @@ tests += \
tst-ns_name \
tst-ns_name_compress \
tst-ns_name_pton \
tst-ns_sprintrr \
tst-res_hconf_reorder \
tst-res_hnok \
tst-resolv-aliases \
tst-resolv-basic \
tst-resolv-binary \
tst-resolv-byaddr \
tst-resolv-dns-section \
tst-resolv-edns \
tst-resolv-invalid-cname \
tst-resolv-invalid-ptr \
tst-resolv-network \
tst-resolv-noaaaa \
tst-resolv-noaaaa-vc \
@@ -115,6 +118,7 @@ tests += \
tst-resolv-semi-failure \
tst-resolv-short-response \
tst-resolv-trailing \
# tests
# This test calls __res_context_send directly, which is not exported
# from libresolv.
@@ -293,6 +297,8 @@ $(objpfx)tst-resolv-aliases: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-binary: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-byaddr: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-dns-section: $(objpfx)libresolv.so \
$(shared-thread-library)
$(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-res_init: $(objpfx)libresolv.so
@@ -302,6 +308,8 @@ $(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \
$(shared-thread-library)
$(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \
$(shared-thread-library)
$(objpfx)tst-resolv-invalid-ptr: $(objpfx)libresolv.so \
$(shared-thread-library)
$(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
@@ -324,5 +332,6 @@ $(objpfx)tst-ns_name: $(objpfx)libresolv.so
$(objpfx)tst-ns_name.out: tst-ns_name.data
$(objpfx)tst-ns_name_compress: $(objpfx)libresolv.so
$(objpfx)tst-ns_name_pton: $(objpfx)libresolv.so
$(objpfx)tst-ns_sprintrr: $(objpfx)libresolv.so
$(objpfx)tst-res_hnok: $(objpfx)libresolv.so
$(objpfx)tst-p_secstodate: $(objpfx)libresolv.so
+53 -117
View File
@@ -78,6 +78,24 @@ ns_sprintrr(const ns_msg *handle, const ns_rr *rr,
}
libresolv_hidden_def (ns_sprintrr)
/* Writes the class/type symbol NUMBER to *BUF, using the name from
*SYMS if possible. If NUMBER is not found in *SYMS, print the
number with PREFIX. */
static int
addsym (const struct res_sym *syms, int number, const char *prefix,
char **buf, size_t *buflen)
{
for (; syms->name != NULL; syms++)
if (number == syms->number)
{
T (addstr (" ", 1, buf, buflen));
return addstr (syms->name, strlen (syms->name), buf, buflen);
}
char tmp[20];
int len = snprintf (tmp, sizeof (tmp), " %s%d", prefix, number);
return addstr (tmp, len, buf, buflen);
}
/*%
* Convert the fields of an RR into presentation format.
*
@@ -97,7 +115,6 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
const char *comment;
char tmp[100];
char errbuf[40];
int len, x;
/*
@@ -128,11 +145,21 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
/*
* TTL, Class, Type.
*/
T(x = ns_format_ttl(ttl, buf, buflen));
addlen(x, &buf, &buflen);
len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type)));
T(addstr(tmp, len, &buf, &buflen));
T(spaced = addtab(x + len, 16, spaced, &buf, &buflen));
{
char *start = buf;
T (x = ns_format_ttl (ttl, buf, buflen));
addlen (x, &buf, &buflen);
T (addsym (__p_class_syms, class, "CLASS", &buf, &buflen));
if (type == ns_t_a6)
/* A6 is not part of __p_type_syms, which is exported.
Adding A6 there would change its size. Handle it here. */
T (addstr (" A6", 3, &buf, &buflen));
else
T (addsym (__p_type_syms, type, "TYPE", &buf, &buflen));
T (spaced = addtab(buf - start, 16, spaced, &buf, &buflen));
}
/*
* RData.
@@ -140,8 +167,9 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
switch (type) {
case ns_t_a:
if (rdlen != (size_t)NS_INADDRSZ)
goto formerr;
(void) inet_ntop(AF_INET, rdata, buf, buflen);
goto formerr;
if (inet_ntop (AF_INET, rdata, buf, buflen) == NULL)
return -1;
addlen(strlen(buf), &buf, &buflen);
break;
@@ -307,16 +335,18 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
}
case ns_t_aaaa:
if (rdlen != (size_t)NS_IN6ADDRSZ)
goto formerr;
(void) inet_ntop(AF_INET6, rdata, buf, buflen);
if (rdlen != (size_t)NS_IN6ADDRSZ)
goto formerr;
if (inet_ntop (AF_INET6, rdata, buf, buflen) == NULL)
return -1;
addlen(strlen(buf), &buf, &buflen);
break;
case ns_t_loc: {
char t[255];
/* XXX protocol format checking? */
if (rdlen != 16)
goto formerr;
(void) loc_ntoa(rdata, t);
T(addstr(t, strlen(t), &buf, &buflen));
break;
@@ -400,7 +430,8 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
goto formerr;
/* Address. */
(void) inet_ntop(AF_INET, rdata, buf, buflen);
if (inet_ntop (AF_INET, rdata, buf, buflen) == NULL)
return -1;
addlen(strlen(buf), &buf, &buflen);
rdata += NS_INADDRSZ;
@@ -434,96 +465,6 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
break;
}
case ns_t_cert: {
u_int c_type, key_tag, alg;
int n;
unsigned int siz;
char base64_cert[8192], tmp[40];
const char *leader;
c_type = ns_get16(rdata); rdata += NS_INT16SZ;
key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
alg = (u_int) *rdata++;
len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg));
T(addstr(tmp, len, &buf, &buflen));
siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
if (siz > sizeof(base64_cert) * 3/4) {
const char *str = "record too long to print";
T(addstr(str, strlen(str), &buf, &buflen));
}
else {
len = b64_ntop(rdata, edata-rdata, base64_cert, siz);
if (len < 0)
goto formerr;
else if (len > 15) {
T(addstr(" (", 2, &buf, &buflen));
leader = "\n\t\t";
spaced = 0;
}
else
leader = " ";
for (n = 0; n < len; n += 48) {
T(addstr(leader, strlen(leader),
&buf, &buflen));
T(addstr(base64_cert + n, MIN(len - n, 48),
&buf, &buflen));
}
if (len > 15)
T(addstr(" )", 2, &buf, &buflen));
}
break;
}
case ns_t_tkey: {
/* KJD - need to complete this */
u_long t;
int mode, err, keysize;
/* Algorithm name. */
T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
T(addstr(" ", 1, &buf, &buflen));
/* Inception. */
t = ns_get32(rdata); rdata += NS_INT32SZ;
len = SPRINTF((tmp, "%lu ", t));
T(addstr(tmp, len, &buf, &buflen));
/* Expiration. */
t = ns_get32(rdata); rdata += NS_INT32SZ;
len = SPRINTF((tmp, "%lu ", t));
T(addstr(tmp, len, &buf, &buflen));
/* Mode , Error, Key Size. */
/* Priority, Weight, Port. */
mode = ns_get16(rdata); rdata += NS_INT16SZ;
err = ns_get16(rdata); rdata += NS_INT16SZ;
keysize = ns_get16(rdata); rdata += NS_INT16SZ;
len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize));
T(addstr(tmp, len, &buf, &buflen));
/* XXX need to dump key, print otherdata length & other data */
break;
}
case ns_t_tsig: {
/* BEW - need to complete this */
int n;
T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
T(addstr(" ", 1, &buf, &buflen));
rdata += 8; /*%< time */
n = ns_get16(rdata); rdata += INT16SZ;
rdata += n; /*%< sig */
n = ns_get16(rdata); rdata += INT16SZ; /*%< original id */
sprintf(buf, "%d", ns_get16(rdata));
rdata += INT16SZ;
addlen(strlen(buf), &buf, &buflen);
break;
}
case ns_t_a6: {
struct in6_addr a;
int pbyte, pbit;
@@ -539,12 +480,14 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
/* address suffix: provided only when prefix len != 128 */
if (pbit < 128) {
if (rdata + pbyte >= edata) goto formerr;
unsigned int bytelen = sizeof(a) - pbyte;
if (edata - rdata < bytelen) goto formerr;
memset(&a, 0, sizeof(a));
memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte);
(void) inet_ntop(AF_INET6, &a, buf, buflen);
memcpy(&a.s6_addr[pbyte], rdata, bytelen);
if (inet_ntop (AF_INET6, &a, buf, buflen) == NULL)
return -1;
addlen(strlen(buf), &buf, &buflen);
rdata += sizeof(a) - pbyte;
rdata += bytelen;
}
/* prefix name: provided only when prefix len > 0 */
@@ -557,25 +500,18 @@ ns_sprintrrf(const u_char *msg, size_t msglen,
break;
}
case ns_t_opt: {
len = SPRINTF((tmp, "%u bytes", class));
T(addstr(tmp, len, &buf, &buflen));
break;
}
default:
snprintf (errbuf, sizeof (errbuf), "unknown RR type %d", type);
comment = errbuf;
comment = "";
goto hexify;
}
return (buf - obuf);
formerr:
comment = "RR format error";
comment = " ; RR format error";
hexify: {
int n, m;
char *p;
len = SPRINTF((tmp, "\\# %u%s\t; %s", (unsigned)(edata - rdata),
len = SPRINTF((tmp, "\\# %u%s%s", (unsigned)(edata - rdata),
rdlen != 0U ? " (" : "", comment));
T(addstr(tmp, len, &buf, &buflen));
while (rdata < edata) {
+2 -2
View File
@@ -820,7 +820,7 @@ getanswer_ptr (unsigned char *packet, size_t packetlen,
/* expected_name may be updated to point into this buffer. */
unsigned char name_buffer[NS_MAXCDNAME];
while (ancount > 0)
for (; ancount > 0; --ancount)
{
struct ns_rr_wire rr;
if (!__ns_rr_cursor_next (&c, &rr))
@@ -866,7 +866,7 @@ getanswer_ptr (unsigned char *packet, size_t packetlen,
char hname[MAXHOSTNAMELEN + 1];
if (__ns_name_unpack (c.begin, c.end, rr.rdata,
name_buffer, sizeof (name_buffer)) < 0
|| !__res_binary_hnok (expected_name)
|| !__res_binary_hnok (name_buffer)
|| __ns_name_ntop (name_buffer, hname, sizeof (hname)) < 0)
{
*h_errnop = NO_RECOVERY;
+4
View File
@@ -207,6 +207,10 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
sprintf (qbuf, "%u.%u.%u.%u.in-addr.arpa", net_bytes[3], net_bytes[2],
net_bytes[1], net_bytes[0]);
break;
default:
/* Default network (net is originally zero). */
strcpy (qbuf, "0.0.0.0.in-addr.arpa");
break;
}
net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
-4
View File
@@ -390,8 +390,6 @@ p_fqname(const u_char *cp, const u_char *msg, FILE *file) {
* that C_ANY is a qclass but not a class. (You can ask for records of class
* C_ANY, but you can't have any records of that class in the database.)
*/
extern const struct res_sym __p_class_syms[];
libresolv_hidden_proto (__p_class_syms)
const struct res_sym __p_class_syms[] = {
{C_IN, (char *) "IN"},
{C_CHAOS, (char *) "CHAOS"},
@@ -426,8 +424,6 @@ const struct res_sym __p_update_section_syms[] attribute_hidden = {
* Names of RR types and qtypes. The list is incomplete because its
* size is part of the ABI.
*/
extern const struct res_sym __p_type_syms[];
libresolv_hidden_proto (__p_type_syms)
const struct res_sym __p_type_syms[] = {
{ns_t_a, (char *) "A", (char *) "address"},
{ns_t_ns, (char *) "NS", (char *) "name server"},
+329
View File
@@ -0,0 +1,329 @@
/* Tests for the ns_sprintrr function.
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 <arpa/nameser.h>
#include <alloc_buffer.h>
#include <arpa/inet.h>
#include <libc-diag.h>
#include <stdbool.h>
#include <string.h>
#include <support/check.h>
#include <support/next_to_fault.h>
#include <stdio.h>
/* Regions that test_one_record uses for input and output. */
static struct support_next_to_fault ntf_in;
static struct support_next_to_fault ntf_out;
/* This is used by test_one_record to construct the packet. */
static const char packet_prefix[] =
/* DNS response with one question, one answer record. */
"AA\x81\x80\0\1\0\1\0\0\0\0"
/* Question: www.example.org/IN/ANY. */
"\3www\7example\3org\0\0\xff\0\1"
/* Response: compression reference. */
"\xc0\x0c";
/* Use ns_sprintrr to format a DNS record (starting with
packet_prefix) of type RTYPE, with a record payload of RDATALEN
bytes starting at RDATA. Check successful formatting against
EXPECTED. Try various truncated input and output buffers to catch
overreads and buffer overflows, using ntf_in and ntf_out above. */
static void
test_one_record (uint16_t rtype, const char *rdata, size_t rdatalen,
const char *expected)
{
struct rr_header
{
uint16_t typ;
uint16_t cls;
uint32_t ttl;
uint16_t rdatalen;
uint16_t pad;
} hdr =
{
.typ = htons (rtype),
.cls = htons (ns_c_in),
.ttl = htonl (86400), /* One day. */
.rdatalen = htons (rdatalen),
};
enum { hdrlen = offsetof (struct rr_header, pad) };
TEST_COMPARE (hdrlen, 10);
/* Construct the packet from packet_prefix, hdr, and rdata. */
unsigned char packet[512];
size_t packetlen;
{
struct alloc_buffer buf = alloc_buffer_create (packet, sizeof (packet));
alloc_buffer_copy_bytes (&buf, packet_prefix, sizeof (packet_prefix) - 1);
alloc_buffer_copy_bytes (&buf, &hdr, hdrlen);
alloc_buffer_copy_bytes (&buf, rdata, rdatalen);
packetlen = sizeof (packet) - alloc_buffer_size (&buf);
}
/* Parse the record. */
ns_msg msg;
TEST_COMPARE (ns_initparse (packet, packetlen, &msg), 0);
ns_rr rr;
TEST_COMPARE (ns_parserr (&msg, ns_s_an, 0, &rr), 0);
/* Try sizes up to this limit. Go a bit beyond the expected size to
check for errors. */
size_t max_result_size = strlen (expected) + 16;
bool success = false;
for (size_t result_size = 1; result_size <= max_result_size; ++result_size)
{
char *result_start = ntf_out.buffer + ntf_out.length - result_size;
memset (result_start, 'X', result_size);
/* ns_sprintrr was deprecated in 2.34. */
DIAG_PUSH_NEEDS_COMMENT;
DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
int ret = ns_sprintrr (&msg, &rr, NULL, NULL, result_start, result_size);
DIAG_POP_NEEDS_COMMENT;
if (ret > 0)
{
TEST_COMPARE_STRING (result_start, expected);
TEST_COMPARE (ret, strlen (expected));
success = true;
}
else
{
TEST_VERIFY (!success);
TEST_COMPARE (ret, -1);
}
}
TEST_VERIFY (success);
/* Test with truncated RDATA. */
for (size_t rdata_size = 0; rdata_size <= rdatalen; ++rdata_size)
{
size_t truncated_packet_size = packetlen - rdatalen + rdata_size;
unsigned char *packet_start
= ((unsigned char *) ntf_in.buffer + ntf_in.length
- truncated_packet_size);
memcpy (packet_start, packet, truncated_packet_size);
/* Patch in the updated RDATA length field. */
uint16_t new_rdatalen = htons (rdata_size);
memcpy (packet_start + truncated_packet_size - rdata_size - 2,
&new_rdatalen, 2);
ns_msg msg;
TEST_COMPARE (ns_initparse (packet_start, truncated_packet_size, &msg),
0);
ns_rr rr;
TEST_COMPARE (ns_parserr (&msg, ns_s_an, 0, &rr), 0);
size_t result_size = strlen (expected) + 1;
char *result_start = ntf_out.buffer + ntf_out.length - result_size;
memset (result_start, 'X', result_size);
/* ns_sprintrr was deprecated in 2.34. */
DIAG_PUSH_NEEDS_COMMENT;
DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wdeprecated-declarations");
int ret = ns_sprintrr (&msg, &rr, NULL, NULL, result_start, result_size);
DIAG_POP_NEEDS_COMMENT;
/* This flag indicates whether the output is syntactically
correct. In some cases, truncation may still yield a valid
payload. */
bool broken = rdata_size < rdatalen;
switch (rtype)
{
case ns_t_wks:
/* WKS records use all trailing bytes for the port bitmap. */
broken = rdata_size < 5;
break;
case ns_t_nsap:
/* Uses all bytes that are available. */
broken = false;
break;
case ns_t_txt:
/* Truncation produces a valid payload if it occurs right
after a complete string in the TXT payload. */
broken = false;
for (size_t pos = 0; pos < rdata_size; )
{
unsigned int slen = rdata[pos] & 0xff;
if (pos + 1 + slen > rdata_size)
{
broken = true;
break;
}
pos += 1 + slen;
}
break;
case ns_t_isdn:
/* The second field is optional. If it is present, it must
not be truncated. */
broken = rdata_size < 6 || (rdata_size > 6 && rdata_size < rdatalen);
break;
case ns_t_a6:
/* The first A6 subtest contains a trailing domain name,
which is ignored and not formatted. */
if (rdata_size > 0 && rdata[0] == 0)
broken = rdata_size < 17;
break;
case ns_t_cert:
case ns_t_tkey:
case ns_t_tsig:
/* Only generic printing, which does not validate anything. */
broken = false;
break;
}
if (broken)
{
if (strstr (result_start, "RR format error") != NULL)
/* No further checks if an error indicator has been added
to the output. */
;
else
TEST_COMPARE (ret, -1);
}
else
TEST_VERIFY (ret > 0);
}
}
static int
do_test (void)
{
ntf_in = support_next_to_fault_allocate (512);
ntf_out = support_next_to_fault_allocate (256);
#define T(rtype, rdata, expected) \
test_one_record (rtype, rdata, sizeof (rdata) - 1, expected)
T (ns_t_a, "\xc0\0\2\1", "www.example.org.\t1D IN A\t\t192.0.2.1");
T (ns_t_cname, "\4www1\4prod\xc0\x10",
"www.example.org.\t1D IN CNAME\twww1.prod.example.org.");
T (ns_t_hinfo, "\5first\6second",
"www.example.org.\t1D IN HINFO\t\"first\" \"second\"");
T (ns_t_isdn, "\5first\6second",
"www.example.org.\t1D IN ISDN\t\"first\" \"second\"");
/* Bug: Extra space at the end in the text representation of ISDN RRs. */
T (ns_t_isdn, "\5first", "www.example.org.\t1D IN ISDN\t\"first\" ");
T (ns_t_soa,
"\2ns\xc0\x10\12hostmaster\xc0\x10"
"\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\4\0\0\0\5",
"www.example.org.\t1D IN SOA\tns.example.org. hostmaster.example.org. (\n"
"\t\t\t\t\t1\t\t; serial\n"
"\t\t\t\t\t2S\t\t; refresh\n"
"\t\t\t\t\t3S\t\t; retry\n"
"\t\t\t\t\t4S\t\t; expiry\n"
"\t\t\t\t\t5S )\t\t; minimum\n");
T (ns_t_mx, "\0\xa\2mx\xc0\x10",
"www.example.org.\t1D IN MX\t10 mx.example.org.");
T (ns_t_px, "\0\xa\3px1\xc0\x10\3px2\xc0\x10",
"www.example.org.\t1D IN PX\t10 px1.example.org. px2.example.org.");
T (ns_t_x25, "\4X.25",
"www.example.org.\t1D IN X25\t\"X.25\"");
T (ns_t_txt, "\1A\2BC\3DEF",
"www.example.org.\t1D IN TXT\t\"A\" \"BC\" \"DEF\"");
T (ns_t_nsap, "",
"www.example.org.\t1D IN NSAP\t");
T (ns_t_nsap, "\1",
"www.example.org.\t1D IN NSAP\t01");
T (ns_t_nsap, "\1\2",
"www.example.org.\t1D IN NSAP\t01.02");
T (ns_t_nsap, "\1\2\3",
"www.example.org.\t1D IN NSAP\t01.0203");
T (ns_t_nsap, "\1\2\3\4",
"www.example.org.\t1D IN NSAP\t01.0203.04");
T (ns_t_nsap,
"\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32"
"\33\34\35\36\37\40\41\42\43\44\45\46\47\50\51\52\53\54\55\56\57\60\61"
"\62\63\64\65\66\67\70\71\72\73\74\75\76\77\100\101\102\103\104\105\106"
"\107\110\111\112\113\114\115\116\117\120\121\122\123\124\125\126\127"
"\130\131\132\133\134\135\136\137\140\141\142\143\144\145\146\147\150"
"\151\152\153\154\155\156\157\160\161\162\163\164\165\166\167\170\171"
"\172\173\174\175\176\177\200\201\202\203\204\205\206\207\210\211\212"
"\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233"
"\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254"
"\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275"
"\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316"
"\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360"
"\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377",
"www.example.org.\t1D IN NSAP\t"
"01.0203.0405.0607.0809.0A0B.0C0D.0E0F.1011.1213.1415.1617.1819.1A1B"
".1C1D.1E1F.2021.2223.2425.2627.2829.2A2B.2C2D.2E2F.3031.3233.3435.3637"
".3839.3A3B.3C3D.3E3F.4041.4243.4445.4647.4849.4A4B.4C4D.4E4F.5051.5253"
".5455.5657.5859.5A5B.5C5D.5E5F.6061.6263.6465.6667.6869.6A6B.6C6D.6E6F"
".7071.7273.7475.7677.7879.7A7B.7C7D.7E7F.8081.8283.8485.8687.8889.8A8B"
".8C8D.8E8F.9091.9293.9495.9697.9899.9A9B.9C9D.9E9F.A0A1.A2A3.A4A5.A6A7"
".A8A9.AAAB.ACAD.AEAF.B0B1.B2B3.B4B5.B6B7.B8B9.BABB.BCBD.BEBF.C0C1.C2C3"
".C4C5.C6C7.C8C9.CACB.CCCD.CECF.D0D1.D2D3.D4D5.D6D7.D8D9.DADB.DCDD.DEDF"
".E0E1.E2E3.E4E5.E6E7.E8E9.EAEB.ECED.EEEF.F0F1.F2F3.F4F5.F6F7.F8F9.FAFB"
".FCFD.FEFF");
T (ns_t_aaaa, "\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\x12\x34",
"www.example.org.\t1D IN AAAA\t2001:db8::1234");
/* Example from RFC 1876. The loc_ntoa format is different from the
official text representation. */
T (ns_t_loc,
"\000\063\026\023\211\027\055\320\160\276\025\360\000\230\215\040",
"www.example.org.\t1D IN LOC"
"\t42 21 54.000 N 71 06 18.000 W -24.00m 30.00m 10000.00m 10.00m");
T (ns_t_naptr,
"\0\1\0\2\5flags\7service\2.*\5naptr\xc0\x10",
"www.example.org.\t1D IN NAPTR\t1 2 \"flags\" \"service\" \".*\""
" naptr.example.org.");
T (ns_t_srv,
"\0\1\0\2\0\x50\4www1\xc0\x10",
"www.example.org.\t1D IN SRV\t1 2 80 www1.example.org.");
T (ns_t_rp, "\3rp1\xc0\x10\3rp2\xc0\x10",
"www.example.org.\t1D IN RP\trp1.example.org. rp2.example.org.");
T (ns_t_wks, "\xc0\0\2\1\6\0\0\0\0\0\0\0\0\0\0\200",
"www.example.org.\t1D IN WKS\t192.0.2.1 6 ( \n\t\t\t\t80 )");
T (ns_t_cert, "\0\1\x04\xd2\0blob",
"www.example.org.\t1D IN CERT\t\\# 9 (\n"
"\t00 01 04 d2 00 62 6c 6f 62 )\t\t\t; .....blob");
T (ns_t_tkey, "\4algo\0\0\0\0\1\0\0\0\2\0\3\0\4"
"\0\5\xa1\xa2\xa3\xa4\xa5\0\3\xb1\xb2\xb3",
"www.example.org.\t1D IN TYPE249\t\\# 30 (\n"
"\t04 61 6c 67 6f 00 00 00 00 01 00 00 00 02 00 03 ; .algo...........\n"
"\t00 04 00 05 a1 a2 a3 a4 a5 00 03 b1 b2 b3 )\t; ..............");
T (ns_t_tsig, "\4algo\0"
"\0\20\xdd\xcd\x64\x10\xe9\x21\x34\x1a\x8e\xe0\xa1\x9a\x30\xfc\x3b\xd1"
"\0\2\0\3\0\5other",
"www.example.org.\t1D IN TSIG\t\\# 35 (\n"
"\t04 61 6c 67 6f 00 00 10 dd cd 64 10 e9 21 34 1a ; .algo.....d..!4.\n"
"\t8e e0 a1 9a 30 fc 3b d1 00 02 00 03 00 05 6f 74 ; ....0.;.......ot\n"
"\t68 65 72 )\t\t\t\t\t; her");
T (ns_t_a6,
"\0\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\x12\x34\6prefix\xc0\x10",
"www.example.org.\t1D IN A6\t0 2001:db8::1234");
T (ns_t_a6,
"\0\x20\x01\x0d\xb8\0\0\0\0\0\0\0\0\0\0\x12\x35",
"www.example.org.\t1D IN A6\t0 2001:db8::1235");
T (ns_t_a6, "\200\6prefix\xc0\x10",
"www.example.org.\t1D IN A6\t128 prefix.example.org.");
T (ns_t_a6, "\x20\0\0\0\0\0\0\0\0\0\0\x12\x36\6prefix\xc0\x10",
"www.example.org.\t1D IN A6\t32 ::1236 prefix.example.org.");
#undef T
support_next_to_fault_free (&ntf_in);
support_next_to_fault_free (&ntf_out);
return 0;
}
#include <support/test-driver.c>
+162
View File
@@ -0,0 +1,162 @@
/* Test handling of invalid section transitions (bug 34014).
Copyright (C) 2022-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 <array_length.h>
#include <errno.h>
#include <netdb.h>
#include <resolv.h>
#include <stdlib.h>
#include <string.h>
#include <support/check.h>
#include <support/format_nss.h>
#include <support/resolv_test.h>
#include <support/support.h>
/* Name of test, and the second section type. */
struct item {
const char *test;
int ns_section;
};
static const struct item test_items[] =
{
{ "Test crossing from ns_s_an to ns_s_ar.", ns_s_ar },
{ "Test crossing from ns_s_an to ns_s_an.", ns_s_ns },
{ NULL, 0 },
};
/* The response is designed to contain the following:
- An Answer section with one T_PTR record that is skipped.
- A second section with a semantically invalid T_PTR record.
The original defect is that the response parsing would cross
section boundaries and handle the additional section T_PTR
as if it were an answer. A conforming implementation would
stop as soon as it reaches the end of the section. */
static void
response (const struct resolv_response_context *ctx,
struct resolv_response_builder *b,
const char *qname, uint16_t qclass, uint16_t qtype)
{
TEST_COMPARE (qclass, C_IN);
/* We only test PTR. */
TEST_COMPARE (qtype, T_PTR);
unsigned int count;
char *tail = NULL;
if (strstr (qname, "in-addr.arpa") != NULL
&& sscanf (qname, "%u.%ms", &count, &tail) == 2)
TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa");
else if (sscanf (qname, "%x.%ms", &count, &tail) == 2)
{
TEST_COMPARE_STRING (tail, "\
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
}
else
FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
free (tail);
/* We have a bounded number of possible tests. */
TEST_VERIFY (count >= 0);
TEST_VERIFY (count <= 15);
struct resolv_response_flags flags = {};
resolv_response_init (b, flags);
resolv_response_add_question (b, qname, qclass, qtype);
resolv_response_section (b, ns_s_an);
/* Actual answer record, but the wrong name (skipped). */
resolv_response_open_record (b, "1.0.0.10.in-addr.arpa", qclass, qtype, 60);
/* Record the answer. */
resolv_response_add_name (b, "test.ptr.example.net");
resolv_response_close_record (b);
/* Add a second section to test section boundary crossing. */
resolv_response_section (b, test_items[count].ns_section);
/* Semantically incorrect, but hide a T_PTR entry. */
resolv_response_open_record (b, qname, qclass, qtype, 60);
resolv_response_add_name (b, "wrong.ptr.example.net");
resolv_response_close_record (b);
}
/* Perform one check using a reverse lookup. */
static void
check_reverse (int af, int count)
{
TEST_VERIFY (af == AF_INET || af == AF_INET6);
TEST_VERIFY (count < array_length (test_items));
char addr[sizeof (struct in6_addr)] = { 0 };
socklen_t addrlen;
if (af == AF_INET)
{
addr[0] = (char) 192;
addr[1] = (char) 168;
addr[2] = (char) 0;
addr[3] = (char) count;
addrlen = 4;
}
else
{
addr[0] = 0x20;
addr[1] = 0x01;
addr[2] = 0x0d;
addr[3] = 0xb8;
addr[4] = addr[5] = addr[6] = addr[7] = 0x0;
addr[8] = addr[9] = addr[10] = addr[11] = 0x0;
addr[12] = 0x0;
addr[13] = 0x0;
addr[14] = 0x0;
addr[15] = count;
addrlen = 16;
}
h_errno = 0;
struct hostent *answer = gethostbyaddr (addr, addrlen, af);
TEST_VERIFY (answer == NULL);
TEST_VERIFY (h_errno == NO_RECOVERY);
if (answer != NULL)
printf ("error: unexpected success: %s\n",
support_format_hostent (answer));
}
static int
do_test (void)
{
struct resolv_test *obj = resolv_test_start
((struct resolv_redirect_config)
{
.response_callback = response
});
for (int i = 0; test_items[i].test != NULL; i++)
{
check_reverse (AF_INET, i);
check_reverse (AF_INET6, i);
}
resolv_test_end (obj);
return 0;
}
#include <support/test-driver.c>
+255
View File
@@ -0,0 +1,255 @@
/* Test handling of invalid T_PTR results (bug 34015).
Copyright (C) 2022-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 <array_length.h>
#include <errno.h>
#include <netdb.h>
#include <resolv.h>
#include <stdlib.h>
#include <string.h>
#include <support/check.h>
#include <support/format_nss.h>
#include <support/resolv_test.h>
#include <support/support.h>
/* Name of test, the answer, the expected error return, and if we
expect the call to fail. */
struct item {
const char *test;
const char *answer;
int expected;
bool fail;
};
static const struct item test_items[] =
{
/* Test for invalid characters. */
{ "Invalid use of \"|\"",
"test.|.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"&\"",
"test.&.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \";\"",
"test.;.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"<\"",
"test.<.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \">\"",
"test.>.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"(\"",
"test.(.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \")\"",
"test.).ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"$\"",
"test.$.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"`\"",
"test.`.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"\\\"",
"test.\\.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"\'\"",
"test.'.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"\"\"",
"test.\".ptr.example", NO_RECOVERY, true },
{ "Invalid use of \" \"",
"test. .ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"\\t\"",
"test.\t.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"\\n\"",
"test.\n.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"\\r\"",
"test.\r.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"*\"",
"test.*.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"?\"",
"test.?.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"[\"",
"test.[.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"]\"",
"test.].ptr.example", NO_RECOVERY, true },
{ "Invalid use of \",\"",
"test.,.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"~\"",
"test.~.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \":\"",
"test.:.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"!\"",
"test.!.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"@\"",
"test.@.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"#\"",
"test.#.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"%\"",
"test.%%.ptr.example", NO_RECOVERY, true },
{ "Invalid use of \"^\"",
"test.^.ptr.example", NO_RECOVERY, true },
/* Test for invalid UTF-8 characters (2-byte, 4-byte, 6-byte). */
{ "Invalid use of UTF-8 (2-byte, U+00C0-U+00C2)",
"ÁÂÃ.test.ptr.example", NO_RECOVERY, true },
{ "Invalid use of UTF-8 (4-byte, U+0750-U+0752)",
"ݐݑݒ.test.ptr.example", NO_RECOVERY, true },
{ "Invalid use of UTF-8 (6-byte, U+0904-U+0906)",
"ऄअआ.test.ptr.example", NO_RECOVERY, true },
/* Test for "-" which may be valid depending on position. */
{ "Invalid leading \"-\"",
"-test.ptr.example", NO_RECOVERY, true },
{ "Valid trailing \"-\"",
"test-.ptr.example", 0, false },
{ "Valid mid-label use of \"-\"",
"te-st.ptr.example", 0, false },
/* Test for "_" which is always valid in any position. */
{ "Valid leading use of \"_\"",
"_test.ptr.example", 0, false },
{ "Valid mid-label use of \"_\"",
"te_st.ptr.example", 0, false },
{ "Valid trailing use of \"_\"",
"test_.ptr.example", 0, false },
/* Sanity test the broader set [A-Za-z0-9_-] of valid characters. */
{ "Valid \"[A-Z]\"",
"test.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ptr.example", 0, false },
{ "Valid \"[a-z]\"",
"test.abcdefghijklmnopqrstuvwxyz.ptr.example", 0, false },
{ "Valid \"[0-9]\"",
"test.0123456789.ptr.example", 0, false },
{ "Valid mixed use of \"[A-Za-z0-9_-]\"",
"test.012abcABZ_-.ptr.example", 0, false },
};
static void
response (const struct resolv_response_context *ctx,
struct resolv_response_builder *b,
const char *qname, uint16_t qclass, uint16_t qtype)
{
TEST_COMPARE (qclass, C_IN);
/* We only test PTR. */
TEST_COMPARE (qtype, T_PTR);
unsigned int count, count1;
char *tail = NULL;
/* The test implementation can handle up to 255 tests. */
if (strstr (qname, "in-addr.arpa") != NULL
&& sscanf (qname, "%u.%ms", &count, &tail) == 2)
TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa");
else if (sscanf (qname, "%x.%x.%ms", &count, &count1, &tail) == 3)
{
TEST_COMPARE_STRING (tail, "\
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
count |= count1 << 4;
}
else
FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
free (tail);
/* Cross check. Count has a fixed bound (soft limit). */
TEST_VERIFY (count >= 0 && count <= 255);
/* We have a fixed number of tests (hard limit). */
TEST_VERIFY_EXIT (count < array_length (test_items));
struct resolv_response_flags flags = {};
resolv_response_init (b, flags);
resolv_response_add_question (b, qname, qclass, qtype);
resolv_response_section (b, ns_s_an);
/* Actual answer record. */
resolv_response_open_record (b, qname, qclass, qtype, 60);
/* Record the answer. */
resolv_response_add_name (b, test_items[count].answer);
resolv_response_close_record (b);
}
/* Perform one check using a reverse lookup. */
static void
check_reverse (int af, int count)
{
TEST_VERIFY (af == AF_INET || af == AF_INET6);
TEST_VERIFY_EXIT (count < array_length (test_items));
/* Generate an address to query for each test. */
char addr[sizeof (struct in6_addr)] = { 0 };
socklen_t addrlen;
if (af == AF_INET)
{
addr[0] = (char) 192;
addr[1] = (char) 168;
addr[2] = (char) 0;
addr[3] = (char) count;
addrlen = 4;
}
else
{
addr[0] = 0x20;
addr[1] = 0x01;
addr[2] = 0x0d;
addr[3] = 0xb8;
addr[4] = addr[5] = addr[6] = addr[7] = 0x0;
addr[8] = addr[9] = addr[10] = addr[11] = 0x0;
addr[12] = 0x0;
addr[13] = 0x0;
addr[14] = 0x0;
addr[15] = (char) count;
addrlen = 16;
}
h_errno = 0;
struct hostent *answer = gethostbyaddr (addr, addrlen, af);
/* Verify h_errno is as expected. */
TEST_COMPARE (h_errno, test_items[count].expected);
if (h_errno != test_items[count].expected)
/* And print more information if it's not. */
printf ("INFO: %s\n", test_items[count].test);
if (test_items[count].fail)
{
/* We expected a failure so verify answer is NULL. */
TEST_VERIFY (answer == NULL);
/* If it's not NULL we should print out what we received. */
if (answer != NULL)
printf ("error: unexpected success: %s\n",
support_format_hostent (answer));
}
else
/* We don't expect a failure so answer must be valid. */
TEST_COMPARE_STRING (answer->h_name, test_items[count].answer);
}
static int
do_test (void)
{
struct resolv_test *obj = resolv_test_start
((struct resolv_redirect_config)
{
.response_callback = response
});
for (int i = 0; i < array_length (test_items); i++)
{
check_reverse (AF_INET, i);
check_reverse (AF_INET6, i);
}
resolv_test_end (obj);
return 0;
}
#include <support/test-driver.c>
+6
View File
@@ -46,6 +46,9 @@ handle_code (const struct resolv_response_context *ctx,
{
switch (code)
{
case 0:
send_ptr (b, qname, qclass, qtype, "0.in-addr.arpa");
break;
case 1:
send_ptr (b, qname, qclass, qtype, "1.in-addr.arpa");
break;
@@ -265,6 +268,9 @@ do_test (void)
"error: TRY_AGAIN\n");
/* Lookup by address, success cases. */
check_reverse (0,
"name: 0.in-addr.arpa\n"
"net: 0x00000000\n");
check_reverse (1,
"name: 1.in-addr.arpa\n"
"net: 0x00000001\n");
+1 -1
View File
@@ -38,7 +38,7 @@ $4 == "*UND*" { next }
$2 == "l" { next }
# If the target uses ST_OTHER, it will be output before the symbol name.
$2 == "g" || $2 == "w" && (NF == 7 || NF == 8) {
$2 == "g" || $2 == "w" && (NF == 6 || NF == 7 || NF == 8) {
type = $3;
size = $5;
sub(/^0*/, "", size);
+6
View File
@@ -260,6 +260,8 @@ tests := \
tllformat \
tst-bz11319 \
tst-bz11319-fortify2 \
tst-bz12701-c \
tst-bz12701-c2 \
tst-cookie \
tst-dprintf-length \
tst-fclose-devzero \
@@ -347,6 +349,7 @@ tests := \
tst-vfprintf-user-type \
tst-vfprintf-width-i18n \
tst-vfprintf-width-prec-alloc \
tst-vfscanf-bz34008 \
tst-wc-printf \
tstdiomisc \
tstgetln \
@@ -562,6 +565,9 @@ tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace \
tst-vfprintf-width-prec-ENV = \
MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
tst-vfscanf-bz34008-ENV = \
MALLOC_CHECK_=3 \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
tst-printf-bz25691-ENV = \
MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
+169
View File
@@ -0,0 +1,169 @@
/* Verify scanf field width handling with the 'c' conversion (BZ #12701).
Copyright (C) 2025-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 <string.h>
#include <libc-diag.h>
#include <support/check.h>
#include <support/next_to_fault.h>
#include <support/xstdio.h>
/* Verify various aspects of field width handling, including the data
obtained, the number of bytes consumed, and the stream position. */
static int
do_test (void)
{
static const char s[43] = "The quick brown fox jumps over the lazy dog";
struct support_next_to_fault ntfo, ntfi;
ntfo = support_next_to_fault_allocate (sizeof (s));
ntfi = support_next_to_fault_allocate (sizeof (s));
char *e = ntfo.buffer + sizeof (s);
char *b = ntfi.buffer;
char *c;
FILE *f;
int n;
int i;
memcpy (ntfi.buffer, s, sizeof (s));
i = 0;
f = xfmemopen (b, sizeof (s), "r");
c = e - 1;
TEST_VERIFY_EXIT (ftell (f) == i);
/* Avoid: "warning: zero width in gnu_scanf format [-Werror=format=]". */
DIAG_PUSH_NEEDS_COMMENT;
DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat");
TEST_VERIFY_EXIT (fscanf (f, "%0c%n", c, &n) == 1);
DIAG_POP_NEEDS_COMMENT;
TEST_VERIFY_EXIT (n == 1);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - 1;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 1);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - 1;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%1c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 1);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - 2;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 2);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - 4;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%4c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 4);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - 8;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%8c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 8);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - 16;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%16c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 16);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - (sizeof (s) - i);
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%32c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 10);
TEST_VERIFY_EXIT (memcmp (c, s + i, sizeof (s) - i) == 0);
TEST_VERIFY_EXIT (ftell (f) == sizeof (s));
TEST_VERIFY_EXIT (feof (f) != 0);
xfclose (f);
i = 0;
f = xfmemopen (b, 3, "r");
c = e - 1;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 1);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - 2;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 2);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - (3 - i);
TEST_VERIFY_EXIT (feof (f) == 0);
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == EOF);
TEST_VERIFY_EXIT (n == 2);
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (feof (f) != 0);
xfclose (f);
i = 0;
f = xfmemopen (b, 3, "r");
c = e - 2;
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 2);
TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0);
i += n;
c = e - (3 - i);
TEST_VERIFY_EXIT (ftell (f) == i);
TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1);
TEST_VERIFY_EXIT (n == 1);
TEST_VERIFY_EXIT (memcmp (c, s + i, 3 - i) == 0);
TEST_VERIFY_EXIT (ftell (f) == 3);
TEST_VERIFY_EXIT (feof (f) != 0);
xfclose (f);
support_next_to_fault_free (&ntfi);
support_next_to_fault_free (&ntfo);
return 0;
}
#include <support/test-driver.c>
+46
View File
@@ -0,0 +1,46 @@
/* Verify scanf memory handling with the 'c' conversion (BZ #12701).
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 <malloc.h>
#include <string.h>
#include <libc-diag.h>
#include <support/check.h>
#include <support/next_to_fault.h>
#include <support/xstdio.h>
static int
do_test (void)
{
char *c = NULL;
int i;
TEST_VERIFY (sscanf ("1234", "%30mc", &c) == 1);
TEST_VERIFY (c != NULL);
TEST_COMPARE_BLOB (c, 5, "1234\0", 5);
for (i = 5; i < 30; i ++)
TEST_VERIFY (c[i] == '\0');
TEST_VERIFY (malloc_usable_size (c) >= 30);
return 0;
}
#include <support/test-driver.c>

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