Skip to content

Coverage-guided fuzzing of both zone-byte parsers, weekly on a dated nightly - #182

Merged
DevomB merged 1 commit into
mainfrom
fuzz-schedule
Oct 5, 2026
Merged

DevomB merged 1 commit into
mainfrom
fuzz-schedule

Conversation

@DevomB

@DevomB DevomB commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Coverage-guided fuzzing for the two hand-written parsers of zone bytes, as broker.md said it should be: a scheduled job on nightly Rust.

Targets. compartments/kryptikd/fuzz (target broker_request) feeds broker::parse_request any header line, cut at the first newline and decoded lossily the way serve_connection does it. Every request it accepts is held to the bounds its refusals enforce: MIME type and clipboard length, zone-name alphabet and transfer name, the time claim's range and source count, and the update lengths. The parser is private to a binary crate, so the fuzz package builds kryptikd's own main.rs with cfg(fuzzing). There #![cfg_attr(fuzzing, no_main, ...)] and a #[cfg(fuzzing)] mod fuzz put the harness in place of main. The ordinary build never compiles either line, and kryptikd's Cargo.toml declares the cfg so unexpected_cfgs stays quiet. compositor/wlproxy/fuzz (target wire) compiles wire.rs, protocol.rs and protocol_tables.rs unchanged through #[path], since they name nothing else in the proxy. It reads a stream of messages, each against the table signature its object id picks, and checks the header round trip, that a new object or bind version appears exactly when the signature has one, that every string sits at the offset a rewrite trusts, and that MessageWriter output parses back to the same string. No proxy source changes.

Supply chain (ADR-010). Each fuzz crate is a separate package with its own [workspace] and its own committed Cargo.lock. libfuzzer-sys 0.4.13 and what it pulls in (arbitrary, plus cc, jobserver, shlex, find-msvc-tools, getrandom, cfg-if and r-efi as cc's build-time helpers) live only in compartments/kryptikd/fuzz/Cargo.lock and compositor/wlproxy/fuzz/Cargo.lock. They are in neither shipped Cargo.lock nor either binary's dependency graph. tools/check-rust-licences.sh and tools/source-bundle.sh read only the two shipped lockfiles, so nothing new is owed a licence text or a vendored source. libc resolves to 0.2.189, the shipped version. The job also builds cargo-fuzz 0.13.2 with cargo install --locked, a tool on the runner that ships nowhere.

Workflow. .github/workflows/fuzz.yml runs Sundays at 04:00 UTC and on workflow_dispatch, with permissions: contents: read. Checkout and upload-artifact are pinned to the SHAs the other workflows use. It uses nightly-2026-09-28 from rustup (rustc 1.101.0-nightly), pinned by date so a new finding is never a new compiler's. cargo fetch --locked must accept each fuzz lockfile before anything builds. The broker target is seeded from fuzz-corpus/broker-requests, one request per file; the proxy has no corpus in the tree, so it starts empty. Each target runs for 20 minutes (-max_total_time=1200, -max_len 4096 and 8192, -timeout=10) in parallel matrix jobs with a 60-minute job limit. There is no sanitizer, because the code under test is safe Rust; panics, the overflow checks and debug assertions the fuzz profiles turn on, and the harness's asserts are the oracle. Any crash, timeout or OOM input is uploaded as the fuzz-<target> artifact for 30 days.

Dispatch result (run 36986534899, this branch): both targets built clean and ran their full 20 minutes with no crash, timeout or OOM, so no artifact was uploaded.

target runs exec/s edges covered corpus at the end
broker_request (26 seeds) 302,045,126 251,494 296 of 721 counters, 700 features 329 inputs, 17 KB
wire (no seeds) 386,974,847 322,210 104 of 530 counters, 664 features 263 inputs, 116 KB

GitHub only dispatches a workflow it already knows, and this one is not on main yet. A push to a short-lived branch registered it: run 36986437209 was cancelled at once and that branch is deleted. After that, gh workflow run on this branch worked. Once merged, the Sunday schedule and plain gh workflow run fuzz.yml need nothing more.

CI on this branch is green: Commit identity, Shell lint, Source manifest, Compartment layer, Kernel currency and CodeQL. The compartment job builds kryptikd with the new main.rs and Cargo.toml for glibc and for musl with no new warnings.

…s wire parser runs weekly, on a dated nightly

cargo-fuzz targets for both parsers of zone bytes. compartments/kryptikd/fuzz
builds the daemon's own main.rs with cfg(fuzzing), where the harness takes
main's place, because the parser is private to a binary crate; it feeds
parse_request any header line and holds every request it accepts to the
bounds its refusals enforce. compositor/wlproxy/fuzz compiles wire.rs,
protocol.rs and the generated tables as they are, since they name nothing
else in the proxy, and reads a message stream against every signature,
checking the string offsets and the writer the rewrites use.

Each is a package with a workspace and lockfile of its own: libfuzzer-sys and
what it pulls in (arbitrary, cc and cc's build helpers) are in neither shipped
Cargo.lock nor either binary's dependencies, and the licence check and the
source bundle read only the shipped lockfiles. kryptikd's Cargo.toml declares
the fuzzing cfg so the ordinary build does not warn about it.

.github/workflows/fuzz.yml runs both on Sundays and on dispatch: a nightly
pinned by date from rustup, cargo-fuzz 0.13.2 installed --locked, the fuzz
lockfile held with cargo fetch --locked, the broker seeded from
fuzz-corpus/broker-requests one request per file, twenty minutes per target
with no sanitizer (the parsers are safe Rust), and any crash input kept as an
artifact for 30 days.
@DevomB
DevomB merged commit e59d239 into main Oct 5, 2026
13 checks passed
@DevomB
DevomB deleted the fuzz-schedule branch October 5, 2026 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant