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::{
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,

View File

@ -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<QemuInstrumentationFilter, Error> {
fn coverage_filter(
&self,
emu: &Emulator,
) -> Result<QemuInstrumentationAddressRangeFilter, Error> {
/* 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::<Vec<Range<GuestAddr>>>();
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::<Vec<Range<GuestAddr>>>();
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,
]))
}
}

View File

@ -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 {

View File

@ -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<Box<AsanGiovese>>,
filter: QemuInstrumentationFilter,
filter: QemuInstrumentationAddressRangeFilter,
}
impl QemuAsanHelper {
@ -742,7 +745,7 @@ impl QemuAsanHelper {
pub fn default(rt: Pin<Box<AsanGiovese>>) -> Self {
Self::new(
rt,
QemuInstrumentationFilter::None,
QemuInstrumentationAddressRangeFilter::None,
QemuAsanOptions::Snapshot,
)
}
@ -750,7 +753,7 @@ impl QemuAsanHelper {
#[must_use]
pub fn new(
mut rt: Pin<Box<AsanGiovese>>,
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<Box<AsanGiovese>>,
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<Box<AsanGiovese>>,
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<QemuInstrumentationAddressRangeFilter> 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
}
}

View File

@ -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<T>
where
T: CallTraceCollectorTuple,
{
filter: QemuInstrumentationFilter,
filter: QemuInstrumentationAddressRangeFilter,
cs: Capstone,
collectors: Option<T>,
}
@ -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<T> HasInstrumentationFilter for QemuCallTracerHelper<T>
impl<T> HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> for QemuCallTracerHelper<T>
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
}
}

View File

@ -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<QemuInstrumentationAddressRangeFilter> 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<QemuInstrumentationAddressRangeFilter> 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
}
}

View File

@ -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<usize, (u16, String)>,
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<usize, (u16, String)>,
filename: PathBuf,
full_trace: bool,
@ -75,12 +78,12 @@ impl QemuDrCovHelper {
}
}
impl HasInstrumentationFilter for QemuDrCovHelper {
fn filter(&self) -> &QemuInstrumentationFilter {
impl HasInstrumentationFilter<QemuInstrumentationAddressRangeFilter> 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
}
}

View File

@ -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<GuestPhysAddr>) -> 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<QemuInstrumentationAddressRangeFilter> 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<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;
#[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<GuestPhysAddr>) -> 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<QemuInstrumentationAddressRangeFilter>
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<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)]
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<GuestPhysAddr>) -> 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<QemuInstrumentationAddressRangeFilter>
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<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>,
{
if let Some(h) = hooks.helpers().match_first_type::<QemuEdgeCoverageHelper>() {
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::<QemuEdgeCoverageChildHelper>()
{
#[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::<QemuEdgeCoverageClassicHelper>()
{
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

View File

@ -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<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
/// 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<String> = 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<CString> = 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<String> = env
.iter()
.map(|(k, v)| format!("{}={}\0", &k, &v))

View File

@ -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<Range<GuestAddr>>),
DenyList(Vec<Range<GuestAddr>>),
#[derive(Debug)]
pub enum QemuFilterList<T: IsFilter + Debug> {
AllowList(T),
DenyList(T),
None,
}
impl QemuInstrumentationFilter {
#[must_use]
pub fn allowed(&self, addr: GuestAddr) -> bool {
impl<T> IsFilter for QemuFilterList<T>
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<HashSet<GuestPhysAddr>>;
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;
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]
pub fn hash_me(mut x: u64) -> u64 {
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();
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
S: UsesInput,
QT: QemuHelperTuple<S>,