Re-add drcov for both usermode and systemmode. (#2573)

* re-add drcov for both usermode and systemmode.
This commit is contained in:
Romain Malmain 2024-10-07 14:20:11 +02:00 committed by GitHub
parent 27677a6461
commit 7344fdf059
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 182 additions and 50 deletions

View File

@ -233,11 +233,11 @@ pub fn fuzz() {
let core = core_id.0; let core = core_id.0;
cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}")); cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));
let emulator_modules = tuple_list!(DrCovModule::new( let emulator_modules = tuple_list!(DrCovModule::builder()
StdAddressFilter::default(), .filter(StdAddressFilter::default())
cov_path, .filename(cov_path)
false, .full_trace(false)
)); .build());
let emulator = Emulator::empty() let emulator = Emulator::empty()
.qemu(qemu) .qemu(qemu)

View File

@ -1,3 +1,5 @@
#[cfg(emulation_mode = "systemmode")]
use std::ptr::addr_of_mut;
use std::{path::PathBuf, sync::Mutex}; use std::{path::PathBuf, sync::Mutex};
use hashbrown::{hash_map::Entry, HashMap}; use hashbrown::{hash_map::Entry, HashMap};
@ -7,9 +9,11 @@ use libafl_targets::drcov::{DrCovBasicBlock, DrCovWriter};
use rangemap::RangeMap; use rangemap::RangeMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(emulation_mode = "systemmode")]
use crate::modules::{NopPageFilter, NOP_PAGE_FILTER};
use crate::{ use crate::{
emu::EmulatorModules, emu::EmulatorModules,
modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple}, modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, NopAddressFilter},
qemu::Hook, qemu::Hook,
}; };
@ -35,14 +39,86 @@ impl DrCovMetadata {
libafl_bolts::impl_serdeany!(DrCovMetadata); libafl_bolts::impl_serdeany!(DrCovMetadata);
#[derive(Debug)]
pub struct DrCovModuleBuilder<F> {
filter: Option<F>,
module_mapping: Option<RangeMap<usize, (u16, String)>>,
filename: Option<PathBuf>,
full_trace: Option<bool>,
}
impl<F> DrCovModuleBuilder<F>
where
F: AddressFilter,
{
pub fn build(self) -> DrCovModule<F> {
DrCovModule::new(
self.filter.unwrap(),
self.filename.unwrap(),
self.module_mapping,
self.full_trace.unwrap(),
)
}
pub fn filter<F2>(self, filter: F2) -> DrCovModuleBuilder<F2> {
DrCovModuleBuilder {
filter: Some(filter),
module_mapping: self.module_mapping,
filename: self.filename,
full_trace: self.full_trace,
}
}
#[must_use]
pub fn module_mapping(self, module_mapping: RangeMap<usize, (u16, String)>) -> Self {
Self {
filter: self.filter,
module_mapping: Some(module_mapping),
filename: self.filename,
full_trace: self.full_trace,
}
}
#[must_use]
pub fn filename(self, filename: PathBuf) -> Self {
Self {
filter: self.filter,
module_mapping: self.module_mapping,
filename: Some(filename),
full_trace: self.full_trace,
}
}
#[must_use]
pub fn full_trace(self, full_trace: bool) -> Self {
Self {
filter: self.filter,
module_mapping: self.module_mapping,
filename: self.filename,
full_trace: Some(full_trace),
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct DrCovModule<F> { pub struct DrCovModule<F> {
filter: F, filter: F,
module_mapping: RangeMap<usize, (u16, String)>, module_mapping: Option<RangeMap<usize, (u16, String)>>,
filename: PathBuf, filename: PathBuf,
full_trace: bool, full_trace: bool,
drcov_len: usize, drcov_len: usize,
} }
impl DrCovModule<NopAddressFilter> {
#[must_use]
pub fn builder() -> DrCovModuleBuilder<NopAddressFilter> {
DrCovModuleBuilder {
filter: Some(NopAddressFilter),
module_mapping: None,
full_trace: None,
filename: None,
}
}
}
impl<F> DrCovModule<F> impl<F> DrCovModule<F>
where where
@ -50,7 +126,12 @@ where
{ {
#[must_use] #[must_use]
#[allow(clippy::let_underscore_untyped)] #[allow(clippy::let_underscore_untyped)]
pub fn new(filter: F, filename: PathBuf, full_trace: bool) -> Self { pub fn new(
filter: F,
filename: PathBuf,
module_mapping: Option<RangeMap<usize, (u16, String)>>,
full_trace: bool,
) -> Self {
if full_trace { if full_trace {
let _ = DRCOV_IDS.lock().unwrap().insert(vec![]); let _ = DRCOV_IDS.lock().unwrap().insert(vec![]);
} }
@ -58,7 +139,7 @@ where
let _ = DRCOV_LENGTHS.lock().unwrap().insert(HashMap::new()); let _ = DRCOV_LENGTHS.lock().unwrap().insert(HashMap::new());
Self { Self {
filter, filter,
module_mapping: RangeMap::new(), module_mapping,
filename, filename,
full_trace, full_trace,
drcov_len: 0, drcov_len: 0,
@ -77,6 +158,8 @@ where
S: Unpin + UsesInput + HasMetadata, S: Unpin + UsesInput + HasMetadata,
{ {
type ModuleAddressFilter = F; type ModuleAddressFilter = F;
#[cfg(emulation_mode = "systemmode")]
type ModulePageFilter = NopPageFilter;
fn init_module<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>) fn init_module<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
where where
@ -89,12 +172,18 @@ where
); );
} }
#[cfg(emulation_mode = "usermode")]
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S) fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
where where
ET: EmulatorModuleTuple<S>, ET: EmulatorModuleTuple<S>,
{ {
if self.module_mapping.is_none() {
log::info!("Auto-filling module mapping for DrCov module from QEMU mapping.");
let qemu = emulator_modules.qemu(); let qemu = emulator_modules.qemu();
let mut module_mapping: RangeMap<usize, (u16, String)> = RangeMap::new();
for (i, (r, p)) in qemu for (i, (r, p)) in qemu
.mappings() .mappings()
.filter_map(|m| { .filter_map(|m| {
@ -104,8 +193,24 @@ where
}) })
.enumerate() .enumerate()
{ {
self.module_mapping.insert(r, (i as u16, p)); module_mapping.insert(r, (i as u16, p));
} }
self.module_mapping = Some(module_mapping);
} else {
log::info!("Using user-provided module mapping for DrCov module.");
}
}
#[cfg(emulation_mode = "systemmode")]
fn first_exec<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
where
ET: EmulatorModuleTuple<S>,
{
assert!(
self.module_mapping.is_some(),
"DrCov should have a module mapping already set."
);
} }
fn post_exec<OT, ET>( fn post_exec<OT, ET>(
@ -127,7 +232,11 @@ where
for id in DRCOV_IDS.lock().unwrap().as_ref().unwrap() { for id in DRCOV_IDS.lock().unwrap().as_ref().unwrap() {
'pcs_full: for (pc, idm) in DRCOV_MAP.lock().unwrap().as_ref().unwrap() { 'pcs_full: for (pc, idm) in DRCOV_MAP.lock().unwrap().as_ref().unwrap() {
let mut module_found = false; let mut module_found = false;
for module in self.module_mapping.iter() { // # Safety
//
// Module mapping is already set. It's checked or filled when the module is first run.
unsafe {
for module in self.module_mapping.as_ref().unwrap_unchecked().iter() {
let (range, (_, _)) = module; let (range, (_, _)) = module;
if *pc >= range.start.try_into().unwrap() if *pc >= range.start.try_into().unwrap()
&& *pc <= range.end.try_into().unwrap() && *pc <= range.end.try_into().unwrap()
@ -136,6 +245,7 @@ where
break; break;
} }
} }
}
if !module_found { if !module_found {
continue 'pcs_full; continue 'pcs_full;
} }
@ -155,17 +265,26 @@ where
} }
} }
DrCovWriter::new(&self.module_mapping) // # Safety
//
// Module mapping is already set. It's checked or filled when the module is first run.
unsafe {
DrCovWriter::new(self.module_mapping.as_ref().unwrap_unchecked())
.write(&self.filename, &drcov_vec) .write(&self.filename, &drcov_vec)
.expect("Failed to write coverage file"); .expect("Failed to write coverage file");
} }
}
self.drcov_len = DRCOV_IDS.lock().unwrap().as_ref().unwrap().len(); self.drcov_len = DRCOV_IDS.lock().unwrap().as_ref().unwrap().len();
} else { } else {
if DRCOV_MAP.lock().unwrap().as_ref().unwrap().len() > self.drcov_len { if DRCOV_MAP.lock().unwrap().as_ref().unwrap().len() > self.drcov_len {
let mut drcov_vec = Vec::<DrCovBasicBlock>::new(); let mut drcov_vec = Vec::<DrCovBasicBlock>::new();
'pcs: for (pc, _) in DRCOV_MAP.lock().unwrap().as_ref().unwrap() { 'pcs: for (pc, _) in DRCOV_MAP.lock().unwrap().as_ref().unwrap() {
let mut module_found = false; let mut module_found = false;
for module in self.module_mapping.iter() { // # Safety
//
// Module mapping is already set. It's checked or filled when the module is first run.
unsafe {
for module in self.module_mapping.as_ref().unwrap_unchecked().iter() {
let (range, (_, _)) = module; let (range, (_, _)) = module;
if *pc >= range.start.try_into().unwrap() if *pc >= range.start.try_into().unwrap()
&& *pc <= range.end.try_into().unwrap() && *pc <= range.end.try_into().unwrap()
@ -174,6 +293,7 @@ where
break; break;
} }
} }
}
if !module_found { if !module_found {
continue 'pcs; continue 'pcs;
} }
@ -190,10 +310,15 @@ where
} }
} }
DrCovWriter::new(&self.module_mapping) // # Safety
//
// Module mapping is already set. It's checked or filled when the module is first run.
unsafe {
DrCovWriter::new(self.module_mapping.as_ref().unwrap_unchecked())
.write(&self.filename, &drcov_vec) .write(&self.filename, &drcov_vec)
.expect("Failed to write coverage file"); .expect("Failed to write coverage file");
} }
}
self.drcov_len = DRCOV_MAP.lock().unwrap().as_ref().unwrap().len(); self.drcov_len = DRCOV_MAP.lock().unwrap().as_ref().unwrap().len();
} }
} }
@ -205,6 +330,16 @@ where
fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter { fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter {
&mut self.filter &mut self.filter
} }
#[cfg(emulation_mode = "systemmode")]
fn page_filter(&self) -> &Self::ModulePageFilter {
&NopPageFilter
}
#[cfg(emulation_mode = "systemmode")]
fn page_filter_mut(&mut self) -> &mut Self::ModulePageFilter {
unsafe { addr_of_mut!(NOP_PAGE_FILTER).as_mut().unwrap().get_mut() }
}
} }
pub fn gen_unique_block_ids<ET, F, S>( pub fn gen_unique_block_ids<ET, F, S>(

View File

@ -18,7 +18,7 @@ pub mod systemmode;
pub use systemmode::*; pub use systemmode::*;
pub mod edges; pub mod edges;
pub use edges::EdgeCoverageModule; pub use edges::*;
#[cfg(not(cpu_target = "hexagon"))] #[cfg(not(cpu_target = "hexagon"))]
pub mod calls; pub mod calls;
@ -30,6 +30,11 @@ pub mod cmplog;
#[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))] #[cfg(not(any(cpu_target = "mips", cpu_target = "hexagon")))]
pub use cmplog::CmpLogModule; pub use cmplog::CmpLogModule;
#[cfg(not(cpu_target = "hexagon"))]
pub mod drcov;
#[cfg(not(cpu_target = "hexagon"))]
pub use drcov::*;
use crate::{emu::EmulatorModules, Qemu}; use crate::{emu::EmulatorModules, Qemu};
/// A module for `libafl_qemu`. /// A module for `libafl_qemu`.

View File

@ -1,9 +1,3 @@
#[cfg(not(cpu_target = "hexagon"))]
pub mod drcov;
#[cfg(not(cpu_target = "hexagon"))]
pub use drcov::DrCovModule;
#[cfg(feature = "injections")] #[cfg(feature = "injections")]
pub mod injections; pub mod injections;
#[cfg(feature = "injections")] #[cfg(feature = "injections")]
@ -12,9 +6,7 @@ pub use injections::InjectionModule;
#[cfg(not(cpu_target = "hexagon"))] #[cfg(not(cpu_target = "hexagon"))]
pub mod snapshot; pub mod snapshot;
#[cfg(not(cpu_target = "hexagon"))] #[cfg(not(cpu_target = "hexagon"))]
pub use snapshot::IntervalSnapshotFilter; pub use snapshot::{IntervalSnapshotFilter, SnapshotModule};
#[cfg(not(cpu_target = "hexagon"))]
pub use snapshot::SnapshotModule;
#[cfg(not(cpu_target = "hexagon"))] #[cfg(not(cpu_target = "hexagon"))]
pub mod asan; pub mod asan;