Skip to content

Commit e864bc2

Browse files
rmalmaintokatoka
andauthored
Abort on triple fault for in process executors, refactor AddressFilter and PageFilter (#3026)
* abort on triple fault in generic inprocess signal handler * refactor qemu filters --------- Co-authored-by: Toka <[email protected]>
1 parent 191bc6d commit e864bc2

File tree

23 files changed

+384
-254
lines changed

23 files changed

+384
-254
lines changed

.github/workflows/build_and_test.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ jobs:
120120
# ---- build normal and examples ----
121121
- name: Run a normal build
122122
run: cargo build --verbose
123+
- name: Run libafl_qemu usermode tests
124+
run: cd libafl_qemu && cargo test
125+
- name: Run libafl_qemu systemmode tests
126+
run: cd libafl_qemu && cargo test --no-default-features --features x86_64,systemmode
123127
- name: Build examples
124128
run: cargo build --examples --verbose
125129

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,10 @@ fuzzer_libpng*
7878
*.patch
7979

8080
# Sometimes this happens
81-
rustc-ice-*
81+
rustc-ice-*
82+
83+
# perf files
84+
*.mm_profdata
85+
86+
# backup files
87+
*.bak

fuzzers/full_system/qemu_linux_kernel/src/fuzzer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use libafl_qemu::{
4141
executor::QemuExecutor,
4242
modules::{
4343
cmplog::CmpLogObserver, edges::StdEdgeCoverageClassicModule,
44-
utils::filters::HasAddressFilterTuples, CmpLogModule, EmulatorModuleTuple,
44+
utils::filters::HasAddressFilterTuple, CmpLogModule, EmulatorModuleTuple,
4545
},
4646
FastSnapshotManager, NopSnapshotManager, QemuInitError,
4747
};
@@ -78,12 +78,12 @@ fn get_emulator<C, ET, I, S>(
7878
QemuInitError,
7979
>
8080
where
81-
ET: EmulatorModuleTuple<I, S> + HasAddressFilterTuples,
81+
ET: EmulatorModuleTuple<I, S> + HasAddressFilterTuple,
8282
I: HasTargetBytes + Unpin,
8383
S: HasExecutions + Unpin,
8484
{
8585
// Allow linux process address space addresses as feedback
86-
modules.allow_address_range_all(LINUX_PROCESS_ADDRESS_RANGE);
86+
modules.allow_address_range_all(&LINUX_PROCESS_ADDRESS_RANGE);
8787

8888
Emulator::builder()
8989
.qemu_parameters(args)

fuzzers/full_system/qemu_linux_process/src/fuzzer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use libafl_qemu::{
4141
executor::QemuExecutor,
4242
modules::{
4343
cmplog::CmpLogObserver, edges::StdEdgeCoverageClassicModule,
44-
utils::filters::HasAddressFilterTuples, CmpLogModule, EmulatorModuleTuple,
44+
utils::filters::HasAddressFilterTuple, CmpLogModule, EmulatorModuleTuple,
4545
},
4646
FastSnapshotManager, NopSnapshotManager, QemuInitError, QemuSnapshotManager,
4747
};
@@ -78,12 +78,12 @@ fn get_emulator<C, ET, I, S>(
7878
QemuInitError,
7979
>
8080
where
81-
ET: EmulatorModuleTuple<I, S> + HasAddressFilterTuples,
81+
ET: EmulatorModuleTuple<I, S> + HasAddressFilterTuple,
8282
I: HasTargetBytes + Unpin,
8383
S: HasExecutions + Unpin,
8484
{
8585
// Allow linux process address space addresses as feedback
86-
modules.allow_address_range_all(LINUX_PROCESS_ADDRESS_RANGE);
86+
modules.allow_address_range_all(&LINUX_PROCESS_ADDRESS_RANGE);
8787

8888
Emulator::builder()
8989
.qemu_parameters(args)

libafl/src/events/simple.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ use core::{fmt::Debug, marker::PhantomData, time::Duration};
88
use libafl_bolts::ClientId;
99
#[cfg(all(feature = "std", any(windows, not(feature = "fork"))))]
1010
use libafl_bolts::os::startable_self;
11-
#[cfg(all(unix, feature = "std", not(miri)))]
12-
use libafl_bolts::os::unix_signals::setup_signal_handler;
1311
#[cfg(all(feature = "std", feature = "fork", unix))]
1412
use libafl_bolts::os::{ForkResult, fork};
13+
#[cfg(all(unix, feature = "std", not(miri)))]
14+
use libafl_bolts::os::{SIGNAL_RECURSION_EXIT, unix_signals::setup_signal_handler};
1515
#[cfg(feature = "std")]
1616
use libafl_bolts::{
1717
os::CTRL_C_EXIT,
@@ -510,6 +510,13 @@ where
510510
return Err(Error::shutting_down());
511511
}
512512

513+
#[cfg(all(unix, feature = "std"))]
514+
if child_status == SIGNAL_RECURSION_EXIT {
515+
return Err(Error::illegal_state(
516+
"The client is stuck in an unexpected signal handler recursion. It is most likely a fuzzer bug.",
517+
));
518+
}
519+
513520
#[expect(clippy::manual_assert)]
514521
if !staterestorer.has_content() {
515522
#[cfg(unix)]

libafl/src/executors/hooks/inprocess.rs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,9 @@ pub struct InProcessExecutorHandlerData {
355355
/// the pointer to the executor
356356
pub executor_ptr: *const c_void,
357357
pub(crate) current_input_ptr: *const c_void,
358-
pub(crate) in_handler: bool,
358+
359+
#[cfg(feature = "std")]
360+
pub(crate) signal_handler_depth: usize,
359361

360362
/// The timeout handler
361363
#[cfg(feature = "std")]
@@ -376,6 +378,9 @@ unsafe impl Send for InProcessExecutorHandlerData {}
376378
unsafe impl Sync for InProcessExecutorHandlerData {}
377379

378380
impl InProcessExecutorHandlerData {
381+
#[cfg(feature = "std")]
382+
const SIGNAL_HANDLER_MAX_DEPTH: usize = 3;
383+
379384
/// # Safety
380385
/// Only safe if not called twice and if the executor is not used from another borrow after this.
381386
#[cfg(all(feature = "std", any(unix, windows)))]
@@ -418,11 +423,19 @@ impl InProcessExecutorHandlerData {
418423
!self.current_input_ptr.is_null()
419424
}
420425

426+
/// Returns true if signal handling max depth has been reached, false otherwise
421427
#[cfg(all(feature = "std", any(unix, windows)))]
422-
pub(crate) fn set_in_handler(&mut self, v: bool) -> bool {
423-
let old = self.in_handler;
424-
self.in_handler = v;
425-
old
428+
pub(crate) fn signal_handler_enter(&mut self) -> (bool, usize) {
429+
self.signal_handler_depth += 1;
430+
(
431+
self.signal_handler_depth >= Self::SIGNAL_HANDLER_MAX_DEPTH,
432+
self.signal_handler_depth,
433+
)
434+
}
435+
436+
#[cfg(all(feature = "std", any(unix, windows)))]
437+
pub(crate) fn signal_handler_exit(&mut self) {
438+
self.signal_handler_depth -= 1;
426439
}
427440

428441
/// if data is valid, safely report a crash and return true.
@@ -500,7 +513,8 @@ pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHan
500513
// The current input for signal handling
501514
current_input_ptr: ptr::null(),
502515

503-
in_handler: false,
516+
#[cfg(feature = "std")]
517+
signal_handler_depth: 0,
504518

505519
// The crash handler fn
506520
#[cfg(feature = "std")]
@@ -560,11 +574,3 @@ pub unsafe fn inprocess_get_executor<'a, E>() -> Option<&'a mut E> {
560574
pub unsafe fn inprocess_get_input<'a, I>() -> Option<&'a I> {
561575
unsafe { (GLOBAL_STATE.current_input_ptr as *const I).as_ref() }
562576
}
563-
564-
/// Returns if we are executing in a crash/timeout handler
565-
#[must_use]
566-
pub fn inprocess_in_handler() -> bool {
567-
// # Safety
568-
// Safe because the state is set up and the handler is a single bool. Worst case we read an old value.
569-
unsafe { GLOBAL_STATE.in_handler }
570-
}

libafl/src/executors/hooks/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! These will be executed right before and after the executor's harness run.
33
44
/// windows crash/timeout handler and asan death callback
5-
#[cfg(windows)]
5+
#[cfg(all(windows, feature = "std"))]
66
pub mod windows;
77

88
/// *nix crash handler

libafl/src/executors/hooks/unix.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ pub mod unix_signal_handler {
55
use core::mem::transmute;
66
use std::{io::Write, panic};
77

8-
use libafl_bolts::os::unix_signals::{Signal, SignalHandler, ucontext_t};
8+
use libafl_bolts::os::{
9+
SIGNAL_RECURSION_EXIT,
10+
unix_signals::{Signal, SignalHandler, ucontext_t},
11+
};
912
use libc::siginfo_t;
1013

1114
use crate::{
@@ -50,11 +53,13 @@ pub mod unix_signal_handler {
5053
) {
5154
unsafe {
5255
let data = &raw mut GLOBAL_STATE;
53-
let in_handler = (*data).set_in_handler(true);
56+
let (max_depth_reached, signal_depth) = (*data).signal_handler_enter();
5457

55-
if in_handler {
56-
log::error!("We crashed inside a crash handler, but this should never happen!");
57-
libc::exit(56);
58+
if max_depth_reached {
59+
log::error!(
60+
"The in process signal handler has been triggered {signal_depth} times recursively, which is not expected. Exiting with error code {SIGNAL_RECURSION_EXIT}..."
61+
);
62+
libc::exit(SIGNAL_RECURSION_EXIT);
5863
}
5964

6065
match signal {
@@ -67,11 +72,11 @@ pub mod unix_signal_handler {
6772
_ => {
6873
if !(*data).crash_handler.is_null() {
6974
let func: HandlerFuncPtr = transmute((*data).crash_handler);
70-
(func)(signal, info, context, data);
75+
func(signal, info, context, data);
7176
}
7277
}
7378
}
74-
(*data).set_in_handler(in_handler);
79+
(*data).signal_handler_exit();
7580
}
7681
}
7782

@@ -95,11 +100,13 @@ pub mod unix_signal_handler {
95100
panic::set_hook(Box::new(move |panic_info| unsafe {
96101
old_hook(panic_info);
97102
let data = &raw mut GLOBAL_STATE;
98-
let in_handler = (*data).set_in_handler(true);
103+
let (max_depth_reached, signal_depth) = (*data).signal_handler_enter();
99104

100-
if in_handler {
101-
log::error!("We crashed inside a crash panic hook, but this should never happen!");
102-
libc::exit(56);
105+
if max_depth_reached {
106+
log::error!(
107+
"The in process signal handler has been triggered {signal_depth} times recursively, which is not expected. Exiting with error code {SIGNAL_RECURSION_EXIT}..."
108+
);
109+
libc::exit(SIGNAL_RECURSION_EXIT);
103110
}
104111

105112
if (*data).is_valid() {
@@ -121,7 +128,8 @@ pub mod unix_signal_handler {
121128

122129
libc::_exit(128 + 6); // SIGABRT exit code
123130
}
124-
(*data).set_in_handler(in_handler);
131+
132+
(*data).signal_handler_exit();
125133
}));
126134
}
127135

libafl/src/executors/hooks/windows.rs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/// In-Process crash handling for `Windows`
2-
#[cfg(all(windows, feature = "std"))]
32
pub mod windows_asan_handler {
43
use alloc::string::String;
54
use core::sync::atomic::{Ordering, compiler_fence};
65

6+
use libafl_bolts::os::SIGNAL_RECURSION_EXIT;
77
use windows::Win32::System::Threading::{
88
CRITICAL_SECTION, EnterCriticalSection, ExitProcess, LeaveCriticalSection,
99
};
@@ -35,13 +35,13 @@ pub mod windows_asan_handler {
3535
{
3636
unsafe {
3737
let data = &raw mut GLOBAL_STATE;
38-
let in_handler = (*data).set_in_handler(true);
38+
let (max_depth_reached, _signal_depth) = (*data).signal_handler_enter();
3939

40-
if in_handler {
40+
if max_depth_reached {
4141
log::error!(
4242
"We crashed inside a asan death handler, but this should never happen!"
4343
);
44-
ExitProcess(56);
44+
ExitProcess(SIGNAL_RECURSION_EXIT as u32);
4545
}
4646

4747
// Have we set a timer_before?
@@ -109,7 +109,6 @@ pub mod windows_asan_handler {
109109
}
110110
}
111111

112-
#[cfg(all(windows, feature = "std"))]
113112
/// The module to take care of windows crash or timeouts
114113
pub mod windows_exception_handler {
115114
#[cfg(feature = "std")]
@@ -126,9 +125,12 @@ pub mod windows_exception_handler {
126125
#[cfg(feature = "std")]
127126
use std::panic;
128127

129-
use libafl_bolts::os::windows_exceptions::{
130-
CRASH_EXCEPTIONS, EXCEPTION_HANDLERS_SIZE, EXCEPTION_POINTERS, ExceptionCode,
131-
ExceptionHandler,
128+
use libafl_bolts::os::{
129+
SIGNAL_RECURSION_EXIT,
130+
windows_exceptions::{
131+
CRASH_EXCEPTIONS, EXCEPTION_HANDLERS_SIZE, EXCEPTION_POINTERS, ExceptionCode,
132+
ExceptionHandler,
133+
},
132134
};
133135
use windows::Win32::System::Threading::{
134136
CRITICAL_SECTION, EnterCriticalSection, ExitProcess, LeaveCriticalSection,
@@ -168,18 +170,18 @@ pub mod windows_exception_handler {
168170
) {
169171
unsafe {
170172
let data = &raw mut GLOBAL_STATE;
171-
let in_handler = (*data).set_in_handler(true);
173+
let (max_depth_reached, _signal_depth) = (*data).signal_handler_enter();
172174

173-
if in_handler {
175+
if max_depth_reached {
174176
log::error!("We crashed inside a crash handler, but this should never happen!");
175-
ExitProcess(56);
177+
ExitProcess(SIGNAL_RECURSION_EXIT as u32);
176178
}
177179

178180
if !(*data).crash_handler.is_null() {
179181
let func: HandlerFuncPtr = transmute((*data).crash_handler);
180182
(func)(exception_pointers, data);
181183
}
182-
(*data).set_in_handler(in_handler);
184+
(*data).signal_handler_exit();
183185
}
184186
}
185187

@@ -208,11 +210,11 @@ pub mod windows_exception_handler {
208210
let old_hook = panic::take_hook();
209211
panic::set_hook(Box::new(move |panic_info| unsafe {
210212
let data = &raw mut GLOBAL_STATE;
211-
let in_handler = (*data).set_in_handler(true);
213+
let (max_depth_reached, _signal_depth) = (*data).signal_handler_enter();
212214

213-
if in_handler {
215+
if max_depth_reached {
214216
log::error!("We crashed inside a crash handler, but this should never happen!");
215-
ExitProcess(56);
217+
ExitProcess(SIGNAL_RECURSION_EXIT as u32);
216218
}
217219

218220
// Have we set a timer_before?
@@ -252,7 +254,7 @@ pub mod windows_exception_handler {
252254
ExitProcess(1);
253255
}
254256
old_hook(panic_info);
255-
(*data).set_in_handler(in_handler);
257+
(*data).signal_handler_exit();
256258
}));
257259
}
258260

libafl_bolts/src/os/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ pub struct ChildHandle {
4848
pub pid: pid_t,
4949
}
5050

51+
/// The special exit code when the target signal handler is crashing recursively
52+
pub const SIGNAL_RECURSION_EXIT: i32 = 101;
53+
5154
#[cfg(unix)]
5255
impl ChildHandle {
5356
/// Block until the child exited and the status code becomes available

0 commit comments

Comments
 (0)