1use rand::{rngs::SmallRng, Rng, SeedableRng};
2use rand_distr::{Distribution, Exp};
3
4#[derive(Debug, Clone, Deserialize)]
5pub struct FuzzerConf {
6 pub rng_seed: u64,
7 pub max_extend_size: usize,
8 pub pnet_mutation_rate: Option<u32>,
10 pub noise_mutation_rate: Option<u32>,
11 pub select_authentication_mutation_rate: Option<u32>,
12 pub select_multiplexing_mutation_rate: Option<u32>,
13 pub select_stream_mutation_rate: Option<u32>,
14 pub yamux_frame_mutation_rate: Option<u32>,
15 pub yamux_flags_mutation_rate: Option<u32>,
16 pub identify_msg_mutation_rate: Option<u32>,
17 pub kad_data_mutation_rate: Option<u32>,
18 pub rpc_data_mutation_rate: Option<u32>,
19 pub pubsub_mutation_rate: Option<u32>,
20}
21
22#[derive(Debug, Clone)]
23pub struct FuzzerState {
24 pub rng: SmallRng,
25 pub conf: FuzzerConf,
26}
27
28#[derive(Debug)]
29pub enum MutationStrategy {
30 FlipBits = 0,
31 ExtendRandom = 1,
32 ExtendCopy = 2,
33 Shrink = 3,
34}
35
36impl MutationStrategy {
37 pub fn range() -> std::ops::RangeInclusive<u32> {
38 MutationStrategy::FlipBits as u32..=MutationStrategy::Shrink as u32
39 }
40}
41
42impl From<u32> for MutationStrategy {
43 fn from(item: u32) -> Self {
44 match item {
45 0 => MutationStrategy::FlipBits,
46 1 => MutationStrategy::ExtendRandom,
47 2 => MutationStrategy::ExtendCopy,
48 3 => MutationStrategy::Shrink,
49 _ => unreachable!(),
50 }
51 }
52}
53
54impl FuzzerState {
55 pub fn gen_ratio(&mut self, numerator: Option<u32>) -> bool {
56 if let Some(numerator) = numerator {
57 self.rng.gen_ratio(numerator, 1000)
58 } else {
59 false
60 }
61 }
62
63 pub fn gen_flips(&mut self, max_size: usize) -> usize {
64 let exp = Exp::new(2.0).unwrap();
66 let v = exp.sample(&mut self.rng);
67
68 min(max(1, (v * 10.0) as usize), max_size)
69 }
70
71 pub fn flip_bits(&mut self, data: &mut u8) {
72 let num_bit_flips = self.gen_flips(8);
73
74 for _ in 0..num_bit_flips {
75 *data ^= 1u8 << self.rng.gen_range(0..8);
76 }
77 }
78
79 pub fn flip_bytes(&mut self, data: &mut [u8]) {
80 let max_size = data.len();
81 let num_byte_flips = self.gen_flips(max_size);
82
83 for _ in 0..num_byte_flips {
84 let rnd_index = self.rng.gen_range(0..max_size);
85
86 self.flip_bits(&mut data[rnd_index]);
87 }
88 }
89
90 pub fn extend_random(&mut self, data: &[u8]) -> Vec<u8> {
91 let extend_size = self.rng.gen_range(1..self.conf.max_extend_size);
92 let random_pos = self.rng.gen_range(0..=data.len());
93
94 let mut random_bytes = vec![0u8; extend_size];
95 self.rng.fill(&mut random_bytes[..]);
96
97 let mut result = Vec::with_capacity(data.len() + extend_size);
98
99 result.extend(&data[0..random_pos]);
100 result.extend(random_bytes);
101 result.extend(&data[random_pos..]);
102 result
103 }
104
105 pub fn extend_copy(&mut self, data: &[u8]) -> Vec<u8> {
106 let random_pos = self.rng.gen_range(0..=data.len());
107 let copy_pos = self.rng.gen_range(0..=data.len() - 1);
108 let copy_size = self.rng.gen_range(1..=data.len() - copy_pos);
109 let mut extend_size = self.rng.gen_range(copy_size..self.conf.max_extend_size);
110 let mut result = Vec::with_capacity(data.len() + extend_size);
111
112 result.extend(&data[0..random_pos]);
113
114 while extend_size > copy_size {
115 result.extend(&data[copy_pos..copy_pos + copy_size]);
116 extend_size -= copy_size;
118 }
119
120 result.extend(&data[random_pos..]);
121 result
122 }
123
124 pub fn shrink(&mut self, data: &[u8]) -> Vec<u8> {
125 let pos = self.rng.gen_range(0..=data.len() - 1);
126 let size = self.rng.gen_range(1..=data.len() - pos);
127 let mut result = data[..pos].to_vec();
128
129 result.extend_from_slice(&data[pos + size..]);
130 result
131 }
132}
133
134use lazy_static::lazy_static;
135use serde::Deserialize;
136use std::{
137 cmp::{max, min},
138 env,
139 fs::File,
140 sync::Mutex,
141};
142
143lazy_static! {
144 pub static ref FUZZER: Option<Mutex<FuzzerState>> = {
145 if let Ok(conf) = env::var("FUZZER_CONF") {
146 let file = File::open(conf).expect("Failed to open fuzzer conf file");
147 let conf: FuzzerConf =
148 serde_json::from_reader(file).expect("Failed to parse fuzzer configuration");
149
150 println!("[+] FUZZER conf: {:?}", conf);
151
152 Some(Mutex::new(FuzzerState {
153 rng: SmallRng::seed_from_u64(conf.rng_seed),
154 conf,
155 }))
156 } else {
157 println!("[-] no FUZZER conf specified");
158 None
159 }
160 };
161}
162
163#[macro_export]
164macro_rules! fuzz {
165 ($expr:expr, $mutator:expr) => {
166 if let Some(fuzzer) = $crate::FUZZER.as_ref() {
167 let mut fuzzer = fuzzer.lock().expect("can't lock FUZZER");
168 $mutator(&mut fuzzer, $expr);
169 }
170 };
171}
172
173#[macro_export]
174macro_rules! fuzzed {
175 ($expr:expr, $mutator:expr) => {
176 if let Some(fuzzer) = $crate::FUZZER.as_ref() {
177 let mut fuzzer = fuzzer.lock().expect("can't lock FUZZER");
178 let mut fuzzed = $expr;
179 $mutator(&mut fuzzer, &mut fuzzed);
180 fuzzed
181 } else {
182 $expr
183 }
184 };
185}