Windows timeout fix with critical sections (#391)

* add

* unix fix

* unsafe positions

* another unsafe!

* ignore

* ignore

* make changes back

* fix

* fix

* fmt

* bug fix

* fmt

* compiler fence

* import

* typo

* add another critical section

* fix

* fix

* exclude windows book test

* typo

* fence

* why

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
This commit is contained in:
Dongjia Zhang 2021-12-09 21:55:20 +09:00 committed by GitHub
parent c6553c5351
commit fc0881194d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 261 additions and 153 deletions

View File

@ -31,6 +31,8 @@ jobs:
- name: Build the book - name: Build the book
run: cd docs && mdbook build run: cd docs && mdbook build
- name: Test the book - name: Test the book
# TODO: fix books test fail with updated windows-rs
if: runner.os != 'Windows'
run: cd docs && mdbook test -L ../target/debug/deps run: cd docs && mdbook test -L ../target/debug/deps
- name: Run tests - name: Run tests
run: cargo test run: cargo test

View File

@ -68,7 +68,7 @@ rand_core = { version = "0.5.1", optional = true } # This dependency allows us t
nix = { version = "0.23.0", optional = true } nix = { version = "0.23.0", optional = true }
regex = { version = "1", optional = true } regex = { version = "1", optional = true }
build_id = { version = "0.2.1", git = "https://github.com/domenukk/build_id", rev = "6a61943", optional = true } build_id = { version = "0.2.1", git = "https://github.com/domenukk/build_id", rev = "6a61943", optional = true }
uuid = { version = "0.8.2", optional = true, features = ["serde"] } uuid = { version = "0.8.2", optional = true, features = ["serde", "v4"] }
libm = "0.2.1" libm = "0.2.1"
wait-timeout = { version = "0.2", optional = true } # used by CommandExecutor to wait for child process wait-timeout = { version = "0.2", optional = true } # used by CommandExecutor to wait for child process
@ -86,11 +86,10 @@ regex = "1.4.5"
backtrace = "0.3" backtrace = "0.3"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
windows = "0.18.0" windows = { version = "0.28.0", features = ["std", "Win32_Foundation", "Win32_System_Threading", "Win32_System_Diagnostics_Debug", "Win32_System_Kernel", "Win32_System_Memory", "Win32_Security"] }
uuid = { version = "0.8", features = ["v4"] }
[target.'cfg(windows)'.build-dependencies] [target.'cfg(windows)'.build-dependencies]
windows = "0.18.0" windows = "0.28.0"
[[bench]] [[bench]]
name = "rand_speeds" name = "rand_speeds"

View File

@ -4,16 +4,6 @@ use rustc_version::{version_meta, Channel};
#[allow(clippy::ptr_arg, clippy::upper_case_acronyms)] #[allow(clippy::ptr_arg, clippy::upper_case_acronyms)]
fn main() { fn main() {
#[cfg(target_os = "windows")]
windows::build!(
Windows::Win32::Foundation::{HANDLE, HWND, BOOL, PSTR, CloseHandle, NTSTATUS},
Windows::Win32::System::{
Memory::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile, FILE_MAP, PAGE_TYPE},
Diagnostics::Debug::{SetUnhandledExceptionFilter, EXCEPTION_POINTERS, EXCEPTION_RECORD, LPTOP_LEVEL_EXCEPTION_FILTER},
Threading::{CreateTimerQueue, CreateTimerQueueTimer, DeleteTimerQueueEx, DeleteTimerQueueTimer, ExitProcess},
},
);
// Set cfg flags depending on release channel // Set cfg flags depending on release channel
match version_meta().unwrap().channel { match version_meta().unwrap().channel {
Channel::Stable => { Channel::Stable => {

View File

@ -1,4 +0,0 @@
//! Generated bindings
#[cfg(all(windows, feature = "std"))]
::windows::include_bindings!();

View File

@ -1,6 +1,5 @@
//! Bolts are no conceptual fuzzing elements, but they keep libafl-based fuzzers together. //! Bolts are no conceptual fuzzing elements, but they keep libafl-based fuzzers together.
pub mod bindings;
#[cfg(feature = "llmp_compression")] #[cfg(feature = "llmp_compression")]
pub mod compress; pub mod compress;
pub mod cpu; pub mod cpu;

View File

@ -1,10 +1,10 @@
//! Exception handling for Windows //! Exception handling for Windows
pub use crate::bolts::bindings::Windows::Win32::System::Diagnostics::Debug::{ pub use windows::Win32::System::Diagnostics::Debug::{
SetUnhandledExceptionFilter, EXCEPTION_POINTERS, SetUnhandledExceptionFilter, EXCEPTION_POINTERS,
}; };
pub use crate::bolts::bindings::Windows::Win32::Foundation::NTSTATUS; pub use windows::Win32::Foundation::NTSTATUS;
use crate::Error; use crate::Error;
use std::os::raw::{c_long, c_void}; use std::os::raw::{c_long, c_void};
@ -310,7 +310,6 @@ unsafe fn internal_handle_exception(
} }
type NativeHandlerType = extern "system" fn(*mut EXCEPTION_POINTERS) -> c_long; type NativeHandlerType = extern "system" fn(*mut EXCEPTION_POINTERS) -> c_long;
static mut PREVIOUS_HANDLER: Option<NativeHandlerType> = None;
/// Internal function that is being called whenever an exception arrives (stdcall). /// Internal function that is being called whenever an exception arrives (stdcall).
unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_POINTERS) -> c_long { unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_POINTERS) -> c_long {
@ -324,7 +323,7 @@ unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_PO
let exception_code = ExceptionCode::try_from(code.0).unwrap(); let exception_code = ExceptionCode::try_from(code.0).unwrap();
// println!("Received {}", exception_code); // println!("Received {}", exception_code);
let ret = internal_handle_exception(exception_code, exception_pointers); let ret = internal_handle_exception(exception_code, exception_pointers);
PREVIOUS_HANDLER.map_or(ret, |prev_handler| prev_handler(exception_pointers)) ret
} }
type NativeSignalHandlerType = unsafe extern "C" fn(i32); type NativeSignalHandlerType = unsafe extern "C" fn(i32);
@ -364,8 +363,6 @@ pub unsafe fn setup_exception_handler<T: 'static + Handler>(handler: &mut T) ->
} }
if let Some(prev) = SetUnhandledExceptionFilter(Some(core::mem::transmute( if let Some(prev) = SetUnhandledExceptionFilter(Some(core::mem::transmute(
handle_exception as *const c_void, handle_exception as *const c_void,
))) { ))) {}
PREVIOUS_HANDLER = Some(core::mem::transmute(prev as *const c_void));
}
Ok(()) Ok(())
} }

View File

@ -1053,16 +1053,7 @@ pub mod unix_shmem {
pub mod win32_shmem { pub mod win32_shmem {
use crate::{ use crate::{
bolts::{ bolts::shmem::{ShMem, ShMemId, ShMemProvider},
bindings::{
Windows::Win32::Foundation::{CloseHandle, BOOL, HANDLE, PSTR},
Windows::Win32::System::Memory::{
CreateFileMappingA, MapViewOfFile, OpenFileMappingA, UnmapViewOfFile,
FILE_MAP_ALL_ACCESS, PAGE_READWRITE,
},
},
shmem::{ShMem, ShMemId, ShMemProvider},
},
Error, Error,
}; };
@ -1072,6 +1063,14 @@ pub mod win32_shmem {
const INVALID_HANDLE_VALUE: isize = -1; const INVALID_HANDLE_VALUE: isize = -1;
use windows::{
Win32::Foundation::{CloseHandle, BOOL, HANDLE, PSTR},
Win32::System::Memory::{
CreateFileMappingA, MapViewOfFile, OpenFileMappingA, UnmapViewOfFile,
FILE_MAP_ALL_ACCESS, PAGE_READWRITE,
},
};
/// The default Sharedmap impl for windows using shmctl & shmget /// The default Sharedmap impl for windows using shmctl & shmget
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Win32ShMem { pub struct Win32ShMem {

View File

@ -37,9 +37,6 @@ use crate::{
Error, Error,
}; };
#[cfg(windows)]
use core::mem::transmute;
/// The inmem executor simply calls a target function, then returns afterwards. /// The inmem executor simply calls a target function, then returns afterwards.
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
@ -143,6 +140,16 @@ where
pub fn harness_mut(&mut self) -> &mut H { pub fn harness_mut(&mut self) -> &mut H {
self.harness_fn self.harness_fn
} }
#[inline]
pub fn handlers(&self) -> &InProcessHandlers {
&self.handlers
}
#[inline]
pub fn handlers_mut(&mut self) -> &mut InProcessHandlers {
&mut self.handlers
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -304,7 +311,13 @@ pub struct InProcessExecutorHandlerData {
pub crash_handler: *const c_void, pub crash_handler: *const c_void,
pub timeout_handler: *const c_void, pub timeout_handler: *const c_void,
#[cfg(windows)] #[cfg(windows)]
pub timer_queue: *mut c_void, pub tp_timer: *mut c_void,
#[cfg(windows)]
pub in_target: u64,
#[cfg(windows)]
pub critical: *mut c_void,
#[cfg(windows)]
pub timeout_input_ptr: *mut c_void,
} }
unsafe impl Send for InProcessExecutorHandlerData {} unsafe impl Send for InProcessExecutorHandlerData {}
@ -327,7 +340,13 @@ pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHan
/// The timeout handler fn /// The timeout handler fn
timeout_handler: ptr::null(), timeout_handler: ptr::null(),
#[cfg(windows)] #[cfg(windows)]
timer_queue: ptr::null_mut(), tp_timer: ptr::null_mut(),
#[cfg(windows)]
in_target: 0,
#[cfg(windows)]
critical: ptr::null_mut(),
#[cfg(windows)]
timeout_input_ptr: ptr::null_mut(),
}; };
#[must_use] #[must_use]
@ -629,11 +648,8 @@ mod windows_exception_handler {
use std::io::{stdout, Write}; use std::io::{stdout, Write};
use crate::{ use crate::{
bolts::{ bolts::os::windows_exceptions::{
bindings::Windows::Win32::{Foundation::HANDLE, System::Threading::ExitProcess}, ExceptionCode, Handler, CRASH_EXCEPTIONS, EXCEPTION_POINTERS,
os::windows_exceptions::{
ExceptionCode, Handler, CRASH_EXCEPTIONS, EXCEPTION_POINTERS,
},
}, },
corpus::{Corpus, Testcase}, corpus::{Corpus, Testcase},
events::{Event, EventFirer, EventRestarter}, events::{Event, EventFirer, EventRestarter},
@ -649,6 +665,9 @@ mod windows_exception_handler {
state::{HasClientPerfMonitor, HasMetadata, HasSolutions}, state::{HasClientPerfMonitor, HasMetadata, HasSolutions},
}; };
use core::sync::atomic::{compiler_fence, Ordering};
use windows::Win32::System::Threading::ExitProcess;
pub type HandlerFuncPtr = pub type HandlerFuncPtr =
unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut InProcessExecutorHandlerData); unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut InProcessExecutorHandlerData);
@ -676,9 +695,14 @@ mod windows_exception_handler {
} }
} }
use windows::Win32::System::Threading::{
EnterCriticalSection, LeaveCriticalSection, RTL_CRITICAL_SECTION,
};
pub unsafe extern "system" fn inproc_timeout_handler<E, EM, I, OC, OF, OT, S, Z>( pub unsafe extern "system" fn inproc_timeout_handler<E, EM, I, OC, OF, OT, S, Z>(
_p0: *mut u8,
global_state: *mut c_void, global_state: *mut c_void,
_p1: u8, _p1: *mut u8,
) where ) where
E: HasObservers<I, OT, S>, E: HasObservers<I, OT, S>,
EM: EventFirer<I> + EventRestarter<S>, EM: EventFirer<I> + EventRestarter<S>,
@ -691,63 +715,86 @@ mod windows_exception_handler {
{ {
let data: &mut InProcessExecutorHandlerData = let data: &mut InProcessExecutorHandlerData =
&mut *(global_state as *mut InProcessExecutorHandlerData); &mut *(global_state as *mut InProcessExecutorHandlerData);
compiler_fence(Ordering::SeqCst);
EnterCriticalSection(
(data.critical as *mut RTL_CRITICAL_SECTION)
.as_mut()
.unwrap(),
);
compiler_fence(Ordering::SeqCst);
let state = (data.state_ptr as *mut S).as_mut().unwrap(); if data.in_target == 1 {
let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); let state = (data.state_ptr as *mut S).as_mut().unwrap();
let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap(); let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap();
let executor = (data.executor_ptr as *const E).as_ref().unwrap(); let fuzzer = (data.fuzzer_ptr as *mut Z).as_mut().unwrap();
let observers = executor.observers(); let executor = (data.executor_ptr as *const E).as_ref().unwrap();
let observers = executor.observers();
if data.current_input_ptr.is_null() { if data.timeout_input_ptr.is_null() {
#[cfg(feature = "std")] #[cfg(feature = "std")]
dbg!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing. Exiting"); dbg!("TIMEOUT or SIGUSR2 happened, but currently not fuzzing. Exiting");
} else { } else {
#[cfg(feature = "std")] #[cfg(feature = "std")]
println!("Timeout in fuzz run."); println!("Timeout in fuzz run.");
#[cfg(feature = "std")] #[cfg(feature = "std")]
let _res = stdout().flush(); let _res = stdout().flush();
let input = (data.current_input_ptr as *const I).as_ref().unwrap(); let input = (data.timeout_input_ptr as *const I).as_ref().unwrap();
data.current_input_ptr = ptr::null(); data.timeout_input_ptr = ptr::null_mut();
let interesting = fuzzer let interesting = fuzzer
.objective_mut()
.is_interesting(state, event_mgr, input, observers, &ExitKind::Timeout)
.expect("In timeout handler objective failure.");
if interesting {
let mut new_testcase = Testcase::new(input.clone());
new_testcase.add_metadata(ExitKind::Timeout);
fuzzer
.objective_mut() .objective_mut()
.append_metadata(state, &mut new_testcase) .is_interesting(state, event_mgr, input, observers, &ExitKind::Timeout)
.expect("Failed adding metadata"); .expect("In timeout handler objective failure.");
state
.solutions_mut() if interesting {
.add(new_testcase) let mut new_testcase = Testcase::new(input.clone());
.expect("In timeout handler solutions failure."); new_testcase.add_metadata(ExitKind::Timeout);
event_mgr fuzzer
.fire( .objective_mut()
state, .append_metadata(state, &mut new_testcase)
Event::Objective { .expect("Failed adding metadata");
objective_size: state.solutions().count(), state
}, .solutions_mut()
) .add(new_testcase)
.expect("Could not send timeouting input"); .expect("In timeout handler solutions failure.");
event_mgr
.fire(
state,
Event::Objective {
objective_size: state.solutions().count(),
},
)
.expect("Could not send timeouting input");
}
event_mgr.on_restart(state).unwrap();
#[cfg(feature = "std")]
println!("Waiting for broker...");
event_mgr.await_restart_safe();
#[cfg(feature = "std")]
println!("Bye!");
event_mgr.await_restart_safe();
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection(
(data.critical as *mut RTL_CRITICAL_SECTION)
.as_mut()
.unwrap(),
);
ExitProcess(1);
} }
event_mgr.on_restart(state).unwrap();
#[cfg(feature = "std")]
println!("Waiting for broker...");
event_mgr.await_restart_safe();
#[cfg(feature = "std")]
println!("Bye!");
event_mgr.await_restart_safe();
ExitProcess(1);
} }
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection(
(data.critical as *mut RTL_CRITICAL_SECTION)
.as_mut()
.unwrap(),
);
compiler_fence(Ordering::SeqCst);
// println!("TIMER INVOKED!"); // println!("TIMER INVOKED!");
} }
@ -766,8 +813,24 @@ mod windows_exception_handler {
Z: HasObjective<I, OF, S>, Z: HasObjective<I, OF, S>,
{ {
// Have we set a timer_before? // Have we set a timer_before?
if let Some(x) = (data.timer_queue as *mut HANDLE).as_mut() { if let Some(x) =
windows_delete_timer_queue(*x); (data.tp_timer as *mut windows::Win32::System::Threading::TP_TIMER).as_mut()
{
/*
We want to prevent the timeout handler being run while the main thread is executing the crash handler
Timeout handler runs if it has access to the critical section or data.in_target == 0
Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid.
*/
compiler_fence(Ordering::SeqCst);
EnterCriticalSection(data.critical as *mut RTL_CRITICAL_SECTION);
compiler_fence(Ordering::SeqCst);
data.in_target = 0;
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection(data.critical as *mut RTL_CRITICAL_SECTION);
compiler_fence(Ordering::SeqCst);
windows_delete_timer_queue(x);
data.tp_timer = ptr::null_mut();
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -855,15 +918,12 @@ mod windows_exception_handler {
} }
#[cfg(windows)] #[cfg(windows)]
type WaitOrTimerCallback = unsafe extern "system" fn(param0: *mut c_void, param1: u8); pub trait HasInProcessHandlers {
fn inprocess_handlers(&self) -> &InProcessHandlers;
#[cfg(windows)]
pub trait HasTimeoutHandler {
fn timeout_handler(&self) -> WaitOrTimerCallback;
} }
#[cfg(windows)] #[cfg(windows)]
impl<'a, H, I, OT, S> HasTimeoutHandler for InProcessExecutor<'a, H, I, OT, S> impl<'a, H, I, OT, S> HasInProcessHandlers for InProcessExecutor<'a, H, I, OT, S>
where where
H: FnMut(&I) -> ExitKind, H: FnMut(&I) -> ExitKind,
I: Input, I: Input,
@ -871,9 +931,8 @@ where
{ {
/// the timeout handler /// the timeout handler
#[inline] #[inline]
fn timeout_handler(&self) -> WaitOrTimerCallback { fn inprocess_handlers(&self) -> &InProcessHandlers {
let func: WaitOrTimerCallback = unsafe { transmute(self.handlers.timeout_handler) }; &self.handlers
func
} }
} }

View File

@ -11,7 +11,7 @@ use crate::{
}; };
#[cfg(all(windows, feature = "std"))] #[cfg(all(windows, feature = "std"))]
use crate::executors::inprocess::{HasTimeoutHandler, GLOBAL_STATE}; use crate::executors::inprocess::{HasInProcessHandlers, GLOBAL_STATE};
#[cfg(unix)] #[cfg(unix)]
use core::{mem::zeroed, ptr::null_mut}; use core::{mem::zeroed, ptr::null_mut};
@ -19,16 +19,23 @@ use core::{mem::zeroed, ptr::null_mut};
use libc::c_int; use libc::c_int;
#[cfg(all(windows, feature = "std"))] #[cfg(all(windows, feature = "std"))]
use crate::bolts::bindings::Windows::Win32::{ use windows::Win32::{
Foundation::HANDLE, Foundation::FILETIME,
System::Threading::{ System::Threading::{
CreateTimerQueue, CreateTimerQueueTimer, DeleteTimerQueueEx, DeleteTimerQueueTimer, CloseThreadpoolTimer, CreateThreadpoolTimer, EnterCriticalSection,
WORKER_THREAD_FLAGS, InitializeCriticalSection, LeaveCriticalSection, SetThreadpoolTimer, RTL_CRITICAL_SECTION,
TP_CALLBACK_ENVIRON_V3, TP_TIMER,
}, },
}; };
#[cfg(all(windows, feature = "std"))] #[cfg(all(windows, feature = "std"))]
use core::{ffi::c_void, ptr::write_volatile}; use core::{
ffi::c_void,
ptr::{write, write_volatile},
};
#[cfg(windows)]
use core::sync::atomic::{compiler_fence, Ordering};
#[repr(C)] #[repr(C)]
#[cfg(unix)] #[cfg(unix)]
@ -54,18 +61,14 @@ const ITIMER_REAL: c_int = 0;
/// Reset and remove the timeout /// Reset and remove the timeout
#[cfg(unix)] #[cfg(unix)]
pub fn unix_remove_timeout() { pub unsafe fn unix_remove_timeout() {
unsafe { let mut itimerval_zero: Itimerval = zeroed();
let mut itimerval_zero: Itimerval = zeroed(); setitimer(ITIMER_REAL, &mut itimerval_zero, null_mut());
setitimer(ITIMER_REAL, &mut itimerval_zero, null_mut());
}
} }
#[cfg(all(windows, feature = "std"))] #[cfg(all(windows, feature = "std"))]
pub fn windows_delete_timer_queue(timer_queue: HANDLE) { pub unsafe fn windows_delete_timer_queue(tp_timer: *mut TP_TIMER) {
unsafe { CloseThreadpoolTimer(tp_timer);
DeleteTimerQueueEx(timer_queue, HANDLE::NULL);
}
} }
/// The timeout excutor is a wrapper that sets a timeout before each run /// The timeout excutor is a wrapper that sets a timeout before each run
@ -74,17 +77,25 @@ pub struct TimeoutExecutor<E> {
#[cfg(unix)] #[cfg(unix)]
itimerval: Itimerval, itimerval: Itimerval,
#[cfg(windows)] #[cfg(windows)]
milli_sec: u32, milli_sec: i64,
#[cfg(windows)] #[cfg(windows)]
ph_new_timer: HANDLE, tp_timer: *mut TP_TIMER,
#[cfg(windows)] #[cfg(windows)]
timer_queue: HANDLE, critical: RTL_CRITICAL_SECTION,
} }
#[cfg(windows)]
#[allow(non_camel_case_types)]
type PTP_TIMER_CALLBACK = unsafe extern "system" fn(
param0: *mut windows::Win32::System::Threading::TP_CALLBACK_INSTANCE,
param1: *mut c_void,
param2: *mut windows::Win32::System::Threading::TP_TIMER,
);
#[cfg(unix)]
impl<E> TimeoutExecutor<E> { impl<E> TimeoutExecutor<E> {
/// Create a new `TimeoutExecutor`, wrapping the given `executor` and checking for timeouts. /// Create a new `TimeoutExecutor`, wrapping the given `executor` and checking for timeouts.
/// This should usually be used for `InProcess` fuzzing. /// This should usually be used for `InProcess` fuzzing.
#[cfg(unix)]
pub fn new(executor: E, exec_tmout: Duration) -> Self { pub fn new(executor: E, exec_tmout: Duration) -> Self {
let milli_sec = exec_tmout.as_millis(); let milli_sec = exec_tmout.as_millis();
let it_value = Timeval { let it_value = Timeval {
@ -104,17 +115,32 @@ impl<E> TimeoutExecutor<E> {
itimerval, itimerval,
} }
} }
}
#[cfg(windows)] #[cfg(windows)]
impl<E: HasInProcessHandlers> TimeoutExecutor<E> {
pub fn new(executor: E, exec_tmout: Duration) -> Self { pub fn new(executor: E, exec_tmout: Duration) -> Self {
let milli_sec = exec_tmout.as_millis() as u32; let milli_sec = exec_tmout.as_millis() as i64;
let timer_queue = unsafe { CreateTimerQueue() }; let timeout_handler: PTP_TIMER_CALLBACK =
let ph_new_timer = HANDLE::NULL; unsafe { std::mem::transmute(executor.inprocess_handlers().timeout_handler) };
let tp_timer = unsafe {
CreateThreadpoolTimer(
Some(timeout_handler),
&mut GLOBAL_STATE as *mut _ as *mut c_void,
&TP_CALLBACK_ENVIRON_V3::default(),
)
};
let mut critical = RTL_CRITICAL_SECTION::default();
unsafe {
InitializeCriticalSection(&mut critical);
}
Self { Self {
executor, executor,
milli_sec, milli_sec,
ph_new_timer, tp_timer,
timer_queue, critical,
} }
} }
@ -149,10 +175,7 @@ impl<E> TimeoutExecutor<E> {
#[cfg(windows)] #[cfg(windows)]
pub fn windows_reset_timeout(&self) -> Result<(), Error> { pub fn windows_reset_timeout(&self) -> Result<(), Error> {
unsafe { unsafe {
let code = DeleteTimerQueueTimer(self.timer_queue, self.ph_new_timer, HANDLE::NULL); SetThreadpoolTimer(self.tp_timer, core::ptr::null(), 0, 0);
if !code.as_bool() {
return Err(Error::Unknown("DeleteTimerQueueTimer failed.".to_string()));
}
} }
Ok(()) Ok(())
} }
@ -161,7 +184,7 @@ impl<E> TimeoutExecutor<E> {
#[cfg(windows)] #[cfg(windows)]
impl<E, EM, I, S, Z> Executor<EM, I, S, Z> for TimeoutExecutor<E> impl<E, EM, I, S, Z> Executor<EM, I, S, Z> for TimeoutExecutor<E>
where where
E: Executor<EM, I, S, Z> + HasTimeoutHandler, E: Executor<EM, I, S, Z> + HasInProcessHandlers,
I: Input, I: Input,
{ {
fn run_target( fn run_target(
@ -173,23 +196,43 @@ where
) -> Result<ExitKind, Error> { ) -> Result<ExitKind, Error> {
unsafe { unsafe {
let data = &mut GLOBAL_STATE; let data = &mut GLOBAL_STATE;
write_volatile(&mut data.tp_timer, self.tp_timer as *mut _ as *mut c_void);
write_volatile( write_volatile(
&mut data.timer_queue, &mut data.critical,
&mut self.timer_queue as *mut _ as *mut c_void, &mut self.critical as *mut _ as *mut c_void,
); );
let code = CreateTimerQueueTimer( write_volatile(
&mut self.ph_new_timer, &mut data.timeout_input_ptr,
&self.timer_queue, &mut data.current_input_ptr as *mut _ as *mut c_void,
Some(self.executor.timeout_handler()),
&mut GLOBAL_STATE as *mut _ as *mut c_void,
self.milli_sec,
0,
WORKER_THREAD_FLAGS::default(),
); );
if !code.as_bool() { let tm: i64 = -1 * self.milli_sec * 10 * 1000;
return Err(Error::Unknown("CreateTimerQueue failed.".to_string())); let mut ft = FILETIME::default();
} ft.dwLowDateTime = (tm & 0xffffffff) as u32;
ft.dwHighDateTime = (tm >> 32) as u32;
compiler_fence(Ordering::SeqCst);
EnterCriticalSection(&mut self.critical);
compiler_fence(Ordering::SeqCst);
data.in_target = 1;
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection(&mut self.critical);
compiler_fence(Ordering::SeqCst);
SetThreadpoolTimer(self.tp_timer, &ft, 0, 0);
let ret = self.executor.run_target(fuzzer, state, mgr, input); let ret = self.executor.run_target(fuzzer, state, mgr, input);
compiler_fence(Ordering::SeqCst);
EnterCriticalSection(&mut self.critical);
compiler_fence(Ordering::SeqCst);
// Timeout handler will do nothing after we increment in_target value.
data.in_target = 0;
compiler_fence(Ordering::SeqCst);
LeaveCriticalSection(&mut self.critical);
compiler_fence(Ordering::SeqCst);
write_volatile(&mut data.timeout_input_ptr, core::ptr::null_mut());
self.windows_reset_timeout()?; self.windows_reset_timeout()?;
ret ret
} }
@ -238,6 +281,8 @@ where
#[cfg(windows)] #[cfg(windows)]
impl<E> Drop for TimeoutExecutor<E> { impl<E> Drop for TimeoutExecutor<E> {
fn drop(&mut self) { fn drop(&mut self) {
windows_delete_timer_queue(self.timer_queue); unsafe {
windows_delete_timer_queue(self.tp_timer);
}
} }
} }

View File

@ -17,6 +17,9 @@ use libafl::{
#[cfg(unix)] #[cfg(unix)]
use crate::asan_errors::ASAN_ERRORS; use crate::asan_errors::ASAN_ERRORS;
#[cfg(windows)]
use libafl::executors::inprocess::{HasInProcessHandlers, InProcessHandlers};
pub struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S> pub struct FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where where
FH: FridaHelper<'b>, FH: FridaHelper<'b>,
@ -125,3 +128,19 @@ where
} }
} }
} }
#[cfg(windows)]
impl<'a, 'b, 'c, FH, H, I, OT, S> HasInProcessHandlers
for FridaInProcessExecutor<'a, 'b, 'c, FH, H, I, OT, S>
where
H: FnMut(&I) -> ExitKind,
I: Input + HasTargetBytes,
OT: ObserversTuple<I, S>,
FH: FridaHelper<'b>,
{
/// the timeout handler
#[inline]
fn inprocess_handlers(&self) -> &InProcessHandlers {
&self.base.handlers()
}
}

View File

@ -30,6 +30,9 @@ do
cargo build || exit 1 cargo build || exit 1
echo "[+] Done building $fuzzer" echo "[+] Done building $fuzzer"
fi fi
# Save disk space
cargo clean
cd .. cd ..
echo "" echo ""
done done