Repository navigation
wire2host: free a replaced TSIG record in ldns_wire2pkt() (#317) - #319
Merged
wtoorop merged 1 commit intoSep 23, 2026
Merged
Conversation
The ADDITIONAL-section loop in ldns_wire2pkt() calls
ldns_pkt_set_tsig(packet, rr);
for every TSIG (type 250) record it parses. That setter is a bare
assignment (packet.c:691) - it neither frees nor takes ownership of the
previous _tsig_rr - so a message that carries more than one parseable
TSIG record silently orphans each earlier one. ldns_pkt_free() only
releases the last surviving _tsig_rr, so the overwritten records (and the
owner dname allocated by ldns_wire2dname()) can never be reclaimed.
RFC 8945 allows at most one TSIG per message, but a malformed message is
exactly the input an attacker controls, and LeakSanitizer reports it
deterministically:
$ ./repro poc.bin # 34-byte DNS message, arcount=3, two TSIG RRs
Direct leak of 48 byte(s) in 1 object(s) allocated from:
NLnetLabs#1 ldns_rr_new rr.c:33
NLnetLabs#2 ldns_wire2rr wire2host.c:325
NLnetLabs#3 ldns_wire2pkt wire2host.c:459
Indirect leak of 24 byte(s) ...
Indirect leak of 1 byte(s) ...
SUMMARY: AddressSanitizer: 73 byte(s) leaked in 3 allocation(s).
Free the record that is about to be replaced. The reproducer above leaks
nothing after the change, and so does a message with four TSIG records; a
message with a single TSIG is unaffected.
wtoorop
added a commit
that referenced
this pull request
Sep 23, 2026
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.
The ADDITIONAL-section loop in
ldns_wire2pkt()callsfor every TSIG (type 250) record it parses (
wire2host.c:479). That setter isa bare assignment (
packet.c:691):It neither frees nor takes ownership of the previous
_tsig_rr, so a messagecarrying more than one parseable TSIG record silently orphans each earlier one.
ldns_pkt_free()(packet.c:917) releases only the last surviving_tsig_rr,so the overwritten records - and the owner dname allocated by
ldns_wire2dname()- can never be reclaimed.RFC 8945 allows at most one TSIG per message, but a malformed message is
exactly the input an attacker controls; a long-running parser that consumes
attacker-supplied messages grows without bound (OOM DoS). No memory corruption,
no info leak.
Reproducer
A 34-byte DNS message: 12-byte header with
arcount=3, followed by two TSIG RRs(
owner=root,type=0x00FA,class=0x1013,ttl=0,rdlen=0):Only the documented public sequence is used, so this is not harness misuse:
With ASan + LSan:
Fix
Release the record that is about to be replaced:
ldns_rr_free()is NULL-safe, so the explicit check is mostly for clarity.Testing
73 byte(s) leaked in 3 allocation(s)before,no leak after.
Fixes #317.