Skip to content

Commit 49ea0b0

Browse files
authored
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
1 parent 56a5463 commit 49ea0b0

File tree

20 files changed

+1467
-894
lines changed

20 files changed

+1467
-894
lines changed

.github/workflows/qemu-fuzzer-tester-prepare/action.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ runs:
55
steps:
66
- name: Install QEMU deps
77
shell: bash
8-
run: apt-get update && apt-get install -y qemu-utils sudo python3-msgpack python3-jinja2 curl
8+
run: apt-get update && apt-get install -y qemu-utils sudo python3-msgpack python3-jinja2 curl python3-dev
99
- uses: dtolnay/rust-toolchain@stable
1010
- name: enable mult-thread for `make`
1111
shell: bash

fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{path::PathBuf, time::Duration};
1+
use std::{path::PathBuf, ptr::NonNull, time::Duration};
22

33
use libafl::{
44
corpus::{InMemoryCorpus, OnDiskCorpus},
@@ -46,7 +46,12 @@ pub fn main() {
4646
libafl::executors::ExitKind::Ok
4747
};
4848
// Create an observation channel using the signals map
49-
let observer = unsafe { ConstMapObserver::<u8, 3>::from_mut_ptr("signals", map_ptr) };
49+
let observer = unsafe {
50+
ConstMapObserver::<u8, 3>::from_mut_ptr(
51+
"signals",
52+
NonNull::new(map_ptr).expect("map ptr is null."),
53+
)
54+
};
5055
// Create a stacktrace observer
5156
let mut bt = shmem_provider.new_on_shmem::<Option<u64>>(None).unwrap();
5257
let bt_observer = BacktraceObserver::new(

fuzzers/baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor/src/main.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::path::PathBuf;
1+
use std::{path::PathBuf, ptr::NonNull};
22

33
use libafl::{
44
corpus::{InMemoryCorpus, OnDiskCorpus},
@@ -35,7 +35,12 @@ pub fn main() {
3535
libafl::executors::ExitKind::Ok
3636
};
3737
// Create an observation channel using the signals map
38-
let observer = unsafe { ConstMapObserver::<u8, 3>::from_mut_ptr("signals", array_ptr) };
38+
let observer = unsafe {
39+
ConstMapObserver::<u8, 3>::from_mut_ptr(
40+
"signals",
41+
NonNull::new(array_ptr).expect("map ptr is null"),
42+
)
43+
};
3944
// Create a stacktrace observer
4045
let bt_observer = BacktraceObserver::owned(
4146
"BacktraceObserver",

fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor/src/main.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ pub fn main() {
4242
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
4343
//let the forkserver know the shmid
4444
shmem.write_to_env("__AFL_SHM_ID").unwrap();
45-
let shmem_map = shmem.as_slice_mut();
45+
let shmem_map: &mut [u8; MAP_SIZE] = shmem
46+
.as_slice_mut()
47+
.try_into()
48+
.expect("could not convert slice to sized slice.");
4649

4750
// Create an observation channel using the signals map
4851
let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new(

fuzzers/binary_only/fuzzbench_fork_qemu/src/fuzzer.rs

+9-7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::{
99
io::{self, Write},
1010
path::PathBuf,
1111
process,
12+
ptr::NonNull,
1213
time::Duration,
1314
};
1415

@@ -160,14 +161,14 @@ fn fuzz(
160161
let mut edges_observer = unsafe {
161162
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_DEFAULT_SIZE>::from_mut_ptr(
162163
"edges",
163-
edges.as_mut_ptr(),
164+
NonNull::new(edges.as_mut_ptr()).expect("map ptr is null."),
164165
))
165166
.track_indices()
166167
};
167168

168169
let emulator_modules = tuple_list!(
169170
StdEdgeCoverageChildModule::builder()
170-
.map_observer(edges_observer.as_mut())
171+
.const_map_observer(edges_observer.as_mut())
171172
.build()?,
172173
CmpLogChildModule::default(),
173174
);
@@ -199,7 +200,8 @@ fn fuzz(
199200

200201
let stack_ptr: u64 = qemu.read_reg(Regs::Sp).unwrap();
201202
let mut ret_addr = [0; 8];
202-
unsafe { qemu.read_mem(stack_ptr, &mut ret_addr) };
203+
qemu.read_mem(stack_ptr, &mut ret_addr)
204+
.expect("qemu read failed");
203205
let ret_addr = u64::from_le_bytes(ret_addr);
204206

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

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

335337
unsafe {
336-
qemu.write_mem(input_addr, buf);
338+
qemu.write_mem_unchecked(input_addr, buf);
337339

338340
qemu.write_reg(Regs::Rdi, input_addr).unwrap();
339341
qemu.write_reg(Regs::Rsi, len as GuestReg).unwrap();
@@ -394,8 +396,8 @@ fn fuzz(
394396
#[cfg(unix)]
395397
{
396398
let null_fd = file_null.as_raw_fd();
397-
// dup2(null_fd, io::stdout().as_raw_fd())?;
398-
// dup2(null_fd, io::stderr().as_raw_fd())?;
399+
dup2(null_fd, io::stdout().as_raw_fd())?;
400+
dup2(null_fd, io::stderr().as_raw_fd())?;
399401
}
400402
// reopen file to make sure we're at the end
401403
log.replace(

fuzzers/binary_only/qemu_cmin/src/fuzzer.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
#[cfg(feature = "i386")]
44
use core::mem::size_of;
5-
use std::{env, io, path::PathBuf, process};
5+
use std::{env, io, path::PathBuf, process, ptr::NonNull};
66

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

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

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

221221
let modules = tuple_list!(StdEdgeCoverageChildModule::builder()
222-
.map_observer(edges_observer.as_mut())
222+
.const_map_observer(edges_observer.as_mut())
223223
.build()?);
224224

225225
let emulator = Emulator::empty().qemu(qemu).modules(modules).build()?;

libafl/src/executors/forkserver.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,7 @@ mod tests {
16351635

16361636
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
16371637
shmem.write_to_env("__AFL_SHM_ID").unwrap();
1638-
let shmem_buf = shmem.as_slice_mut();
1638+
let shmem_buf: &mut [u8; MAP_SIZE] = shmem.as_slice_mut().try_into().unwrap();
16391639

16401640
let edges_observer = HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::new(
16411641
"shared_mem",

libafl/src/observers/map/const_map.rs

+24-56
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,26 @@ use core::{
55
fmt::Debug,
66
hash::{Hash, Hasher},
77
ops::{Deref, DerefMut},
8+
ptr::NonNull,
89
};
910

1011
use ahash::RandomState;
11-
use libafl_bolts::{ownedref::OwnedMutSlice, AsSlice, AsSliceMut, HasLen, Named};
12+
use libafl_bolts::{ownedref::OwnedMutSizedSlice, HasLen, Named};
1213
use serde::{de::DeserializeOwned, Deserialize, Serialize};
1314

1415
use crate::{
15-
observers::{map::MapObserver, Observer, VariableLengthMapObserver},
16+
observers::{map::MapObserver, ConstLenMapObserver, Observer},
1617
Error,
1718
};
1819

19-
// TODO: remove the size field and implement ConstantLengthMapObserver
20-
2120
/// Use a const size to speedup `Feedback::is_interesting` when the user can
2221
/// know the size of the map at compile time.
2322
#[derive(Serialize, Deserialize, Debug)]
2423
#[allow(clippy::unsafe_derive_deserialize)]
2524
pub struct ConstMapObserver<'a, T, const N: usize> {
26-
map: OwnedMutSlice<'a, T>,
25+
map: OwnedMutSizedSlice<'a, T, N>,
2726
initial: T,
2827
name: Cow<'static, str>,
29-
size: usize,
3028
}
3129

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

8886
#[inline]
8987
fn get(&self, idx: usize) -> T {
90-
self.as_slice()[idx]
88+
self[idx]
9189
}
9290

9391
#[inline]
9492
fn set(&mut self, idx: usize, val: T) {
95-
self.map.as_slice_mut()[idx] = val;
93+
(*self)[idx] = val;
9694
}
9795

9896
/// Count the set bytes in the map
9997
fn count_bytes(&self) -> u64 {
10098
let initial = self.initial();
10199
let cnt = self.usable_count();
102-
let map = self.as_slice();
100+
let map = self.map.as_slice();
103101
let mut res = 0;
104102
for x in &map[0..cnt] {
105103
if *x != initial {
@@ -110,7 +108,7 @@ where
110108
}
111109

112110
fn usable_count(&self) -> usize {
113-
self.as_slice().len()
111+
self.len()
114112
}
115113

116114
#[inline]
@@ -124,22 +122,22 @@ where
124122
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
125123
let initial = self.initial();
126124
let cnt = self.usable_count();
127-
let map = self.as_slice_mut();
125+
let map = &mut (*self);
128126
for x in &mut map[0..cnt] {
129127
*x = initial;
130128
}
131129
Ok(())
132130
}
133131

134132
fn to_vec(&self) -> Vec<T> {
135-
self.as_slice().to_vec()
133+
self.map.to_vec()
136134
}
137135

138136
/// Get the number of set entries with the specified indexes
139137
fn how_many_set(&self, indexes: &[usize]) -> usize {
140138
let initial = self.initial();
141139
let cnt = self.usable_count();
142-
let map = self.as_slice();
140+
let map = self.map.as_slice();
143141
let mut res = 0;
144142
for i in indexes {
145143
if *i < cnt && map[*i] != initial {
@@ -150,37 +148,30 @@ where
150148
}
151149
}
152150

153-
impl<T, const N: usize> VariableLengthMapObserver for ConstMapObserver<'_, T, N>
151+
impl<T, const N: usize> ConstLenMapObserver<N> for ConstMapObserver<'_, T, N>
154152
where
155153
T: PartialEq + Copy + Hash + Serialize + DeserializeOwned + Debug + 'static,
156154
{
157-
fn map_slice(&mut self) -> &[Self::Entry] {
158-
self.map.as_slice()
159-
}
160-
161-
fn map_slice_mut(&mut self) -> &mut [Self::Entry] {
162-
self.map.as_slice_mut()
163-
}
164-
165-
fn size(&mut self) -> &usize {
166-
&N
155+
fn map_slice(&self) -> &[Self::Entry; N] {
156+
&self.map
167157
}
168158

169-
fn size_mut(&mut self) -> &mut usize {
170-
&mut self.size
159+
fn map_slice_mut(&mut self) -> &mut [Self::Entry; N] {
160+
&mut self.map
171161
}
172162
}
173163

174164
impl<T, const N: usize> Deref for ConstMapObserver<'_, T, N> {
175165
type Target = [T];
166+
176167
fn deref(&self) -> &[T] {
177-
&self.map
168+
self.map.as_slice()
178169
}
179170
}
180171

181172
impl<T, const N: usize> DerefMut for ConstMapObserver<'_, T, N> {
182173
fn deref_mut(&mut self) -> &mut [T] {
183-
&mut self.map
174+
self.map.as_mut_slice()
184175
}
185176
}
186177

@@ -194,48 +185,25 @@ where
194185
/// Will get a pointer to the map and dereference it at any point in time.
195186
/// The map must not move in memory!
196187
#[must_use]
197-
pub fn new(name: &'static str, map: &'a mut [T]) -> Self {
188+
pub fn new(name: &'static str, map: &'a mut [T; N]) -> Self {
198189
assert!(map.len() >= N);
199190
Self {
200-
map: OwnedMutSlice::from(map),
191+
map: OwnedMutSizedSlice::from(map),
201192
name: Cow::from(name),
202193
initial: T::default(),
203-
size: N,
204194
}
205195
}
206196

207197
/// Creates a new [`MapObserver`] from a raw pointer
208198
///
209199
/// # Safety
210200
/// Will dereference the `map_ptr` with up to len elements.
211-
pub unsafe fn from_mut_ptr(name: &'static str, map_ptr: *mut T) -> Self {
201+
#[must_use]
202+
pub unsafe fn from_mut_ptr(name: &'static str, map_ptr: NonNull<T>) -> Self {
212203
ConstMapObserver {
213-
map: OwnedMutSlice::from_raw_parts_mut(map_ptr, N),
204+
map: OwnedMutSizedSlice::from_raw_mut(map_ptr),
214205
name: Cow::from(name),
215206
initial: T::default(),
216-
size: N,
217-
}
218-
}
219-
}
220-
221-
impl<T, const N: usize> ConstMapObserver<'_, T, N>
222-
where
223-
T: Default + Clone,
224-
{
225-
/// Creates a new [`MapObserver`] with an owned map
226-
#[must_use]
227-
pub fn owned(name: &'static str, map: Vec<T>) -> Self {
228-
assert!(map.len() >= N);
229-
let initial = if map.is_empty() {
230-
T::default()
231-
} else {
232-
map[0].clone()
233-
};
234-
Self {
235-
map: OwnedMutSlice::from(map),
236-
name: Cow::from(name),
237-
initial,
238-
size: N,
239207
}
240208
}
241209
}

0 commit comments

Comments
 (0)