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.
{
uint16_t offset = (*len);
- if (offset > max) {
+ if (offset >= max) {
return NULL;
}
*len = *(begin + offset);
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;
}
}
else {
+ if (p + llen + 1 > end) {
+ rdns_info("invalid DNS label: data overruns the packet");
+ return false;
+ }
namelen += llen;
p += llen + 1;
labels ++;