use crate::fuzz::Input;
pub struct Rng([u64; 4]);
fn splitmix64(z: &mut u64) -> u64 {
*z = z.wrapping_add(0x9e37_79b9_7f4a_7c15);
let mut x = *z;
x = (x ^ (x >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
x = (x ^ (x >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
x ^ (x >> 31)
}
impl Rng {
pub fn from_seed(seed: u64) -> Rng {
let mut z = seed;
Rng([
splitmix64(&mut z),
splitmix64(&mut z),
splitmix64(&mut z),
splitmix64(&mut z),
])
}
pub fn next_u64(&mut self) -> u64 {
let s = &mut self.0;
let out = s[1].wrapping_mul(5).rotate_left(7).wrapping_mul(9);
let t = s[1] << 17;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= t;
s[3] = s[3].rotate_left(45);
out
}
pub fn below(&mut self, n: u64) -> u64 {
if n == 0 { 0 } else { self.next_u64() % n }
}
pub fn pick<'a, T>(&mut self, xs: &'a [T]) -> Option<&'a T> {
xs.get(self.below(xs.len() as u64) as usize)
}
}
const INTERESTING_8: &[i8] = &[-128, -1, 0, 1, 16, 32, 64, 100, 127];
const INTERESTING_16: &[i16] = &[-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767];
const INTERESTING_32: &[i32] = &[
-2147483648,
-100663046,
-32769,
32768,
65535,
65536,
2147483647,
];
const ARITH_MAX: u64 = 35;
const HAVOC_STACK: u64 = 8;
pub fn havoc(input: &mut Input, rng: &mut Rng) {
let edits: &[fn(&mut Input, &mut Rng)] = &[
bit_flip,
byte_flip,
arith8,
arith16,
arith32,
interesting8,
interesting16,
interesting32,
block_dup,
block_del,
];
for _ in 0..=rng.below(HAVOC_STACK) {
if let Some(edit) = rng.pick(edits) {
edit(input, rng);
}
}
}
pub fn bit_flip(input: &mut Input, rng: &mut Rng) {
let bits = input.len() * 8;
if bits == 0 {
return;
}
let at = rng.below(bits);
let (byte, bit) = ((at / 8) as usize, at % 8);
input.as_mut_slice()[byte] ^= 1 << bit;
}
pub fn byte_flip(input: &mut Input, rng: &mut Rng) {
let Some(at) = offset(input, rng, 1) else {
return;
};
let v = rng.next_u64() as u8;
input.as_mut_slice()[at] ^= v;
}
pub fn arith8(input: &mut Input, rng: &mut Rng) {
let Some(at) = offset(input, rng, 1) else {
return;
};
let delta = delta(rng) as u8;
let buf = input.as_mut_slice();
buf[at] = buf[at].wrapping_add(delta);
}
pub fn arith16(input: &mut Input, rng: &mut Rng) {
let Some(at) = offset(input, rng, 2) else {
return;
};
let buf = input.as_mut_slice();
let old = u16::from_le_bytes([buf[at], buf[at + 1]]);
let new = old.wrapping_add(delta(rng) as u16);
buf[at..at + 2].copy_from_slice(&new.to_le_bytes());
}
pub fn arith32(input: &mut Input, rng: &mut Rng) {
let Some(at) = offset(input, rng, 4) else {
return;
};
let buf = input.as_mut_slice();
let old = u32::from_le_bytes([buf[at], buf[at + 1], buf[at + 2], buf[at + 3]]);
let new = old.wrapping_add(delta(rng) as u32);
buf[at..at + 4].copy_from_slice(&new.to_le_bytes());
}
pub fn interesting8(input: &mut Input, rng: &mut Rng) {
let (Some(at), Some(v)) = (offset(input, rng, 1), rng.pick(INTERESTING_8)) else {
return;
};
input.as_mut_slice()[at] = *v as u8;
}
pub fn interesting16(input: &mut Input, rng: &mut Rng) {
let (Some(at), Some(v)) = (offset(input, rng, 2), rng.pick(INTERESTING_16).copied()) else {
return;
};
input.as_mut_slice()[at..at + 2].copy_from_slice(&(v as u16).to_le_bytes());
}
pub fn interesting32(input: &mut Input, rng: &mut Rng) {
let (Some(at), Some(v)) = (offset(input, rng, 4), rng.pick(INTERESTING_32).copied()) else {
return;
};
input.as_mut_slice()[at..at + 4].copy_from_slice(&(v as u32).to_le_bytes());
}
pub fn block_dup(input: &mut Input, rng: &mut Rng) {
let len = input.len();
if len == 0 {
return;
}
let from = rng.below(len) as usize;
let n = (rng.below(len - from as u64) + 1) as usize;
let to = rng.below(len) as usize;
let block: Vec<u8> = input.as_slice()[from..from + n].to_vec();
let room = (input.cap() as usize).saturating_sub(to);
let n = n.min(room);
if to + n > len as usize {
input.set_len((to + n) as u64);
}
input.as_mut_slice()[to..to + n].copy_from_slice(&block[..n]);
}
pub fn block_del(input: &mut Input, rng: &mut Rng) {
let len = input.len() as usize;
if len < 2 {
return;
}
let at = rng.below(len as u64 - 1) as usize;
let n = (rng.below((len - at) as u64) + 1) as usize;
input.as_mut_slice().copy_within(at + n.., at);
input.set_len((len - n) as u64);
}
fn offset(input: &Input, rng: &mut Rng, width: u64) -> Option<usize> {
let len = input.len();
(len >= width).then(|| rng.below(len - width + 1) as usize)
}
fn delta(rng: &mut Rng) -> i64 {
let n = (rng.below(ARITH_MAX) + 1) as i64;
if rng.next_u64() & 1 == 0 { n } else { -n }
}