Skip to content

Commit

Permalink
fuzzing: initial fuzzing implementation
Browse files Browse the repository at this point in the history
Add fuzzing to the COCONUT SVSM project via cargo-fuzz. This commit
adds the base infrastructure for fuzzing, as well as two harnesses
for testing the fw_meta and ACPI table interfaces respectively.

This works towards issue coconut-svsm#34.

Signed-off-by: Carlos López <[email protected]>
  • Loading branch information
00xc committed Oct 14, 2023
1 parent a0b6565 commit 743369e
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 12 deletions.
4 changes: 4 additions & 0 deletions fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
150 changes: 150 additions & 0 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "svsm-fuzz"
version = "0.0.0"
publish = false
edition = "2021"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"

[dependencies.svsm]
path = ".."

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[profile.release]
debug = 1

[[bin]]
name = "fw_meta"
path = "fuzz_targets/fw_meta.rs"
test = false
doc = false

[[bin]]
name = "acpi"
path = "fuzz_targets/acpi.rs"
test = false
doc = false
4 changes: 4 additions & 0 deletions fuzz/acpi-dict.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"etc/acpi/rsdp"
"etc/acpi/tables"
"APIC"
"\x00\x00\x00\x14"
65 changes: 65 additions & 0 deletions fuzz/fuzz_targets/acpi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2023 SUSE LLC
//
// Author: Carlos López <[email protected]>

#![no_main]

use core::num::NonZeroUsize;
use libfuzzer_sys::{fuzz_target, Corpus};
use std::cell::Cell;
use std::hint::black_box;
use svsm::acpi::tables::load_acpi_cpu_info;
use svsm::fw_cfg::FwCfg;
use svsm::io::IOPort;

/// A structure that emulates port I/O from a libfuzzer input.
#[derive(Clone, Debug)]
struct FuzzIo<'a> {
data: &'a [u8],
len: NonZeroUsize,
pos: Cell<usize>,
}

impl<'a> FuzzIo<'a> {
/// Create a new [`FuzzIo`] instance. Returns [`None`] if the input is
/// empty.
fn new(data: &'a [u8]) -> Option<Self> {
let len = NonZeroUsize::new(data.len())?;
let pos = Cell::new(0);
Some(Self { data, len, pos })
}
}

impl IOPort for FuzzIo<'_> {
fn outb(&self, _port: u16, _value: u8) {}
fn outw(&self, _port: u16, _value: u16) {}

fn inb(&self, _port: u16) -> u8 {
let pos = self.pos.get();
let val = unsafe { *self.data.get_unchecked(pos) };
self.pos.set((pos + 1) % self.len);
val
}

fn inw(&self, port: u16) -> u16 {
let mut buf = [0u8; 2];
buf[0] = self.inb(port);
buf[1] = self.inb(port);
u16::from_le_bytes(buf)
}
}

fuzz_target!(|data: &[u8]| -> Corpus {
let Some(io) = FuzzIo::new(data) else {
return Corpus::Reject;
};
let fwcfg = FwCfg::new(&io);

if let Ok(info) = load_acpi_cpu_info(&fwcfg) {
let _ = black_box(info);
}

Corpus::Keep
});
27 changes: 27 additions & 0 deletions fuzz/fuzz_targets/fw_meta.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2023 SUSE LLC
//
// Author: Carlos López <[email protected]>

#![no_main]

use libfuzzer_sys::{fuzz_target, Corpus};
use std::hint::black_box;
use svsm::address::VirtAddr;
use svsm::fw_meta::parse_fw_meta_data;
use svsm::types::PAGE_SIZE;

fuzz_target!(|data: &[u8]| -> Corpus {
if data.len() != PAGE_SIZE {
return Corpus::Reject;
}

let addr = VirtAddr::from(data.as_ptr());
let fw_meta = parse_fw_meta_data(addr);
if let Ok(meta) = fw_meta {
let _ = black_box(meta);
}

Corpus::Keep
});
6 changes: 3 additions & 3 deletions src/fs/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ impl SvsmFs {
self.root = Some(root.clone());
}

#[cfg(test)]
#[cfg(any(test, fuzzing))]
fn uninitialize(&mut self) {
self.root = None;
}
Expand All @@ -138,8 +138,8 @@ pub fn initialize_fs() {
}
}

#[cfg(test)]
fn uninitialize_fs() {
#[cfg(any(test, fuzzing))]
pub fn uninitialize_fs() {
unsafe {
FS_ROOT.uninitialize();
}
Expand Down
10 changes: 5 additions & 5 deletions src/mm/address_space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//
// Author: Joerg Roedel <[email protected]>

#[cfg(test)]
#[cfg(any(test, fuzzing))]
use crate::address::Address;
use crate::address::{PhysAddr, VirtAddr};
use crate::utils::immut_after_init::ImmutAfterInitCell;
Expand All @@ -30,7 +30,7 @@ pub fn init_kernel_mapping_info(vstart: VirtAddr, vend: VirtAddr, pstart: PhysAd
.expect("Already initialized kernel mapping info");
}

#[cfg(not(test))]
#[cfg(not(any(test, fuzzing)))]
pub fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
if vaddr < KERNEL_MAPPING.virt_start || vaddr >= KERNEL_MAPPING.virt_end {
panic!("Invalid physical address {:#018x}", vaddr);
Expand All @@ -41,7 +41,7 @@ pub fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
KERNEL_MAPPING.phys_start + offset
}

#[cfg(not(test))]
#[cfg(not(any(test, fuzzing)))]
pub fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
let size: usize = KERNEL_MAPPING.virt_end - KERNEL_MAPPING.virt_start;
if paddr < KERNEL_MAPPING.phys_start || paddr >= KERNEL_MAPPING.phys_start + size {
Expand All @@ -53,12 +53,12 @@ pub fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
KERNEL_MAPPING.virt_start + offset
}

#[cfg(test)]
#[cfg(any(test, fuzzing))]
pub fn virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
PhysAddr::from(vaddr.bits())
}

#[cfg(test)]
#[cfg(any(test, fuzzing))]
pub fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
VirtAddr::from(paddr.bits())
}
Expand Down
8 changes: 4 additions & 4 deletions src/mm/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1264,13 +1264,13 @@ pub fn root_mem_init(pstart: PhysAddr, vstart: VirtAddr, page_count: usize) {
.expect("Failed to initialize SLAB_PAGE_SLAB");
}

#[cfg(test)]
#[cfg(any(test, fuzzing))]
static TEST_ROOT_MEM_LOCK: SpinLock<()> = SpinLock::new(());

#[cfg(test)]
#[cfg(any(test, fuzzing))]
use crate::locking::LockGuard;

#[cfg(test)]
#[cfg(any(test, fuzzing))]
// Allocate a memory region from the standard Rust allocator and pass it to
// root_mem_init().
pub fn setup_test_root_mem(size: usize) -> LockGuard<'static, ()> {
Expand All @@ -1295,7 +1295,7 @@ pub fn setup_test_root_mem(size: usize) -> LockGuard<'static, ()> {
lock
}

#[cfg(test)]
#[cfg(any(test, fuzzing))]
// Undo the setup done from setup_test_root_mem().
pub fn destroy_test_root_mem(lock: LockGuard<'static, ()>) {
extern crate alloc;
Expand Down

0 comments on commit 743369e

Please sign in to comment.