diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..1a45eee --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..7b6f937 --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "linked_list_allocator-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +arbitrary = { version = "1", features = ["derive"] } + +[dependencies.linked_list_allocator] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "chaos" +path = "fuzz_targets/chaos.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/chaos.rs b/fuzz/fuzz_targets/chaos.rs new file mode 100644 index 0000000..61a2d7d --- /dev/null +++ b/fuzz/fuzz_targets/chaos.rs @@ -0,0 +1,120 @@ +#![no_main] +use arbitrary::Arbitrary; +use libfuzzer_sys::fuzz_target; +use linked_list_allocator::Heap; +use std::alloc::Layout; +use std::ptr::NonNull; + +#[derive(Debug, Arbitrary)] +enum Action { + // allocate a chunk with the size specified + Alloc { size: u16, align_bit: u8 }, + // free the pointer at the index specified + Free { index: u8 }, + // extend the heap by amount specified + Extend { additional: u16 }, +} +use Action::*; + +const MAX_HEAP_SIZE: usize = 5000; +static mut HEAP_MEM: [u8; MAX_HEAP_SIZE] = [0; MAX_HEAP_SIZE]; +const DEBUG: bool = false; + +fuzz_target!(|data: (u16, Vec)| { + let (size, actions) = data; + let _ = fuzz(size, actions); +}); + +fn fuzz(size: u16, actions: Vec) { + // init heap + let mut heap = unsafe { + let size = size as usize; + if size > MAX_HEAP_SIZE || size < 3 * core::mem::size_of::() { + return; + } + + Heap::new(HEAP_MEM.as_mut_ptr(), size) + }; + let mut ptrs: Vec<(NonNull, Layout)> = Vec::new(); + + if DEBUG { + heap.debug(); + } + + // process operations + for action in actions { + if DEBUG { + println!("-----\nnext action: {:?}", action); + } + match action { + Alloc { size, align_bit } => { + let layout = { + let align = 1_usize.rotate_left(align_bit as u32); + if align == 1 << 63 { + return; + } + Layout::from_size_align(size as usize, align).unwrap() + }; + + if let Ok(ptr) = heap.allocate_first_fit(layout) { + if DEBUG { + println!("alloc'd {:?}", ptr); + } + ptrs.push((ptr, layout)); + } else { + return; + } + } + Free { index } => { + if index as usize >= ptrs.len() { + return; + } + + let (ptr, layout) = ptrs.swap_remove(index as usize); + if DEBUG { + println!("removing {:?}, size: {}", ptr, layout.size()); + } + unsafe { + heap.deallocate(ptr, layout); + } + } + Extend { additional } => + // safety: new heap size never exceeds MAX_HEAP_SIZE + unsafe { + let remaining_space = HEAP_MEM + .as_mut_ptr() + .add(MAX_HEAP_SIZE) + .offset_from(heap.top()); + assert!(remaining_space >= 0); + + if additional as isize > remaining_space { + return; + } + + heap.extend(additional as usize); + if DEBUG { + println!("new heap size: {}, top: {:?}", heap.size(), heap.top()); + } + }, + } + if DEBUG { + println!("after action:"); + print!("live allocs: "); + for ptr in &ptrs { + print!("({:?}, {},{}), ", ptr.0, ptr.1.size(), ptr.1.align()); + } + println!(); + heap.debug(); + } + } + + // free the remaining allocations + for (ptr, layout) in ptrs { + if DEBUG { + println!("removing {:?}, size: {}", ptr, layout.size()); + } + unsafe { + heap.deallocate(ptr, layout); + } + } +} diff --git a/src/hole.rs b/src/hole.rs index 5dca909..5722e0c 100644 --- a/src/hole.rs +++ b/src/hole.rs @@ -296,7 +296,7 @@ impl HoleList { } } - #[cfg(test)] + #[cfg(any(test, fuzzing))] #[allow(dead_code)] pub(crate) fn debug(&mut self) { if let Some(cursor) = self.cursor() { diff --git a/src/lib.rs b/src/lib.rs index f76e0e5..5c82a00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ )] #![no_std] -#[cfg(test)] +#[cfg(any(test, fuzzing))] #[macro_use] extern crate std; @@ -37,6 +37,20 @@ pub struct Heap { holes: HoleList, } +#[cfg(fuzzing)] +impl Heap { + pub fn debug(&mut self) { + println!( + "bottom: {:?}, top: {:?}, size: {}, pending: {}", + self.bottom(), + self.top(), + self.size(), + self.holes.first.size, + ); + self.holes.debug(); + } +} + unsafe impl Send for Heap {} impl Heap {