Skip to content

Commit

Permalink
fuzzing: add SvsmAllocator fuzzer
Browse files Browse the repository at this point in the history
Add a new fuzzing harness for the SvsmAllocator, which will perform
allocations will run a series of semi-random actions like allocating,
freeing and reading memory.

Unfortunately this requires access to the SvsmAllocator type, so it
needs to be public.

Signed-off-by: Carlos López <[email protected]>
  • Loading branch information
00xc committed Nov 7, 2023
1 parent 9eff519 commit 90923d7
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 2 deletions.
6 changes: 6 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,9 @@ name = "page_alloc"
path = "fuzz_targets/page_alloc.rs"
test = false
doc = false

[[bin]]
name = "alloc"
path = "fuzz_targets/alloc.rs"
test = false
doc = false
154 changes: 154 additions & 0 deletions fuzz/fuzz_targets/alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2023 SUSE LLC
//
// Author: Carlos López <[email protected]>

#![no_main]

use arbitrary::Arbitrary;
use core::alloc::{GlobalAlloc, Layout, LayoutError};
use core::num::NonZeroUsize;
use libfuzzer_sys::fuzz_target;
use svsm::mm::alloc::{SvsmAllocator, TestRootMem};

const MIN_ROOT_MEM_SIZE: usize = 0x8000;
const MAX_ROOT_MEM_SIZE: usize = 0x100000;

#[inline]
fn adjust_mem_size(size: usize) -> usize {
MIN_ROOT_MEM_SIZE + (size % (MAX_ROOT_MEM_SIZE - MIN_ROOT_MEM_SIZE + 1))
}

#[derive(Arbitrary, Debug)]
struct FuzzLayout {
size: usize,
align: usize,
}

impl TryFrom<FuzzLayout> for Layout {
type Error = LayoutError;

fn try_from(ly: FuzzLayout) -> Result<Self, Self::Error> {
Self::from_size_align(ly.size, ly.align)
}
}

/// A wrapper around SvsmAllocator that marks memory as initialized or
/// uninitialized on allocation and deallocation respectively.
struct PoisonAllocator {
heap: SvsmAllocator,
}

impl PoisonAllocator {
const POISON_BYTE: u8 = 0xf7;
const WRITE_BYTE: u8 = 0x8;

fn new() -> Self {
Self {
heap: SvsmAllocator::new(),
}
}

unsafe fn unpoison_mem(&self, ptr: *mut u8, size: usize) {
ptr.write_bytes(Self::WRITE_BYTE, size);
}

unsafe fn poison_mem(&self, ptr: *mut u8, size: usize) {
ptr.write_bytes(Self::POISON_BYTE, size);
}

unsafe fn check_mem(&self, ptr: *mut u8, size: usize) {
for i in 0..size {
assert_eq!(ptr.add(i).read_volatile(), Self::WRITE_BYTE);
}
}

unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = self.heap.alloc(layout);
if !ptr.is_null() {
self.unpoison_mem(ptr, layout.size());
}
ptr
}

unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.check_mem(ptr, layout.size());
self.poison_mem(ptr, layout.size());
self.heap.dealloc(ptr, layout);
}

unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> *mut u8 {
self.check_mem(ptr, layout.size());
self.poison_mem(ptr, layout.size());
let ptr = self.heap.realloc(ptr, layout, new_layout.size());
if !ptr.is_null() {
self.unpoison_mem(ptr, new_layout.size());
}
ptr
}
}

#[derive(Arbitrary, Debug)]
enum Action {
Alloc(FuzzLayout),
Free(usize),
Realloc(usize, NonZeroUsize),
Read(usize),
}

#[derive(Arbitrary, Debug)]
struct FuzzInput {
root_mem_size: usize,
actions: Vec<Action>,
}

fuzz_target!(|inp: FuzzInput| {
let _mem = TestRootMem::setup(adjust_mem_size(inp.root_mem_size));
let heap = PoisonAllocator::new();
let mut ptrs = Vec::new();

for action in inp.actions.into_iter() {
match action {
Action::Alloc(layout) => {
let Ok(layout) = Layout::try_from(layout) else {
continue;
};
let ptr = unsafe { heap.alloc(layout) };
if !ptr.is_null() {
ptrs.push((ptr, layout));
}
}
Action::Free(idx) => {
if let Some(idx) = idx.checked_rem(ptrs.len()) {
let (ptr, layout) = ptrs.swap_remove(idx);
unsafe { heap.dealloc(ptr, layout) };
}
}
Action::Read(idx) => {
if let Some(idx) = idx.checked_rem(ptrs.len()) {
let (ptr, layout) = ptrs[idx];
unsafe { heap.check_mem(ptr, layout.size()) };
};
}
Action::Realloc(idx, new_size) => {
let Some(idx) = idx.checked_rem(ptrs.len()) else {
continue;
};

// Try to get the new layout. Alignment must be the same.
let new_size = new_size.get();
let (ptr, layout) = ptrs.swap_remove(idx);
let Ok(new_layout) = Layout::from_size_align(new_size, layout.align()) else {
ptrs.push((ptr, layout));
continue;
};

let ptr = unsafe { heap.realloc(ptr, layout, new_layout) };
if !ptr.is_null() {
ptrs.push((ptr, new_layout));
}
}
}
}
});
4 changes: 2 additions & 2 deletions src/mm/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1164,15 +1164,15 @@ impl Slab {
static SLAB_PAGE_SLAB: SpinLock<SlabPageSlab> = SpinLock::new(SlabPageSlab::new());

#[derive(Debug)]
struct SvsmAllocator {
pub struct SvsmAllocator {
slabs: [SpinLock<Slab>; 7],
}

impl SvsmAllocator {
const MIN_SLAB_SIZE: u16 = 32;
const MIN_ALIGNMENT: u32 = Self::MIN_SLAB_SIZE.trailing_zeros();

const fn new() -> Self {
pub const fn new() -> Self {
Self {
slabs: [
SpinLock::new(Slab::new(Self::MIN_SLAB_SIZE)),
Expand Down

0 comments on commit 90923d7

Please sign in to comment.