diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 360661c7..c874a506 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -1,11 +1,12 @@ # Fuzzing lives on `cargo fuzz`, which needs a nightly toolchain and must never -# be part of the stable `cargo clippy --workspace` gate in ci.yml. This workflow -# is therefore SCHEDULED (not a PR push/PR gate) and runs against the corpus -# that is checked into this repo under `fuzz/corpus/node/`. +# be part of the stable `cargo clippy --workspace` gate in ci.yml (ci.sh only +# checks that the fuzz crate still compiles on stable). This workflow is +# therefore SCHEDULED (not a PR gate) and starts from the corpus checked into +# this repo under `fuzz/corpus/node/`. # -# The 24 CPU-hour budget mentioned in Task 12 is release-time; this scheduled run -# uses a modest per-run budget so it stays cheap while still re-executing every -# known input and continuing to grow the corpus. +# The 24 CPU-hour release budget (spec ยง7) is spent by hand before a release; +# this scheduled run uses a modest per-run budget so it stays cheap while still +# re-executing every known input. name: Fuzz @@ -19,32 +20,48 @@ on: permissions: contents: read +env: + CARGO_TERM_COLOR: always + jobs: fuzz-node: runs-on: ubuntu-latest timeout-minutes: 120 steps: - - uses: actions/checkout@v4 - with: - # The corpus is committed, so the run continues where it left off and - # re-runs every known crashing/non-crashing input on each schedule. - fetch-depth: 0 + - name: Checkout + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + # rust-toolchain.toml pins stable, which overrides `rustup default`, so + # every cargo call below names `+nightly` explicitly (cargo-fuzz passes + # `-Z` flags that only nightly accepts). - name: Install nightly toolchain + cargo-fuzz run: | rustup toolchain install nightly --profile minimal --component rust-src - rustup default nightly - cargo install cargo-fuzz + cargo +nightly install cargo-fuzz --locked - - name: Run the Node harness against the checked-in corpus - # Re-runs every corpus entry (fast path) and then continues fuzzing for - # the run budget. The corpus directory is part of the repo, so it grows - # in place across schedules; a fresh checkout still has prior inputs. - run: cargo fuzz run node -- -max_total_time=1800 + - name: Run the Node harness from the checked-in corpus + # Re-runs every corpus entry first, then keeps fuzzing for the budget. + # Both input modes (basic and adversarial, chosen by the first input + # byte) run in the one `node` target. + run: cargo +nightly fuzz run node -- -max_total_time=1800 - - name: Upload corpus artifacts (optional retention) + # A crash leaves its reproducer under fuzz/artifacts/node/. Reproduce + # with `cargo +nightly fuzz run node `. + - name: Upload crash reproducers + if: failure() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: fuzz-artifacts-node + path: fuzz/artifacts/node/ + if-no-files-found: ignore + retention-days: 30 + + # New inputs found by this run exist only on the runner and in this + # 7-day artifact; nothing commits them back. To keep them, download the + # artifact, minimise with `cargo +nightly fuzz cmin node`, and commit. + - name: Upload grown corpus if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: fuzz-corpus-node path: fuzz/corpus/node/