From 2c236ae24fbda928c35556a5961ef58aea5ecf4d Mon Sep 17 00:00:00 2001 From: Erick Cestari Date: Mon, 19 May 2025 14:38:32 -0300 Subject: [PATCH] fuzz: Add p2p address round-trip fuzzing test --- .github/workflows/cron-daily-fuzz.yml | 1 + fuzz/Cargo.toml | 4 + .../bitcoin/p2p_address_roundtrip.rs | 86 +++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 fuzz/fuzz_targets/bitcoin/p2p_address_roundtrip.rs diff --git a/.github/workflows/cron-daily-fuzz.yml b/.github/workflows/cron-daily-fuzz.yml index 253be2af2..d3ab431e1 100644 --- a/.github/workflows/cron-daily-fuzz.yml +++ b/.github/workflows/cron-daily-fuzz.yml @@ -27,6 +27,7 @@ jobs: bitcoin_deserialize_witness, bitcoin_deser_net_msg, bitcoin_outpoint_string, + bitcoin_p2p_address_roundtrip, bitcoin_script_bytes_to_asm_fmt, hashes_json, hashes_ripemd160, diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index f69af96f7..38adb8b8c 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -85,3 +85,7 @@ path = "fuzz_targets/hashes/sha512.rs" [[bin]] name = "units_deserialize_amount" path = "fuzz_targets/units/deserialize_amount.rs" + +[[bin]] +name = "bitcoin_p2p_address_roundtrip" +path = "fuzz_targets/bitcoin/p2p_address_roundtrip.rs" diff --git a/fuzz/fuzz_targets/bitcoin/p2p_address_roundtrip.rs b/fuzz/fuzz_targets/bitcoin/p2p_address_roundtrip.rs new file mode 100644 index 000000000..f5dd65a64 --- /dev/null +++ b/fuzz/fuzz_targets/bitcoin/p2p_address_roundtrip.rs @@ -0,0 +1,86 @@ +use std::convert::TryFrom; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; + +use bitcoin::consensus::Decodable; +use bitcoin::p2p::address::AddrV2; +use honggfuzz::fuzz; + +fn do_test(data: &[u8]) { + if data.len() < 2 { + return; + } + + let mut cursor = std::io::Cursor::new(data); + let addr_v2 = if let Ok(addr) = AddrV2::consensus_decode(&mut cursor) { + addr + } else { + return; + }; + + 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) = 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(socket_addr) = SocketAddr::try_from(addr_v2.clone()) { + let round_trip: AddrV2 = AddrV2::from(socket_addr); + assert_eq!( + addr_v2, round_trip, + "AddrV2 -> SocketAddr -> AddrV2 should round-trip correctly" + ); + } +} + +fn main() { + loop { + fuzz!(|data| { + do_test(data); + }); + } +} + +#[cfg(all(test, fuzzing))] +mod tests { + fn extend_vec_from_hex(hex: &str, out: &mut Vec) { + let mut b = 0; + for (idx, c) in hex.as_bytes().iter().enumerate() { + b <<= 4; + match *c { + b'A'..=b'F' => b |= c - b'A' + 10, + b'a'..=b'f' => b |= c - b'a' + 10, + b'0'..=b'9' => b |= c - b'0', + _ => panic!("Bad hex"), + } + if (idx & 1) == 1 { + out.push(b); + b = 0; + } + } + } + + #[test] + fn duplicate_crash() { + let mut a = Vec::new(); + extend_vec_from_hex("00", &mut a); + super::do_test(&a); + } +}