Version in base suite: 2.36-9+deb12u13 Base version: glibc_2.36-9+deb12u13 Target version: glibc_2.36-9+deb12u14 Base file: /srv/ftp-master.debian.org/ftp/pool/main/g/glibc/glibc_2.36-9+deb12u13.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/g/glibc/glibc_2.36-9+deb12u14.dsc changelog | 35 patches/amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff | 50 patches/git-updates.diff | 1718 +++++++++- patches/series | 2 4 files changed, 1777 insertions(+), 28 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp2t3notxz/glibc_2.36-9+deb12u13.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp2t3notxz/glibc_2.36-9+deb12u14.dsc: no acceptable signature found diff -Nru glibc-2.36/debian/changelog glibc-2.36/debian/changelog --- glibc-2.36/debian/changelog 2025-08-25 19:11:05.000000000 +0000 +++ glibc-2.36/debian/changelog 2026-04-27 20:14:33.000000000 +0000 @@ -1,3 +1,38 @@ +glibc (2.36-9+deb12u14) bookworm; urgency=medium + + * debian/patches/git-updates.diff: update from upstream stable branch: + - Fix a performance bottleneck with the Address Sanitizer (ASAN) on 32-bit + arm. + - Fix _dl_find_object when ld.so has LOAD segment gaps, causing wrong + backtrace unwinding. This affects at least arm64. + - Add GLIBC_ABI_DT_X86_64_PLT symbol version on amd64. + - Fix typo in wmemset ifunc selector that caused AVX2/AVX512 paths to be + skipped. + - Fix POWER optimized rawmemchr function on ppc64el. + - Optimize trylock for high cache contention workloads. + - Fix and integer overflow in _int_memalign leading to heap corruption + (CVE-2026-0861). Closes: #1125678. + - Fix stack contents leak in getnetbyaddr (CVE-2026-0915). Closes: + #1125748. + - Fix bug in wordexp, which could return uninitialized memory when using + WRDE_REUSE together with WRDE_APPEND (CVE-2025-15281). Closes: #1126266. + - Fix invalid pointer arithmetic in ANSI_X3.110 iconv module + - Fix a typo preventing new tst-wordexp-reuse-mem to run + - Fix incorrect handling of DNS responses in gethostbyaddr and + gethostbyaddr_r (CVE-2026-4437). Closes: #1131435. + - Fix invalid DNS hostnames returned by gethostbyaddr and + gethostbyaddr_r (CVE-2026-4438). Closes: #1131887. + - Fix random failure of tst-link-map-contiguous-ldso. + - Fix a possible crash due to an assertion failure when converting + inputs from the IBM139x character sets (CVE-2026-4046). Closes: + #1132499. + * d/p/amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff: + revert addition of the GLIBC_ABI_DT_X86_64_PLT symbol version used as ABI + flag, as the dpkg-shlibdeps version in bookworm is not able to handle it + (see #1122107). + + -- Aurelien Jarno Mon, 27 Apr 2026 22:14:33 +0200 + glibc (2.36-9+deb12u13) bookworm; urgency=medium * debian/patches/git-updates.diff: update from upstream stable branch: diff -Nru glibc-2.36/debian/patches/amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff glibc-2.36/debian/patches/amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff --- glibc-2.36/debian/patches/amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff 1970-01-01 00:00:00.000000000 +0000 +++ glibc-2.36/debian/patches/amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff 2026-04-27 20:14:33.000000000 +0000 @@ -0,0 +1,50 @@ +From cc8046413227196540f7f3106739e91e6e8a86d6 Mon Sep 17 00:00:00 2001 +From: Aurelien Jarno +Date: Sat, 7 Mar 2026 23:26:12 +0100 +Subject: [PATCH] Revert "x86-64: Add GLIBC_ABI_DT_X86_64_PLT [BZ #33212]" + +This reverts commit 99338e3841dd8dcc36e35c274e1063c33b5e5e87. +--- + sysdeps/x86_64/Makefile | 9 --------- + sysdeps/x86_64/Versions | 5 ----- + 2 files changed, 14 deletions(-) + +diff --git a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile +index f173c5ef4a..c19bef2dec 100644 +--- a/sysdeps/x86_64/Makefile ++++ b/sysdeps/x86_64/Makefile +@@ -190,15 +190,6 @@ endif + + tests-internal += tst-x86-64-tls-1 + +-tests-special += $(objpfx)check-dt-x86-64-plt.out +- +-$(objpfx)check-dt-x86-64-plt.out: $(common-objpfx)libc.so +- LC_ALL=C $(READELF) -V -W $< \ +- | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ +- | grep GLIBC_ABI_DT_X86_64_PLT > $@; \ +- $(evaluate-test) +-generated += check-dt-x86-64-plt.out +- + endif # $(subdir) == elf + + ifeq ($(subdir),csu) +diff --git a/sysdeps/x86_64/Versions b/sysdeps/x86_64/Versions +index 6a989ad3b3..e94758b236 100644 +--- a/sysdeps/x86_64/Versions ++++ b/sysdeps/x86_64/Versions +@@ -5,11 +5,6 @@ libc { + GLIBC_2.13 { + __fentry__; + } +- GLIBC_ABI_DT_X86_64_PLT { +- # This symbol is used only for empty version map and will be removed +- # by scripts/versions.awk. +- __placeholder_only_for_empty_version_map; +- } + } + libm { + GLIBC_2.1 { +-- +2.51.0 + diff -Nru glibc-2.36/debian/patches/git-updates.diff glibc-2.36/debian/patches/git-updates.diff --- glibc-2.36/debian/patches/git-updates.diff 2025-08-25 19:11:05.000000000 +0000 +++ glibc-2.36/debian/patches/git-updates.diff 2026-04-27 20:14:33.000000000 +0000 @@ -85,10 +85,10 @@ else # -s verbose := diff --git a/NEWS b/NEWS -index f61e521fc8..60ac79f4e6 100644 +index f61e521fc8..127817782f 100644 --- a/NEWS +++ b/NEWS -@@ -5,6 +5,117 @@ See the end for copying conditions. +@@ -5,6 +5,123 @@ See the end for copying conditions. Please send GNU C library bug reports via using `glibc' in the "product" field. @@ -138,6 +138,9 @@ + information, which may lead to a buffer overflow if the message string + size aligns to page size. + ++ GLIBC-SA-2026-0003: wordexp with WRDE_REUSE and WRDE_APPEND may return ++ uninitialized memory (CVE-2025-15281). ++ +The following bugs are resolved with this release: + + [12154] Do not fail DNS resolution for CNAMEs which are not host names @@ -193,6 +196,7 @@ + [31185] Incorrect thread point access in _dl_tlsdesc_undefweak and _dl_tlsdesc_dynamic + [31476] resolv: Track single-request fallback via _res._flags + [31890] resolv: Allow short error responses to match any DNS query ++ [31943] _dl_find_object can fail if ld.so contains gaps between load segments + [31965] rseq extension mechanism does not work as intended + [31968] mremap implementation in C does not handle arguments correctly + [32052] Name space violation in fortify wrappers @@ -202,6 +206,8 @@ + [32582] Fix underallocation of abort_msg_s struct (CVE-2025-0395) + [32987] elf: Fix subprocess status handling for tst-dlopen-sgid + [33185] Fix double-free after allocation failure in regcomp ++ [33814] glob: wordexp with WRDE_REUSE and WRDE_APPEND may return ++ uninitialized memory + Version 2.36 @@ -532,7 +538,7 @@ void * diff --git a/elf/Makefile b/elf/Makefile -index fd77d0c7c8..3e08a8046d 100644 +index fd77d0c7c8..97befdaf02 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -53,6 +53,7 @@ routines = \ @@ -584,7 +590,27 @@ tst-relsort1 \ tst-ro-dynamic \ tst-rtld-run-static \ -@@ -631,6 +638,7 @@ ifeq ($(run-built-tests),yes) +@@ -497,6 +504,8 @@ tests-internal += \ + tst-dl_find_object \ + tst-dl_find_object-threads \ + tst-dlmopen2 \ ++ tst-link-map-contiguous-ldso \ ++ tst-link-map-contiguous-libc \ + tst-ptrguard1 \ + tst-stackguard1 \ + tst-tls-surplus \ +@@ -508,6 +517,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 \ +@@ -631,6 +644,7 @@ ifeq ($(run-built-tests),yes) tests-special += \ $(objpfx)noload-mem.out \ $(objpfx)tst-ldconfig-X.out \ @@ -592,7 +618,7 @@ $(objpfx)tst-leaks1-mem.out \ $(objpfx)tst-rtld-help.out \ # tests-special -@@ -765,6 +773,8 @@ modules-names += \ +@@ -765,6 +779,8 @@ modules-names += \ tst-alignmod3 \ tst-array2dep \ tst-array5dep \ @@ -601,7 +627,7 @@ tst-audit11mod1 \ tst-audit11mod2 \ tst-audit12mod1 \ -@@ -798,6 +808,7 @@ modules-names += \ +@@ -798,6 +814,7 @@ modules-names += \ tst-auditmanymod7 \ tst-auditmanymod8 \ tst-auditmanymod9 \ @@ -609,7 +635,7 @@ tst-auditmod1 \ tst-auditmod9a \ tst-auditmod9b \ -@@ -834,6 +845,9 @@ modules-names += \ +@@ -834,6 +851,9 @@ modules-names += \ tst-dlmopen1mod \ tst-dlmopen-dlerror-mod \ tst-dlmopen-gethostbyname-mod \ @@ -619,7 +645,7 @@ tst-dlopenfaillinkmod \ tst-dlopenfailmod1 \ tst-dlopenfailmod2 \ -@@ -866,6 +880,23 @@ modules-names += \ +@@ -866,6 +886,23 @@ modules-names += \ tst-null-argv-lib \ tst-p_alignmod-base \ tst-p_alignmod3 \ @@ -643,7 +669,7 @@ tst-relsort1mod1 \ tst-relsort1mod2 \ tst-ro-dynamic-mod \ -@@ -990,23 +1021,8 @@ modules-names += tst-gnu2-tls1mod +@@ -990,23 +1027,8 @@ modules-names += tst-gnu2-tls1mod $(objpfx)tst-gnu2-tls1: $(objpfx)tst-gnu2-tls1mod.so tst-gnu2-tls1mod.so-no-z-defs = yes CFLAGS-tst-gnu2-tls1mod.c += -mtls-dialect=gnu2 @@ -668,7 +694,7 @@ ifeq (yes,$(have-protected-data)) modules-names += tst-protected1moda tst-protected1modb tests += tst-protected1a tst-protected1b -@@ -2410,6 +2426,11 @@ $(objpfx)tst-ldconfig-X.out : tst-ldconfig-X.sh $(objpfx)ldconfig +@@ -2410,6 +2432,11 @@ $(objpfx)tst-ldconfig-X.out : tst-ldconfig-X.sh $(objpfx)ldconfig '$(run-program-env)' > $@; \ $(evaluate-test) @@ -680,7 +706,7 @@ # Test static linking of all the libraries we can possibly link # together. Note that in some configurations this may be less than the # complete list of libraries we build but we try to maxmimize this list. -@@ -2967,3 +2988,35 @@ $(objpfx)tst-tls-allocation-failure-static-patched.out: \ +@@ -2967,3 +2994,35 @@ $(objpfx)tst-tls-allocation-failure-static-patched.out: \ grep -q '^Fatal glibc error: Cannot allocate TLS block$$' $@ \ && grep -q '^status: 127$$' $@; \ $(evaluate-test) @@ -878,7 +904,7 @@ if (tls_free_end == GL(dl_tls_static_used)) GL(dl_tls_static_used) = tls_free_start; diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c -index 4d5831b6f4..2e5b456c11 100644 +index 4d5831b6f4..cb8555a543 100644 --- a/elf/dl-find_object.c +++ b/elf/dl-find_object.c @@ -46,7 +46,7 @@ _dl_find_object_slow (void *pc, struct dl_find_object *result) @@ -890,6 +916,134 @@ } /* Object not found. */ +@@ -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,22 @@ _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 ++#ifdef SHARED ++ && l == &GL(dl_rtld_map) ++#endif ++ ) ++ 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) + { +@@ -756,7 +777,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; + } + +diff --git a/elf/dl-find_object.h b/elf/dl-find_object.h +index 3b49877e0e..9d13163ccd 100644 +--- a/elf/dl-find_object.h ++++ b/elf/dl-find_object.h +@@ -87,7 +87,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) +@@ -100,6 +100,8 @@ _dl_find_object_from_map (struct link_map *l, + atomic_store_relaxed (&result->eh_dbase, (void *) l->l_info[DT_PLTGOT]); + #endif + ++ l->l_find_object_processed = 1; ++ + for (const ElfW(Phdr) *ph = l->l_phdr, *ph_end = l->l_phdr + l->l_phnum; + ph < ph_end; ++ph) + if (ph->p_type == DLFO_EH_SEGMENT_TYPE) diff --git a/elf/dl-fini.c b/elf/dl-fini.c index 030b1fcbcd..50ff94db16 100644 --- a/elf/dl-fini.c @@ -1875,7 +2029,7 @@ else # -s verbose := diff --git a/elf/rtld.c b/elf/rtld.c -index cbbaf4a331..6e40893e52 100644 +index cbbaf4a331..bb3fc4806a 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -791,6 +791,8 @@ init_tls (size_t naudit) @@ -1887,7 +2041,103 @@ /* Store for detection of the special case by __tls_get_addr so it knows not to pass this dtv to the normal realloc. */ GL(dl_initial_dtv) = GET_DTV (tcbp); -@@ -2122,6 +2124,12 @@ dl_main (const ElfW(Phdr) *phdr, +@@ -1293,6 +1295,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; ++ ++ GL(dl_rtld_map).l_phdr = rtld_phdr; ++ GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum; ++ ++ ++ GL(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 (GL(dl_rtld_map).l_contiguous && expected_load_address != 0 ++ && expected_load_address != mapstart) ++ GL(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) ++ { ++ GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr; ++ GL(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 +@@ -1762,33 +1818,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; +- +- GL(dl_rtld_map).l_phdr = rtld_phdr; +- GL(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) +- { +- GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr; +- GL(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 (GL(dl_rtld_map).l_tls_blocksize != 0) +@@ -2122,6 +2152,12 @@ dl_main (const ElfW(Phdr) *phdr, if (l->l_faked) /* The library was not found. */ _dl_printf ("\t%s => not found\n", l->l_libname->name); @@ -2395,6 +2645,284 @@ +esac + +exit $errors +diff --git a/elf/tst-link-map-contiguous-ldso.c b/elf/tst-link-map-contiguous-ldso.c +new file mode 100644 +index 0000000000..f0e26682f2 +--- /dev/null ++++ b/elf/tst-link-map-contiguous-ldso.c +@@ -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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 +diff --git a/elf/tst-link-map-contiguous-libc.c b/elf/tst-link-map-contiguous-libc.c +new file mode 100644 +index 0000000000..eb5728c765 +--- /dev/null ++++ b/elf/tst-link-map-contiguous-libc.c +@@ -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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 +diff --git a/elf/tst-link-map-contiguous-main.c b/elf/tst-link-map-contiguous-main.c +new file mode 100644 +index 0000000000..2d1a054f0f +--- /dev/null ++++ b/elf/tst-link-map-contiguous-main.c +@@ -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 ++ . */ ++ ++#include ++#include ++#include ++ ++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 diff --git a/elf/tst-recursive-tls.c b/elf/tst-recursive-tls.c new file mode 100644 index 0000000000..716d1f783a @@ -3101,7 +3629,7 @@ free (conf); diff --git a/iconvdata/Makefile b/iconvdata/Makefile -index f4c089ed5d..d01b3fcab6 100644 +index f4c089ed5d..1ca3a193c7 100644 --- a/iconvdata/Makefile +++ b/iconvdata/Makefile @@ -75,7 +75,8 @@ ifeq (yes,$(build-shared)) @@ -3110,19 +3638,148 @@ bug-iconv10 bug-iconv11 bug-iconv12 tst-iconv-big5-hkscs-to-2ucs4 \ - bug-iconv13 bug-iconv14 bug-iconv15 + bug-iconv13 bug-iconv14 bug-iconv15 \ -+ tst-iconv-iso-2022-cn-ext ++ tst-iconv-iso-2022-cn-ext tst-bug33980 ifeq ($(have-thread-library),yes) tests += bug-iconv3 endif -@@ -330,6 +331,8 @@ $(objpfx)bug-iconv14.out: $(addprefix $(objpfx), $(gconv-modules)) \ +@@ -330,6 +331,10 @@ $(objpfx)bug-iconv14.out: $(addprefix $(objpfx), $(gconv-modules)) \ $(addprefix $(objpfx),$(modules.so)) $(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)iconv-test.out: run-iconv-test.sh \ $(addprefix $(objpfx), $(gconv-modules)) \ +diff --git a/iconvdata/ansi_x3.110.c b/iconvdata/ansi_x3.110.c +index fa2f6b46e5..6c9e5a024e 100644 +--- a/iconvdata/ansi_x3.110.c ++++ b/iconvdata/ansi_x3.110.c +@@ -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; \ +diff --git a/iconvdata/ibm1364.c b/iconvdata/ibm1364.c +index ce3172416c..e79f98a769 100644 +--- a/iconvdata/ibm1364.c ++++ b/iconvdata/ibm1364.c +@@ -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 \ ++ { \ ++ put32u (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) \ + { \ diff --git a/iconvdata/iso-2022-cn-ext.c b/iconvdata/iso-2022-cn-ext.c index e09f358cad..2cc478a8c6 100644 --- a/iconvdata/iso-2022-cn-ext.c @@ -3153,6 +3810,165 @@ assert ((used >> 5) >= 3 && (used >> 5) <= 7); \ escseq = "+I+J+K+L+M" + ((used >> 5) - 3) * 2; \ *outptr++ = ESC; \ +diff --git a/iconvdata/tst-bug33980.c b/iconvdata/tst-bug33980.c +new file mode 100644 +index 0000000000..c9693e0efe +--- /dev/null ++++ b/iconvdata/tst-bug33980.c +@@ -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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* 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 diff --git a/iconvdata/tst-iconv-iso-2022-cn-ext.c b/iconvdata/tst-iconv-iso-2022-cn-ext.c new file mode 100644 index 0000000000..96a8765fd5 @@ -4080,6 +4896,67 @@ if (n >= 1) narenas_limit = NARENAS_FROM_NCORES (n); +diff --git a/malloc/malloc.c b/malloc/malloc.c +index bd3c76ed31..53d6e06bb0 100644 +--- a/malloc/malloc.c ++++ b/malloc/malloc.c +@@ -4949,7 +4949,7 @@ _int_memalign (mstate av, size_t alignment, size_t bytes) + + + nb = checked_request2size (bytes); +- if (nb == 0) ++ if (nb == 0 || alignment > PTRDIFF_MAX) + { + __set_errno (ENOMEM); + return NULL; +@@ -4960,8 +4960,10 @@ _int_memalign (mstate av, size_t alignment, size_t bytes) + request, and then possibly free the leading and trailing space. + */ + +- /* 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 == 0) +diff --git a/malloc/tst-malloc-too-large.c b/malloc/tst-malloc-too-large.c +index dac3c8086c..e7017981aa 100644 +--- a/malloc/tst-malloc-too-large.c ++++ b/malloc/tst-malloc-too-large.c +@@ -151,7 +151,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 +@@ -170,8 +169,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) +@@ -264,11 +263,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. + diff --git a/math/test-tgmath2.c b/math/test-tgmath2.c index d758231a89..95897948fb 100644 --- a/math/test-tgmath2.c @@ -4963,6 +5840,32 @@ } __attribute ((aligned (TCB_ALIGNMENT))); static inline bool +diff --git a/nptl/pthread_mutex_trylock.c b/nptl/pthread_mutex_trylock.c +index 8a7de8e598..69aa11b0aa 100644 +--- a/nptl/pthread_mutex_trylock.c ++++ b/nptl/pthread_mutex_trylock.c +@@ -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. */ diff --git a/nptl/tst-cancel7.c b/nptl/tst-cancel7.c index 903457db76..1c80457553 100644 --- a/nptl/tst-cancel7.c @@ -5958,18 +6861,56 @@ static struct hostent host_table_2[] = { diff --git a/posix/Makefile b/posix/Makefile -index d1df7c27cb..336bee4a4a 100644 +index d1df7c27cb..36ae315fad 100644 --- a/posix/Makefile +++ b/posix/Makefile -@@ -109,7 +109,7 @@ tests := test-errno tstgetopt testfnm runtests runptests \ +@@ -109,7 +109,8 @@ tests := test-errno tstgetopt testfnm runtests runptests \ tst-glob-tilde test-ssize-max tst-spawn4 bug-regex37 \ bug-regex38 tst-regcomp-truncated tst-spawn-chdir \ tst-wordexp-nocmd tst-execveat tst-spawn5 \ - tst-sched_getaffinity tst-spawn6 -+ tst-sched_getaffinity tst-spawn6 tst-regcomp-bracket-free ++ tst-sched_getaffinity tst-spawn6 tst-regcomp-bracket-free \ ++ tst-wordexp-reuse # Test for the glob symbol version that was replaced in glibc 2.27. ifeq ($(have-GLIBC_2.26)$(build-shared),yesyes) +@@ -156,11 +157,13 @@ generated += $(addprefix wordexp-test-result, 1 2 3 4 5 6 7 8 9 10) \ + bug-glob2.mtrace bug-glob2-mem.out tst-vfork3-mem.out \ + tst-vfork3.mtrace getconf.speclist tst-fnmatch-mem.out \ + tst-fnmatch.mtrace bug-regex36.mtrace \ +- testcases.h ptestcases.h ++ testcases.h ptestcases.h tst-wordexp-reuse-mem.out \ ++ tst-wordexp-reuse.mtrace + + ifeq ($(run-built-tests),yes) + ifeq (yes,$(build-shared)) +-tests-special += $(objpfx)globtest.out $(objpfx)wordexp-tst.out ++tests-special += $(objpfx)globtest.out $(objpfx)wordexp-tst.out \ ++ $(objpfx)wordexp-tst.out + endif + endif + +@@ -174,7 +177,8 @@ tests-special += $(objpfx)bug-regex2-mem.out $(objpfx)bug-regex14-mem.out \ + $(objpfx)tst-boost-mem.out $(objpfx)tst-getconf.out \ + $(objpfx)bug-glob2-mem.out $(objpfx)tst-vfork3-mem.out \ + $(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out \ +- $(objpfx)tst-glob-tilde-mem.out $(objpfx)bug-ga2-mem.out ++ $(objpfx)tst-glob-tilde-mem.out $(objpfx)bug-ga2-mem.out \ ++ $(objpfx)tst-wordexp-reuse-mem.out + endif + + include ../Rules +@@ -451,3 +455,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) diff --git a/posix/regcomp.c b/posix/regcomp.c index 84fc1cc82b..7bd0beeafe 100644 --- a/posix/regcomp.c @@ -6243,8 +7184,117 @@ return 0; } +diff --git a/posix/tst-wordexp-reuse.c b/posix/tst-wordexp-reuse.c +new file mode 100644 +index 0000000000..3926b9f557 +--- /dev/null ++++ b/posix/tst-wordexp-reuse.c +@@ -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 ++ . */ ++ ++#include ++#include ++ ++#include ++ ++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 +diff --git a/posix/wordexp.c b/posix/wordexp.c +index d4cb9c734b..25f5b509a8 100644 +--- a/posix/wordexp.c ++++ b/posix/wordexp.c +@@ -2219,7 +2219,9 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags) + { + /* 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) diff --git a/resolv/Makefile b/resolv/Makefile -index 5b15321f9b..e5165fb34b 100644 +index 5b15321f9b..d1b22f5b64 100644 --- a/resolv/Makefile +++ b/resolv/Makefile @@ -40,12 +40,16 @@ routines := \ @@ -6264,7 +7314,7 @@ ns_samename \ nsap_addr \ nss_dns_functions \ -@@ -89,14 +93,20 @@ tests += \ +@@ -89,21 +93,42 @@ tests += \ tst-ns_name_pton \ tst-res_hconf_reorder \ tst-res_hnok \ @@ -6272,8 +7322,10 @@ 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 \ @@ -6283,9 +7335,10 @@ + tst-resolv-semi-failure \ + tst-resolv-short-response \ tst-resolv-trailing \ ++ # tests # This test calls __res_context_send directly, which is not exported -@@ -104,6 +114,18 @@ tests += \ + # from libresolv. tests-internal += tst-resolv-txnid-collision tests-static += tst-resolv-txnid-collision @@ -6304,7 +7357,7 @@ # These tests need libdl. ifeq (yes,$(build-shared)) tests += \ -@@ -258,8 +280,10 @@ $(objpfx)tst-resolv-ai_idn.out: $(gen-locales) +@@ -258,8 +283,12 @@ $(objpfx)tst-resolv-ai_idn.out: $(gen-locales) $(objpfx)tst-resolv-ai_idn-latin1.out: $(gen-locales) $(objpfx)tst-resolv-ai_idn-nolibidn2.out: \ $(gen-locales) $(objpfx)tst-no-libidn2.so @@ -6312,15 +7365,19 @@ $(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 -@@ -267,11 +291,18 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \ +@@ -267,11 +296,20 @@ $(objpfx)tst-resolv-res_init-multi: $(objpfx)libresolv.so \ $(shared-thread-library) $(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) @@ -6798,7 +7855,7 @@ + return *a == 0 && *b == 0; +} diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c -index 544cffbecd..227734da5c 100644 +index 544cffbecd..7053f212ae 100644 --- a/resolv/nss_dns/dns-host.c +++ b/resolv/nss_dns/dns-host.c @@ -69,6 +69,7 @@ @@ -7522,7 +8579,7 @@ + unsigned char name_buffer[NS_MAXCDNAME]; - if (__glibc_unlikely (class != C_IN)) -+ while (ancount > 0) ++ for (; ancount > 0; --ancount) + { + struct ns_rr_wire rr; + if (!__ns_rr_cursor_next (&c, &rr)) @@ -7647,7 +8704,7 @@ + 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) { - cp += n; @@ -8245,6 +9302,21 @@ + abuf, &pat, errnop, h_errnop, ttlp, true); return status; } +diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c +index 09cd917444..3458bd4615 100644 +--- a/resolv/nss_dns/dns-network.c ++++ b/resolv/nss_dns/dns-network.c +@@ -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); diff --git a/resolv/res-name-checking.c b/resolv/res-name-checking.c index 07a412d8ff..213edceaf3 100644 --- a/resolv/res-name-checking.c @@ -9416,6 +10488,174 @@ +} + +#include +diff --git a/resolv/tst-resolv-dns-section.c b/resolv/tst-resolv-dns-section.c +new file mode 100644 +index 0000000000..1171baef51 +--- /dev/null ++++ b/resolv/tst-resolv-dns-section.c +@@ -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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 diff --git a/resolv/tst-resolv-invalid-cname.c b/resolv/tst-resolv-invalid-cname.c new file mode 100644 index 0000000000..63dac90e02 @@ -9828,6 +11068,267 @@ +} + +#include +diff --git a/resolv/tst-resolv-invalid-ptr.c b/resolv/tst-resolv-invalid-ptr.c +new file mode 100644 +index 0000000000..0c802ab967 +--- /dev/null ++++ b/resolv/tst-resolv-invalid-ptr.c +@@ -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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 diff --git a/resolv/tst-resolv-maybe_insert_sig.h b/resolv/tst-resolv-maybe_insert_sig.h new file mode 100644 index 0000000000..05725225af @@ -9866,6 +11367,30 @@ + resolv_response_add_data (b, "", 1); + resolv_response_close_record (b); +} +diff --git a/resolv/tst-resolv-network.c b/resolv/tst-resolv-network.c +index 956f847db7..f1f11613ac 100644 +--- a/resolv/tst-resolv-network.c ++++ b/resolv/tst-resolv-network.c +@@ -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"); diff --git a/resolv/tst-resolv-noaaaa-vc.c b/resolv/tst-resolv-noaaaa-vc.c new file mode 100644 index 0000000000..9f5aebd99f @@ -15420,6 +16945,81 @@ @ save the entry point in another register\n\ mov r6, r0\n\ @ get the original arg count\n\ +diff --git a/sysdeps/arm/find_exidx.c b/sysdeps/arm/find_exidx.c +index aab06d25f6..0236b1f4ca 100644 +--- a/sysdeps/arm/find_exidx.c ++++ b/sysdeps/arm/find_exidx.c +@@ -15,65 +15,17 @@ + License along with the GNU C Library. If not, see + . */ + ++#include + #include +-#include +- +-struct unw_eh_callback_data +-{ +- _Unwind_Ptr pc; +- _Unwind_Ptr exidx_start; +- int exidx_len; +-}; +- +- +-/* Callback to determins if the PC lies within an object, and remember the +- location of the exception index table if it does. */ +- +-static int +-find_exidx_callback (struct dl_phdr_info * info, size_t size, void * ptr) +-{ +- struct unw_eh_callback_data * data; +- const ElfW(Phdr) *phdr; +- int i; +- int match; +- _Unwind_Ptr load_base; +- +- data = (struct unw_eh_callback_data *) ptr; +- load_base = info->dlpi_addr; +- phdr = info->dlpi_phdr; +- +- match = 0; +- for (i = info->dlpi_phnum; i > 0; i--, phdr++) +- { +- if (phdr->p_type == PT_LOAD) +- { +- _Unwind_Ptr vaddr = phdr->p_vaddr + load_base; +- if (data->pc >= vaddr && data->pc < vaddr + phdr->p_memsz) +- match = 1; +- } +- else if (phdr->p_type == PT_ARM_EXIDX) +- { +- data->exidx_start = (_Unwind_Ptr) (phdr->p_vaddr + load_base); +- data->exidx_len = phdr->p_memsz; +- } +- } +- +- return match; +-} +- + + /* Find the exception index table containing PC. */ + + _Unwind_Ptr + __gnu_Unwind_Find_exidx (_Unwind_Ptr pc, int * pcount) + { +- struct unw_eh_callback_data data; +- +- data.pc = pc; +- data.exidx_start = 0; +- if (__dl_iterate_phdr (find_exidx_callback, &data) <= 0) ++ struct dl_find_object data; ++ if (GLRO(dl_find_object) ((void *) pc, &data) < 0) + return 0; +- +- *pcount = data.exidx_len / 8; +- return data.exidx_start; ++ *pcount = data.dlfo_eh_count; ++ return (_Unwind_Ptr) data.dlfo_eh_frame; + } diff --git a/sysdeps/arm/utmp-size.h b/sysdeps/arm/utmp-size.h new file mode 100644 index 0000000000..8f21ebe1b6 @@ -16373,6 +17973,19 @@ /* The PLT uses Elf64_Rela relocs. */ #define elf_machine_relplt elf_machine_rela +diff --git a/sysdeps/powerpc/powerpc64/le/power10/strlen.S b/sysdeps/powerpc/powerpc64/le/power10/strlen.S +index 5c42f78de9..b9c7f511cf 100644 +--- a/sysdeps/powerpc/powerpc64/le/power10/strlen.S ++++ b/sysdeps/powerpc/powerpc64/le/power10/strlen.S +@@ -31,7 +31,7 @@ + # define FUNCNAME RAWMEMCHR + # endif + # define MCOUNT_NARGS 2 +-# define VREG_ZERO v20 ++# define VREG_ZERO v17 + # define OFF_START_LOOP 256 + # define RAWMEMCHR_SUBTRACT_VECTORS \ + vsububm v4,v4,v18; \ diff --git a/sysdeps/powerpc/utmp-size.h b/sysdeps/powerpc/utmp-size.h new file mode 100644 index 0000000000..8f21ebe1b6 @@ -19742,6 +21355,42 @@ @@ -0,0 +1,2 @@ +#define UTMP_SIZE 384 +#define LASTLOG_SIZE 292 +diff --git a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile +index c19bef2dec..f173c5ef4a 100644 +--- a/sysdeps/x86_64/Makefile ++++ b/sysdeps/x86_64/Makefile +@@ -190,6 +190,15 @@ endif + + tests-internal += tst-x86-64-tls-1 + ++tests-special += $(objpfx)check-dt-x86-64-plt.out ++ ++$(objpfx)check-dt-x86-64-plt.out: $(common-objpfx)libc.so ++ LC_ALL=C $(READELF) -V -W $< \ ++ | sed -ne '/.gnu.version_d/, /.gnu.version_r/ p' \ ++ | grep GLIBC_ABI_DT_X86_64_PLT > $@; \ ++ $(evaluate-test) ++generated += check-dt-x86-64-plt.out ++ + endif # $(subdir) == elf + + ifeq ($(subdir),csu) +diff --git a/sysdeps/x86_64/Versions b/sysdeps/x86_64/Versions +index e94758b236..6a989ad3b3 100644 +--- a/sysdeps/x86_64/Versions ++++ b/sysdeps/x86_64/Versions +@@ -5,6 +5,11 @@ libc { + GLIBC_2.13 { + __fentry__; + } ++ GLIBC_ABI_DT_X86_64_PLT { ++ # This symbol is used only for empty version map and will be removed ++ # by scripts/versions.awk. ++ __placeholder_only_for_empty_version_map; ++ } + } + libm { + GLIBC_2.1 { diff --git a/sysdeps/x86_64/dl-tls.c b/sysdeps/x86_64/dl-tls.c index 24a6e643b0..86517573dc 100644 --- a/sysdeps/x86_64/dl-tls.c @@ -20409,6 +22058,19 @@ && X86_ISA_CPU_FEATURES_ARCH_P (cpu_features, AVX_Fast_Unaligned_Load, )) { +diff --git a/sysdeps/x86_64/multiarch/ifunc-wmemset.h b/sysdeps/x86_64/multiarch/ifunc-wmemset.h +index 3810c719c6..9e58bc00b5 100644 +--- a/sysdeps/x86_64/multiarch/ifunc-wmemset.h ++++ b/sysdeps/x86_64/multiarch/ifunc-wmemset.h +@@ -35,7 +35,7 @@ IFUNC_SELECTOR (void) + + if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX2) + && X86_ISA_CPU_FEATURES_ARCH_P (cpu_features, +- AVX_Fast_Unaligned_Load, !)) ++ AVX_Fast_Unaligned_Load,)) + { + if (X86_ISA_CPU_FEATURE_USABLE_P (cpu_features, AVX512VL)) + { diff --git a/sysdeps/x86_64/multiarch/memcmp-sse2.S b/sysdeps/x86_64/multiarch/memcmp-sse2.S index afd450d020..51bc9344f0 100644 --- a/sysdeps/x86_64/multiarch/memcmp-sse2.S diff -Nru glibc-2.36/debian/patches/series glibc-2.36/debian/patches/series --- glibc-2.36/debian/patches/series 2025-08-25 19:11:05.000000000 +0000 +++ glibc-2.36/debian/patches/series 2026-04-27 20:14:33.000000000 +0000 @@ -21,6 +21,8 @@ alpha/submitted-fts64.diff alpha/submitted-makecontext.diff +amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff + arm/local-sigaction.diff arm/unsubmitted-ldso-multilib.diff arm/local-arm-futex.diff