Skip to content

Commit b7c61d7

Browse files
committed
- Fix CVE-2024-8508, unbounded name compression could lead to denial of
service.
1 parent 01883fb commit b7c61d7

File tree

1 file changed

+46
-31
lines changed

1 file changed

+46
-31
lines changed

util/data/msgencode.c

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
#define RETVAL_TRUNC -4
6363
/** return code that means all is peachy keen. Equal to DNS rcode NOERROR */
6464
#define RETVAL_OK 0
65+
/** Max compressions we are willing to perform; more than that will result
66+
* in semi-compressed messages, or truncated even on TCP for huge messages, to
67+
* avoid locking the CPU for long */
68+
#define MAX_COMPRESSION_PER_MESSAGE 120
6569

6670
/**
6771
* Data structure to help domain name compression in outgoing messages.
@@ -284,15 +288,17 @@ write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs,
284288

285289
/** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */
286290
static int
287-
compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
288-
struct regional* region, struct compress_tree_node** tree,
289-
size_t owner_pos, uint16_t* owner_ptr, int owner_labs)
291+
compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
292+
struct regional* region, struct compress_tree_node** tree,
293+
size_t owner_pos, uint16_t* owner_ptr, int owner_labs,
294+
size_t* compress_count)
290295
{
291296
struct compress_tree_node* p;
292297
struct compress_tree_node** insertpt = NULL;
293298
if(!*owner_ptr) {
294299
/* compress first time dname */
295-
if((p = compress_tree_lookup(tree, key->rk.dname,
300+
if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
301+
(p = compress_tree_lookup(tree, key->rk.dname,
296302
owner_labs, &insertpt))) {
297303
if(p->labs == owner_labs)
298304
/* avoid ptr chains, since some software is
@@ -301,6 +307,7 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
301307
if(!write_compressed_dname(pkt, key->rk.dname,
302308
owner_labs, p))
303309
return RETVAL_TRUNC;
310+
(*compress_count)++;
304311
/* check if typeclass+4 ttl + rdatalen is available */
305312
if(sldns_buffer_remaining(pkt) < 4+4+2)
306313
return RETVAL_TRUNC;
@@ -313,7 +320,8 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
313320
if(owner_pos <= PTR_MAX_OFFSET)
314321
*owner_ptr = htons(PTR_CREATE(owner_pos));
315322
}
316-
if(!compress_tree_store(key->rk.dname, owner_labs,
323+
if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
324+
!compress_tree_store(key->rk.dname, owner_labs,
317325
owner_pos, region, p, insertpt))
318326
return RETVAL_OUTMEM;
319327
} else {
@@ -333,20 +341,24 @@ compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
333341

334342
/** compress any domain name to the packet, return RETVAL_* */
335343
static int
336-
compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs,
337-
struct regional* region, struct compress_tree_node** tree)
344+
compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs,
345+
struct regional* region, struct compress_tree_node** tree,
346+
size_t* compress_count)
338347
{
339348
struct compress_tree_node* p;
340349
struct compress_tree_node** insertpt = NULL;
341350
size_t pos = sldns_buffer_position(pkt);
342-
if((p = compress_tree_lookup(tree, dname, labs, &insertpt))) {
351+
if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
352+
(p = compress_tree_lookup(tree, dname, labs, &insertpt))) {
343353
if(!write_compressed_dname(pkt, dname, labs, p))
344354
return RETVAL_TRUNC;
355+
(*compress_count)++;
345356
} else {
346357
if(!dname_buffer_write(pkt, dname))
347358
return RETVAL_TRUNC;
348359
}
349-
if(!compress_tree_store(dname, labs, pos, region, p, insertpt))
360+
if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
361+
!compress_tree_store(dname, labs, pos, region, p, insertpt))
350362
return RETVAL_OUTMEM;
351363
return RETVAL_OK;
352364
}
@@ -364,9 +376,9 @@ type_rdata_compressable(struct ub_packed_rrset_key* key)
364376

365377
/** compress domain names in rdata, return RETVAL_* */
366378
static int
367-
compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen,
368-
struct regional* region, struct compress_tree_node** tree,
369-
const sldns_rr_descriptor* desc)
379+
compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen,
380+
struct regional* region, struct compress_tree_node** tree,
381+
const sldns_rr_descriptor* desc, size_t* compress_count)
370382
{
371383
int labs, r, rdf = 0;
372384
size_t dname_len, len, pos = sldns_buffer_position(pkt);
@@ -380,8 +392,8 @@ compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen,
380392
switch(desc->_wireformat[rdf]) {
381393
case LDNS_RDF_TYPE_DNAME:
382394
labs = dname_count_size_labels(rdata, &dname_len);
383-
if((r=compress_any_dname(rdata, pkt, labs, region,
384-
tree)) != RETVAL_OK)
395+
if((r=compress_any_dname(rdata, pkt, labs, region,
396+
tree, compress_count)) != RETVAL_OK)
385397
return r;
386398
rdata += dname_len;
387399
todolen -= dname_len;
@@ -449,7 +461,8 @@ static int
449461
packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
450462
uint16_t* num_rrs, time_t timenow, struct regional* region,
451463
int do_data, int do_sig, struct compress_tree_node** tree,
452-
sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset)
464+
sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset,
465+
size_t* compress_count)
453466
{
454467
size_t i, j, owner_pos;
455468
int r, owner_labs;
@@ -477,9 +490,9 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
477490
for(i=0; i<data->count; i++) {
478491
/* rrset roundrobin */
479492
j = (i + rr_offset) % data->count;
480-
if((r=compress_owner(key, pkt, region, tree,
481-
owner_pos, &owner_ptr, owner_labs))
482-
!= RETVAL_OK)
493+
if((r=compress_owner(key, pkt, region, tree,
494+
owner_pos, &owner_ptr, owner_labs,
495+
compress_count)) != RETVAL_OK)
483496
return r;
484497
sldns_buffer_write(pkt, &key->rk.type, 2);
485498
sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
@@ -489,8 +502,8 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
489502
else sldns_buffer_write_u32(pkt, data->rr_ttl[j]-adjust);
490503
if(c) {
491504
if((r=compress_rdata(pkt, data->rr_data[j],
492-
data->rr_len[j], region, tree, c))
493-
!= RETVAL_OK)
505+
data->rr_len[j], region, tree, c,
506+
compress_count)) != RETVAL_OK)
494507
return r;
495508
} else {
496509
if(sldns_buffer_remaining(pkt) < data->rr_len[j])
@@ -510,9 +523,9 @@ packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
510523
return RETVAL_TRUNC;
511524
sldns_buffer_write(pkt, &owner_ptr, 2);
512525
} else {
513-
if((r=compress_any_dname(key->rk.dname,
514-
pkt, owner_labs, region, tree))
515-
!= RETVAL_OK)
526+
if((r=compress_any_dname(key->rk.dname,
527+
pkt, owner_labs, region, tree,
528+
compress_count)) != RETVAL_OK)
516529
return r;
517530
if(sldns_buffer_remaining(pkt) <
518531
4+4+data->rr_len[i])
@@ -544,7 +557,8 @@ static int
544557
insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
545558
sldns_buffer* pkt, size_t rrsets_before, time_t timenow,
546559
struct regional* region, struct compress_tree_node** tree,
547-
sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset)
560+
sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset,
561+
size_t* compress_count)
548562
{
549563
int r;
550564
size_t i, setstart;
@@ -560,7 +574,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
560574
setstart = sldns_buffer_position(pkt);
561575
if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
562576
pkt, num_rrs, timenow, region, 1, 1, tree,
563-
s, qtype, dnssec, rr_offset))
577+
s, qtype, dnssec, rr_offset, compress_count))
564578
!= RETVAL_OK) {
565579
/* Bad, but if due to size must set TC bit */
566580
/* trim off the rrset neatly. */
@@ -573,7 +587,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
573587
setstart = sldns_buffer_position(pkt);
574588
if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
575589
pkt, num_rrs, timenow, region, 1, 0, tree,
576-
s, qtype, dnssec, rr_offset))
590+
s, qtype, dnssec, rr_offset, compress_count))
577591
!= RETVAL_OK) {
578592
sldns_buffer_set_position(pkt, setstart);
579593
return r;
@@ -584,7 +598,7 @@ insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
584598
setstart = sldns_buffer_position(pkt);
585599
if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
586600
pkt, num_rrs, timenow, region, 0, 1, tree,
587-
s, qtype, dnssec, rr_offset))
601+
s, qtype, dnssec, rr_offset, compress_count))
588602
!= RETVAL_OK) {
589603
sldns_buffer_set_position(pkt, setstart);
590604
return r;
@@ -677,6 +691,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
677691
struct compress_tree_node* tree = 0;
678692
int r;
679693
size_t rr_offset;
694+
size_t compress_count=0;
680695

681696
sldns_buffer_clear(buffer);
682697
if(udpsize < sldns_buffer_limit(buffer))
@@ -723,7 +738,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
723738
arep.rrsets = &qinfo->local_alias->rrset;
724739
if((r=insert_section(&arep, 1, &ancount, buffer, 0,
725740
timezero, region, &tree, LDNS_SECTION_ANSWER,
726-
qinfo->qtype, dnssec, rr_offset)) != RETVAL_OK) {
741+
qinfo->qtype, dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
727742
if(r == RETVAL_TRUNC) {
728743
/* create truncated message */
729744
sldns_buffer_write_u16_at(buffer, 6, ancount);
@@ -738,7 +753,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
738753
/* insert answer section */
739754
if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
740755
0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype,
741-
dnssec, rr_offset)) != RETVAL_OK) {
756+
dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
742757
if(r == RETVAL_TRUNC) {
743758
/* create truncated message */
744759
sldns_buffer_write_u16_at(buffer, 6, ancount);
@@ -756,7 +771,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
756771
if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer,
757772
rep->an_numrrsets, timenow, region, &tree,
758773
LDNS_SECTION_AUTHORITY, qinfo->qtype,
759-
dnssec, rr_offset)) != RETVAL_OK) {
774+
dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
760775
if(r == RETVAL_TRUNC) {
761776
/* create truncated message */
762777
sldns_buffer_write_u16_at(buffer, 8, nscount);
@@ -773,7 +788,7 @@ reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
773788
if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer,
774789
rep->an_numrrsets + rep->ns_numrrsets, timenow, region,
775790
&tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype,
776-
dnssec, rr_offset)) != RETVAL_OK) {
791+
dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
777792
if(r == RETVAL_TRUNC) {
778793
/* no need to set TC bit, this is the additional */
779794
sldns_buffer_write_u16_at(buffer, 10, arcount);

0 commit comments

Comments
 (0)