Skip to content

Commit cbda505

Browse files
committed
Document and check fuzz corpus policy
1 parent 5fa6379 commit cbda505

9 files changed

Lines changed: 133 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
- Added `scripts/check_miri.sh` and routed CI/release Miri checks through it so
2828
both no-default scalar and all-features alloc/stream surfaces run under Miri
2929
when nightly Miri is installed.
30+
- Added `docs/FUZZING.md` and `scripts/check_fuzz_corpus.sh` to document and
31+
enforce reviewed fuzz corpus handling.
3032

3133
## 0.5.0 - 2026-05-14
3234

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ For dependency admission rules, see [docs/DEPENDENCIES.md](docs/DEPENDENCIES.md)
484484
For adoption guidance from the established `base64` crate, see
485485
[docs/MIGRATION.md](docs/MIGRATION.md).
486486
For performance evidence guidance, see [docs/BENCHMARKS.md](docs/BENCHMARKS.md).
487+
For fuzz target and corpus policy, see [docs/FUZZING.md](docs/FUZZING.md).
487488

488489
## Local Checks
489490

@@ -548,6 +549,12 @@ Compile fuzz targets without running a campaign:
548549
scripts/check_fuzz.sh
549550
```
550551

552+
Validate the committed fuzz corpus policy directly:
553+
554+
```sh
555+
scripts/check_fuzz_corpus.sh
556+
```
557+
551558
Compile and audit the isolated performance harness:
552559

553560
```sh

docs/FUZZING.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Fuzzing Policy
2+
3+
`base64-ng` keeps fuzzing isolated from the published crate. The root crate
4+
remains dependency-free; fuzz-only dependencies live under `fuzz/` and are
5+
checked separately.
6+
7+
Run fuzz harness checks with:
8+
9+
```sh
10+
scripts/check_fuzz.sh
11+
```
12+
13+
Run corpus policy checks directly with:
14+
15+
```sh
16+
scripts/check_fuzz_corpus.sh
17+
```
18+
19+
## Targets
20+
21+
Current fuzz targets:
22+
23+
- `decode`: arbitrary strict, legacy, and constant-time-oriented decode input
24+
- `in_place`: in-place encode/decode and legacy compaction behavior
25+
- `stream_chunks`: fragmented stream reader/writer state machines and adjacent
26+
framed payload boundaries
27+
- `differential`: canonical output comparison against the established Base64
28+
behavior used by the harness
29+
30+
## Corpus Admission
31+
32+
Committed corpus inputs are allowed only under:
33+
34+
- `fuzz/corpus/decode/`
35+
- `fuzz/corpus/in_place/`
36+
- `fuzz/corpus/stream_chunks/`
37+
- `fuzz/corpus/differential/`
38+
39+
Each committed corpus input must be:
40+
41+
- small enough to review manually, with a hard local limit of 64 KiB
42+
- relevant to a previously fixed bug, a protocol boundary, or an edge case not
43+
already represented by deterministic tests
44+
- non-sensitive and safe to publish
45+
- named or documented well enough that reviewers can understand why it exists
46+
47+
Generated crashes, hangs, and local artifacts must stay out of commits. The
48+
release gate rejects files under `fuzz/artifacts/` other than `.gitignore`.
49+
50+
## Running Local Campaigns
51+
52+
Install nightly and cargo-fuzz:
53+
54+
```sh
55+
rustup toolchain install nightly
56+
cargo install --locked cargo-fuzz
57+
```
58+
59+
Run bounded smoke campaigns before release-sensitive stream or decode changes:
60+
61+
```sh
62+
cargo +nightly fuzz run decode -- -runs=1000
63+
cargo +nightly fuzz run in_place -- -runs=1000
64+
cargo +nightly fuzz run stream_chunks -- -runs=1000
65+
cargo +nightly fuzz run differential -- -runs=1000
66+
```
67+
68+
Longer campaigns are useful before release candidates, but generated corpus
69+
changes should be reviewed deliberately. Keep only the inputs that improve
70+
coverage or preserve a regression.

docs/PLAN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,14 +324,14 @@ the zero-runtime-dependency stance.
324324
documentation.
325325
- Strengthened Miri coverage with a shared check script for no-default scalar
326326
APIs and all-features alloc/stream APIs when nightly Miri is installed.
327+
- Stabilized fuzz corpus handling with documented admission rules and a local
328+
corpus policy check.
327329
- Expand Kani proof coverage for length helpers, in-place decode bounds, and
328330
selected scalar decoder invariants when Kani supports the pinned Rust
329331
toolchain.
330332
- Add a documented constant-time verification plan for the `ct` module,
331333
including generated-code review requirements before making cryptographic
332334
constant-time claims.
333-
- Stabilize fuzz corpus handling and document which corpus inputs are accepted
334-
into the repository.
335335

336336
### v0.7
337337

docs/RELEASE.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,19 @@ Fuzz-only dependencies are checked separately with:
133133
scripts/check_fuzz.sh
134134
```
135135

136-
The `fuzz/` package is not part of the published crate.
136+
The `fuzz/` package is not part of the published crate. Corpus admission rules
137+
are documented in `docs/FUZZING.md` and checked by
138+
`scripts/check_fuzz_corpus.sh`.
137139

138140
Run the streaming fuzz smoke when changing stream state machines:
139141

140142
```sh
141143
cargo +nightly fuzz run stream_chunks -- -runs=1000
142144
```
143145

144-
Review generated local corpus files before committing.
146+
Review generated local corpus files before committing. Commit only small,
147+
non-sensitive inputs that preserve a regression, protocol boundary, or edge
148+
case not already represented by deterministic tests.
145149

146150
Reserved SIMD feature bundles are checked with:
147151

docs/RELEASE_EVIDENCE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ The release gate runs:
5353
- Miri through `scripts/check_miri.sh` when nightly Miri is installed,
5454
covering no-default-features scalar APIs and all-features alloc/stream APIs
5555
- fuzz target compile check when `cargo-fuzz` is installed
56+
- fuzz corpus policy validation for target-specific reviewed corpus inputs and
57+
release-blocking artifact cleanup
5658
- isolated fuzz and performance harness dependency checks
5759
- installed-target `no_std` checks for the reserved `simd` feature
5860
- reserved SIMD feature-bundle compile checks for AVX2, AVX-512 VBMI, and

scripts/check_fuzz.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ fi
99
echo "fuzz checks: compile harnesses"
1010
cargo check --manifest-path fuzz/Cargo.toml --bins
1111

12+
echo "fuzz checks: corpus policy"
13+
scripts/check_fuzz_corpus.sh
14+
1215
echo "fuzz checks: RustSec advisories"
1316
cargo audit --file fuzz/Cargo.lock
1417

scripts/check_fuzz_corpus.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env sh
2+
set -eu
3+
4+
if [ ! -d fuzz ]; then
5+
echo "fuzz corpus: skipping; fuzz/ is not present"
6+
exit 0
7+
fi
8+
9+
test -s docs/FUZZING.md
10+
11+
for target in decode in_place stream_chunks differential; do
12+
mkdir -p "fuzz/corpus/$target"
13+
done
14+
15+
find fuzz/artifacts -type f ! -name .gitignore -print | while IFS= read -r artifact; do
16+
echo "fuzz corpus: artifact must not be committed or left for release gates: $artifact" >&2
17+
exit 1
18+
done
19+
20+
find fuzz/corpus -type f ! -name .gitkeep -print | while IFS= read -r corpus_file; do
21+
case "$corpus_file" in
22+
fuzz/corpus/decode/* | fuzz/corpus/in_place/* | fuzz/corpus/stream_chunks/* | fuzz/corpus/differential/*)
23+
;;
24+
*)
25+
echo "fuzz corpus: unknown corpus target for $corpus_file" >&2
26+
exit 1
27+
;;
28+
esac
29+
30+
size="$(wc -c <"$corpus_file" | tr -d '[:space:]')"
31+
if [ "$size" -gt 65536 ]; then
32+
echo "fuzz corpus: $corpus_file is larger than 65536 bytes" >&2
33+
exit 1
34+
fi
35+
done
36+
37+
echo "fuzz corpus: ok"

scripts/validate-release-metadata.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ test -s docs/ASYNC.md
6060
test -s docs/BENCHMARKS.md
6161
test -s docs/CONSTANT_TIME.md
6262
test -s docs/DEPENDENCIES.md
63+
test -s docs/FUZZING.md
6364
test -s docs/MIGRATION.md
6465
test -s docs/PANIC_POLICY.md
6566
test -s docs/PLAN.md
@@ -73,6 +74,7 @@ test -s docs/UNSAFE.md
7374
for required_script in \
7475
"scripts/check_backend_evidence.sh" \
7576
"scripts/check_fuzz.sh" \
77+
"scripts/check_fuzz_corpus.sh" \
7678
"scripts/check_kani.sh" \
7779
"scripts/check_miri.sh" \
7880
"scripts/check_perf.sh" \
@@ -132,6 +134,7 @@ for required_package_file in \
132134
"docs/BENCHMARKS.md" \
133135
"docs/CONSTANT_TIME.md" \
134136
"docs/DEPENDENCIES.md" \
137+
"docs/FUZZING.md" \
135138
"docs/MIGRATION.md" \
136139
"docs/PANIC_POLICY.md" \
137140
"docs/PLAN.md" \
@@ -143,6 +146,7 @@ for required_package_file in \
143146
"docs/UNSAFE.md" \
144147
"scripts/check_backend_evidence.sh" \
145148
"scripts/check_fuzz.sh" \
149+
"scripts/check_fuzz_corpus.sh" \
146150
"scripts/check_kani.sh" \
147151
"scripts/check_miri.sh" \
148152
"scripts/check_perf.sh" \

0 commit comments

Comments
 (0)