Skip to content

chore(main): release 0.20.0 #468

chore(main): release 0.20.0

chore(main): release 0.20.0 #468

Workflow file for this run

name: Fuzzing Smoke Tests
on:
pull_request:
branches: [ main, develop ]
paths:
- 'rust/**'
- '.github/workflows/fuzz-smoke.yml'
permissions:
contents: read
pull-requests: read
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
fuzz-smoke:
name: Fuzz Smoke Test (60s per target)
runs-on: ubuntu-latest
# Budget (cold): ~2 min rustup + ~10 min cargo-fuzz install + ~10 min
# one-shot ASAN build of all targets + ~1 min per fuzz target (count derived
# from `cargo fuzz list`, ~14 min today) + slack. rust-cache below skips the
# install and most of the build on a warm run — this workflow is PR-only, so
# a PR's first push is cold and later pushes to the same PR are warm.
timeout-minutes: 45
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Validate corpus layout
run: |
cd rust/fuzz
# Every [[bin]] target must have committed seeds in corpus/<target>/ —
# a target without them fuzzes cold from random bytes and the run is
# far shallower than its green badge suggests. Pure bash,
# fails loudly on a missing/empty seed directory or a blown 10MB
# budget. Without this step the seed requirement is manual-only.
#
# Runs before the toolchain install deliberately: it needs only bash +
# coreutils and reads files present straight from checkout, so putting
# it here fails a missing seed directory in seconds instead of after
# ~12 min of rustup + cargo-fuzz install.
bash scripts/validate_corpus.sh
- name: Install Rust nightly
run: |
# Pin nightly: cargo-fuzz's rustix dependency uses
# rustc_layout_scalar_valid_range_* attributes reserved after
# nightly-2026-04-27. Last known-good date.
rustup toolchain install nightly-2026-04-27
rustup default nightly-2026-04-27
# After the toolchain step so the cache key hashes the pinned nightly, not
# the image's stable. Caches ~/.cargo/bin (cargo-fuzz) and the fuzz crate's
# dependency artifacts; the fuzz targets themselves always rebuild.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: rust/fuzz
- name: Install cargo-fuzz
# Version-pinned: a floating `cargo install` hands a hijacked cargo-fuzz
# release immediate code exec on the CI runner.
run: cargo install --locked cargo-fuzz --version 0.13.2
- name: Verify fuzz lockfile is current
run: |
cd rust/fuzz
# `cargo fuzz build/run` have no --locked passthrough, so the committed
# Cargo.lock is advisory to them: on any manifest/lock drift cargo would
# silently re-resolve every transitive to newest-on-crates.io. This is
# the only step that enforces the lock — it fails loudly on drift, and
# warms the registry cache besides.
cargo +nightly-2026-04-27 fetch --locked
- name: Build all fuzz targets
run: |
cd rust/fuzz
# One cargo invocation builds every [[bin]] target, sharing dependency
# compilation. A target that fails to compile fails the job here.
cargo +nightly-2026-04-27 fuzz build
- name: Run fuzzing smoke tests
id: fuzz
run: |
cd rust/fuzz
# Create artifacts directory
mkdir -p artifacts
# Every target `cargo fuzz list` reports runs — no hand-maintained
# allow-list in this workflow to drift out of date.
# Resolve the list in a standalone assignment: `for t in $(cmd)` does
# not propagate a failing $(cmd) under `set -e`, and an empty list
# would loop zero times and go green having fuzzed nothing.
TARGETS=$(cargo +nightly-2026-04-27 fuzz list)
if [ -z "$TARGETS" ]; then
echo "::error::cargo fuzz list returned no targets"
exit 1
fi
# `cargo fuzz list` enumerates the [[bin]] stanzas in Cargo.toml, NOT the
# files in fuzz_targets/. So a new fuzz_targets/*.rs added without its
# stanza is never built and never run, and this job still goes green —
# the same silent-dark mode the old hardcoded array caused. Nothing in
# cargo enforces that the two agree, so assert it here. Count only
# fuzz_target! sources: validate_corpus.sh deliberately tolerates a
# shared helper module in fuzz_targets/, and an unfiltered count would
# fail this job on the same tree that just passed validation — 12
# minutes later, with a misleading "add the missing stanza" error.
SRC_COUNT=$(grep -l 'fuzz_target!' fuzz_targets/*.rs | wc -l)
LIST_COUNT=$(printf '%s\n' "$TARGETS" | wc -l)
if [ "$SRC_COUNT" -ne "$LIST_COUNT" ]; then
echo "::error::fuzz_targets/ holds $SRC_COUNT sources but Cargo.toml declares $LIST_COUNT [[bin]] targets — add the missing [[bin]] stanza so the new target actually runs"
exit 1
fi
# A non-zero exit (crash found, or target failed to run) fails the
# job immediately: green must mean "fuzzed and clean".
for target in $TARGETS; do
echo "Fuzzing $target..."
cargo +nightly-2026-04-27 fuzz run "$target" -- -max_total_time=60
done
# Belt-and-braces: fail on any crash artifact even if the runs above
# all exited zero.
if find artifacts -name 'crash-*' -o -name 'timeout-*' -o -name 'oom-*' | grep -q .; then
echo "::error::Fuzzing discovered crashes or errors. See artifacts for details."
exit 1
fi
echo "All fuzz targets completed without crashes"
- name: Upload crash artifacts
if: failure()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: fuzz-crash-artifacts
path: rust/fuzz/artifacts/
# Short window deliberately: this repo is public and this job runs on
# pull_request, so the artifact is a working libFuzzer reproducer for an
# unfixed defect in the shipped compression/AES-GCM path, downloadable by
# anyone. 3 days is enough to triage; 30 is a month-long public window.
retention-days: 3
if-no-files-found: warn