diff --git a/glibc-upstream-2.39-289.patch b/glibc-upstream-2.39-289.patch new file mode 100644 index 0000000..1fc3ef7 --- /dev/null +++ b/glibc-upstream-2.39-289.patch @@ -0,0 +1,287 @@ +commit 60b039bf6afe7f124e1d4fd3ec3bef2d745d567c +Author: Sergey Kolosov +Date: Tue Jun 25 11:19:03 2024 +0200 + + socket: Add new test for shutdown + + This commit adds shutdown test with SHUT_RD, SHUT_WR, SHUT_RDWR for an + UNIX socket connection. + + Reviewed-by: DJ Delorie + (cherry picked from commit 50f5a09e68e0c4ea60f5dfee3cc6963df2762e14) + +diff --git a/socket/Makefile b/socket/Makefile +index fc1bd0a2608f04bc..df732fa9b72570cd 100644 +--- a/socket/Makefile ++++ b/socket/Makefile +@@ -71,6 +71,7 @@ tests := \ + tst-cmsg_cloexec \ + tst-cmsghdr \ + tst-connect \ ++ tst-shutdown \ + tst-sockopt \ + # tests + +diff --git a/socket/tst-shutdown.c b/socket/tst-shutdown.c +new file mode 100644 +index 0000000000000000..a305e5e494cc38f1 +--- /dev/null ++++ b/socket/tst-shutdown.c +@@ -0,0 +1,257 @@ ++/* Test the shutdown function. ++ Copyright (C) 2024 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct connection ++{ ++ int sockets[2]; ++}; ++ ++void ++establish_connection (struct connection *conn) ++{ ++ if (socketpair (AF_UNIX, SOCK_STREAM, 0, conn->sockets) != 0) ++ { ++ FAIL_EXIT1 ("socketpair (AF_UNIX, SOCK_STREAM, 0): %m\n"); ++ } ++} ++ ++void ++close_connection (struct connection *conn) ++{ ++ xclose (conn->sockets[0]); ++ xclose (conn->sockets[1]); ++} ++ ++/* Open a file and check that shutdown fails with the ENOTSOCK error code. */ ++void ++do_test_enotsock (void) ++{ ++ /* Open file and check that shutdown will fail with ENOTSOCK. */ ++ int fd = xopen ("/dev/null", O_RDWR, 0); ++ ++ int result = shutdown (fd, SHUT_RD); ++ if (result == 0 || errno != ENOTSOCK) ++ { ++ FAIL_EXIT1 ("shutdown should fail with ENOTSOCK"); ++ } ++ xclose (fd); ++} ++ ++/* Test shutdown with SHUT_RD. */ ++void ++do_test_shut_rd (void) ++{ ++ struct connection conn; ++ const char *str = "AAAAAAA"; ++ int len = 8; ++ int ret; ++ void *s_buf = xmalloc (len); ++ bzero (s_buf, len); ++ ++ establish_connection (&conn); ++ int server = conn.sockets[0]; ++ int client = conn.sockets[1]; ++ ++ /* Call shutdown with SHUT_RD on server socket. */ ++ if (shutdown (server, SHUT_RD) != 0) ++ { ++ FAIL_EXIT1 ("shutdown with SHUT_RD on socket %d failed", server); ++ } ++ ++ ret = send (server, str, len, 0); ++ if (ret <= 0) ++ { ++ FAIL_EXIT1 ("send (%d, data, %d): %m", server, len); ++ } ++ ++ ret = recv (client, s_buf, len, 0); ++ if (ret <= 0) ++ { ++ FAIL_EXIT1 ("recv (%d, data, %d): %m", client, len); ++ } ++ ++ TEST_COMPARE_BLOB (str, len, s_buf, len); ++ ++ /* Send data should be disallowed on shutdown socket. */ ++ errno = 0; ++ ret = send (client, str, len, MSG_NOSIGNAL); ++ if (ret >= 0 || errno != EPIPE) ++ { ++ FAIL_EXIT1 ("Send on SHUT_RD socket should be disallowed: %m"); ++ } ++ ++ /* Recv should return zero and no error. */ ++ errno = 0; ++ ret = recv (server, s_buf, len, 0); ++ if (ret != 0 || errno != 0) ++ { ++ FAIL_EXIT1 ("recv should return 0 without error: %m"); ++ } ++ ++ close_connection (&conn); ++} ++ ++/* Test shutdown with SHUT_WR. */ ++void ++do_test_shut_wr (void) ++{ ++ struct connection conn; ++ const char *str1 = "CCCCCCC"; ++ const char *str2 = "DDDDDDD"; ++ const char *str3 = "EEEEEEE"; ++ int len = 8; ++ int ret; ++ void *c_buf = xmalloc (len); ++ void *s_buf = xmalloc (len); ++ ++ establish_connection (&conn); ++ int server = conn.sockets[0]; ++ int client = conn.sockets[1]; ++ ++ xwrite (client, str1, len); ++ ++ if (shutdown (client, SHUT_WR) != 0) ++ { ++ FAIL_EXIT1 ("shutdown with SHUT_WR on socket %d failed", client); ++ } ++ ++ ret = send (client, str2, len, MSG_NOSIGNAL); ++ if (ret >= 0) ++ { ++ FAIL_EXIT1 ("send on SHUT_WR socket should fail"); ++ } ++ ++ /* Read data written before shutdown and check if it's correct. */ ++ xread (server, s_buf, len); ++ TEST_COMPARE_BLOB (str1, len, s_buf, len); ++ ++ /* Second read should return zero without error. */ ++ errno = 0; ++ if (read (server, s_buf, len) != 0 || errno != 0) ++ { ++ FAIL_EXIT1 ("read after shutdown should return zero without error: %m"); ++ } ++ ++ /* Write some data to socket and check it still can be read on other side. */ ++ memcpy (s_buf, str3, len); ++ xwrite (server, s_buf, len); ++ ++ xread (client, c_buf, len); ++ TEST_COMPARE_BLOB (s_buf, len, c_buf, len); ++ ++ close_connection (&conn); ++} ++ ++/* Test shutdown with SHUT_RDWR. */ ++void ++do_test_shut_rdwr (void) ++{ ++ struct connection conn; ++ struct sockaddr peer; ++ socklen_t peer_len = sizeof (peer); ++ ++ const char *str1 = "FFFFFFF"; ++ const char *str2 = "GGGGGGG"; ++ int len = 8; ++ int ret; ++ void *s_buf = xmalloc (len); ++ bzero (s_buf, len); ++ ++ establish_connection (&conn); ++ int server = conn.sockets[0]; ++ int client = conn.sockets[1]; ++ ++ /* Send some data to both sockets before shutdown. */ ++ xwrite (client, str1, len); ++ xwrite (server, str2, len); ++ ++ /* Call shutdown with SHUT_RDWR on client socket. */ ++ if (shutdown (client, SHUT_RDWR) != 0) ++ { ++ FAIL_EXIT1 ("shutdown with SHUT_RDWR on socket %d failed", client); ++ } ++ ++ /* Verify that socket is still connected. */ ++ xgetsockname (client, &peer, &peer_len); ++ ++ /* Read data written before shutdown. */ ++ xread (client, s_buf, len); ++ TEST_COMPARE_BLOB (s_buf, len, str2, len); ++ ++ /* Second read should return zero, but no error. */ ++ errno = 0; ++ if (read (client, s_buf, len) != 0 || errno != 0) ++ { ++ FAIL_EXIT1 ("read after shutdown should return zero without error: %m"); ++ } ++ ++ /* Send some data to shutdown socket and expect error. */ ++ errno = 0; ++ ret = send (server, str2, len, MSG_NOSIGNAL); ++ if (ret >= 0 || errno != EPIPE) ++ { ++ FAIL_EXIT1 ("send to RDWR shutdown socket should fail with EPIPE"); ++ } ++ ++ /* Read data written before shutdown. */ ++ xread (server, s_buf, len); ++ TEST_COMPARE_BLOB (s_buf, len, str1, len); ++ ++ /* Second read should return zero, but no error. */ ++ errno = 0; ++ if (read (server, s_buf, len) != 0 || errno != 0) ++ { ++ FAIL_EXIT1 ("read after shutdown should return zero without error: %m"); ++ } ++ ++ /* Send some data to shutdown socket and expect error. */ ++ errno = 0; ++ ret = send (client, str1, len, MSG_NOSIGNAL); ++ if (ret >= 0 || errno != EPIPE) ++ { ++ FAIL_EXIT1 ("send to RDWR shutdown socket should fail with EPIPE"); ++ } ++ ++ close_connection (&conn); ++} ++ ++static int ++do_test (void) ++{ ++ do_test_enotsock (); ++ do_test_shut_rd (); ++ do_test_shut_wr (); ++ do_test_shut_rdwr (); ++ ++ return 0; ++} ++ ++#include diff --git a/glibc-upstream-2.39-290.patch b/glibc-upstream-2.39-290.patch new file mode 100644 index 0000000..4b65c0e --- /dev/null +++ b/glibc-upstream-2.39-290.patch @@ -0,0 +1,36 @@ +commit ba29a36aa32caa03a49d8b3116baecac6a5754ed +Author: Yury Khrustalev +Date: Thu Nov 6 12:57:58 2025 +0000 + + posix: Fix invalid flags test for p{write,read}v2 + + Two tests fail from time to time when a new flag is added for the + p{write,read}v2 functions in a new Linux kernel: + + - misc/tst-preadvwritev2 + - misc/tst-preadvwritev64v2 + + This disrupts when testing Glibc on a system with a newer kernel + and it seems we can try improve testing for invalid flags setting + all the bits that are not supposed to be supported (rather than + setting only the next unsupported bit). + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit 58a31b4316f1f687184eb147ffa1c676bc6a190e) + +diff --git a/misc/tst-preadvwritev2-common.c b/misc/tst-preadvwritev2-common.c +index 4556421a4394f3a1..ec990491330377c3 100644 +--- a/misc/tst-preadvwritev2-common.c ++++ b/misc/tst-preadvwritev2-common.c +@@ -109,9 +109,8 @@ do_test_with_invalid_iov (void) + static void + do_test_with_invalid_flags (void) + { +- /* Set the next bit from the mask of all supported flags. */ +- int invalid_flag = RWF_SUPPORTED != 0 ? __builtin_clz (RWF_SUPPORTED) : 2; +- invalid_flag = 0x1 << ((sizeof (int) * CHAR_BIT) - invalid_flag); ++ /* Set all the bits that are not used by the supported flags. */ ++ int invalid_flag = ~RWF_SUPPORTED; + + char buf[32]; + const struct iovec vec = { .iov_base = buf, .iov_len = sizeof (buf) }; diff --git a/glibc-upstream-2.39-291.patch b/glibc-upstream-2.39-291.patch new file mode 100644 index 0000000..aa5e280 --- /dev/null +++ b/glibc-upstream-2.39-291.patch @@ -0,0 +1,25 @@ +commit 2760e4c5ed7ed142e126ab165dd953619e70df28 +Author: Florian Weimer +Date: Fri Nov 28 11:46:09 2025 +0100 + + iconvdata: Fix invalid pointer arithmetic in ANSI_X3.110 module + + The expression inptr + 1 can technically be invalid: if inptr == inend, + inptr may point one element past the end of an array. + + Reviewed-by: Adhemerval Zanella + (cherry picked from commit e98bd0c54d5e296ad1be91b6fe35260c6b87e733) + +diff --git a/iconvdata/ansi_x3.110.c b/iconvdata/ansi_x3.110.c +index 869cdf654087185f..44bd12a863ae8f54 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/glibc-upstream-2.39-292.patch b/glibc-upstream-2.39-292.patch new file mode 100644 index 0000000..a2d2255 --- /dev/null +++ b/glibc-upstream-2.39-292.patch @@ -0,0 +1,27 @@ +commit c53cd6e738ad627a2c5a18dad697d2accf7b5fd2 +Author: Florian Weimer +Date: Mon Jan 26 17:12:37 2026 +0100 + + posix: Run tst-wordexp-reuse-mem test + + The test was not properly scheduled for execution with a Makefile + dependency. + + Fixes commit 80cc58ea2de214f85b0a1d902a3b668ad2ecb302 ("posix: Reset + wordexp_t fields with WRDE_REUSE (CVE-2025-15281 / BZ 33814"). + + (cherry picked from commit bed2db02f3183e93f21d506786c5f884a1dec9e7) + +diff --git a/posix/Makefile b/posix/Makefile +index 6dc240a5bf9f3046..443b1ed10bd4d564 100644 +--- a/posix/Makefile ++++ b/posix/Makefile +@@ -493,7 +493,7 @@ tests-special += \ + $(objpfx)tst-pcre-mem.out \ + $(objpfx)tst-rxspencer-no-utf8-mem.out \ + $(objpfx)tst-vfork3-mem.out \ +- $(objpfx)tst-wordexp-reuse.out \ ++ $(objpfx)tst-wordexp-reuse-mem.out \ + # tests-special + endif + endif diff --git a/glibc-upstream-2.39-293.patch b/glibc-upstream-2.39-293.patch new file mode 100644 index 0000000..6849e16 --- /dev/null +++ b/glibc-upstream-2.39-293.patch @@ -0,0 +1,234 @@ +commit 5663ab0b833df187b15e7bb4b18173e22beb8bd1 +Author: Carlos O'Donell +Date: Fri Mar 20 16:43:33 2026 -0400 + + resolv: Count records correctly (CVE-2026-4437) + + The answer section boundary was previously ignored, and the code in + getanswer_ptr would iterate past the last resource record, but not + beyond the end of the returned data. This could lead to subsequent data + being interpreted as answer records, thus violating the DNS + specification. Such resource records could be maliciously crafted and + hidden from other tooling, but processed by the glibc stub resolver and + acted upon by the application. While we trust the data returned by the + configured recursive resolvers, we should not trust its format and + should validate it as required. It is a security issue to incorrectly + process the DNS protocol. + + A regression test is added for response section crossing. + + No regressions on x86_64-linux-gnu. + + Reviewed-by: Collin Funk + (cherry picked from commit 9f5f18aab40ec6b61fa49a007615e6077e9a979b) + +diff --git a/resolv/Makefile b/resolv/Makefile +index 05fb04edf1082690..8dba948b632d3abc 100644 +--- a/resolv/Makefile ++++ b/resolv/Makefile +@@ -104,6 +104,7 @@ tests += \ + tst-resolv-basic \ + tst-resolv-binary \ + tst-resolv-byaddr \ ++ tst-resolv-dns-section \ + tst-resolv-edns \ + tst-resolv-invalid-cname \ + tst-resolv-network \ +@@ -115,6 +116,7 @@ tests += \ + tst-resolv-semi-failure \ + tst-resolv-short-response \ + tst-resolv-trailing \ ++ # tests + + # This test calls __res_context_send directly, which is not exported + # from libresolv. +@@ -292,6 +294,8 @@ $(objpfx)tst-resolv-aliases: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-binary: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-byaddr: $(objpfx)libresolv.so $(shared-thread-library) ++$(objpfx)tst-resolv-dns-section: $(objpfx)libresolv.so \ ++ $(shared-thread-library) + $(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library) + $(objpfx)tst-resolv-res_init: $(objpfx)libresolv.so +diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c +index 95a7b3f0e5b6855a..74a7c08d0f055d6e 100644 +--- a/resolv/nss_dns/dns-host.c ++++ b/resolv/nss_dns/dns-host.c +@@ -820,7 +820,7 @@ getanswer_ptr (unsigned char *packet, size_t packetlen, + /* expected_name may be updated to point into this buffer. */ + unsigned char name_buffer[NS_MAXCDNAME]; + +- while (ancount > 0) ++ for (; ancount > 0; --ancount) + { + struct ns_rr_wire rr; + if (!__ns_rr_cursor_next (&c, &rr)) +diff --git a/resolv/tst-resolv-dns-section.c b/resolv/tst-resolv-dns-section.c +new file mode 100644 +index 0000000000000000..1171baef51e3cc36 +--- /dev/null ++++ b/resolv/tst-resolv-dns-section.c +@@ -0,0 +1,162 @@ ++/* Test handling of invalid section transitions (bug 34014). ++ Copyright (C) 2022-2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Name of test, and the second section type. */ ++struct item { ++ const char *test; ++ int ns_section; ++}; ++ ++static const struct item test_items[] = ++ { ++ { "Test crossing from ns_s_an to ns_s_ar.", ns_s_ar }, ++ { "Test crossing from ns_s_an to ns_s_an.", ns_s_ns }, ++ ++ { NULL, 0 }, ++ }; ++ ++/* The response is designed to contain the following: ++ - An Answer section with one T_PTR record that is skipped. ++ - A second section with a semantically invalid T_PTR record. ++ The original defect is that the response parsing would cross ++ section boundaries and handle the additional section T_PTR ++ as if it were an answer. A conforming implementation would ++ stop as soon as it reaches the end of the section. */ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_COMPARE (qclass, C_IN); ++ ++ /* We only test PTR. */ ++ TEST_COMPARE (qtype, T_PTR); ++ ++ unsigned int count; ++ char *tail = NULL; ++ ++ if (strstr (qname, "in-addr.arpa") != NULL ++ && sscanf (qname, "%u.%ms", &count, &tail) == 2) ++ TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa"); ++ else if (sscanf (qname, "%x.%ms", &count, &tail) == 2) ++ { ++ TEST_COMPARE_STRING (tail, "\ ++0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"); ++ } ++ else ++ FAIL_EXIT1 ("invalid QNAME: %s\n", qname); ++ free (tail); ++ ++ /* We have a bounded number of possible tests. */ ++ TEST_VERIFY (count >= 0); ++ TEST_VERIFY (count <= 15); ++ ++ struct resolv_response_flags flags = {}; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ ++ /* Actual answer record, but the wrong name (skipped). */ ++ resolv_response_open_record (b, "1.0.0.10.in-addr.arpa", qclass, qtype, 60); ++ ++ /* Record the answer. */ ++ resolv_response_add_name (b, "test.ptr.example.net"); ++ resolv_response_close_record (b); ++ ++ /* Add a second section to test section boundary crossing. */ ++ resolv_response_section (b, test_items[count].ns_section); ++ /* Semantically incorrect, but hide a T_PTR entry. */ ++ resolv_response_open_record (b, qname, qclass, qtype, 60); ++ resolv_response_add_name (b, "wrong.ptr.example.net"); ++ resolv_response_close_record (b); ++} ++ ++ ++/* Perform one check using a reverse lookup. */ ++static void ++check_reverse (int af, int count) ++{ ++ TEST_VERIFY (af == AF_INET || af == AF_INET6); ++ TEST_VERIFY (count < array_length (test_items)); ++ ++ char addr[sizeof (struct in6_addr)] = { 0 }; ++ socklen_t addrlen; ++ if (af == AF_INET) ++ { ++ addr[0] = (char) 192; ++ addr[1] = (char) 168; ++ addr[2] = (char) 0; ++ addr[3] = (char) count; ++ addrlen = 4; ++ } ++ else ++ { ++ addr[0] = 0x20; ++ addr[1] = 0x01; ++ addr[2] = 0x0d; ++ addr[3] = 0xb8; ++ addr[4] = addr[5] = addr[6] = addr[7] = 0x0; ++ addr[8] = addr[9] = addr[10] = addr[11] = 0x0; ++ addr[12] = 0x0; ++ addr[13] = 0x0; ++ addr[14] = 0x0; ++ addr[15] = count; ++ addrlen = 16; ++ } ++ ++ h_errno = 0; ++ struct hostent *answer = gethostbyaddr (addr, addrlen, af); ++ TEST_VERIFY (answer == NULL); ++ TEST_VERIFY (h_errno == NO_RECOVERY); ++ if (answer != NULL) ++ printf ("error: unexpected success: %s\n", ++ support_format_hostent (answer)); ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response ++ }); ++ ++ for (int i = 0; test_items[i].test != NULL; i++) ++ { ++ check_reverse (AF_INET, i); ++ check_reverse (AF_INET6, i); ++ } ++ ++ resolv_test_end (obj); ++ ++ return 0; ++} ++ ++#include diff --git a/glibc-upstream-2.39-294.patch b/glibc-upstream-2.39-294.patch new file mode 100644 index 0000000..045780b --- /dev/null +++ b/glibc-upstream-2.39-294.patch @@ -0,0 +1,314 @@ +commit 9344c796f7a4ac8f2c59de63f3e1e936b51e817d +Author: Carlos O'Donell +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 + (cherry picked from commit e10977481f4db4b2a3ce34fa4c3a1e26651ae312) + +diff --git a/resolv/Makefile b/resolv/Makefile +index 8dba948b632d3abc..87a5c1ccc6dabc8a 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 \ +@@ -305,6 +306,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 74a7c08d0f055d6e..b8f5d61b2ae20a4e 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 0000000000000000..0c802ab96780efb0 +--- /dev/null ++++ b/resolv/tst-resolv-invalid-ptr.c +@@ -0,0 +1,255 @@ ++/* Test handling of invalid T_PTR results (bug 34015). ++ Copyright (C) 2022-2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Name of test, the answer, the expected error return, and if we ++ expect the call to fail. */ ++struct item { ++ const char *test; ++ const char *answer; ++ int expected; ++ bool fail; ++}; ++ ++static const struct item test_items[] = ++ { ++ /* Test for invalid characters. */ ++ { "Invalid use of \"|\"", ++ "test.|.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"&\"", ++ "test.&.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \";\"", ++ "test.;.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"<\"", ++ "test.<.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \">\"", ++ "test.>.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"(\"", ++ "test.(.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \")\"", ++ "test.).ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"$\"", ++ "test.$.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"`\"", ++ "test.`.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\\"", ++ "test.\\.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\'\"", ++ "test.'.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\"\"", ++ "test.\".ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \" \"", ++ "test. .ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\t\"", ++ "test.\t.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\n\"", ++ "test.\n.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"\\r\"", ++ "test.\r.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"*\"", ++ "test.*.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"?\"", ++ "test.?.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"[\"", ++ "test.[.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"]\"", ++ "test.].ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \",\"", ++ "test.,.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"~\"", ++ "test.~.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \":\"", ++ "test.:.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"!\"", ++ "test.!.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"@\"", ++ "test.@.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"#\"", ++ "test.#.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"%\"", ++ "test.%%.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of \"^\"", ++ "test.^.ptr.example", NO_RECOVERY, true }, ++ ++ /* Test for invalid UTF-8 characters (2-byte, 4-byte, 6-byte). */ ++ { "Invalid use of UTF-8 (2-byte, U+00C0-U+00C2)", ++ "ÁÂÃ.test.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of UTF-8 (4-byte, U+0750-U+0752)", ++ "ݐݑݒ.test.ptr.example", NO_RECOVERY, true }, ++ { "Invalid use of UTF-8 (6-byte, U+0904-U+0906)", ++ "ऄअआ.test.ptr.example", NO_RECOVERY, true }, ++ ++ /* Test for "-" which may be valid depending on position. */ ++ { "Invalid leading \"-\"", ++ "-test.ptr.example", NO_RECOVERY, true }, ++ { "Valid trailing \"-\"", ++ "test-.ptr.example", 0, false }, ++ { "Valid mid-label use of \"-\"", ++ "te-st.ptr.example", 0, false }, ++ ++ /* Test for "_" which is always valid in any position. */ ++ { "Valid leading use of \"_\"", ++ "_test.ptr.example", 0, false }, ++ { "Valid mid-label use of \"_\"", ++ "te_st.ptr.example", 0, false }, ++ { "Valid trailing use of \"_\"", ++ "test_.ptr.example", 0, false }, ++ ++ /* Sanity test the broader set [A-Za-z0-9_-] of valid characters. */ ++ { "Valid \"[A-Z]\"", ++ "test.ABCDEFGHIJKLMNOPQRSTUVWXYZ.ptr.example", 0, false }, ++ { "Valid \"[a-z]\"", ++ "test.abcdefghijklmnopqrstuvwxyz.ptr.example", 0, false }, ++ { "Valid \"[0-9]\"", ++ "test.0123456789.ptr.example", 0, false }, ++ { "Valid mixed use of \"[A-Za-z0-9_-]\"", ++ "test.012abcABZ_-.ptr.example", 0, false }, ++ }; ++ ++static void ++response (const struct resolv_response_context *ctx, ++ struct resolv_response_builder *b, ++ const char *qname, uint16_t qclass, uint16_t qtype) ++{ ++ TEST_COMPARE (qclass, C_IN); ++ ++ /* We only test PTR. */ ++ TEST_COMPARE (qtype, T_PTR); ++ ++ unsigned int count, count1; ++ char *tail = NULL; ++ ++ /* The test implementation can handle up to 255 tests. */ ++ if (strstr (qname, "in-addr.arpa") != NULL ++ && sscanf (qname, "%u.%ms", &count, &tail) == 2) ++ TEST_COMPARE_STRING (tail, "0.168.192.in-addr.arpa"); ++ else if (sscanf (qname, "%x.%x.%ms", &count, &count1, &tail) == 3) ++ { ++ TEST_COMPARE_STRING (tail, "\ ++0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa"); ++ count |= count1 << 4; ++ } ++ else ++ FAIL_EXIT1 ("invalid QNAME: %s\n", qname); ++ free (tail); ++ ++ /* Cross check. Count has a fixed bound (soft limit). */ ++ TEST_VERIFY (count >= 0 && count <= 255); ++ ++ /* We have a fixed number of tests (hard limit). */ ++ TEST_VERIFY_EXIT (count < array_length (test_items)); ++ ++ struct resolv_response_flags flags = {}; ++ resolv_response_init (b, flags); ++ resolv_response_add_question (b, qname, qclass, qtype); ++ resolv_response_section (b, ns_s_an); ++ ++ /* Actual answer record. */ ++ resolv_response_open_record (b, qname, qclass, qtype, 60); ++ ++ /* Record the answer. */ ++ resolv_response_add_name (b, test_items[count].answer); ++ resolv_response_close_record (b); ++} ++ ++/* Perform one check using a reverse lookup. */ ++static void ++check_reverse (int af, int count) ++{ ++ TEST_VERIFY (af == AF_INET || af == AF_INET6); ++ TEST_VERIFY_EXIT (count < array_length (test_items)); ++ ++ /* Generate an address to query for each test. */ ++ char addr[sizeof (struct in6_addr)] = { 0 }; ++ socklen_t addrlen; ++ if (af == AF_INET) ++ { ++ addr[0] = (char) 192; ++ addr[1] = (char) 168; ++ addr[2] = (char) 0; ++ addr[3] = (char) count; ++ addrlen = 4; ++ } ++ else ++ { ++ addr[0] = 0x20; ++ addr[1] = 0x01; ++ addr[2] = 0x0d; ++ addr[3] = 0xb8; ++ addr[4] = addr[5] = addr[6] = addr[7] = 0x0; ++ addr[8] = addr[9] = addr[10] = addr[11] = 0x0; ++ addr[12] = 0x0; ++ addr[13] = 0x0; ++ addr[14] = 0x0; ++ addr[15] = (char) count; ++ addrlen = 16; ++ } ++ ++ h_errno = 0; ++ struct hostent *answer = gethostbyaddr (addr, addrlen, af); ++ ++ /* Verify h_errno is as expected. */ ++ TEST_COMPARE (h_errno, test_items[count].expected); ++ if (h_errno != test_items[count].expected) ++ /* And print more information if it's not. */ ++ printf ("INFO: %s\n", test_items[count].test); ++ ++ if (test_items[count].fail) ++ { ++ /* We expected a failure so verify answer is NULL. */ ++ TEST_VERIFY (answer == NULL); ++ /* If it's not NULL we should print out what we received. */ ++ if (answer != NULL) ++ printf ("error: unexpected success: %s\n", ++ support_format_hostent (answer)); ++ } ++ else ++ /* We don't expect a failure so answer must be valid. */ ++ TEST_COMPARE_STRING (answer->h_name, test_items[count].answer); ++} ++ ++static int ++do_test (void) ++{ ++ struct resolv_test *obj = resolv_test_start ++ ((struct resolv_redirect_config) ++ { ++ .response_callback = response ++ }); ++ ++ for (int i = 0; i < array_length (test_items); i++) ++ { ++ check_reverse (AF_INET, i); ++ check_reverse (AF_INET6, i); ++ } ++ resolv_test_end (obj); ++ ++ return 0; ++} ++ ++#include diff --git a/glibc-upstream-2.39-295.patch b/glibc-upstream-2.39-295.patch new file mode 100644 index 0000000..63b27eb --- /dev/null +++ b/glibc-upstream-2.39-295.patch @@ -0,0 +1,127 @@ +commit 0dc95ae10929aba1cc660e97cfc94903c2568efa +Author: Xi Ruoyao +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 + Reviewed-by: Adhemerval Zanella + (cherry picked from commit aed8390a6a22e5751fc12704c0c5f2a8271fc286) + +diff --git a/elf/tst-link-map-contiguous-ldso.c b/elf/tst-link-map-contiguous-ldso.c +index 04de808bb234fe38..f0e26682f2858414 100644 +--- a/elf/tst-link-map-contiguous-ldso.c ++++ b/elf/tst-link-map-contiguous-ldso.c +@@ -18,15 +18,73 @@ + + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + #include ++#include + #include + #include + ++/* Slow path in case we cannot find a gap with mmap (when the runtime has ++ mapped all the pages in the gap for some reason). */ ++static bool ++find_gap_with_proc_self_map (const struct link_map *l) ++{ ++ int pagesize = getpagesize (); ++ ++ support_need_proc ("Reads /proc/self/maps to find gap in ld.so mapping"); ++ ++ /* Parse /proc/self/maps and find all the mappings in the ld.so range ++ but not from ld.so. */ ++ FILE *f = xfopen ("/proc/self/maps", "r"); ++ char *line = NULL, *path_ldso = NULL; ++ size_t len; ++ bool found = false; ++ while (xgetline (&line, &len, f)) ++ { ++ uintptr_t from, to; ++ char *path = NULL; ++ int r = sscanf (line, "%" SCNxPTR "-%" SCNxPTR "%*s%*s%*s%*s%ms", ++ &from, &to, &path); ++ ++ TEST_VERIFY (r == 2 || r == 3); ++ TEST_COMPARE (from % pagesize, 0); ++ TEST_COMPARE (to % pagesize, 0); ++ ++ if (path_ldso == NULL && l->l_map_start == from) ++ { ++ TEST_COMPARE (r, 3); ++ path_ldso = path; ++ continue; ++ } ++ ++ if (from > l->l_map_start && to < l->l_map_end ++ && (r == 2 || (path_ldso != NULL && strcmp (path, path_ldso)))) ++ { ++ if (r == 2) ++ printf ("info: anonymous mapping found at 0x%" PRIxPTR " - 0x%" ++ PRIxPTR "\n", from, to); ++ else ++ printf ("info: object \"%s\" found at 0x%" PRIxPTR " - 0x%" ++ PRIxPTR "\n", path, from, to); ++ ++ found = true; ++ } ++ ++ free (path); ++ } ++ ++ free (path_ldso); ++ free (line); ++ xfclose (f); ++ return found; ++} ++ + static int + do_test (void) + { +@@ -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 diff --git a/glibc.spec b/glibc.spec index a8bbaff..f3f0d0f 100644 --- a/glibc.spec +++ b/glibc.spec @@ -2357,7 +2357,7 @@ update_gconv_modules_cache () %endif %changelog -* Thu Apr 02 2026 Eduard Abdullin - 2.39-119.alma.1 +* Fri Apr 03 2026 Eduard Abdullin - 2.39-120.alma.1 - Overwrite target for x86_64_v2 - Update patch-git.lua to handle AlmaLinux branches correctly