diff --git a/.github/workflows/qemu-fuzzer-tester-prepare/action.yml b/.github/workflows/qemu-fuzzer-tester-prepare/action.yml index bf47024a2c..adbf97ad50 100644 --- a/.github/workflows/qemu-fuzzer-tester-prepare/action.yml +++ b/.github/workflows/qemu-fuzzer-tester-prepare/action.yml @@ -5,7 +5,7 @@ runs: steps: - name: Install QEMU deps shell: bash - run: apt-get update && apt-get install -y qemu-utils sudo python3-msgpack python3-jinja2 curl + run: apt-get update && apt-get install -y qemu-utils sudo python3-msgpack python3-jinja2 curl python3-dev - uses: dtolnay/rust-toolchain@stable - name: enable mult-thread for `make` shell: bash diff --git a/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs b/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs index 8cee4b8500..6e2208626f 100644 --- a/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs +++ b/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, time::Duration}; +use std::{path::PathBuf, ptr::NonNull, time::Duration}; use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, @@ -46,7 +46,12 @@ pub fn main() { libafl::executors::ExitKind::Ok }; // Create an observation channel using the signals map - let observer = unsafe { ConstMapObserver::::from_mut_ptr("signals", map_ptr) }; + let observer = unsafe { + ConstMapObserver::::from_mut_ptr( + "signals", + NonNull::new(map_ptr).expect("map ptr is null."), + ) + }; // Create a stacktrace observer let mut bt = shmem_provider.new_on_shmem::>(None).unwrap(); let bt_observer = BacktraceObserver::new( diff --git a/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor/src/main.rs b/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor/src/main.rs index 6b43c9bba4..d638032324 100644 --- a/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor/src/main.rs +++ b/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor/src/main.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{path::PathBuf, ptr::NonNull}; use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, @@ -35,7 +35,12 @@ pub fn main() { libafl::executors::ExitKind::Ok }; // Create an observation channel using the signals map - let observer = unsafe { ConstMapObserver::::from_mut_ptr("signals", array_ptr) }; + let observer = unsafe { + ConstMapObserver::::from_mut_ptr( + "signals", + NonNull::new(array_ptr).expect("map ptr is null"), + ) + }; // Create a stacktrace observer let bt_observer = BacktraceObserver::owned( "BacktraceObserver", diff --git a/fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor/src/main.rs b/fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor/src/main.rs index 693b105c7d..220f822643 100644 --- a/fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor/src/main.rs +++ b/fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor/src/main.rs @@ -42,7 +42,10 @@ pub fn main() { let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap(); //let the forkserver know the shmid shmem.write_to_env("__AFL_SHM_ID").unwrap(); - let shmem_map = shmem.as_slice_mut(); + let shmem_map: &mut [u8; MAP_SIZE] = shmem + .as_slice_mut() + .try_into() + .expect("could not convert slice to sized slice."); // Create an observation channel using the signals map let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new( diff --git a/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs b/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs index cf56953dad..1e9aefde6f 100644 --- a/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs +++ b/fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs @@ -9,6 +9,7 @@ use std::{ io::{self, Write}, path::PathBuf, process, + ptr::NonNull, time::Duration, }; @@ -160,14 +161,14 @@ fn fuzz( let mut edges_observer = unsafe { HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_DEFAULT_SIZE>::from_mut_ptr( "edges", - edges.as_mut_ptr(), + NonNull::new(edges.as_mut_ptr()).expect("map ptr is null."), )) .track_indices() }; let emulator_modules = tuple_list!( StdEdgeCoverageChildModule::builder() - .map_observer(edges_observer.as_mut()) + .const_map_observer(edges_observer.as_mut()) .build()?, CmpLogChildModule::default(), ); @@ -199,7 +200,8 @@ fn fuzz( let stack_ptr: u64 = qemu.read_reg(Regs::Sp).unwrap(); let mut ret_addr = [0; 8]; - unsafe { qemu.read_mem(stack_ptr, &mut ret_addr) }; + qemu.read_mem(stack_ptr, &mut ret_addr) + .expect("qemu read failed"); let ret_addr = u64::from_le_bytes(ret_addr); println!("Stack pointer = {stack_ptr:#x}"); @@ -323,7 +325,7 @@ fn fuzz( let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); // The wrapped harness function, calling out to the LLVM-style harness - let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| { + let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| { let target = input.target_bytes(); let mut buf = target.as_slice(); let mut len = buf.len(); @@ -333,7 +335,7 @@ fn fuzz( } unsafe { - qemu.write_mem(input_addr, buf); + qemu.write_mem_unchecked(input_addr, buf); qemu.write_reg(Regs::Rdi, input_addr).unwrap(); qemu.write_reg(Regs::Rsi, len as GuestReg).unwrap(); @@ -394,8 +396,8 @@ fn fuzz( #[cfg(unix)] { let null_fd = file_null.as_raw_fd(); - // dup2(null_fd, io::stdout().as_raw_fd())?; - // dup2(null_fd, io::stderr().as_raw_fd())?; + dup2(null_fd, io::stdout().as_raw_fd())?; + dup2(null_fd, io::stderr().as_raw_fd())?; } // reopen file to make sure we're at the end log.replace( diff --git a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs index ded0700df8..0132afd983 100644 --- a/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_cmin/src/fuzzer.rs @@ -2,7 +2,7 @@ //! #[cfg(feature = "i386")] use core::mem::size_of; -use std::{env, io, path::PathBuf, process}; +use std::{env, io, path::PathBuf, process, ptr::NonNull}; use clap::{builder::Str, Parser}; use libafl::{ @@ -162,7 +162,7 @@ pub fn fuzz() -> Result<(), Error> { let mut edges_observer = unsafe { HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_DEFAULT_SIZE>::from_mut_ptr( "edges", - edges.as_mut_ptr(), + NonNull::new(edges.as_mut_ptr()).expect("The edge map pointer is null."), )) }; @@ -196,7 +196,7 @@ pub fn fuzz() -> Result<(), Error> { let len = len as GuestReg; unsafe { - qemu.write_mem(input_addr, buf); + qemu.write_mem(input_addr, buf).expect("qemu write failed."); qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap(); qemu.write_reg(Regs::Sp, stack_ptr).unwrap(); qemu.write_return_address(ret_addr).unwrap(); @@ -219,7 +219,7 @@ pub fn fuzz() -> Result<(), Error> { }; let modules = tuple_list!(StdEdgeCoverageChildModule::builder() - .map_observer(edges_observer.as_mut()) + .const_map_observer(edges_observer.as_mut()) .build()?); let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?; diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 75cc129f41..4794dc4aac 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1635,7 +1635,7 @@ mod tests { let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap(); shmem.write_to_env("__AFL_SHM_ID").unwrap(); - let shmem_buf = shmem.as_slice_mut(); + let shmem_buf: &mut [u8; MAP_SIZE] = shmem.as_slice_mut().try_into().unwrap(); let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new( "shared_mem", diff --git a/libafl/src/observers/map/const_map.rs b/libafl/src/observers/map/const_map.rs index 0e244ce407..b84428949a 100644 --- a/libafl/src/observers/map/const_map.rs +++ b/libafl/src/observers/map/const_map.rs @@ -5,28 +5,26 @@ use core::{ fmt::Debug, hash::{Hash, Hasher}, ops::{Deref, DerefMut}, + ptr::NonNull, }; use ahash::RandomState; -use libafl_bolts::{ownedref::OwnedMutSlice, AsSlice, AsSliceMut, HasLen, Named}; +use libafl_bolts::{ownedref::OwnedMutSizedSlice, HasLen, Named}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ - observers::{map::MapObserver, Observer, VariableLengthMapObserver}, + observers::{map::MapObserver, ConstLenMapObserver, Observer}, Error, }; -// TODO: remove the size field and implement ConstantLengthMapObserver - /// Use a const size to speedup `Feedback::is_interesting` when the user can /// know the size of the map at compile time. #[derive(Serialize, Deserialize, Debug)] #[allow(clippy::unsafe_derive_deserialize)] pub struct ConstMapObserver<'a, T, const N: usize> { - map: OwnedMutSlice<'a, T>, + map: OwnedMutSizedSlice<'a, T, N>, initial: T, name: Cow<'static, str>, - size: usize, } impl Observer for ConstMapObserver<'_, T, N> @@ -87,19 +85,19 @@ where #[inline] fn get(&self, idx: usize) -> T { - self.as_slice()[idx] + self[idx] } #[inline] fn set(&mut self, idx: usize, val: T) { - self.map.as_slice_mut()[idx] = val; + (*self)[idx] = val; } /// Count the set bytes in the map fn count_bytes(&self) -> u64 { let initial = self.initial(); let cnt = self.usable_count(); - let map = self.as_slice(); + let map = self.map.as_slice(); let mut res = 0; for x in &map[0..cnt] { if *x != initial { @@ -110,7 +108,7 @@ where } fn usable_count(&self) -> usize { - self.as_slice().len() + self.len() } #[inline] @@ -124,7 +122,7 @@ where // Normal memset, see https://rust.godbolt.org/z/Trs5hv let initial = self.initial(); let cnt = self.usable_count(); - let map = self.as_slice_mut(); + let map = &mut (*self); for x in &mut map[0..cnt] { *x = initial; } @@ -132,14 +130,14 @@ where } fn to_vec(&self) -> Vec { - self.as_slice().to_vec() + self.map.to_vec() } /// Get the number of set entries with the specified indexes fn how_many_set(&self, indexes: &[usize]) -> usize { let initial = self.initial(); let cnt = self.usable_count(); - let map = self.as_slice(); + let map = self.map.as_slice(); let mut res = 0; for i in indexes { if *i < cnt && map[*i] != initial { @@ -150,37 +148,30 @@ where } } -impl VariableLengthMapObserver for ConstMapObserver<'_, T, N> +impl ConstLenMapObserver for ConstMapObserver<'_, T, N> where T: PartialEq + Copy + Hash + Serialize + DeserializeOwned + Debug + 'static, { - fn map_slice(&mut self) -> &[Self::Entry] { - self.map.as_slice() - } - - fn map_slice_mut(&mut self) -> &mut [Self::Entry] { - self.map.as_slice_mut() - } - - fn size(&mut self) -> &usize { - &N + fn map_slice(&self) -> &[Self::Entry; N] { + &self.map } - fn size_mut(&mut self) -> &mut usize { - &mut self.size + fn map_slice_mut(&mut self) -> &mut [Self::Entry; N] { + &mut self.map } } impl Deref for ConstMapObserver<'_, T, N> { type Target = [T]; + fn deref(&self) -> &[T] { - &self.map + self.map.as_slice() } } impl DerefMut for ConstMapObserver<'_, T, N> { fn deref_mut(&mut self) -> &mut [T] { - &mut self.map + self.map.as_mut_slice() } } @@ -194,13 +185,12 @@ where /// Will get a pointer to the map and dereference it at any point in time. /// The map must not move in memory! #[must_use] - pub fn new(name: &'static str, map: &'a mut [T]) -> Self { + pub fn new(name: &'static str, map: &'a mut [T; N]) -> Self { assert!(map.len() >= N); Self { - map: OwnedMutSlice::from(map), + map: OwnedMutSizedSlice::from(map), name: Cow::from(name), initial: T::default(), - size: N, } } @@ -208,34 +198,12 @@ where /// /// # Safety /// Will dereference the `map_ptr` with up to len elements. - pub unsafe fn from_mut_ptr(name: &'static str, map_ptr: *mut T) -> Self { + #[must_use] + pub unsafe fn from_mut_ptr(name: &'static str, map_ptr: NonNull) -> Self { ConstMapObserver { - map: OwnedMutSlice::from_raw_parts_mut(map_ptr, N), + map: OwnedMutSizedSlice::from_raw_mut(map_ptr), name: Cow::from(name), initial: T::default(), - size: N, - } - } -} - -impl ConstMapObserver<'_, T, N> -where - T: Default + Clone, -{ - /// Creates a new [`MapObserver`] with an owned map - #[must_use] - pub fn owned(name: &'static str, map: Vec) -> Self { - assert!(map.len() >= N); - let initial = if map.is_empty() { - T::default() - } else { - map[0].clone() - }; - Self { - map: OwnedMutSlice::from(map), - name: Cow::from(name), - initial, - size: N, } } } diff --git a/libafl/src/observers/map/hitcount_map.rs b/libafl/src/observers/map/hitcount_map.rs index 38e6879723..c6521775b7 100644 --- a/libafl/src/observers/map/hitcount_map.rs +++ b/libafl/src/observers/map/hitcount_map.rs @@ -14,7 +14,9 @@ use serde::{Deserialize, Serialize}; use crate::{ executors::ExitKind, - observers::{map::MapObserver, DifferentialObserver, Observer, VariableLengthMapObserver}, + observers::{ + map::MapObserver, ConstLenMapObserver, DifferentialObserver, Observer, VarLenMapObserver, + }, Error, }; @@ -230,11 +232,24 @@ where } } -impl VariableLengthMapObserver for HitcountsMapObserver +impl ConstLenMapObserver for HitcountsMapObserver where - M: VariableLengthMapObserver + MapObserver, + M: ConstLenMapObserver + MapObserver, { - fn map_slice(&mut self) -> &[Self::Entry] { + fn map_slice(&self) -> &[Self::Entry; N] { + self.base.map_slice() + } + + fn map_slice_mut(&mut self) -> &mut [Self::Entry; N] { + self.base.map_slice_mut() + } +} + +impl VarLenMapObserver for HitcountsMapObserver +where + M: VarLenMapObserver + MapObserver, +{ + fn map_slice(&self) -> &[Self::Entry] { self.base.map_slice() } @@ -242,7 +257,7 @@ where self.base.map_slice_mut() } - fn size(&mut self) -> &usize { + fn size(&self) -> &usize { self.base.size() } diff --git a/libafl/src/observers/map/mod.rs b/libafl/src/observers/map/mod.rs index 0415f10a44..d73dedfbb3 100644 --- a/libafl/src/observers/map/mod.rs +++ b/libafl/src/observers/map/mod.rs @@ -387,27 +387,30 @@ pub trait MapObserver: /// The "real" length of the underlying map could change at any point in time. /// Thus, the size of the map should be fetched each time it is used. -pub trait VariableLengthMapObserver: MapObserver { +pub trait VarLenMapObserver: MapObserver { /// A mutable slice reference to the map. /// The length of the map gives the maximum allocatable size. - fn map_slice(&mut self) -> &[Self::Entry]; + fn map_slice(&self) -> &[Self::Entry]; /// A slice reference to the map. /// The length of the map gives the maximum allocatable size. fn map_slice_mut(&mut self) -> &mut [Self::Entry]; /// A reference to the size of the map. - fn size(&mut self) -> &usize; + fn size(&self) -> &usize; /// A mutable reference to the size of the map. fn size_mut(&mut self) -> &mut usize; } /// Implementors guarantee the size of the map is constant at any point in time and equals N. -pub trait ConstantLengthMapObserver: MapObserver { +pub trait ConstLenMapObserver: MapObserver { /// The size of the map const LENGTH: usize = N; + /// A mutable slice reference to the map + fn map_slice(&self) -> &[Self::Entry; N]; + /// A mutable slice reference to the map fn map_slice_mut(&mut self) -> &mut [Self::Entry; N]; } diff --git a/libafl/src/observers/map/variable_map.rs b/libafl/src/observers/map/variable_map.rs index b5b4e56305..ec1a5feda3 100644 --- a/libafl/src/observers/map/variable_map.rs +++ b/libafl/src/observers/map/variable_map.rs @@ -15,7 +15,7 @@ use libafl_bolts::{ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ - observers::{map::MapObserver, Observer, VariableLengthMapObserver}, + observers::{map::MapObserver, Observer, VarLenMapObserver}, Error, }; @@ -149,11 +149,11 @@ where } } -impl VariableLengthMapObserver for VariableMapObserver<'_, T> +impl VarLenMapObserver for VariableMapObserver<'_, T> where T: PartialEq + Copy + Hash + Serialize + DeserializeOwned + Debug, { - fn map_slice(&mut self) -> &[Self::Entry] { + fn map_slice(&self) -> &[Self::Entry] { self.map.as_ref() } @@ -161,7 +161,7 @@ where self.map.as_mut() } - fn size(&mut self) -> &usize { + fn size(&self) -> &usize { self.size.as_ref() } diff --git a/libafl_bolts/src/lib.rs b/libafl_bolts/src/lib.rs index dd6c78c67f..4c7bec9b13 100644 --- a/libafl_bolts/src/lib.rs +++ b/libafl_bolts/src/lib.rs @@ -665,7 +665,18 @@ pub trait AsSlice<'a> { fn as_slice(&'a self) -> Self::SliceRef; } -impl<'a, T, R> AsSlice<'a> for R +/// Can be converted to a slice +pub trait AsSizedSlice<'a, const N: usize> { + /// Type of the entries of this slice + type Entry: 'a; + /// Type of the reference to this slice + type SliceRef: Deref; + + /// Convert to a slice + fn as_sized_slice(&'a self) -> Self::SliceRef; +} + +impl<'a, T, R: ?Sized> AsSlice<'a> for R where T: 'a, R: Deref, @@ -678,6 +689,19 @@ where } } +impl<'a, T, const N: usize, R: ?Sized> AsSizedSlice<'a, N> for R +where + T: 'a, + R: Deref, +{ + type Entry = T; + type SliceRef = &'a [T; N]; + + fn as_sized_slice(&'a self) -> Self::SliceRef { + self + } +} + /// Can be converted to a mutable slice pub trait AsSliceMut<'a>: AsSlice<'a> { /// Type of the mutable reference to this slice @@ -687,7 +711,16 @@ pub trait AsSliceMut<'a>: AsSlice<'a> { fn as_slice_mut(&'a mut self) -> Self::SliceRefMut; } -impl<'a, T, R> AsSliceMut<'a> for R +/// Can be converted to a mutable slice +pub trait AsSizedSliceMut<'a, const N: usize>: AsSizedSlice<'a, N> { + /// Type of the mutable reference to this slice + type SliceRefMut: DerefMut; + + /// Convert to a slice + fn as_sized_slice_mut(&'a mut self) -> Self::SliceRefMut; +} + +impl<'a, T, R: ?Sized> AsSliceMut<'a> for R where T: 'a, R: DerefMut, @@ -699,6 +732,18 @@ where } } +impl<'a, T, const N: usize, R: ?Sized> AsSizedSliceMut<'a, N> for R +where + T: 'a, + R: DerefMut, +{ + type SliceRefMut = &'a mut [T; N]; + + fn as_sized_slice_mut(&'a mut self) -> Self::SliceRefMut { + &mut *self + } +} + /// Create an `Iterator` from a reference pub trait AsIter<'it> { /// The item type diff --git a/libafl_bolts/src/ownedref.rs b/libafl_bolts/src/ownedref.rs index 4c0beb128d..e11e74afa0 100644 --- a/libafl_bolts/src/ownedref.rs +++ b/libafl_bolts/src/ownedref.rs @@ -10,13 +10,67 @@ use core::{ clone::Clone, fmt::Debug, ops::{Deref, DerefMut, RangeBounds}, + ptr::NonNull, slice, slice::SliceIndex, }; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::{shmem::ShMem, AsSlice, AsSliceMut, IntoOwned, Truncate}; +use crate::{ + shmem::ShMem, AsSizedSlice, AsSizedSliceMut, AsSlice, AsSliceMut, IntoOwned, Truncate, +}; + +/// Constant size array visitor for serde deserialization. +/// Mostly taken from +mod arrays { + use alloc::{boxed::Box, fmt, vec::Vec}; + use core::{convert::TryInto, marker::PhantomData}; + + use serde::{ + de::{SeqAccess, Visitor}, + Deserialize, Deserializer, + }; + + struct ArrayVisitor(PhantomData); + + impl<'de, T, const N: usize> Visitor<'de> for ArrayVisitor + where + T: Deserialize<'de>, + { + type Value = Box<[T; N]>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(&format!("an array of length {N}")) + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + // can be optimized using MaybeUninit + let mut data = Vec::with_capacity(N); + for _ in 0..N { + match (seq.next_element())? { + Some(val) => data.push(val), + None => return Err(serde::de::Error::invalid_length(N, &self)), + } + } + match data.try_into() { + Ok(arr) => Ok(arr), + Err(_) => unreachable!(), + } + } + } + pub fn deserialize<'de, D, T, const N: usize>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + T: Deserialize<'de>, + { + deserializer.deserialize_tuple(N, ArrayVisitor::(PhantomData)) + } +} /// Private part of the unsafe marker, making sure this cannot be initialized directly. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -815,6 +869,213 @@ impl<'a, T> From<&'a mut &'a mut [T]> for OwnedMutSlice<'a, T> { } } +/// Wrap a mutable slice and convert to a Box on serialize. +/// We use a hidden inner enum so the public API can be safe, +/// unless the user uses the unsafe [`OwnedMutSizedSlice::from_raw_mut`]. +/// The variable length version is [`OwnedMutSlice`]. +#[derive(Debug)] +pub enum OwnedMutSizedSliceInner<'a, T: 'a + Sized, const N: usize> { + /// A raw ptr to a memory location of length N + RefRaw(*mut [T; N], UnsafeMarker), + /// A ptr to a mutable slice of the type + Ref(&'a mut [T; N]), + /// An owned [`Box`] of the type + Owned(Box<[T; N]>), +} + +impl<'a, T: 'a + Sized + Serialize, const N: usize> Serialize + for OwnedMutSizedSliceInner<'a, T, N> +{ + fn serialize(&self, se: S) -> Result + where + S: Serializer, + { + match self { + OwnedMutSizedSliceInner::RefRaw(rr, _) => unsafe { &**rr }.serialize(se), + OwnedMutSizedSliceInner::Ref(r) => (*r).serialize(se), + OwnedMutSizedSliceInner::Owned(b) => (*b).serialize(se), + } + } +} + +impl<'de, 'a, T: 'a + Sized, const N: usize> Deserialize<'de> for OwnedMutSizedSliceInner<'a, T, N> +where + T: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + arrays::deserialize(deserializer).map(OwnedMutSizedSliceInner::Owned) + } +} + +/// Wrap a mutable slice of constant size N and convert to a Box on serialize +#[allow(clippy::unsafe_derive_deserialize)] +#[derive(Debug, Serialize, Deserialize)] +pub struct OwnedMutSizedSlice<'a, T: 'a + Sized, const N: usize> { + inner: OwnedMutSizedSliceInner<'a, T, N>, +} + +impl<'it, T, const N: usize> IntoIterator for &'it mut OwnedMutSizedSlice<'_, T, N> { + type Item = as Iterator>::Item; + type IntoIter = IterMut<'it, T>; + + fn into_iter(self) -> Self::IntoIter { + self.as_sized_slice_mut().iter_mut() + } +} + +impl<'it, T, const N: usize> IntoIterator for &'it OwnedMutSizedSlice<'_, T, N> { + type Item = as Iterator>::Item; + type IntoIter = Iter<'it, T>; + + fn into_iter(self) -> Self::IntoIter { + self.as_sized_slice().iter() + } +} + +impl<'a, T: 'a + Sized, const N: usize> OwnedMutSizedSlice<'a, T, N> { + /// Create a new [`OwnedMutSizedSlice`] from a raw pointer + /// + /// # Safety + /// + /// The pointer must be valid and point to a map of the size `size_of() * N` + /// The content will be dereferenced in subsequent operations. + #[must_use] + pub unsafe fn from_raw_mut(ptr: NonNull) -> OwnedMutSizedSlice<'a, T, N> { + Self { + inner: OwnedMutSizedSliceInner::RefRaw( + ptr.as_ptr() as *mut [T; N], + UnsafeMarker::new(), + ), + } + } + + /// Returns an iterator over the slice. + pub fn iter(&self) -> Iter<'_, T> { + <&Self as IntoIterator>::into_iter(self) + } + + /// Returns a mutable iterator over the slice. + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + <&mut Self as IntoIterator>::into_iter(self) + } +} + +impl Deref for OwnedMutSizedSlice<'_, T, N> { + type Target = [T; N]; + + fn deref(&self) -> &Self::Target { + match &self.inner { + OwnedMutSizedSliceInner::RefRaw(rr, _) => unsafe { &**rr }, + OwnedMutSizedSliceInner::Ref(r) => r, + OwnedMutSizedSliceInner::Owned(v) => v, + } + } +} + +impl DerefMut for OwnedMutSizedSlice<'_, T, N> { + fn deref_mut(&mut self) -> &mut [T; N] { + match &mut self.inner { + OwnedMutSizedSliceInner::RefRaw(rr, _) => unsafe { &mut **rr }, + OwnedMutSizedSliceInner::Ref(r) => r, + OwnedMutSizedSliceInner::Owned(v) => v, + } + } +} + +impl IntoOwned for OwnedMutSizedSlice<'_, T, N> +where + T: Sized + Clone, +{ + #[must_use] + fn is_owned(&self) -> bool { + match self.inner { + OwnedMutSizedSliceInner::RefRaw(..) | OwnedMutSizedSliceInner::Ref(_) => false, + OwnedMutSizedSliceInner::Owned(_) => true, + } + } + + #[must_use] + fn into_owned(self) -> Self { + let slice: Box<[T; N]> = match self.inner { + OwnedMutSizedSliceInner::RefRaw(rr, _) => unsafe { Box::from((*rr).clone()) }, + OwnedMutSizedSliceInner::Ref(r) => Box::from(r.clone()), + OwnedMutSizedSliceInner::Owned(v) => v, + }; + Self { + inner: OwnedMutSizedSliceInner::Owned(slice), + } + } +} + +impl<'a, T: 'a + Clone, const N: usize> Clone for OwnedMutSizedSlice<'a, T, N> { + fn clone(&self) -> Self { + let slice: Box<[T; N]> = match &self.inner { + OwnedMutSizedSliceInner::RefRaw(rr, _) => unsafe { Box::from((**rr).clone()) }, + OwnedMutSizedSliceInner::Ref(r) => Box::from((*r).clone()), + OwnedMutSizedSliceInner::Owned(v) => v.clone(), + }; + + Self { + inner: OwnedMutSizedSliceInner::Owned(slice), + } + } +} + +/// Create a new [`OwnedMutSizedSlice`] from a sized slice +impl From> for OwnedMutSizedSlice<'_, T, N> { + fn from(s: Box<[T; N]>) -> Self { + Self { + inner: OwnedMutSizedSliceInner::Owned(s), + } + } +} + +/// Create a Boxed slice from an [`OwnedMutSizedSlice`], or return the owned boxed sized slice. +impl<'a, T, const N: usize> From> for Box<[T; N]> +where + T: Clone, +{ + fn from(slice: OwnedMutSizedSlice<'a, T, N>) -> Self { + let slice = slice.into_owned(); + match slice.inner { + OwnedMutSizedSliceInner::Owned(b) => b, + _ => panic!("Could not own slice!"), + } + } +} + +/// Create a new [`OwnedMutSizedSlice`] from a reference to a boxed sized slice +#[allow(clippy::mut_mut)] // This makes use in some iterators easier +impl<'a, T, const N: usize> From<&'a mut Box<[T; N]>> for OwnedMutSizedSlice<'a, T, N> { + fn from(r: &'a mut Box<[T; N]>) -> Self { + Self { + inner: OwnedMutSizedSliceInner::Ref((*r).as_mut()), + } + } +} + +/// Create a new [`OwnedMutSizedSlice`] from a reference to ref to a slice +impl<'a, T, const N: usize> From<&'a mut [T; N]> for OwnedMutSizedSlice<'a, T, N> { + fn from(r: &'a mut [T; N]) -> Self { + Self { + inner: OwnedMutSizedSliceInner::Ref(r), + } + } +} + +/// Create a new [`OwnedMutSizedSlice`] from a reference to ref to a slice +#[allow(clippy::mut_mut)] // This makes use in some iterators easier +impl<'a, T, const N: usize> From<&'a mut &'a mut [T; N]> for OwnedMutSizedSlice<'a, T, N> { + fn from(r: &'a mut &'a mut [T; N]) -> Self { + Self { + inner: OwnedMutSizedSliceInner::Ref(r), + } + } +} + /// Wrap a C-style pointer and convert to a Box on serialize #[derive(Clone, Debug)] pub enum OwnedPtr { diff --git a/libafl_qemu/src/modules/edges.rs b/libafl_qemu/src/modules/edges.rs deleted file mode 100644 index 6038840338..0000000000 --- a/libafl_qemu/src/modules/edges.rs +++ /dev/null @@ -1,802 +0,0 @@ -use std::{cell::UnsafeCell, cmp::max, fmt::Debug, ptr, ptr::addr_of}; - -use hashbrown::{hash_map::Entry, HashMap}; -use libafl::{inputs::UsesInput, observers::VariableLengthMapObserver, HasMetadata}; -use libafl_bolts::Error; -use libafl_qemu_sys::GuestAddr; -#[cfg(feature = "systemmode")] -use libafl_qemu_sys::GuestPhysAddr; -use libafl_targets::EDGES_MAP; -use serde::{Deserialize, Serialize}; - -use crate::{ - emu::EmulatorModules, - modules::{ - hash_me, AddressFilter, EmulatorModule, EmulatorModuleTuple, PageFilter, StdAddressFilter, - StdPageFilter, - }, - qemu::Hook, -}; - -#[no_mangle] -static mut LIBAFL_QEMU_EDGES_MAP_PTR: *mut u8 = ptr::null_mut(); - -#[no_mangle] -static mut LIBAFL_QEMU_EDGES_MAP_SIZE_PTR: *mut usize = ptr::null_mut(); - -#[no_mangle] -static mut LIBAFL_QEMU_EDGES_MAP_ALLOCATED_SIZE: usize = 0; - -#[no_mangle] -static mut LIBAFL_QEMU_EDGES_MAP_MASK_MAX: usize = 0; - -#[cfg_attr( - any(not(feature = "serdeany_autoreg"), miri), - allow(clippy::unsafe_derive_deserialize) -)] // for SerdeAny -#[derive(Debug, Default, Serialize, Deserialize)] -pub struct QemuEdgesMapMetadata { - pub map: HashMap<(GuestAddr, GuestAddr), u64>, - pub current_id: u64, -} - -libafl_bolts::impl_serdeany!(QemuEdgesMapMetadata); - -impl QemuEdgesMapMetadata { - #[must_use] - pub fn new() -> Self { - Self { - map: HashMap::new(), - current_id: 0, - } - } -} - -/// Standard edge coverage module, adapted to most use cases -pub type StdEdgeCoverageModule = StdEdgeCoverageFullModule; - -/// Standard edge coverage module builder, adapted to most use cases -pub type StdEdgeCoverageModuleBuilder = StdEdgeCoverageFullModuleBuilder; - -pub type CollidingEdgeCoverageModule = EdgeCoverageModule; - -pub trait EdgeCoverageVariant: 'static + Debug { - const DO_SIDE_EFFECTS: bool = true; - - fn jit_hitcount(&mut self, _emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - panic!("JIT hitcount is not supported.") - } - - fn jit_no_hitcount(&mut self, _emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - panic!("JIT no hitcount is not supported.") - } - - fn fn_hitcount(&mut self, _emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - panic!("Func hitcount is not supported.") - } - - fn fn_no_hitcount(&mut self, _emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - panic!("Func no hitcount is not supported.") - } -} - -#[derive(Debug)] -pub struct EdgeCoverageFullVariant; - -pub type StdEdgeCoverageFullModule = - EdgeCoverageModule; -pub type StdEdgeCoverageFullModuleBuilder = - EdgeCoverageModuleBuilder; - -impl EdgeCoverageVariant for EdgeCoverageFullVariant { - fn jit_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - let hook_id = emulator_modules.edges( - Hook::Function(gen_unique_edge_ids::), - Hook::Empty, - ); - unsafe { - libafl_qemu_sys::libafl_qemu_edge_hook_set_jit( - hook_id.0, - Some(libafl_qemu_sys::libafl_jit_trace_edge_hitcount), - ); - } - } - - fn jit_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - let hook_id = emulator_modules.edges( - Hook::Function(gen_unique_edge_ids::), - Hook::Empty, - ); - unsafe { - libafl_qemu_sys::libafl_qemu_edge_hook_set_jit( - hook_id.0, - Some(libafl_qemu_sys::libafl_jit_trace_edge_single), - ); - } - } - - fn fn_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - emulator_modules.edges( - Hook::Function(gen_unique_edge_ids::), - Hook::Raw(trace_edge_hitcount), - ); - } - - fn fn_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - emulator_modules.edges( - Hook::Function(gen_unique_edge_ids::), - Hook::Raw(trace_edge_single), - ); - } -} - -impl Default for StdEdgeCoverageFullModuleBuilder { - fn default() -> Self { - Self { - variant: EdgeCoverageFullVariant, - address_filter: StdAddressFilter::default(), - page_filter: StdPageFilter::default(), - use_hitcounts: true, - use_jit: true, - } - } -} - -impl StdEdgeCoverageFullModule { - #[must_use] - pub fn builder() -> StdEdgeCoverageFullModuleBuilder { - EdgeCoverageModuleBuilder::default() - } -} - -#[derive(Debug)] -pub struct EdgeCoverageClassicVariant; - -pub type StdEdgeCoverageClassicModule = - EdgeCoverageModule; -pub type StdEdgeCoverageClassicModuleBuilder = - EdgeCoverageModuleBuilder; - -impl EdgeCoverageVariant for EdgeCoverageClassicVariant { - const DO_SIDE_EFFECTS: bool = false; - - fn jit_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - let hook_id = emulator_modules.blocks( - Hook::Function(gen_hashed_block_ids::), - Hook::Empty, - Hook::Empty, - ); - - unsafe { - libafl_qemu_sys::libafl_qemu_block_hook_set_jit( - hook_id.0, - Some(libafl_qemu_sys::libafl_jit_trace_block_hitcount), - ); - } - } - - fn jit_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - let hook_id = emulator_modules.blocks( - Hook::Function(gen_hashed_block_ids::), - Hook::Empty, - Hook::Empty, - ); - - unsafe { - libafl_qemu_sys::libafl_qemu_block_hook_set_jit( - hook_id.0, - Some(libafl_qemu_sys::libafl_jit_trace_block_single), - ); - } - } - - fn fn_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - emulator_modules.blocks( - Hook::Function(gen_hashed_block_ids::), - Hook::Empty, - Hook::Raw(trace_block_transition_hitcount), - ); - } - - fn fn_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - emulator_modules.blocks( - Hook::Function(gen_hashed_block_ids::), - Hook::Empty, - Hook::Raw(trace_block_transition_single), - ); - } -} - -impl Default for StdEdgeCoverageClassicModuleBuilder { - fn default() -> Self { - Self { - variant: EdgeCoverageClassicVariant, - address_filter: StdAddressFilter::default(), - page_filter: StdPageFilter::default(), - use_hitcounts: true, - use_jit: true, - } - } -} - -impl StdEdgeCoverageClassicModule { - #[must_use] - pub fn builder() -> StdEdgeCoverageClassicModuleBuilder { - EdgeCoverageModuleBuilder::default() - } -} - -#[derive(Debug)] -pub struct EdgeCoverageChildVariant; -pub type StdEdgeCoverageChildModule = - EdgeCoverageModule; -pub type StdEdgeCoverageChildModuleBuilder = - EdgeCoverageModuleBuilder; - -impl EdgeCoverageVariant for EdgeCoverageChildVariant { - const DO_SIDE_EFFECTS: bool = false; - - fn fn_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - emulator_modules.edges( - Hook::Function(gen_hashed_edge_ids::), - Hook::Raw(trace_edge_hitcount_ptr), - ); - } - - fn fn_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) - where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - { - emulator_modules.edges( - Hook::Function(gen_hashed_edge_ids::), - Hook::Raw(trace_edge_single_ptr), - ); - } -} - -impl Default for StdEdgeCoverageChildModuleBuilder { - fn default() -> Self { - Self { - variant: EdgeCoverageChildVariant, - address_filter: StdAddressFilter::default(), - page_filter: StdPageFilter::default(), - use_hitcounts: true, - use_jit: true, - } - } -} - -impl StdEdgeCoverageChildModule { - #[must_use] - pub fn builder() -> StdEdgeCoverageChildModuleBuilder { - EdgeCoverageModuleBuilder::default().jit(false) - } -} - -#[derive(Debug)] -pub struct EdgeCoverageModuleBuilder { - variant: V, - address_filter: AF, - page_filter: PF, - use_hitcounts: bool, - use_jit: bool, -} - -#[derive(Debug)] -pub struct EdgeCoverageModule { - variant: V, - address_filter: AF, - // we only use it in system mode at the moment. - #[cfg_attr(not(feature = "systemmode"), allow(dead_code))] - page_filter: PF, - use_hitcounts: bool, - use_jit: bool, -} - -impl EdgeCoverageModuleBuilder { - pub fn build(self) -> Result, Error> { - Ok(EdgeCoverageModule::new( - self.address_filter, - self.page_filter, - self.variant, - self.use_hitcounts, - self.use_jit, - )) - } -} - -impl EdgeCoverageModuleBuilder { - fn new( - variant: V, - address_filter: AF, - page_filter: PF, - use_hitcounts: bool, - use_jit: bool, - ) -> Self { - Self { - variant, - address_filter, - page_filter, - use_hitcounts, - use_jit, - } - } - - #[must_use] - pub fn map_observer(self, map_observer: &mut O) -> EdgeCoverageModuleBuilder - where - O: VariableLengthMapObserver, - { - let map_ptr = map_observer.map_slice_mut().as_mut_ptr() as *mut u8; - let map_max_size = map_observer.map_slice_mut().len(); - let size_ptr = map_observer.as_mut().size_mut() as *mut usize; - - unsafe { - LIBAFL_QEMU_EDGES_MAP_PTR = map_ptr; - LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = size_ptr; - LIBAFL_QEMU_EDGES_MAP_ALLOCATED_SIZE = map_max_size; - LIBAFL_QEMU_EDGES_MAP_MASK_MAX = map_max_size - 1; - } - - EdgeCoverageModuleBuilder::::new( - self.variant, - self.address_filter, - self.page_filter, - self.use_hitcounts, - self.use_jit, - ) - } - - pub fn variant(self, variant: V2) -> EdgeCoverageModuleBuilder { - EdgeCoverageModuleBuilder::new( - variant, - self.address_filter, - self.page_filter, - self.use_hitcounts, - self.use_jit, - ) - } - - pub fn address_filter( - self, - address_filter: AF2, - ) -> EdgeCoverageModuleBuilder { - EdgeCoverageModuleBuilder::new( - self.variant, - address_filter, - self.page_filter, - self.use_hitcounts, - self.use_jit, - ) - } - - pub fn page_filter( - self, - page_filter: PF2, - ) -> EdgeCoverageModuleBuilder { - EdgeCoverageModuleBuilder::new( - self.variant, - self.address_filter, - page_filter, - self.use_hitcounts, - self.use_jit, - ) - } - - #[must_use] - pub fn hitcounts( - self, - use_hitcounts: bool, - ) -> EdgeCoverageModuleBuilder { - EdgeCoverageModuleBuilder::new( - self.variant, - self.address_filter, - self.page_filter, - use_hitcounts, - self.use_jit, - ) - } - - #[must_use] - pub fn jit(self, use_jit: bool) -> EdgeCoverageModuleBuilder { - EdgeCoverageModuleBuilder::new( - self.variant, - self.address_filter, - self.page_filter, - self.use_hitcounts, - use_jit, - ) - } -} - -impl EdgeCoverageModule { - #[must_use] - pub fn new( - address_filter: AF, - page_filter: PF, - variant: V, - use_hitcounts: bool, - use_jit: bool, - ) -> Self { - Self { - variant, - address_filter, - page_filter, - use_hitcounts, - use_jit, - } - } -} - -impl EdgeCoverageModule -where - AF: AddressFilter, - PF: PageFilter, -{ - #[cfg(feature = "usermode")] - #[must_use] - pub fn must_instrument(&self, addr: GuestAddr) -> bool { - self.address_filter.allowed(&addr) - } - - #[cfg(feature = "systemmode")] - #[must_use] - pub fn must_instrument(&self, addr: GuestAddr, page_id: Option) -> bool { - if let Some(page_id) = page_id { - self.address_filter.allowed(&addr) && self.page_filter.allowed(&page_id) - } else { - self.address_filter.allowed(&addr) - } - } -} - -impl EmulatorModule for EdgeCoverageModule -where - AF: AddressFilter + 'static, - PF: PageFilter + 'static, - S: Unpin + UsesInput + HasMetadata, - V: EdgeCoverageVariant + 'static, -{ - const HOOKS_DO_SIDE_EFFECTS: bool = V::DO_SIDE_EFFECTS; - - type ModuleAddressFilter = AF; - #[cfg(feature = "systemmode")] - type ModulePageFilter = PF; - - fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) - where - ET: EmulatorModuleTuple, - { - if self.use_hitcounts { - if self.use_jit { - self.variant.jit_hitcount(emulator_modules); - } else { - self.variant.fn_hitcount(emulator_modules); - } - } else if self.use_jit { - self.variant.jit_no_hitcount(emulator_modules); - } else { - self.variant.fn_no_hitcount(emulator_modules); - } - } - - fn address_filter(&self) -> &Self::ModuleAddressFilter { - &self.address_filter - } - - fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter { - &mut self.address_filter - } - - #[cfg(feature = "systemmode")] - fn page_filter(&self) -> &Self::ModulePageFilter { - &self.page_filter - } - - #[cfg(feature = "systemmode")] - fn page_filter_mut(&mut self) -> &mut Self::ModulePageFilter { - &mut self.page_filter - } -} - -thread_local!(static PREV_LOC : UnsafeCell = const { UnsafeCell::new(0) }); -pub fn gen_unique_edge_ids( - emulator_modules: &mut EmulatorModules, - state: Option<&mut S>, - src: GuestAddr, - dest: GuestAddr, -) -> Option -where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - V: EdgeCoverageVariant, -{ - if let Some(module) = emulator_modules.get::>() { - unsafe { - assert!(LIBAFL_QEMU_EDGES_MAP_MASK_MAX > 0); - assert_ne!(*addr_of!(LIBAFL_QEMU_EDGES_MAP_SIZE_PTR), ptr::null_mut()); - } - - #[cfg(feature = "usermode")] - { - if !module.must_instrument(src) && !module.must_instrument(dest) { - return None; - } - } - - #[cfg(feature = "systemmode")] - { - let paging_id = emulator_modules - .qemu() - .current_cpu() - .and_then(|cpu| cpu.current_paging_id()); - - if !module.must_instrument(src, paging_id) && !module.must_instrument(dest, paging_id) { - return None; - } - } - } - - let state = state.expect("The gen_unique_edge_ids hook works only for in-process fuzzing"); - let meta = state.metadata_or_insert_with(QemuEdgesMapMetadata::new); - - match meta.map.entry((src, dest)) { - Entry::Occupied(e) => { - let id = *e.get(); - unsafe { - let nxt = (id as usize + 1) & LIBAFL_QEMU_EDGES_MAP_MASK_MAX; - *LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = max(*LIBAFL_QEMU_EDGES_MAP_SIZE_PTR, nxt); - } - Some(id) - } - Entry::Vacant(e) => { - let id = meta.current_id; - e.insert(id); - unsafe { - meta.current_id = (id + 1) & (LIBAFL_QEMU_EDGES_MAP_MASK_MAX as u64); - *LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = meta.current_id as usize; - } - // GuestAddress is u32 for 32 bit guests - #[allow(clippy::unnecessary_cast)] - Some(id as u64) - } - } -} - -/// # Safety -/// -/// Calling this concurrently for the same id is racey and may lose updates. -pub unsafe extern "C" fn trace_edge_hitcount(_: *const (), id: u64) { - unsafe { - EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1); - } -} - -pub extern "C" fn trace_edge_single(_: *const (), id: u64) { - // # Safety - // Worst case we set the byte to 1 multiple times.. - unsafe { - EDGES_MAP[id as usize] = 1; - } -} - -#[allow(clippy::unnecessary_cast)] -pub fn gen_hashed_edge_ids( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - src: GuestAddr, - dest: GuestAddr, -) -> Option -where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - V: EdgeCoverageVariant, -{ - if let Some(module) = emulator_modules.get::>() { - #[cfg(feature = "usermode")] - if !module.must_instrument(src) && !module.must_instrument(dest) { - return None; - } - - #[cfg(feature = "systemmode")] - { - let paging_id = emulator_modules - .qemu() - .current_cpu() - .and_then(|cpu| cpu.current_paging_id()); - - if !module.must_instrument(src, paging_id) && !module.must_instrument(dest, paging_id) { - return None; - } - } - - let id = hash_me(src as u64) ^ hash_me(dest as u64); - - unsafe { - let nxt = (id as usize + 1) & LIBAFL_QEMU_EDGES_MAP_MASK_MAX; - *LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = nxt; - } - - // GuestAddress is u32 for 32 bit guests - #[allow(clippy::unnecessary_cast)] - Some(id) - } else { - None - } -} - -/// # Safety -/// Increases id at `EDGES_MAP_PTR` - potentially racey if called concurrently. -pub unsafe extern "C" fn trace_edge_hitcount_ptr(_: *const (), id: u64) { - unsafe { - let ptr = LIBAFL_QEMU_EDGES_MAP_PTR.add(id as usize); - *ptr = (*ptr).wrapping_add(1); - } -} - -/// # Safety -/// Fine. -/// Worst case we set the byte to 1 multiple times. -pub unsafe extern "C" fn trace_edge_single_ptr(_: *const (), id: u64) { - unsafe { - let ptr = LIBAFL_QEMU_EDGES_MAP_PTR.add(id as usize); - *ptr = 1; - } -} - -#[allow(clippy::unnecessary_cast)] -pub fn gen_hashed_block_ids( - emulator_modules: &mut EmulatorModules, - _state: Option<&mut S>, - pc: GuestAddr, -) -> Option -where - AF: AddressFilter, - ET: EmulatorModuleTuple, - PF: PageFilter, - S: Unpin + UsesInput + HasMetadata, - V: EdgeCoverageVariant, -{ - // first check if we should filter - if let Some(module) = emulator_modules.get::>() { - #[cfg(feature = "usermode")] - { - if !module.must_instrument(pc) { - return None; - } - } - #[cfg(feature = "systemmode")] - { - let page_id = emulator_modules - .qemu() - .current_cpu() - .and_then(|cpu| cpu.current_paging_id()); - - if !module.must_instrument(pc, page_id) { - return None; - } - } - } - - let id = hash_me(pc as u64); - - unsafe { - let nxt = (id as usize + 1) & LIBAFL_QEMU_EDGES_MAP_MASK_MAX; - *LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = nxt; - } - - // GuestAddress is u32 for 32 bit guests - #[allow(clippy::unnecessary_cast)] - Some(id) -} - -/// # Safety -/// Dereferences the global `PREV_LOC` variable. May not be called concurrently. -pub unsafe extern "C" fn trace_block_transition_hitcount(_: *const (), id: u64) { - unsafe { - PREV_LOC.with(|prev_loc| { - let x = ((*prev_loc.get() ^ id) as usize) & LIBAFL_QEMU_EDGES_MAP_MASK_MAX; - let entry = LIBAFL_QEMU_EDGES_MAP_PTR.add(x); - *entry = (*entry).wrapping_add(1); - *prev_loc.get() = id.overflowing_shr(1).0; - }); - } -} - -/// # Safety -/// Dereferences the global `PREV_LOC` variable. May not be called concurrently. -pub unsafe extern "C" fn trace_block_transition_single(_: *const (), id: u64) { - unsafe { - PREV_LOC.with(|prev_loc| { - let x = ((*prev_loc.get() ^ id) as usize) & LIBAFL_QEMU_EDGES_MAP_MASK_MAX; - let entry = LIBAFL_QEMU_EDGES_MAP_PTR.add(x); - *entry = 1; - *prev_loc.get() = id.overflowing_shr(1).0; - }); - } -} diff --git a/libafl_qemu/src/modules/edges/child.rs b/libafl_qemu/src/modules/edges/child.rs new file mode 100644 index 0000000000..b823621940 --- /dev/null +++ b/libafl_qemu/src/modules/edges/child.rs @@ -0,0 +1,77 @@ +use libafl::{inputs::UsesInput, HasMetadata}; + +use super::{ + helpers::{gen_hashed_edge_ids, trace_edge_hitcount_ptr, trace_edge_single_ptr}, + EdgeCoverageVariant, +}; +use crate::{ + modules::{ + AddressFilter, EdgeCoverageModule, EdgeCoverageModuleBuilder, EmulatorModuleTuple, + PageFilter, StdAddressFilter, StdPageFilter, + }, + EmulatorModules, Hook, +}; + +#[derive(Debug)] +pub struct EdgeCoverageChildVariant; +pub type StdEdgeCoverageChildModule = + EdgeCoverageModule; +pub type StdEdgeCoverageChildModuleBuilder = EdgeCoverageModuleBuilder< + StdAddressFilter, + StdPageFilter, + EdgeCoverageChildVariant, + false, + false, + 0, +>; + +impl + EdgeCoverageVariant for EdgeCoverageChildVariant +{ + const DO_SIDE_EFFECTS: bool = false; + + fn fn_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + emulator_modules.edges( + Hook::Function(gen_hashed_edge_ids::), + Hook::Raw(trace_edge_hitcount_ptr), + ); + } + + fn fn_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + emulator_modules.edges( + Hook::Function(gen_hashed_edge_ids::), + Hook::Raw(trace_edge_single_ptr), + ); + } +} + +impl Default for StdEdgeCoverageChildModuleBuilder { + fn default() -> Self { + Self { + variant: EdgeCoverageChildVariant, + address_filter: StdAddressFilter::default(), + page_filter: StdPageFilter::default(), + use_hitcounts: true, + use_jit: true, + } + } +} + +impl StdEdgeCoverageChildModule { + #[must_use] + pub fn builder() -> StdEdgeCoverageChildModuleBuilder { + EdgeCoverageModuleBuilder::default().jit(false) + } +} diff --git a/libafl_qemu/src/modules/edges/classic.rs b/libafl_qemu/src/modules/edges/classic.rs new file mode 100644 index 0000000000..ed16c124ef --- /dev/null +++ b/libafl_qemu/src/modules/edges/classic.rs @@ -0,0 +1,124 @@ +use libafl::{inputs::UsesInput, HasMetadata}; + +use super::{ + helpers::{ + gen_hashed_block_ids, trace_block_transition_hitcount, trace_block_transition_single, + }, + EdgeCoverageVariant, +}; +use crate::{ + modules::{ + AddressFilter, EdgeCoverageModule, EdgeCoverageModuleBuilder, EmulatorModuleTuple, + PageFilter, StdAddressFilter, StdPageFilter, + }, + EmulatorModules, Hook, +}; + +#[derive(Debug)] +pub struct EdgeCoverageClassicVariant; + +pub type StdEdgeCoverageClassicModule = + EdgeCoverageModule; +pub type StdEdgeCoverageClassicModuleBuilder = EdgeCoverageModuleBuilder< + StdAddressFilter, + StdPageFilter, + EdgeCoverageClassicVariant, + false, + false, + 0, +>; + +impl + EdgeCoverageVariant for EdgeCoverageClassicVariant +{ + const DO_SIDE_EFFECTS: bool = false; + + fn jit_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + let hook_id = emulator_modules.blocks( + Hook::Function(gen_hashed_block_ids::), + Hook::Empty, + Hook::Empty, + ); + + unsafe { + libafl_qemu_sys::libafl_qemu_block_hook_set_jit( + hook_id.0, + Some(libafl_qemu_sys::libafl_jit_trace_block_hitcount), + ); + } + } + + fn jit_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + let hook_id = emulator_modules.blocks( + Hook::Function(gen_hashed_block_ids::), + Hook::Empty, + Hook::Empty, + ); + + unsafe { + libafl_qemu_sys::libafl_qemu_block_hook_set_jit( + hook_id.0, + Some(libafl_qemu_sys::libafl_jit_trace_block_single), + ); + } + } + + fn fn_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + emulator_modules.blocks( + Hook::Function(gen_hashed_block_ids::), + Hook::Empty, + Hook::Raw(trace_block_transition_hitcount), + ); + } + + fn fn_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + emulator_modules.blocks( + Hook::Function(gen_hashed_block_ids::), + Hook::Empty, + Hook::Raw(trace_block_transition_single), + ); + } +} + +impl Default for StdEdgeCoverageClassicModuleBuilder { + fn default() -> Self { + Self { + variant: EdgeCoverageClassicVariant, + address_filter: StdAddressFilter::default(), + page_filter: StdPageFilter::default(), + use_hitcounts: true, + use_jit: true, + } + } +} + +impl StdEdgeCoverageClassicModule { + #[must_use] + pub fn builder() -> StdEdgeCoverageClassicModuleBuilder { + EdgeCoverageModuleBuilder::default() + } +} diff --git a/libafl_qemu/src/modules/edges/full.rs b/libafl_qemu/src/modules/edges/full.rs new file mode 100644 index 0000000000..43de4a45d8 --- /dev/null +++ b/libafl_qemu/src/modules/edges/full.rs @@ -0,0 +1,114 @@ +use libafl::{inputs::UsesInput, HasMetadata}; + +use super::{ + helpers::{gen_unique_edge_ids, trace_edge_hitcount, trace_edge_single}, + EdgeCoverageVariant, +}; +use crate::{ + modules::{ + AddressFilter, EdgeCoverageModule, EdgeCoverageModuleBuilder, EmulatorModuleTuple, + PageFilter, StdAddressFilter, StdPageFilter, + }, + EmulatorModules, Hook, +}; + +#[derive(Debug)] +pub struct EdgeCoverageFullVariant; + +pub type StdEdgeCoverageFullModule = + EdgeCoverageModule; +pub type StdEdgeCoverageFullModuleBuilder = EdgeCoverageModuleBuilder< + StdAddressFilter, + StdPageFilter, + EdgeCoverageFullVariant, + false, + false, + 0, +>; + +impl + EdgeCoverageVariant for EdgeCoverageFullVariant +{ + fn jit_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + let hook_id = emulator_modules.edges( + Hook::Function(gen_unique_edge_ids::), + Hook::Empty, + ); + unsafe { + libafl_qemu_sys::libafl_qemu_edge_hook_set_jit( + hook_id.0, + Some(libafl_qemu_sys::libafl_jit_trace_edge_hitcount), + ); + } + } + + fn jit_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + let hook_id = emulator_modules.edges( + Hook::Function(gen_unique_edge_ids::), + Hook::Empty, + ); + unsafe { + libafl_qemu_sys::libafl_qemu_edge_hook_set_jit( + hook_id.0, + Some(libafl_qemu_sys::libafl_jit_trace_edge_single), + ); + } + } + + fn fn_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + emulator_modules.edges( + Hook::Function(gen_unique_edge_ids::), + Hook::Raw(trace_edge_hitcount), + ); + } + + fn fn_no_hitcount(&mut self, emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + emulator_modules.edges( + Hook::Function(gen_unique_edge_ids::), + Hook::Raw(trace_edge_single), + ); + } +} + +impl Default for StdEdgeCoverageFullModuleBuilder { + fn default() -> Self { + Self { + variant: EdgeCoverageFullVariant, + address_filter: StdAddressFilter::default(), + page_filter: StdPageFilter::default(), + use_hitcounts: true, + use_jit: true, + } + } +} + +impl StdEdgeCoverageFullModule { + #[must_use] + pub fn builder() -> StdEdgeCoverageFullModuleBuilder { + EdgeCoverageModuleBuilder::default() + } +} diff --git a/libafl_qemu/src/modules/edges/helpers.rs b/libafl_qemu/src/modules/edges/helpers.rs new file mode 100644 index 0000000000..bf9f0255e9 --- /dev/null +++ b/libafl_qemu/src/modules/edges/helpers.rs @@ -0,0 +1,345 @@ +use std::ptr; + +/// Generators, responsible for generating block/edge ids +pub use generators::{gen_hashed_block_ids, gen_hashed_edge_ids, gen_unique_edge_ids}; +use hashbrown::HashMap; +use libafl_qemu_sys::GuestAddr; +use serde::{Deserialize, Serialize}; +/// Tracers, responsible for propagating an ID in a map. +pub use tracers::{ + trace_block_transition_hitcount, trace_block_transition_single, trace_edge_hitcount, + trace_edge_hitcount_ptr, trace_edge_single, trace_edge_single_ptr, +}; + +// Constants used for variable-length maps + +#[no_mangle] +pub(super) static mut LIBAFL_QEMU_EDGES_MAP_PTR: *mut u8 = ptr::null_mut(); + +#[no_mangle] +pub(super) static mut LIBAFL_QEMU_EDGES_MAP_SIZE_PTR: *mut usize = ptr::null_mut(); + +#[no_mangle] +pub(super) static mut LIBAFL_QEMU_EDGES_MAP_ALLOCATED_SIZE: usize = 0; + +#[no_mangle] +pub(super) static mut LIBAFL_QEMU_EDGES_MAP_MASK_MAX: usize = 0; + +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] // for SerdeAny +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct QemuEdgesMapMetadata { + pub map: HashMap<(GuestAddr, GuestAddr), u64>, + pub current_id: u64, +} + +libafl_bolts::impl_serdeany!(QemuEdgesMapMetadata); + +impl QemuEdgesMapMetadata { + #[must_use] + pub fn new() -> Self { + Self { + map: HashMap::new(), + current_id: 0, + } + } +} + +mod generators { + use std::{cmp::max, ptr, ptr::addr_of}; + + use hashbrown::hash_map::Entry; + use libafl::{inputs::UsesInput, HasMetadata}; + use libafl_qemu_sys::GuestAddr; + + use super::{ + super::EdgeCoverageVariant, QemuEdgesMapMetadata, LIBAFL_QEMU_EDGES_MAP_MASK_MAX, + LIBAFL_QEMU_EDGES_MAP_SIZE_PTR, + }; + use crate::{ + modules::{hash_me, AddressFilter, EdgeCoverageModule, EmulatorModuleTuple, PageFilter}, + EmulatorModules, + }; + + fn get_mask() -> usize { + if IS_CONST_MAP { + const { + assert!( + !IS_CONST_MAP || MAP_SIZE > 0, + "The size of a const map should be bigger than 0." + ); + MAP_SIZE.overflowing_sub(1).0 + } + } else { + unsafe { LIBAFL_QEMU_EDGES_MAP_MASK_MAX } + } + } + + pub fn gen_unique_edge_ids( + emulator_modules: &mut EmulatorModules, + state: Option<&mut S>, + src: GuestAddr, + dest: GuestAddr, + ) -> Option + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + V: EdgeCoverageVariant, + { + if let Some(module) = + emulator_modules.get::>() + { + unsafe { + assert!(LIBAFL_QEMU_EDGES_MAP_MASK_MAX > 0); + assert_ne!(*addr_of!(LIBAFL_QEMU_EDGES_MAP_SIZE_PTR), ptr::null_mut()); + } + + #[cfg(feature = "usermode")] + { + if !module.must_instrument(src) && !module.must_instrument(dest) { + return None; + } + } + + #[cfg(feature = "systemmode")] + { + let paging_id = emulator_modules + .qemu() + .current_cpu() + .and_then(|cpu| cpu.current_paging_id()); + + if !module.must_instrument(src, paging_id) + && !module.must_instrument(dest, paging_id) + { + return None; + } + } + } + + let mask: usize = get_mask::(); + + let state = state.expect("The gen_unique_edge_ids hook works only for in-process fuzzing"); + let meta = state.metadata_or_insert_with(QemuEdgesMapMetadata::new); + + match meta.map.entry((src, dest)) { + Entry::Occupied(e) => { + let id = *e.get(); + unsafe { + let nxt = (id as usize + 1) & mask; + + if !IS_CONST_MAP { + *LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = max(*LIBAFL_QEMU_EDGES_MAP_SIZE_PTR, nxt); + } + } + Some(id) + } + Entry::Vacant(e) => { + let id = meta.current_id; + e.insert(id); + unsafe { + meta.current_id = (id + 1) & (mask as u64); + + if !IS_CONST_MAP { + *LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = meta.current_id as usize; + } + } + // GuestAddress is u32 for 32 bit guests + #[allow(clippy::unnecessary_cast)] + Some(id as u64) + } + } + } + + #[allow(clippy::unnecessary_cast)] + pub fn gen_hashed_edge_ids( + emulator_modules: &mut EmulatorModules, + _state: Option<&mut S>, + src: GuestAddr, + dest: GuestAddr, + ) -> Option + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + V: EdgeCoverageVariant, + { + if let Some(module) = + emulator_modules.get::>() + { + #[cfg(feature = "usermode")] + if !module.must_instrument(src) && !module.must_instrument(dest) { + return None; + } + + #[cfg(feature = "systemmode")] + { + let paging_id = emulator_modules + .qemu() + .current_cpu() + .and_then(|cpu| cpu.current_paging_id()); + + if !module.must_instrument(src, paging_id) + && !module.must_instrument(dest, paging_id) + { + return None; + } + } + + let mask: usize = get_mask::(); + + let id = hash_me(src as u64) ^ hash_me(dest as u64); + + unsafe { + let nxt = (id as usize + 1) & mask; + + if !IS_CONST_MAP { + *LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = nxt; + } + } + + // GuestAddress is u32 for 32 bit guests + #[allow(clippy::unnecessary_cast)] + Some(id) + } else { + None + } + } + + #[allow(clippy::unnecessary_cast)] + pub fn gen_hashed_block_ids( + emulator_modules: &mut EmulatorModules, + _state: Option<&mut S>, + pc: GuestAddr, + ) -> Option + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + V: EdgeCoverageVariant, + { + // first check if we should filter + if let Some(module) = + emulator_modules.get::>() + { + #[cfg(feature = "usermode")] + { + if !module.must_instrument(pc) { + return None; + } + } + #[cfg(feature = "systemmode")] + { + let page_id = emulator_modules + .qemu() + .current_cpu() + .and_then(|cpu| cpu.current_paging_id()); + + if !module.must_instrument(pc, page_id) { + return None; + } + } + } + + let mask: usize = get_mask::(); + + let id = hash_me(pc as u64); + + unsafe { + let nxt = (id as usize + 1) & mask; + + if !IS_CONST_MAP { + *LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = nxt; + } + } + + // GuestAddress is u32 for 32 bit guests + #[allow(clippy::unnecessary_cast)] + Some(id) + } +} + +mod tracers { + use std::cell::UnsafeCell; + + use libafl_targets::EDGES_MAP; + + use super::{LIBAFL_QEMU_EDGES_MAP_MASK_MAX, LIBAFL_QEMU_EDGES_MAP_PTR}; + + thread_local!(static PREV_LOC : UnsafeCell = const { UnsafeCell::new(0) }); + + /// # Safety + /// + /// - @id should be the one generated by a gen_* function from this module. + /// - Calling this concurrently for the same id is racey and may lose updates. + pub unsafe extern "C" fn trace_edge_hitcount(_: *const (), id: u64) { + unsafe { + EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1); + } + } + + /// # Safety + /// + /// - @id should be the one generated by a gen_* function from this module. + pub unsafe extern "C" fn trace_edge_single(_: *const (), id: u64) { + // # Safety + // Worst case we set the byte to 1 multiple times.. + unsafe { + EDGES_MAP[id as usize] = 1; + } + } + + /// # Safety + /// + /// Increases id at `EDGES_MAP_PTR` - potentially racey if called concurrently. + pub unsafe extern "C" fn trace_edge_hitcount_ptr(_: *const (), id: u64) { + unsafe { + let ptr = LIBAFL_QEMU_EDGES_MAP_PTR.add(id as usize); + *ptr = (*ptr).wrapping_add(1); + } + } + + /// # Safety + /// + /// Fine. + /// Worst case we set the byte to 1 multiple times. + pub unsafe extern "C" fn trace_edge_single_ptr(_: *const (), id: u64) { + unsafe { + let ptr = LIBAFL_QEMU_EDGES_MAP_PTR.add(id as usize); + *ptr = 1; + } + } + + /// # Safety + /// + /// Dereferences the global `PREV_LOC` variable. May not be called concurrently. + pub unsafe extern "C" fn trace_block_transition_hitcount(_: *const (), id: u64) { + unsafe { + PREV_LOC.with(|prev_loc| { + let x = ((*prev_loc.get() ^ id) as usize) & LIBAFL_QEMU_EDGES_MAP_MASK_MAX; + let entry = LIBAFL_QEMU_EDGES_MAP_PTR.add(x); + *entry = (*entry).wrapping_add(1); + *prev_loc.get() = id.overflowing_shr(1).0; + }); + } + } + + /// # Safety + /// + /// Dereferences the global `PREV_LOC` variable. May not be called concurrently. + pub unsafe extern "C" fn trace_block_transition_single(_: *const (), id: u64) { + unsafe { + PREV_LOC.with(|prev_loc| { + let x = ((*prev_loc.get() ^ id) as usize) & LIBAFL_QEMU_EDGES_MAP_MASK_MAX; + let entry = LIBAFL_QEMU_EDGES_MAP_PTR.add(x); + *entry = 1; + *prev_loc.get() = id.overflowing_shr(1).0; + }); + } + } +} diff --git a/libafl_qemu/src/modules/edges/mod.rs b/libafl_qemu/src/modules/edges/mod.rs new file mode 100644 index 0000000000..e3fc3efb4e --- /dev/null +++ b/libafl_qemu/src/modules/edges/mod.rs @@ -0,0 +1,403 @@ +use std::fmt::Debug; + +use libafl::{inputs::UsesInput, observers::VarLenMapObserver, HasMetadata}; +use libafl_bolts::Error; +use libafl_qemu_sys::GuestAddr; +#[cfg(feature = "systemmode")] +use libafl_qemu_sys::GuestPhysAddr; + +use crate::{ + emu::EmulatorModules, + modules::{AddressFilter, EmulatorModule, EmulatorModuleTuple, PageFilter}, +}; + +mod helpers; +use helpers::{ + LIBAFL_QEMU_EDGES_MAP_ALLOCATED_SIZE, LIBAFL_QEMU_EDGES_MAP_MASK_MAX, + LIBAFL_QEMU_EDGES_MAP_PTR, LIBAFL_QEMU_EDGES_MAP_SIZE_PTR, +}; + +pub mod full; +pub use full::{ + EdgeCoverageFullVariant, StdEdgeCoverageFullModule, StdEdgeCoverageFullModuleBuilder, +}; + +pub mod classic; +pub use classic::{ + EdgeCoverageClassicVariant, StdEdgeCoverageClassicModule, StdEdgeCoverageClassicModuleBuilder, +}; + +pub mod child; +pub use child::{ + EdgeCoverageChildVariant, StdEdgeCoverageChildModule, StdEdgeCoverageChildModuleBuilder, +}; +use libafl::observers::ConstLenMapObserver; + +/// Standard edge coverage module, adapted to most use cases +pub type StdEdgeCoverageModule = StdEdgeCoverageFullModule; + +/// Standard edge coverage module builder, adapted to most use cases +pub type StdEdgeCoverageModuleBuilder = StdEdgeCoverageFullModuleBuilder; + +pub type CollidingEdgeCoverageModule = + EdgeCoverageModule; + +/// An edge coverage module variant. +trait EdgeCoverageVariant: + 'static + Debug +{ + const DO_SIDE_EFFECTS: bool = true; + + fn jit_hitcount(&mut self, _emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + panic!("JIT hitcount is not supported.") + } + + fn jit_no_hitcount(&mut self, _emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + panic!("JIT no hitcount is not supported.") + } + + fn fn_hitcount(&mut self, _emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + panic!("Func hitcount is not supported.") + } + + fn fn_no_hitcount(&mut self, _emulator_modules: &mut EmulatorModules) + where + AF: AddressFilter, + ET: EmulatorModuleTuple, + PF: PageFilter, + S: Unpin + UsesInput + HasMetadata, + { + panic!("Func no hitcount is not supported.") + } +} + +#[derive(Debug)] +pub struct EdgeCoverageModuleBuilder< + AF, + PF, + V, + const IS_INITIALIZED: bool, + const IS_CONST_MAP: bool, + const MAP_SIZE: usize, +> { + variant: V, + address_filter: AF, + page_filter: PF, + use_hitcounts: bool, + use_jit: bool, +} + +#[derive(Debug)] +pub struct EdgeCoverageModule { + variant: V, + address_filter: AF, + // we only use it in system mode at the moment. + #[cfg_attr(not(feature = "systemmode"), allow(dead_code))] + page_filter: PF, + use_hitcounts: bool, + use_jit: bool, +} + +impl + EdgeCoverageModuleBuilder +{ + pub fn build(self) -> Result, Error> { + const { + assert!( + IS_INITIALIZED, + "The edge module builder must be first initialized with a call to `map_observer`." + ); + }; + + Ok(EdgeCoverageModule::new( + self.address_filter, + self.page_filter, + self.variant, + self.use_hitcounts, + self.use_jit, + )) + } +} + +impl + EdgeCoverageModuleBuilder +{ + fn new( + variant: V, + address_filter: AF, + page_filter: PF, + use_hitcounts: bool, + use_jit: bool, + ) -> Self { + Self { + variant, + address_filter, + page_filter, + use_hitcounts, + use_jit, + } + } + + #[must_use] + pub fn map_observer( + self, + map_observer: &mut O, + ) -> EdgeCoverageModuleBuilder + where + O: VarLenMapObserver, + { + let map_ptr = map_observer.map_slice_mut().as_mut_ptr() as *mut u8; + let map_max_size = map_observer.map_slice_mut().len(); + let size_ptr = map_observer.as_mut().size_mut() as *mut usize; + + unsafe { + LIBAFL_QEMU_EDGES_MAP_PTR = map_ptr; + LIBAFL_QEMU_EDGES_MAP_SIZE_PTR = size_ptr; + LIBAFL_QEMU_EDGES_MAP_ALLOCATED_SIZE = map_max_size; + LIBAFL_QEMU_EDGES_MAP_MASK_MAX = map_max_size - 1; + } + + EdgeCoverageModuleBuilder::::new( + self.variant, + self.address_filter, + self.page_filter, + self.use_hitcounts, + self.use_jit, + ) + } + + #[must_use] + pub fn const_map_observer( + self, + _const_map_observer: &mut O, + ) -> EdgeCoverageModuleBuilder + where + O: ConstLenMapObserver, + { + EdgeCoverageModuleBuilder::::new( + self.variant, + self.address_filter, + self.page_filter, + self.use_hitcounts, + self.use_jit, + ) + } + + pub fn variant( + self, + variant: V2, + ) -> EdgeCoverageModuleBuilder { + EdgeCoverageModuleBuilder::new( + variant, + self.address_filter, + self.page_filter, + self.use_hitcounts, + self.use_jit, + ) + } + + pub fn address_filter( + self, + address_filter: AF2, + ) -> EdgeCoverageModuleBuilder { + EdgeCoverageModuleBuilder::new( + self.variant, + address_filter, + self.page_filter, + self.use_hitcounts, + self.use_jit, + ) + } + + pub fn page_filter( + self, + page_filter: PF2, + ) -> EdgeCoverageModuleBuilder { + EdgeCoverageModuleBuilder::new( + self.variant, + self.address_filter, + page_filter, + self.use_hitcounts, + self.use_jit, + ) + } + + #[must_use] + pub fn hitcounts( + self, + use_hitcounts: bool, + ) -> EdgeCoverageModuleBuilder { + EdgeCoverageModuleBuilder::new( + self.variant, + self.address_filter, + self.page_filter, + use_hitcounts, + self.use_jit, + ) + } + + #[must_use] + pub fn jit( + self, + use_jit: bool, + ) -> EdgeCoverageModuleBuilder { + EdgeCoverageModuleBuilder::new( + self.variant, + self.address_filter, + self.page_filter, + self.use_hitcounts, + use_jit, + ) + } +} + +impl + EdgeCoverageModule +{ + #[must_use] + pub fn new( + address_filter: AF, + page_filter: PF, + variant: V, + use_hitcounts: bool, + use_jit: bool, + ) -> Self { + Self { + variant, + address_filter, + page_filter, + use_hitcounts, + use_jit, + } + } +} + +impl + EdgeCoverageModule +where + AF: AddressFilter, + PF: PageFilter, +{ + #[cfg(feature = "usermode")] + #[must_use] + pub fn must_instrument(&self, addr: GuestAddr) -> bool { + self.address_filter.allowed(&addr) + } + + #[cfg(feature = "systemmode")] + #[must_use] + pub fn must_instrument(&self, addr: GuestAddr, page_id: Option) -> bool { + if let Some(page_id) = page_id { + self.address_filter.allowed(&addr) && self.page_filter.allowed(&page_id) + } else { + self.address_filter.allowed(&addr) + } + } +} + +impl EmulatorModule + for EdgeCoverageModule +where + AF: AddressFilter + 'static, + PF: PageFilter + 'static, + S: Unpin + UsesInput + HasMetadata, + V: EdgeCoverageVariant + 'static, +{ + type ModuleAddressFilter = AF; + + #[cfg(feature = "systemmode")] + type ModulePageFilter = PF; + const HOOKS_DO_SIDE_EFFECTS: bool = V::DO_SIDE_EFFECTS; + + fn first_exec(&mut self, emulator_modules: &mut EmulatorModules, _state: &mut S) + where + ET: EmulatorModuleTuple, + { + if self.use_hitcounts { + if self.use_jit { + self.variant.jit_hitcount(emulator_modules); + } else { + self.variant.fn_hitcount(emulator_modules); + } + } else if self.use_jit { + self.variant.jit_no_hitcount(emulator_modules); + } else { + self.variant.fn_no_hitcount(emulator_modules); + } + } + + fn address_filter(&self) -> &Self::ModuleAddressFilter { + &self.address_filter + } + + fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter { + &mut self.address_filter + } + + #[cfg(feature = "systemmode")] + fn page_filter(&self) -> &Self::ModulePageFilter { + &self.page_filter + } + + #[cfg(feature = "systemmode")] + fn page_filter_mut(&mut self) -> &mut Self::ModulePageFilter { + &mut self.page_filter + } +} + +#[cfg(any(test, doc))] +mod tests { + use std::ptr::addr_of_mut; + + use libafl::observers::{CanTrack, HitcountsMapObserver, VariableMapObserver}; + use libafl_bolts::ownedref::OwnedMutSlice; + use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND}; + + use crate::modules::StdEdgeCoverageModule; + + /// The test is actually implemented as a doctest, since Rust does not + /// permit tests that must not compile by default... + /// + /// ```compile_fail + /// use libafl_qemu::modules::StdEdgeCoverageModule; + /// + /// StdEdgeCoverageModule::builder().build().unwrap(); + /// ``` + #[allow(unused)] + pub fn does_not_build() {} + + #[test] + pub fn does_build() { + let mut edges_observer = unsafe { + HitcountsMapObserver::new(VariableMapObserver::from_mut_slice( + "edges", + OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_DEFAULT_SIZE), + addr_of_mut!(MAX_EDGES_FOUND), + )) + .track_indices() + }; + + StdEdgeCoverageModule::builder() + .map_observer(edges_observer.as_mut()) + .build() + .unwrap(); + } +} diff --git a/libafl_qemu/src/modules/mod.rs b/libafl_qemu/src/modules/mod.rs index 4ebe0e8865..73ce932ade 100644 --- a/libafl_qemu/src/modules/mod.rs +++ b/libafl_qemu/src/modules/mod.rs @@ -18,7 +18,12 @@ pub mod systemmode; pub use systemmode::*; pub mod edges; -pub use edges::*; +pub use edges::{ + EdgeCoverageModule, EdgeCoverageModuleBuilder, StdEdgeCoverageChildModule, + StdEdgeCoverageChildModuleBuilder, StdEdgeCoverageClassicModule, + StdEdgeCoverageClassicModuleBuilder, StdEdgeCoverageFullModule, + StdEdgeCoverageFullModuleBuilder, StdEdgeCoverageModule, StdEdgeCoverageModuleBuilder, +}; #[cfg(not(cpu_target = "hexagon"))] pub mod calls; @@ -33,7 +38,7 @@ pub use cmplog::CmpLogModule; #[cfg(not(cpu_target = "hexagon"))] pub mod drcov; #[cfg(not(cpu_target = "hexagon"))] -pub use drcov::*; +pub use drcov::{DrCovMetadata, DrCovModule, DrCovModuleBuilder}; use crate::{emu::EmulatorModules, Qemu};