QEMU filtering rework + paging filtering (#1705)

* Added paging filtering.
Reworked address range filtering to fit with new generic code.

* Fix: renamed remaining QemuInstrumentationFilter instances.

* Fix: clippy + format

* Updated qemu-libafl-bridge

* Fix QEMU userspace crash handler (#1706)

* Fix QEMU userspace crash handler

* no_std

* libafl_cc custom llvm_config lookup for solaris/illumos (#1708)

* fix simd (#1709)

* Updated qemu

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
Co-authored-by: David CARLIER <devnexen@gmail.com>
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
Romain Malmain 2023-12-21 15:51:14 +01:00 committed by GitHub
parent ea61b79012
commit 830faec95f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 435 additions and 121 deletions

View File

@ -28,7 +28,7 @@ use libafl_bolts::{
}; };
use libafl_qemu::{ use libafl_qemu::{
drcov::QemuDrCovHelper, elf::EasyElf, emu::Emulator, ArchExtras, CallingConvention, GuestAddr, drcov::QemuDrCovHelper, elf::EasyElf, emu::Emulator, ArchExtras, CallingConvention, GuestAddr,
GuestReg, MmapPerms, QemuExecutor, QemuHooks, QemuInstrumentationFilter, Regs, GuestReg, MmapPerms, QemuExecutor, QemuHooks, QemuInstrumentationAddressRangeFilter, Regs,
}; };
use rangemap::RangeMap; use rangemap::RangeMap;
@ -238,7 +238,7 @@ pub fn fuzz() {
let mut hooks = QemuHooks::new( let mut hooks = QemuHooks::new(
emu.clone(), emu.clone(),
tuple_list!(QemuDrCovHelper::new( tuple_list!(QemuDrCovHelper::new(
QemuInstrumentationFilter::None, QemuInstrumentationAddressRangeFilter::None,
rangemap, rangemap,
PathBuf::from(coverage), PathBuf::from(coverage),
false, false,

View File

@ -15,7 +15,7 @@ use libafl_qemu::{
cmplog::QemuCmpLogHelper, cmplog::QemuCmpLogHelper,
edges::QemuEdgeCoverageHelper, edges::QemuEdgeCoverageHelper,
elf::EasyElf, elf::EasyElf,
ArchExtras, Emulator, GuestAddr, QemuInstrumentationFilter, ArchExtras, Emulator, GuestAddr, QemuInstrumentationAddressRangeFilter,
}; };
use crate::{instance::Instance, options::FuzzerOptions}; use crate::{instance::Instance, options::FuzzerOptions};
@ -59,7 +59,10 @@ impl<'a> Client<'a> {
Ok(start_pc) Ok(start_pc)
} }
fn coverage_filter(&self, emu: &Emulator) -> Result<QemuInstrumentationFilter, Error> { fn coverage_filter(
&self,
emu: &Emulator,
) -> Result<QemuInstrumentationAddressRangeFilter, Error> {
/* Conversion is required on 32-bit targets, but not on 64-bit ones */ /* Conversion is required on 32-bit targets, but not on 64-bit ones */
if let Some(includes) = &self.options.include { if let Some(includes) = &self.options.include {
#[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))] #[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))]
@ -70,7 +73,7 @@ impl<'a> Client<'a> {
end: x.end.into(), end: x.end.into(),
}) })
.collect::<Vec<Range<GuestAddr>>>(); .collect::<Vec<Range<GuestAddr>>>();
Ok(QemuInstrumentationFilter::AllowList(rules)) Ok(QemuInstrumentationAddressRangeFilter::AllowList(rules))
} else if let Some(excludes) = &self.options.exclude { } else if let Some(excludes) = &self.options.exclude {
#[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))] #[cfg_attr(target_pointer_width = "64", allow(clippy::useless_conversion))]
let rules = excludes let rules = excludes
@ -80,14 +83,16 @@ impl<'a> Client<'a> {
end: x.end.into(), end: x.end.into(),
}) })
.collect::<Vec<Range<GuestAddr>>>(); .collect::<Vec<Range<GuestAddr>>>();
Ok(QemuInstrumentationFilter::DenyList(rules)) Ok(QemuInstrumentationAddressRangeFilter::DenyList(rules))
} else { } else {
let mut elf_buffer = Vec::new(); let mut elf_buffer = Vec::new();
let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?; let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer)?;
let range = elf let range = elf
.get_section(".text", emu.load_addr()) .get_section(".text", emu.load_addr())
.ok_or_else(|| Error::key_not_found("Failed to find .text section"))?; .ok_or_else(|| Error::key_not_found("Failed to find .text section"))?;
Ok(QemuInstrumentationFilter::AllowList(vec![range])) Ok(QemuInstrumentationAddressRangeFilter::AllowList(vec![
range,
]))
} }
} }

View File

@ -8,7 +8,7 @@ use which::which;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "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]) { fn build_dep_check(tools: &[&str]) {
for tool in tools { for tool in tools {

View File

@ -21,7 +21,10 @@ use rangemap::RangeMap;
use crate::{ use crate::{
calls::FullBacktraceCollector, calls::FullBacktraceCollector,
emu::{EmuError, Emulator, MemAccessInfo, SyscallHookResult}, emu::{EmuError, Emulator, MemAccessInfo, SyscallHookResult},
helper::{HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
QemuInstrumentationAddressRangeFilter,
},
hooks::{Hook, QemuHooks}, hooks::{Hook, QemuHooks},
snapshot::QemuSnapshotHelper, snapshot::QemuSnapshotHelper,
GuestAddr, Regs, GuestAddr, Regs,
@ -734,7 +737,7 @@ pub struct QemuAsanHelper {
detect_leaks: bool, detect_leaks: bool,
empty: bool, empty: bool,
rt: Pin<Box<AsanGiovese>>, rt: Pin<Box<AsanGiovese>>,
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
} }
impl QemuAsanHelper { impl QemuAsanHelper {
@ -742,7 +745,7 @@ impl QemuAsanHelper {
pub fn default(rt: Pin<Box<AsanGiovese>>) -> Self { pub fn default(rt: Pin<Box<AsanGiovese>>) -> Self {
Self::new( Self::new(
rt, rt,
QemuInstrumentationFilter::None, QemuInstrumentationAddressRangeFilter::None,
QemuAsanOptions::Snapshot, QemuAsanOptions::Snapshot,
) )
} }
@ -750,7 +753,7 @@ impl QemuAsanHelper {
#[must_use] #[must_use]
pub fn new( pub fn new(
mut rt: Pin<Box<AsanGiovese>>, mut rt: Pin<Box<AsanGiovese>>,
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
options: QemuAsanOptions, options: QemuAsanOptions,
) -> Self { ) -> Self {
assert!(unsafe { ASAN_INITED }, "The ASan runtime is not initialized, use init_with_asan(...) instead of just Emulator::new(...)"); 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] #[must_use]
pub fn with_error_callback( pub fn with_error_callback(
mut rt: Pin<Box<AsanGiovese>>, mut rt: Pin<Box<AsanGiovese>>,
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
error_callback: AsanErrorCallback, error_callback: AsanErrorCallback,
options: QemuAsanOptions, options: QemuAsanOptions,
) -> Self { ) -> Self {
@ -798,7 +801,7 @@ impl QemuAsanHelper {
#[must_use] #[must_use]
pub fn with_asan_report( pub fn with_asan_report(
rt: Pin<Box<AsanGiovese>>, rt: Pin<Box<AsanGiovese>>,
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
options: QemuAsanOptions, options: QemuAsanOptions,
) -> Self { ) -> Self {
Self::with_error_callback(rt, filter, Box::new(asan_report), options) Self::with_error_callback(rt, filter, Box::new(asan_report), options)
@ -922,12 +925,12 @@ impl QemuAsanHelper {
} }
} }
impl HasInstrumentationFilter for QemuAsanHelper { impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuAsanHelper {
fn filter(&self) -> &QemuInstrumentationFilter { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter &self.filter
} }
fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter {
&mut self.filter &mut self.filter
} }
} }

View File

@ -12,7 +12,10 @@ use thread_local::ThreadLocal;
use crate::{ use crate::{
capstone, capstone,
emu::{ArchExtras, Emulator}, emu::{ArchExtras, Emulator},
helper::{HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
QemuInstrumentationAddressRangeFilter,
},
hooks::{Hook, QemuHooks}, hooks::{Hook, QemuHooks},
GuestAddr, GuestAddr,
}; };
@ -215,7 +218,7 @@ pub struct QemuCallTracerHelper<T>
where where
T: CallTraceCollectorTuple, T: CallTraceCollectorTuple,
{ {
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
cs: Capstone, cs: Capstone,
collectors: Option<T>, collectors: Option<T>,
} }
@ -225,7 +228,7 @@ where
T: CallTraceCollectorTuple, T: CallTraceCollectorTuple,
{ {
#[must_use] #[must_use]
pub fn new(filter: QemuInstrumentationFilter, collectors: T) -> Self { pub fn new(filter: QemuInstrumentationAddressRangeFilter, collectors: T) -> Self {
Self { Self {
filter, filter,
cs: capstone().detail(true).build().unwrap(), cs: capstone().detail(true).build().unwrap(),
@ -380,15 +383,15 @@ where
} }
} }
impl<T> HasInstrumentationFilter for QemuCallTracerHelper<T> impl<T> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCallTracerHelper<T>
where where
T: CallTraceCollectorTuple, T: CallTraceCollectorTuple,
{ {
fn filter(&self) -> &QemuInstrumentationFilter { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter &self.filter
} }
fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter {
&mut self.filter &mut self.filter
} }
} }

View File

@ -18,7 +18,8 @@ use crate::{
}; };
use crate::{ use crate::{
helper::{ helper::{
hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter, hash_me, HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
QemuInstrumentationAddressRangeFilter,
}, },
hooks::{Hook, QemuHooks}, hooks::{Hook, QemuHooks},
GuestAddr, GuestAddr,
@ -48,12 +49,12 @@ libafl_bolts::impl_serdeany!(QemuCmpsMapMetadata);
#[derive(Debug)] #[derive(Debug)]
pub struct QemuCmpLogHelper { pub struct QemuCmpLogHelper {
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
} }
impl QemuCmpLogHelper { impl QemuCmpLogHelper {
#[must_use] #[must_use]
pub fn new(filter: QemuInstrumentationFilter) -> Self { pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { filter } Self { filter }
} }
@ -65,16 +66,16 @@ impl QemuCmpLogHelper {
impl Default for QemuCmpLogHelper { impl Default for QemuCmpLogHelper {
fn default() -> Self { fn default() -> Self {
Self::new(QemuInstrumentationFilter::None) Self::new(QemuInstrumentationAddressRangeFilter::None)
} }
} }
impl HasInstrumentationFilter for QemuCmpLogHelper { impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCmpLogHelper {
fn filter(&self) -> &QemuInstrumentationFilter { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter &self.filter
} }
fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter {
&mut self.filter &mut self.filter
} }
} }
@ -99,12 +100,12 @@ where
#[derive(Debug)] #[derive(Debug)]
pub struct QemuCmpLogChildHelper { pub struct QemuCmpLogChildHelper {
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
} }
impl QemuCmpLogChildHelper { impl QemuCmpLogChildHelper {
#[must_use] #[must_use]
pub fn new(filter: QemuInstrumentationFilter) -> Self { pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { filter } Self { filter }
} }
@ -116,7 +117,7 @@ impl QemuCmpLogChildHelper {
impl Default for QemuCmpLogChildHelper { impl Default for QemuCmpLogChildHelper {
fn default() -> Self { 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")] #[cfg(emulation_mode = "usermode")]
#[derive(Debug)] #[derive(Debug)]
pub struct QemuCmpLogRoutinesHelper { pub struct QemuCmpLogRoutinesHelper {
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
cs: Capstone, cs: Capstone,
} }
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
impl QemuCmpLogRoutinesHelper { impl QemuCmpLogRoutinesHelper {
#[must_use] #[must_use]
pub fn new(filter: QemuInstrumentationFilter) -> Self { pub fn new(filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { Self {
filter, filter,
cs: capstone().detail(true).build().unwrap(), cs: capstone().detail(true).build().unwrap(),
@ -348,12 +349,12 @@ impl QemuCmpLogRoutinesHelper {
} }
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
impl HasInstrumentationFilter for QemuCmpLogRoutinesHelper { impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCmpLogRoutinesHelper {
fn filter(&self) -> &QemuInstrumentationFilter { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter &self.filter
} }
fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter {
&mut self.filter &mut self.filter
} }
} }

View File

@ -10,7 +10,10 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
emu::{GuestAddr, GuestUsize}, emu::{GuestAddr, GuestUsize},
helper::{HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, helper::{
HasInstrumentationFilter, IsFilter, QemuHelper, QemuHelperTuple,
QemuInstrumentationAddressRangeFilter,
},
hooks::{Hook, QemuHooks}, hooks::{Hook, QemuHooks},
Emulator, Emulator,
}; };
@ -39,7 +42,7 @@ libafl_bolts::impl_serdeany!(QemuDrCovMetadata);
#[derive(Debug)] #[derive(Debug)]
pub struct QemuDrCovHelper { pub struct QemuDrCovHelper {
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
module_mapping: RangeMap<usize, (u16, String)>, module_mapping: RangeMap<usize, (u16, String)>,
filename: PathBuf, filename: PathBuf,
full_trace: bool, full_trace: bool,
@ -50,7 +53,7 @@ impl QemuDrCovHelper {
#[must_use] #[must_use]
#[allow(clippy::let_underscore_untyped)] #[allow(clippy::let_underscore_untyped)]
pub fn new( pub fn new(
filter: QemuInstrumentationFilter, filter: QemuInstrumentationAddressRangeFilter,
module_mapping: RangeMap<usize, (u16, String)>, module_mapping: RangeMap<usize, (u16, String)>,
filename: PathBuf, filename: PathBuf,
full_trace: bool, full_trace: bool,
@ -75,12 +78,12 @@ impl QemuDrCovHelper {
} }
} }
impl HasInstrumentationFilter for QemuDrCovHelper { impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuDrCovHelper {
fn filter(&self) -> &QemuInstrumentationFilter { fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.filter &self.filter
} }
fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter {
&mut self.filter &mut self.filter
} }
} }

View File

@ -11,10 +11,14 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
emu::GuestAddr, emu::GuestAddr,
helper::{ helper::{
hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter, hash_me, HasInstrumentationFilter, QemuHelper, QemuHelperTuple,
QemuInstrumentationAddressRangeFilter,
}, },
hooks::{Hook, QemuHooks}, hooks::{Hook, QemuHooks},
IsFilter,
}; };
#[cfg(emulation_mode = "systemmode")]
use crate::{helper::QemuInstrumentationPagingFilter, GuestPhysAddr};
#[cfg_attr( #[cfg_attr(
any(not(feature = "serdeany_autoreg"), miri), any(not(feature = "serdeany_autoreg"), miri),
@ -38,48 +42,112 @@ impl QemuEdgesMapMetadata {
libafl_bolts::impl_serdeany!(QemuEdgesMapMetadata); libafl_bolts::impl_serdeany!(QemuEdgesMapMetadata);
#[cfg(emulation_mode = "usermode")]
#[derive(Debug)] #[derive(Debug)]
pub struct QemuEdgeCoverageHelper { pub struct QemuEdgeCoverageHelper {
filter: QemuInstrumentationFilter, address_filter: QemuInstrumentationAddressRangeFilter,
use_hitcounts: bool, 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 { impl QemuEdgeCoverageHelper {
#[must_use] #[must_use]
pub fn new(filter: QemuInstrumentationFilter) -> Self { pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { Self {
filter, address_filter,
use_hitcounts: true, use_hitcounts: true,
} }
} }
#[must_use] #[must_use]
pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self { pub fn without_hitcounts(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { Self {
filter, address_filter,
use_hitcounts: false, use_hitcounts: false,
} }
} }
#[must_use] #[must_use]
pub fn must_instrument(&self, addr: GuestAddr) -> bool { 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<GuestPhysAddr>) -> bool {
self.address_filter.allowed(addr) && self.paging_filter.allowed(paging_id)
}
}
#[cfg(emulation_mode = "usermode")]
impl Default for QemuEdgeCoverageHelper { impl Default for QemuEdgeCoverageHelper {
fn default() -> Self { fn default() -> Self {
Self::new(QemuInstrumentationFilter::None) Self::new(QemuInstrumentationAddressRangeFilter::None)
} }
} }
impl HasInstrumentationFilter for QemuEdgeCoverageHelper { #[cfg(emulation_mode = "systemmode")]
fn filter(&self) -> &QemuInstrumentationFilter { impl Default for QemuEdgeCoverageHelper {
&self.filter fn default() -> Self {
Self::new(
QemuInstrumentationAddressRangeFilter::None,
QemuInstrumentationPagingFilter::None,
)
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuEdgeCoverageHelper {
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.address_filter
} }
fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter {
&mut self.filter &mut self.address_filter
}
}
#[cfg(emulation_mode = "systemmode")]
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> 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; pub type QemuCollidingEdgeCoverageHelper = QemuEdgeCoverageChildHelper;
#[cfg(emulation_mode = "usermode")]
#[derive(Debug)] #[derive(Debug)]
pub struct QemuEdgeCoverageChildHelper { pub struct QemuEdgeCoverageChildHelper {
filter: QemuInstrumentationFilter, address_filter: QemuInstrumentationAddressRangeFilter,
use_hitcounts: bool, 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 { impl QemuEdgeCoverageChildHelper {
#[must_use] #[must_use]
pub fn new(filter: QemuInstrumentationFilter) -> Self { pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { Self {
filter, address_filter,
use_hitcounts: true, use_hitcounts: true,
} }
} }
#[must_use] #[must_use]
pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self { pub fn without_hitcounts(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { Self {
filter, address_filter,
use_hitcounts: false, use_hitcounts: false,
} }
} }
#[must_use] #[must_use]
pub fn must_instrument(&self, addr: GuestAddr) -> bool { 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<GuestPhysAddr>) -> bool {
self.address_filter.allowed(addr) && self.paging_filter.allowed(paging_id)
}
}
#[cfg(emulation_mode = "usermode")]
impl Default for QemuEdgeCoverageChildHelper { impl Default for QemuEdgeCoverageChildHelper {
fn default() -> Self { fn default() -> Self {
Self::new(QemuInstrumentationFilter::None) Self::new(QemuInstrumentationAddressRangeFilter::None)
} }
} }
impl HasInstrumentationFilter for QemuEdgeCoverageChildHelper { #[cfg(emulation_mode = "systemmode")]
fn filter(&self) -> &QemuInstrumentationFilter { impl Default for QemuEdgeCoverageChildHelper {
&self.filter fn default() -> Self {
Self::new(
QemuInstrumentationAddressRangeFilter::None,
QemuInstrumentationPagingFilter::None,
)
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
for QemuEdgeCoverageChildHelper
{
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.address_filter
} }
fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter {
&mut self.filter &mut self.address_filter
}
}
#[cfg(emulation_mode = "systemmode")]
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> 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)] #[derive(Debug)]
pub struct QemuEdgeCoverageClassicHelper { pub struct QemuEdgeCoverageClassicHelper {
filter: QemuInstrumentationFilter, address_filter: QemuInstrumentationAddressRangeFilter,
use_hitcounts: bool, 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 { impl QemuEdgeCoverageClassicHelper {
#[must_use] #[must_use]
pub fn new(filter: QemuInstrumentationFilter) -> Self { pub fn new(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { Self {
filter, address_filter,
use_hitcounts: true, use_hitcounts: true,
} }
} }
#[must_use] #[must_use]
pub fn without_hitcounts(filter: QemuInstrumentationFilter) -> Self { pub fn without_hitcounts(address_filter: QemuInstrumentationAddressRangeFilter) -> Self {
Self { Self {
filter, address_filter,
use_hitcounts: false, use_hitcounts: false,
} }
} }
#[must_use] #[must_use]
pub fn must_instrument(&self, addr: GuestAddr) -> bool { 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<GuestPhysAddr>) -> bool {
self.address_filter.allowed(addr) && self.paging_filter.allowed(paging_id)
}
}
#[cfg(emulation_mode = "usermode")]
impl Default for QemuEdgeCoverageClassicHelper { impl Default for QemuEdgeCoverageClassicHelper {
fn default() -> Self { fn default() -> Self {
Self::new(QemuInstrumentationFilter::None) Self::new(QemuInstrumentationAddressRangeFilter::None)
} }
} }
impl HasInstrumentationFilter for QemuEdgeCoverageClassicHelper { #[cfg(emulation_mode = "systemmode")]
fn filter(&self) -> &QemuInstrumentationFilter { impl Default for QemuEdgeCoverageClassicHelper {
&self.filter fn default() -> Self {
Self::new(
QemuInstrumentationAddressRangeFilter::None,
QemuInstrumentationPagingFilter::None,
)
}
}
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter>
for QemuEdgeCoverageClassicHelper
{
fn filter(&self) -> &QemuInstrumentationAddressRangeFilter {
&self.address_filter
} }
fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter { fn filter_mut(&mut self) -> &mut QemuInstrumentationAddressRangeFilter {
&mut self.filter &mut self.address_filter
}
}
#[cfg(emulation_mode = "systemmode")]
impl HasInstrumentationFilter<QemuInstrumentationPagingFilter> 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<S>, QT: QemuHelperTuple<S>,
{ {
if let Some(h) = hooks.helpers().match_first_type::<QemuEdgeCoverageHelper>() { if let Some(h) = hooks.helpers().match_first_type::<QemuEdgeCoverageHelper>() {
if !h.must_instrument(src) && !h.must_instrument(dest) { #[cfg(emulation_mode = "usermode")]
return None; {
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"); let state = state.expect("The gen_unique_edge_ids hook works only for in-process fuzzing");
@ -339,9 +555,23 @@ where
.helpers() .helpers()
.match_first_type::<QemuEdgeCoverageChildHelper>() .match_first_type::<QemuEdgeCoverageChildHelper>()
{ {
#[cfg(emulation_mode = "usermode")]
if !h.must_instrument(src) && !h.must_instrument(dest) { if !h.must_instrument(src) && !h.must_instrument(dest) {
return None; 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 // GuestAddress is u32 for 32 bit guests
#[allow(clippy::unnecessary_cast)] #[allow(clippy::unnecessary_cast)]
@ -391,8 +621,23 @@ where
.helpers() .helpers()
.match_first_type::<QemuEdgeCoverageClassicHelper>() .match_first_type::<QemuEdgeCoverageClassicHelper>()
{ {
if !h.must_instrument(pc) { #[cfg(emulation_mode = "usermode")]
return None; {
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 // GuestAddress is u32 for 32 bit guests

View File

@ -10,11 +10,8 @@ use core::{
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
use std::cell::OnceCell; use std::cell::OnceCell;
#[cfg(emulation_mode = "systemmode")] #[cfg(emulation_mode = "systemmode")]
use std::{ use std::{ffi::CStr, ptr::null_mut};
ffi::{CStr, CString}, use std::{ffi::CString, ptr, slice::from_raw_parts, str::from_utf8_unchecked};
ptr::null_mut,
};
use std::{slice::from_raw_parts, str::from_utf8_unchecked};
#[cfg(emulation_mode = "usermode")] #[cfg(emulation_mode = "usermode")]
use libc::c_int; use libc::c_int;
@ -393,6 +390,9 @@ extern "C" {
data: *const (), data: *const (),
); );
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize); 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")] #[cfg(emulation_mode = "usermode")]
@ -589,6 +589,18 @@ impl CPU {
} }
} }
#[cfg(emulation_mode = "systemmode")]
#[must_use]
pub fn get_current_paging_id(&self) -> Option<GuestPhysAddr> {
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 // TODO expose tlb_set_dirty and tlb_reset_dirty
/// Write a value to a guest address. /// Write a value to a guest address.
@ -945,8 +957,12 @@ impl Emulator {
#[allow(clippy::cast_possible_wrap)] #[allow(clippy::cast_possible_wrap)]
let argc = argc as i32; let argc = argc as i32;
let args: Vec<String> = args.iter().map(|x| x.clone() + "\0").collect(); let args: Vec<CString> = args
let argv: Vec<*const u8> = args.iter().map(|x| x.as_bytes().as_ptr()).collect(); .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<String> = env let env_strs: Vec<String> = env
.iter() .iter()
.map(|(k, v)| format!("{}={}\0", &k, &v)) .map(|(k, v)| format!("{}={}\0", &k, &v))

View File

@ -1,4 +1,5 @@
use core::{fmt::Debug, ops::Range}; use core::{fmt::Debug, ops::Range};
use std::{collections::HashSet, hash};
use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple}; use libafl::{executors::ExitKind, inputs::UsesInput, observers::ObserversTuple};
use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType}; use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
@ -6,6 +7,7 @@ use libafl_bolts::tuples::{MatchFirstType, SplitBorrowExtractFirstType};
use crate::{ use crate::{
emu::{Emulator, GuestAddr}, emu::{Emulator, GuestAddr},
hooks::QemuHooks, hooks::QemuHooks,
GuestPhysAddr,
}; };
/// A helper for `libafl_qemu`. /// A helper for `libafl_qemu`.
@ -143,49 +145,83 @@ where
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug)]
pub enum QemuInstrumentationFilter { pub enum QemuFilterList<T: IsFilter + Debug> {
AllowList(Vec<Range<GuestAddr>>), AllowList(T),
DenyList(Vec<Range<GuestAddr>>), DenyList(T),
None, None,
} }
impl QemuInstrumentationFilter { impl<T> IsFilter for QemuFilterList<T>
#[must_use] where
pub fn allowed(&self, addr: GuestAddr) -> bool { T: IsFilter,
{
type FilterParameter = T::FilterParameter;
fn allowed(&self, filter_parameter: Self::FilterParameter) -> bool {
match self { match self {
QemuInstrumentationFilter::AllowList(l) => { QemuFilterList::AllowList(allow_list) => allow_list.allowed(filter_parameter),
for rng in l { QemuFilterList::DenyList(deny_list) => !deny_list.allowed(filter_parameter),
if rng.contains(&addr) { QemuFilterList::None => true,
return true;
}
}
false
}
QemuInstrumentationFilter::DenyList(l) => {
for rng in l {
if rng.contains(&addr) {
return false;
}
}
true
}
QemuInstrumentationFilter::None => true,
} }
} }
} }
pub trait HasInstrumentationFilter { pub type QemuInstrumentationPagingFilter = QemuFilterList<HashSet<GuestPhysAddr>>;
fn filter(&self) -> &QemuInstrumentationFilter;
fn filter_mut(&mut self) -> &mut QemuInstrumentationFilter; impl<H: hash::BuildHasher> IsFilter for HashSet<GuestPhysAddr, H> {
type FilterParameter = Option<GuestPhysAddr>;
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<Vec<Range<GuestAddr>>>;
impl IsFilter for Vec<Range<GuestAddr>> {
type FilterParameter = GuestAddr;
fn allowed(&self, addr: Self::FilterParameter) -> bool {
for rng in self {
if rng.contains(&addr) {
return true;
}
}
false
}
}
pub trait HasInstrumentationFilter<F>
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; *self.filter_mut() = filter;
emu.flush_jit(); emu.flush_jit();
} }
} }
pub trait IsFilter: Debug {
type FilterParameter;
fn allowed(&self, filter_parameter: Self::FilterParameter) -> bool;
}
pub trait IsAddressFilter: IsFilter<FilterParameter = GuestAddr> {}
#[cfg(emulation_mode = "systemmode")]
pub trait IsPagingFilter: IsFilter<FilterParameter = Option<GuestPhysAddr>> {}
#[cfg(emulation_mode = "systemmode")]
impl IsPagingFilter for QemuInstrumentationPagingFilter {}
impl IsAddressFilter for QemuInstrumentationAddressRangeFilter {}
#[must_use] #[must_use]
pub fn hash_me(mut x: u64) -> u64 { pub fn hash_me(mut x: u64) -> u64 {
x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0; x = (x.overflowing_shr(16).0 ^ x).overflowing_mul(0x45d9f3b).0;

View File

@ -87,7 +87,9 @@ macro_rules! hook_to_repr {
} }
static mut QEMU_HOOKS_PTR: *const c_void = ptr::null(); static mut QEMU_HOOKS_PTR: *const c_void = ptr::null();
unsafe fn get_qemu_hooks<'a, QT, S>() -> &'a mut QemuHooks<QT, S>
#[must_use]
pub unsafe fn get_qemu_hooks<'a, QT, S>() -> &'a mut QemuHooks<QT, S>
where where
S: UsesInput, S: UsesInput,
QT: QemuHelperTuple<S>, QT: QemuHelperTuple<S>,