fuzz: Replace Unstructured with auto Arbitrary arg for do_test
What changed, and why it matters
This commit is a refactoring of internal fuzz-testing code only. It changes how test inputs are generated for automated fuzzing targets, switching from a manual byte-slice approach to a newer automatic approach supported by the fuzzing library. It does not change any production Bitcoin parsing, validation, or networking code, and it does not fix or introduce any security vulnerability.
No action required; this is a non-security test-infrastructure cleanup.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit updates five fuzz targets under fuzz/fuzz_targets/ to use libfuzzer-sys’s structure-aware fuzzing feature, where types implementing Arbitrary are passed directly to fuzz_target! instead of being constructed manually from an Unstructured byte slice. The functional behavior of each target is preserved: the same assertions, round-trip checks, and method calls remain. No production library code is modified.
Changed components
fuzz/fuzz_targets/bitcoin/arbitrary_block.rsfuzz/fuzz_targets/bitcoin/arbitrary_transaction.rsfuzz/fuzz_targets/bitcoin/arbitrary_witness.rsfuzz/fuzz_targets/bitcoin/deserialize_psbt.rsfuzz/fuzz_targets/p2p/arbitrary_addrv2.rsInspect captured patch +61 / −100
diff --git a/fuzz/fuzz_targets/bitcoin/arbitrary_block.rs b/fuzz/fuzz_targets/bitcoin/arbitrary_block.rs
index d8ff2783..0a54dfe6 100644
--- a/fuzz/fuzz_targets/bitcoin/arbitrary_block.rs
+++ b/fuzz/fuzz_targets/bitcoin/arbitrary_block.rs
@@ -1,7 +1,6 @@
#![cfg_attr(fuzzing, no_main)]
#![cfg_attr(not(fuzzing), allow(unused))]
-use arbitrary::{Arbitrary, Unstructured};
use bitcoin::block::{self, Block, BlockCheckedExt as _};
use bitcoin::consensus::{deserialize, serialize};
use libfuzzer_sys::fuzz_target;
@@ -9,31 +8,26 @@ use libfuzzer_sys::fuzz_target;
#[cfg(not(fuzzing))]
fn main() {}
-fn do_test(data: &[u8]) {
- let mut u = Unstructured::new(data);
- let b = Block::arbitrary(&mut u);
+fn do_test(block: Block) {
+ let serialized = serialize(&block);
- if let Ok(block) = b {
- let serialized = serialize(&block);
+ // Manually call all compute functions with unchecked block data.
+ let (header, transactions) = block.clone().into_parts();
+ block::compute_merkle_root(&transactions);
+ // Use 32-byte zero array as witness_reserved_value per BIP-0141 requirement.
+ block.compute_witness_commitment(&[0u8; 32]);
+ block::compute_witness_root(&transactions);
- // Manually call all compute functions with unchecked block data.
- let (header, transactions) = block.clone().into_parts();
- block::compute_merkle_root(&transactions);
- // Use 32-byte zero array as witness_reserved_value per BIP-0141 requirement.
- block.compute_witness_commitment(&[0u8; 32]);
- block::compute_witness_root(&transactions);
-
- if let Ok(block) = Block::new_checked(header, transactions) {
- let _ = block.bip34_block_height();
- block.block_hash();
- block.weight();
- }
-
- let deserialized: Result<Block, _> = deserialize(serialized.as_slice());
- assert_eq!(deserialized.unwrap(), block);
+ if let Ok(block) = Block::new_checked(header, transactions) {
+ let _ = block.bip34_block_height();
+ block.block_hash();
+ block.weight();
}
+
+ let deserialized: Result<Block, _> = deserialize(serialized.as_slice());
+ assert_eq!(deserialized.unwrap(), block);
}
-fuzz_target!(|data| {
+fuzz_target!(|data: Block| {
do_test(data);
});
diff --git a/fuzz/fuzz_targets/bitcoin/arbitrary_transaction.rs b/fuzz/fuzz_targets/bitcoin/arbitrary_transaction.rs
index 38de8655..65d9c258 100644
--- a/fuzz/fuzz_targets/bitcoin/arbitrary_transaction.rs
+++ b/fuzz/fuzz_targets/bitcoin/arbitrary_transaction.rs
@@ -1,7 +1,6 @@
#![cfg_attr(fuzzing, no_main)]
#![cfg_attr(not(fuzzing), allow(unused))]
-use arbitrary::{Arbitrary, Unstructured};
use bitcoin::consensus::{deserialize, serialize};
use bitcoin::transaction::TransactionExt as _;
use bitcoin::Transaction;
@@ -10,33 +9,28 @@ use libfuzzer_sys::fuzz_target;
#[cfg(not(fuzzing))]
fn main() {}
-fn do_test(data: &[u8]) {
- let mut u = Unstructured::new(data);
- let t = Transaction::arbitrary(&mut u);
+fn do_test(mut tx: Transaction) {
+ let serialized = serialize(&tx);
+ let deserialized: Result<Transaction, _> = deserialize(serialized.as_slice());
+ assert_eq!(deserialized.unwrap(), tx);
- if let Ok(mut tx) = t {
- let serialized = serialize(&tx);
- let deserialized: Result<Transaction, _> = deserialize(serialized.as_slice());
- assert_eq!(deserialized.unwrap(), tx);
-
- let len = serialized.len();
- let calculated_weight = tx.weight().to_wu() as usize;
- for input in &mut tx.inputs {
- input.witness = bitcoin::witness::Witness::default();
- }
- let no_witness_len = bitcoin::consensus::encode::serialize(&tx).len();
- // For 0-input transactions, `no_witness_len` will be incorrect because
- // we serialize as SegWit even after "stripping the witnesses". We need
- // to drop two bytes (i.e. eight weight). Similarly, calculated_weight is
- // incorrect and needs 2 wu removing for the marker/flag bytes.
- if tx.inputs.is_empty() {
- assert_eq!(no_witness_len * 3 + len - 8, calculated_weight - 2);
- } else {
- assert_eq!(no_witness_len * 3 + len, calculated_weight);
- }
+ let len = serialized.len();
+ let calculated_weight = tx.weight().to_wu() as usize;
+ for input in &mut tx.inputs {
+ input.witness = bitcoin::witness::Witness::default();
+ }
+ let no_witness_len = bitcoin::consensus::encode::serialize(&tx).len();
+ // For 0-input transactions, `no_witness_len` will be incorrect because
+ // we serialize as SegWit even after "stripping the witnesses". We need
+ // to drop two bytes (i.e. eight weight). Similarly, calculated_weight is
+ // incorrect and needs 2 wu removing for the marker/flag bytes.
+ if tx.inputs.is_empty() {
+ assert_eq!(no_witness_len * 3 + len - 8, calculated_weight - 2);
+ } else {
+ assert_eq!(no_witness_len * 3 + len, calculated_weight);
}
}
-fuzz_target!(|data| {
+fuzz_target!(|data: Transaction| {
do_test(data);
});
diff --git a/fuzz/fuzz_targets/bitcoin/arbitrary_witness.rs b/fuzz/fuzz_targets/bitcoin/arbitrary_witness.rs
index 155c254b..3b337774 100644
--- a/fuzz/fuzz_targets/bitcoin/arbitrary_witness.rs
+++ b/fuzz/fuzz_targets/bitcoin/arbitrary_witness.rs
@@ -1,7 +1,6 @@
#![cfg_attr(fuzzing, no_main)]
#![cfg_attr(not(fuzzing), allow(unused))]
-use arbitrary::{Arbitrary, Unstructured};
use bitcoin::blockdata::witness::WitnessExt;
use bitcoin::consensus::{deserialize, serialize};
use bitcoin::Witness;
@@ -10,24 +9,21 @@ use libfuzzer_sys::fuzz_target;
#[cfg(not(fuzzing))]
fn main() {}
-fn do_test(data: &[u8]) {
- let mut u = Unstructured::new(data);
+fn do_test(data: (Witness, Vec<u8>)) {
+ let mut witness = data.0;
+ let element_bytes = data.1;
- if let Ok(mut witness) = Witness::arbitrary(&mut u) {
- let serialized = serialize(&witness);
+ let serialized = serialize(&witness);
- let _ = witness.witness_script();
- let _ = witness.taproot_leaf_script();
+ let _ = witness.witness_script();
+ let _ = witness.taproot_leaf_script();
- let deserialized: Result<Witness, _> = deserialize(serialized.as_slice());
- assert_eq!(deserialized.unwrap(), witness);
+ let deserialized: Result<Witness, _> = deserialize(serialized.as_slice());
+ assert_eq!(deserialized.unwrap(), witness);
- if let Ok(element_bytes) = Vec::<u8>::arbitrary(&mut u) {
- witness.push(element_bytes.as_slice());
- }
- }
+ witness.push(element_bytes.as_slice());
}
-fuzz_target!(|data| {
+fuzz_target!(|data: (Witness, Vec<u8>)| {
do_test(data);
});
diff --git a/fuzz/fuzz_targets/bitcoin/deserialize_psbt.rs b/fuzz/fuzz_targets/bitcoin/deserialize_psbt.rs
index 21a00a5e..b9d783eb 100644
--- a/fuzz/fuzz_targets/bitcoin/deserialize_psbt.rs
+++ b/fuzz/fuzz_targets/bitcoin/deserialize_psbt.rs
@@ -1,21 +1,13 @@
#![cfg_attr(fuzzing, no_main)]
#![cfg_attr(not(fuzzing), allow(unused))]
-use arbitrary::{Arbitrary, Unstructured};
use libfuzzer_sys::fuzz_target;
#[cfg(not(fuzzing))]
fn main() {}
-fn do_test(data: &[u8]) {
- let mut unstructured = Unstructured::new(data);
-
- let Ok(bytes_a) = <&[u8]>::arbitrary(&mut unstructured) else {
- return;
- };
- let Ok(bytes_b) = <&[u8]>::arbitrary(&mut unstructured) else {
- return;
- };
+fn do_test(data: (&[u8], &[u8])) {
+ let (bytes_a, bytes_b) = data;
let Ok(psbt_a) = bitcoin::psbt::Psbt::deserialize(bytes_a) else {
return;
@@ -33,6 +25,6 @@ fn do_test(data: &[u8]) {
assert_eq!(psbt_b.combine(psbt_a).is_ok(), psbt_a_clone.combine(psbt_b).is_ok());
}
-fuzz_target!(|data| {
+fuzz_target!(|data: (&[u8], &[u8])| {
do_test(data);
});
diff --git a/fuzz/fuzz_targets/p2p/arbitrary_addrv2.rs b/fuzz/fuzz_targets/p2p/arbitrary_addrv2.rs
index 8f9e79ed..3ebd82d3 100644
--- a/fuzz/fuzz_targets/p2p/arbitrary_addrv2.rs
+++ b/fuzz/fuzz_targets/p2p/arbitrary_addrv2.rs
@@ -4,44 +4,29 @@
use std::convert::TryFrom;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
-use arbitrary::{Arbitrary, Unstructured};
use libfuzzer_sys::fuzz_target;
use p2p::address::AddrV2;
#[cfg(not(fuzzing))]
fn main() {}
-fn do_test(data: &[u8]) {
- let mut u = Unstructured::new(data);
- let a = AddrV2::arbitrary(&mut u);
-
- if let Ok(addr_v2) = a {
- if let Ok(ip_addr) = IpAddr::try_from(addr_v2.clone()) {
- let round_trip: AddrV2 = AddrV2::from(ip_addr);
- assert_eq!(
- addr_v2, round_trip,
- "AddrV2 -> IpAddr -> AddrV2 should round-trip correctly"
- );
- }
+fn do_test(addr_v2: AddrV2) {
+ if let Ok(ip_addr) = IpAddr::try_from(addr_v2.clone()) {
+ let round_trip: AddrV2 = AddrV2::from(ip_addr);
+ assert_eq!(addr_v2, round_trip, "AddrV2 -> IpAddr -> AddrV2 should round-trip correctly");
+ }
- if let Ok(ip_addr) = Ipv4Addr::try_from(addr_v2.clone()) {
- let round_trip: AddrV2 = AddrV2::from(ip_addr);
- assert_eq!(
- addr_v2, round_trip,
- "AddrV2 -> Ipv4Addr -> AddrV2 should round-trip correctly"
- );
- }
+ if let Ok(ip_addr) = Ipv4Addr::try_from(addr_v2.clone()) {
+ let round_trip: AddrV2 = AddrV2::from(ip_addr);
+ assert_eq!(addr_v2, round_trip, "AddrV2 -> Ipv4Addr -> AddrV2 should round-trip correctly");
+ }
- if let Ok(ip_addr) = Ipv6Addr::try_from(addr_v2.clone()) {
- let round_trip: AddrV2 = AddrV2::from(ip_addr);
- assert_eq!(
- addr_v2, round_trip,
- "AddrV2 -> Ipv6Addr -> AddrV2 should round-trip correctly"
- );
- }
+ if let Ok(ip_addr) = Ipv6Addr::try_from(addr_v2.clone()) {
+ let round_trip: AddrV2 = AddrV2::from(ip_addr);
+ assert_eq!(addr_v2, round_trip, "AddrV2 -> Ipv6Addr -> AddrV2 should round-trip correctly");
}
}
-fuzz_target!(|data| {
+fuzz_target!(|data: AddrV2| {
do_test(data);
});
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.