Commit bcb99aa
authored
test(fuzz): fuzz the guest-facing parsers, and fix what it found (#169)
Adds fuzzing for the parsers that read guest-controlled bytes — the control-channel frame decoder, the userspace vsock state machine, the split-virtqueue reader, and the 9P server together with the transport beneath it. Five `cargo fuzz` targets share their harness bodies with `tests/fuzz_corpus.rs`, which replays every committed seed and crash artifact on stable inside a plain `cargo test`. Only that replay blocks a merge: libFuzzer needs nightly and its search is non-deterministic, so a run that finds a bug says nothing about whether the change under review introduced it. Discovery runs weekly and on demand instead (ADR-0012).
The fuzzing found seven guest-reachable defects in shipped code, all fixed here. A guest could kill the VMM process for every sandbox it hosts with two MMIO writes — activating a virtqueue it never sized, so every ring index divided by zero. It could escape the 9P shared directory, because `Tlcreate` and `Tmkdir` validated the parent fid and then joined an unfiltered guest name onto it. It could hang the host in a descriptor cycle, size 4 GiB host allocations from a descriptor length or a `Tread` count, overflow ring-address arithmetic under `overflow-checks`, and desynchronize a 9P mount with `Rread`, `Rreaddir`, or `Rwalk` replies exceeding the negotiated `msize`. The same allocation and overflow bugs sat in the userspace vsock TX path, and neither device clamped `QueueNum` to the size it advertises.
**One wire-path behavior change:** a `Tversion` asking for less than 4 KiB is now refused with `Rerror`/`EINVAL` rather than served. The device derives its request-assembly budget from `msize`, and a budget that small starves every later request including the `Tversion` that would renegotiate. Raising the client's value instead would break the negotiation the other way, since the reply commits the server not to exceed what the client can receive. Linux's 9P client rejects `msize < 4096` at mount time, so a conforming mount never reaches it.
The gate checks that it is still covering something. Each harness reports the work it performed, and every seed must clear a floor — a harness that reaches none of its parser returns cleanly and replays green, which is exactly how the `nine_p_transport` target shipped inert in an earlier revision of this branch and was caught by review rather than by CI. The floor applies where a harness drives its parser from a loop over the fuzzer's bytes, since that is what a consumption bug can starve; the other two hand the raw input straight in and cannot go inert that way. Discovered inputs go to a gitignored `fuzz/corpus-run/`, so `fuzz/corpus/` stays the curated set the floor polices.
Every fix is verified by reverting it and requiring the gate to fail: nine of nine caught, five by the corpus and four by unit-test oracles, plus the floor's own check. Two fixes cannot be pinned by any input — a 4 GiB `alloc_zeroed` is mapped lazily and never faults, and an out-of-table descriptor index reads zeroed memory and ends the walk indistinguishably — so both carry oracles instead. Fuzzing pins crashes; everything else needs an assertion.
Validated on Linux at rustc 1.98: fmt, clippy, workspace tests, corpus replay, and all five targets clean with no artifacts.
Closes #155.1 parent 43ac2ca commit bcb99aa
49 files changed
Lines changed: 1938 additions & 81 deletions
File tree
- .github/workflows
- docs/adr
- fuzz
- corpus
- nine_p_transport
- nine_p
- virtqueue
- vsock_frame
- vsock_packet
- fuzz_targets
- src
- devices
- tests
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
256 | | - | |
| 256 | + | |
257 | 257 | | |
258 | 258 | | |
259 | 259 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
2 | 13 | | |
3 | 14 | | |
4 | 15 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
862 | 862 | | |
863 | 863 | | |
864 | 864 | | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
| 872 | + | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
| 877 | + | |
| 878 | + | |
| 879 | + | |
| 880 | + | |
| 881 | + | |
| 882 | + | |
| 883 | + | |
| 884 | + | |
| 885 | + | |
| 886 | + | |
| 887 | + | |
| 888 | + | |
| 889 | + | |
| 890 | + | |
| 891 | + | |
| 892 | + | |
| 893 | + | |
| 894 | + | |
| 895 | + | |
| 896 | + | |
| 897 | + | |
| 898 | + | |
865 | 899 | | |
866 | 900 | | |
867 | 901 | | |
| |||
0 commit comments