I found a safe-path invariant violation in domain 0.11.1 around DNSKEY parsing.
Summary
Dnskey::new enforces that wire-format RDATA length is at most 65535 bytes via LongRecordData::check_len(...).
However, Dnskey::parse does not enforce the same bound and directly calls unsafe { Dnskey::new_unchecked(...) } with parser.remaining() - 4 bytes.
This allows constructing a Dnskey with total wire length > 65535 using only safe APIs.
Poc:
use domain::rdata::dnssec::Dnskey;
use octseq::Parser;
fn main() {
// Build a DNSKEY-like blob larger than the 16-bit DNS RDATA limit.
let mut buf = vec![0u8; 70_000];
buf[2] = 3;
buf[3] = 8;
let mut parser = Parser::from_ref(&buf[..]);
// Safe parse accepts the oversized payload.
let dnskey = Dnskey::parse(&mut parser).expect("Parse should succeed");
let total_wire_length = 4 + dnskey.into_public_key().as_ref().len();
println!("total wire length: {total_wire_length}");
// This should not be possible if the constructor invariant were preserved.
assert!(total_wire_length > 65_535);
}
Observed output:
It breaks the documented safety precondition of new_unchecked through a safe public API path, creating a soundness-risk and making downstream assumptions about DNS RDATA size unreliable.
Suggested fix:
Before calling new_unchecked in Dnskey::parse, validate total RDATA length using the same check as Dnskey::new (or just construct via Dnskey::new after parsing fields).
Optionally, audit similar parse -> new_unchecked paths in other record types for the same invariant bypass pattern.
I found a safe-path invariant violation in domain 0.11.1 around DNSKEY parsing.
Summary
Dnskey::newenforces that wire-format RDATA length is at most 65535 bytes viaLongRecordData::check_len(...).However,
Dnskey::parsedoes not enforce the same bound and directly callsunsafe { Dnskey::new_unchecked(...) }withparser.remaining() - 4bytes.This allows constructing a
Dnskeywith total wire length > 65535 using only safe APIs.Poc:
Observed output:
It breaks the documented safety precondition of
new_uncheckedthrough a safe public API path, creating a soundness-risk and making downstream assumptions about DNS RDATA size unreliable.Suggested fix:
Before calling
new_uncheckedinDnskey::parse, validate total RDATA length using the same check asDnskey::new(or just construct viaDnskey::newafter parsing fields).Optionally, audit similar
parse -> new_uncheckedpaths in other record types for the same invariant bypass pattern.