e2fsprogs: improve fuzzing coverage#58
Conversation
Fuzzing Coverage ReportTested: project
Per-harness
Δ = (after − before) / before, to accommodate that denominators may change. "new" when before is 0; "deleted" when after is 0. |
2e65ee6 to
4655f18
Compare
|
CI failures are both fuzz-for-me infra issues, not from this PR's diff:
Both reproduce identically on consecutive runs and against the unchanged baseline diff. The actual project changes ( |
177029d to
30a48e5
Compare
4655f18 to
8f81b4a
Compare
|
Updated this PR with a second new harness ( CI status: the project's own builds all pass — |
c053f96 to
3fe38a2
Compare
8f81b4a to
c3ebf8f
Compare
|
CI is fully green: |
ad01771 to
55041f5
Compare
c3ebf8f to
7504d0e
Compare
ed731ba to
64e909c
Compare
7504d0e to
7745f3c
Compare
9a36aa8 to
6a4a93d
Compare
6125235 to
e512e7e
Compare
7745f3c to
60da077
Compare
b5fd10c to
712a52f
Compare
60da077 to
87de67f
Compare
a7587cd to
b952f62
Compare
af7f9a7 to
806b281
Compare
Summary
This change improves coverage of the three existing libFuzzer harnesses and adds two new ones: a filesystem walker (read side) and a write-side harness exercising allocation, write-back, journal-inode creation, and metadata mutation.
According to Fuzz Introspector the project currently sits at 10.99% line coverage (1,814 lines), with `ext2fs_check_directory_fuzzer` reported at 0%. The three existing harnesses ship without seed corpora, so libFuzzer starts from a zero-byte input and never gets past the superblock magic check in `ext2fs_open` within a typical CI run.
What changes
`build.sh` — bundle a per-harness seed corpus
Two seed sources, generated at build time so no binaries are checked in:
Each candidate seed is replayed once through its target harness (3-second timeout) and only kept if the replay exits cleanly. A `.options` file is emitted alongside each fuzzer — including the three pre-existing ones — setting `max_len = 1048576`. libFuzzer's default 4 KB cap would otherwise discard every seed.
`fuzz/ext2fs_iterate_fuzzer.cc` — new read-side walker
Mirrors the maintainer-authored `misc/check_fuzzer.c` approach but broadens it. After `ext2fs_open` + `ext2fs_read_bitmaps` + `ext2fs_check_desc`, it walks the root directory recursively, then the inode scan. For each entry: `check_directory`, `read_inode`, `get_pathname`, `dirhash`, `lookup`. For each inode: `xattrs_open`/`xattrs_read`/`xattrs_iterate`/`xattr_get`, then directory iteration, extent walking (`extent_open` + `extent_get` with multiple traversal ops), or `block_iterate3`, plus `bmap2` and `file_read`.
`fuzz/ext2fs_write_fuzzer.cc` — new write-side harness
Opens the image `EXT2_FLAG_RW` and exercises allocation/write paths: `new_block2`/`block_alloc_stats2`, `new_inode`/`write_new_inode`/`write_inode`, `link`/`unlink`, `mkdir` + bulk link of 32 entries (htree split paths), `xattr_set`/`xattrs_write`/`xattr_remove`, `punch` (bounded and `~0ULL`), per-inode `extent_set_bmap`/`extent_fix_parents`/`extent_node_split`/`extent_delete`, `bmap2` with `BMAP_ALLOC`, `expand_dir`, `init_dblist` + `add_dir_block2` + `dblist_iterate`, `create_icount2` + `icount_store`/`increment`/`decrement`/`fetch`/`validate`, `inline_data_init` + `inline_data_set`, `add_journal_inode2`, `create_orphan_file`, `set_gdt_csum`, then `ext2fs_flush` + `ext2fs_close` to drive close-time superblock/bitmap/csum write paths in `closefs.c`, `csum.c`, `rw_bitmaps.c`.
Coverage after this change
Running `fuzz_for_pr` for 5 minutes wall-time with all harnesses concurrent, measured by `llvm-cov`:
Compared with the Fuzz Introspector baseline of 1,814 lines, this is +6,550 absolute lines of attacker-reachable libext2fs code under fuzzing — 4.6× more code exercised. Notable per-file gains (union across all harnesses): `extent.c` ~64%, `inode.c` ~70%, `block.c` ~70%, `dir_iterate.c` ~73%, `mkdir.c` ~86%, `unlink.c` ~84%, `csum.c` ~61%, `closefs.c` ~59%, `punch.c` ~67%, `orphan.c` ~72%, `bmap.c` ~41%, `inline_data.c` ~65%, `mmp.c` ~50%, `icount.c` ~50%, `alloc.c` ~44%, `ext_attr.c` ~48%.
Test plan