]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] rdns: reject DNS labels that overrun the packet
authorVsevolod Stakhov <vsevolod@rspamd.com>
Wed, 20 May 2026 11:05:08 +0000 (12:05 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Wed, 20 May 2026 11:05:08 +0000 (12:05 +0100)
rdns_parse_labels computes the name length in a first pass that only
reads label length bytes, then a second pass copies the label data.
The first pass never checked that a label's data actually fits within
the packet, so a reply whose final label declared more bytes than
remained made the second-pass memcpy read past the end of the reply
buffer. On the DNS-over-TCP path that buffer is malloc'd to exactly
the advertised message size, so the over-read ran past the allocation.

Validate in the first pass that both plain and compressed label data
stay within the packet, and reject the name otherwise. Also fix an
off-by-one in rdns_decompress_label where an offset equal to the
packet length was accepted and read one byte past the end.

contrib/librdns/parse.c

index f9025e159734091e856c026f041011a06ce03844..17afd8e8951f64ad5fb1d0c9da9e735acb1e6e09 100644 (file)
@@ -31,7 +31,7 @@ rdns_decompress_label (uint8_t *begin, uint16_t *len, uint16_t max)
 {
        uint16_t offset = (*len);
 
-       if (offset > max) {
+       if (offset >= max) {
                return NULL;
        }
        *len = *(begin + offset);
@@ -169,6 +169,10 @@ rdns_parse_labels (struct rdns_resolver *resolver,
                                        rdns_info  ("invalid pointer in DNS packet");
                                        return false;
                                }
+                               if (l + *l + 1 > end) {
+                                       rdns_info("invalid DNS label: compressed data overruns the packet");
+                                       return false;
+                               }
                                begin = l;
                                length = end - begin;
                                p = l + *l + 1;
@@ -182,6 +186,10 @@ rdns_parse_labels (struct rdns_resolver *resolver,
                        }
                }
                else {
+                       if (p + llen + 1 > end) {
+                               rdns_info("invalid DNS label: data overruns the packet");
+                               return false;
+                       }
                        namelen += llen;
                        p += llen + 1;
                        labels ++;