Skip to content

Commit aa7d9f6

Browse files
authored
Merge pull request #69 from evanrichter/fuzz-harness
random action fuzzer
2 parents 9e5878a + 0c116a4 commit aa7d9f6

File tree

5 files changed

+168
-2
lines changed

5 files changed

+168
-2
lines changed

fuzz/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
target
2+
corpus
3+
artifacts
4+
coverage

fuzz/Cargo.toml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
name = "linked_list_allocator-fuzz"
3+
version = "0.0.0"
4+
publish = false
5+
edition = "2021"
6+
7+
[package.metadata]
8+
cargo-fuzz = true
9+
10+
[dependencies]
11+
libfuzzer-sys = "0.4"
12+
arbitrary = { version = "1", features = ["derive"] }
13+
14+
[dependencies.linked_list_allocator]
15+
path = ".."
16+
17+
# Prevent this from interfering with workspaces
18+
[workspace]
19+
members = ["."]
20+
21+
[profile.release]
22+
debug = 1
23+
24+
[[bin]]
25+
name = "chaos"
26+
path = "fuzz_targets/chaos.rs"
27+
test = false
28+
doc = false

fuzz/fuzz_targets/chaos.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#![no_main]
2+
use arbitrary::Arbitrary;
3+
use libfuzzer_sys::fuzz_target;
4+
use linked_list_allocator::Heap;
5+
use std::alloc::Layout;
6+
use std::ptr::NonNull;
7+
8+
#[derive(Debug, Arbitrary)]
9+
enum Action {
10+
// allocate a chunk with the size specified
11+
Alloc { size: u16, align_bit: u8 },
12+
// free the pointer at the index specified
13+
Free { index: u8 },
14+
// extend the heap by amount specified
15+
Extend { additional: u16 },
16+
}
17+
use Action::*;
18+
19+
const MAX_HEAP_SIZE: usize = 5000;
20+
static mut HEAP_MEM: [u8; MAX_HEAP_SIZE] = [0; MAX_HEAP_SIZE];
21+
const DEBUG: bool = false;
22+
23+
fuzz_target!(|data: (u16, Vec<Action>)| {
24+
let (size, actions) = data;
25+
let _ = fuzz(size, actions);
26+
});
27+
28+
fn fuzz(size: u16, actions: Vec<Action>) {
29+
// init heap
30+
let mut heap = unsafe {
31+
let size = size as usize;
32+
if size > MAX_HEAP_SIZE || size < 3 * core::mem::size_of::<usize>() {
33+
return;
34+
}
35+
36+
Heap::new(HEAP_MEM.as_mut_ptr(), size)
37+
};
38+
let mut ptrs: Vec<(NonNull<u8>, Layout)> = Vec::new();
39+
40+
if DEBUG {
41+
heap.debug();
42+
}
43+
44+
// process operations
45+
for action in actions {
46+
if DEBUG {
47+
println!("-----\nnext action: {:?}", action);
48+
}
49+
match action {
50+
Alloc { size, align_bit } => {
51+
let layout = {
52+
let align = 1_usize.rotate_left(align_bit as u32);
53+
if align == 1 << 63 {
54+
return;
55+
}
56+
Layout::from_size_align(size as usize, align).unwrap()
57+
};
58+
59+
if let Ok(ptr) = heap.allocate_first_fit(layout) {
60+
if DEBUG {
61+
println!("alloc'd {:?}", ptr);
62+
}
63+
ptrs.push((ptr, layout));
64+
} else {
65+
return;
66+
}
67+
}
68+
Free { index } => {
69+
if index as usize >= ptrs.len() {
70+
return;
71+
}
72+
73+
let (ptr, layout) = ptrs.swap_remove(index as usize);
74+
if DEBUG {
75+
println!("removing {:?}, size: {}", ptr, layout.size());
76+
}
77+
unsafe {
78+
heap.deallocate(ptr, layout);
79+
}
80+
}
81+
Extend { additional } =>
82+
// safety: new heap size never exceeds MAX_HEAP_SIZE
83+
unsafe {
84+
let remaining_space = HEAP_MEM
85+
.as_mut_ptr()
86+
.add(MAX_HEAP_SIZE)
87+
.offset_from(heap.top());
88+
assert!(remaining_space >= 0);
89+
90+
if additional as isize > remaining_space {
91+
return;
92+
}
93+
94+
heap.extend(additional as usize);
95+
if DEBUG {
96+
println!("new heap size: {}, top: {:?}", heap.size(), heap.top());
97+
}
98+
},
99+
}
100+
if DEBUG {
101+
println!("after action:");
102+
print!("live allocs: ");
103+
for ptr in &ptrs {
104+
print!("({:?}, {},{}), ", ptr.0, ptr.1.size(), ptr.1.align());
105+
}
106+
println!();
107+
heap.debug();
108+
}
109+
}
110+
111+
// free the remaining allocations
112+
for (ptr, layout) in ptrs {
113+
if DEBUG {
114+
println!("removing {:?}, size: {}", ptr, layout.size());
115+
}
116+
unsafe {
117+
heap.deallocate(ptr, layout);
118+
}
119+
}
120+
}

src/hole.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ impl HoleList {
296296
}
297297
}
298298

299-
#[cfg(test)]
299+
#[cfg(any(test, fuzzing))]
300300
#[allow(dead_code)]
301301
pub(crate) fn debug(&mut self) {
302302
if let Some(cursor) = self.cursor() {

src/lib.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
)]
66
#![no_std]
77

8-
#[cfg(test)]
8+
#[cfg(any(test, fuzzing))]
99
#[macro_use]
1010
extern crate std;
1111

@@ -37,6 +37,20 @@ pub struct Heap {
3737
holes: HoleList,
3838
}
3939

40+
#[cfg(fuzzing)]
41+
impl Heap {
42+
pub fn debug(&mut self) {
43+
println!(
44+
"bottom: {:?}, top: {:?}, size: {}, pending: {}",
45+
self.bottom(),
46+
self.top(),
47+
self.size(),
48+
self.holes.first.size,
49+
);
50+
self.holes.debug();
51+
}
52+
}
53+
4054
unsafe impl Send for Heap {}
4155

4256
impl Heap {

0 commit comments

Comments
 (0)