stamparm/maltrail · warning
question
Error message
question
What it means
A test-side `Option::expect` in the DNS module: after verifying that out-of-bounds offsets return `None`, the test builds a well-formed DNS query via `testkit::dns_query` and asserts `question(...)` parses it, panicking with 'question' if parsing fails. It is the test's way of saying 'the happy path must still work after the bounds guards were added'; a panic means the parser now rejects a valid query.
Solutions
- Re-run the test and inspect the produced query bytes with a hex dump; confirm `testkit::dns_query` still emits a valid question section.
- Step through `dns::question` with the testkit bytes to find which guard returns `None` for this valid input, and fix the over-tight bound check.
- If testkit was changed, restore a correctly encoded query (correct name pointer/labels, qtype/qclass present) or update the expectation accordingly.
- Add a few more happy-path fixtures (multiple labels, long names) to catch over-strict guards earlier.
Example fix
// before (test fails because question() returns None)
let query = super::super::dns::question(crate::testkit::dns_query("evil.com", 1, 1, 0x0100)).expect("question");
// after (diagnose instead of a bare panic)
let bytes = crate::testkit::dns_query("evil.com", 1, 1, 0x0100);
let query = super::super::dns::question(&bytes)
.unwrap_or_else(|| panic!("question() rejected valid query: {:02x?}", bytes)); Defensive patterns
Strategy: validation
Validate before calling
let bytes = crate::testkit::dns_query("evil.com", 1, 1, 0x0100);
assert!(!bytes.is_empty() && bytes.len() > 12, "testkit emitted a malformed query"); Try / catch
let query = dns::question(&bytes)
.unwrap_or_else(|| panic!("question() rejected valid query {:02x?}", bytes)); Prevention
- Keep happy-path fixtures alongside hostile-input tests
- Hex-dump testkit packets when parser tests fail
- After tightening bounds guards, always re-run happy-path tests
- Return descriptive errors from parsers instead of bare None during debugging
When it happens
Trigger: Running `hostile_name_end_offsets_never_overflow` after a change to `dns::question` (or the bounds guards it relies on) that makes it return `None` for a valid single-question query built by `testkit::dns_query("evil.com", 1, 1, 0x0100)`. Also fires if the testkit helper starts emitting malformed packets (bad offsets, wrong counts, truncated name encoding).
Common situations: Refactoring the DNS name parser or question bounds-checking; tightening offset validation so the happy path is accidentally rejected; changing `testkit::dns_query` wire format (e.g. flags or compression handling); inconsistent qdcount/name encoding.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- well-formed hello parses
- one of the two matched
- just probed
- hmac accepts any key length
- lookahead should match
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/0d1e50cdb4ff5345.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/src/protocols/dns.rs:160
/// Named, deterministic offset-overflow contract.
///
/// The fuzz suite catches this class, but only by chance of input; these pin the exact boundary
/// values forever. Both functions take a caller-supplied `name_end` and must return `None`
/// rather than overflow — a debug build panicked on `start + 4` here, and release wrapped it
/// into an empty range, which is a correctness property that must not depend on the profile.
#[test]
fn hostile_name_end_offsets_never_overflow() {
let data = [0u8; 64];
for offset in [usize::MAX, usize::MAX - 1, usize::MAX - 4, usize::MAX - 5, usize::MAX - 6] {
assert_eq!(question_type_class(&data, offset), None, "question_type_class({offset})");
assert_eq!(first_a_record(&data, offset), None, "first_a_record({offset})");
}
// A sane offset past the end is also None, not a panic.
assert_eq!(question_type_class(&data, 1_000), None);
assert_eq!(first_a_record(&data, 1_000), None);
// ... and a valid offset still works, so the guards did not break the happy path.
let query =
super::super::dns::question(&crate::testkit::dns_query("evil.com", 1, 1, 0x0100)).expect("question");
assert_eq!(query.name, "evil.com");
}
fn query_message(name: &str, qtype: u16, qclass: u16, flags: u16) -> Vec<u8> {
let mut v = Vec::new();
v.extend_from_slice(&0x1234u16.to_be_bytes());
v.extend_from_slice(&flags.to_be_bytes());
v.extend_from_slice(&1u16.to_be_bytes());
v.extend_from_slice(&0u16.to_be_bytes());
v.extend_from_slice(&0u16.to_be_bytes());
v.extend_from_slice(&0u16.to_be_bytes());
for label in name.split('.') {
v.push(label.len() as u8);
v.extend_from_slice(label.as_bytes());
}
v.push(0);
v.extend_from_slice(&qtype.to_be_bytes());
v.extend_from_slice(&qclass.to_be_bytes());View on GitHub (pinned to 77cfb06d76)