Compile-time edge module compilation check, native support for ConstMapObserver (#2592)
* compile-time edge module compilation trick * clippy * possible since rust 1.79 * split edge module in submodules * Update frida to 0.14.0 (#2596) * update frida crate to the latest version * adapt libafl_frida to the latest version of frida * tracers and generators private modules * do not use star export. * same for drcov * forgot a file... * first draft of generic-based edge module for ConstantLengthMapObserver. * integration of OwnedSizedSlice. replaced OwnedSlice in ConstMapObserver by the new OwnedSizedSlice. * fix serde stuff * no std * import * fixed qemu_cmin with new constant map abstraction. * fix const map * fix clippy from another pr... * fix non-null usage * fix ci? * new feature stuff * fixes * minor fixes * fmt * non null * im stupid * fmt * fix fuzzer * fix fuzzers * sized slice * fuzzer fixes * ptr::NonNull -> NonNull * shorter trait length * fmt
This commit is contained in:
parent
56a5463ae4
commit
49ea0b03a6
@ -5,7 +5,7 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Install QEMU deps
|
- name: Install QEMU deps
|
||||||
shell: bash
|
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
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- name: enable mult-thread for `make`
|
- name: enable mult-thread for `make`
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{path::PathBuf, time::Duration};
|
use std::{path::PathBuf, ptr::NonNull, time::Duration};
|
||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{InMemoryCorpus, OnDiskCorpus},
|
corpus::{InMemoryCorpus, OnDiskCorpus},
|
||||||
@ -46,7 +46,12 @@ pub fn main() {
|
|||||||
libafl::executors::ExitKind::Ok
|
libafl::executors::ExitKind::Ok
|
||||||
};
|
};
|
||||||
// Create an observation channel using the signals map
|
// Create an observation channel using the signals map
|
||||||
let observer = unsafe { ConstMapObserver::<u8, 3>::from_mut_ptr("signals", map_ptr) };
|
let observer = unsafe {
|
||||||
|
ConstMapObserver::<u8, 3>::from_mut_ptr(
|
||||||
|
"signals",
|
||||||
|
NonNull::new(map_ptr).expect("map ptr is null."),
|
||||||
|
)
|
||||||
|
};
|
||||||
// Create a stacktrace observer
|
// Create a stacktrace observer
|
||||||
let mut bt = shmem_provider.new_on_shmem::<Option<u64>>(None).unwrap();
|
let mut bt = shmem_provider.new_on_shmem::<Option<u64>>(None).unwrap();
|
||||||
let bt_observer = BacktraceObserver::new(
|
let bt_observer = BacktraceObserver::new(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::path::PathBuf;
|
use std::{path::PathBuf, ptr::NonNull};
|
||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{InMemoryCorpus, OnDiskCorpus},
|
corpus::{InMemoryCorpus, OnDiskCorpus},
|
||||||
@ -35,7 +35,12 @@ pub fn main() {
|
|||||||
libafl::executors::ExitKind::Ok
|
libafl::executors::ExitKind::Ok
|
||||||
};
|
};
|
||||||
// Create an observation channel using the signals map
|
// Create an observation channel using the signals map
|
||||||
let observer = unsafe { ConstMapObserver::<u8, 3>::from_mut_ptr("signals", array_ptr) };
|
let observer = unsafe {
|
||||||
|
ConstMapObserver::<u8, 3>::from_mut_ptr(
|
||||||
|
"signals",
|
||||||
|
NonNull::new(array_ptr).expect("map ptr is null"),
|
||||||
|
)
|
||||||
|
};
|
||||||
// Create a stacktrace observer
|
// Create a stacktrace observer
|
||||||
let bt_observer = BacktraceObserver::owned(
|
let bt_observer = BacktraceObserver::owned(
|
||||||
"BacktraceObserver",
|
"BacktraceObserver",
|
||||||
|
@ -42,7 +42,10 @@ pub fn main() {
|
|||||||
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
||||||
//let the forkserver know the shmid
|
//let the forkserver know the shmid
|
||||||
shmem.write_to_env("__AFL_SHM_ID").unwrap();
|
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
|
// Create an observation channel using the signals map
|
||||||
let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new(
|
let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new(
|
||||||
|
@ -9,6 +9,7 @@ use std::{
|
|||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process,
|
process,
|
||||||
|
ptr::NonNull,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,14 +161,14 @@ fn fuzz(
|
|||||||
let mut edges_observer = unsafe {
|
let mut edges_observer = unsafe {
|
||||||
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_DEFAULT_SIZE>::from_mut_ptr(
|
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_DEFAULT_SIZE>::from_mut_ptr(
|
||||||
"edges",
|
"edges",
|
||||||
edges.as_mut_ptr(),
|
NonNull::new(edges.as_mut_ptr()).expect("map ptr is null."),
|
||||||
))
|
))
|
||||||
.track_indices()
|
.track_indices()
|
||||||
};
|
};
|
||||||
|
|
||||||
let emulator_modules = tuple_list!(
|
let emulator_modules = tuple_list!(
|
||||||
StdEdgeCoverageChildModule::builder()
|
StdEdgeCoverageChildModule::builder()
|
||||||
.map_observer(edges_observer.as_mut())
|
.const_map_observer(edges_observer.as_mut())
|
||||||
.build()?,
|
.build()?,
|
||||||
CmpLogChildModule::default(),
|
CmpLogChildModule::default(),
|
||||||
);
|
);
|
||||||
@ -199,7 +200,8 @@ fn fuzz(
|
|||||||
|
|
||||||
let stack_ptr: u64 = qemu.read_reg(Regs::Sp).unwrap();
|
let stack_ptr: u64 = qemu.read_reg(Regs::Sp).unwrap();
|
||||||
let mut ret_addr = [0; 8];
|
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);
|
let ret_addr = u64::from_le_bytes(ret_addr);
|
||||||
|
|
||||||
println!("Stack pointer = {stack_ptr:#x}");
|
println!("Stack pointer = {stack_ptr:#x}");
|
||||||
@ -323,7 +325,7 @@ fn fuzz(
|
|||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// 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 target = input.target_bytes();
|
||||||
let mut buf = target.as_slice();
|
let mut buf = target.as_slice();
|
||||||
let mut len = buf.len();
|
let mut len = buf.len();
|
||||||
@ -333,7 +335,7 @@ fn fuzz(
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
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::Rdi, input_addr).unwrap();
|
||||||
qemu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
|
qemu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
|
||||||
@ -394,8 +396,8 @@ fn fuzz(
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
let null_fd = file_null.as_raw_fd();
|
let null_fd = file_null.as_raw_fd();
|
||||||
// dup2(null_fd, io::stdout().as_raw_fd())?;
|
dup2(null_fd, io::stdout().as_raw_fd())?;
|
||||||
// dup2(null_fd, io::stderr().as_raw_fd())?;
|
dup2(null_fd, io::stderr().as_raw_fd())?;
|
||||||
}
|
}
|
||||||
// reopen file to make sure we're at the end
|
// reopen file to make sure we're at the end
|
||||||
log.replace(
|
log.replace(
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
#[cfg(feature = "i386")]
|
#[cfg(feature = "i386")]
|
||||||
use core::mem::size_of;
|
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 clap::{builder::Str, Parser};
|
||||||
use libafl::{
|
use libafl::{
|
||||||
@ -162,7 +162,7 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
let mut edges_observer = unsafe {
|
let mut edges_observer = unsafe {
|
||||||
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_DEFAULT_SIZE>::from_mut_ptr(
|
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_DEFAULT_SIZE>::from_mut_ptr(
|
||||||
"edges",
|
"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;
|
let len = len as GuestReg;
|
||||||
|
|
||||||
unsafe {
|
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::Pc, test_one_input_ptr).unwrap();
|
||||||
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
||||||
qemu.write_return_address(ret_addr).unwrap();
|
qemu.write_return_address(ret_addr).unwrap();
|
||||||
@ -219,7 +219,7 @@ pub fn fuzz() -> Result<(), Error> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let modules = tuple_list!(StdEdgeCoverageChildModule::builder()
|
let modules = tuple_list!(StdEdgeCoverageChildModule::builder()
|
||||||
.map_observer(edges_observer.as_mut())
|
.const_map_observer(edges_observer.as_mut())
|
||||||
.build()?);
|
.build()?);
|
||||||
|
|
||||||
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
|
||||||
|
@ -1635,7 +1635,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
|
||||||
shmem.write_to_env("__AFL_SHM_ID").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(
|
let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new(
|
||||||
"shared_mem",
|
"shared_mem",
|
||||||
|
@ -5,28 +5,26 @@ use core::{
|
|||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
|
ptr::NonNull,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ahash::RandomState;
|
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 serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
observers::{map::MapObserver, Observer, VariableLengthMapObserver},
|
observers::{map::MapObserver, ConstLenMapObserver, Observer},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: remove the size field and implement ConstantLengthMapObserver
|
|
||||||
|
|
||||||
/// Use a const size to speedup `Feedback::is_interesting` when the user can
|
/// Use a const size to speedup `Feedback::is_interesting` when the user can
|
||||||
/// know the size of the map at compile time.
|
/// know the size of the map at compile time.
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
#[allow(clippy::unsafe_derive_deserialize)]
|
||||||
pub struct ConstMapObserver<'a, T, const N: usize> {
|
pub struct ConstMapObserver<'a, T, const N: usize> {
|
||||||
map: OwnedMutSlice<'a, T>,
|
map: OwnedMutSizedSlice<'a, T, N>,
|
||||||
initial: T,
|
initial: T,
|
||||||
name: Cow<'static, str>,
|
name: Cow<'static, str>,
|
||||||
size: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, S, T, const N: usize> Observer<I, S> for ConstMapObserver<'_, T, N>
|
impl<I, S, T, const N: usize> Observer<I, S> for ConstMapObserver<'_, T, N>
|
||||||
@ -87,19 +85,19 @@ where
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get(&self, idx: usize) -> T {
|
fn get(&self, idx: usize) -> T {
|
||||||
self.as_slice()[idx]
|
self[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set(&mut self, idx: usize, val: T) {
|
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
|
/// Count the set bytes in the map
|
||||||
fn count_bytes(&self) -> u64 {
|
fn count_bytes(&self) -> u64 {
|
||||||
let initial = self.initial();
|
let initial = self.initial();
|
||||||
let cnt = self.usable_count();
|
let cnt = self.usable_count();
|
||||||
let map = self.as_slice();
|
let map = self.map.as_slice();
|
||||||
let mut res = 0;
|
let mut res = 0;
|
||||||
for x in &map[0..cnt] {
|
for x in &map[0..cnt] {
|
||||||
if *x != initial {
|
if *x != initial {
|
||||||
@ -110,7 +108,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usable_count(&self) -> usize {
|
fn usable_count(&self) -> usize {
|
||||||
self.as_slice().len()
|
self.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -124,7 +122,7 @@ where
|
|||||||
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
|
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
|
||||||
let initial = self.initial();
|
let initial = self.initial();
|
||||||
let cnt = self.usable_count();
|
let cnt = self.usable_count();
|
||||||
let map = self.as_slice_mut();
|
let map = &mut (*self);
|
||||||
for x in &mut map[0..cnt] {
|
for x in &mut map[0..cnt] {
|
||||||
*x = initial;
|
*x = initial;
|
||||||
}
|
}
|
||||||
@ -132,14 +130,14 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn to_vec(&self) -> Vec<T> {
|
fn to_vec(&self) -> Vec<T> {
|
||||||
self.as_slice().to_vec()
|
self.map.to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of set entries with the specified indexes
|
/// Get the number of set entries with the specified indexes
|
||||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||||
let initial = self.initial();
|
let initial = self.initial();
|
||||||
let cnt = self.usable_count();
|
let cnt = self.usable_count();
|
||||||
let map = self.as_slice();
|
let map = self.map.as_slice();
|
||||||
let mut res = 0;
|
let mut res = 0;
|
||||||
for i in indexes {
|
for i in indexes {
|
||||||
if *i < cnt && map[*i] != initial {
|
if *i < cnt && map[*i] != initial {
|
||||||
@ -150,37 +148,30 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> VariableLengthMapObserver for ConstMapObserver<'_, T, N>
|
impl<T, const N: usize> ConstLenMapObserver<N> for ConstMapObserver<'_, T, N>
|
||||||
where
|
where
|
||||||
T: PartialEq + Copy + Hash + Serialize + DeserializeOwned + Debug + 'static,
|
T: PartialEq + Copy + Hash + Serialize + DeserializeOwned + Debug + 'static,
|
||||||
{
|
{
|
||||||
fn map_slice(&mut self) -> &[Self::Entry] {
|
fn map_slice(&self) -> &[Self::Entry; N] {
|
||||||
self.map.as_slice()
|
&self.map
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map_slice_mut(&mut self) -> &mut [Self::Entry] {
|
fn map_slice_mut(&mut self) -> &mut [Self::Entry; N] {
|
||||||
self.map.as_slice_mut()
|
&mut self.map
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&mut self) -> &usize {
|
|
||||||
&N
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_mut(&mut self) -> &mut usize {
|
|
||||||
&mut self.size
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> Deref for ConstMapObserver<'_, T, N> {
|
impl<T, const N: usize> Deref for ConstMapObserver<'_, T, N> {
|
||||||
type Target = [T];
|
type Target = [T];
|
||||||
|
|
||||||
fn deref(&self) -> &[T] {
|
fn deref(&self) -> &[T] {
|
||||||
&self.map
|
self.map.as_slice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, const N: usize> DerefMut for ConstMapObserver<'_, T, N> {
|
impl<T, const N: usize> DerefMut for ConstMapObserver<'_, T, N> {
|
||||||
fn deref_mut(&mut self) -> &mut [T] {
|
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.
|
/// Will get a pointer to the map and dereference it at any point in time.
|
||||||
/// The map must not move in memory!
|
/// The map must not move in memory!
|
||||||
#[must_use]
|
#[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);
|
assert!(map.len() >= N);
|
||||||
Self {
|
Self {
|
||||||
map: OwnedMutSlice::from(map),
|
map: OwnedMutSizedSlice::from(map),
|
||||||
name: Cow::from(name),
|
name: Cow::from(name),
|
||||||
initial: T::default(),
|
initial: T::default(),
|
||||||
size: N,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,34 +198,12 @@ where
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Will dereference the `map_ptr` with up to len elements.
|
/// 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<T>) -> Self {
|
||||||
ConstMapObserver {
|
ConstMapObserver {
|
||||||
map: OwnedMutSlice::from_raw_parts_mut(map_ptr, N),
|
map: OwnedMutSizedSlice::from_raw_mut(map_ptr),
|
||||||
name: Cow::from(name),
|
name: Cow::from(name),
|
||||||
initial: T::default(),
|
initial: T::default(),
|
||||||
size: N,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, const N: usize> 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<T>) -> 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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
observers::{map::MapObserver, DifferentialObserver, Observer, VariableLengthMapObserver},
|
observers::{
|
||||||
|
map::MapObserver, ConstLenMapObserver, DifferentialObserver, Observer, VarLenMapObserver,
|
||||||
|
},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -230,11 +232,24 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> VariableLengthMapObserver for HitcountsMapObserver<M>
|
impl<M, const N: usize> ConstLenMapObserver<N> for HitcountsMapObserver<M>
|
||||||
where
|
where
|
||||||
M: VariableLengthMapObserver + MapObserver<Entry = u8>,
|
M: ConstLenMapObserver<N> + MapObserver<Entry = u8>,
|
||||||
{
|
{
|
||||||
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<M> VarLenMapObserver for HitcountsMapObserver<M>
|
||||||
|
where
|
||||||
|
M: VarLenMapObserver + MapObserver<Entry = u8>,
|
||||||
|
{
|
||||||
|
fn map_slice(&self) -> &[Self::Entry] {
|
||||||
self.base.map_slice()
|
self.base.map_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +257,7 @@ where
|
|||||||
self.base.map_slice_mut()
|
self.base.map_slice_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&mut self) -> &usize {
|
fn size(&self) -> &usize {
|
||||||
self.base.size()
|
self.base.size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,27 +387,30 @@ pub trait MapObserver:
|
|||||||
|
|
||||||
/// The "real" length of the underlying map could change at any point in time.
|
/// 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.
|
/// 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.
|
/// A mutable slice reference to the map.
|
||||||
/// The length of the map gives the maximum allocatable size.
|
/// 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.
|
/// A slice reference to the map.
|
||||||
/// The length of the map gives the maximum allocatable size.
|
/// The length of the map gives the maximum allocatable size.
|
||||||
fn map_slice_mut(&mut self) -> &mut [Self::Entry];
|
fn map_slice_mut(&mut self) -> &mut [Self::Entry];
|
||||||
|
|
||||||
/// A reference to the size of the map.
|
/// 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.
|
/// A mutable reference to the size of the map.
|
||||||
fn size_mut(&mut self) -> &mut usize;
|
fn size_mut(&mut self) -> &mut usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementors guarantee the size of the map is constant at any point in time and equals N.
|
/// Implementors guarantee the size of the map is constant at any point in time and equals N.
|
||||||
pub trait ConstantLengthMapObserver<const N: usize>: MapObserver {
|
pub trait ConstLenMapObserver<const N: usize>: MapObserver {
|
||||||
/// The size of the map
|
/// The size of the map
|
||||||
const LENGTH: usize = N;
|
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
|
/// A mutable slice reference to the map
|
||||||
fn map_slice_mut(&mut self) -> &mut [Self::Entry; N];
|
fn map_slice_mut(&mut self) -> &mut [Self::Entry; N];
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ use libafl_bolts::{
|
|||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
observers::{map::MapObserver, Observer, VariableLengthMapObserver},
|
observers::{map::MapObserver, Observer, VarLenMapObserver},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -149,11 +149,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> VariableLengthMapObserver for VariableMapObserver<'_, T>
|
impl<T> VarLenMapObserver for VariableMapObserver<'_, T>
|
||||||
where
|
where
|
||||||
T: PartialEq + Copy + Hash + Serialize + DeserializeOwned + Debug,
|
T: PartialEq + Copy + Hash + Serialize + DeserializeOwned + Debug,
|
||||||
{
|
{
|
||||||
fn map_slice(&mut self) -> &[Self::Entry] {
|
fn map_slice(&self) -> &[Self::Entry] {
|
||||||
self.map.as_ref()
|
self.map.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ where
|
|||||||
self.map.as_mut()
|
self.map.as_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&mut self) -> &usize {
|
fn size(&self) -> &usize {
|
||||||
self.size.as_ref()
|
self.size.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,7 +680,18 @@ pub trait AsSlice<'a> {
|
|||||||
fn as_slice(&'a self) -> Self::SliceRef;
|
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<Target = [Self::Entry; N]>;
|
||||||
|
|
||||||
|
/// Convert to a slice
|
||||||
|
fn as_sized_slice(&'a self) -> Self::SliceRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T, R: ?Sized> AsSlice<'a> for R
|
||||||
where
|
where
|
||||||
T: 'a,
|
T: 'a,
|
||||||
R: Deref<Target = [T]>,
|
R: Deref<Target = [T]>,
|
||||||
@ -693,6 +704,19 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, T, const N: usize, R: ?Sized> AsSizedSlice<'a, N> for R
|
||||||
|
where
|
||||||
|
T: 'a,
|
||||||
|
R: Deref<Target = [T; N]>,
|
||||||
|
{
|
||||||
|
type Entry = T;
|
||||||
|
type SliceRef = &'a [T; N];
|
||||||
|
|
||||||
|
fn as_sized_slice(&'a self) -> Self::SliceRef {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Can be converted to a mutable slice
|
/// Can be converted to a mutable slice
|
||||||
pub trait AsSliceMut<'a>: AsSlice<'a> {
|
pub trait AsSliceMut<'a>: AsSlice<'a> {
|
||||||
/// Type of the mutable reference to this slice
|
/// Type of the mutable reference to this slice
|
||||||
@ -702,7 +726,16 @@ pub trait AsSliceMut<'a>: AsSlice<'a> {
|
|||||||
fn as_slice_mut(&'a mut self) -> Self::SliceRefMut;
|
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<Target = [Self::Entry; N]>;
|
||||||
|
|
||||||
|
/// Convert to a slice
|
||||||
|
fn as_sized_slice_mut(&'a mut self) -> Self::SliceRefMut;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T, R: ?Sized> AsSliceMut<'a> for R
|
||||||
where
|
where
|
||||||
T: 'a,
|
T: 'a,
|
||||||
R: DerefMut<Target = [T]>,
|
R: DerefMut<Target = [T]>,
|
||||||
@ -714,6 +747,18 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, T, const N: usize, R: ?Sized> AsSizedSliceMut<'a, N> for R
|
||||||
|
where
|
||||||
|
T: 'a,
|
||||||
|
R: DerefMut<Target = [T; N]>,
|
||||||
|
{
|
||||||
|
type SliceRefMut = &'a mut [T; N];
|
||||||
|
|
||||||
|
fn as_sized_slice_mut(&'a mut self) -> Self::SliceRefMut {
|
||||||
|
&mut *self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create an `Iterator` from a reference
|
/// Create an `Iterator` from a reference
|
||||||
pub trait AsIter<'it> {
|
pub trait AsIter<'it> {
|
||||||
/// The item type
|
/// The item type
|
||||||
|
@ -10,13 +10,67 @@ use core::{
|
|||||||
clone::Clone,
|
clone::Clone,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
ops::{Deref, DerefMut, RangeBounds},
|
ops::{Deref, DerefMut, RangeBounds},
|
||||||
|
ptr::NonNull,
|
||||||
slice,
|
slice,
|
||||||
slice::SliceIndex,
|
slice::SliceIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
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 <https://github.com/serde-rs/serde/issues/1937#issuecomment-812137971>
|
||||||
|
mod arrays {
|
||||||
|
use alloc::{boxed::Box, fmt, vec::Vec};
|
||||||
|
use core::{convert::TryInto, marker::PhantomData};
|
||||||
|
|
||||||
|
use serde::{
|
||||||
|
de::{SeqAccess, Visitor},
|
||||||
|
Deserialize, Deserializer,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ArrayVisitor<T, const N: usize>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<'de, T, const N: usize> Visitor<'de> for ArrayVisitor<T, N>
|
||||||
|
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
|
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<Box<[T; N]>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
T: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_tuple(N, ArrayVisitor::<T, N>(PhantomData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Private part of the unsafe marker, making sure this cannot be initialized directly.
|
/// Private part of the unsafe marker, making sure this cannot be initialized directly.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[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<S>(&self, se: S) -> Result<S::Ok, S::Error>
|
||||||
|
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<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
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 = <IterMut<'it, T> 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 = <Iter<'it, T> 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<T>() * N`
|
||||||
|
/// The content will be dereferenced in subsequent operations.
|
||||||
|
#[must_use]
|
||||||
|
pub unsafe fn from_raw_mut(ptr: NonNull<T>) -> 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<T: Sized, const N: usize> 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<T: Sized, const N: usize> 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<T, const N: usize> 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<T, const N: usize> From<Box<[T; N]>> 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<OwnedMutSizedSlice<'a, T, N>> 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
|
/// Wrap a C-style pointer and convert to a Box on serialize
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum OwnedPtr<T: Sized> {
|
pub enum OwnedPtr<T: Sized> {
|
||||||
|
@ -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<AF, PF> = EdgeCoverageModule<AF, PF, EdgeCoverageChildVariant>;
|
|
||||||
|
|
||||||
pub trait EdgeCoverageVariant<AF, PF>: 'static + Debug {
|
|
||||||
const DO_SIDE_EFFECTS: bool = true;
|
|
||||||
|
|
||||||
fn jit_hitcount<ET, S>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
panic!("JIT hitcount is not supported.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jit_no_hitcount<ET, S>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
panic!("JIT no hitcount is not supported.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fn_hitcount<ET, S>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
panic!("Func hitcount is not supported.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fn_no_hitcount<ET, S>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
panic!("Func no hitcount is not supported.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EdgeCoverageFullVariant;
|
|
||||||
|
|
||||||
pub type StdEdgeCoverageFullModule =
|
|
||||||
EdgeCoverageModule<StdAddressFilter, StdPageFilter, EdgeCoverageFullVariant>;
|
|
||||||
pub type StdEdgeCoverageFullModuleBuilder =
|
|
||||||
EdgeCoverageModuleBuilder<StdAddressFilter, StdPageFilter, EdgeCoverageFullVariant, false>;
|
|
||||||
|
|
||||||
impl<AF, PF> EdgeCoverageVariant<AF, PF> for EdgeCoverageFullVariant {
|
|
||||||
fn jit_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
let hook_id = emulator_modules.edges(
|
|
||||||
Hook::Function(gen_unique_edge_ids::<AF, ET, PF, S, Self>),
|
|
||||||
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<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
let hook_id = emulator_modules.edges(
|
|
||||||
Hook::Function(gen_unique_edge_ids::<AF, ET, PF, S, Self>),
|
|
||||||
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<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
emulator_modules.edges(
|
|
||||||
Hook::Function(gen_unique_edge_ids::<AF, ET, PF, S, Self>),
|
|
||||||
Hook::Raw(trace_edge_hitcount),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fn_no_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
emulator_modules.edges(
|
|
||||||
Hook::Function(gen_unique_edge_ids::<AF, ET, PF, S, Self>),
|
|
||||||
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<StdAddressFilter, StdPageFilter, EdgeCoverageClassicVariant>;
|
|
||||||
pub type StdEdgeCoverageClassicModuleBuilder =
|
|
||||||
EdgeCoverageModuleBuilder<StdAddressFilter, StdPageFilter, EdgeCoverageClassicVariant, false>;
|
|
||||||
|
|
||||||
impl<AF, PF> EdgeCoverageVariant<AF, PF> for EdgeCoverageClassicVariant {
|
|
||||||
const DO_SIDE_EFFECTS: bool = false;
|
|
||||||
|
|
||||||
fn jit_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
let hook_id = emulator_modules.blocks(
|
|
||||||
Hook::Function(gen_hashed_block_ids::<AF, ET, PF, S, Self>),
|
|
||||||
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<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
let hook_id = emulator_modules.blocks(
|
|
||||||
Hook::Function(gen_hashed_block_ids::<AF, ET, PF, S, Self>),
|
|
||||||
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<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
emulator_modules.blocks(
|
|
||||||
Hook::Function(gen_hashed_block_ids::<AF, ET, PF, S, Self>),
|
|
||||||
Hook::Empty,
|
|
||||||
Hook::Raw(trace_block_transition_hitcount),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fn_no_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
emulator_modules.blocks(
|
|
||||||
Hook::Function(gen_hashed_block_ids::<AF, ET, PF, S, Self>),
|
|
||||||
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<StdAddressFilter, StdPageFilter, EdgeCoverageChildVariant>;
|
|
||||||
pub type StdEdgeCoverageChildModuleBuilder =
|
|
||||||
EdgeCoverageModuleBuilder<StdAddressFilter, StdPageFilter, EdgeCoverageChildVariant, false>;
|
|
||||||
|
|
||||||
impl<AF, PF> EdgeCoverageVariant<AF, PF> for EdgeCoverageChildVariant {
|
|
||||||
const DO_SIDE_EFFECTS: bool = false;
|
|
||||||
|
|
||||||
fn fn_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
emulator_modules.edges(
|
|
||||||
Hook::Function(gen_hashed_edge_ids::<AF, ET, PF, S, Self>),
|
|
||||||
Hook::Raw(trace_edge_hitcount_ptr),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fn_no_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
emulator_modules.edges(
|
|
||||||
Hook::Function(gen_hashed_edge_ids::<AF, ET, PF, S, Self>),
|
|
||||||
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<AF, PF, V, const IS_INITIALIZED: bool> {
|
|
||||||
variant: V,
|
|
||||||
address_filter: AF,
|
|
||||||
page_filter: PF,
|
|
||||||
use_hitcounts: bool,
|
|
||||||
use_jit: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct EdgeCoverageModule<AF, PF, V> {
|
|
||||||
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<AF, PF, V> EdgeCoverageModuleBuilder<AF, PF, V, true> {
|
|
||||||
pub fn build(self) -> Result<EdgeCoverageModule<AF, PF, V>, Error> {
|
|
||||||
Ok(EdgeCoverageModule::new(
|
|
||||||
self.address_filter,
|
|
||||||
self.page_filter,
|
|
||||||
self.variant,
|
|
||||||
self.use_hitcounts,
|
|
||||||
self.use_jit,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AF, PF, V, const IS_INITIALIZED: bool> EdgeCoverageModuleBuilder<AF, PF, V, IS_INITIALIZED> {
|
|
||||||
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<O>(self, map_observer: &mut O) -> EdgeCoverageModuleBuilder<AF, PF, V, true>
|
|
||||||
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::<AF, PF, V, true>::new(
|
|
||||||
self.variant,
|
|
||||||
self.address_filter,
|
|
||||||
self.page_filter,
|
|
||||||
self.use_hitcounts,
|
|
||||||
self.use_jit,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn variant<V2>(self, variant: V2) -> EdgeCoverageModuleBuilder<AF, PF, V2, IS_INITIALIZED> {
|
|
||||||
EdgeCoverageModuleBuilder::new(
|
|
||||||
variant,
|
|
||||||
self.address_filter,
|
|
||||||
self.page_filter,
|
|
||||||
self.use_hitcounts,
|
|
||||||
self.use_jit,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn address_filter<AF2>(
|
|
||||||
self,
|
|
||||||
address_filter: AF2,
|
|
||||||
) -> EdgeCoverageModuleBuilder<AF2, PF, V, IS_INITIALIZED> {
|
|
||||||
EdgeCoverageModuleBuilder::new(
|
|
||||||
self.variant,
|
|
||||||
address_filter,
|
|
||||||
self.page_filter,
|
|
||||||
self.use_hitcounts,
|
|
||||||
self.use_jit,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn page_filter<PF2>(
|
|
||||||
self,
|
|
||||||
page_filter: PF2,
|
|
||||||
) -> EdgeCoverageModuleBuilder<AF, PF2, V, IS_INITIALIZED> {
|
|
||||||
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<AF, PF, V, IS_INITIALIZED> {
|
|
||||||
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<AF, PF, V, IS_INITIALIZED> {
|
|
||||||
EdgeCoverageModuleBuilder::new(
|
|
||||||
self.variant,
|
|
||||||
self.address_filter,
|
|
||||||
self.page_filter,
|
|
||||||
self.use_hitcounts,
|
|
||||||
use_jit,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AF, PF, V> EdgeCoverageModule<AF, PF, V> {
|
|
||||||
#[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<AF, PF, V> EdgeCoverageModule<AF, PF, V>
|
|
||||||
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<GuestPhysAddr>) -> 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<S, AF, PF, V> EmulatorModule<S> for EdgeCoverageModule<AF, PF, V>
|
|
||||||
where
|
|
||||||
AF: AddressFilter + 'static,
|
|
||||||
PF: PageFilter + 'static,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
V: EdgeCoverageVariant<AF, PF> + 'static,
|
|
||||||
{
|
|
||||||
const HOOKS_DO_SIDE_EFFECTS: bool = V::DO_SIDE_EFFECTS;
|
|
||||||
|
|
||||||
type ModuleAddressFilter = AF;
|
|
||||||
#[cfg(feature = "systemmode")]
|
|
||||||
type ModulePageFilter = PF;
|
|
||||||
|
|
||||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
|
||||||
where
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
{
|
|
||||||
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<u64> = const { UnsafeCell::new(0) });
|
|
||||||
pub fn gen_unique_edge_ids<AF, ET, PF, S, V>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
state: Option<&mut S>,
|
|
||||||
src: GuestAddr,
|
|
||||||
dest: GuestAddr,
|
|
||||||
) -> Option<u64>
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
V: EdgeCoverageVariant<AF, PF>,
|
|
||||||
{
|
|
||||||
if let Some(module) = emulator_modules.get::<EdgeCoverageModule<AF, PF, V>>() {
|
|
||||||
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<AF, ET, PF, S, V>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
src: GuestAddr,
|
|
||||||
dest: GuestAddr,
|
|
||||||
) -> Option<u64>
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
V: EdgeCoverageVariant<AF, PF>,
|
|
||||||
{
|
|
||||||
if let Some(module) = emulator_modules.get::<EdgeCoverageModule<AF, PF, V>>() {
|
|
||||||
#[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<AF, ET, PF, S, V>(
|
|
||||||
emulator_modules: &mut EmulatorModules<ET, S>,
|
|
||||||
_state: Option<&mut S>,
|
|
||||||
pc: GuestAddr,
|
|
||||||
) -> Option<u64>
|
|
||||||
where
|
|
||||||
AF: AddressFilter,
|
|
||||||
ET: EmulatorModuleTuple<S>,
|
|
||||||
PF: PageFilter,
|
|
||||||
S: Unpin + UsesInput + HasMetadata,
|
|
||||||
V: EdgeCoverageVariant<AF, PF>,
|
|
||||||
{
|
|
||||||
// first check if we should filter
|
|
||||||
if let Some(module) = emulator_modules.get::<EdgeCoverageModule<AF, PF, V>>() {
|
|
||||||
#[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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
77
libafl_qemu/src/modules/edges/child.rs
Normal file
77
libafl_qemu/src/modules/edges/child.rs
Normal file
@ -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<StdAddressFilter, StdPageFilter, EdgeCoverageChildVariant, false, 0>;
|
||||||
|
pub type StdEdgeCoverageChildModuleBuilder = EdgeCoverageModuleBuilder<
|
||||||
|
StdAddressFilter,
|
||||||
|
StdPageFilter,
|
||||||
|
EdgeCoverageChildVariant,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<AF, PF, const IS_CONST_MAP: bool, const MAP_SIZE: usize>
|
||||||
|
EdgeCoverageVariant<AF, PF, IS_CONST_MAP, MAP_SIZE> for EdgeCoverageChildVariant
|
||||||
|
{
|
||||||
|
const DO_SIDE_EFFECTS: bool = false;
|
||||||
|
|
||||||
|
fn fn_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
emulator_modules.edges(
|
||||||
|
Hook::Function(gen_hashed_edge_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
Hook::Raw(trace_edge_hitcount_ptr),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_no_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
emulator_modules.edges(
|
||||||
|
Hook::Function(gen_hashed_edge_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
124
libafl_qemu/src/modules/edges/classic.rs
Normal file
124
libafl_qemu/src/modules/edges/classic.rs
Normal file
@ -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<StdAddressFilter, StdPageFilter, EdgeCoverageClassicVariant, false, 0>;
|
||||||
|
pub type StdEdgeCoverageClassicModuleBuilder = EdgeCoverageModuleBuilder<
|
||||||
|
StdAddressFilter,
|
||||||
|
StdPageFilter,
|
||||||
|
EdgeCoverageClassicVariant,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<AF, PF, const IS_CONST_MAP: bool, const MAP_SIZE: usize>
|
||||||
|
EdgeCoverageVariant<AF, PF, IS_CONST_MAP, MAP_SIZE> for EdgeCoverageClassicVariant
|
||||||
|
{
|
||||||
|
const DO_SIDE_EFFECTS: bool = false;
|
||||||
|
|
||||||
|
fn jit_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
let hook_id = emulator_modules.blocks(
|
||||||
|
Hook::Function(gen_hashed_block_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
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<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
let hook_id = emulator_modules.blocks(
|
||||||
|
Hook::Function(gen_hashed_block_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
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<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
emulator_modules.blocks(
|
||||||
|
Hook::Function(gen_hashed_block_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
Hook::Empty,
|
||||||
|
Hook::Raw(trace_block_transition_hitcount),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_no_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
emulator_modules.blocks(
|
||||||
|
Hook::Function(gen_hashed_block_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
114
libafl_qemu/src/modules/edges/full.rs
Normal file
114
libafl_qemu/src/modules/edges/full.rs
Normal file
@ -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<StdAddressFilter, StdPageFilter, EdgeCoverageFullVariant, false, 0>;
|
||||||
|
pub type StdEdgeCoverageFullModuleBuilder = EdgeCoverageModuleBuilder<
|
||||||
|
StdAddressFilter,
|
||||||
|
StdPageFilter,
|
||||||
|
EdgeCoverageFullVariant,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
>;
|
||||||
|
|
||||||
|
impl<AF, PF, const IS_CONST_MAP: bool, const MAP_SIZE: usize>
|
||||||
|
EdgeCoverageVariant<AF, PF, IS_CONST_MAP, MAP_SIZE> for EdgeCoverageFullVariant
|
||||||
|
{
|
||||||
|
fn jit_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
let hook_id = emulator_modules.edges(
|
||||||
|
Hook::Function(gen_unique_edge_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
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<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
let hook_id = emulator_modules.edges(
|
||||||
|
Hook::Function(gen_unique_edge_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
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<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
emulator_modules.edges(
|
||||||
|
Hook::Function(gen_unique_edge_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
Hook::Raw(trace_edge_hitcount),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_no_hitcount<ET, S>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
emulator_modules.edges(
|
||||||
|
Hook::Function(gen_unique_edge_ids::<AF, ET, PF, S, Self, IS_CONST_MAP, MAP_SIZE>),
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
345
libafl_qemu/src/modules/edges/helpers.rs
Normal file
345
libafl_qemu/src/modules/edges/helpers.rs
Normal file
@ -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<const IS_CONST_MAP: bool, const MAP_SIZE: usize>() -> 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<AF, ET, PF, S, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>(
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
state: Option<&mut S>,
|
||||||
|
src: GuestAddr,
|
||||||
|
dest: GuestAddr,
|
||||||
|
) -> Option<u64>
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
V: EdgeCoverageVariant<AF, PF, IS_CONST_MAP, MAP_SIZE>,
|
||||||
|
{
|
||||||
|
if let Some(module) =
|
||||||
|
emulator_modules.get::<EdgeCoverageModule<AF, PF, V, IS_CONST_MAP, MAP_SIZE>>()
|
||||||
|
{
|
||||||
|
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::<IS_CONST_MAP, MAP_SIZE>();
|
||||||
|
|
||||||
|
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<AF, ET, PF, S, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>(
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
src: GuestAddr,
|
||||||
|
dest: GuestAddr,
|
||||||
|
) -> Option<u64>
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
V: EdgeCoverageVariant<AF, PF, IS_CONST_MAP, MAP_SIZE>,
|
||||||
|
{
|
||||||
|
if let Some(module) =
|
||||||
|
emulator_modules.get::<EdgeCoverageModule<AF, PF, V, IS_CONST_MAP, MAP_SIZE>>()
|
||||||
|
{
|
||||||
|
#[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::<IS_CONST_MAP, MAP_SIZE>();
|
||||||
|
|
||||||
|
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<AF, ET, PF, S, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>(
|
||||||
|
emulator_modules: &mut EmulatorModules<ET, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
pc: GuestAddr,
|
||||||
|
) -> Option<u64>
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
V: EdgeCoverageVariant<AF, PF, IS_CONST_MAP, MAP_SIZE>,
|
||||||
|
{
|
||||||
|
// first check if we should filter
|
||||||
|
if let Some(module) =
|
||||||
|
emulator_modules.get::<EdgeCoverageModule<AF, PF, V, IS_CONST_MAP, MAP_SIZE>>()
|
||||||
|
{
|
||||||
|
#[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::<IS_CONST_MAP, MAP_SIZE>();
|
||||||
|
|
||||||
|
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<u64> = 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
403
libafl_qemu/src/modules/edges/mod.rs
Normal file
403
libafl_qemu/src/modules/edges/mod.rs
Normal file
@ -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<AF, PF, const IS_CONST_MAP: bool, const MAP_SIZE: usize> =
|
||||||
|
EdgeCoverageModule<AF, PF, EdgeCoverageChildVariant, IS_CONST_MAP, MAP_SIZE>;
|
||||||
|
|
||||||
|
/// An edge coverage module variant.
|
||||||
|
trait EdgeCoverageVariant<AF, PF, const IS_CONST_MAP: bool, const MAP_SIZE: usize>:
|
||||||
|
'static + Debug
|
||||||
|
{
|
||||||
|
const DO_SIDE_EFFECTS: bool = true;
|
||||||
|
|
||||||
|
fn jit_hitcount<ET, S>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
panic!("JIT hitcount is not supported.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jit_no_hitcount<ET, S>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
panic!("JIT no hitcount is not supported.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_hitcount<ET, S>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
PF: PageFilter,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
panic!("Func hitcount is not supported.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_no_hitcount<ET, S>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>)
|
||||||
|
where
|
||||||
|
AF: AddressFilter,
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
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<AF, PF, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize> {
|
||||||
|
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<AF, PF, V, const IS_INITIALIZED: bool, const IS_CONST_MAP: bool, const MAP_SIZE: usize>
|
||||||
|
EdgeCoverageModuleBuilder<AF, PF, V, IS_INITIALIZED, IS_CONST_MAP, MAP_SIZE>
|
||||||
|
{
|
||||||
|
pub fn build(self) -> Result<EdgeCoverageModule<AF, PF, V, IS_CONST_MAP, MAP_SIZE>, 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<AF, PF, V, const IS_INITIALIZED: bool, const IS_CONST_MAP: bool, const MAP_SIZE: usize>
|
||||||
|
EdgeCoverageModuleBuilder<AF, PF, V, IS_INITIALIZED, IS_CONST_MAP, MAP_SIZE>
|
||||||
|
{
|
||||||
|
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<O>(
|
||||||
|
self,
|
||||||
|
map_observer: &mut O,
|
||||||
|
) -> EdgeCoverageModuleBuilder<AF, PF, V, true, false, 0>
|
||||||
|
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::<AF, PF, V, true, false, 0>::new(
|
||||||
|
self.variant,
|
||||||
|
self.address_filter,
|
||||||
|
self.page_filter,
|
||||||
|
self.use_hitcounts,
|
||||||
|
self.use_jit,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn const_map_observer<O, const NEW_MAP_SIZE: usize>(
|
||||||
|
self,
|
||||||
|
_const_map_observer: &mut O,
|
||||||
|
) -> EdgeCoverageModuleBuilder<AF, PF, V, true, true, NEW_MAP_SIZE>
|
||||||
|
where
|
||||||
|
O: ConstLenMapObserver<NEW_MAP_SIZE>,
|
||||||
|
{
|
||||||
|
EdgeCoverageModuleBuilder::<AF, PF, V, true, true, NEW_MAP_SIZE>::new(
|
||||||
|
self.variant,
|
||||||
|
self.address_filter,
|
||||||
|
self.page_filter,
|
||||||
|
self.use_hitcounts,
|
||||||
|
self.use_jit,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variant<V2>(
|
||||||
|
self,
|
||||||
|
variant: V2,
|
||||||
|
) -> EdgeCoverageModuleBuilder<AF, PF, V2, IS_INITIALIZED, IS_CONST_MAP, MAP_SIZE> {
|
||||||
|
EdgeCoverageModuleBuilder::new(
|
||||||
|
variant,
|
||||||
|
self.address_filter,
|
||||||
|
self.page_filter,
|
||||||
|
self.use_hitcounts,
|
||||||
|
self.use_jit,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn address_filter<AF2>(
|
||||||
|
self,
|
||||||
|
address_filter: AF2,
|
||||||
|
) -> EdgeCoverageModuleBuilder<AF2, PF, V, IS_INITIALIZED, IS_CONST_MAP, MAP_SIZE> {
|
||||||
|
EdgeCoverageModuleBuilder::new(
|
||||||
|
self.variant,
|
||||||
|
address_filter,
|
||||||
|
self.page_filter,
|
||||||
|
self.use_hitcounts,
|
||||||
|
self.use_jit,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn page_filter<PF2>(
|
||||||
|
self,
|
||||||
|
page_filter: PF2,
|
||||||
|
) -> EdgeCoverageModuleBuilder<AF, PF2, V, IS_INITIALIZED, IS_CONST_MAP, MAP_SIZE> {
|
||||||
|
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<AF, PF, V, IS_INITIALIZED, IS_CONST_MAP, MAP_SIZE> {
|
||||||
|
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<AF, PF, V, IS_INITIALIZED, IS_CONST_MAP, MAP_SIZE> {
|
||||||
|
EdgeCoverageModuleBuilder::new(
|
||||||
|
self.variant,
|
||||||
|
self.address_filter,
|
||||||
|
self.page_filter,
|
||||||
|
self.use_hitcounts,
|
||||||
|
use_jit,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<AF, PF, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>
|
||||||
|
EdgeCoverageModule<AF, PF, V, IS_CONST_MAP, MAP_SIZE>
|
||||||
|
{
|
||||||
|
#[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<AF, PF, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize>
|
||||||
|
EdgeCoverageModule<AF, PF, V, IS_CONST_MAP, MAP_SIZE>
|
||||||
|
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<GuestPhysAddr>) -> 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<S, AF, PF, V, const IS_CONST_MAP: bool, const MAP_SIZE: usize> EmulatorModule<S>
|
||||||
|
for EdgeCoverageModule<AF, PF, V, IS_CONST_MAP, MAP_SIZE>
|
||||||
|
where
|
||||||
|
AF: AddressFilter + 'static,
|
||||||
|
PF: PageFilter + 'static,
|
||||||
|
S: Unpin + UsesInput + HasMetadata,
|
||||||
|
V: EdgeCoverageVariant<AF, PF, IS_CONST_MAP, MAP_SIZE> + 'static,
|
||||||
|
{
|
||||||
|
type ModuleAddressFilter = AF;
|
||||||
|
|
||||||
|
#[cfg(feature = "systemmode")]
|
||||||
|
type ModulePageFilter = PF;
|
||||||
|
const HOOKS_DO_SIDE_EFFECTS: bool = V::DO_SIDE_EFFECTS;
|
||||||
|
|
||||||
|
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
||||||
|
where
|
||||||
|
ET: EmulatorModuleTuple<S>,
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,12 @@ pub mod systemmode;
|
|||||||
pub use systemmode::*;
|
pub use systemmode::*;
|
||||||
|
|
||||||
pub mod edges;
|
pub mod edges;
|
||||||
pub use edges::*;
|
pub use edges::{
|
||||||
|
EdgeCoverageModule, EdgeCoverageModuleBuilder, StdEdgeCoverageChildModule,
|
||||||
|
StdEdgeCoverageChildModuleBuilder, StdEdgeCoverageClassicModule,
|
||||||
|
StdEdgeCoverageClassicModuleBuilder, StdEdgeCoverageFullModule,
|
||||||
|
StdEdgeCoverageFullModuleBuilder, StdEdgeCoverageModule, StdEdgeCoverageModuleBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub mod calls;
|
pub mod calls;
|
||||||
@ -33,7 +38,7 @@ pub use cmplog::CmpLogModule;
|
|||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub mod drcov;
|
pub mod drcov;
|
||||||
#[cfg(not(cpu_target = "hexagon"))]
|
#[cfg(not(cpu_target = "hexagon"))]
|
||||||
pub use drcov::*;
|
pub use drcov::{DrCovMetadata, DrCovModule, DrCovModuleBuilder};
|
||||||
|
|
||||||
use crate::{emu::EmulatorModules, Qemu};
|
use crate::{emu::EmulatorModules, Qemu};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user