Skip to content

Commit

Permalink
add slab fuzz target (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
dataphract authored Aug 20, 2022
1 parent a5ced93 commit ba2de06
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 67 deletions.
6 changes: 6 additions & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
unstable_features = true

comment_width = 100
wrap_comments = true

imports_granularity = "Crate"
46 changes: 18 additions & 28 deletions acid_alloc_hater/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#![deny(unsafe_op_in_unsafe_fn)]

use std::{alloc::Layout, mem::ManuallyDrop, ops::Range, ptr::NonNull};
use std::{alloc::Layout, ops::Range, ptr::NonNull};

use acid_alloc::{AllocInitError, Buddy, Global, Raw};
use acid_alloc::{AllocInitError, Buddy, Global, Slab};
use alloc_hater::Subject;

pub struct BuddySubject<const BLK_SIZE: usize, const LEVELS: usize>(
Expand Down Expand Up @@ -39,36 +39,26 @@ impl<const BLK_SIZE: usize, const LEVELS: usize> Subject for BuddySubject<BLK_SI
fn handle_custom_op(&mut self, (): ()) {}
}

pub struct RawBuddySubject<const BLK_SIZE: usize, const LEVELS: usize>(
ManuallyDrop<Buddy<BLK_SIZE, LEVELS, Raw>>,
);

impl<const BLK_SIZE: usize, const LEVELS: usize> RawBuddySubject<BLK_SIZE, LEVELS> {
pub fn new_unpopulated(num_blocks: usize) -> Result<Self, AllocInitError> {
let metadata_layout = Buddy::<BLK_SIZE, LEVELS, Raw>::metadata_layout(num_blocks)?;
let region_layout = Buddy::<BLK_SIZE, LEVELS, Raw>::region_layout(num_blocks)?;
pub struct SlabSubject(Slab<Global>);

let metadata = NonNull::new(unsafe { std::alloc::alloc(metadata_layout) })
.ok_or(AllocInitError::AllocFailed(metadata_layout))?;
let region =
NonNull::new(unsafe { std::alloc::alloc(region_layout) }).ok_or_else(|| {
unsafe { std::alloc::dealloc(metadata.as_ptr(), metadata_layout) };
AllocInitError::AllocFailed(region_layout)
})?;
impl SlabSubject {
pub fn new(block_size: usize, num_blocks: usize) -> Result<Self, AllocInitError> {
let s = Slab::try_new(block_size, num_blocks)?;
Ok(SlabSubject(s))
}
}

let buddy = unsafe { Buddy::new_raw_unpopulated(metadata, region, num_blocks) }?;
impl Subject for SlabSubject {
type Op = ();
type AllocError = acid_alloc::AllocError;

Ok(RawBuddySubject(ManuallyDrop::new(buddy)))
fn allocate(&mut self, layout: Layout) -> Result<NonNull<[u8]>, Self::AllocError> {
self.0.allocate(layout)
}
}

impl<const BLK_SIZE: usize, const LEVELS: usize> Drop for RawBuddySubject<BLK_SIZE, LEVELS> {
fn drop(&mut self) {
unsafe {
let raw = ManuallyDrop::take(&mut self.0);
let parts = raw.into_raw_parts();
std::alloc::dealloc(parts.metadata.as_ptr(), parts.metadata_layout);
std::alloc::dealloc(parts.region.as_ptr(), parts.region_layout);
}
unsafe fn deallocate(&mut self, ptr: NonNull<u8>, _layout: std::alloc::Layout) {
unsafe { self.0.deallocate(ptr) };
}

fn handle_custom_op(&mut self, (): ()) {}
}
19 changes: 7 additions & 12 deletions alloc_hater/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,21 @@ impl arbitrary::Arbitrary<'_> for ArbLayout {
enum AllocatorOpTag {
Alloc,
Dealloc,
Custom,
}

#[derive(Clone, Debug)]
pub enum AllocatorOp<T: for<'a> arbitrary::Arbitrary<'a>> {
pub enum AllocatorOp {
Alloc(Layout),
Dealloc(usize),
Custom(T),
}

impl<T: for<'a> arbitrary::Arbitrary<'a>> arbitrary::Arbitrary<'_> for AllocatorOp<T> {
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 => AllocatorOp::Alloc(ArbLayout::arbitrary(u)?.0),
AllocatorOpTag::Dealloc => AllocatorOp::Dealloc(usize::arbitrary(u)?),
AllocatorOpTag::Custom => AllocatorOp::Custom(T::arbitrary(u)?),
};

Ok(op)
Expand Down Expand Up @@ -190,19 +187,19 @@ pub struct Evaluator<S: Subject> {
}

#[derive(Clone, Debug)]
pub struct Failed<T: for<'a> arbitrary::Arbitrary<'a>> {
pub completed: Vec<AllocatorOp<T>>,
pub failed_op: AllocatorOp<T>,
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<I>(&mut self, ops: I) -> Result<(), Failed<S::Op>>
pub fn evaluate<I>(&mut self, ops: I) -> Result<(), Failed>
where
I: for<'a> IntoIterator<Item = AllocatorOp<S::Op>>,
I: for<'a> IntoIterator<Item = AllocatorOp>,
{
let mut completed = Vec::new();
let mut blocks = Blocks::new();
Expand Down Expand Up @@ -238,8 +235,6 @@ impl<S: Subject> Evaluator<S> {
self.subject.deallocate(block.ptr.cast(), block.layout);
}
}

AllocatorOp::Custom(_) => todo!(),
}

completed.push(op);
Expand Down
6 changes: 6 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ name = "buddy_discontiguous"
path = "fuzz_targets/buddy_discontiguous.rs"
test = false
doc = false

[[bin]]
name = "slab"
path = "fuzz_targets/slab.rs"
test = false
doc = false
45 changes: 45 additions & 0 deletions fuzz/fuzz_targets/slab.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#![no_main]
use acid_alloc_hater::SlabSubject;
use alloc_hater::AllocatorOp;
use arbitrary::{Arbitrary, Unstructured};
use libfuzzer_sys::fuzz_target;

const MAX_NUM_BLOCKS: usize = 1024;
const MAX_BLOCK_SIZE: usize = 1024;

#[derive(Clone, Debug)]
struct Args {
block_size: usize,
num_blocks: usize,
ops: Vec<AllocatorOp>,
}

impl Arbitrary<'_> for Args {
fn arbitrary(un: &mut Unstructured) -> arbitrary::Result<Args> {
let block_size = usize::arbitrary(un)? % MAX_BLOCK_SIZE;
let num_blocks = usize::arbitrary(un)? % MAX_NUM_BLOCKS;
let ops = Vec::arbitrary(un)?;

Ok(Args {
block_size,
num_blocks,
ops,
})
}
}

fuzz_target!(|args: Args| {
let Args {
block_size,
num_blocks,
ops,
} = args;

let mut slab = match SlabSubject::new(args.block_size, args.num_blocks) {
Ok(s) => s,
Err(_) => return,
};

let mut eval = alloc_hater::Evaluator::new(slab);
eval.evaluate(ops).unwrap();
});
49 changes: 22 additions & 27 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
//!
//! ---
//!
//! This crate provides allocators that can function without the backing of another
//! allocator. This makes them suitable for use on bare metal or with OS allocation
//! facilities like `mmap(2)`/`brk(2)`.
//! This crate provides allocators that can function without the backing of another allocator. This
//! makes them suitable for use on bare metal or with OS allocation facilities like
//! `mmap(2)`/`brk(2)`.
//!
//! ## Allocators
//!
Expand All @@ -20,8 +20,8 @@
//! ## Features
//!
//! All allocators provided by this crate are available in a `#![no_std]`,
//! `#![cfg(no_global_oom_handling)]` environment. Additional functionality is
//! available when enabling feature flags:
//! `#![cfg(no_global_oom_handling)]` environment. Additional functionality is available when
//! enabling feature flags:
//!
//! <table>
//! <tr>
Expand Down Expand Up @@ -69,8 +69,8 @@
#![cfg_attr(feature = "unstable", feature(int_log))]
#![cfg_attr(feature = "unstable", feature(strict_provenance))]
#![cfg_attr(docs_rs, feature(doc_cfg))]
// This is necessary to allow `sptr` and `crate::core` to shadow methods
// provided by unstable features.
// This is necessary to allow `sptr` and `crate::core` to shadow methods provided by unstable
// features.
#![allow(unstable_name_collisions)]

#[cfg(test)]
Expand Down Expand Up @@ -128,10 +128,8 @@ requires_sptr_or_unstable! {

#[cfg(not(feature = "unstable"))]
pub(crate) fn layout_error() -> LayoutError {
// HACK: LayoutError is #[non_exhaustive], so it can't be
// constructed outside the standard library. As a workaround,
// deliberately pass bad values to the constructor to get one.

// HACK: LayoutError is #[non_exhaustive], so it can't be constructed outside the standard
// library. As a workaround, deliberately pass bad values to the constructor to get one.
Layout::from_size_align(0, 0).unwrap_err()
}

Expand All @@ -140,38 +138,35 @@ requires_sptr_or_unstable! {
pub enum AllocInitError {
/// A necessary allocation failed.
///
/// This variant is returned when a constructor attempts to allocate
/// memory, either for metadata or the managed region, but the
/// underlying allocator fails.
/// This variant is returned when a constructor attempts to allocate memory, either for
/// metadata or the managed region, but the underlying allocator fails.
///
/// The variant contains the [`Layout`] that could not be allocated.
AllocFailed(Layout),

/// The configuration of the allocator is invalid.
///
/// This variant is returned when an allocator's configuration
/// parameters are impossible to satisfy.
/// This variant is returned when an allocator's configuration parameters are impossible to
/// satisfy.
InvalidConfig,

/// The location of the allocator is invalid.
///
/// This variant is returned when the full size of the managed region
/// would not fit at the provided address, i.e., pointer calculations
/// would overflow.
/// This variant is returned when the full size of the managed region would not fit at the
/// provided address, i.e., pointer calculations would overflow.
InvalidLocation,
}

/// Types which provide memory which backs an allocator.
///
/// This is a supertrait of [`Allocator`], and is implemented by the following types:
/// - The `Raw` marker type indicates that an allocator is not backed by another
/// allocator. This is the case when constructing the allocator from raw
/// pointers. Memory used by this allocator can be reclaimed using
/// `.into_raw_parts()`.
/// - The `Global` marker type indicates that an allocator is backed by the
/// global allocator. The allocator will free its memory on drop.
/// - Any type `A` which implements [`Allocator`] indicates that an allocator is
/// backed by an instance of `A`. The allocator will free its memory on drop.
/// - The `Raw` marker type indicates that an allocator is not backed by another allocator. This
/// is the case when constructing the allocator from raw pointers. Memory used by this
/// allocator can be reclaimed using `.into_raw_parts()`.
/// - The `Global` marker type indicates that an allocator is backed by the global allocator.
/// The allocator will free its memory on drop.
/// - Any type `A` which implements [`Allocator`] indicates that an allocator is backed by an
/// instance of `A`. The allocator will free its memory on drop.
///
/// [`Allocator`]: https://doc.rust-lang.org/stable/core/alloc/trait.Allocator.html
pub trait BackingAllocator: Sealed {
Expand Down

0 comments on commit ba2de06

Please sign in to comment.