diff --git a/fuzzers/qemu_coverage/src/fuzzer.rs b/fuzzers/qemu_coverage/src/fuzzer.rs index 90087497e6..4632e75fd4 100644 --- a/fuzzers/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/qemu_coverage/src/fuzzer.rs @@ -28,7 +28,7 @@ use libafl_bolts::{ }; use libafl_qemu::{ drcov::QemuDrCovHelper, elf::EasyElf, emu::Emulator, ArchExtras, CallingConvention, GuestAddr, - GuestReg, MmapPerms, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs, + GuestReg, MmapPerms, QemuExecutor, QemuHooks, QemuInstrumentationAddressRangeFilter, Regs, }; use rangemap::RangeMap; @@ -238,7 +238,7 @@ pub fn fuzz() { let mut hooks = QemuHooks::new( emu.clone(), tuple_list!(QemuDrCovHelper::new( - QemuInstrumentationFilter::None, + QemuInstrumentationAddressRangeFilter::None, rangemap, PathBuf::from(coverage), false, diff --git a/fuzzers/qemu_launcher/src/client.rs b/fuzzers/qemu_launcher/src/client.rs index b8f73ade55..49c71db51f 100644 --- a/fuzzers/qemu_launcher/src/client.rs +++ b/fuzzers/qemu_launcher/src/client.rs @@ -15,7 +15,7 @@ use libafl_qemu::{ cmplog::QemuCmpLogHelper, edges::QemuEdgeCoverageHelper, elf::EasyElf, - ArchExtras, Emulator, GuestAddr, QemuInstrumentationFilter, + ArchExtras, Emulator, GuestAddr, QemuInstrumentationAddressRangeFilter, }; use crate::{instance::Instance, options::FuzzerOptions}; @@ -59,7 +59,10 @@ impl<'a> Client<'a> { Ok(start_pc) } - fn coverage_filter(&self, emu: &Emulator) -> Result { + fn coverage_filter( + &self, + emu: &Emulator, + ) -> Result { /* Conversion is required on 32-bit targets, but not on 64-bit ones */ if let Some(includes) = &self.options.include { #[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))] @@ -70,7 +73,7 @@ impl<'a> Client<'a> { end: x.end.into(), }) .collect::>>(); - Ok(QemuInstrumentationFilter::AllowList(rules)) + Ok(QemuInstrumentationAddressRangeFilter::AllowList(rules)) } else if let Some(excludes) = &self.options.exclude { #[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))] let rules = excludes @@ -80,14 +83,16 @@ impl<'a> Client<'a> { end: x.end.into(), }) .collect::>>(); - Ok(QemuInstrumentationFilter::DenyList(rules)) + Ok(QemuInstrumentationAddressRangeFilter::DenyList(rules)) } else { let mut elf_buffer = Vec::new(); let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?; let range = elf .get_section(".text", emu.load_addr()) .ok_or_else(|| Error::key_not_found("Failed to find .text section"))?; - Ok(QemuInstrumentationFilter::AllowList(vec![range])) + Ok(QemuInstrumentationAddressRangeFilter::AllowList(vec![ + range, + ])) } } diff --git a/libafl_qemu/libafl_qemu_build/src/build.rs b/libafl_qemu/libafl_qemu_build/src/build.rs index fdcee927ae..dd8e611b05 100644 --- a/libafl_qemu/libafl_qemu_build/src/build.rs +++ b/libafl_qemu/libafl_qemu_build/src/build.rs @@ -8,7 +8,7 @@ use which::which; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; -const QEMU_REVISION: &str = "32206d23c33a55c9e519e4ae67038ab27d713a24"; +const QEMU_REVISION: &str = "c92d7c2ef66811278e8d665d4aec57661c980186"; fn build_dep_check(tools: &[&str]) { for tool in tools { diff --git a/libafl_qemu/src/asan.rs b/libafl_qemu/src/asan.rs index 4a25023b0f..3da6ff4f13 100644 --- a/libafl_qemu/src/asan.rs +++ b/libafl_qemu/src/asan.rs @@ -21,7 +21,10 @@ use rangemap::RangeMap; use crate::{ calls::FullBacktraceCollector, emu::{EmuError, Emulator, MemAccessInfo, SyscallHookResult}, - helper::{HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, + helper::{ + HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple, + QemuInstrumentationAddressRangeFilter, + }, hooks::{Hook, QemuHooks}, snapshot::QemuSnapshotHelper, GuestAddr, Regs, @@ -734,7 +737,7 @@ pub struct QemuAsanHelper { detect_leaks: bool, empty: bool, rt: Pin>, - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, } impl QemuAsanHelper { @@ -742,7 +745,7 @@ impl QemuAsanHelper { pub fn default(rt: Pin>) -> Self { Self::new( rt, - QemuInstrumentationFilter::None, + QemuInstrumentationAddressRangeFilter::None, QemuAsanOptions::Snapshot, ) } @@ -750,7 +753,7 @@ impl QemuAsanHelper { #[must_use] pub fn new( mut rt: Pin>, - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, options: QemuAsanOptions, ) -> Self { assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)"); @@ -773,7 +776,7 @@ impl QemuAsanHelper { #[must_use] pub fn with_error_callback( mut rt: Pin>, - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, error_callback: AsanErrorCallback, options: QemuAsanOptions, ) -> Self { @@ -798,7 +801,7 @@ impl QemuAsanHelper { #[must_use] pub fn with_asan_report( rt: Pin>, - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, options: QemuAsanOptions, ) -> Self { Self::with_error_callback(rt, filter, Box::new(asan_report), options) @@ -922,12 +925,12 @@ impl QemuAsanHelper { } } -impl HasInstrumentationFilter for QemuAsanHelper { - fn filter(&self) -> &QemuInstrumentationFilter { +impl HasInstrumentationFilter for QemuAsanHelper { + fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } - fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { + fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter { &mut self.filter } } diff --git a/libafl_qemu/src/calls.rs b/libafl_qemu/src/calls.rs index ec8ea05465..f024260c6c 100644 --- a/libafl_qemu/src/calls.rs +++ b/libafl_qemu/src/calls.rs @@ -12,7 +12,10 @@ use thread_local::ThreadLocal; use crate::{ capstone, emu::{ArchExtras, Emulator}, - helper::{HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, + helper::{ + HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple, + QemuInstrumentationAddressRangeFilter, + }, hooks::{Hook, QemuHooks}, GuestAddr, }; @@ -215,7 +218,7 @@ pub struct QemuCallTracerHelper where T: CallTraceCollectorTuple, { - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, cs: Capstone, collectors: Option, } @@ -225,7 +228,7 @@ where T: CallTraceCollectorTuple, { #[must_use] - pub fn new(filter: QemuInstrumentationFilter, collectors: T) -> Self { + pub fn new(filter: QemuInstrumentationAddressRangeFilter, collectors: T) -> Self { Self { filter, cs: capstone().detail(true).build().unwrap(), @@ -380,15 +383,15 @@ where } } -impl HasInstrumentationFilter for QemuCallTracerHelper +impl HasInstrumentationFilter for QemuCallTracerHelper where T: CallTraceCollectorTuple, { - fn filter(&self) -> &QemuInstrumentationFilter { + fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } - fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { + fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter { &mut self.filter } } diff --git a/libafl_qemu/src/cmplog.rs b/libafl_qemu/src/cmplog.rs index 53de83b554..07499eab8d 100644 --- a/libafl_qemu/src/cmplog.rs +++ b/libafl_qemu/src/cmplog.rs @@ -18,7 +18,8 @@ use crate::{ }; use crate::{ helper::{ - hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter, + hash_me, HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple, + QemuInstrumentationAddressRangeFilter, }, hooks::{Hook, QemuHooks}, GuestAddr, @@ -48,12 +49,12 @@ libafl_bolts::impl_serdeany!(QemuCmpsMapMetadata); #[derive(Debug)] pub struct QemuCmpLogHelper { - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, } impl QemuCmpLogHelper { #[must_use] - pub fn new(filter: QemuInstrumentationFilter) -> Self { + pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { filter } } @@ -65,16 +66,16 @@ impl QemuCmpLogHelper { impl Default for QemuCmpLogHelper { fn default() -> Self { - Self::new(QemuInstrumentationFilter::None) + Self::new(QemuInstrumentationAddressRangeFilter::None) } } -impl HasInstrumentationFilter for QemuCmpLogHelper { - fn filter(&self) -> &QemuInstrumentationFilter { +impl HasInstrumentationFilter for QemuCmpLogHelper { + fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } - fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { + fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter { &mut self.filter } } @@ -99,12 +100,12 @@ where #[derive(Debug)] pub struct QemuCmpLogChildHelper { - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, } impl QemuCmpLogChildHelper { #[must_use] - pub fn new(filter: QemuInstrumentationFilter) -> Self { + pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { filter } } @@ -116,7 +117,7 @@ impl QemuCmpLogChildHelper { impl Default for QemuCmpLogChildHelper { fn default() -> Self { - Self::new(QemuInstrumentationFilter::None) + Self::new(QemuInstrumentationAddressRangeFilter::None) } } @@ -219,14 +220,14 @@ pub extern "C" fn trace_cmp8_cmplog(_: *const (), id: u64, v0: u64, v1: u64) { #[cfg(emulation_mode = "usermode")] #[derive(Debug)] pub struct QemuCmpLogRoutinesHelper { - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, cs: Capstone, } #[cfg(emulation_mode = "usermode")] impl QemuCmpLogRoutinesHelper { #[must_use] - pub fn new(filter: QemuInstrumentationFilter) -> Self { + pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { filter, cs: capstone().detail(true).build().unwrap(), @@ -348,12 +349,12 @@ impl QemuCmpLogRoutinesHelper { } #[cfg(emulation_mode = "usermode")] -impl HasInstrumentationFilter for QemuCmpLogRoutinesHelper { - fn filter(&self) -> &QemuInstrumentationFilter { +impl HasInstrumentationFilter for QemuCmpLogRoutinesHelper { + fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } - fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { + fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter { &mut self.filter } } diff --git a/libafl_qemu/src/drcov.rs b/libafl_qemu/src/drcov.rs index 3cd2b51532..10f0fd1200 100644 --- a/libafl_qemu/src/drcov.rs +++ b/libafl_qemu/src/drcov.rs @@ -10,7 +10,10 @@ use serde::{Deserialize, Serialize}; use crate::{ emu::{GuestAddr, GuestUsize}, - helper::{HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, + helper::{ + HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple, + QemuInstrumentationAddressRangeFilter, + }, hooks::{Hook, QemuHooks}, Emulator, }; @@ -39,7 +42,7 @@ libafl_bolts::impl_serdeany!(QemuDrCovMetadata); #[derive(Debug)] pub struct QemuDrCovHelper { - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, module_mapping: RangeMap, filename: PathBuf, full_trace: bool, @@ -50,7 +53,7 @@ impl QemuDrCovHelper { #[must_use] #[allow(clippy::let_underscore_untyped)] pub fn new( - filter: QemuInstrumentationFilter, + filter: QemuInstrumentationAddressRangeFilter, module_mapping: RangeMap, filename: PathBuf, full_trace: bool, @@ -75,12 +78,12 @@ impl QemuDrCovHelper { } } -impl HasInstrumentationFilter for QemuDrCovHelper { - fn filter(&self) -> &QemuInstrumentationFilter { +impl HasInstrumentationFilter for QemuDrCovHelper { + fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { &self.filter } - fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { + fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter { &mut self.filter } } diff --git a/libafl_qemu/src/edges.rs b/libafl_qemu/src/edges.rs index 8049f5ec0d..512d699ca4 100644 --- a/libafl_qemu/src/edges.rs +++ b/libafl_qemu/src/edges.rs @@ -11,10 +11,14 @@ use serde::{Deserialize, Serialize}; use crate::{ emu::GuestAddr, helper::{ - hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter, + hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple, + QemuInstrumentationAddressRangeFilter, }, hooks::{Hook, QemuHooks}, + IsFilter, }; +#[cfg(emulation_mode = "systemmode")] +use crate::{helper::QemuInstrumentationPagingFilter, GuestPhysAddr}; #[cfg_attr( any(not(feature = "serdeany_autoreg"), miri), @@ -38,48 +42,112 @@ impl QemuEdgesMapMetadata { libafl_bolts::impl_serdeany!(QemuEdgesMapMetadata); +#[cfg(emulation_mode = "usermode")] #[derive(Debug)] pub struct QemuEdgeCoverageHelper { - filter: QemuInstrumentationFilter, + address_filter: QemuInstrumentationAddressRangeFilter, use_hitcounts: bool, } +#[cfg(emulation_mode = "systemmode")] +#[derive(Debug)] +pub struct QemuEdgeCoverageHelper { + address_filter: QemuInstrumentationAddressRangeFilter, + paging_filter: QemuInstrumentationPagingFilter, + use_hitcounts: bool, +} + +#[cfg(emulation_mode = "usermode")] impl QemuEdgeCoverageHelper { #[must_use] - pub fn new(filter: QemuInstrumentationFilter) -> Self { + pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { - filter, + address_filter, use_hitcounts: true, } } #[must_use] - pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self { + pub fn without_hitcounts(address_filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { - filter, + address_filter, use_hitcounts: false, } } #[must_use] pub fn must_instrument(&self, addr: GuestAddr) -> bool { - self.filter.allowed(addr) + self.address_filter.allowed(addr) } } +#[cfg(emulation_mode = "systemmode")] +impl QemuEdgeCoverageHelper { + #[must_use] + pub fn new( + address_filter: QemuInstrumentationAddressRangeFilter, + paging_filter: QemuInstrumentationPagingFilter, + ) -> Self { + Self { + address_filter, + paging_filter, + use_hitcounts: true, + } + } + + #[must_use] + pub fn without_hitcounts( + address_filter: QemuInstrumentationAddressRangeFilter, + paging_filter: QemuInstrumentationPagingFilter, + ) -> Self { + Self { + address_filter, + paging_filter, + use_hitcounts: false, + } + } + + #[must_use] + pub fn must_instrument(&self, addr: GuestAddr, paging_id: Option) -> bool { + self.address_filter.allowed(addr) && self.paging_filter.allowed(paging_id) + } +} + +#[cfg(emulation_mode = "usermode")] impl Default for QemuEdgeCoverageHelper { fn default() -> Self { - Self::new(QemuInstrumentationFilter::None) + Self::new(QemuInstrumentationAddressRangeFilter::None) } } -impl HasInstrumentationFilter for QemuEdgeCoverageHelper { - fn filter(&self) -> &QemuInstrumentationFilter { - &self.filter +#[cfg(emulation_mode = "systemmode")] +impl Default for QemuEdgeCoverageHelper { + fn default() -> Self { + Self::new( + QemuInstrumentationAddressRangeFilter::None, + QemuInstrumentationPagingFilter::None, + ) + } +} + +impl HasInstrumentationFilter for QemuEdgeCoverageHelper { + fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { + &self.address_filter } - fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { - &mut self.filter + fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter { + &mut self.address_filter + } +} + +#[cfg(emulation_mode = "systemmode")] +impl HasInstrumentationFilter for QemuEdgeCoverageHelper { + fn filter(&self) -> &QemuInstrumentationPagingFilter { + &self.paging_filter + } + + fn filter_mut(&mut self) -> &mut QemuInstrumentationPagingFilter { + &mut self.paging_filter } } @@ -121,48 +189,114 @@ where pub type QemuCollidingEdgeCoverageHelper = QemuEdgeCoverageChildHelper; +#[cfg(emulation_mode = "usermode")] #[derive(Debug)] pub struct QemuEdgeCoverageChildHelper { - filter: QemuInstrumentationFilter, + address_filter: QemuInstrumentationAddressRangeFilter, use_hitcounts: bool, } +#[cfg(emulation_mode = "systemmode")] +#[derive(Debug)] +pub struct QemuEdgeCoverageChildHelper { + address_filter: QemuInstrumentationAddressRangeFilter, + paging_filter: QemuInstrumentationPagingFilter, + use_hitcounts: bool, +} + +#[cfg(emulation_mode = "usermode")] impl QemuEdgeCoverageChildHelper { #[must_use] - pub fn new(filter: QemuInstrumentationFilter) -> Self { + pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { - filter, + address_filter, use_hitcounts: true, } } #[must_use] - pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self { + pub fn without_hitcounts(address_filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { - filter, + address_filter, use_hitcounts: false, } } #[must_use] pub fn must_instrument(&self, addr: GuestAddr) -> bool { - self.filter.allowed(addr) + self.address_filter.allowed(addr) } } +#[cfg(emulation_mode = "systemmode")] +impl QemuEdgeCoverageChildHelper { + #[must_use] + pub fn new( + address_filter: QemuInstrumentationAddressRangeFilter, + paging_filter: QemuInstrumentationPagingFilter, + ) -> Self { + Self { + address_filter, + paging_filter, + use_hitcounts: true, + } + } + + #[must_use] + pub fn without_hitcounts( + address_filter: QemuInstrumentationAddressRangeFilter, + paging_filter: QemuInstrumentationPagingFilter, + ) -> Self { + Self { + address_filter, + paging_filter, + use_hitcounts: false, + } + } + + #[must_use] + pub fn must_instrument(&self, addr: GuestAddr, paging_id: Option) -> bool { + self.address_filter.allowed(addr) && self.paging_filter.allowed(paging_id) + } +} + +#[cfg(emulation_mode = "usermode")] impl Default for QemuEdgeCoverageChildHelper { fn default() -> Self { - Self::new(QemuInstrumentationFilter::None) + Self::new(QemuInstrumentationAddressRangeFilter::None) } } -impl HasInstrumentationFilter for QemuEdgeCoverageChildHelper { - fn filter(&self) -> &QemuInstrumentationFilter { - &self.filter +#[cfg(emulation_mode = "systemmode")] +impl Default for QemuEdgeCoverageChildHelper { + fn default() -> Self { + Self::new( + QemuInstrumentationAddressRangeFilter::None, + QemuInstrumentationPagingFilter::None, + ) + } +} + +impl HasInstrumentationFilter + for QemuEdgeCoverageChildHelper +{ + fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { + &self.address_filter } - fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { - &mut self.filter + fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter { + &mut self.address_filter + } +} + +#[cfg(emulation_mode = "systemmode")] +impl HasInstrumentationFilter for QemuEdgeCoverageChildHelper { + fn filter(&self) -> &QemuInstrumentationPagingFilter { + &self.paging_filter + } + + fn filter_mut(&mut self) -> &mut QemuInstrumentationPagingFilter { + &mut self.paging_filter } } @@ -191,48 +325,114 @@ where } } +#[cfg(emulation_mode = "usermode")] #[derive(Debug)] pub struct QemuEdgeCoverageClassicHelper { - filter: QemuInstrumentationFilter, + address_filter: QemuInstrumentationAddressRangeFilter, use_hitcounts: bool, } +#[cfg(emulation_mode = "systemmode")] +#[derive(Debug)] +pub struct QemuEdgeCoverageClassicHelper { + address_filter: QemuInstrumentationAddressRangeFilter, + paging_filter: QemuInstrumentationPagingFilter, + use_hitcounts: bool, +} + +#[cfg(emulation_mode = "usermode")] impl QemuEdgeCoverageClassicHelper { #[must_use] - pub fn new(filter: QemuInstrumentationFilter) -> Self { + pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { - filter, + address_filter, use_hitcounts: true, } } #[must_use] - pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self { + pub fn without_hitcounts(address_filter: QemuInstrumentationAddressRangeFilter) -> Self { Self { - filter, + address_filter, use_hitcounts: false, } } #[must_use] pub fn must_instrument(&self, addr: GuestAddr) -> bool { - self.filter.allowed(addr) + self.address_filter.allowed(addr) } } +#[cfg(emulation_mode = "systemmode")] +impl QemuEdgeCoverageClassicHelper { + #[must_use] + pub fn new( + address_filter: QemuInstrumentationAddressRangeFilter, + paging_filter: QemuInstrumentationPagingFilter, + ) -> Self { + Self { + address_filter, + paging_filter, + use_hitcounts: true, + } + } + + #[must_use] + pub fn without_hitcounts( + address_filter: QemuInstrumentationAddressRangeFilter, + paging_filter: QemuInstrumentationPagingFilter, + ) -> Self { + Self { + address_filter, + paging_filter, + use_hitcounts: false, + } + } + + #[must_use] + pub fn must_instrument(&self, addr: GuestAddr, paging_id: Option) -> bool { + self.address_filter.allowed(addr) && self.paging_filter.allowed(paging_id) + } +} + +#[cfg(emulation_mode = "usermode")] impl Default for QemuEdgeCoverageClassicHelper { fn default() -> Self { - Self::new(QemuInstrumentationFilter::None) + Self::new(QemuInstrumentationAddressRangeFilter::None) } } -impl HasInstrumentationFilter for QemuEdgeCoverageClassicHelper { - fn filter(&self) -> &QemuInstrumentationFilter { - &self.filter +#[cfg(emulation_mode = "systemmode")] +impl Default for QemuEdgeCoverageClassicHelper { + fn default() -> Self { + Self::new( + QemuInstrumentationAddressRangeFilter::None, + QemuInstrumentationPagingFilter::None, + ) + } +} + +impl HasInstrumentationFilter + for QemuEdgeCoverageClassicHelper +{ + fn filter(&self) -> &QemuInstrumentationAddressRangeFilter { + &self.address_filter } - fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { - &mut self.filter + fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter { + &mut self.address_filter + } +} + +#[cfg(emulation_mode = "systemmode")] +impl HasInstrumentationFilter for QemuEdgeCoverageClassicHelper { + fn filter(&self) -> &QemuInstrumentationPagingFilter { + &self.paging_filter + } + + fn filter_mut(&mut self) -> &mut QemuInstrumentationPagingFilter { + &mut self.paging_filter } } @@ -277,8 +477,24 @@ where QT: QemuHelperTuple, { if let Some(h) = hooks.helpers().match_first_type::() { - if !h.must_instrument(src) && !h.must_instrument(dest) { - return None; + #[cfg(emulation_mode = "usermode")] + { + if !h.must_instrument(src) && !h.must_instrument(dest) { + return None; + } + } + + #[cfg(emulation_mode = "systemmode")] + { + let paging_id = hooks + .emulator() + .current_cpu() + .map(|cpu| cpu.get_current_paging_id()) + .flatten(); + + if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) { + return None; + } } } let state = state.expect("The gen_unique_edge_ids hook works only for in-process fuzzing"); @@ -339,9 +555,23 @@ where .helpers() .match_first_type::() { + #[cfg(emulation_mode = "usermode")] if !h.must_instrument(src) && !h.must_instrument(dest) { return None; } + + #[cfg(emulation_mode = "systemmode")] + { + let paging_id = hooks + .emulator() + .current_cpu() + .map(|cpu| cpu.get_current_paging_id()) + .flatten(); + + if !h.must_instrument(src, paging_id) && !h.must_instrument(dest, paging_id) { + return None; + } + } } // GuestAddress is u32 for 32 bit guests #[allow(clippy::unnecessary_cast)] @@ -391,8 +621,23 @@ where .helpers() .match_first_type::() { - if !h.must_instrument(pc) { - return None; + #[cfg(emulation_mode = "usermode")] + { + if !h.must_instrument(pc) { + return None; + } + } + #[cfg(emulation_mode = "systemmode")] + { + let paging_id = hooks + .emulator() + .current_cpu() + .map(|cpu| cpu.get_current_paging_id()) + .flatten(); + + if !h.must_instrument(pc, paging_id) { + return None; + } } } // GuestAddress is u32 for 32 bit guests diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index f0e87ed55d..c97fc9eaaf 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -10,11 +10,8 @@ use core::{ #[cfg(emulation_mode = "usermode")] use std::cell::OnceCell; #[cfg(emulation_mode = "systemmode")] -use std::{ - ffi::{CStr, CString}, - ptr::null_mut, -}; -use std::{slice::from_raw_parts, str::from_utf8_unchecked}; +use std::{ffi::CStr, ptr::null_mut}; +use std::{ffi::CString, ptr, slice::from_raw_parts, str::from_utf8_unchecked}; #[cfg(emulation_mode = "usermode")] use libc::c_int; @@ -393,6 +390,9 @@ extern "C" { data: *const (), ); fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); + + #[cfg(emulation_mode = "systemmode")] + fn libafl_qemu_current_paging_id(cpu: CPUStatePtr) -> GuestPhysAddr; } #[cfg(emulation_mode = "usermode")] @@ -589,6 +589,18 @@ impl CPU { } } + #[cfg(emulation_mode = "systemmode")] + #[must_use] + pub fn get_current_paging_id(&self) -> Option { + let paging_id = unsafe { libafl_qemu_current_paging_id(self.ptr) }; + + if paging_id == 0 { + None + } else { + Some(paging_id) + } + } + // TODO expose tlb_set_dirty and tlb_reset_dirty /// Write a value to a guest address. @@ -945,8 +957,12 @@ impl Emulator { #[allow(clippy::cast_possible_wrap)] let argc = argc as i32; - let args: Vec = args.iter().map(|x| x.clone() + "\0").collect(); - let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); + let args: Vec = args + .iter() + .map(|x| CString::new(x.clone()).unwrap()) + .collect(); + let mut argv: Vec<*const u8> = args.iter().map(|x| x.as_ptr() as *const u8).collect(); + argv.push(ptr::null()); // argv is always null terminated. let env_strs: Vec = env .iter() .map(|(k, v)| format!("{}={}\0", &k, &v)) diff --git a/libafl_qemu/src/helper.rs b/libafl_qemu/src/helper.rs index 707d408cf4..ab421085da 100644 --- a/libafl_qemu/src/helper.rs +++ b/libafl_qemu/src/helper.rs @@ -1,4 +1,5 @@ use core::{fmt::Debug, ops::Range}; +use std::{collections::HashSet, hash}; use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple}; use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType}; @@ -6,6 +7,7 @@ use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType}; use crate::{ emu::{Emulator, GuestAddr}, hooks::QemuHooks, + GuestPhysAddr, }; /// A helper for `libafl_qemu`. @@ -143,49 +145,83 @@ where } } -#[derive(Debug, PartialEq)] -pub enum QemuInstrumentationFilter { - AllowList(Vec>), - DenyList(Vec>), +#[derive(Debug)] +pub enum QemuFilterList { + AllowList(T), + DenyList(T), None, } -impl QemuInstrumentationFilter { - #[must_use] - pub fn allowed(&self, addr: GuestAddr) -> bool { +impl IsFilter for QemuFilterList +where + T: IsFilter, +{ + type FilterParameter = T::FilterParameter; + + fn allowed(&self, filter_parameter: Self::FilterParameter) -> bool { match self { - QemuInstrumentationFilter::AllowList(l) => { - for rng in l { - if rng.contains(&addr) { - return true; - } - } - false - } - QemuInstrumentationFilter::DenyList(l) => { - for rng in l { - if rng.contains(&addr) { - return false; - } - } - true - } - QemuInstrumentationFilter::None => true, + QemuFilterList::AllowList(allow_list) => allow_list.allowed(filter_parameter), + QemuFilterList::DenyList(deny_list) => !deny_list.allowed(filter_parameter), + QemuFilterList::None => true, } } } -pub trait HasInstrumentationFilter { - fn filter(&self) -> &QemuInstrumentationFilter; +pub type QemuInstrumentationPagingFilter = QemuFilterList>; - fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter; +impl IsFilter for HashSet { + type FilterParameter = Option; - fn update_filter(&mut self, filter: QemuInstrumentationFilter, emu: &Emulator) { + fn allowed(&self, paging_id: Self::FilterParameter) -> bool { + paging_id.is_some_and(|pid| self.contains(&pid)) + } +} + +pub type QemuInstrumentationAddressRangeFilter = QemuFilterList>>; + +impl IsFilter for Vec> { + type FilterParameter = GuestAddr; + + fn allowed(&self, addr: Self::FilterParameter) -> bool { + for rng in self { + if rng.contains(&addr) { + return true; + } + } + false + } +} + +pub trait HasInstrumentationFilter +where + F: IsFilter, +{ + fn filter(&self) -> &F; + + fn filter_mut(&mut self) -> &mut F; + + fn update_filter(&mut self, filter: F, emu: &Emulator) { *self.filter_mut() = filter; emu.flush_jit(); } } +pub trait IsFilter: Debug { + type FilterParameter; + + fn allowed(&self, filter_parameter: Self::FilterParameter) -> bool; +} + +pub trait IsAddressFilter: IsFilter {} + +#[cfg(emulation_mode = "systemmode")] +pub trait IsPagingFilter: IsFilter> {} + +#[cfg(emulation_mode = "systemmode")] +impl IsPagingFilter for QemuInstrumentationPagingFilter {} + +impl IsAddressFilter for QemuInstrumentationAddressRangeFilter {} + #[must_use] pub fn hash_me(mut x: u64) -> u64 { x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0; diff --git a/libafl_qemu/src/hooks.rs b/libafl_qemu/src/hooks.rs index 12ee0ebd0f..3517280d2f 100644 --- a/libafl_qemu/src/hooks.rs +++ b/libafl_qemu/src/hooks.rs @@ -87,7 +87,9 @@ macro_rules! hook_to_repr { } static mut QEMU_HOOKS_PTR: *const c_void = ptr::null(); -unsafe fn get_qemu_hooks<'a, QT, S>() -> &'a mut QemuHooks + +#[must_use] +pub unsafe fn get_qemu_hooks<'a, QT, S>() -> &'a mut QemuHooks where S: UsesInput, QT: QemuHelperTuple,