Skip to content

Commit

Permalink
Merge pull request nervosnetwork#4468 from joii2020/dev-fuzz-network
Browse files Browse the repository at this point in the history
Add fuzz on network
  • Loading branch information
zhangsoledad authored Jun 13, 2024
2 parents 5872af1 + cdc1c2d commit 87e8bca
Show file tree
Hide file tree
Showing 10 changed files with 443 additions and 0 deletions.
2 changes: 2 additions & 0 deletions network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
description = "ckb network implementation"
homepage = "https://github.com/nervosnetwork/ckb"
repository = "https://github.com/nervosnetwork/ckb"
exclude = [ "fuzz" ]

[dependencies]
rand = "0.7"
Expand Down Expand Up @@ -41,6 +42,7 @@ p2p = { version="0.4.0", package="tentacle", features = ["upnp", "parking_lot",
[features]
with_sentry = ["sentry"]
with_dns_seeding = ["lazy_static", "bs58", "faster-hex", "trust-dns-resolver", "secp256k1"]
fuzz = []

[dev-dependencies]
tempfile.workspace = true
Expand Down
4 changes: 4 additions & 0 deletions network/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
51 changes: 51 additions & 0 deletions network/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[package]
name = "ckb-network-fuzz"
version = "0.117.0-pre"
publish = false
edition = "2021"
license = "MIT"
description = "ckb network fuzz testing"
homepage = "https://github.com/nervosnetwork/ckb"
repository = "https://github.com/nervosnetwork/ckb"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"
ipnetwork = "0.18"

[dependencies.ckb-network]
path = ".."
features = ["fuzz"]

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[profile.release]
debug = 1

[[bin]]
name = "fuzz_compress"
path = "fuzz_targets/fuzz_compress.rs"
test = false
doc = false

[[bin]]
name = "fuzz_decompress"
path = "fuzz_targets/fuzz_decompress.rs"
test = false
doc = false

[[bin]]
name = "fuzz_addr_manager"
path = "fuzz_targets/fuzz_addr_manager.rs"
test = false
doc = false

[[bin]]
name = "fuzz_peer_store"
path = "fuzz_targets/fuzz_peer_store.rs"
test = false
doc = false
98 changes: 98 additions & 0 deletions network/fuzz/fuzz_targets/fuzz_addr_manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#![no_main]

use libfuzzer_sys::fuzz_target;

use ckb_network::{
peer_store::{addr_manager::AddrManager, types::AddrInfo},
PeerId,
};

use std::collections::HashSet;
use std::net::Ipv4Addr;

const ADDR_SIZE: usize = 4 + 32; // IPv4+Port+SHA256

fn new_addr(data: &[u8], index: usize) -> AddrInfo {
let pos = 8 + index * ADDR_SIZE;
let data = data[pos..pos + ADDR_SIZE].to_vec();

let ip = Ipv4Addr::from(u32::from_le_bytes(data[0..4].try_into().unwrap()));
// let ip = Ipv4Addr::from(((225 << 24) + index) as u32);
// let port = u16::from_le_bytes(data[4..6].try_into().unwrap());
let peer_id =
PeerId::from_bytes(vec![vec![0x12], vec![0x20], data[4..].to_vec()].concat()).unwrap();

AddrInfo::new(
format!("/ip4/{}/tcp/43/p2p/{}", ip, peer_id.to_base58())
.parse()
.unwrap(),
0,
0,
0,
)
}

fn test_remove(
mut addr_manager: AddrManager,
basic: &HashSet<AddrInfo>,
rm_num: usize,
) -> AddrManager {
let removed = addr_manager.fetch_random(rm_num, |_| true);
// assert_eq!(removed.len(), rm_num.min(basic.len()));
assert!(removed.len() <= rm_num.min(basic.len()));

for addr in &removed {
addr_manager.remove(&addr.addr);
}
assert!(addr_manager.count() <= (basic.len() - removed.len()));
for addr in removed {
addr_manager.add(addr);
}
assert!(addr_manager.count() <= basic.len());

addr_manager
}

fuzz_target!(|data: &[u8]| {
if data.len() < 8 + ADDR_SIZE {
return;
}
if (data.len() - 8) % ADDR_SIZE != 0 {
return;
}
let scale: u16 = u16::from_le_bytes(data[0..2].try_into().unwrap()) % 100;
let (basic_len, added_len) = {
let t = (data.len() - 8) / ADDR_SIZE;

let b = (t as f32 / 100.0 * scale as f32) as usize;
(b, t - b)
};
let mut addr_manager = AddrManager::default();

let mut basic = HashSet::new();
for i in 0..basic_len {
let addr = new_addr(data, i);
basic.insert(addr.clone());
addr_manager.add(addr);
}
assert!(basic.len() >= addr_manager.count());

let removed_num1 =
u16::from_le_bytes(data[2..4].try_into().unwrap()) as usize % (basic.len() + 8);
let mut addr_manager = test_remove(addr_manager, &basic, removed_num1);

let mut added = Vec::new();
for i in 0..added_len {
let addr = new_addr(data, i + basic_len);
added.push(addr.clone());

addr_manager.add(addr.clone());
basic.insert(addr);
}
assert!(basic.len() >= addr_manager.count());

let removed_num2 =
u16::from_le_bytes(data[4..6].try_into().unwrap()) as usize % (basic.len() + 4);

let mut _addr_manager = test_remove(addr_manager, &basic, removed_num2);
});
14 changes: 14 additions & 0 deletions network/fuzz/fuzz_targets/fuzz_compress.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![no_main]

use libfuzzer_sys::fuzz_target;

use ckb_network::bytes::{Bytes, BytesMut};
use ckb_network::compress::{compress, decompress};

fuzz_target!(|data: &[u8]| {
let raw_data = Bytes::from(data.to_vec());

let cmp_data = compress(raw_data.clone());
let demsg = decompress(BytesMut::from(cmp_data.as_ref())).unwrap();
assert_eq!(raw_data, demsg);
});
11 changes: 11 additions & 0 deletions network/fuzz/fuzz_targets/fuzz_decompress.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![no_main]

use libfuzzer_sys::fuzz_target;

use ckb_network::bytes::{Bytes, BytesMut};
use ckb_network::compress::decompress;

fuzz_target!(|data: &[u8]| {
let raw_data = Bytes::from(data.to_vec());
let _demsg = decompress(BytesMut::from(raw_data.as_ref()));
});
99 changes: 99 additions & 0 deletions network/fuzz/fuzz_targets/fuzz_peer_store.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#![no_main]

use libfuzzer_sys::fuzz_target;

use ckb_network::{
multiaddr::MultiAddr, peer_store::types::BannedAddr, peer_store::PeerStore, Flags, PeerId,
};
use ckb_network_fuzz::BufManager;

fn new_multi_addr(data: &mut BufManager) -> (MultiAddr, Flags) {
let flags = data.get();
let addr_flag = data.get::<u8>();

let mut addr_str = if addr_flag & 0b1 != 1 {
let buf = data.get_buf(16);
format!(
"/ip6/{}",
std::net::Ipv6Addr::from(u128::from_le_bytes(buf.try_into().unwrap())).to_string()
)
} else {
format!("/ip4/{}", data.get::<std::net::Ipv4Addr>().to_string())
};

addr_str += &format!("/tcp/{}", data.get::<u16>());

addr_str += &format!("/p2p/{}", data.get::<ckb_network::PeerId>().to_base58());

(addr_str.parse().unwrap(), flags)
}

fn add_ban_addr(data: &mut BufManager, peer_store: &mut PeerStore) {
let num = data.get::<u8>() as usize;
for _ in 0..num {
let flags = data.get::<u8>();

let network = if flags & 0b1 == 1 {
data.get::<ipnetwork::Ipv4Network>().into()
} else {
data.get::<ipnetwork::Ipv6Network>().into()
};

let ban_addr = BannedAddr {
address: network,
ban_until: data.get(),
created_at: data.get(),
ban_reason: String::new(),
};
peer_store.mut_ban_list().ban(ban_addr);
}
}

fn add_basic_addr(data: &mut BufManager, peer_store: &mut PeerStore) {
let flags = data.get::<u32>();
if flags & 0b1 == 0 {
return;
}

if (flags >> 1) & 0b1 == 1 {
add_ban_addr(data, peer_store);
}

let basic_num = data.get::<u32>();

let num = basic_num % 16 + (16384) - 8; // ±8

for i in 0..num {
let addr = format!(
"/ip4/{}/tcp/43/p2p/{}",
std::net::Ipv4Addr::from(i as u32).to_string(),
PeerId::random().to_base58()
)
.parse()
.unwrap();
let _ = peer_store.add_addr_fuzz(addr, Flags::all(), data.get(), data.get());
}
}

fuzz_target!(|data: &[u8]| {
let mut data = BufManager::new(data);

let mut peer_store: PeerStore = Default::default();

// basic addr:
add_basic_addr(&mut data, &mut peer_store);

let fetch_count = data.get::<u16>() as usize;
let fetch_flag = data.get();

while !data.is_end() {
let (addr, flag) = new_multi_addr(&mut data);
let last_connected_time = data.get();
let attempts_count = data.get::<u32>();
let _res = peer_store.add_addr_fuzz(addr, flag, last_connected_time, attempts_count);
// _res.expect("msg");
}

let ret = peer_store.fetch_random_addrs(fetch_count, fetch_flag);
assert!(ret.len() <= fetch_count);
});
1 change: 1 addition & 0 deletions network/fuzz/rust-toolchain
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nightly
Loading

0 comments on commit 87e8bca

Please sign in to comment.