use hashbrown::HashMap; use libafl::{inputs::UsesInput, state::HasMetadata}; pub use libafl_targets::{ cmplog::__libafl_targets_cmplog_instructions, CmpLogMap, CmpLogObserver, CMPLOG_MAP, CMPLOG_MAP_H, CMPLOG_MAP_PTR, CMPLOG_MAP_SIZE, CMPLOG_MAP_W, }; use serde::{Deserialize, Serialize}; use crate::{ helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter}, hooks::QemuHooks, GuestAddr, }; #[derive(Debug, Default, Serialize, Deserialize)] pub struct QemuCmpsMapMetadata { pub map: HashMap, pub current_id: u64, } impl QemuCmpsMapMetadata { #[must_use] pub fn new() -> Self { Self { map: HashMap::new(), current_id: 0, } } } libafl::impl_serdeany!(QemuCmpsMapMetadata); #[derive(Debug)] pub struct QemuCmpLogHelper { filter: QemuInstrumentationFilter, } impl QemuCmpLogHelper { #[must_use] pub fn new(filter: QemuInstrumentationFilter) -> Self { Self { filter } } #[must_use] pub fn must_instrument(&self, addr: u64) -> bool { self.filter.allowed(addr) } } impl Default for QemuCmpLogHelper { fn default() -> Self { Self::new(QemuInstrumentationFilter::None) } } impl QemuHelper for QemuCmpLogHelper where S: UsesInput + HasMetadata, { fn init_hooks(&self, hooks: &QemuHooks<'_, QT, S>) where QT: QemuHelperTuple, { hooks.cmps_raw( Some(gen_unique_cmp_ids::), Some(trace_cmp1_cmplog), Some(trace_cmp2_cmplog), Some(trace_cmp4_cmplog), Some(trace_cmp8_cmplog), ); } } #[derive(Debug)] pub struct QemuCmpLogChildHelper { filter: QemuInstrumentationFilter, } impl QemuCmpLogChildHelper { #[must_use] pub fn new(filter: QemuInstrumentationFilter) -> Self { Self { filter } } #[must_use] pub fn must_instrument(&self, addr: u64) -> bool { self.filter.allowed(addr) } } impl Default for QemuCmpLogChildHelper { fn default() -> Self { Self::new(QemuInstrumentationFilter::None) } } impl QemuHelper for QemuCmpLogChildHelper where S: UsesInput, S: HasMetadata, { const HOOKS_DO_SIDE_EFFECTS: bool = false; fn init_hooks(&self, hooks: &QemuHooks<'_, QT, S>) where QT: QemuHelperTuple, { hooks.cmps_raw( Some(gen_hashed_cmp_ids::), Some(trace_cmp1_cmplog), Some(trace_cmp2_cmplog), Some(trace_cmp4_cmplog), Some(trace_cmp8_cmplog), ); } } pub fn gen_unique_cmp_ids( hooks: &mut QemuHooks<'_, QT, S>, state: Option<&mut S>, pc: GuestAddr, _size: usize, ) -> Option where S: HasMetadata, S: UsesInput, QT: QemuHelperTuple, { if let Some(h) = hooks.match_helper_mut::() { if !h.must_instrument(pc.into()) { return None; } } let state = state.expect("The gen_unique_cmp_ids hook works only for in-process fuzzing"); if state.metadata().get::().is_none() { state.add_metadata(QemuCmpsMapMetadata::new()); } let meta = state .metadata_mut() .get_mut::() .unwrap(); let id = meta.current_id as usize; Some(*meta.map.entry(pc.into()).or_insert_with(|| { meta.current_id = ((id + 1) & (CMPLOG_MAP_W - 1)) as u64; id as u64 })) } pub fn gen_hashed_cmp_ids( hooks: &mut QemuHooks<'_, QT, S>, _state: Option<&mut S>, pc: GuestAddr, _size: usize, ) -> Option where S: HasMetadata, S: UsesInput, QT: QemuHelperTuple, { if let Some(h) = hooks.match_helper_mut::() { if !h.must_instrument(pc.into()) { return None; } } Some(hash_me(pc.into()) & (CMPLOG_MAP_W as u64 - 1)) } pub extern "C" fn trace_cmp1_cmplog(id: u64, v0: u8, v1: u8, _data: u64) { unsafe { __libafl_targets_cmplog_instructions(id as usize, 1, u64::from(v0), u64::from(v1)); } } pub extern "C" fn trace_cmp2_cmplog(id: u64, v0: u16, v1: u16, _data: u64) { unsafe { __libafl_targets_cmplog_instructions(id as usize, 2, u64::from(v0), u64::from(v1)); } } pub extern "C" fn trace_cmp4_cmplog(id: u64, v0: u32, v1: u32, _data: u64) { unsafe { __libafl_targets_cmplog_instructions(id as usize, 4, u64::from(v0), u64::from(v1)); } } pub extern "C" fn trace_cmp8_cmplog(id: u64, v0: u64, v1: u64, _data: u64) { unsafe { __libafl_targets_cmplog_instructions(id as usize, 8, v0, v1); } }