fuzz: fold cycle.sh script into fuzz.sh
What changed, and why it matters
This commit is a routine cleanup of the project's fuzz-testing shell scripts. It merges the old cycle.sh script into fuzz.sh and adds a -cycle command-line flag. There is no change to the actual Bitcoin library code, no bug fix, and no security patch.
No security action needed; this is a developer-experience tooling change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff refactors fuzzing tooling only: cycle.sh is deleted and its continuous-loop logic is folded into fuzz.sh with a new -cycle option, default duration handling, and SCHED_IDLE priority (chrt) only in cycle mode. No Rust source, cryptographic, parsing, or consensus code is modified.
Changed components
fuzz/fuzz.shfuzz/cycle.shfuzz/README.mdInspect captured patch +64 / −49
diff --git a/fuzz/README.md b/fuzz/README.md
index 448bfc99..cd5cc679 100644
--- a/fuzz/README.md
+++ b/fuzz/README.md
@@ -52,20 +52,24 @@ To see the full list of targets, the most straightforward way is to run
cargo fuzz list
```
-To run each of them for an hour, run
+To run continuous fuzzing, use the `-cycle` flag:
```bash
-./cycle.sh
+./fuzz.sh -cycle
+```
+
+This will loop through each target indefinitely, running each for 1 hour
+by default. The fuzzer will run at low process priority (using `chrt`) to
+avoid blocking other work. Pass `-max_total_time` to customize the duration:
+
+```bash
+./fuzz.sh -cycle -max_total_time=7200
```
-This script uses the `chrt` utility to try to reduce the priority of the
-jobs. If you would like to run for longer, the most straightforward way
-is to edit `cycle.sh` before starting. To run the fuzz-tests in parallel,
-you will need to implement a custom harness.
To run a single fuzztest indefinitely, run
```bash
-cargo +nightly fuzz run "<target>"
+./fuzz.sh -cycle <target>
```
## Adding fuzz tests
diff --git a/fuzz/cycle.sh b/fuzz/cycle.sh
deleted file mode 100755
index 84c1cb6a..00000000
--- a/fuzz/cycle.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-# Continuously cycle over fuzz targets running each for 1 hour.
-# It uses chrt SCHED_IDLE so that other process takes priority.
-#
-# For cargo-fuzz usage see https://github.com/rust-fuzz/cargo-fuzz?tab=readme-ov-file#usage
-
-set -euo pipefail
-
-REPO_DIR=$(git rev-parse --show-toplevel)
-# can't find the file because of the ENV var
-# shellcheck source=/dev/null
-source "$REPO_DIR/fuzz/fuzz-util.sh"
-
-while :
-do
- for targetFile in $(listTargetFiles); do
- targetName=$(targetFileToName "$targetFile")
- echo "Fuzzing target $targetName ($targetFile)"
-
- # fuzz for one hour
- chrt -i 0 cargo +nightly fuzz run "$targetName" -- -max_total_time=3600
- cargo +nightly fuzz cmin "$targetName"
- done
-done
-
diff --git a/fuzz/fuzz.sh b/fuzz/fuzz.sh
index e4052637..eb43f8d0 100755
--- a/fuzz/fuzz.sh
+++ b/fuzz/fuzz.sh
@@ -1,8 +1,14 @@
#!/usr/bin/env bash
-# Briefly fuzz the provided target, or all targets if none provided.
+# Fuzz the provided target(s), or all targets if none provided.
#
-# Usage: fuzz.sh [TARGET] [-max_total_time=SECONDS]
+# Usage: fuzz.sh [TARGET] [-max_total_time=SECONDS] [-cycle]
+#
+# Options:
+# TARGET Specific fuzz target to run (e.g., bitcoin_deserialize_block)
+# -max_total_time=SECONDS Fuzzing duration in seconds (default: 100 in normal mode, 3600 in cycle mode)
+# -cycle Continuous fuzzing: loop through all targets indefinitely,
+# running corpus minimization after each target, with low process priority
set -euox pipefail
@@ -13,13 +19,17 @@ REPO_DIR=$(git rev-parse --show-toplevel)
source "$REPO_DIR/fuzz/fuzz-util.sh"
target=
-max_total_time=100
+max_total_time=
+cycle_mode=false
for arg in "$@"; do
case "$arg" in
-max_total_time=*)
max_total_time="${arg#-max_total_time=}"
;;
+ -cycle)
+ cycle_mode=true
+ ;;
-*)
echo "Unknown option: $arg"
exit 2
@@ -34,6 +44,15 @@ for arg in "$@"; do
esac
done
+# Set default max_total_time based on mode, 1 hour for cycle, 100 seconds for default.
+if [ -z "$max_total_time" ]; then
+ if [ "$cycle_mode" = true ]; then
+ max_total_time=3600
+ else
+ max_total_time=100
+ fi
+fi
+
case "$max_total_time" in
''|*[!0-9]*)
echo "-max_total_time must be a non-negative integer number of seconds"
@@ -53,19 +72,37 @@ fi
cargo --version
rustc --version
-# Testing
cargo install --force --locked --version 0.12.0 cargo-fuzz
-for targetFile in $targetFiles; do
- targetName=$(targetFileToName "$targetFile")
-
- echo "Fuzzing target $targetName ($targetFile) for $max_total_time seconds"
- # Enable fuzz stubs in the hashes and cryptography libraries by default,
- # unless we are fuzzing the hashes targets themselves.
- fuzz_rustflags=''
- if [[ ! "$targetName" =~ ^hashes_ ]]; then
- fuzz_rustflags='--cfg=hashes_fuzz --cfg=secp256k1_fuzz'
+while :; do
+ for targetFile in $targetFiles; do
+ targetName=$(targetFileToName "$targetFile")
+
+ echo "Fuzzing target $targetName ($targetFile) for $max_total_time seconds"
+ # Enable fuzz stubs in the hashes and cryptography libraries by default,
+ # unless we are fuzzing the hashes targets themselves.
+ fuzz_rustflags=''
+ if [[ ! "$targetName" =~ ^hashes_ ]]; then
+ fuzz_rustflags='--cfg=hashes_fuzz --cfg=secp256k1_fuzz'
+ fi
+ # cargo-fuzz will check for the corpus at fuzz/corpus/<target>
+ # Use chrt to run at SCHED_IDLE priority (lowest) to avoid blocking other work.
+ chrt_cmd=''
+ if [ "$cycle_mode" = true ]; then
+ chrt_cmd='chrt -i 0'
+ fi
+ RUSTFLAGS="$RUSTFLAGS $fuzz_rustflags" $chrt_cmd cargo +nightly fuzz run "$targetName" -- -max_total_time="$max_total_time"
+
+ echo "Minimizing corpus for target $targetName"
+ cargo +nightly fuzz cmin "$targetName"
+
+ # Check for artifacts/crashes in normal mode.
+ if [ "$cycle_mode" = false ]; then
+ checkReport "$targetName"
+ fi
+ done
+
+ # Exit after one cycle if not in cycle mode.
+ if [ "$cycle_mode" = false ]; then
+ break
fi
- # cargo-fuzz will check for the corpus at fuzz/corpus/<target>
- RUSTFLAGS="$RUSTFLAGS $fuzz_rustflags" cargo +nightly fuzz run "$targetName" -- -max_total_time="$max_total_time"
- checkReport "$targetName"
done
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.