--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:glibc
User: [email protected]
Usertags: pu
[ Reason ]
The upstream stable branch received many fixes since the latest bookworm
point release, and this update pulls them into the debian package. This
includes most notably 6 security fixes.
[ Impact ]
In case the update isn't approved, systems will be left with a few
issues, including 6 security ones, and the differences with upstream
will increase.
[ Tests ]
Most of the changes come with additional upstream tests, and one
additional test has been enabled.
[ Risks ]
I believe the risks are low, as a significant part of the changes are
already in trixie since the last point release. All the other changes
are in testing for more than 3 weeks, with the exception of the fix for
CVE-2026-4046 which entered testing only a couple of days ago.
[ Checklist ]
[x] *all* changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[x] attach debdiff against the package in (old)stable
[x] the issue is verified as fixed in unstable
[ Changes ]
The changes are summarized in the changelog, let me give you a few extra
details for some of them:
- Fix a performance bottleneck with the Address Sanitizer (ASAN) on 32-bit
arm.
This is https://sourceware.org/bugzilla/show_bug.cgi?id=31405 upstream
- Fix _dl_find_object when ld.so has LOAD segment gaps, causing wrong
backtrace unwinding. This affects at least arm64.
This is https://sourceware.org/bugzilla/show_bug.cgi?id=31943 upstream
- Fix typo in wmemset ifunc selector that caused AVX2/AVX512 paths to be
skipped.
This is https://sourceware.org/bugzilla/show_bug.cgi?id=33542 upstream
- Fix POWER optimized rawmemchr function on ppc64el.
This is https://sourceware.org/bugzilla/show_bug.cgi?id=33091 upstream
- Optimize trylock for high cache contention workloads.
This is https://sourceware.org/bugzilla/show_bug.cgi?id=33704 upstream
- Fix a typo preventing new tst-wordexp-reuse-mem to run
There was a typo in the makefile, causing the test to be ignored. This
just fixes that.
- Fix random failure of tst-link-map-contiguous-ldso.
This test failure appears randomly, but has significantly more chances
to happen on a 16KiB page size kernel. This is the case for instance on
the 16k kernel we ship on arm64.
- Add GLIBC_ABI_DT_X86_64_PLT symbol version on amd64.
* 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).
This is the tricky part of the of this new bookworm-pu version. A bug in
the R_X86_64_GLOB_DAT and R_X86_64_JUMP_SLOT relocation (emitted when
using -z mark-plt) has been fixed in 2.36 (so already included in
bookworm). The GLIBC_ABI_DT_X86_64_PLT symbol version is therefore
emitted by binutils to ensure that binaries built with the fixed ABI are
only executed with a glibc that has the fix. This makes possible to
backport the fix to older glibc version instead of requiring a newer
glibc.
However the dpkg-shlibdeps version in bookworm is not able to handle
symbol versions not associated with any symbol (see #1122107). Therefore
the chosen approach is to just revert the symbol version addition (see
below). I do not expect the dpkg-shlibdeps bug to be fixed in bookworm
given it starts to be old, but users needing a binary compatibility with
other distribution can upgrade.
Note that that the same approach that was done in 2.41-12+deb13u2 in the
13.4 point release (just extended to more flags).
[ Other info ]
There are still a few pending CVEs to be fixed, but no visibility when
they'll be fixed, and I believe that it's better to have an upload not
too late before the point release.
diff --git a/debian/changelog b/debian/changelog
index 87be104d..88dc4614 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -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 <[email protected]> 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 --git
a/debian/patches/amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff
b/debian/patches/amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff
new file mode 100644
index 00000000..829c7593
--- /dev/null
+++
b/debian/patches/amd64/local-revert-x86-64-add-GLIBC_ABI_DT_X86_64_PLT-version.diff
@@ -0,0 +1,50 @@
+From cc8046413227196540f7f3106739e91e6e8a86d6 Mon Sep 17 00:00:00 2001
+From: Aurelien Jarno <[email protected]>
+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 --git a/debian/patches/git-updates.diff b/debian/patches/git-updates.diff
index 361d0520..6655f339 100644
--- a/debian/patches/git-updates.diff
+++ b/debian/patches/git-updates.diff
@@ -85,10 +85,10 @@ index d1e139d03c..09c0cf8357 100644
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 <https://sourceware.org/bugzilla/>
using `glibc' in the "product" field.
@@ -138,6 +138,9 @@ index f61e521fc8..60ac79f4e6 100644
+ 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 @@ index f61e521fc8..60ac79f4e6 100644
+ [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 @@ index f61e521fc8..60ac79f4e6 100644
+ [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 @@ index 2696dde4b1..9b07b4e132 100644
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 @@ index fd77d0c7c8..3e08a8046d 100644
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 @@ index fd77d0c7c8..3e08a8046d 100644
$(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 @@ index fd77d0c7c8..3e08a8046d 100644
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 @@ index fd77d0c7c8..3e08a8046d 100644
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 @@ index fd77d0c7c8..3e08a8046d 100644
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 @@ index fd77d0c7c8..3e08a8046d 100644
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 @@ index fd77d0c7c8..3e08a8046d 100644
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 @@ index fd77d0c7c8..3e08a8046d 100644
# 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 @@ index bcd6e206e9..ef909d8c66 100644
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 @@ index 4d5831b6f4..2e5b456c11 100644
}
/* 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 @@ index ca00dd1fe2..3c5e273f2b 100644
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 @@ index cbbaf4a331..6e40893e52 100644
/* 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 @@ index 0000000000..ec937bf4ec
+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
++ <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>
+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
++ <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>
+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
++ <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>
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 @@ index debb96b322..b72933b526 100644
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 @@ index f4c089ed5d..d01b3fcab6 100644
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 @@ index e09f358cad..2cc478a8c6 100644
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
++ <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>
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 @@ index 0a684a720d..a1ee7928d3 100644
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 @@ index 5cacb286f3..bbb26607ef 100644
} __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 @@ index fdc5bdd65b..bc32bb132a 100644
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, $^) > [email protected]
+ mv -f [email protected] $@
++
++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 @@ index c8093c5473..39061ce6c5 100644
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
++ <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>
+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 @@ index 5b15321f9b..e5165fb34b 100644
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 @@ index 5b15321f9b..e5165fb34b 100644
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 @@ index 5b15321f9b..e5165fb34b 100644
+ 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 @@ index 5b15321f9b..e5165fb34b 100644
# 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,14 +7365,18 @@ index 5b15321f9b..e5165fb34b 100644
$(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)
@@ -6798,7 +7855,7 @@ index 0000000000..9a47d8e97a
+ 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 @@ index 544cffbecd..227734da5c 100644
+ 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 @@ index 544cffbecd..227734da5c 100644
+ 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 @@ index 544cffbecd..227734da5c 100644
+ 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 @@ index 0000000000..6299e89837
+}
+
+#include <support/test-driver.c>
+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
++ <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>
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 @@ index 0000000000..63dac90e02
+}
+
+#include <support/test-driver.c>
+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
++ <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 \"@\"",
++ "[email protected]", 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>
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 @@ index 0000000000..05725225af
+ 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 @@ index 6a422713bd..659c6f16da 100644
@ 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
+ <https://www.gnu.org/licenses/>. */
+
++#include <ldsodefs.h>
+ #include <link.h>
+-#include <unwind.h>
+-
+-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 @@ index bb0ccd0811..3868bcc2f7 100644
/* 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 @@ index 0000000000..8f21ebe1b6
@@ -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 @@ index 68646ef199..7622af259c 100644
&& 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 --git a/debian/patches/series b/debian/patches/series
index 153a5d9d..7217d7dc 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -21,6 +21,8 @@ alpha/local-string-functions.diff
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
--- End Message ---