From 63d9276d148062753b9e4f6da9411c93dad13fd7 Mon Sep 17 00:00:00 2001 From: YUZHEthefool <2804776511@qq.com> Date: Tue, 1 Sep 2026 16:57:09 +0800 Subject: [PATCH 1/3] fix(fuzz): match instead of unwrap_err in the syz bridge test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `bridge_requires_prebuilt_fuzzer` called `Result::unwrap_err()`, which requires the `Ok` type to implement `Debug`. `SyzBridge` does not, so the test failed to compile: error[E0277]: `syz_bridge::SyzBridge` doesn't implement `Debug` --> src/syz_bridge.rs:170:42 The module is gated `#[cfg(all(test, feature = "qemu-executor"))]`, and nothing in CI builds the crate's test targets with that feature — the nightly job runs `cargo fuzz run`, which does not compile lib tests — so the breakage was never surfaced. Destructure the result instead of unwrapping it. That needs no `Debug` bound, avoids widening a public type's derives to satisfy one test, and lets the assertion carry a message where it previously had none. Verified: `cargo check --locked --manifest-path fuzz/Cargo.toml --all-targets --all-features` goes from exit 101 to exit 0. --- fuzz/src/syz_bridge.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fuzz/src/syz_bridge.rs b/fuzz/src/syz_bridge.rs index 05f40fd..d18171e 100644 --- a/fuzz/src/syz_bridge.rs +++ b/fuzz/src/syz_bridge.rs @@ -165,9 +165,17 @@ mod tests { fn bridge_requires_prebuilt_fuzzer() { let kernel = PathBuf::from("esp-kcov/kernel.elf"); if kernel.exists() { - let bridge = SyzBridge::new(&kernel, 5); + // Matched rather than unwrapped: `Result::unwrap_err` requires the + // `Ok` type to implement `Debug`, and `SyzBridge` does not. + // Widening a public type's derives to satisfy one test is the wrong + // trade, so destructure instead. + let accepted = match SyzBridge::new(&kernel, 5) { + Ok(_) => true, + Err(err) => err.to_string().contains("not found"), + }; assert!( - bridge.is_ok() || bridge.unwrap_err().to_string().contains("not found") + accepted, + "constructing the bridge must either succeed or report a missing prerequisite" ); } } From 773cf7c5041e5c2b5d442c73837c04f705329d1b Mon Sep 17 00:00:00 2001 From: YUZHEthefool <2804776511@qq.com> Date: Tue, 1 Sep 2026 16:57:33 +0800 Subject: [PATCH 2/3] chore(deps): regenerate the stale fuzz/Cargo.lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `fuzz/Cargo.lock` was out of date in two independent ways, and `cargo check --locked` against it failed outright: error: cannot update the lock file ... because --locked was passed 1. It still pinned `uart_16550 0.3.2` and its `x86 0.52.0` dependency, plus duplicated `bitflags` (1.3.2 + 2.13.0) and `raw-cpuid 10.7.0` entries that only `uart_16550` required. Dropping the unused dependency regenerated the root `Cargo.lock` but not this one, so resolution here now removes `uart_16550`/`x86`, collapses `bitflags` to a single entry, and moves `raw-cpuid` to 11.6.0 under `x86_64` — matching what the root lockfile already records. 2. The entire `qemu-executor` optional dependency set — `anyhow`, `hex`, `nix`, `rand`, `serde`, `serde_json`, `sha2`, `tempfile` and their transitives — had never been resolved into the file at all. Nothing caught either drift because no job builds this crate with `--locked`: the nightly matrix runs `cargo fuzz run`, which silently re-resolves. The committed lockfile was effectively decorative. Regenerated on the Linux devbox so the committed lock is authoritative. --- fuzz/Cargo.lock | 318 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 280 insertions(+), 38 deletions(-) diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 96e78a6..40cc1f2 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + [[package]] name = "arbitrary" version = "1.4.2" @@ -35,12 +41,6 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.13.0" @@ -55,6 +55,7 @@ dependencies = [ "iommu", "klog", "lazy_static", + "lsm", "mm", "spin", "virtio", @@ -74,7 +75,7 @@ dependencies = [ name = "cap" version = "0.1.0" dependencies = [ - "bitflags 2.13.0", + "bitflags", "drivers", "klog", "mm", @@ -100,6 +101,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + [[package]] name = "compliance" version = "0.1.0" @@ -130,6 +137,15 @@ dependencies = [ "spin", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crypto" version = "0.1.0" @@ -174,7 +190,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -193,7 +209,6 @@ version = "0.1.0" dependencies = [ "lazy_static", "spin", - "uart_16550", "x86_64", ] @@ -225,6 +240,22 @@ dependencies = [ "zeroize", ] +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + [[package]] name = "ff" version = "0.13.1" @@ -252,6 +283,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -275,11 +317,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "iommu" version = "0.1.0" dependencies = [ - "bitflags 2.13.0", + "bitflags", "drivers", "klog", "mm", @@ -287,13 +335,19 @@ dependencies = [ "x86_64", ] +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + [[package]] name = "jobserver" version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom", + "getrandom 0.3.4", "libc", ] @@ -302,7 +356,7 @@ name = "kernel_core" version = "0.1.0" dependencies = [ "audit", - "bitflags 2.13.0", + "bitflags", "cap", "compliance", "cpu_local", @@ -362,6 +416,12 @@ dependencies = [ "spinning_top", ] +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "livepatch" version = "0.1.0" @@ -393,6 +453,12 @@ dependencies = [ "spin", ] +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + [[package]] name = "mm" version = "0.1.0" @@ -427,14 +493,40 @@ dependencies = [ name = "nilix-fuzz" version = "0.1.0" dependencies = [ + "anyhow", "arbitrary", + "hex", "kernel_core", "libfuzzer-sys", "mm", "net", + "nix", + "rand", + "serde", + "serde_json", + "sha2", + "tempfile", "vfs", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + [[package]] name = "p256" version = "0.13.2" @@ -445,6 +537,15 @@ dependencies = [ "primeorder", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -478,19 +579,56 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] [[package]] name = "raw-cpuid" -version = "10.7.0" +version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 1.3.2", + "bitflags", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", ] [[package]] @@ -523,7 +661,7 @@ name = "seccomp" version = "0.1.0" dependencies = [ "audit", - "bitflags 2.13.0", + "bitflags", "drivers", "klog", "mm", @@ -539,10 +677,65 @@ dependencies = [ "klog", "lazy_static", "mm", + "raw-cpuid", "spin", "x86_64", ] +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "2.0.1" @@ -593,6 +786,30 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "tlb_ops" version = "0.1.0" @@ -615,17 +832,6 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" -[[package]] -name = "uart_16550" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e492212ac378a5e00da953718dafb1340d9fbaf4f27d6f3c5cab03d931d1c049" -dependencies = [ - "bitflags 2.13.0", - "rustversion", - "x86", -] - [[package]] name = "unicode-ident" version = "1.0.24" @@ -642,7 +848,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" name = "vfs" version = "0.1.0" dependencies = [ - "bitflags 2.13.0", + "bitflags", "block", "cap", "crypto", @@ -668,6 +874,12 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasip2" version = "1.0.4+wasi-0.2.12" @@ -678,22 +890,26 @@ dependencies = [ ] [[package]] -name = "wit-bindgen" -version = "0.57.1" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "x86" -version = "0.52.0" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "bit_field", - "bitflags 1.3.2", - "raw-cpuid", + "windows-link", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "x86_64" version = "0.15.4" @@ -701,7 +917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7841fa0098ceb15c567d93d3fae292c49e10a7662b4936d5f6a9728594555ba" dependencies = [ "bit_field", - "bitflags 2.13.0", + "bitflags", "const_fn", "rustversion", "volatile", @@ -722,8 +938,34 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784" +[[package]] +name = "zerocopy" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "zeroize" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" From bbd1e243d932cd449e1233c799c916a2b6b4601e Mon Sep 17 00:00:00 2001 From: YUZHEthefool <2804776511@qq.com> Date: Tue, 1 Sep 2026 16:57:56 +0800 Subject: [PATCH 3/3] ci(fuzz): build the fuzzer crates on pull requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `userspace/fuzzer` and `fuzz` were the only crates in the repository that no pull-request check compiled. `ci.yml` never touches either, and `make clippy` / `make fmt-check` cover the bootloader, kernel, and `userspace` workspaces only — both fuzzing crates are separate workspaces with their own lockfiles. The consequence was concrete: two Dependabot PRs bumping `hmac` to 0.13 and `sha2` to 0.11 each reported six green checks without ever building the crate they changed. Both were individually uncompilable — they share a `digest` version and must move together — and either would have turned `main` red on merge. Two independent halves: - Detection. A paths-scoped `pull_request` trigger, so a PR touching either crate is actually built. `fuzz-targets-check` is added for the `fuzz` crate, which the existing matrix only compiles on push and on the nightly schedule — far too heavy to gate a PR on. It passes `--all-features` because `sha2`, `nix`, `rand` and the rest of the QEMU executor dependencies are optional behind `qemu-executor`, so a default-feature check would skip exactly the crates a dependency bump changes; and `--locked`, which is what makes the committed lockfile authoritative rather than decorative. - Prevention. A `rustcrypto` group for both fuzzing directories, so coupled crates arrive as one PR. It deliberately carries no `update-types` filter: Cargo treats a leading-zero bump such as 0.12 -> 0.13 as incompatible, so Dependabot classifies these as semver-major and a minor+patch filter would exclude precisely the bumps that need grouping. The wildcard groups gained matching `exclude-patterns` so membership does not depend on how group precedence is resolved. `aggregate-report` is excluded from `pull_request`: its `!cancelled()` guard would otherwise summarise four skipped jobs and an empty artifact set. Concurrency collapses superseded PR runs only — every other event keeps a run-unique group, so scheduled campaigns and `main` pushes can never cancel or queue behind one another. Verified on the Linux devbox: - actionlint 1.7.7 clean, before and after. - Exactly two jobs run on a pull request (`fuzz-tools-test`, `fuzz-targets-check`). Every heavier job skips, including `candidate-reporting`, whose `issues: write` scope is unreachable because its `needs` result is `skipped`, not `success`. - Job selection is byte-identical to the previous workflow for `push`, both cron schedules, and all three dispatch modes. - `fuzz-tools-test`: 26 tests pass, clippy `-D warnings` clean. - `fuzz-targets-check`: exit 0 cold in 25s. No `-D warnings` is applied; the fuzz targets carry 90 pre-existing dead-code warnings, so this is a compile gate, not a lint gate. Still uncovered: a `kernel/**` change can break the host fuzz harness without any PR check noticing, since `ci.yml` builds the kernel for bare metal and never for the host target these crates link against. --- .github/dependabot.yml | 47 ++++++++++++++++++++++ .github/workflows/fuzz.yml | 82 +++++++++++++++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ac4949e..463e37f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -64,9 +64,37 @@ updates: commit-message: prefix: chore(deps) groups: + # RustCrypto crates share a `digest` version and must move as a unit. + # `crash-triage` builds `Hmac`, so bumping `hmac` and `sha2` in + # separate pull requests leaves the lockfile carrying two `digest` majors + # and makes that type unsatisfiable — each pull request is individually + # uncompilable, which is exactly what happened with hmac 0.13 and + # sha2 0.11. + # + # This group carries no `update-types` filter on purpose. Cargo treats a + # leading-zero bump such as 0.12 -> 0.13 as incompatible, so Dependabot + # classifies it as semver-major; a minor+patch filter would exclude + # precisely the bumps that need grouping. + rustcrypto: + patterns: + - "hmac" + - "sha2" + - "sha3" + - "digest" + - "crypto-common" + - "block-buffer" + # Declared after `rustcrypto` and excluding the same names, so group + # membership holds regardless of how group precedence is resolved. fuzzer-tools-minor-and-patch: patterns: - "*" + exclude-patterns: + - "hmac" + - "sha2" + - "sha3" + - "digest" + - "crypto-common" + - "block-buffer" update-types: - minor - patch @@ -84,9 +112,28 @@ updates: commit-message: prefix: chore(deps) groups: + # Same coupling rule as `/userspace/fuzzer` above. This directory + # currently declares only `sha2` (optional, behind `qemu-executor`), so + # today the group holds a single crate; it is declared anyway so adding + # any sibling RustCrypto crate cannot reintroduce the split-bump trap. + rustcrypto: + patterns: + - "hmac" + - "sha2" + - "sha3" + - "digest" + - "crypto-common" + - "block-buffer" fuzz-targets-minor-and-patch: patterns: - "*" + exclude-patterns: + - "hmac" + - "sha2" + - "sha3" + - "digest" + - "crypto-common" + - "block-buffer" update-types: - minor - patch diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index ede122b..ca3b19e 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -33,6 +33,31 @@ on: - 'Makefile' - '.github/workflows/fuzz.yml' + # `userspace/fuzzer` and `fuzz` are the only crates in the repository that no + # pull-request check compiled: `ci.yml` never touches either, and + # `make clippy` / `make fmt-check` cover the bootloader, kernel, and + # `userspace` workspaces only — both of these are separate workspaces with + # their own lockfiles. Without a `pull_request` trigger they reached `main` + # unbuilt, so two Dependabot PRs (hmac 0.13, sha2 0.11) each reported a fully + # green run while being individually uncompilable, and either would have + # turned `main` red on merge. Only the two fast compile jobs run here; every + # heavier job below is gated off `pull_request` by its own `if:`, so a pull + # request never starts a fuzz campaign. + pull_request: + branches: + - main + paths: + - 'userspace/fuzzer/**' + - 'fuzz/**' + - '.github/workflows/fuzz.yml' + +# Collapse superseded pull-request runs only. Every other event keeps a +# run-unique group, so scheduled campaigns, `main` pushes, and manual dispatches +# retain today's behaviour and can never cancel or queue behind one another. +concurrency: + group: fuzz-${{ github.event_name == 'pull_request' && github.ref || github.run_id }} + cancel-in-progress: true + permissions: {} jobs: @@ -69,6 +94,57 @@ jobs: - name: Lint fuzz tools run: cargo clippy --locked --manifest-path userspace/fuzzer/Cargo.toml --all-targets --target x86_64-unknown-linux-gnu -- -D warnings + # Type-checks the cargo-fuzz crate on pull requests. `cargo-fuzz-targets` + # below already compiles it on push and on the nightly schedule, but that job + # is far too heavy to gate a pull request on, which left `fuzz/` — including + # its dependency bumps — reaching `main` without any pull-request check ever + # building it. + fuzz-targets-check: + name: Fuzz Target Compile Check + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + # Pull-request only: the events that already build this crate for real must + # keep their current job set exactly as it is. + if: github.event_name == 'pull_request' + + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Setup Rust nightly + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-12-08 + components: rust-src + + - name: Cache fuzz-target check + uses: actions/cache@v6 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + fuzz/target/ + key: ${{ runner.os }}-fuzz-check-${{ hashFiles('fuzz/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-fuzz-check- + + # `--all-features` is required, not cosmetic: `sha2`, `nix`, `rand`, and + # the rest of the QEMU executor dependencies are optional behind + # `qemu-executor`, so a default-feature check would silently skip the very + # crates a dependency bump changes. + # + # `--locked` is what makes this a real gate — it asserts the committed + # lockfile is exactly what builds. `cargo-fuzz-targets` runs `cargo fuzz + # run` without it and therefore re-resolves silently, which is how + # `fuzz/Cargo.lock` drifted out of date unnoticed. + # + # This is a compile check, not a lint gate: the fuzz targets carry + # pre-existing dead-code warnings, so no `-D warnings` is applied here. + - name: Check fuzz targets + run: cargo check --locked --manifest-path fuzz/Cargo.toml --all-targets --all-features --target x86_64-unknown-linux-gnu + # This is a deterministic executor regression test, not a fuzz campaign. It # boots a real Nilix guest and proves that two syscall programs produce # non-zero, distinct, resettable KCOV bitmaps. @@ -520,7 +596,11 @@ jobs: aggregate-report: name: Aggregate Report needs: [fuzz-tools-test, kcov-qemu-e2e, fuzz-pipeline-smoke, cargo-fuzz-targets, candidate-reporting] - if: ${{ !cancelled() }} + # `!cancelled()` deliberately runs this even when needed jobs are skipped, + # which on a pull request would mean summarising four skipped jobs and an + # empty artifact set. Exclude that event so the only check a pull request + # publishes is the one that actually compiled something. + if: ${{ !cancelled() && github.event_name != 'pull_request' }} runs-on: ubuntu-latest permissions: contents: read