diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82f250c79bd6..a141d45cf557 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,6 +152,7 @@ jobs: --exclude islet_sdk --exclude ciborium --exclude islet_mc_harnesses + --exclude rmm_fuzz - name: Check rust coding-style run: | diff --git a/Cargo.lock b/Cargo.lock index 112f9fdea9c2..2722298cbc99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "armv9a" version = "0.0.1" @@ -138,6 +147,11 @@ name = "cc" version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] [[package]] name = "cexpr" @@ -318,6 +332,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "digest" version = "0.10.7" @@ -700,6 +725,15 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -715,6 +749,16 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libfuzzer-sys" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libloading" version = "0.8.5" @@ -1024,6 +1068,14 @@ dependencies = [ "subtle", ] +[[package]] +name = "rmm_fuzz" +version = "0.0.1" +dependencies = [ + "islet_rmm", + "libfuzzer-sys", +] + [[package]] name = "rsi-test" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index 5a47991c4c9f..f955d797d596 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "plat/fvp", "realm/rsi-test", "rmm/", + "rmm/fuzz/", "sdk", ] @@ -19,3 +20,7 @@ panic = "abort" [profile.release] lto = true panic = "abort" + +[profile.fuzz] +inherits = "dev" +panic = "unwind" diff --git a/rmm/Cargo.toml b/rmm/Cargo.toml index da997d14b626..7e604137e4fd 100644 --- a/rmm/Cargo.toml +++ b/rmm/Cargo.toml @@ -50,4 +50,4 @@ mc_rmi_rec_destroy = [] mc_rmi_version = [] [lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)'] } +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(kani)', 'cfg(fuzzing)'] } diff --git a/rmm/fuzz/.cargo/config.toml b/rmm/fuzz/.cargo/config.toml new file mode 100644 index 000000000000..779ac009e896 --- /dev/null +++ b/rmm/fuzz/.cargo/config.toml @@ -0,0 +1,5 @@ +[build] +target = "aarch64-unknown-linux-gnu" + +[target.aarch64-unknown-linux-gnu] +linker = "../../assets/toolchain/aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc" diff --git a/rmm/fuzz/Cargo.toml b/rmm/fuzz/Cargo.toml new file mode 100644 index 000000000000..fd0f5c9998c3 --- /dev/null +++ b/rmm/fuzz/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "rmm_fuzz" +version = "0.0.1" +authors = ["Islet Contributors"] +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = { version = "0.4.0", features = ["arbitrary-derive"] } +islet_rmm = { path = "../" } + +[[bin]] +name = "rmi_version_fuzz" +path = "fuzz_targets/rmi_version_fuzz.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "rmi_features_fuzz" +path = "fuzz_targets/rmi_features_fuzz.rs" +test = false +doc = false +bench = false diff --git a/rmm/fuzz/fuzz_targets/rmi_features_fuzz.rs b/rmm/fuzz/fuzz_targets/rmi_features_fuzz.rs new file mode 100644 index 000000000000..0f4e5d2d8322 --- /dev/null +++ b/rmm/fuzz/fuzz_targets/rmi_features_fuzz.rs @@ -0,0 +1,11 @@ +#![no_main] + +use islet_rmm::rmi::{FEATURES}; +use islet_rmm::test_utils::*; + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: u64| { + let feature_reg_index = data as usize; + let _ret = rmi::(&[feature_reg_index]); +}); diff --git a/rmm/fuzz/fuzz_targets/rmi_version_fuzz.rs b/rmm/fuzz/fuzz_targets/rmi_version_fuzz.rs new file mode 100644 index 000000000000..12ad1fe2b3c1 --- /dev/null +++ b/rmm/fuzz/fuzz_targets/rmi_version_fuzz.rs @@ -0,0 +1,11 @@ +#![no_main] + +use islet_rmm::rmi::{VERSION}; +use islet_rmm::test_utils::*; + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: u64| { + let req = data as usize; + let _ret = rmi::(&[req]); +}); diff --git a/rmm/src/asm.rs b/rmm/src/asm.rs index 98b723373b3e..394841f5d2b7 100644 --- a/rmm/src/asm.rs +++ b/rmm/src/asm.rs @@ -1,7 +1,7 @@ use core::arch::asm; pub const SMC_SUCCESS: usize = 0; -#[cfg(any(kani, miri, test))] +#[cfg(any(kani, miri, test, fuzzing))] pub const SMC_ERROR: usize = 1; pub fn smc(cmd: usize, args: &[usize]) -> [usize; 8] { @@ -22,7 +22,7 @@ pub fn smc(cmd: usize, args: &[usize]) -> [usize; 8] { }; put(&mut ret); put(&mut padded_args); - #[cfg(any(kani, miri, test))] + #[cfg(any(kani, miri, test, fuzzing))] if cmd == crate::rmi::gpt::MARK_REALM { use crate::get_granule; use crate::granule::entry::GranuleGpt; @@ -50,7 +50,7 @@ pub fn smc(cmd: usize, args: &[usize]) -> [usize; 8] { } // TODO: support more number of registers than 8 if needed - #[cfg(not(any(kani, miri, test)))] + #[cfg(not(any(kani, miri, test, fuzzing)))] unsafe { asm!( "smc #0x0", diff --git a/rmm/src/exception/trap.rs b/rmm/src/exception/trap.rs index 02c97348e06b..335870aefbef 100644 --- a/rmm/src/exception/trap.rs +++ b/rmm/src/exception/trap.rs @@ -236,7 +236,7 @@ pub extern "C" fn handle_lower_exception( rec.context.simd.is_used = true; unsafe { if rec.context.simd.is_saved { - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] match Syndrome::from(esr) { Syndrome::FPU => simd::restore_fpu(&rec.context.simd.fpu), Syndrome::SVE | Syndrome::SME => { diff --git a/rmm/src/granule/array/entry.rs b/rmm/src/granule/array/entry.rs index c1c1ced03d91..9716c59eb29d 100644 --- a/rmm/src/granule/array/entry.rs +++ b/rmm/src/granule/array/entry.rs @@ -7,14 +7,14 @@ use safe_abstraction::raw_ptr; use spinning_top::{Spinlock, SpinlockGuard}; use vmsa::guard::Content; -#[cfg(not(any(kani, miri, test)))] +#[cfg(not(any(kani, miri, test, fuzzing)))] use crate::granule::{FVP_DRAM0_REGION, FVP_DRAM1_IDX, FVP_DRAM1_REGION}; // Safety: concurrency safety // - For a granule status table that manages granules, it doesn't use a big lock for efficiency. // So, we need to associate "lock" with each granule entry. -#[cfg(not(any(kani, miri, test)))] +#[cfg(not(any(kani, miri, test, fuzzing)))] #[derive(Debug)] pub struct Granule { /// granule state @@ -22,7 +22,7 @@ pub struct Granule { /// granule ref count ref_count: AtomicU8, } -#[cfg(any(kani, miri, test))] +#[cfg(any(kani, miri, test, fuzzing))] // DIFF: `gpt` ghost field is added to track GPT entry's status pub struct Granule { /// granule state @@ -41,7 +41,7 @@ pub enum GranuleGpt { GPT_REALM, } -#[cfg(any(miri, test))] +#[cfg(any(miri, test, fuzzing))] #[derive(Copy, Clone, PartialEq)] pub enum GranuleGpt { GPT_NS, @@ -50,13 +50,13 @@ pub enum GranuleGpt { } impl Granule { - #[cfg(not(any(kani, miri, test)))] + #[cfg(not(any(kani, miri, test, fuzzing)))] fn new() -> Self { let state = GranuleState::Undelegated; let ref_count = AtomicU8::new(0); Granule { state, ref_count } } - #[cfg(any(kani, miri, test))] + #[cfg(any(kani, miri, test, fuzzing))] // DIFF: `state` and `gpt` are filled with non-deterministic values fn new() -> Self { #[cfg(kani)] @@ -80,7 +80,7 @@ impl Granule { } } - #[cfg(any(miri, test))] + #[cfg(any(miri, test, fuzzing))] { Self { state: GranuleState::Undelegated, @@ -106,12 +106,12 @@ impl Granule { self.ref_count.load(Ordering::SeqCst) as usize } - #[cfg(any(kani, miri, test))] + #[cfg(any(kani, miri, test, fuzzing))] pub fn set_gpt(&mut self, gpt: GranuleGpt) { self.gpt = gpt; } - #[cfg(any(kani, miri, test))] + #[cfg(any(kani, miri, test, fuzzing))] pub fn is_valid(&self) -> bool { self.state >= GranuleState::Undelegated && self.state <= GranuleState::RTT && @@ -176,7 +176,7 @@ impl Granule { (entry_addr - table_base) / core::mem::size_of::() } - #[cfg(not(any(kani, miri, test)))] + #[cfg(not(any(kani, miri, test, fuzzing)))] fn index_to_addr(&self) -> usize { let idx = self.index(); if idx < FVP_DRAM1_IDX { @@ -184,14 +184,14 @@ impl Granule { } FVP_DRAM1_REGION.start + ((idx - FVP_DRAM1_IDX) * GRANULE_SIZE) } - #[cfg(any(kani, miri, test))] + #[cfg(any(kani, miri, test, fuzzing))] // DIFF: calculate addr using GRANULE_REGION pub fn index_to_addr(&self) -> usize { use crate::granule::{GRANULE_REGION, GRANULE_STATUS_TABLE_SIZE}; let idx = self.index(); assert!(idx < GRANULE_STATUS_TABLE_SIZE); - #[cfg(any(miri, test))] + #[cfg(any(miri, test, fuzzing))] return crate::test_utils::align_up(unsafe { GRANULE_REGION.as_ptr() as usize + (idx * GRANULE_SIZE) }); @@ -200,7 +200,7 @@ impl Granule { return unsafe { GRANULE_REGION.as_ptr() as usize + (idx * GRANULE_SIZE) }; } - #[cfg(not(any(kani, miri, test)))] + #[cfg(not(any(kani, miri, test, fuzzing)))] fn zeroize(&mut self) { let addr = self.index_to_addr(); @@ -211,7 +211,7 @@ impl Granule { core::ptr::write_bytes(addr as *mut u8, 0x0, GRANULE_SIZE); } } - #[cfg(any(kani, miri, test))] + #[cfg(any(kani, miri, test, fuzzing))] // DIFF: assertion is added to reduce the proof burden // `write_bytes()` uses a small count value fn zeroize(&mut self) { @@ -229,11 +229,11 @@ impl Granule { pub struct Entry(Spinlock); impl Entry { - #[cfg(not(any(kani, miri, test)))] + #[cfg(not(any(kani, miri, test, fuzzing)))] pub fn new() -> Self { Self(Spinlock::new(Granule::new())) } - #[cfg(any(kani, miri, test))] + #[cfg(any(kani, miri, test, fuzzing))] // DIFF: assertion is added to reduce the proof burden pub fn new() -> Self { let granule = Granule::new(); diff --git a/rmm/src/granule/array/mod.rs b/rmm/src/granule/array/mod.rs index 359fad46bf11..f5bb0c7e8e05 100644 --- a/rmm/src/granule/array/mod.rs +++ b/rmm/src/granule/array/mod.rs @@ -24,9 +24,9 @@ pub(super) const FVP_DRAM1_REGION: core::ops::Range = core::ops::Range { pub(super) const FVP_DRAM1_IDX: usize = (FVP_DRAM0_REGION.end - FVP_DRAM0_REGION.start) / GRANULE_SIZE; -#[cfg(any(kani, miri, test))] +#[cfg(any(kani, miri, test, fuzzing))] pub const GRANULE_MEM_SIZE: usize = GRANULE_SIZE * GRANULE_STATUS_TABLE_SIZE; -#[cfg(any(kani, miri, test))] +#[cfg(any(kani, miri, test, fuzzing))] // We model the Host memory as a pre-allocated memory region which // can avoid a false positive related to invalid memory accesses // in model checking. Also, instead of using the same starting @@ -39,7 +39,7 @@ pub const GRANULE_MEM_SIZE: usize = GRANULE_SIZE * GRANULE_STATUS_TABLE_SIZE; // so the last region cannot be utilized. pub static mut GRANULE_REGION: [u8; GRANULE_MEM_SIZE] = [0; GRANULE_MEM_SIZE]; -#[cfg(not(any(kani, miri, test)))] +#[cfg(not(any(kani, miri, test, fuzzing)))] pub fn validate_addr(addr: usize) -> bool { if addr % GRANULE_SIZE != 0 { // if the address is out of range. @@ -53,7 +53,7 @@ pub fn validate_addr(addr: usize) -> bool { } true } -#[cfg(any(kani, miri, test))] +#[cfg(any(kani, miri, test, fuzzing))] // DIFF: check against GRANULE_REGION pub fn validate_addr(addr: usize) -> bool { if addr % GRANULE_SIZE != 0 { @@ -66,7 +66,7 @@ pub fn validate_addr(addr: usize) -> bool { addr >= g_start && addr < g_end } -#[cfg(not(any(kani, miri, test)))] +#[cfg(not(any(kani, miri, test, fuzzing)))] pub fn granule_addr_to_index(addr: usize) -> usize { if FVP_DRAM0_REGION.contains(&addr) { return (addr - FVP_DRAM0_REGION.start) / GRANULE_SIZE; @@ -76,7 +76,7 @@ pub fn granule_addr_to_index(addr: usize) -> usize { } usize::MAX } -#[cfg(any(kani, miri, test))] +#[cfg(any(kani, miri, test, fuzzing))] // DIFF: calculate index using GRANULE_REGION pub fn granule_addr_to_index(addr: usize) -> usize { let g_start = unsafe { GRANULE_REGION.as_ptr() as usize }; @@ -120,11 +120,11 @@ lazy_static! { pub static ref GRANULE_STATUS_TABLE: GranuleStatusTable = GranuleStatusTable::new(); } -#[cfg(not(any(kani, miri, test)))] +#[cfg(not(any(kani, miri, test, fuzzing)))] pub const GRANULE_STATUS_TABLE_SIZE: usize = 0xfc000; // == RMM_MAX_GRANULES #[cfg(kani)] pub const GRANULE_STATUS_TABLE_SIZE: usize = 6; -#[cfg(any(miri, test))] +#[cfg(any(miri, test, fuzzing))] pub const GRANULE_STATUS_TABLE_SIZE: usize = 55; pub struct GranuleStatusTable { diff --git a/rmm/src/lib.rs b/rmm/src/lib.rs index d43b07edc0d7..aede94cc095b 100644 --- a/rmm/src/lib.rs +++ b/rmm/src/lib.rs @@ -4,7 +4,7 @@ #![feature(specialization)] #![warn(rust_2018_idioms)] -#[cfg(not(test))] +#[cfg(not(any(test, fuzzing)))] pub mod allocator; pub mod asm; pub mod config; @@ -18,7 +18,7 @@ pub(crate) mod host; pub mod logger; pub mod mm; pub mod mmio; -#[cfg(not(any(test, kani, miri)))] +#[cfg(not(any(test, kani, miri, fuzzing)))] pub mod panic; pub mod realm; pub mod rec; @@ -28,6 +28,8 @@ pub mod rsi; pub mod stat; #[cfg(any(test, miri))] pub(crate) mod test_utils; +#[cfg(fuzzing)] +pub mod test_utils; #[macro_use] pub mod r#macro; mod measurement; diff --git a/rmm/src/mm/page_table/entry.rs b/rmm/src/mm/page_table/entry.rs index fb87dcd7782a..f383703077e0 100644 --- a/rmm/src/mm/page_table/entry.rs +++ b/rmm/src/mm/page_table/entry.rs @@ -70,7 +70,7 @@ impl page_table::Entry for Entry { fn set(&mut self, addr: PhysAddr, flags: u64) -> Result<(), Error> { self.0.set(addr.as_u64() | flags); - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] unsafe { core::arch::asm!( "dsb ishst", diff --git a/rmm/src/realm/mm/entry.rs b/rmm/src/realm/mm/entry.rs index 3360ebc45218..7b6ba55edc7e 100644 --- a/rmm/src/realm/mm/entry.rs +++ b/rmm/src/realm/mm/entry.rs @@ -59,7 +59,7 @@ impl page_table::Entry for Entry { fn set(&mut self, addr: PhysAddr, flags: u64) -> Result<(), Error> { self.0.set(addr.as_u64() | flags); - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] unsafe { core::arch::asm!( "dsb ishst", diff --git a/rmm/src/realm/mm/stage2_translation.rs b/rmm/src/realm/mm/stage2_translation.rs index 221203f9108a..daf7791dc42f 100644 --- a/rmm/src/realm/mm/stage2_translation.rs +++ b/rmm/src/realm/mm/stage2_translation.rs @@ -394,12 +394,12 @@ impl IPATranslation for Stage2Translation<'_> { if let Ok(_x) = res { match invalidate { Tlbi::LEAF(vmid) => { - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] Self::tlbi_by_vmid_ipa(level, map_addr, vmid); self.dirty = true; } Tlbi::BREAKDOWN(vmid) => { - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] Self::tlbi_by_vmid_ipa_range(level, map_addr, vmid); self.dirty = true; } diff --git a/rmm/src/rec/context.rs b/rmm/src/rec/context.rs index 364041419063..eda3c32cdbac 100644 --- a/rmm/src/rec/context.rs +++ b/rmm/src/rec/context.rs @@ -83,8 +83,9 @@ impl Context { pub unsafe fn into_current(rec: &Rec<'_>) { TPIDR_EL2.set(rec as *const _ as u64); gic::restore_state(rec); + #[cfg(not(fuzzing))] timer::restore_state(rec); - #[cfg(not(any(test, miri)))] + #[cfg(not(any(test, miri, fuzzing)))] simd::restore_state(rec); } @@ -96,8 +97,9 @@ impl Context { /// ensure that this is appropriate in the current execution context. pub unsafe fn from_current(rec: &mut Rec<'_>) { gic::save_state(rec); + #[cfg(not(fuzzing))] timer::save_state(rec); - #[cfg(not(any(test, miri)))] + #[cfg(not(any(test, miri, fuzzing)))] simd::save_state(rec); } } diff --git a/rmm/src/rec/simd.rs b/rmm/src/rec/simd.rs index 2fe07902286a..a03e352cf132 100644 --- a/rmm/src/rec/simd.rs +++ b/rmm/src/rec/simd.rs @@ -84,7 +84,7 @@ lazy_static! { let mut sme_en: bool = false; trace!("Reading simd features"); - #[cfg(not(any(test, miri)))] + #[cfg(not(any(test, miri, fuzzing)))] if ID_AA64PFR0_EL1.is_set(ID_AA64PFR0_EL1::SVE) { trace!("SVE is set"); // Get effective vl @@ -102,7 +102,7 @@ lazy_static! { } // init sme - #[cfg(not(any(test, miri)))] + #[cfg(not(any(test, miri, fuzzing)))] if ID_AA64PFR1_SME_EL1.is_set(ID_AA64PFR1_SME_EL1::SME) { trace!("SME is set"); // Find the architecturally permitted SVL diff --git a/rmm/src/rec/timer.rs b/rmm/src/rec/timer.rs index 49da5e5c4b63..4650aabc7fa7 100644 --- a/rmm/src/rec/timer.rs +++ b/rmm/src/rec/timer.rs @@ -14,6 +14,7 @@ pub fn set_cnthctl(rec: &mut Rec<'_>, val: u64) { timer.cnthctl_el2 = val; } +#[cfg(not(fuzzing))] pub fn restore_state(rec: &Rec<'_>) { let timer = &rec.context.timer; @@ -26,6 +27,7 @@ pub fn restore_state(rec: &Rec<'_>) { CNTHCTL_EL2.set(timer.cnthctl_el2); } +#[cfg(not(fuzzing))] pub fn save_state(rec: &mut Rec<'_>) { let timer = &mut rec.context.timer; diff --git a/rmm/src/rmi/gpt.rs b/rmm/src/rmi/gpt.rs index d0bf3efb1541..cbed0f6faf81 100644 --- a/rmm/src/rmi/gpt.rs +++ b/rmm/src/rmi/gpt.rs @@ -32,7 +32,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { let mut granule = get_granule_if!(addr, GranuleState::Undelegated)?; // Avoid deadlock in get_granule() in smc() on {miri, test} mode - #[cfg(any(miri, test))] + #[cfg(any(miri, test, fuzzing))] core::mem::drop(granule); if smc(MARK_REALM, &[addr])[0] != SMC_SUCCESS { @@ -43,7 +43,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { // `page_table` is currently not reachable in model checking harnesses rmm.page_table.map(addr, true); - #[cfg(any(miri, test))] + #[cfg(any(miri, test, fuzzing))] let mut granule = get_granule_if!(addr, GranuleState::Undelegated)?; set_granule(&mut granule, GranuleState::Delegated).inspect_err(|_| { #[cfg(not(kani))] @@ -62,7 +62,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { let mut granule = get_granule_if!(addr, GranuleState::Delegated)?; // Avoid deadlock in get_granule() in smc() on {miri, test} mode - #[cfg(any(miri, test))] + #[cfg(any(miri, test, fuzzing))] core::mem::drop(granule); if smc(MARK_NONSECURE, &[addr])[0] != SMC_SUCCESS { @@ -72,7 +72,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { ); } - #[cfg(any(miri, test))] + #[cfg(any(miri, test, fuzzing))] let mut granule = get_granule_if!(addr, GranuleState::Delegated)?; #[cfg(not(kani))] diff --git a/rmm/src/rmi/rec/handlers.rs b/rmm/src/rmi/rec/handlers.rs index 86c4bb318767..47f54ce1feba 100644 --- a/rmm/src/rmi/rec/handlers.rs +++ b/rmm/src/rmi/rec/handlers.rs @@ -210,7 +210,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { return Err(Error::RmiErrorRec); } - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] if !crate::rec::gic::validate_state(&run) { return Err(Error::RmiErrorRec); } @@ -220,7 +220,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { do_host_call(arg, ret, rmm, &mut rec, &mut run)?; } - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] crate::rec::gic::receive_state_from_host(&mut rec, &run)?; crate::mmio::emulate_mmio(&mut rec, &run)?; @@ -232,7 +232,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { warn!("TWI(E) in HCR_EL2 is currently fixed to 'no trap'"); } - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] activate_stage2_mmu(&rec); let mut ret_ns; @@ -242,7 +242,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { rec.set_state(RecState::Running); - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] { use crate::rmi::rec::exit::handle_realm_exit; @@ -263,7 +263,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { } } - #[cfg(any(miri, test))] + #[cfg(any(miri, test, fuzzing))] { use crate::test_utils::mock; mock::realm::setup_psci_complete(&mut rec, &mut run); @@ -277,7 +277,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { } } - #[cfg(not(any(miri, test)))] + #[cfg(not(any(miri, test, fuzzing)))] crate::rec::gic::send_state_to_host(&rec, &mut run)?; crate::rec::timer::send_state_to_host(&rec, &mut run)?; diff --git a/scripts/fuzz-coverage.sh b/scripts/fuzz-coverage.sh new file mode 100755 index 000000000000..bfbaadf7a7bb --- /dev/null +++ b/scripts/fuzz-coverage.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +ROOT=$(git rev-parse --show-toplevel) + +FUZZER=$1 +TIMEOUT=$2 + +mkdir -p $ROOT/out + +cd $ROOT/rmm/ +COVERAGE=1 $ROOT/scripts/fuzz.sh $FUZZER -max_total_time=$TIMEOUT + +rm -rf ../code-coverage +grcov . -s . --binary-path $ROOT/out/aarch64-unknown-linux-gnu/fuzz/ -t html --ignore tests/ -o ../code-coverage diff --git a/scripts/fuzz.sh b/scripts/fuzz.sh new file mode 100755 index 000000000000..763143d7ea35 --- /dev/null +++ b/scripts/fuzz.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +ROOT=$(git rev-parse --show-toplevel) + +mkdir -p $ROOT/out + +cd $ROOT/rmm/fuzz + +if [ $? -ne 0 ]; then + exit 1 +fi + +export RUSTFLAGS="-C passes=sancov-module \ + -C llvm-args=-sanitizer-coverage-level=3 \ + -C llvm-args=-sanitizer-coverage-inline-8bit-counters \ + -C llvm-args=-sanitizer-coverage-trace-compares \ + --cfg fuzzing \ + -A warnings" + +# Used by fuzz-coverage.sh to enable coverage mode +if [ "$COVERAGE" == "1" ]; then + export RUSTFLAGS="$RUSTFLAGS -C instrument-coverage" +fi + +if [ "$(uname --machine)" == "aarch64" ]; then + # Note: ASAN does not work in QEMU userspace emulation + # Hence, it is included only in this case. + + export RUSTFLAGS="$RUSTFLAGS -Z sanitizer=address" + cargo run --profile fuzz --bin $1 -- ${@:2} +else + cargo build --profile fuzz --bin $1 + + if [ $? -ne 0 ]; then + exit 1 + fi + + if ! which qemu-aarch64 &>/dev/null; then + sudo apt-get update + sudo apt-get install -y -qq --no-install-recommends qemu-user + fi + + qemu-aarch64 \ + -E "LD_LIBRARY_PATH=../../assets/toolchain/aarch64-none-linux-gnu/aarch64-none-linux-gnu/lib64/:../../assets/toolchain/aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/lib64/" \ + -L ../../assets/toolchain/aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/ \ + ../../out/aarch64-unknown-linux-gnu/fuzz/$1 -- ${@:2} +fi