]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dns-packet: bail out early if the packet is too short 42189/head
authorFrantisek Sumsal <frantisek@sumsal.cz>
Wed, 20 May 2026 08:47:22 +0000 (10:47 +0200)
committerFrantisek Sumsal <frantisek@sumsal.cz>
Wed, 20 May 2026 09:16:29 +0000 (11:16 +0200)
Let's bail out early if the packet claims to contain some
questions or answer RRs, but the remaining packet data size is not
enough to hold a single such entry.

Follow-up for e7cd836dcffb5f85d66a156904fc68f8b654a290.

src/shared/dns-packet.c

index bb17c94d4aa6039bd05395a95d1ff62c9645bd86..429e5c82c1774c7ce345caabca91a69785c3133a 100644 (file)
@@ -2446,7 +2446,7 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) {
 
 static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question) {
         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
-        unsigned n;
+        unsigned n, prealloc;
         int r;
 
         assert(ret_question);
@@ -2457,6 +2457,14 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question)
                 return 0;
         }
 
+        /* Calculate the maximum number of potential questions the remaining packet data can actually
+         * contain: p->size - p->rindex are the remaining unread bytes in the packet, and 5U is the minimum
+         * size of each question - 1 (QNAME) + 2 (QTYPE) + 2 (QCLASS). */
+        prealloc = (p->size - p->rindex) / 5U;
+        if (prealloc == 0)
+                /* QDCOUNT > 0 but there's not enough space left for a single question. */
+                return -EMSGSIZE;
+
         question = dns_question_new(n);
         if (!question)
                 return -ENOMEM;
@@ -2470,12 +2478,11 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question)
         /* Pre-allocate the question hashmap, but cap the pre-allocation to a number of questions the
          * packet can realistically contain. That is, pick the minimal value from the claimed number
          * of questions (n) and a maximum number of potential questions the remaining packet data can
-         * actually contain: p->size - p->rindex are the remaining unread bytes in the packet, and 5U
-         * is the minimum size of each question - 1 (QNAME) + 2 (QTYPE) + 2 (QCLASS).
+         * actually contain, see above.
          *
          * Note for the multiplication: higher multipliers give slightly higher efficiency through
          * hash collisions, but the gains quickly drop off after 2. */
-        r = set_reserve(keys, MIN(n, (p->size - p->rindex) / 5U) * 2);
+        r = set_reserve(keys, MIN(n, prealloc) * 2);
         if (r < 0)
                 return r;
 
@@ -2509,7 +2516,7 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question)
 
 static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
         _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
-        unsigned n;
+        unsigned n, prealloc;
         _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL;
         bool bad_opt = false;
         int r;
@@ -2522,12 +2529,18 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
                 return 0;
         }
 
+        /* Calculate the maximum number of potential RRs the remaining packet data can actually contain:
+         * p->size - p->rindex are the remaining unread bytes in the packet, and the 11U is the minimum size
+         * of each RR - 1 (NAME) + 2 (TYPE) + 2 (CLASS) + 4 (TTL) + 2 (RDLENGTH). */
+        prealloc = (p->size - p->rindex) / 11U;
+        if (prealloc == 0)
+                /* RRCOUNT > 0 but there's not enough space left for a single RR. */
+                return -EMSGSIZE;
+
         /* Pre-allocate the answer hashmap, but cap the pre-allocation to a number of RRs the packet can
          * realistically contain. That is, pick the minimal value from the claimed number of RRs (n) and a
-         * maximum number of potential RRs the remaining packet data can actually contain: p->size -
-         * p->rindex are the remaining unread bytes in the packet, and the 11U is the minimum size of each RR
-         * - 1 (NAME) + 2 (TYPE) + 2 (CLASS) + 4 (TTL) + 2 (RDLENGTH). */
-        answer = dns_answer_new(MIN(n, (p->size - p->rindex) / 11U));
+         * maximum number of potential RRs the remaining packet data can actually contain, see above. */
+        answer = dns_answer_new(MIN(n, prealloc));
         if (!answer)
                 return -ENOMEM;