new file mode 100644
@@ -0,0 +1,159 @@
+From e47c203f91cd8b749c8736bc18d75a31ffdec8f4 Mon Sep 17 00:00:00 2001
+From: jmestwa-coder <jmestwa@gmail.com>
+Date: Sun, 12 Apr 2026 23:50:54 +0530
+Subject: [PATCH] validate RR counts before preallocation to reject malformed
+ packets early (#1134)
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+## Summary
+
+Validate DNS RR counts before preallocation to reject malformed packets
+early.
+
+## What’s the issue?
+
+The parser currently trusts RR counts from the DNS header (`ANCOUNT`,
+`NSCOUNT`, `ARCOUNT`) without verifying that the packet contains enough
+data.
+
+A malformed packet can claim a large number of records without providing
+sufficient payload, causing the parser to proceed with unnecessary work
+and incorrect handling.
+
+## What changed?
+
+- Added validation to ensure RR counts are feasible based on the
+remaining buffer size
+- Moved RR preallocation from `ares_dns_parse_header` to
+`ares_dns_parse_buf`
+- Perform validation after question parsing and before preallocation
+- Return `ARES_EBADRESP` early for malformed packets
+
+## Why this approach?
+
+This keeps the parser aligned with its natural flow:
+
+parse header → parse question → validate → preallocate → parse records
+
+It avoids trusting unverified input and ensures malformed packets are
+rejected early and consistently.
+
+## Test
+
+Added a regression test (`ParseMalformedRRCount`) that reproduces the
+issue using a malformed packet with inconsistent RR count.
+
+Signed-off-by: @jmestwa-coder
+
+CVE: CVE-2026-69186
+Upstream-Status: Backport [https://github.com/c-ares/c-ares/commit/e47c203f91cd8b749c8736bc18d75a31ffdec8f4]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ src/lib/ares_dns_parse.c | 55 ++++++++++++++++++++++------------------
+ test/ares-test-parse.cc | 11 ++++++++
+ 2 files changed, 42 insertions(+), 24 deletions(-)
+
+diff --git a/src/lib/ares_dns_parse.c b/src/lib/ares_dns_parse.c
+index 2d2a7bb2..01f3f43c 100644
+--- a/src/lib/ares_dns_parse.c
++++ b/src/lib/ares_dns_parse.c
+@@ -848,30 +848,6 @@ static ares_status_t ares_dns_parse_header(ares__buf_t *buf, unsigned int flags,
+
+ (*dnsrec)->raw_rcode = rcode;
+
+- if (*ancount > 0) {
+- status =
+- ares_dns_record_rr_prealloc(*dnsrec, ARES_SECTION_ANSWER, *ancount);
+- if (status != ARES_SUCCESS) {
+- goto fail;
+- }
+- }
+-
+- if (*nscount > 0) {
+- status =
+- ares_dns_record_rr_prealloc(*dnsrec, ARES_SECTION_AUTHORITY, *nscount);
+- if (status != ARES_SUCCESS) {
+- goto fail;
+- }
+- }
+-
+- if (*arcount > 0) {
+- status =
+- ares_dns_record_rr_prealloc(*dnsrec, ARES_SECTION_ADDITIONAL, *arcount);
+- if (status != ARES_SUCCESS) {
+- goto fail;
+- }
+- }
+-
+ return ARES_SUCCESS;
+
+ fail:
+@@ -1134,6 +1110,8 @@ static ares_status_t ares_dns_parse_buf(ares__buf_t *buf, unsigned int flags,
+ ares_dns_record_t **dnsrec)
+ {
+ ares_status_t status;
++ size_t total_rr_count;
++ const size_t min_rr_wire_len = 11;
+ unsigned short qdcount;
+ unsigned short ancount;
+ unsigned short nscount;
+@@ -1194,6 +1172,35 @@ static ares_status_t ares_dns_parse_buf(ares__buf_t *buf, unsigned int flags,
+ }
+ }
+
++ total_rr_count = (size_t)ancount + (size_t)nscount + (size_t)arcount;
++ if (total_rr_count > ares__buf_len(buf) / min_rr_wire_len) {
++ status = ARES_EBADRESP;
++ goto fail;
++ }
++
++ if (ancount > 0) {
++ status = ares_dns_record_rr_prealloc(*dnsrec, ARES_SECTION_ANSWER, ancount);
++ if (status != ARES_SUCCESS) {
++ goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
++ }
++ }
++
++ if (nscount > 0) {
++ status =
++ ares_dns_record_rr_prealloc(*dnsrec, ARES_SECTION_AUTHORITY, nscount);
++ if (status != ARES_SUCCESS) {
++ goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
++ }
++ }
++
++ if (arcount > 0) {
++ status =
++ ares_dns_record_rr_prealloc(*dnsrec, ARES_SECTION_ADDITIONAL, arcount);
++ if (status != ARES_SUCCESS) {
++ goto fail; /* LCOV_EXCL_LINE: OutOfMemory */
++ }
++ }
++
+ /* Parse Answers */
+ for (i = 0; i < ancount; i++) {
+ status = ares_dns_parse_rr(buf, flags, ARES_SECTION_ANSWER, *dnsrec);
+diff --git a/test/ares-test-parse.cc b/test/ares-test-parse.cc
+index ff1fe14d..81fd7deb 100644
+--- a/test/ares-test-parse.cc
++++ b/test/ares-test-parse.cc
+@@ -207,6 +207,17 @@ TEST_F(LibraryTest, ParseFullyCompressedName) {
+ ares_free_hostent(host);
+ }
+
++TEST_F(LibraryTest, ParseMalformedRRCount) {
++ ares_dns_record_t *dnsrec = NULL;
++ const unsigned char data[] = {
++ 0x12, 0x34, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01,
++ 0x00, 0x00, 0x00, 0x00, 0x01, 'a', 0x00, 0x00,
++ 0x01, 0x00, 0x01,
++ };
++
++ EXPECT_EQ(ARES_EBADRESP, ares_dns_parse(data, sizeof(data), 0, &dnsrec));
++ EXPECT_EQ(nullptr, dnsrec);
++}
+
+ } // namespace test
+ } // namespace ares
@@ -6,7 +6,9 @@ LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE.md;md5=fdbc58a6da11a9f68aa73c453818decc"
SRC_URI = "https://github.com/c-ares/c-ares/releases/download/cares-1_27_0/${BPN}-${PV}.tar.gz \
- file://run-ptest"
+ file://run-ptest \
+ file://CVE-2026-69186.patch \
+"
SRC_URI[sha256sum] = "0a72be66959955c43e2af2fbd03418e82a2bd5464604ec9a62147e37aceb420b"
PACKAGECONFIG ?= "${@bb.utils.contains('PTEST_ENABLED', '1', 'tests', '', d)}"