From d86297ea8912593db7fd6f850c117fbae223ea34 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Fri, 20 Mar 2026 08:42:11 +0100 Subject: [PATCH 1/2] ci: split fuzz sanity check into separate parallel job The sanity check (cargo test on fuzz targets) doesn't use the restored corpus and was blocking the actual fuzz run. Move it to a separate fuzz_sanity job so both run in parallel. AI tools were used in preparing this commit. --- .github/workflows/build.yml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d512791420..13650d222b5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -205,6 +205,21 @@ jobs: - name: Simulate docs.rs build run: ci/check-docsrs.sh + fuzz_sanity: + runs-on: self-hosted + env: + TOOLCHAIN: 1.75 + steps: + - name: Checkout source code + uses: actions/checkout@v4 + - name: Install Rust ${{ env.TOOLCHAIN }} toolchain + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain ${{ env.TOOLCHAIN }} + - name: Sanity check fuzz targets on Rust ${{ env.TOOLCHAIN }} + run: | + cd fuzz + RUSTFLAGS="--cfg=fuzzing --cfg=secp256k1_fuzz --cfg=hashes_fuzz" cargo test --quiet --color always --lib --bins -j8 + fuzz: runs-on: self-hosted env: @@ -238,11 +253,6 @@ jobs: key: fuzz-corpus-refs/heads/main-${{ github.sha }} restore-keys: | fuzz-corpus-refs/heads/main- - - name: Sanity check fuzz targets on Rust ${{ env.TOOLCHAIN }} - run: | - cd fuzz - RUSTFLAGS="--cfg=fuzzing --cfg=secp256k1_fuzz --cfg=hashes_fuzz" cargo test --verbose --color always --lib --bins -j8 - cargo clean - name: Run fuzzers run: cd fuzz && ./ci-fuzz.sh && cd .. - name: Upload honggfuzz corpus @@ -308,7 +318,7 @@ jobs: TOR_PROXY="127.0.0.1:9050" RUSTFLAGS="--cfg=tor" cargo test --verbose --color always -p lightning-net-tokio notify-failure: - needs: [build-workspace, build-features, build-bindings, build-nostd, build-cfg-flags, build-sync, fuzz, linting, rustfmt, check_release, check_docs, benchmark, ext-test, tor-connect, coverage] + needs: [build-workspace, build-features, build-bindings, build-nostd, build-cfg-flags, build-sync, fuzz_sanity, fuzz, linting, rustfmt, check_release, check_docs, benchmark, ext-test, tor-connect, coverage] if: failure() && github.ref == 'refs/heads/main' runs-on: ubuntu-latest permissions: From d3ec1c9af2ec2d8b9ffa351c0af37662805b0566 Mon Sep 17 00:00:00 2001 From: Joost Jager Date: Fri, 20 Mar 2026 15:38:12 +0100 Subject: [PATCH 2/2] fuzz: improve iteration scaling, add minimization and summary table Replace the fixed 30s run_time with iteration counts scaled to 8x corpus size (plus a 1000 baseline) with a 10-minute hard cap per target. This ensures the full corpus is replayed with room for mutations, while small targets finish quickly. On main (and on PRs with the fuzz-minimize label), run honggfuzz corpus minimization after each target to prune inputs that don't contribute unique coverage, keeping the cache size manageable. Print a summary table at the end with per-target stats: iterations, corpus sizes before/after fuzzing and minimization, and run times. Other changes: - Use -q (quiet) to suppress per-iteration status output - Set 3s per-input timeout (-t 3) for all targets - Pass FUZZ_MINIMIZE env var from PR label in workflow AI tools were used in preparing this commit. --- .github/workflows/build.yml | 2 ++ fuzz/ci-fuzz.sh | 52 ++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 13650d222b5..664b6d9a9c0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -255,6 +255,8 @@ jobs: fuzz-corpus-refs/heads/main- - name: Run fuzzers run: cd fuzz && ./ci-fuzz.sh && cd .. + env: + FUZZ_MINIMIZE: ${{ contains(github.event.pull_request.labels.*.name, 'fuzz-minimize') }} - name: Upload honggfuzz corpus uses: actions/upload-artifact@v4 with: diff --git a/fuzz/ci-fuzz.sh b/fuzz/ci-fuzz.sh index d57a5ad78fa..778e37fac6e 100755 --- a/fuzz/ci-fuzz.sh +++ b/fuzz/ci-fuzz.sh @@ -30,15 +30,28 @@ sed -i 's/lto = true//' Cargo.toml export HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" cargo --color always hfuzz build -j8 + +SUMMARY="" + for TARGET in src/bin/*.rs; do FILENAME=$(basename $TARGET) FILE="${FILENAME%.*}" - HFUZZ_RUN_ARGS="--exit_upon_crash -v -n8 --run_time 30" + CORPUS_DIR="hfuzz_workspace/$FILE/input" + CORPUS_COUNT=$(find "$CORPUS_DIR" -type f 2>/dev/null | wc -l) + # Run 8x the corpus size plus a baseline, ensuring full corpus replay + # with room for new mutations. The 10-minute hard cap (--run_time 600) + # prevents slow-per-iteration targets from running too long. + ITERATIONS=$((CORPUS_COUNT * 8 + 1000)) + HFUZZ_RUN_ARGS="--exit_upon_crash -q -n8 -t 3 -N $ITERATIONS --run_time 600" if [ "$FILE" = "chanmon_consistency_target" -o "$FILE" = "fs_store_target" ]; then HFUZZ_RUN_ARGS="$HFUZZ_RUN_ARGS -F 64" fi export HFUZZ_RUN_ARGS + FUZZ_START=$(date +%s) cargo --color always hfuzz run $FILE + FUZZ_END=$(date +%s) + FUZZ_TIME=$((FUZZ_END - FUZZ_START)) + FUZZ_CORPUS_COUNT=$(find "$CORPUS_DIR" -type f 2>/dev/null | wc -l) if [ -f hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT ]; then cat hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT for CASE in hfuzz_workspace/$FILE/SIG*; do @@ -46,4 +59,41 @@ for TARGET in src/bin/*.rs; do done exit 1 fi + if [ "$GITHUB_REF" = "refs/heads/main" ] || [ "$FUZZ_MINIMIZE" = "true" ]; then + HFUZZ_RUN_ARGS="-M -q -n8 -t 3" + export HFUZZ_RUN_ARGS + MIN_START=$(date +%s) + cargo --color always hfuzz run $FILE + MIN_END=$(date +%s) + MIN_TIME=$((MIN_END - MIN_START)) + MIN_CORPUS_COUNT=$(find "$CORPUS_DIR" -type f 2>/dev/null | wc -l) + SUMMARY="${SUMMARY}${FILE}|${ITERATIONS}|${CORPUS_COUNT}|${FUZZ_CORPUS_COUNT}|${FUZZ_TIME}|${MIN_CORPUS_COUNT}|${MIN_TIME}\n" + else + SUMMARY="${SUMMARY}${FILE}|${ITERATIONS}|${CORPUS_COUNT}|${FUZZ_CORPUS_COUNT}|${FUZZ_TIME}|-|-\n" + fi +done + +fmt_time() { + local secs=$1 + printf "%dm%ds" $((secs / 60)) $((secs % 60)) +} + +# Print summary table +set +x +echo "" +echo "==== Fuzz Summary ====" +HDR="%-40s %7s %7s %-15s %9s %-15s %9s\n" +FMT="%-40s %7s %7s %6s %-9s %9s %6s %-9s %9s\n" +printf "$HDR" "Target" "Iters" "Corpus" " Fuzzed" "Fuzz time" " Minimized" "Min. time" +printf "$HDR" "------" "-----" "------" "---------------" "---------" "---------------" "---------" +echo -e "$SUMMARY" | while IFS='|' read -r name iters orig fuzzed ftime minimized mtime; do + [ -z "$name" ] && continue + fuzz_delta=$((fuzzed - orig)) + if [ "$minimized" = "-" ]; then + printf "$FMT" "$name" "$iters" "$orig" "$fuzzed" "(+$fuzz_delta)" "$(fmt_time "$ftime")" "-" "" "-" + else + min_delta=$((minimized - fuzzed)) + printf "$FMT" "$name" "$iters" "$orig" "$fuzzed" "(+$fuzz_delta)" "$(fmt_time "$ftime")" "$minimized" "($min_delta)" "$(fmt_time "$mtime")" + fi done +echo "======================"