From 67b70e4066670f88d6684d2ca5825b4f47765688 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 8 Sep 2025 22:29:24 +0000 Subject: [PATCH 1/6] ci: fix new lifetime lint This lint appears when running the stable compiler, not just clippy. We don't pin the stable compiler version, so this has started showing up in CI, which is a bit noisy. --- src/descriptor/mod.rs | 2 +- src/descriptor/tr/mod.rs | 2 +- src/descriptor/tr/spend_info.rs | 2 +- src/descriptor/tr/taptree.rs | 2 +- src/miniscript/iter.rs | 4 ++-- src/policy/concrete.rs | 2 +- src/primitives/threshold.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 44b93ce59..9bc6f2c31 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -294,7 +294,7 @@ impl Descriptor { /// /// If the descriptor is not a Taproot descriptor, **or** if the descriptor is a /// Taproot descriptor containing only a keyspend, returns an empty iterator. - pub fn tap_tree_iter(&self) -> tr::TapTreeIter { + pub fn tap_tree_iter(&self) -> tr::TapTreeIter<'_, Pk> { if let Descriptor::Tr(ref tr) = self { if let Some(tree) = tr.tap_tree() { return tree.leaves(); diff --git a/src/descriptor/tr/mod.rs b/src/descriptor/tr/mod.rs index f0f4ccfc4..203e3cee2 100644 --- a/src/descriptor/tr/mod.rs +++ b/src/descriptor/tr/mod.rs @@ -108,7 +108,7 @@ impl Tr { /// /// The yielded elements include the Miniscript for each leave as well as its depth /// in the tree, which is the data required by PSBT (BIP 371). - pub fn leaves(&self) -> TapTreeIter { + pub fn leaves(&self) -> TapTreeIter<'_, Pk> { match self.tree { Some(ref t) => t.leaves(), None => TapTreeIter::empty(), diff --git a/src/descriptor/tr/spend_info.rs b/src/descriptor/tr/spend_info.rs index 3bace2d18..b0d23121d 100644 --- a/src/descriptor/tr/spend_info.rs +++ b/src/descriptor/tr/spend_info.rs @@ -181,7 +181,7 @@ impl TrSpendInfo { /// This yields the same leaves in the same order as [`super::Tr::leaves`] on the original /// [`super::Tr`]. However, in addition to yielding the leaves and their depths, it also /// yields their scripts, leafhashes, and control blocks. - pub fn leaves(&self) -> TrSpendInfoIter { + pub fn leaves(&self) -> TrSpendInfoIter<'_, Pk> { TrSpendInfoIter { spend_info: self, index: 0, diff --git a/src/descriptor/tr/taptree.rs b/src/descriptor/tr/taptree.rs index 35f23a95e..27e860390 100644 --- a/src/descriptor/tr/taptree.rs +++ b/src/descriptor/tr/taptree.rs @@ -53,7 +53,7 @@ impl TapTree { /// /// The yielded elements include the Miniscript for each leave as well as its depth /// in the tree, which is the data required by PSBT (BIP 371). - pub fn leaves(&self) -> TapTreeIter { TapTreeIter::from_tree(self) } + pub fn leaves(&self) -> TapTreeIter<'_, Pk> { TapTreeIter::from_tree(self) } /// Converts keys from one type of public key to another. pub fn translate_pk( diff --git a/src/miniscript/iter.rs b/src/miniscript/iter.rs index f82e329aa..0ac48e651 100644 --- a/src/miniscript/iter.rs +++ b/src/miniscript/iter.rs @@ -18,12 +18,12 @@ impl Miniscript { /// Creates a new [Iter] iterator that will iterate over all [Miniscript] items within /// AST by traversing its branches. For the specific algorithm please see /// [Iter::next] function. - pub fn iter(&self) -> Iter { Iter::new(self) } + pub fn iter(&self) -> Iter<'_, Pk, Ctx> { Iter::new(self) } /// Creates a new [PkIter] iterator that will iterate over all plain public keys (and not /// key hash values) present in [Miniscript] items within AST by traversing all its branches. /// For the specific algorithm please see [PkIter::next] function. - pub fn iter_pk(&self) -> PkIter { PkIter::new(self) } + pub fn iter_pk(&self) -> PkIter<'_, Pk, Ctx> { PkIter::new(self) } /// Enumerates all child nodes of the current AST node (`self`) and returns a `Vec` referencing /// them. diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 01ac117a2..547b4e86d 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -192,7 +192,7 @@ impl Policy { /// Since this splitting might lead to exponential blow-up, we constrain the number of /// leaf-nodes to [`MAX_COMPILATION_LEAVES`]. #[cfg(feature = "compiler")] - fn tapleaf_probability_iter(&self) -> TapleafProbabilityIter { + fn tapleaf_probability_iter(&self) -> TapleafProbabilityIter<'_, Pk> { TapleafProbabilityIter { stack: vec![(1.0, self)] } } diff --git a/src/primitives/threshold.rs b/src/primitives/threshold.rs index ee0c1bda5..f1021e9a4 100644 --- a/src/primitives/threshold.rs +++ b/src/primitives/threshold.rs @@ -217,7 +217,7 @@ impl Threshold { pub fn into_data(self) -> Vec { self.inner } /// Passthrough to an iterator on the underlying vector. - pub fn iter(&self) -> core::slice::Iter { self.inner.iter() } + pub fn iter(&self) -> core::slice::Iter<'_, T> { self.inner.iter() } } impl Threshold { From 46e9dc1d407d56b073e677ccd817e038eb764489 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 9 Sep 2025 21:35:59 +0000 Subject: [PATCH 2/6] rustdoc: remove link to doc/compiler.md I'm not sure if it's possible to link to .md files in the source code. If so, this isn't the way to do it. --- src/policy/concrete.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 547b4e86d..a2c41a987 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -346,7 +346,7 @@ impl Policy { /// /// It is **not recommended** to use policy as a stable identifier for a miniscript. You should /// use the policy compiler once, and then use the miniscript output as a stable identifier. See - /// the compiler document in [`doc/compiler.md`] for more details. + /// the compiler document in `doc/compiler.md` for more details. #[cfg(feature = "compiler")] pub fn compile_to_descriptor( &self, @@ -375,7 +375,7 @@ impl Policy { /// /// It is **not recommended** to use policy as a stable identifier for a miniscript. You should /// use the policy compiler once, and then use the miniscript output as a stable identifier. See - /// the compiler document in doc/compiler.md for more details. + /// the compiler document in `doc/compiler.md` for more details. #[cfg(feature = "compiler")] pub fn compile(&self) -> Result, CompilerError> { self.is_valid()?; From fcdaf86176139f7e3ee699e6394b83052511152f Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 9 Sep 2025 23:37:41 +0000 Subject: [PATCH 3/6] clippy: a couple more lints TBH I had thought we'd fixed these years ago.. --- src/descriptor/key.rs | 4 +--- src/miniscript/mod.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index 2162010d7..241f931ee 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -1203,9 +1203,7 @@ impl DescriptorXKey { }; if &compare_fingerprint == fingerprint - && compare_path - .into_iter() - .eq(path_excluding_wildcard.into_iter()) + && compare_path.into_iter().eq(&path_excluding_wildcard) { Some(path_excluding_wildcard) } else { diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index e319304a2..9b1b85bf2 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -604,7 +604,7 @@ impl Miniscript { /// The type information and extra properties are implied by the AST. impl PartialOrd for Miniscript { fn partial_cmp(&self, other: &Miniscript) -> Option { - Some(self.node.cmp(&other.node)) + Some(self.cmp(other)) } } From ffeb6999a18a37d2f809c935db24538c0d0094c2 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 9 Sep 2025 21:36:31 +0000 Subject: [PATCH 4/6] bump nightly-version to 2025-09-05 Manually do #805 --- nightly-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nightly-version b/nightly-version index ba747383d..0db8f0002 100644 --- a/nightly-version +++ b/nightly-version @@ -1 +1 @@ -nightly-2025-03-21 +nightly-2025-09-05 From 035a997b26ec2bc3878fbe7b3d302e07a9f52cea Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Mon, 8 Sep 2025 22:07:28 +0000 Subject: [PATCH 5/6] fuzz: switch from honggfuzz to cargo-fuzz honggfuzz is increasingly hard for me to maintain in my local CI. It requires an exact match between the version of honggfuzz in the lockfile and the binary installed in the nix derivation, and these exact versions come with specific requirements about which rustc versions are allowable (and in particular, honggfuzz 0.5.56 does not work with rustc 1.74.0, which is the MSRV for rust-bitcoin). I also have to bend over backward to set compiler flags and make source directories writable. cargo-fuzz, meanwhile, requires nightly to *run* the fuzz tests, but does not require it for just running unit tests or building. (The latest version of libfuzzer-sys uses once_cell and requires a higher MSRV than we have, but we don't need to use the very latest version.) --- .github/workflows/cron-daily-fuzz.yml | 24 +++++- Cargo-minimal.lock | 59 ++++---------- Cargo-recent.lock | 79 ++++++++----------- fuzz/Cargo.toml | 5 +- fuzz/fuzz.sh | 21 ++--- fuzz/fuzz_targets/compile_descriptor.rs | 24 ++++-- fuzz/fuzz_targets/compile_taproot.rs | 24 ++++-- fuzz/fuzz_targets/miniscript_satisfy.rs | 17 ++-- fuzz/fuzz_targets/parse_descriptor.rs | 24 ++++-- .../fuzz_targets/parse_descriptor_definite.rs | 24 ++++-- fuzz/fuzz_targets/parse_descriptor_priv.rs | 24 ++++-- fuzz/fuzz_targets/parse_descriptor_secret.rs | 24 ++++-- fuzz/fuzz_targets/regression_compiler.rs | 17 ++-- .../regression_descriptor_parse.rs | 17 ++-- fuzz/fuzz_targets/regression_taptree.rs | 17 ++-- fuzz/fuzz_targets/roundtrip_concrete.rs | 17 ++-- fuzz/fuzz_targets/roundtrip_descriptor.rs | 17 ++-- .../roundtrip_miniscript_script.rs | 17 ++-- .../roundtrip_miniscript_script_tap.rs | 24 ++++-- fuzz/fuzz_targets/roundtrip_miniscript_str.rs | 17 ++-- fuzz/fuzz_targets/roundtrip_semantic.rs | 17 ++-- fuzz/generate-files.sh | 29 ++++++- 22 files changed, 302 insertions(+), 236 deletions(-) diff --git a/.github/workflows/cron-daily-fuzz.yml b/.github/workflows/cron-daily-fuzz.yml index 6d8c00208..7931675f3 100644 --- a/.github/workflows/cron-daily-fuzz.yml +++ b/.github/workflows/cron-daily-fuzz.yml @@ -9,9 +9,21 @@ on: - cron: '00 06 * * *' jobs: + Prepare: + runs-on: ubuntu-24.04 + outputs: + nightly_version: ${{ steps.read_toolchain.outputs.nightly_version }} + steps: + - name: "Checkout repo" + uses: actions/checkout@v4 + - name: "Read nightly version" + id: read_toolchain + run: echo "nightly_version=$(cat nightly-version)" >> $GITHUB_OUTPUT + fuzz: if: ${{ !github.event.act }} - runs-on: ubuntu-20.04 + needs: Prepare + runs-on: ubuntu-25.04 strategy: fail-fast: false matrix: @@ -47,9 +59,15 @@ roundtrip_semantic, fuzz/target target key: cache-${{ matrix.target }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} + + - name: Install toolchain - uses: dtolnay/rust-toolchain@stable with: - toolchain: '1.65.0' + toolchain: ${{ needs.Prepare.outputs.nightly_version }} + + - name: Install Dependencies + run: cargo update && cargo update -p cc --precise 1.0.83 && cargo install --force cargo-fuzz + - name: fuzz run: cd fuzz && ./fuzz.sh "${{ matrix.fuzz_target }}" - run: echo "${{ matrix.fuzz_target }}" >executed_${{ matrix.fuzz_target }} @@ -63,7 +81,7 @@ roundtrip_semantic, needs: fuzz runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 - name: Display structure of downloaded files run: ls -R diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 861bd2c62..c08dbaafd 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + [[package]] name = "arrayvec" version = "0.7.4" @@ -110,7 +116,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "descriptor-fuzz" version = "0.0.1" dependencies = [ - "honggfuzz", + "libfuzzer-sys", "miniscript 12.3.0", "miniscript 13.0.0", "regex", @@ -148,23 +154,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" -[[package]] -name = "honggfuzz" -version = "0.5.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c76b6234c13c9ea73946d1379d33186151148e0da231506b964b44f3d023505" -dependencies = [ - "lazy_static", - "memmap2", - "rustc_version", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.154" @@ -172,19 +161,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] -name = "memchr" -version = "2.5.0" +name = "libfuzzer-sys" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "86c975d637bc2a2f99440932b731491fc34c7f785d239e38af3addd3c2fd0e46" +dependencies = [ + "arbitrary", + "cc", +] [[package]] -name = "memmap2" -version = "0.9.4" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" -dependencies = [ - "libc", -] +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "miniscript" @@ -279,15 +269,6 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "secp256k1" version = "0.29.0" @@ -309,12 +290,6 @@ dependencies = [ "cc", ] -[[package]] -name = "semver" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" - [[package]] name = "serde" version = "1.0.199" diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 861bd2c62..4c0cf8036 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -11,6 +11,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + [[package]] name = "arrayvec" version = "0.7.4" @@ -96,9 +102,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.28" +version = "1.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" +checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cfg-if" @@ -110,12 +120,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "descriptor-fuzz" version = "0.0.1" dependencies = [ - "honggfuzz", + "libfuzzer-sys", "miniscript 12.3.0", "miniscript 13.0.0", "regex", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + [[package]] name = "getrandom" version = "0.2.14" @@ -148,23 +164,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" -[[package]] -name = "honggfuzz" -version = "0.5.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c76b6234c13c9ea73946d1379d33186151148e0da231506b964b44f3d023505" -dependencies = [ - "lazy_static", - "memmap2", - "rustc_version", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.154" @@ -172,19 +171,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] -name = "memchr" -version = "2.5.0" +name = "libfuzzer-sys" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8a9dc6556604b8ad76486563d5a47fad989b643932fa006e76e23d948bef0f5b" +dependencies = [ + "arbitrary", + "cc", +] [[package]] -name = "memmap2" -version = "0.9.4" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" -dependencies = [ - "libc", -] +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "miniscript" @@ -279,15 +279,6 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - [[package]] name = "secp256k1" version = "0.29.0" @@ -309,12 +300,6 @@ dependencies = [ "cc", ] -[[package]] -name = "semver" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" - [[package]] name = "serde" version = "1.0.199" @@ -344,6 +329,12 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "syn" version = "2.0.56" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 205182e0e..88b571fac 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,7 +10,7 @@ publish = false cargo-fuzz = true [dependencies] -honggfuzz = { version = "0.5.56", default-features = false } +libfuzzer-sys = "0.4" # We shouldn't need an explicit version on the next line, but Andrew's tools # choke on it otherwise. See https://github.com/nix-community/crate2nix/issues/373 miniscript = { path = "..", features = [ "compiler" ], version = "13.0" } @@ -18,6 +18,9 @@ old_miniscript = { package = "miniscript", features = [ "compiler" ], version = regex = "1.0" +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } + [[bin]] name = "compile_descriptor" path = "fuzz_targets/compile_descriptor.rs" diff --git a/fuzz/fuzz.sh b/fuzz/fuzz.sh index 75a9789a5..2f287b132 100755 --- a/fuzz/fuzz.sh +++ b/fuzz/fuzz.sh @@ -1,10 +1,10 @@ #!/usr/bin/env bash -set -ex +set -o errexit # exit immediately if any command fails +set -o xtrace # print trace of executed commands -REPO_DIR=$(git rev-parse --show-toplevel || jj workspace show) +REPO_DIR=$(git rev-parse --show-toplevel) -# can't find the file because of the ENV var -# shellcheck source=/dev/null +# shellcheck source=./fuzz/fuzz-util.sh source "$REPO_DIR/fuzz/fuzz-util.sh" # Check that input files are correct Windows file names @@ -19,17 +19,8 @@ fi cargo --version rustc --version -# Testing -cargo install --force honggfuzz --no-default-features +# Run fuzz target for targetFile in $targetFiles; do targetName=$(targetFileToName "$targetFile") - echo "Fuzzing target $targetName ($targetFile)" - if [ -d "hfuzz_input/$targetName" ]; then - HFUZZ_INPUT_ARGS="-f hfuzz_input/$targetName/input\"" - else - HFUZZ_INPUT_ARGS="" - fi - HFUZZ_RUN_ARGS="--run_time 30 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run "$targetName" - - checkReport "$targetName" + cargo-fuzz run "$targetName" -- -max_total_time=30 done diff --git a/fuzz/fuzz_targets/compile_descriptor.rs b/fuzz/fuzz_targets/compile_descriptor.rs index b246ed7a7..59b779e8d 100644 --- a/fuzz/fuzz_targets/compile_descriptor.rs +++ b/fuzz/fuzz_targets/compile_descriptor.rs @@ -1,8 +1,9 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] use std::str::FromStr; -use honggfuzz::fuzz; use miniscript::{policy, Miniscript, Segwitv0}; use policy::Liftable; @@ -28,10 +29,19 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } + +#[cfg(test)] +mod tests { + use miniscript::hex; + + #[test] + fn duplicate_crash() { + let v = hex::decode_to_vec("abcd").unwrap(); + super::do_test(&v); } } diff --git a/fuzz/fuzz_targets/compile_taproot.rs b/fuzz/fuzz_targets/compile_taproot.rs index 4d43ffff2..3bbd00384 100644 --- a/fuzz/fuzz_targets/compile_taproot.rs +++ b/fuzz/fuzz_targets/compile_taproot.rs @@ -1,8 +1,9 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] use std::str::FromStr; -use honggfuzz::fuzz; use miniscript::{policy, Miniscript, Tap}; use policy::Liftable; @@ -28,10 +29,19 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } + +#[cfg(test)] +mod tests { + use miniscript::hex; + + #[test] + fn duplicate_crash() { + let v = hex::decode_to_vec("abcd").unwrap(); + super::do_test(&v); } } diff --git a/fuzz/fuzz_targets/miniscript_satisfy.rs b/fuzz/fuzz_targets/miniscript_satisfy.rs index d22bde00c..03bbfee86 100644 --- a/fuzz/fuzz_targets/miniscript_satisfy.rs +++ b/fuzz/fuzz_targets/miniscript_satisfy.rs @@ -1,9 +1,10 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] use std::sync::atomic::{AtomicUsize, Ordering}; use descriptor_fuzz::FuzzPk; -use honggfuzz::fuzz; use miniscript::bitcoin::hashes::hash160; use miniscript::bitcoin::locktime::{absolute, relative}; use miniscript::bitcoin::taproot::Signature; @@ -156,13 +157,11 @@ fn do_test(data: &[u8]) { }; } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } #[cfg(test)] mod tests { diff --git a/fuzz/fuzz_targets/parse_descriptor.rs b/fuzz/fuzz_targets/parse_descriptor.rs index 040b6730d..410f2a8e6 100644 --- a/fuzz/fuzz_targets/parse_descriptor.rs +++ b/fuzz/fuzz_targets/parse_descriptor.rs @@ -1,8 +1,9 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] use std::str::FromStr; -use honggfuzz::fuzz; use miniscript::DescriptorPublicKey; fn do_test(data: &[u8]) { @@ -13,10 +14,19 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } + +#[cfg(test)] +mod tests { + use miniscript::hex; + + #[test] + fn duplicate_crash() { + let v = hex::decode_to_vec("abcd").unwrap(); + super::do_test(&v); } } diff --git a/fuzz/fuzz_targets/parse_descriptor_definite.rs b/fuzz/fuzz_targets/parse_descriptor_definite.rs index f6a43476c..d95f6b498 100644 --- a/fuzz/fuzz_targets/parse_descriptor_definite.rs +++ b/fuzz/fuzz_targets/parse_descriptor_definite.rs @@ -1,6 +1,7 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] -use honggfuzz::fuzz; use miniscript::{DefiniteDescriptorKey, Descriptor}; fn do_test(data: &[u8]) { @@ -12,10 +13,19 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } + +#[cfg(test)] +mod tests { + use miniscript::hex; + + #[test] + fn duplicate_crash() { + let v = hex::decode_to_vec("abcd").unwrap(); + super::do_test(&v); } } diff --git a/fuzz/fuzz_targets/parse_descriptor_priv.rs b/fuzz/fuzz_targets/parse_descriptor_priv.rs index 98bfb992c..6160bd199 100644 --- a/fuzz/fuzz_targets/parse_descriptor_priv.rs +++ b/fuzz/fuzz_targets/parse_descriptor_priv.rs @@ -1,6 +1,7 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] -use honggfuzz::fuzz; use miniscript::bitcoin::secp256k1; use miniscript::Descriptor; @@ -14,10 +15,19 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } + +#[cfg(test)] +mod tests { + use miniscript::hex; + + #[test] + fn duplicate_crash() { + let v = hex::decode_to_vec("abcd").unwrap(); + super::do_test(&v); } } diff --git a/fuzz/fuzz_targets/parse_descriptor_secret.rs b/fuzz/fuzz_targets/parse_descriptor_secret.rs index 76c4eb4fc..2f3827a97 100644 --- a/fuzz/fuzz_targets/parse_descriptor_secret.rs +++ b/fuzz/fuzz_targets/parse_descriptor_secret.rs @@ -1,8 +1,9 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] use std::str::FromStr; -use honggfuzz::fuzz; use miniscript::descriptor::DescriptorSecretKey; fn do_test(data: &[u8]) { @@ -13,10 +14,19 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } + +#[cfg(test)] +mod tests { + use miniscript::hex; + + #[test] + fn duplicate_crash() { + let v = hex::decode_to_vec("abcd").unwrap(); + super::do_test(&v); } } diff --git a/fuzz/fuzz_targets/regression_compiler.rs b/fuzz/fuzz_targets/regression_compiler.rs index 8703e7f3f..79154cd3d 100644 --- a/fuzz/fuzz_targets/regression_compiler.rs +++ b/fuzz/fuzz_targets/regression_compiler.rs @@ -1,5 +1,8 @@ +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] + use descriptor_fuzz::FuzzPk; -use honggfuzz::fuzz; use miniscript::{policy, ParseError, ParseNumError}; use old_miniscript::policy as old_policy; @@ -51,13 +54,11 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } #[cfg(test)] mod tests { diff --git a/fuzz/fuzz_targets/regression_descriptor_parse.rs b/fuzz/fuzz_targets/regression_descriptor_parse.rs index 379b01814..6d422b640 100644 --- a/fuzz/fuzz_targets/regression_descriptor_parse.rs +++ b/fuzz/fuzz_targets/regression_descriptor_parse.rs @@ -1,6 +1,9 @@ +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] + use core::str::FromStr; -use honggfuzz::fuzz; use miniscript::Descriptor; use old_miniscript::Descriptor as OldDescriptor; @@ -45,13 +48,11 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } #[cfg(test)] mod tests { diff --git a/fuzz/fuzz_targets/regression_taptree.rs b/fuzz/fuzz_targets/regression_taptree.rs index ab50d06bd..92aedbffd 100644 --- a/fuzz/fuzz_targets/regression_taptree.rs +++ b/fuzz/fuzz_targets/regression_taptree.rs @@ -1,7 +1,10 @@ +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] + use std::collections::{HashMap, HashSet}; use descriptor_fuzz::FuzzPk; -use honggfuzz::fuzz; use miniscript::descriptor::Tr; use old_miniscript::descriptor::Tr as OldTr; @@ -49,13 +52,11 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } #[cfg(test)] mod tests { diff --git a/fuzz/fuzz_targets/roundtrip_concrete.rs b/fuzz/fuzz_targets/roundtrip_concrete.rs index 698d2e908..bdaa6d962 100644 --- a/fuzz/fuzz_targets/roundtrip_concrete.rs +++ b/fuzz/fuzz_targets/roundtrip_concrete.rs @@ -1,8 +1,9 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] use std::str::FromStr; -use honggfuzz::fuzz; use miniscript::policy; use regex::Regex; @@ -20,13 +21,11 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } #[cfg(test)] mod tests { diff --git a/fuzz/fuzz_targets/roundtrip_descriptor.rs b/fuzz/fuzz_targets/roundtrip_descriptor.rs index de9232a62..7392a56d3 100644 --- a/fuzz/fuzz_targets/roundtrip_descriptor.rs +++ b/fuzz/fuzz_targets/roundtrip_descriptor.rs @@ -1,8 +1,9 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] use std::str::FromStr; -use honggfuzz::fuzz; use miniscript::Descriptor; fn do_test(data: &[u8]) { @@ -15,13 +16,11 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } #[cfg(test)] mod tests { diff --git a/fuzz/fuzz_targets/roundtrip_miniscript_script.rs b/fuzz/fuzz_targets/roundtrip_miniscript_script.rs index 8c21e66c6..ab2e290dc 100644 --- a/fuzz/fuzz_targets/roundtrip_miniscript_script.rs +++ b/fuzz/fuzz_targets/roundtrip_miniscript_script.rs @@ -1,6 +1,7 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] -use honggfuzz::fuzz; use miniscript::bitcoin::blockdata::script; use miniscript::{Miniscript, Segwitv0}; @@ -15,13 +16,11 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } #[cfg(test)] mod tests { diff --git a/fuzz/fuzz_targets/roundtrip_miniscript_script_tap.rs b/fuzz/fuzz_targets/roundtrip_miniscript_script_tap.rs index 7dd16fc9c..93f307405 100644 --- a/fuzz/fuzz_targets/roundtrip_miniscript_script_tap.rs +++ b/fuzz/fuzz_targets/roundtrip_miniscript_script_tap.rs @@ -1,6 +1,7 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] -use honggfuzz::fuzz; use miniscript::bitcoin::blockdata::script; use miniscript::{Miniscript, Tap}; @@ -15,10 +16,19 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } + +#[cfg(test)] +mod tests { + use miniscript::hex; + + #[test] + fn duplicate_crash() { + let v = hex::decode_to_vec("abcd").unwrap(); + super::do_test(&v); } } diff --git a/fuzz/fuzz_targets/roundtrip_miniscript_str.rs b/fuzz/fuzz_targets/roundtrip_miniscript_str.rs index 93bbdc5c0..a06ca9689 100644 --- a/fuzz/fuzz_targets/roundtrip_miniscript_str.rs +++ b/fuzz/fuzz_targets/roundtrip_miniscript_str.rs @@ -1,8 +1,9 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0 + +#![cfg_attr(fuzzing, no_main)] use std::str::FromStr; -use honggfuzz::fuzz; use miniscript::{Miniscript, Segwitv0, Tap}; fn do_test(data: &[u8]) { @@ -20,13 +21,11 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } #[cfg(test)] mod tests { diff --git a/fuzz/fuzz_targets/roundtrip_semantic.rs b/fuzz/fuzz_targets/roundtrip_semantic.rs index 668faa354..8d9aa626e 100644 --- a/fuzz/fuzz_targets/roundtrip_semantic.rs +++ b/fuzz/fuzz_targets/roundtrip_semantic.rs @@ -1,8 +1,9 @@ -#![allow(unexpected_cfgs)] +// SPDX-License-Identifier: CC0-1.0b + +#![cfg_attr(fuzzing, no_main)] use std::str::FromStr; -use honggfuzz::fuzz; use miniscript::policy; type Policy = policy::Semantic; @@ -15,10 +16,8 @@ fn do_test(data: &[u8]) { } } -fn main() { - loop { - fuzz!(|data| { - do_test(data); - }); - } -} +#[cfg(fuzzing)] +libfuzzer_sys::fuzz_target!(|data| { do_test(data); }); + +#[cfg(not(fuzzing))] +fn main() { do_test(&[]); } diff --git a/fuzz/generate-files.sh b/fuzz/generate-files.sh index d9ce4d408..fae88036d 100755 --- a/fuzz/generate-files.sh +++ b/fuzz/generate-files.sh @@ -22,13 +22,16 @@ publish = false cargo-fuzz = true [dependencies] -honggfuzz = { version = "0.5.56", default-features = false } +libfuzzer-sys = "0.4" # We shouldn't need an explicit version on the next line, but Andrew's tools # choke on it otherwise. See https://github.com/nix-community/crate2nix/issues/373 miniscript = { path = "..", features = [ "compiler" ], version = "13.0" } old_miniscript = { package = "miniscript", features = [ "compiler" ], version = "12.3" } regex = "1.0" + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } EOF for targetFile in $(listTargetFiles); do @@ -54,9 +57,21 @@ on: - cron: '00 06 * * *' jobs: + Prepare: + runs-on: ubuntu-24.04 + outputs: + nightly_version: \${{ steps.read_toolchain.outputs.nightly_version }} + steps: + - name: "Checkout repo" + uses: actions/checkout@v4 + - name: "Read nightly version" + id: read_toolchain + run: echo "nightly_version=\$(cat nightly-version)" >> \$GITHUB_OUTPUT + fuzz: if: \${{ !github.event.act }} - runs-on: ubuntu-20.04 + needs: Prepare + runs-on: ubuntu-25.04 strategy: fail-fast: false matrix: @@ -77,9 +92,15 @@ $(for name in $(listTargetNames); do echo "$name,"; done) fuzz/target target key: cache-\${{ matrix.target }}-\${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} + + - name: Install toolchain - uses: dtolnay/rust-toolchain@stable with: - toolchain: '1.65.0' + toolchain: \${{ needs.Prepare.outputs.nightly_version }} + + - name: Install Dependencies + run: cargo update && cargo update -p cc --precise 1.0.83 && cargo install --force cargo-fuzz + - name: fuzz run: cd fuzz && ./fuzz.sh "\${{ matrix.fuzz_target }}" - run: echo "\${{ matrix.fuzz_target }}" >executed_\${{ matrix.fuzz_target }} @@ -93,7 +114,7 @@ $(for name in $(listTargetNames); do echo "$name,"; done) needs: fuzz runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 - name: Display structure of downloaded files run: ls -R From 49427d43610d05a31ef4945cb2622f00ec5cb381 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 9 Sep 2025 15:01:23 +0000 Subject: [PATCH 6/6] fuzz: update README.md --- fuzz/README.md | 75 +++++++++++++++----------------------------------- 1 file changed, 22 insertions(+), 53 deletions(-) diff --git a/fuzz/README.md b/fuzz/README.md index df865391f..a58a994cb 100644 --- a/fuzz/README.md +++ b/fuzz/README.md @@ -1,29 +1,20 @@ # Fuzzing -`miniscript` has a fuzzing harness setup for use with honggfuzz. +`miniscript` has a fuzzing harness setup for use with cargo-fuzz. To run the fuzz-tests as in CI -- briefly fuzzing every target -- simply run - ./fuzz.sh + RUSTUP_TOOLCHAIN=nightly ./fuzz.sh in this directory. -To build honggfuzz, you must have libunwind on your system, as well as -libopcodes and libbfd from binutils **2.38** on your system. The most -recently-released binutils 2.39 has changed their API in a breaking way. +You need a nightly compiler to run the fuzz tests. You will also need +`cargo-fuzz` installed: -On Nix, you can obtain these libraries, and disable some hardening flags -which conflict with the way honggfuzz builds its targets, by running + cargo install --force cargo-fuzz - nix-shell -p libopcodes_2_38 -p libunwind - # In the nix-shell run these - NIX_HARDENING_ENABLE=''${NIX_HARDENING_ENABLE/fortify/} - NIX_HARDENING_ENABLE=''${NIX_HARDENING_ENABLE/fortify3/} - -and then run fuzz.sh as above. - -# Fuzzing with weak cryptography +## Fuzzing with weak cryptography You may wish to replace the hashing and signing code with broken crypto, which will be faster and enable the fuzzer to do otherwise impossible @@ -46,7 +37,7 @@ secp256k1 library with broken cryptography. Needless to say, NEVER COMPILE REAL CODE WITH THESE FLAGS because if a fuzzer can break your crypto, so can anybody. -# Long-term fuzzing +## Long-term fuzzing To see the full list of targets, the most straightforward way is to run @@ -59,22 +50,19 @@ To run each of them for an hour, run To run a single fuzztest indefinitely, run - cargo hfuzz run + cargo +nightly fuzz run `cycle.sh` 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. -# Adding fuzz tests +## Adding fuzz tests All fuzz tests can be found in the `fuzz_target/` directory. Adding a new one is as simple as copying an existing one and editing the `do_test` function to do what you want. -If your test clearly belongs to a specific crate, please put it in that -crate's directory. Otherwise you can put it directly in `fuzz_target/`. - If you need to add dependencies, edit the file `generate-files.sh` to add it to the generated `Cargo.toml`. @@ -91,43 +79,24 @@ If it is working, you will see a rapid stream of data for many seconds (you can hit Ctrl+C to stop it early). If not, you should quickly see an error. -# Reproducing Failures +## Computing code coverage -If a fuzztest fails, it will exit with a summary which looks something like +Compute the code coverage of the corpus of a given target using the following command: -``` -... - fuzzTarget : hfuzz_target/x86_64-unknown-linux-gnu/release/hashes_sha256 -CRASH: -DESCRIPTION: -ORIG_FNAME: 00000000000000000000000000000000.00000000.honggfuzz.cov -FUZZ_FNAME: hfuzz_workspace/hashes_sha256/SIGABRT.PC.7ffff7c8abc7.STACK.18826d9b64.CODE.-6.ADDR.0.INSTR.mov____%eax,%ebp.fuzz -... -===================================================================== -fff400610004 +```bash +cargo fuzz coverage TARGET ``` -The final line is a hex-encoded version of the input that caused the crash. You -can test this directly by editing the `duplicate_crash` test to copy/paste the -hex output into the call to `extend_vec_from_hex`. Then run the test with +Generate a human-readable HTML coverage report using a command as below. _The exact paths might differ depending on the target architecture._ - cargo test +The demangler `rustfilt` must be installed. -Note that if you set your `RUSTFLAGS` while fuzzing (see above) you must make -sure they are set the same way when running `cargo test`. +```bash +cargo cov -- show -Xdemangler=rustfilt target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/TARGET -instr-profile=fuzz/coverage/TARGET/coverage.profdata -show-line-counts-or-regions -show-instantiations --format html --output-dir=OUTPUT_DIR -ignore-filename-regex="\.cargo" +``` -If the `duplicate_crash` function is not present, please add it. A template is -as follows: +More information is available in the [rustc book](https://doc.rust-lang.org/stable/rustc/instrument-coverage.html#running-the-instrumented-binary-to-generate-raw-coverage-profiling-data). -``` -#[cfg(test)] -mod tests { - use miniscript::bitcoin::hex::FromHex; - - #[test] - fn duplicate_crash() { - let v = Vec::from_hex("abcd").unwrap(); - super::do_test(&v); - } -} -``` +## Reproducing and Minimizing Failures + +(todo -- wait for some failures to happen before filling in this section)