-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8fbfdbc
commit be172ce
Showing
13 changed files
with
417 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "acid_alloc_hater" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
acid_alloc = { path = "../", features = ["alloc", "sptr"] } | ||
alloc_hater = { path = "../alloc_hater" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
#![deny(unsafe_op_in_unsafe_fn)] | ||
|
||
use std::{alloc::Layout, ops::Range, ptr::NonNull}; | ||
|
||
use acid_alloc::{AllocInitError, Buddy, Global}; | ||
use alloc_hater::Subject; | ||
|
||
pub struct BuddySubject<const BLK_SIZE: usize, const LEVELS: usize>( | ||
Buddy<BLK_SIZE, LEVELS, Global>, | ||
); | ||
|
||
impl<const BLK_SIZE: usize, const LEVELS: usize> BuddySubject<BLK_SIZE, LEVELS> { | ||
pub fn new(num_blocks: usize) -> Result<Self, AllocInitError> { | ||
let b = Buddy::try_new(num_blocks)?; | ||
Ok(BuddySubject(b)) | ||
} | ||
|
||
pub fn new_with_offset_gaps( | ||
num_blocks: usize, | ||
gaps: impl IntoIterator<Item = Range<usize>>, | ||
) -> Result<Self, AllocInitError> { | ||
let b = Buddy::try_new_with_offset_gaps(num_blocks, gaps)?; | ||
Ok(BuddySubject(b)) | ||
} | ||
} | ||
|
||
impl<const BLK_SIZE: usize, const LEVELS: usize> Subject for BuddySubject<BLK_SIZE, LEVELS> { | ||
type AllocError = acid_alloc::AllocError; | ||
|
||
fn allocate(&mut self, layout: Layout) -> Result<NonNull<[u8]>, Self::AllocError> { | ||
self.0.allocate(layout) | ||
} | ||
|
||
unsafe fn deallocate(&mut self, ptr: NonNull<u8>, _layout: std::alloc::Layout) { | ||
unsafe { self.0.deallocate(ptr) }; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "alloc_hater" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
arbitrary = { version = "1.1.3", features = ["derive"] } | ||
quickcheck = "1.0.3" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
//! A small library for ~~hating on~~ evaluating the correctness of allocators. | ||
#![deny(unsafe_op_in_unsafe_fn)] | ||
|
||
use core::{alloc::Layout, mem::MaybeUninit, ptr::NonNull, slice}; | ||
use std::cmp; | ||
|
||
#[derive(arbitrary::Arbitrary)] | ||
enum AllocatorOpTag { | ||
Alloc, | ||
Dealloc, | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
pub enum AllocatorOp { | ||
Alloc(Layout), | ||
Dealloc(usize), | ||
} | ||
|
||
impl arbitrary::Arbitrary<'_> for AllocatorOp { | ||
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> { | ||
let tag = AllocatorOpTag::arbitrary(u)?; | ||
|
||
let op = match tag { | ||
AllocatorOpTag::Alloc => { | ||
// Select a random bit index and shift to obtain a power of two. | ||
let align_shift = u8::arbitrary(u)? % usize::BITS as u8; | ||
let align: usize = 1 << align_shift; | ||
assert!(align.is_power_of_two()); | ||
|
||
// Clamp size to prevent Layout creation errors. | ||
let size = cmp::min(usize::arbitrary(u)?, isize::MAX as usize - (align - 1)); | ||
|
||
let layout = match Layout::from_size_align(size, align) { | ||
Ok(l) => l, | ||
Err(_) => { | ||
panic!("invalid layout params: size=0x{size:X} align=0x{align:X}"); | ||
} | ||
}; | ||
|
||
AllocatorOp::Alloc(layout) | ||
} | ||
|
||
AllocatorOpTag::Dealloc => AllocatorOp::Dealloc(usize::arbitrary(u)?), | ||
}; | ||
|
||
Ok(op) | ||
} | ||
} | ||
|
||
pub trait Subject { | ||
type AllocError; | ||
|
||
/// Allocates a block of memory according to `layout`. | ||
fn allocate(&mut self, layout: Layout) -> Result<NonNull<[u8]>, Self::AllocError>; | ||
|
||
/// Deallocates the block of memory with layout `layout` pointed to by `ptr`. | ||
/// | ||
/// # Safety | ||
/// | ||
/// `ptr` must denote a block of memory currently allocated by this | ||
/// allocator, and it must have been allocated with `layout`. | ||
unsafe fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout); | ||
} | ||
|
||
struct Block { | ||
// A pointer to the allocated region. | ||
ptr: NonNull<[u8]>, | ||
// The original allocation layout. | ||
layout: Layout, | ||
// The unique ID of the last operation that wrote to this allocation. | ||
id: u64, | ||
} | ||
|
||
unsafe fn paint(ptr: NonNull<[u8]>, id: u64) { | ||
let slice: &mut [MaybeUninit<u8>] = | ||
unsafe { slice::from_raw_parts_mut(ptr.cast().as_ptr(), ptr.len()) }; | ||
let id_bytes = id.to_le_bytes().into_iter().cycle(); | ||
|
||
for (byte, value) in slice.iter_mut().zip(id_bytes) { | ||
byte.write(value); | ||
} | ||
} | ||
|
||
impl Block { | ||
unsafe fn init(ptr: NonNull<[u8]>, layout: Layout, id: u64) -> Block { | ||
unsafe { paint(ptr, id) }; | ||
|
||
Block { ptr, layout, id } | ||
} | ||
|
||
unsafe fn paint(&mut self, id: u64) { | ||
unsafe { paint(self.ptr, id) }; | ||
} | ||
|
||
// Safety: must be initialized | ||
unsafe fn verify(&self) -> bool { | ||
let slice: &[u8] = unsafe { self.ptr.as_ref() }; | ||
let id_bytes = self.id.to_le_bytes().into_iter().cycle(); | ||
|
||
for (byte, value) in slice.iter().zip(id_bytes) { | ||
if *byte != value { | ||
return false; | ||
} | ||
} | ||
|
||
true | ||
} | ||
} | ||
|
||
pub struct Evaluator<S: Subject> { | ||
subject: S, | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct Failed { | ||
pub completed: Vec<AllocatorOp>, | ||
pub failed_op: AllocatorOp, | ||
} | ||
|
||
impl<S: Subject> Evaluator<S> { | ||
pub fn new(subject: S) -> Evaluator<S> { | ||
Evaluator { subject } | ||
} | ||
|
||
pub fn evaluate(&mut self, ops: impl IntoIterator<Item = AllocatorOp>) -> Result<(), Failed> { | ||
let mut completed = Vec::new(); | ||
let mut blocks = Vec::new(); | ||
|
||
for (op_id, op) in ops.into_iter().enumerate() { | ||
match op { | ||
AllocatorOp::Alloc(layout) => { | ||
let ptr = match self.subject.allocate(layout) { | ||
Ok(p) => p, | ||
Err(_) => continue, | ||
}; | ||
|
||
let id: u64 = op_id.try_into().unwrap(); | ||
let block = unsafe { Block::init(ptr, layout, id) }; | ||
blocks.push(block); | ||
} | ||
|
||
AllocatorOp::Dealloc(raw_idx) => { | ||
if blocks.is_empty() { | ||
continue; | ||
} | ||
|
||
let idx = raw_idx % blocks.len(); | ||
let mut block = blocks.swap_remove(idx); | ||
if unsafe { !block.verify() } { | ||
return Err(Failed { | ||
completed, | ||
failed_op: op, | ||
}); | ||
} | ||
|
||
let id: u64 = op_id.try_into().unwrap(); | ||
unsafe { | ||
block.paint(id); | ||
self.subject.deallocate(block.ptr.cast(), block.layout); | ||
} | ||
} | ||
} | ||
|
||
completed.push(op); | ||
} | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
target | ||
corpus | ||
artifacts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
[package] | ||
name = "acid_alloc_fuzz" | ||
version = "0.0.0" | ||
authors = ["Automatically generated"] | ||
publish = false | ||
edition = "2018" | ||
|
||
[package.metadata] | ||
cargo-fuzz = true | ||
|
||
[dependencies] | ||
acid_alloc_hater = { path = "../acid_alloc_hater" } | ||
alloc_hater = { path = "../alloc_hater" } | ||
arbitrary = { version = "1.1.3", features = ["derive"] } | ||
libfuzzer-sys = "0.4" | ||
|
||
[dependencies.acid_alloc] | ||
path = ".." | ||
features = ["alloc", "sptr"] | ||
|
||
# Prevent this from interfering with workspaces | ||
[workspace] | ||
members = ["."] | ||
|
||
[[bin]] | ||
name = "buddy_contiguous" | ||
path = "fuzz_targets/buddy_contiguous.rs" | ||
test = false | ||
doc = false | ||
|
||
[[bin]] | ||
name = "buddy_discontiguous" | ||
path = "fuzz_targets/buddy_discontiguous.rs" | ||
test = false | ||
doc = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
#![no_main] | ||
#![feature(allocator_api)] | ||
|
||
use acid_alloc_hater::BuddySubject; | ||
use alloc_hater::AllocatorOp; | ||
use arbitrary::{Arbitrary, Unstructured}; | ||
use libfuzzer_sys::fuzz_target; | ||
|
||
const BLK_SIZE: usize = 16384; | ||
const LEVELS: usize = 8; | ||
|
||
const MAX_BLOCKS: usize = 1024; | ||
|
||
#[derive(Clone, Debug)] | ||
struct Args { | ||
num_blocks: usize, | ||
ops: Vec<AllocatorOp>, | ||
} | ||
|
||
impl Arbitrary<'_> for Args { | ||
fn arbitrary(un: &mut Unstructured) -> arbitrary::Result<Args> { | ||
let num_blocks = usize::arbitrary(un)? % MAX_BLOCKS; | ||
let ops = Vec::arbitrary(un)?; | ||
|
||
Ok(Args { num_blocks, ops }) | ||
} | ||
} | ||
|
||
fuzz_target!(|args: Args| { | ||
let buddy: BuddySubject<BLK_SIZE, LEVELS> = match BuddySubject::new(args.num_blocks) { | ||
Ok(a) => a, | ||
Err(_) => return, | ||
}; | ||
|
||
let mut eval = alloc_hater::Evaluator::new(buddy); | ||
eval.evaluate(args.ops).unwrap(); | ||
}); |
Oops, something went wrong.