Repository navigation
dnssec: do not read the window length byte past the end of an NSEC bitmap - #320
Merged
wtoorop merged 1 commit intoOct 6, 2026
Conversation
…tmap ldns_nsec_bitmap_covers_type(), ldns_nsec_bitmap_set_type() and ldns_nsec_bitmap_clear_type() walk an NSEC/NSEC3 type bitmap as a sequence of windows, each introduced by a two-byte header (window number, bitmap length). The loop condition only guaranteed that one byte remained (dptr < dend), while both the body and the advance step read dptr[1], the length byte. A bitmap whose data ends immediately after a window number therefore caused a one-byte read past the end of the buffer. Require two bytes before entering the loop, so a trailing partial window header is treated as the end of the bitmap. A well-formed bitmap always has both header bytes present, so behaviour on valid input is unchanged, including for a type in the last window.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
ldns_nsec_bitmap_covers_type(),ldns_nsec_bitmap_set_type()andldns_nsec_bitmap_clear_type()indnssec.cwalk an NSEC/NSEC3 type bitmap asa sequence of windows, each introduced by a two-byte header (window number,
bitmap length), per RFC 3845 section 2.1.2.
The loop condition only guaranteed that one byte remained:
Both the
ifcondition and the advance step readdptr[1], the length byte.The existing
dptr + dptr[1] + 2 <= dendcheck guards the bitmap bytes fromthe third byte onward, but nothing guards the read of
dptr[1]itself. Abitmap whose data ends immediately after a window number is one byte short of
a complete header, and all three functions then read one byte past the end of
the buffer.
The fix requires two bytes before entering the loop, so a trailing partial
window header is treated as the end of the bitmap.
Reproducer
Built against this tree with
-fsanitize=address,undefined:Before:
After: no ASan report, the call returns
false.Checks run
ldns_rr_new_frm_str()onexample.com. 3600 IN NSEC a.example.com. A MX RRSIG NSEC TYPE1234, givesbyte-identical answers before and after for
covers_typeon present types(including
TYPE1234, which lives in the last window and is the case atoo-strict guard would break), on absent types, and for a
set_type/clear_typeround trip.test/12-unit-tests-dnssec,test/13-unit-tests-base,test/15-unit-tests-rrtypesandtest/16-unit-tests-edns, built againstthe patched library under ASan/UBSan: all pass.
Reachability, for the record
This is a one-byte overread with no write primitive. On the
ldns_wire2pkt()path it is currently not reachable, becauseldns_wire2rdf()allocates each generic rdata field using the RR's totalRDLENGTHrather than that field's own length, which leaves at least one byteof slack past the bitmap rdf's logical size and absorbs the overread. That
masking is incidental rather than intended: allocating
cur_rdf_lengthinstead, which is the obvious correctness fix for that allocation, would
unmask this for every wire-parsed NSEC/NSEC3 RR. Code that builds an
LDNS_RDF_TYPE_BITMAPrdf directly throughldns_rdf_new()orldns_rdf_new_frm_data()with a tightly sized buffer, as the reproducer doesand as the Python bindings allow, reaches it today.
Reported by mail first; posting as a public pull request at NLnet Labs'
suggestion.