Skip to content

Commit

Permalink
Compile-time edge module compilation check, native support for ConstM…
Browse files Browse the repository at this point in the history
…apObserver (#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
  • Loading branch information
rmalmain authored Nov 4, 2024
1 parent 56a5463 commit 49ea0b0
Show file tree
Hide file tree
Showing 20 changed files with 1,467 additions and 894 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/qemu-fuzzer-tester-prepare/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ runs:
steps:
- name: Install QEMU deps
shell: bash
run: apt-get update && apt-get install -y qemu-utils sudo python3-msgpack python3-jinja2 curl
run: apt-get update && apt-get install -y qemu-utils sudo python3-msgpack python3-jinja2 curl python3-dev
- uses: dtolnay/rust-toolchain@stable
- name: enable mult-thread for `make`
shell: bash
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{path::PathBuf, time::Duration};
use std::{path::PathBuf, ptr::NonNull, time::Duration};

use libafl::{
corpus::{InMemoryCorpus, OnDiskCorpus},
Expand Down Expand Up @@ -46,7 +46,12 @@ pub fn main() {
libafl::executors::ExitKind::Ok
};
// 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
let mut bt = shmem_provider.new_on_shmem::<Option<u64>>(None).unwrap();
let bt_observer = BacktraceObserver::new(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::{path::PathBuf, ptr::NonNull};

use libafl::{
corpus::{InMemoryCorpus, OnDiskCorpus},
Expand Down Expand Up @@ -35,7 +35,12 @@ pub fn main() {
libafl::executors::ExitKind::Ok
};
// 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
let bt_observer = BacktraceObserver::owned(
"BacktraceObserver",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ pub fn main() {
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
//let the forkserver know the shmid
shmem.write_to_env("__AFL_SHM_ID").unwrap();
let shmem_map = shmem.as_slice_mut();
let shmem_map: &mut [u8; MAP_SIZE] = shmem
.as_slice_mut()
.try_into()
.expect("could not convert slice to sized slice.");

// Create an observation channel using the signals map
let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new(
Expand Down
16 changes: 9 additions & 7 deletions fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::{
io::{self, Write},
path::PathBuf,
process,
ptr::NonNull,
time::Duration,
};

Expand Down Expand Up @@ -160,14 +161,14 @@ fn fuzz(
let mut edges_observer = unsafe {
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_DEFAULT_SIZE>::from_mut_ptr(
"edges",
edges.as_mut_ptr(),
NonNull::new(edges.as_mut_ptr()).expect("map ptr is null."),
))
.track_indices()
};

let emulator_modules = tuple_list!(
StdEdgeCoverageChildModule::builder()
.map_observer(edges_observer.as_mut())
.const_map_observer(edges_observer.as_mut())
.build()?,
CmpLogChildModule::default(),
);
Expand Down Expand Up @@ -199,7 +200,8 @@ fn fuzz(

let stack_ptr: u64 = qemu.read_reg(Regs::Sp).unwrap();
let mut ret_addr = [0; 8];
unsafe { qemu.read_mem(stack_ptr, &mut ret_addr) };
qemu.read_mem(stack_ptr, &mut ret_addr)
.expect("qemu read failed");
let ret_addr = u64::from_le_bytes(ret_addr);

println!("Stack pointer = {stack_ptr:#x}");
Expand Down Expand Up @@ -323,7 +325,7 @@ fn fuzz(
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);

// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>, input: &BytesInput| {
let target = input.target_bytes();
let mut buf = target.as_slice();
let mut len = buf.len();
Expand All @@ -333,7 +335,7 @@ fn fuzz(
}

unsafe {
qemu.write_mem(input_addr, buf);
qemu.write_mem_unchecked(input_addr, buf);

qemu.write_reg(Regs::Rdi, input_addr).unwrap();
qemu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
Expand Down Expand Up @@ -394,8 +396,8 @@ fn fuzz(
#[cfg(unix)]
{
let null_fd = file_null.as_raw_fd();
// dup2(null_fd, io::stdout().as_raw_fd())?;
// dup2(null_fd, io::stderr().as_raw_fd())?;
dup2(null_fd, io::stdout().as_raw_fd())?;
dup2(null_fd, io::stderr().as_raw_fd())?;
}
// reopen file to make sure we're at the end
log.replace(
Expand Down
8 changes: 4 additions & 4 deletions fuzzers/binary_only/qemu_cmin/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
#[cfg(feature = "i386")]
use core::mem::size_of;
use std::{env, io, path::PathBuf, process};
use std::{env, io, path::PathBuf, process, ptr::NonNull};

use clap::{builder::Str, Parser};
use libafl::{
Expand Down Expand Up @@ -162,7 +162,7 @@ pub fn fuzz() -> Result<(), Error> {
let mut edges_observer = unsafe {
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_DEFAULT_SIZE>::from_mut_ptr(
"edges",
edges.as_mut_ptr(),
NonNull::new(edges.as_mut_ptr()).expect("The edge map pointer is null."),
))
};

Expand Down Expand Up @@ -196,7 +196,7 @@ pub fn fuzz() -> Result<(), Error> {
let len = len as GuestReg;

unsafe {
qemu.write_mem(input_addr, buf);
qemu.write_mem(input_addr, buf).expect("qemu write failed.");
qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
qemu.write_return_address(ret_addr).unwrap();
Expand All @@ -219,7 +219,7 @@ pub fn fuzz() -> Result<(), Error> {
};

let modules = tuple_list!(StdEdgeCoverageChildModule::builder()
.map_observer(edges_observer.as_mut())
.const_map_observer(edges_observer.as_mut())
.build()?);

let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;
Expand Down
2 changes: 1 addition & 1 deletion libafl/src/executors/forkserver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1635,7 +1635,7 @@ mod tests {

let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
shmem.write_to_env("__AFL_SHM_ID").unwrap();
let shmem_buf = shmem.as_slice_mut();
let shmem_buf: &mut [u8; MAP_SIZE] = shmem.as_slice_mut().try_into().unwrap();

let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new(
"shared_mem",
Expand Down
80 changes: 24 additions & 56 deletions libafl/src/observers/map/const_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,26 @@ use core::{
fmt::Debug,
hash::{Hash, Hasher},
ops::{Deref, DerefMut},
ptr::NonNull,
};

use ahash::RandomState;
use libafl_bolts::{ownedref::OwnedMutSlice, AsSlice, AsSliceMut, HasLen, Named};
use libafl_bolts::{ownedref::OwnedMutSizedSlice, HasLen, Named};
use serde::{de::DeserializeOwned, Deserialize, Serialize};

use crate::{
observers::{map::MapObserver, Observer, VariableLengthMapObserver},
observers::{map::MapObserver, ConstLenMapObserver, Observer},
Error,
};

// TODO: remove the size field and implement ConstantLengthMapObserver

/// Use a const size to speedup `Feedback::is_interesting` when the user can
/// know the size of the map at compile time.
#[derive(Serialize, Deserialize, Debug)]
#[allow(clippy::unsafe_derive_deserialize)]
pub struct ConstMapObserver<'a, T, const N: usize> {
map: OwnedMutSlice<'a, T>,
map: OwnedMutSizedSlice<'a, T, N>,
initial: T,
name: Cow<'static, str>,
size: usize,
}

impl<I, S, T, const N: usize> Observer<I, S> for ConstMapObserver<'_, T, N>
Expand Down Expand Up @@ -87,19 +85,19 @@ where

#[inline]
fn get(&self, idx: usize) -> T {
self.as_slice()[idx]
self[idx]
}

#[inline]
fn set(&mut self, idx: usize, val: T) {
self.map.as_slice_mut()[idx] = val;
(*self)[idx] = val;
}

/// Count the set bytes in the map
fn count_bytes(&self) -> u64 {
let initial = self.initial();
let cnt = self.usable_count();
let map = self.as_slice();
let map = self.map.as_slice();
let mut res = 0;
for x in &map[0..cnt] {
if *x != initial {
Expand All @@ -110,7 +108,7 @@ where
}

fn usable_count(&self) -> usize {
self.as_slice().len()
self.len()
}

#[inline]
Expand All @@ -124,22 +122,22 @@ where
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
let initial = self.initial();
let cnt = self.usable_count();
let map = self.as_slice_mut();
let map = &mut (*self);
for x in &mut map[0..cnt] {
*x = initial;
}
Ok(())
}

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
fn how_many_set(&self, indexes: &[usize]) -> usize {
let initial = self.initial();
let cnt = self.usable_count();
let map = self.as_slice();
let map = self.map.as_slice();
let mut res = 0;
for i in indexes {
if *i < cnt && map[*i] != initial {
Expand All @@ -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
T: PartialEq + Copy + Hash + Serialize + DeserializeOwned + Debug + 'static,
{
fn map_slice(&mut self) -> &[Self::Entry] {
self.map.as_slice()
}

fn map_slice_mut(&mut self) -> &mut [Self::Entry] {
self.map.as_slice_mut()
}

fn size(&mut self) -> &usize {
&N
fn map_slice(&self) -> &[Self::Entry; N] {
&self.map
}

fn size_mut(&mut self) -> &mut usize {
&mut self.size
fn map_slice_mut(&mut self) -> &mut [Self::Entry; N] {
&mut self.map
}
}

impl<T, const N: usize> Deref for ConstMapObserver<'_, T, N> {
type Target = [T];

fn deref(&self) -> &[T] {
&self.map
self.map.as_slice()
}
}

impl<T, const N: usize> DerefMut for ConstMapObserver<'_, T, N> {
fn deref_mut(&mut self) -> &mut [T] {
&mut self.map
self.map.as_mut_slice()
}
}

Expand All @@ -194,48 +185,25 @@ where
/// Will get a pointer to the map and dereference it at any point in time.
/// The map must not move in memory!
#[must_use]
pub fn new(name: &'static str, map: &'a mut [T]) -> Self {
pub fn new(name: &'static str, map: &'a mut [T; N]) -> Self {
assert!(map.len() >= N);
Self {
map: OwnedMutSlice::from(map),
map: OwnedMutSizedSlice::from(map),
name: Cow::from(name),
initial: T::default(),
size: N,
}
}

/// Creates a new [`MapObserver`] from a raw pointer
///
/// # Safety
/// Will dereference the `map_ptr` with up to len elements.
pub unsafe fn from_mut_ptr(name: &'static str, map_ptr: *mut T) -> Self {
#[must_use]
pub unsafe fn from_mut_ptr(name: &'static str, map_ptr: NonNull<T>) -> Self {
ConstMapObserver {
map: OwnedMutSlice::from_raw_parts_mut(map_ptr, N),
map: OwnedMutSizedSlice::from_raw_mut(map_ptr),
name: Cow::from(name),
initial: T::default(),
size: N,
}
}
}

impl<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,
}
}
}
Loading

0 comments on commit 49ea0b0

Please sign in to comment.