Skip to content

Commit b88d2eb

Browse files
authored
add fuzz targets for varint and frame decode (closes M0) (#19)
* feature: added fuzz tests * added nightly flag for fuzz build
1 parent d57b9b5 commit b88d2eb

8 files changed

Lines changed: 216 additions & 5 deletions

File tree

.agents/skills/protocol_testing/SKILL.md

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,41 @@ Every codec (varint, frame) needs three test classes:
5353
| Malformed rejection | Known-bad byte sequences must return an error, not panic |
5454
| Boundary conditions | Max varint value, empty payload, max frame length |
5555

56-
## Fuzzing (future)
56+
## Fuzzing
57+
58+
Fuzz targets live in `crates/istok-core/fuzz/fuzz_targets/` and are built with
59+
`cargo fuzz` (nightly required). Currently covering:
60+
- `fuzz_varint_decode``varint::decode`
61+
- `fuzz_frame_decode``h3_frame::decode_frame_header`
62+
63+
To run locally (`+nightly` is required — cargo-fuzz uses `-Z` flags):
64+
```sh
65+
cd crates/istok-core
66+
cargo +nightly fuzz run fuzz_varint_decode
67+
cargo +nightly fuzz run fuzz_frame_decode
68+
```
69+
70+
Runs indefinitely until `Ctrl+C`. Corpus is saved in `fuzz/corpus/` and seeds future runs.
71+
72+
For a time-bounded run (CI-style):
73+
```sh
74+
ASAN_OPTIONS="detect_odr_violation=0:quarantine_size_mb=1:malloc_context_size=0" \
75+
cargo +nightly fuzz run fuzz_varint_decode -- -max_total_time=30 -max_len=8 -rss_limit_mb=256
76+
```
5777

58-
Fuzz targets will live in `fuzz/` and cover:
59-
- Frame decoding (`Frame::parse`)
60-
- QPACK decoding (once M2 is active)
78+
Use `max_len=8` for varint (QUIC varint max is 8 bytes) and `max_len=16` for frame
79+
decode (type varint + length varint = up to 16 bytes).
6180

6281
Rule: crashes are bugs. The engine must never panic on arbitrary input.
6382

83+
### Adding a new fuzz target
84+
85+
1. Add a `fuzz_targets/<name>.rs` file (see existing targets for the one-liner pattern).
86+
2. Add a `[[bin]]` entry to `crates/istok-core/fuzz/Cargo.toml`.
87+
3. **The fuzz `Cargo.toml` must have `[workspace]` at the top** — without it, Cargo
88+
treats the fuzz crate as part of the parent workspace and `cargo fuzz` fails.
89+
This is already present; do not remove it when editing the file.
90+
6491
## Anti-patterns
6592

6693
- Real `tokio::time::sleep` or `tokio::net` in unit tests — use the mock harness.

.github/workflows/ci.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,26 @@ jobs:
5151
run: cargo build -p istok-core --no-default-features --locked
5252
- name: Build istok-core with alloc only
5353
run: cargo build -p istok-core --no-default-features --features alloc --locked
54+
55+
fuzz:
56+
name: Fuzz (build + short run)
57+
runs-on: ubuntu-latest
58+
steps:
59+
- uses: actions/checkout@v4
60+
- uses: dtolnay/rust-toolchain@nightly
61+
- uses: Swatinem/rust-cache@v2
62+
- name: Install cargo-fuzz
63+
run: cargo install cargo-fuzz
64+
- name: Build fuzz targets
65+
working-directory: crates/istok-core
66+
run: cargo +nightly fuzz build
67+
- name: Fuzz varint decode (30s)
68+
working-directory: crates/istok-core
69+
env:
70+
ASAN_OPTIONS: "detect_odr_violation=0:quarantine_size_mb=1:malloc_context_size=0"
71+
run: cargo +nightly fuzz run fuzz_varint_decode -- -max_total_time=30 -max_len=8 -rss_limit_mb=256
72+
- name: Fuzz frame decode (30s)
73+
working-directory: crates/istok-core
74+
env:
75+
ASAN_OPTIONS: "detect_odr_violation=0:quarantine_size_mb=1:malloc_context_size=0"
76+
run: cargo +nightly fuzz run fuzz_frame_decode -- -max_total_time=30 -max_len=16 -rss_limit_mb=256

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,10 @@ target
2020
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
2121
.idea/
2222

23+
# Fuzz corpus and crash artifacts — local only
24+
crates/istok-core/fuzz/corpus/
25+
crates/istok-core/fuzz/artifacts/
26+
crates/istok-core/fuzz/coverage/
27+
2328
# Local agent task handoff — never committed
2429
.agents/TASK.md

crates/istok-core/fuzz/Cargo.lock

Lines changed: 110 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/istok-core/fuzz/Cargo.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[workspace]
2+
3+
[package]
4+
name = "istok-core-fuzz"
5+
version = "0.0.0"
6+
edition = "2024"
7+
publish = false
8+
9+
[package.metadata]
10+
cargo-fuzz = true
11+
12+
# libfuzzer-sys provides the fuzz_target! macro and links against libFuzzer.
13+
# Requires a nightly toolchain — run via `cargo fuzz build` / `cargo fuzz run`.
14+
[dependencies]
15+
libfuzzer-sys = "0.4"
16+
istok-core = { path = ".." }
17+
18+
[[bin]]
19+
name = "fuzz_varint_decode"
20+
path = "fuzz_targets/fuzz_varint_decode.rs"
21+
doc = false
22+
23+
[[bin]]
24+
name = "fuzz_frame_decode"
25+
path = "fuzz_targets/fuzz_frame_decode.rs"
26+
doc = false
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![no_main]
2+
3+
use istok_core::codec::h3_frame;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
// Invariant: decode_frame_header must never panic on arbitrary input.
7+
// Any byte sequence must produce Ok or Err, never a panic.
8+
fuzz_target!(|data: &[u8]| {
9+
let _ = h3_frame::decode_frame_header(data);
10+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![no_main]
2+
3+
use istok_core::codec::varint;
4+
use libfuzzer_sys::fuzz_target;
5+
6+
// Invariant: decode must never panic on arbitrary input.
7+
// Any byte sequence must produce Ok or Err, never a panic.
8+
fuzz_target!(|data: &[u8]| {
9+
let _ = varint::decode(data);
10+
});

docs/milestones.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Varint and H3 frame codecs with tests. QPACK excluded (see M2).
1515
- [x] varint codec + tests
1616
- [x] H3 frame codec + tests
1717
- [x] SETTINGS payload encoding (M0: empty) + tests
18-
- [ ] fuzz target(s) for frame/varint parsing (optional in v0 CI)
18+
- [x] fuzz target(s) for frame/varint parsing (optional in v0 CI)
1919

2020
### DoD checklist
2121

0 commit comments

Comments
 (0)