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>
48 lines
1.8 KiB
Markdown
48 lines
1.8 KiB
Markdown
# Fuzz targets
|
|
|
|
Coverage-guided fuzzing of everything that reads input we do not control.
|
|
The property tests (`cargo test`, e.g. `crates/packet/tests/properties.rs`)
|
|
check the same things on generated input and run on every build; these
|
|
targets go further by letting libFuzzer follow code coverage for as long as
|
|
you leave them running.
|
|
|
|
| Target | Input |
|
|
|----------------|------------------------------------------------------------------|
|
|
| `message` | Raw bytes as they come off a TCP connection |
|
|
| `message_body` | A message body behind a correct header (reaches the body parsers) |
|
|
| `prefix_text` | A prefix as text |
|
|
| `config` | The configuration file |
|
|
|
|
For the two message targets a finding is either a panic, or a message the
|
|
decoder accepted that cannot be written and read back as itself.
|
|
|
|
## Running
|
|
|
|
libFuzzer needs a nightly toolchain and `cargo-fuzz`:
|
|
|
|
```bash
|
|
rustup toolchain install nightly
|
|
cargo install cargo-fuzz
|
|
```
|
|
|
|
Then, from this directory:
|
|
|
|
```bash
|
|
cargo +nightly fuzz run message_body corpus/message_body seeds/message_body -- -max_total_time=300
|
|
```
|
|
|
|
The first directory is the working corpus, which the fuzzer adds to and
|
|
which is not checked in. `seeds/` holds a few valid inputs to start from.
|
|
Leave out `-max_total_time` to run until interrupted.
|
|
|
|
## When it finds something
|
|
|
|
The input is saved under `artifacts/<target>/`. Reproduce it with
|
|
|
|
```bash
|
|
cargo +nightly fuzz run message_body artifacts/message_body/crash-...
|
|
```
|
|
|
|
Fix the bug, and add a unit test with the input next to the code it broke,
|
|
so that `cargo test` keeps it fixed without anyone having to run the fuzzer.
|