diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000000..1a45eee776 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock new file mode 100644 index 0000000000..e2bd515772 --- /dev/null +++ b/fuzz/Cargo.lock @@ -0,0 +1,150 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arbitrary" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "packit" +version = "0.1.0" +source = "git+https://github.com/coconut-svsm/packit#fffebdc18a3f559f0a01425b17cf41b1c249fbe0" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "svsm" +version = "0.1.0" +dependencies = [ + "bitflags", + "log", + "packit", +] + +[[package]] +name = "svsm-fuzz" +version = "0.0.0" +dependencies = [ + "libfuzzer-sys", + "svsm", +] + +[[package]] +name = "syn" +version = "2.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "zerocopy" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20707b61725734c595e840fb3704378a0cd2b9c74cc9e6e20724838fc6a1e2f9" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56097d5b91d711293a42be9289403896b68654625021732067eac7a4ca388a1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000000..8c3d168b6b --- /dev/null +++ b/fuzz/Cargo.toml @@ -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 diff --git a/fuzz/acpi-dict.txt b/fuzz/acpi-dict.txt new file mode 100644 index 0000000000..c20a7e0bf4 --- /dev/null +++ b/fuzz/acpi-dict.txt @@ -0,0 +1,4 @@ +"etc/acpi/rsdp" +"etc/acpi/tables" +"APIC" +"\x00\x00\x00\x14" diff --git a/fuzz/fuzz_targets/acpi.rs b/fuzz/fuzz_targets/acpi.rs new file mode 100644 index 0000000000..0bc1aa81eb --- /dev/null +++ b/fuzz/fuzz_targets/acpi.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2023 SUSE LLC +// +// Author: Carlos López + +#![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, +} + +impl<'a> FuzzIo<'a> { + /// Create a new [`FuzzIo`] instance. Returns [`None`] if the input is + /// empty. + fn new(data: &'a [u8]) -> Option { + 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 +}); diff --git a/fuzz/fuzz_targets/fw_meta.rs b/fuzz/fuzz_targets/fw_meta.rs new file mode 100644 index 0000000000..898d00ed75 --- /dev/null +++ b/fuzz/fuzz_targets/fw_meta.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2023 SUSE LLC +// +// Author: Carlos López + +#![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 +}); diff --git a/src/fs/filesystem.rs b/src/fs/filesystem.rs index a4c6a0cfed..ccf54588e5 100644 --- a/src/fs/filesystem.rs +++ b/src/fs/filesystem.rs @@ -114,7 +114,7 @@ impl SvsmFs { self.root = Some(root.clone()); } - #[cfg(test)] + #[cfg(any(test, fuzzing))] fn uninitialize(&mut self) { self.root = None; } @@ -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(); } diff --git a/src/mm/address_space.rs b/src/mm/address_space.rs index 5545d575c3..5bae356dfd 100644 --- a/src/mm/address_space.rs +++ b/src/mm/address_space.rs @@ -4,7 +4,7 @@ // // Author: Joerg Roedel -#[cfg(test)] +#[cfg(any(test, fuzzing))] use crate::address::Address; use crate::address::{PhysAddr, VirtAddr}; use crate::utils::immut_after_init::ImmutAfterInitCell; @@ -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); @@ -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 { @@ -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()) } diff --git a/src/mm/alloc.rs b/src/mm/alloc.rs index 35fcb0a2d8..9957a0adf5 100644 --- a/src/mm/alloc.rs +++ b/src/mm/alloc.rs @@ -1266,8 +1266,8 @@ unsafe impl GlobalAlloc for SvsmAllocator { } } -#[cfg_attr(not(any(test, doctest)), global_allocator)] -#[cfg_attr(any(test, doctest), allow(dead_code))] +#[cfg_attr(not(any(test, doctest, fuzzing)), global_allocator)] +#[cfg_attr(any(test, doctest, fuzzing), allow(dead_code))] static mut ALLOCATOR: SvsmAllocator = SvsmAllocator::new(); pub fn root_mem_init(pstart: PhysAddr, vstart: VirtAddr, page_count: usize) { @@ -1286,13 +1286,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, ()> { @@ -1317,7 +1317,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;