--- /dev/null
+From 5c6fca0c62ce5bd6e68e259f138097756cbafd4d Mon Sep 17 00:00:00 2001
+From: Carlos O'Donell <carlos@redhat.com>
+Date: Fri, 20 Mar 2026 16:43:33 -0400
+Subject: [PATCH] resolv: Count records correctly (CVE-2026-4437)
+
+The answer section boundary was previously ignored, and the code in
+getanswer_ptr would iterate past the last resource record, but not
+beyond the end of the returned data. This could lead to subsequent data
+being interpreted as answer records, thus violating the DNS
+specification. Such resource records could be maliciously crafted and
+hidden from other tooling, but processed by the glibc stub resolver and
+acted upon by the application. While we trust the data returned by the
+configured recursive resolvers, we should not trust its format and
+should validate it as required. It is a security issue to incorrectly
+process the DNS protocol.
+
+A regression test is added for response section crossing.
+
+No regressions on x86_64-linux-gnu.
+
+Reviewed-by: Collin Funk <collin.funk1@gmail.com>
+(cherry picked from commit 9f5f18aab40ec6b61fa49a007615e6077e9a979b)
+---
+ resolv/Makefile | 4 +
+ resolv/nss_dns/dns-host.c | 2 +-
+ resolv/tst-resolv-dns-section.c | 162 ++++++++++++++++++++++++++++++++
+ 3 files changed, 167 insertions(+), 1 deletion(-)
+ create mode 100644 resolv/tst-resolv-dns-section.c
+
+diff --git a/resolv/Makefile b/resolv/Makefile
+index b74c8f325e..d5ef63cdbc 100644
+--- a/resolv/Makefile
++++ b/resolv/Makefile
+@@ -107,6 +107,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 \
+@@ -118,6 +119,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.
+@@ -301,6 +303,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 6a60c87532..893137027e 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 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>
+--
+2.43.7
+