diff --git a/.github/workflows/corpus-update.yml b/.github/workflows/corpus-update.yml index a0b7e2f66..d201d8ff7 100644 --- a/.github/workflows/corpus-update.yml +++ b/.github/workflows/corpus-update.yml @@ -162,9 +162,16 @@ jobs: run: | T="${{ matrix.target }}" mkdir -p "afl-output/$T" + # The lockstep target's execs (~0.8 s on accept-heavy seeds) exceed AFL++'s + # default 1000 ms calibration limit and abort the dry run; give only it an + # auto-scaled cap (-t 5000+). + TFLAG="" + if [ "$T" = "fuzz_stateful_model_lockstep" ]; then + TFLAG="-t 5000+" + fi set +e timeout ${{ needs.config.outputs.duration }} \ - afl-fuzz -i "afl-seeds/$T" -o "afl-output/$T" -- "fuzz/target/release/$T" + afl-fuzz $TFLAG -i "afl-seeds/$T" -o "afl-output/$T" -- "fuzz/target/release/$T" rc=$? set -e # 124 = SIGALRM from timeout (expected end); 0 = clean exit; else real failure @@ -203,8 +210,15 @@ jobs: exit 0 fi before=$(ls "$SRC" | wc -l) + # The lockstep target's accept-heavy seeds run ~0.8 s/exec; with the default + # 1000 ms limit afl-cmin would drop them as timeouts and corrupt the minimised + # corpus. cmin's arg parser rejects the '+' suffix, so use a plain 5 s cap. + TFLAG="" + if [ "$T" = "fuzz_stateful_model_lockstep" ]; then + TFLAG="-t 5000" + fi # afl-cmin can fail on pathological corpora; fall back to leaving SRC as-is. - if afl-cmin -i "$SRC" -o "afl-cmin/$T" -- "fuzz/target/release/$T"; then + if afl-cmin $TFLAG -i "$SRC" -o "afl-cmin/$T" -- "fuzz/target/release/$T"; then rm -rf "$SRC" mkdir -p "$SRC" cp "afl-cmin/$T"/* "$SRC"/ 2>/dev/null || true diff --git a/.github/workflows/fuzz-afl.yml b/.github/workflows/fuzz-afl.yml index 3a2b619b7..7a3a0a3b9 100644 --- a/.github/workflows/fuzz-afl.yml +++ b/.github/workflows/fuzz-afl.yml @@ -5,7 +5,7 @@ on: - cron: "0 2 * * *" workflow_dispatch: push: - branches: [main, chore-corpus-update] + branches: [main, fix-add-timeout-stateful-lockstep] env: RISC0_DEV_MODE: "1" @@ -90,11 +90,20 @@ jobs: run: | TARGET="${{ matrix.target }}" mkdir -p afl-output/${TARGET} + # The model-based lockstep target runs the real state machine (risc0 executor) + # once per command, so accept-heavy seeds take ~0.8 s/exec — over AFL++'s default + # 1000 ms calibration limit, which aborts the dry run. Give only that target an + # auto-scaled cap (-t 5000+); all others keep the default timeout. + TFLAG="" + if [ "$TARGET" = "fuzz_stateful_model_lockstep" ]; then + TFLAG="-t 5000+" + fi # Disable errexit so that timeout's exit code 124 (expected signal) does not # cause bash -e to abort the script before the guard below can run. set +e timeout 60 \ afl-fuzz \ + $TFLAG \ -i afl-seeds/${TARGET} \ -o afl-output/${TARGET} \ -- fuzz/target/release/${TARGET} @@ -124,8 +133,15 @@ jobs: BINARY="fuzz/target/release/${TARGET}" showmap_filled="n/a" showmap_pct="n/a" + # The lockstep target's accept-heavy seeds run ~0.8 s/exec; without a raised + # timeout afl-showmap would skip them, undercounting edge coverage. afl-showmap + # takes a fixed cap only (no '+' auto-scale), so use a plain 5 s cap for it. + SHOWMAP_TFLAG="" + if [ "$TARGET" = "fuzz_stateful_model_lockstep" ]; then + SHOWMAP_TFLAG="-t 5000" + fi if [ -d "$CORPUS" ] && [ -f "$BINARY" ]; then - afl-showmap -C \ + afl-showmap -C $SHOWMAP_TFLAG \ -i "$CORPUS" \ -o "afl-edges-${TARGET}.txt" \ -- "$BINARY" 2>/dev/null || true diff --git a/Justfile b/Justfile index 4dd333168..84923e561 100644 --- a/Justfile +++ b/Justfile @@ -299,9 +299,18 @@ fuzz-afl TARGET="" TIME="30": echo "Binary not found — building $t first…" just afl-build-target "$t" fi + # Per-target execution timeout. The model-based lockstep target runs the real + # state machine (risc0 executor) once per command, so accept-heavy seeds take + # ~0.8 s/exec — over AFL++'s default 1000 ms calibration limit, which aborts the + # dry run. Give it an auto-scaled cap (-t 5000+); all other targets keep the + # default. Unquoted so an empty value expands to no argument (bash 3.2 safe). + local TFLAG="" + if [ "$t" = "fuzz_stateful_model_lockstep" ]; then + TFLAG="-t 5000+" + fi # Use afl-fuzz's own -V (run for N seconds then exit) instead of GNU # `timeout`, which is not installed by default on macOS. - afl-fuzz -V "$TIME" -i "$CORPUS" -o "$OUTPUT" -- "$BINARY" || true + afl-fuzz -V "$TIME" $TFLAG -i "$CORPUS" -o "$OUTPUT" -- "$BINARY" || true } for t in "${TARGETS[@]}"; do echo "=== afl++ $t for ${TIME}s ===" @@ -429,9 +438,17 @@ fuzz-afl-parallel TIME="30" JOBS="4": echo "Binary not found — building $t first…" just afl-build-target "$t" fi + # Per-target execution timeout — see the note in `fuzz-afl`. The lockstep target + # runs the risc0 executor per command (~0.8 s/exec on accept-heavy seeds), which + # exceeds AFL++'s default 1000 ms calibration limit; give it -t 5000+. Unquoted so + # an empty value expands to no argument (bash 3.2 safe). + local TFLAG="" + if [ "$t" = "fuzz_stateful_model_lockstep" ]; then + TFLAG="-t 5000+" + fi # Use afl-fuzz's own -V (run for N seconds then exit) instead of GNU # `timeout`, which is not installed by default on macOS. - afl-fuzz -V {{TIME}} -i "$CORPUS" -o "$OUTPUT" -- "$BINARY" || true + afl-fuzz -V {{TIME}} $TFLAG -i "$CORPUS" -o "$OUTPUT" -- "$BINARY" || true } echo "Targets: ${#TARGETS[@]} | max parallel: {{JOBS}} | time per target: {{TIME}}s" for t in "${TARGETS[@]}"; do