diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index d74a3f1d97..fbaefb34af 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -63,7 +63,8 @@ nix = "0.20.0" uds = "0.2.3" [target.'cfg(windows)'.dependencies] -windows = "0.3.1" +windows = "0.4.0" +uuid = { version = "0.8", features = ["v4"] } [target.'cfg(windows)'.build-dependencies] -windows = "0.3.1" +windows = "0.4.0" diff --git a/libafl/build.rs b/libafl/build.rs index 1c6e87c8ce..ff4a614f3d 100644 --- a/libafl/build.rs +++ b/libafl/build.rs @@ -1,9 +1,10 @@ fn main() { #[cfg(target_os = "windows")] windows::build!( - windows::win32::system_services::HANDLE, + windows::win32::system_services::{HANDLE, BOOL, PAGE_TYPE, PSTR, ExitProcess}, windows::win32::windows_programming::CloseHandle, // API needed for the shared memory windows::win32::system_services::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile}, + windows::win32::debug::{SetUnhandledExceptionFilter, EXCEPTION_POINTERS, EXCEPTION_RECORD, LPTOP_LEVEL_EXCEPTION_FILTER} ); } diff --git a/libafl/src/bolts/os/mod.rs b/libafl/src/bolts/os/mod.rs index 0a1b95601f..7a45acb215 100644 --- a/libafl/src/bolts/os/mod.rs +++ b/libafl/src/bolts/os/mod.rs @@ -1,2 +1,4 @@ #[cfg(unix)] pub mod unix_signals; +#[cfg(windows)] +pub mod windows_exceptions; diff --git a/libafl/src/bolts/os/windows_exceptions.rs b/libafl/src/bolts/os/windows_exceptions.rs new file mode 100644 index 0000000000..ed43a04c00 --- /dev/null +++ b/libafl/src/bolts/os/windows_exceptions.rs @@ -0,0 +1,332 @@ +pub use crate::bolts::bindings::windows::win32::debug::EXCEPTION_POINTERS; + +use crate::{bolts::bindings::windows::win32::debug::SetUnhandledExceptionFilter, Error}; + +use alloc::vec::Vec; +use core::{ + cell::UnsafeCell, + convert::TryFrom, + fmt::{self, Display, Formatter}, + ptr::write_volatile, + sync::atomic::{compiler_fence, Ordering}, +}; +use std::os::raw::{c_long, c_void}; + +use num_enum::{IntoPrimitive, TryFromPrimitive}; + +const EXCEPTION_CONTINUE_EXECUTION: c_long = -1; +//const EXCEPTION_CONTINUE_SEARCH: c_long = 0; +const EXCEPTION_EXECUTE_HANDLER: c_long = 1; + +// From https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L611 +pub const STATUS_WAIT_0: u32 = 0x00000000; +pub const STATUS_ABANDONED_WAIT_0: u32 = 0x00000080; +pub const STATUS_USER_APC: u32 = 0x000000C0; +pub const STATUS_TIMEOUT: u32 = 0x00000102; +pub const STATUS_PENDING: u32 = 0x00000103; +pub const STATUS_SEGMENT_NOTIFICATION: u32 = 0x40000005; +pub const STATUS_FATAL_APP_EXIT: u32 = 0x40000015; +pub const STATUS_GUARD_PAGE_VIOLATION: u32 = 0x80000001; +pub const STATUS_DATATYPE_MISALIGNMENT: u32 = 0x80000002; +pub const STATUS_BREAKPOINT: u32 = 0x80000003; +pub const STATUS_SINGLE_STEP: u32 = 0x80000004; +pub const STATUS_LONGJUMP: u32 = 0x80000026; +pub const STATUS_UNWIND_CONSOLIDATE: u32 = 0x80000029; +pub const STATUS_ACCESS_VIOLATION: u32 = 0xC0000005; +pub const STATUS_IN_PAGE_ERROR: u32 = 0xC0000006; +pub const STATUS_INVALID_HANDLE: u32 = 0xC0000008; +pub const STATUS_NO_MEMORY: u32 = 0xC0000017; +pub const STATUS_ILLEGAL_INSTRUCTION: u32 = 0xC000001D; +pub const STATUS_NONCONTINUABLE_EXCEPTION: u32 = 0xC0000025; +pub const STATUS_INVALID_DISPOSITION: u32 = 0xC0000026; +pub const STATUS_ARRAY_BOUNDS_EXCEEDED: u32 = 0xC000008C; +pub const STATUS_FLOAT_DENORMAL_OPERAND: u32 = 0xC000008D; +pub const STATUS_FLOAT_DIVIDE_BY_ZERO: u32 = 0xC000008E; +pub const STATUS_FLOAT_INEXACT_RESULT: u32 = 0xC000008F; +pub const STATUS_FLOAT_INVALID_OPERATION: u32 = 0xC0000090; +pub const STATUS_FLOAT_OVERFLOW: u32 = 0xC0000091; +pub const STATUS_FLOAT_STACK_CHECK: u32 = 0xC0000092; +pub const STATUS_FLOAT_UNDERFLOW: u32 = 0xC0000093; +pub const STATUS_INTEGER_DIVIDE_BY_ZERO: u32 = 0xC0000094; +pub const STATUS_INTEGER_OVERFLOW: u32 = 0xC0000095; +pub const STATUS_PRIVILEGED_INSTRUCTION: u32 = 0xC0000096; +pub const STATUS_STACK_OVERFLOW: u32 = 0xC00000FD; +pub const STATUS_DLL_NOT_FOUND: u32 = 0xC0000135; +pub const STATUS_ORDINAL_NOT_FOUND: u32 = 0xC0000138; +pub const STATUS_ENTRYPOINT_NOT_FOUND: u32 = 0xC0000139; +pub const STATUS_CONTROL_C_EXIT: u32 = 0xC000013A; +pub const STATUS_DLL_INIT_FAILED: u32 = 0xC0000142; +pub const STATUS_FLOAT_MULTIPLE_FAULTS: u32 = 0xC00002B4; +pub const STATUS_FLOAT_MULTIPLE_TRAPS: u32 = 0xC00002B5; +pub const STATUS_REG_NAT_CONSUMPTION: u32 = 0xC00002C9; +pub const STATUS_HEAP_CORRUPTION: u32 = 0xC0000374; +pub const STATUS_STACK_BUFFER_OVERRUN: u32 = 0xC0000409; +pub const STATUS_INVALID_CRUNTIME_PARAMETER: u32 = 0xC0000417; +pub const STATUS_ASSERTION_FAILURE: u32 = 0xC0000420; +pub const STATUS_SXS_EARLY_DEACTIVATION: u32 = 0xC015000F; +pub const STATUS_SXS_INVALID_DEACTIVATION: u32 = 0xC0150010; + +#[derive(IntoPrimitive, TryFromPrimitive, Clone, Copy)] +#[repr(u32)] +pub enum ExceptionCode { + // From https://docs.microsoft.com/en-us/windows/win32/debug/getexceptioncode + AccessViolation = STATUS_ACCESS_VIOLATION, + ArrayBoundsExceeded = STATUS_ARRAY_BOUNDS_EXCEEDED, + Breakpoint = STATUS_BREAKPOINT, + DatatypeMisalignment = STATUS_DATATYPE_MISALIGNMENT, + FltDenormalOperand = STATUS_FLOAT_DENORMAL_OPERAND, + FltDivideByZero = STATUS_FLOAT_DIVIDE_BY_ZERO, + FltInexactResult = STATUS_FLOAT_INEXACT_RESULT, + FltInvalidOperation = STATUS_FLOAT_INVALID_OPERATION, + FltOverflow = STATUS_FLOAT_OVERFLOW, + FltStackCheck = STATUS_FLOAT_STACK_CHECK, + FltUnderflow = STATUS_FLOAT_UNDERFLOW, + GuardPageViolation = STATUS_GUARD_PAGE_VIOLATION, + IllegalInstruction = STATUS_ILLEGAL_INSTRUCTION, + InPageError = STATUS_IN_PAGE_ERROR, + IntegerDivideByZero = STATUS_INTEGER_DIVIDE_BY_ZERO, + IntegerOverflow = STATUS_INTEGER_OVERFLOW, + InvalidDisposition = STATUS_INVALID_DISPOSITION, + InvalidHandle = STATUS_INVALID_HANDLE, + NoncontinuableException = STATUS_NONCONTINUABLE_EXCEPTION, + PrivilegedInstruction = STATUS_PRIVILEGED_INSTRUCTION, + SingleStep = STATUS_SINGLE_STEP, + StackOverflow = STATUS_STACK_OVERFLOW, + UnwindConsolidate = STATUS_UNWIND_CONSOLIDATE, + // Addition exceptions + Wait0 = STATUS_WAIT_0, + AbandonedWait0 = STATUS_ABANDONED_WAIT_0, + UserAPC = STATUS_USER_APC, + Timeout = STATUS_TIMEOUT, + Pending = STATUS_PENDING, + SegmentNotification = STATUS_SEGMENT_NOTIFICATION, + FatalAppExit = STATUS_FATAL_APP_EXIT, + Longjump = STATUS_LONGJUMP, + DLLNotFound = STATUS_DLL_NOT_FOUND, + OrdinalNotFound = STATUS_ORDINAL_NOT_FOUND, + EntryPointNotFound = STATUS_ENTRYPOINT_NOT_FOUND, + ControlCExit = STATUS_CONTROL_C_EXIT, + DllInitFailed = STATUS_DLL_INIT_FAILED, + FltMultipleFaults = STATUS_FLOAT_MULTIPLE_FAULTS, + FltMultipleTraps = STATUS_FLOAT_MULTIPLE_TRAPS, + RegNatConsumption = STATUS_REG_NAT_CONSUMPTION, + HeapCorruption = STATUS_HEAP_CORRUPTION, + StackBufferOverrun = STATUS_STACK_BUFFER_OVERRUN, + InvalidCRuntimeParameter = STATUS_INVALID_CRUNTIME_PARAMETER, + AssertionFailure = STATUS_ASSERTION_FAILURE, + SXSEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION, + SXSInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION, +} + +pub static CRASH_EXCEPTIONS: &[ExceptionCode] = &[ + ExceptionCode::AccessViolation, + ExceptionCode::ArrayBoundsExceeded, + ExceptionCode::FltDivideByZero, + ExceptionCode::GuardPageViolation, + ExceptionCode::IllegalInstruction, + ExceptionCode::InPageError, + ExceptionCode::IntegerDivideByZero, + ExceptionCode::InvalidHandle, + ExceptionCode::NoncontinuableException, + ExceptionCode::PrivilegedInstruction, + ExceptionCode::StackOverflow, + ExceptionCode::HeapCorruption, + ExceptionCode::StackBufferOverrun, + ExceptionCode::AssertionFailure, +]; + +impl PartialEq for ExceptionCode { + fn eq(&self, other: &Self) -> bool { + *self as u32 == *other as u32 + } +} + +impl Eq for ExceptionCode {} + +unsafe impl Sync for ExceptionCode {} + +impl Display for ExceptionCode { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + match self { + ExceptionCode::AccessViolation => write!(f, "STATUS_ACCESS_VIOLATION")?, + ExceptionCode::ArrayBoundsExceeded => write!(f, "STATUS_ARRAY_BOUNDS_EXCEEDED")?, + ExceptionCode::Breakpoint => write!(f, "STATUS_BREAKPOINT")?, + ExceptionCode::DatatypeMisalignment => write!(f, "STATUS_DATATYPE_MISALIGNMENT")?, + ExceptionCode::FltDenormalOperand => write!(f, "STATUS_FLOAT_DENORMAL_OPERAND")?, + ExceptionCode::FltDivideByZero => write!(f, "STATUS_FLOAT_DIVIDE_BY_ZERO")?, + ExceptionCode::FltInexactResult => write!(f, "STATUS_FLOAT_INEXACT_RESULT")?, + ExceptionCode::FltInvalidOperation => write!(f, "STATUS_FLOAT_INVALID_OPERATION")?, + ExceptionCode::FltOverflow => write!(f, "STATUS_FLOAT_OVERFLOW")?, + ExceptionCode::FltStackCheck => write!(f, "STATUS_FLOAT_STACK_CHECK")?, + ExceptionCode::FltUnderflow => write!(f, "STATUS_FLOAT_UNDERFLOW")?, + ExceptionCode::GuardPageViolation => write!(f, "STATUS_GUARD_PAGE_VIOLATION")?, + ExceptionCode::IllegalInstruction => write!(f, "STATUS_ILLEGAL_INSTRUCTION")?, + ExceptionCode::InPageError => write!(f, "STATUS_IN_PAGE_ERROR")?, + ExceptionCode::IntegerDivideByZero => write!(f, "STATUS_INTEGER_DIVIDE_BY_ZERO")?, + ExceptionCode::IntegerOverflow => write!(f, "STATUS_INTEGER_OVERFLOW")?, + ExceptionCode::InvalidDisposition => write!(f, "STATUS_INVALID_DISPOSITION")?, + ExceptionCode::InvalidHandle => write!(f, "STATUS_INVALID_HANDLE")?, + ExceptionCode::NoncontinuableException => write!(f, "STATUS_NONCONTINUABLE_EXCEPTION")?, + ExceptionCode::PrivilegedInstruction => write!(f, "STATUS_PRIVILEGED_INSTRUCTION")?, + ExceptionCode::SingleStep => write!(f, "STATUS_SINGLE_STEP")?, + ExceptionCode::StackOverflow => write!(f, "STATUS_STACK_OVERFLOW")?, + ExceptionCode::UnwindConsolidate => write!(f, "STATUS_UNWIND_CONSOLIDATE")?, + ExceptionCode::Wait0 => write!(f, "STATUS_WAIT_0")?, + ExceptionCode::AbandonedWait0 => write!(f, "STATUS_ABANDONED_WAIT_0")?, + ExceptionCode::UserAPC => write!(f, "STATUS_USER_APC")?, + ExceptionCode::Timeout => write!(f, "STATUS_TIMEOUT")?, + ExceptionCode::Pending => write!(f, "STATUS_PENDING")?, + ExceptionCode::SegmentNotification => write!(f, "STATUS_SEGMENT_NOTIFICATION")?, + ExceptionCode::FatalAppExit => write!(f, "STATUS_FATAL_APP_EXIT")?, + ExceptionCode::Longjump => write!(f, "STATUS_LONGJUMP")?, + ExceptionCode::DLLNotFound => write!(f, "STATUS_DLL_NOT_FOUND")?, + ExceptionCode::OrdinalNotFound => write!(f, "STATUS_ORDINAL_NOT_FOUND")?, + ExceptionCode::EntryPointNotFound => write!(f, "STATUS_ENTRYPOINT_NOT_FOUND")?, + ExceptionCode::ControlCExit => write!(f, "STATUS_CONTROL_C_EXIT")?, + ExceptionCode::DllInitFailed => write!(f, "STATUS_DLL_INIT_FAILED")?, + ExceptionCode::FltMultipleFaults => write!(f, "STATUS_FLOAT_MULTIPLE_FAULTS")?, + ExceptionCode::FltMultipleTraps => write!(f, "STATUS_FLOAT_MULTIPLE_TRAPS")?, + ExceptionCode::RegNatConsumption => write!(f, "STATUS_REG_NAT_CONSUMPTION")?, + ExceptionCode::HeapCorruption => write!(f, "STATUS_HEAP_CORRUPTION")?, + ExceptionCode::StackBufferOverrun => write!(f, "STATUS_STACK_BUFFER_OVERRUN")?, + ExceptionCode::InvalidCRuntimeParameter => { + write!(f, "STATUS_INVALID_CRUNTIME_PARAMETER")? + } + ExceptionCode::AssertionFailure => write!(f, "STATUS_ASSERTION_FAILURE")?, + ExceptionCode::SXSEarlyDeactivation => write!(f, "STATUS_SXS_EARLY_DEACTIVATION")?, + ExceptionCode::SXSInvalidDeactivation => write!(f, "STATUS_SXS_INVALID_DEACTIVATION")?, + }; + + Ok(()) + } +} + +pub static EXCEPTION_CODES_MAPPING: [ExceptionCode; 45] = [ + ExceptionCode::AccessViolation, + ExceptionCode::ArrayBoundsExceeded, + ExceptionCode::Breakpoint, + ExceptionCode::DatatypeMisalignment, + ExceptionCode::FltDenormalOperand, + ExceptionCode::FltDivideByZero, + ExceptionCode::FltInexactResult, + ExceptionCode::FltInvalidOperation, + ExceptionCode::FltOverflow, + ExceptionCode::FltStackCheck, + ExceptionCode::FltUnderflow, + ExceptionCode::GuardPageViolation, + ExceptionCode::IllegalInstruction, + ExceptionCode::InPageError, + ExceptionCode::IntegerDivideByZero, + ExceptionCode::IntegerOverflow, + ExceptionCode::InvalidDisposition, + ExceptionCode::InvalidHandle, + ExceptionCode::NoncontinuableException, + ExceptionCode::PrivilegedInstruction, + ExceptionCode::SingleStep, + ExceptionCode::StackOverflow, + ExceptionCode::UnwindConsolidate, + ExceptionCode::Wait0, + ExceptionCode::AbandonedWait0, + ExceptionCode::UserAPC, + ExceptionCode::Timeout, + ExceptionCode::Pending, + ExceptionCode::SegmentNotification, + ExceptionCode::FatalAppExit, + ExceptionCode::Longjump, + ExceptionCode::DLLNotFound, + ExceptionCode::OrdinalNotFound, + ExceptionCode::EntryPointNotFound, + ExceptionCode::ControlCExit, + ExceptionCode::DllInitFailed, + ExceptionCode::FltMultipleFaults, + ExceptionCode::FltMultipleTraps, + ExceptionCode::RegNatConsumption, + ExceptionCode::HeapCorruption, + ExceptionCode::StackBufferOverrun, + ExceptionCode::InvalidCRuntimeParameter, + ExceptionCode::AssertionFailure, + ExceptionCode::SXSEarlyDeactivation, + ExceptionCode::SXSInvalidDeactivation, +]; + +pub trait Handler { + /// Handle an exception + fn handle( + &mut self, + exception_code: ExceptionCode, + exception_pointers: *mut EXCEPTION_POINTERS, + ); + /// Return a list of exceptions to handle + fn exceptions(&self) -> Vec; +} + +struct HandlerHolder { + handler: UnsafeCell<*mut dyn Handler>, +} + +unsafe impl Send for HandlerHolder {} + +/// Keep track of which handler is registered for which exception +static mut EXCEPTION_HANDLERS: [Option; 64] = [ + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, +]; + +type NativeHandlerType = extern "system" fn(*mut EXCEPTION_POINTERS) -> c_long; +static mut PREVIOUS_HANDLER: Option = None; + +/// Internal function that is being called whenever an exception arrives (stdcall). +unsafe extern "system" fn handle_exception(exception_pointers: *mut EXCEPTION_POINTERS) -> c_long { + let code = exception_pointers + .as_mut() + .unwrap() + .exception_record + .as_mut() + .unwrap() + .exception_code; + let exception_code = ExceptionCode::try_from(code).unwrap(); + let index = EXCEPTION_CODES_MAPPING + .iter() + .position(|x| *x == exception_code) + .unwrap(); + let ret = match &EXCEPTION_HANDLERS[index] { + Some(handler_holder) => { + let handler = &mut **handler_holder.handler.get(); + handler.handle(exception_code, exception_pointers); + EXCEPTION_EXECUTE_HANDLER + } + None => EXCEPTION_EXECUTE_HANDLER, + }; + if let Some(prev_handler) = PREVIOUS_HANDLER { + prev_handler(exception_pointers) + } else { + ret + } +} + +/// Setup Win32 exception handlers in a somewhat rusty way. +pub unsafe fn setup_exception_handler(handler: &mut T) -> Result<(), Error> { + let exceptions = handler.exceptions(); + for exception_code in exceptions { + let index = EXCEPTION_CODES_MAPPING + .iter() + .position(|x| *x == exception_code) + .unwrap(); + write_volatile( + &mut EXCEPTION_HANDLERS[index], + Some(HandlerHolder { + handler: UnsafeCell::new(handler as *mut dyn Handler), + }), + ); + } + compiler_fence(Ordering::SeqCst); + + if let Some(prev) = SetUnhandledExceptionFilter(Some(core::mem::transmute( + handle_exception as *const c_void, + ))) { + PREVIOUS_HANDLER = Some(core::mem::transmute(prev as *const c_void)); + } + Ok(()) +} diff --git a/libafl/src/bolts/shmem.rs b/libafl/src/bolts/shmem.rs index 539cab9611..a4d55d0100 100644 --- a/libafl/src/bolts/shmem.rs +++ b/libafl/src/bolts/shmem.rs @@ -318,14 +318,14 @@ pub mod unix_shmem { impl Drop for UnixShMem { fn drop(&mut self) { unsafe { - afl_shmem_deinit(self); + unix_shmem_deinit(self); } } } /// Create an uninitialized shmap #[cfg(unix)] - const fn afl_shmem_unitialized() -> UnixShMem { + const fn unix_shmem_unitialized() -> UnixShMem { UnixShMem { shm_str: [0; 20], shm_id: -1, @@ -337,8 +337,8 @@ pub mod unix_shmem { #[cfg(unix)] impl UnixShMem { pub fn from_str(shm_str: &CStr, map_size: usize) -> Result { - let mut ret = afl_shmem_unitialized(); - let map = unsafe { afl_shmem_by_str(&mut ret, shm_str, map_size) }; + let mut ret = unix_shmem_unitialized(); + let map = unsafe { unix_shmem_by_str(&mut ret, shm_str, map_size) }; if !map.is_null() { Ok(ret) } else { @@ -350,8 +350,8 @@ pub mod unix_shmem { } pub fn new(map_size: usize) -> Result { - let mut ret = afl_shmem_unitialized(); - let map = unsafe { afl_shmem_init(&mut ret, map_size) }; + let mut ret = unix_shmem_unitialized(); + let map = unsafe { unix_shmem_init(&mut ret, map_size) }; if !map.is_null() { Ok(ret) } else { @@ -364,7 +364,7 @@ pub mod unix_shmem { } /// Deinitialize this shmem instance - unsafe fn afl_shmem_deinit(shm: *mut UnixShMem) { + unsafe fn unix_shmem_deinit(shm: *mut UnixShMem) { if shm.is_null() || (*shm).map.is_null() { /* Serialized map id */ // Not set or not initialized; @@ -377,7 +377,7 @@ pub mod unix_shmem { /// Functions to create Shared memory region, for observation channels and /// opening inputs and stuff. - unsafe fn afl_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar { + unsafe fn unix_shmem_init(shm: *mut UnixShMem, map_size: usize) -> *mut c_uchar { (*shm).map_size = map_size; (*shm).map = ptr::null_mut(); (*shm).shm_id = shmget( @@ -409,7 +409,7 @@ pub mod unix_shmem { } /// Uses a shmap id string to open a shared map - unsafe fn afl_shmem_by_str( + unsafe fn unix_shmem_by_str( shm: *mut UnixShMem, shm_str: &CStr, map_size: usize, @@ -443,18 +443,140 @@ pub mod unix_shmem { #[cfg(all(feature = "std", windows))] pub mod shmem { - //TODO use super::ShMem; + use super::ShMem; + use crate::{ + bolts::bindings::{ + windows::win32::system_services::{ + CreateFileMappingA, MapViewOfFile, OpenFileMappingA, UnmapViewOfFile, + }, + windows::win32::system_services::{BOOL, HANDLE, PAGE_TYPE, PSTR}, + windows::win32::windows_programming::CloseHandle, + }, + Error, + }; + + use core::{ffi::c_void, ptr, slice}; + use uuid::Uuid; + + const INVALID_HANDLE_VALUE: isize = -1; + const FILE_MAP_ALL_ACCESS: u32 = 0xf001f; /// The default Sharedmap impl for windows using shmctl & shmget #[derive(Clone, Debug)] pub struct Win32ShMem { - pub filename: [u8; 64], - //TODO pub handle: windows::win32::system_services::HANDLE, + pub shm_str: [u8; 20], + pub handle: HANDLE, pub map: *mut u8, pub map_size: usize, } - // TODO complete + impl ShMem for Win32ShMem { + fn existing_from_shm_slice( + map_str_bytes: &[u8; 20], + map_size: usize, + ) -> Result { + Self::from_str(map_str_bytes, map_size) + } + + fn new_map(map_size: usize) -> Result { + Self::new(map_size) + } + + fn shm_slice(&self) -> &[u8; 20] { + &self.shm_str + } + + fn map(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.map, self.map_size) } + } + + fn map_mut(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.map, self.map_size) } + } + } + + /// Deinit sharedmaps on drop + impl Drop for Win32ShMem { + fn drop(&mut self) { + unsafe { + UnmapViewOfFile(self.map as *mut c_void); + CloseHandle(self.handle); + } + } + } + + impl Win32ShMem { + pub fn from_str(map_str_bytes: &[u8; 20], map_size: usize) -> Result { + unsafe { + let handle = OpenFileMappingA( + FILE_MAP_ALL_ACCESS, + BOOL(0), + PSTR(map_str_bytes as *const u8 as *mut u8), + ); + if handle == HANDLE(0) { + return Err(Error::Unknown(format!( + "Cannot open shared memory {}", + String::from_utf8_lossy(map_str_bytes) + ))); + } + let map = + MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; + if map == ptr::null_mut() { + return Err(Error::Unknown(format!( + "Cannot map shared memory {}", + String::from_utf8_lossy(map_str_bytes) + ))); + } + let mut ret = Self { + shm_str: [0; 20], + handle: handle, + map: map, + map_size: map_size, + }; + ret.shm_str.clone_from_slice(map_str_bytes); + Ok(ret) + } + } + + pub fn new(map_size: usize) -> Result { + unsafe { + let uuid = Uuid::new_v4(); + let mut map_str = format!("libafl_{}", uuid.to_simple()); + let map_str_bytes = map_str.as_mut_vec(); + map_str_bytes[19] = 0; // Trucate to size 20 + let handle = CreateFileMappingA( + HANDLE(INVALID_HANDLE_VALUE), + ptr::null_mut(), + PAGE_TYPE::PAGE_READWRITE, + 0, + map_size as u32, + PSTR(map_str_bytes.as_mut_ptr()), + ); + if handle == HANDLE(0) { + return Err(Error::Unknown(format!( + "Cannot create shared memory {}", + String::from_utf8_lossy(map_str_bytes) + ))); + } + let map = + MapViewOfFile(handle.clone(), FILE_MAP_ALL_ACCESS, 0, 0, map_size) as *mut u8; + if map == ptr::null_mut() { + return Err(Error::Unknown(format!( + "Cannot map shared memory {}", + String::from_utf8_lossy(map_str_bytes) + ))); + } + let mut ret = Self { + shm_str: [0; 20], + handle: handle, + map: map, + map_size: map_size, + }; + ret.shm_str.clone_from_slice(&map_str_bytes[0..20]); + Ok(ret) + } + } + } } #[cfg(test)] diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index e040bd18dd..7606284edf 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -1,16 +1,18 @@ //! The InProcess Executor is a libfuzzer-like executor, that will simply call a function. //! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective. -use core::marker::PhantomData; - -#[cfg(unix)] use core::{ + ffi::c_void, + marker::PhantomData, ptr::{self, write_volatile}, sync::atomic::{compiler_fence, Ordering}, }; #[cfg(unix)] -use crate::bolts::os::unix_signals::{c_void, setup_signal_handler}; +use crate::bolts::os::unix_signals::setup_signal_handler; +#[cfg(windows)] +use crate::bolts::os::windows_exceptions::setup_exception_handler; + use crate::{ bolts::tuples::Named, corpus::Corpus, @@ -60,6 +62,27 @@ where &mut data.current_input_ptr, _input as *const _ as *const c_void, ); + write_volatile( + &mut data.observers_ptr, + &self.observers as *const _ as *const c_void, + ); + // Direct raw pointers access /aliasing is pretty undefined behavior. + // Since the state and event may have moved in memory, refresh them right before the signal may happen + write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void); + write_volatile(&mut data.event_mgr_ptr, _event_mgr as *mut _ as *mut c_void); + compiler_fence(Ordering::SeqCst); + } + #[cfg(windows)] + unsafe { + let data = &mut windows_exception_handler::GLOBAL_STATE; + write_volatile( + &mut data.current_input_ptr, + _input as *const _ as *const c_void, + ); + write_volatile( + &mut data.observers_ptr, + &self.observers as *const _ as *const c_void, + ); // Direct raw pointers access /aliasing is pretty undefined behavior. // Since the state and event may have moved in memory, refresh them right before the signal may happen write_volatile(&mut data.state_ptr, _state as *mut _ as *mut c_void); @@ -73,6 +96,16 @@ where fn run_target(&mut self, input: &I) -> Result { let bytes = input.target_bytes(); let ret = (self.harness_fn)(self, bytes.as_slice()); + Ok(ret) + } + + #[inline] + fn post_exec( + &mut self, + _state: &mut S, + _event_mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> { #[cfg(unix)] unsafe { write_volatile( @@ -81,7 +114,15 @@ where ); compiler_fence(Ordering::SeqCst); } - Ok(ret) + #[cfg(windows)] + unsafe { + write_volatile( + &mut windows_exception_handler::GLOBAL_STATE.current_input_ptr, + ptr::null(), + ); + compiler_fence(Ordering::SeqCst); + } + Ok(()) } } @@ -139,10 +180,6 @@ where #[cfg(unix)] unsafe { let data = &mut unix_signal_handler::GLOBAL_STATE; - write_volatile( - &mut data.observers_ptr, - &observers as *const _ as *const c_void, - ); write_volatile( &mut data.crash_handler, unix_signal_handler::inproc_crash_handler::, @@ -155,6 +192,21 @@ where setup_signal_handler(data)?; compiler_fence(Ordering::SeqCst); } + #[cfg(windows)] + unsafe { + let data = &mut windows_exception_handler::GLOBAL_STATE; + write_volatile( + &mut data.crash_handler, + windows_exception_handler::inproc_crash_handler::, + ); + //write_volatile( + // &mut data.timeout_handler, + // windows_exception_handler::inproc_timeout_handler::, + //); + + setup_exception_handler(data)?; + compiler_fence(Ordering::SeqCst); + } Ok(Self { harness_fn, @@ -187,6 +239,8 @@ mod unix_signal_handler { state::{HasObjectives, HasSolutions}, }; + // TODO merge GLOBAL_STATE with the Windows one + /// Signal handling on unix systems needs some nasty unsafe. pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData { /// The state ptr for signal handling @@ -408,6 +462,166 @@ mod unix_signal_handler { } } +#[cfg(windows)] +mod windows_exception_handler { + use alloc::vec::Vec; + use core::{ffi::c_void, ptr}; + #[cfg(feature = "std")] + use std::io::{stdout, Write}; + + use crate::{ + bolts::{ + bindings::windows::win32::system_services::ExitProcess, + os::windows_exceptions::{ + ExceptionCode, Handler, CRASH_EXCEPTIONS, EXCEPTION_POINTERS, + }, + }, + corpus::{Corpus, Testcase}, + events::{Event, EventManager}, + executors::ExitKind, + feedbacks::FeedbacksTuple, + inputs::{HasTargetBytes, Input}, + observers::ObserversTuple, + state::{HasObjectives, HasSolutions}, + }; + + /// Signal handling on unix systems needs some nasty unsafe. + pub static mut GLOBAL_STATE: InProcessExecutorHandlerData = InProcessExecutorHandlerData { + /// The state ptr for signal handling + state_ptr: ptr::null_mut(), + /// The event manager ptr for signal handling + event_mgr_ptr: ptr::null_mut(), + /// The observers ptr for signal handling + observers_ptr: ptr::null(), + /// The current input for signal handling + current_input_ptr: ptr::null(), + /// The crash handler fn + crash_handler: nop_handler, + // The timeout handler fn + //timeout_handler: nop_handler, + }; + + pub struct InProcessExecutorHandlerData { + pub state_ptr: *mut c_void, + pub event_mgr_ptr: *mut c_void, + pub observers_ptr: *const c_void, + pub current_input_ptr: *const c_void, + pub crash_handler: unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut Self), + //pub timeout_handler: unsafe fn(ExceptionCode, *mut EXCEPTION_POINTERS, &mut Self), + } + + unsafe impl Send for InProcessExecutorHandlerData {} + unsafe impl Sync for InProcessExecutorHandlerData {} + + unsafe fn nop_handler( + _code: ExceptionCode, + _exception_pointers: *mut EXCEPTION_POINTERS, + _data: &mut InProcessExecutorHandlerData, + ) { + } + + impl Handler for InProcessExecutorHandlerData { + fn handle(&mut self, code: ExceptionCode, exception_pointers: *mut EXCEPTION_POINTERS) { + unsafe { + let data = &mut GLOBAL_STATE; + (data.crash_handler)(code, exception_pointers, data) + } + } + + fn exceptions(&self) -> Vec { + CRASH_EXCEPTIONS.to_vec() + } + } + + pub unsafe fn inproc_crash_handler( + code: ExceptionCode, + exception_pointers: *mut EXCEPTION_POINTERS, + data: &mut InProcessExecutorHandlerData, + ) where + EM: EventManager, + OT: ObserversTuple, + OC: Corpus, + OFT: FeedbacksTuple, + S: HasObjectives + HasSolutions, + I: Input + HasTargetBytes, + { + #[cfg(feature = "std")] + println!("Crashed with {}", code); + if !data.current_input_ptr.is_null() { + let state = (data.state_ptr as *mut S).as_mut().unwrap(); + let event_mgr = (data.event_mgr_ptr as *mut EM).as_mut().unwrap(); + let observers = (data.observers_ptr as *const OT).as_ref().unwrap(); + + #[cfg(feature = "std")] + println!("Child crashed!"); + #[cfg(feature = "std")] + let _ = stdout().flush(); + + let input = (data.current_input_ptr as *const I).as_ref().unwrap(); + // Make sure we don't crash in the crash handler forever. + data.current_input_ptr = ptr::null(); + + let obj_fitness = state + .objectives_mut() + .is_interesting_all(&input, observers, ExitKind::Crash) + .expect("In crash handler objectives failure."); + if obj_fitness > 0 { + let new_input = input.clone(); + state + .solutions_mut() + .add(Testcase::new(new_input)) + .expect("In crash handler solutions failure."); + event_mgr + .fire( + state, + Event::Objective { + objective_size: state.solutions().count(), + }, + ) + .expect("Could not send crashing input"); + } + + event_mgr.on_restart(state).unwrap(); + + #[cfg(feature = "std")] + println!("Waiting for broker..."); + event_mgr.await_restart_safe(); + #[cfg(feature = "std")] + println!("Bye!"); + + ExitProcess(1); + } else { + #[cfg(feature = "std")] + { + println!("Double crash\n"); + let crash_addr = exception_pointers + .as_mut() + .unwrap() + .exception_record + .as_mut() + .unwrap() + .exception_address as usize; + + println!( + "We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.", + crash_addr + ); + } + #[cfg(feature = "std")] + { + println!("Type QUIT to restart the child"); + let mut line = String::new(); + while line.trim() != "QUIT" { + std::io::stdin().read_line(&mut line).unwrap(); + } + } + + // TODO tell the parent to not restart + ExitProcess(1); + } + } +} + #[cfg(test)] mod tests { diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 77cfdda548..a5ee692469 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -96,7 +96,12 @@ where /// Called right after execution finished. #[inline] - fn post_exec(&mut self, _state: &S, _event_mgr: &mut EM, _input: &I) -> Result<(), Error> + fn post_exec( + &mut self, + _state: &mut S, + _event_mgr: &mut EM, + _input: &I, + ) -> Result<(), Error> where EM: EventManager, { diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs index aa83b5069c..6acb753032 100644 --- a/libafl/src/executors/timeout.rs +++ b/libafl/src/executors/timeout.rs @@ -132,7 +132,7 @@ where #[inline] fn post_exec, S>( &mut self, - _state: &S, + _state: &mut S, _event_mgr: &mut EM, _input: &I, ) -> Result<(), Error> {