resolve: the PTR answer of dnsname walked inside its record data

The label walk of a DNS PTR answer ran to the end of the whole message,
not to the end of the record's data, and took a compression pointer for
a label length, so it could write past the end of the answer buffer on
the stack. An answer of 3841 bytes or more reached past the buffer
itself. A record that ended exactly at the end of the message was also
read one byte past it. The walk now stops at the end of the record's
data, a name that does not end there, or one that is compressed or
empty is not accepted, and the answer loop reads nothing past the
message.
This commit is contained in:
Vladimir Dubrovin 2026-10-30 12:42:01 +03:00
parent 9be415b731
commit e9714fa4df

View File

@ -167,6 +167,7 @@ uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_
k += 4;
if(na > 255) na = 255; /* somebody is very evil */
for (j = 0; j < na; j++) { /* now there should be answers */
if(k >= len) break;
while(buf[k] < 192 && buf[k] !=0 && (k+buf[k]+14) < len) k+= (buf[k] + 1);
if(!buf[k]) k--;
if((k+(af == AF_INET6?28:16)) > len) {
@ -195,15 +196,17 @@ uint32_t udpresolve(int af, unsigned char * name, unsigned char * value, uint32_
k+= (12 + flen);
continue; /* we need A PTR */
}
for (s2 = buf + k + 12; s2 < (buf + k + 12 + len) && *s2; ){
s1 = s2 + ((unsigned)*s2) + 1;
s1 = buf + k + 12 + flen; /* the end of this record's data, inside the answer */
for (s2 = buf + k + 12; s2 < s1 && *s2 && *s2 < 64; ){
unsigned char *next = s2 + ((unsigned)*s2) + 1;
*s2 = '.';
s2 = s1;
s2 = next;
}
*s2 = 0;
if(s2 >= s1 || *s2 || s2 == buf + k + 12)
break; /* the name runs past its data, is compressed or empty */
if(param->username)free(param->username);
param->username = (unsigned char *)strdup ((char *)buf + k + 13);
return udpresolve(af,param->username, value, NULL, NULL, 2);
}
}