glibc: 2.42-58 -> 2.42-61, fixes CVE-2026-4438

Wasn't backported to the 2.42 branch when I bumped for CVE-2026-4437 (#503779).
This commit is contained in:
Maximilian Bosch
2026-04-04 08:25:52 +02:00
parent 7e2000dd3e
commit c66e479167
2 changed files with 499 additions and 2 deletions
@@ -6657,3 +6657,500 @@ index 0000000000..1171baef51
+}
+
+#include <support/test-driver.c>
commit 426378547e6ddead92f28f5558a124eb0821d2f9
Author: Carlos O'Donell <carlos@redhat.com>
Date: Fri Mar 20 17:14:33 2026 -0400
resolv: Check hostname for validity (CVE-2026-4438)
The processed hostname in getanswer_ptr should be correctly checked to
avoid invalid characters from being allowed, including shell
metacharacters. It is a security issue to fail to check the returned
hostname for validity.
A regression test is added for invalid metacharacters and other cases
of invalid or valid characters.
No regressions on x86_64-linux-gnu.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
(cherry picked from commit e10977481f4db4b2a3ce34fa4c3a1e26651ae312)
diff --git a/resolv/Makefile b/resolv/Makefile
index 0ba5fba710..088a22ea18 100644
--- a/resolv/Makefile
+++ b/resolv/Makefile
@@ -107,6 +107,7 @@ tests += \
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 \
@@ -306,6 +307,8 @@ $(objpfx)tst-resolv-res_init-thread: $(objpfx)libresolv.so \
$(shared-thread-library)
$(objpfx)tst-resolv-invalid-cname: $(objpfx)libresolv.so \
$(shared-thread-library)
+$(objpfx)tst-resolv-invalid-ptr: $(objpfx)libresolv.so \
+ $(shared-thread-library)
$(objpfx)tst-resolv-noaaaa: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-noaaaa-vc: $(objpfx)libresolv.so $(shared-thread-library)
$(objpfx)tst-resolv-nondecimal: $(objpfx)libresolv.so $(shared-thread-library)
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
index 27096edad2..1bc2e1df95 100644
--- a/resolv/nss_dns/dns-host.c
+++ b/resolv/nss_dns/dns-host.c
@@ -866,7 +866,7 @@ getanswer_ptr (unsigned char *packet, size_t packetlen,
char hname[MAXHOSTNAMELEN + 1];
if (__ns_name_unpack (c.begin, c.end, rr.rdata,
name_buffer, sizeof (name_buffer)) < 0
- || !__res_binary_hnok (expected_name)
+ || !__res_binary_hnok (name_buffer)
|| __ns_name_ntop (name_buffer, hname, sizeof (hname)) < 0)
{
*h_errnop = NO_RECOVERY;
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 \"@\"",
+ "test.@.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"#\"",
+ "test.#.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"%\"",
+ "test.%%.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of \"^\"",
+ "test.^.ptr.example", NO_RECOVERY, true },
+
+ /* Test for invalid UTF-8 characters (2-byte, 4-byte, 6-byte). */
+ { "Invalid use of UTF-8 (2-byte, U+00C0-U+00C2)",
+ "ÁÂÃ.test.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of UTF-8 (4-byte, U+0750-U+0752)",
+ "ݐݑݒ.test.ptr.example", NO_RECOVERY, true },
+ { "Invalid use of UTF-8 (6-byte, U+0904-U+0906)",
+ "ऄअआ.test.ptr.example", NO_RECOVERY, true },
+
+ /* Test for "-" which may be valid depending on position. */
+ { "Invalid leading \"-\"",
+ "-test.ptr.example", NO_RECOVERY, true },
+ { "Valid trailing \"-\"",
+ "test-.ptr.example", 0, false },
+ { "Valid mid-label use of \"-\"",
+ "te-st.ptr.example", 0, false },
+
+ /* Test for "_" which is always valid in any position. */
+ { "Valid leading use of \"_\"",
+ "_test.ptr.example", 0, false },
+ { "Valid mid-label use of \"_\"",
+ "te_st.ptr.example", 0, false },
+ { "Valid trailing use of \"_\"",
+ "test_.ptr.example", 0, false },
+
+ /* Sanity test the broader set [A-Za-z0-9_-] of valid characters. */
+ { "Valid \"[A-Z]\"",
+ "test.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ptr.example", 0, false },
+ { "Valid \"[a-z]\"",
+ "test.abcdefghijklmnopqrstuvwxyz.ptr.example", 0, false },
+ { "Valid \"[0-9]\"",
+ "test.0123456789.ptr.example", 0, false },
+ { "Valid mixed use of \"[A-Za-z0-9_-]\"",
+ "test.012abcABZ_-.ptr.example", 0, false },
+ };
+
+static void
+response (const struct resolv_response_context *ctx,
+ struct resolv_response_builder *b,
+ const char *qname, uint16_t qclass, uint16_t qtype)
+{
+ TEST_COMPARE (qclass, C_IN);
+
+ /* We only test PTR. */
+ TEST_COMPARE (qtype, T_PTR);
+
+ unsigned int count, count1;
+ char *tail = NULL;
+
+ /* The test implementation can handle up to 255 tests. */
+ if (strstr (qname, "in-addr.arpa") != NULL
+ && sscanf (qname, "%u.%ms", &count, &tail) == 2)
+ TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa");
+ else if (sscanf (qname, "%x.%x.%ms", &count, &count1, &tail) == 3)
+ {
+ TEST_COMPARE_STRING (tail, "\
+0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa");
+ count |= count1 << 4;
+ }
+ else
+ FAIL_EXIT1 ("invalid QNAME: %s\n", qname);
+ free (tail);
+
+ /* Cross check. Count has a fixed bound (soft limit). */
+ TEST_VERIFY (count >= 0 && count <= 255);
+
+ /* We have a fixed number of tests (hard limit). */
+ TEST_VERIFY_EXIT (count < array_length (test_items));
+
+ struct resolv_response_flags flags = {};
+ resolv_response_init (b, flags);
+ resolv_response_add_question (b, qname, qclass, qtype);
+ resolv_response_section (b, ns_s_an);
+
+ /* Actual answer record. */
+ resolv_response_open_record (b, qname, qclass, qtype, 60);
+
+ /* Record the answer. */
+ resolv_response_add_name (b, test_items[count].answer);
+ resolv_response_close_record (b);
+}
+
+/* Perform one check using a reverse lookup. */
+static void
+check_reverse (int af, int count)
+{
+ TEST_VERIFY (af == AF_INET || af == AF_INET6);
+ TEST_VERIFY_EXIT (count < array_length (test_items));
+
+ /* Generate an address to query for each test. */
+ char addr[sizeof (struct in6_addr)] = { 0 };
+ socklen_t addrlen;
+ if (af == AF_INET)
+ {
+ addr[0] = (char) 192;
+ addr[1] = (char) 168;
+ addr[2] = (char) 0;
+ addr[3] = (char) count;
+ addrlen = 4;
+ }
+ else
+ {
+ addr[0] = 0x20;
+ addr[1] = 0x01;
+ addr[2] = 0x0d;
+ addr[3] = 0xb8;
+ addr[4] = addr[5] = addr[6] = addr[7] = 0x0;
+ addr[8] = addr[9] = addr[10] = addr[11] = 0x0;
+ addr[12] = 0x0;
+ addr[13] = 0x0;
+ addr[14] = 0x0;
+ addr[15] = (char) count;
+ addrlen = 16;
+ }
+
+ h_errno = 0;
+ struct hostent *answer = gethostbyaddr (addr, addrlen, af);
+
+ /* Verify h_errno is as expected. */
+ TEST_COMPARE (h_errno, test_items[count].expected);
+ if (h_errno != test_items[count].expected)
+ /* And print more information if it's not. */
+ printf ("INFO: %s\n", test_items[count].test);
+
+ if (test_items[count].fail)
+ {
+ /* We expected a failure so verify answer is NULL. */
+ TEST_VERIFY (answer == NULL);
+ /* If it's not NULL we should print out what we received. */
+ if (answer != NULL)
+ printf ("error: unexpected success: %s\n",
+ support_format_hostent (answer));
+ }
+ else
+ /* We don't expect a failure so answer must be valid. */
+ TEST_COMPARE_STRING (answer->h_name, test_items[count].answer);
+}
+
+static int
+do_test (void)
+{
+ struct resolv_test *obj = resolv_test_start
+ ((struct resolv_redirect_config)
+ {
+ .response_callback = response
+ });
+
+ for (int i = 0; i < array_length (test_items); i++)
+ {
+ check_reverse (AF_INET, i);
+ check_reverse (AF_INET6, i);
+ }
+ resolv_test_end (obj);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
commit 68099ccc941664481386c62cba40bbc5dac8b00e
Author: Xi Ruoyao <xry111@xry111.site>
Date: Tue Feb 3 16:20:12 2026 +0800
elf: parse /proc/self/maps as the last resort to find the gap for tst-link-map-contiguous-ldso
The initialization process of libc.so calls mmap() several times and the
kernel may lay the maps into the gap. If all pages in the gap are
occupied, the test would not be able to find the gap with mmap() and the
test would fail.
The failure reproduces most frequently on LoongArch because with the
commonly used page size (16 KiB) the gap only contains 4 pages and the
probability they are all occupied is not near to zero.
With the changes in the patch, a test run may output:
info: ld.so link map is not contiguous
info: object "/dev/zero" found at 0x7ffff1fe0000 - 0x7ffff1fe4000
info: anonymous mapping found at 0x7ffff1fe4000 - 0x7ffff1fec000
Also take the chance to fix a mistake in the "object found at" message
which has puzzled me during the initial debug session.
Signed-off-by: Xi Ruoyao <xry111@xry111.site>
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
(cherry picked from commit aed8390a6a22e5751fc12704c0c5f2a8271fc286)
diff --git a/elf/tst-link-map-contiguous-ldso.c b/elf/tst-link-map-contiguous-ldso.c
index 04de808bb2..f0e26682f2 100644
--- a/elf/tst-link-map-contiguous-ldso.c
+++ b/elf/tst-link-map-contiguous-ldso.c
@@ -18,15 +18,73 @@
#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)
{
@@ -64,16 +122,18 @@ do_test (void)
if ((void *) dlfo.dlfo_link_map != (void *) l)
{
printf ("info: object \"%s\" found at %p\n",
- dlfo.dlfo_link_map->l_name, ptr);
+ dlfo.dlfo_link_map->l_name, expected);
gap_found = true;
}
}
else
TEST_COMPARE (dlfo_ret, -1);
+
xmunmap (ptr, 1);
addr += pagesize;
}
- if (!gap_found)
+
+ if (!gap_found && !find_gap_with_proc_self_map (l))
FAIL ("no ld.so gap found");
}
else
commit a56a2943d2ce541102c630142c2eae0fbfc5886b
Author: Michael Jeanson <mjeanson@efficios.com>
Date: Fri Feb 20 11:01:00 2026 -0500
tests: fix tst-rseq with Linux 7.0
A sub-test of tst-rseq is to validate the return code and errno of the
rseq syscall when attempting to register the exact same rseq area as was
done in the dynamic loader.
This involves finding the rseq area address by adding the
'__rseq_offset' to the thread pointer and calculating the area size from
the AT_RSEQ_FEATURE_SIZE auxiliary vector. However the test currently
calculates the size of the rseq area allocation in the TLS block which
must be a multiple of AT_RSEQ_ALIGN.
Up until now that happened to be the same value since the feature size
and alignment exposed by the kernel were below the minimum ABI size of
32. Starting with Linux 7.0 the feature size has reached 33 while the
alignment is now 64.
This results in the test trying to re-register the rseq area with a
different size and thus not getting the expected errno value.
Signed-off-by: Michael Jeanson <mjeanson@efficios.com>
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
(cherry picked from commit 67f303b47dc584f204e3f2441b9832082415eebc)
diff --git a/sysdeps/unix/sysv/linux/tst-rseq.c b/sysdeps/unix/sysv/linux/tst-rseq.c
index 00181cfefb..e83ea2b939 100644
--- a/sysdeps/unix/sysv/linux/tst-rseq.c
+++ b/sysdeps/unix/sysv/linux/tst-rseq.c
@@ -48,8 +48,7 @@ do_rseq_main_test (void)
size_t rseq_align = MAX (getauxval (AT_RSEQ_ALIGN), RSEQ_MIN_ALIGN);
size_t rseq_feature_size = MAX (getauxval (AT_RSEQ_FEATURE_SIZE),
RSEQ_AREA_SIZE_INITIAL_USED);
- size_t rseq_alloc_size = roundup (MAX (rseq_feature_size,
- RSEQ_AREA_SIZE_INITIAL_USED), rseq_align);
+ size_t rseq_reg_size = MAX (rseq_feature_size, RSEQ_AREA_SIZE_INITIAL);
struct rseq *rseq_abi = __thread_pointer () + __rseq_offset;
TEST_VERIFY_EXIT (rseq_thread_registered ());
@@ -89,8 +88,8 @@ do_rseq_main_test (void)
/* Test a rseq registration with the same arguments as the internal
registration which should fail with errno == EBUSY. */
TEST_VERIFY (((unsigned long) rseq_abi % rseq_align) == 0);
- TEST_VERIFY (__rseq_size <= rseq_alloc_size);
- int ret = syscall (__NR_rseq, rseq_abi, rseq_alloc_size, 0, RSEQ_SIG);
+ TEST_VERIFY (__rseq_size <= rseq_reg_size);
+ int ret = syscall (__NR_rseq, rseq_abi, rseq_reg_size, 0, RSEQ_SIG);
TEST_VERIFY (ret != 0);
TEST_COMPARE (errno, EBUSY);
}
+2 -2
View File
@@ -51,7 +51,7 @@
let
version = "2.42";
patchSuffix = "-58";
patchSuffix = "-61";
sha256 = "sha256-0XdeMuRijmTvkw9DW2e7Y691may2viszW58Z8WUJ8X8=";
in
@@ -69,7 +69,7 @@ stdenv.mkDerivation (
/*
No tarballs for stable upstream branch, only https://sourceware.org/git/glibc.git and using git would complicate bootstrapping.
$ git fetch --all -p && git checkout origin/release/2.40/master && git describe
glibc-2.42-51-gcbf39c26b2
glibc-2.42-61-ga56a2943d2
$ git show --minimal --reverse glibc-2.42.. ':!ADVISORIES' > 2.42-master.patch
To compare the archive contents zdiff can be used.