Property tests (run by `cargo test`): - crates/packet/tests/properties.rs: every kind of message, generated, must be written, be as long as wire_len says, and read back equal; and noise, noise behind a valid header, and valid messages with bytes damaged must never do more than return an error. Anything the decoder accepts must be showable and survive being written and read again. - IpPrefix: text round trip, noise as text, `contains` against a bitwise model. - The session's framing: a stream of messages yields the same messages however it is cut into reads. Fuzz targets (fuzz/, cargo-fuzz on nightly, a minute each in CI): raw messages, message bodies behind a valid header, prefix text, the configuration file. Bugs found, each with a named regression test: - A prefix longer than its address family (e.g. an IPv4 NLRI of length 99) was accepted, and showing it panicked. The session logs every route, so one UPDATE from a peer ended the session task. Such a prefix is now malformed, and showing a prefix cannot fail. - Bits past a prefix's length were kept, so the same prefix sent with stray bits was a different key in the RIB. Prefixes now have one form. - An opaque extended community was read as seven octets instead of eight, putting every community after it out of step. - AGGREGATOR could be neither read from nor written to a peer with two-octet AS numbers. - The Outbound Route Filter and Long-Lived Graceful Restart capabilities were written differently from how they are read. - A capability value or unknown attribute too long for its length field was written with the length wrapped around; it is now an error, or for the attribute the two-octet length. Also: ROUTE-REFRESH carries its AFI and SAFI instead of being modelled as an empty message, and one with a body of the wrong length is refused. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
25 lines
834 B
Rust
25 lines
834 B
Rust
//! A message body behind a correct header, so that the fuzzer spends its
|
|
//! time in the OPEN, UPDATE and NOTIFICATION parsers and not on guessing
|
|
//! the marker and length. The first byte picks the AS number width and the
|
|
//! message type.
|
|
#![no_main]
|
|
|
|
use libfuzzer_sys::fuzz_target;
|
|
|
|
/// The longest body that fits a BGP message (4096 less the 19-byte header).
|
|
const MAX_BODY: usize = 4096 - 19;
|
|
|
|
fuzz_target!(|data: &[u8]| {
|
|
let Some((selector, body)) = data.split_first() else {
|
|
return;
|
|
};
|
|
let body = &body[..body.len().min(MAX_BODY)];
|
|
let msg_type = 1 + (selector >> 1) % 5;
|
|
|
|
let mut wire = vec![0xff; 16];
|
|
wire.extend_from_slice(&((19 + body.len()) as u16).to_be_bytes());
|
|
wire.push(msg_type);
|
|
wire.extend_from_slice(body);
|
|
bgp_fuzz::decode_and_check(selector & 1 == 1, &wire);
|
|
});
|