diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c index f2f20a5c1..1e3e01330 100644 --- a/iterator/iter_scrub.c +++ b/iterator/iter_scrub.c @@ -294,7 +294,14 @@ synth_cname_rrset(uint8_t** sname, size_t* snamelen, uint8_t* alias, if(ttl_t > MAX_TTL) ttl_t = MAX_TTL; ttl = (uint32_t)ttl_t; sldns_write_uint32(cn->rr_first->ttl_data, ttl); - sldns_write_uint32(rrset->rr_first->ttl_data, ttl); + /* Do NOT write the clamp back into the packet buffer: + * parse_packet already sized every name from the original + * bytes and rdata_copy re-walks them trusting those sizes; + * mutating packet bytes between the walks breaks that + * invariant (compression pointers can target these TTL + * bytes). The DNAME rrset receives the same clamp at store + * time in rdata_copy, so the DNAME and the synthesized + * CNAME still carry equal TTLs in the cache. */ } sldns_write_uint16(cn->rr_first->ttl_data+4, aliaslen); memmove(cn->rr_first->ttl_data+6, alias, aliaslen); diff --git a/util/data/dname.c b/util/data/dname.c index 5370aa6f9..9fbbe1092 100644 --- a/util/data/dname.c +++ b/util/data/dname.c @@ -192,34 +192,34 @@ pkt_dname_len(sldns_buffer* pkt) while(1) { /* read next label */ if(sldns_buffer_remaining(pkt) < 1) - return 0; + goto fail; labellen = sldns_buffer_read_u8(pkt); if(LABEL_IS_PTR(labellen)) { /* compression ptr */ uint16_t ptr; if(sldns_buffer_remaining(pkt) < 1) - return 0; + goto fail; ptr = PTR_OFFSET(labellen, sldns_buffer_read_u8(pkt)); if(ptrcount++ > MAX_COMPRESS_PTRS) - return 0; /* loop! */ + goto fail; /* loop! */ if(sldns_buffer_limit(pkt) <= ptr) - return 0; /* out of bounds! */ + goto fail; /* out of bounds! */ if(!endpos) endpos = sldns_buffer_position(pkt); sldns_buffer_set_position(pkt, ptr); } else { /* label contents */ if(labellen > 0x3f) - return 0; /* label too long */ + goto fail; /* label too long */ len += 1 + labellen; if(len > LDNS_MAX_DOMAINLEN) - return 0; + goto fail; if(labellen == 0) { /* end of dname */ break; } if(sldns_buffer_remaining(pkt) < labellen) - return 0; + goto fail; sldns_buffer_skip(pkt, (ssize_t)labellen); } } @@ -227,6 +227,13 @@ pkt_dname_len(sldns_buffer* pkt) sldns_buffer_set_position(pkt, endpos); return len; +fail: + /* Restore the position on failure too: callers (rdata_copy) compute + * the consumed field length from the buffer position and must not + * see a partial walk of a name that failed to parse. */ + if(endpos) + sldns_buffer_set_position(pkt, endpos); + return 0; } int diff --git a/util/data/msgreply.c b/util/data/msgreply.c index 0beb893c3..7fdca8c71 100644 --- a/util/data/msgreply.c +++ b/util/data/msgreply.c @@ -314,6 +314,8 @@ rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to, dname_pkt_copy(pkt, to, sldns_buffer_current(pkt)); to += pkt_dname_len(pkt); + if(sldns_buffer_position(pkt)-oldpos > pkt_len) + return 0; /* malformed: walks diverged */ pkt_len -= sldns_buffer_position(pkt)-oldpos; count--; len = 0;