Skip to content

Commit 81a3cab

Browse files
committed
fuzzing: initial fuzzing implementation
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 <carlos.lopez@suse.com>
1 parent 07b2acb commit 81a3cab

File tree

9 files changed

+320
-14
lines changed

9 files changed

+320
-14
lines changed

fuzz/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
target
2+
corpus
3+
artifacts
4+
coverage

fuzz/Cargo.lock

+175
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fuzz/Cargo.toml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
[package]
2+
name = "svsm-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+
13+
[dependencies.svsm]
14+
path = ".."
15+
16+
# Prevent this from interfering with workspaces
17+
[workspace]
18+
members = ["."]
19+
20+
[profile.release]
21+
debug = 1
22+
23+
[[bin]]
24+
name = "fw_meta"
25+
path = "fuzz_targets/fw_meta.rs"
26+
test = false
27+
doc = false
28+
29+
[[bin]]
30+
name = "acpi"
31+
path = "fuzz_targets/acpi.rs"
32+
test = false
33+
doc = false

fuzz/acpi-dict.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"etc/acpi/rsdp"
2+
"etc/acpi/tables"
3+
"APIC"
4+
"\x00\x00\x00\x14"

fuzz/fuzz_targets/acpi.rs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
//
3+
// Copyright (c) 2023 SUSE LLC
4+
//
5+
// Author: Carlos López <carlos.lopez@suse.com>
6+
7+
#![no_main]
8+
9+
use core::num::NonZeroUsize;
10+
use core::sync::atomic::{AtomicUsize, Ordering};
11+
use libfuzzer_sys::{fuzz_target, Corpus};
12+
use std::hint::black_box;
13+
use svsm::acpi::tables::load_acpi_cpu_info;
14+
use svsm::fw_cfg::FwCfg;
15+
use svsm::io::IOPort;
16+
17+
/// A structure that emulates port I/O from a libfuzzer input.
18+
#[derive(Debug)]
19+
struct FuzzIo<'a> {
20+
data: &'a [u8],
21+
len: NonZeroUsize,
22+
pos: AtomicUsize,
23+
}
24+
25+
impl<'a> FuzzIo<'a> {
26+
/// Create a new [`FuzzIo`] instance. Returns [`None`] if the input is
27+
/// empty.
28+
fn new(data: &'a [u8]) -> Option<Self> {
29+
let len = NonZeroUsize::new(data.len())?;
30+
let pos = AtomicUsize::new(0);
31+
Some(Self { data, len, pos })
32+
}
33+
}
34+
35+
impl IOPort for FuzzIo<'_> {
36+
fn outb(&self, _port: u16, _value: u8) {}
37+
fn outw(&self, _port: u16, _value: u16) {}
38+
39+
fn inb(&self, _port: u16) -> u8 {
40+
let pos = self.pos.load(Ordering::Relaxed);
41+
let val = unsafe { *self.data.get_unchecked(pos) };
42+
self.pos.store((pos + 1) % self.len, Ordering::Relaxed);
43+
val
44+
}
45+
46+
fn inw(&self, port: u16) -> u16 {
47+
let mut buf = [0u8; 2];
48+
buf[0] = self.inb(port);
49+
buf[1] = self.inb(port);
50+
u16::from_le_bytes(buf)
51+
}
52+
}
53+
54+
fuzz_target!(|data: &[u8]| -> Corpus {
55+
let Some(io) = FuzzIo::new(data) else {
56+
return Corpus::Reject;
57+
};
58+
let fwcfg = FwCfg::new(&io);
59+
60+
if let Ok(info) = load_acpi_cpu_info(&fwcfg) {
61+
let _ = black_box(info);
62+
}
63+
64+
Corpus::Keep
65+
});

fuzz/fuzz_targets/fw_meta.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
//
3+
// Copyright (c) 2023 SUSE LLC
4+
//
5+
// Author: Carlos López <carlos.lopez@suse.com>
6+
7+
#![no_main]
8+
9+
use libfuzzer_sys::{fuzz_target, Corpus};
10+
use std::hint::black_box;
11+
use svsm::fw_meta::parse_fw_meta_data;
12+
use svsm::types::PAGE_SIZE;
13+
14+
fuzz_target!(|data: &[u8]| -> Corpus {
15+
if data.len() != PAGE_SIZE {
16+
return Corpus::Reject;
17+
}
18+
19+
let fw_meta = parse_fw_meta_data(data);
20+
if let Ok(meta) = fw_meta {
21+
let _ = black_box(meta);
22+
}
23+
24+
Corpus::Keep
25+
});

src/fs/filesystem.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl SvsmFs {
110110
self.root = Some(root.clone());
111111
}
112112

113-
#[cfg(test)]
113+
#[cfg(any(test, fuzzing))]
114114
fn uninitialize(&mut self) {
115115
self.root = None;
116116
}
@@ -134,8 +134,8 @@ pub fn initialize_fs() {
134134
}
135135
}
136136

137-
#[cfg(test)]
138-
fn uninitialize_fs() {
137+
#[cfg(any(test, fuzzing))]
138+
pub fn uninitialize_fs() {
139139
unsafe {
140140
FS_ROOT.uninitialize();
141141
}

src/mm/address_space.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//
55
// Author: Joerg Roedel <jroedel@suse.de>
66

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

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

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

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

61-
#[cfg(test)]
61+
#[cfg(any(test, fuzzing))]
6262
pub fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
6363
VirtAddr::from(paddr.bits())
6464
}

0 commit comments

Comments
 (0)