Support on_crash & on_timeout callbacks for libafl_qemu modules (#2620)
* support (unsafe) on_crash / on_timeout callbacks for modules * use libc types in bindgen
This commit is contained in:
parent
f0da4d15da
commit
d96d833760
@ -181,6 +181,8 @@ pub fn generate(
|
||||
.allowlist_function("qemu_main_loop")
|
||||
.allowlist_function("qemu_cleanup")
|
||||
.blocklist_function("main_loop_wait") // bindgen issue #1313
|
||||
.blocklist_type("siginfo_t")
|
||||
.raw_line("use libc::siginfo_t;")
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));
|
||||
|
||||
// arch specific functions
|
||||
|
@ -1,7 +1,9 @@
|
||||
/* 1.83.0-nightly */
|
||||
/* qemu git hash: d6637939526f453c69f4c6bfe4635feb5dc5c0be */
|
||||
/* 1.84.0-nightly */
|
||||
/* qemu git hash: 805b14ffc44999952562e8f219d81c21a4fa50b9 */
|
||||
/* automatically generated by rust-bindgen 0.70.1 */
|
||||
|
||||
use libc::siginfo_t;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct __BindgenBitfieldUnit<Storage> {
|
||||
@ -516,15 +518,6 @@ impl ::std::fmt::Debug for sigval {
|
||||
pub type __sigval_t = sigval;
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct siginfo_t {
|
||||
pub si_signo: ::std::os::raw::c_int,
|
||||
pub si_errno: ::std::os::raw::c_int,
|
||||
pub si_code: ::std::os::raw::c_int,
|
||||
pub __pad0: ::std::os::raw::c_int,
|
||||
pub _sifields: siginfo_t__bindgen_ty_1,
|
||||
}
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union siginfo_t__bindgen_ty_1 {
|
||||
pub _pad: [::std::os::raw::c_int; 28usize],
|
||||
pub _kill: siginfo_t__bindgen_ty_1__bindgen_ty_1,
|
||||
@ -827,31 +820,6 @@ impl ::std::fmt::Debug for siginfo_t__bindgen_ty_1 {
|
||||
write!(f, "siginfo_t__bindgen_ty_1 {{ union }}")
|
||||
}
|
||||
}
|
||||
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
|
||||
const _: () = {
|
||||
["Size of siginfo_t"][::std::mem::size_of::<siginfo_t>() - 128usize];
|
||||
["Alignment of siginfo_t"][::std::mem::align_of::<siginfo_t>() - 8usize];
|
||||
["Offset of field: siginfo_t::si_signo"][::std::mem::offset_of!(siginfo_t, si_signo) - 0usize];
|
||||
["Offset of field: siginfo_t::si_errno"][::std::mem::offset_of!(siginfo_t, si_errno) - 4usize];
|
||||
["Offset of field: siginfo_t::si_code"][::std::mem::offset_of!(siginfo_t, si_code) - 8usize];
|
||||
["Offset of field: siginfo_t::__pad0"][::std::mem::offset_of!(siginfo_t, __pad0) - 12usize];
|
||||
["Offset of field: siginfo_t::_sifields"]
|
||||
[::std::mem::offset_of!(siginfo_t, _sifields) - 16usize];
|
||||
};
|
||||
impl Default for siginfo_t {
|
||||
fn default() -> Self {
|
||||
let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
|
||||
unsafe {
|
||||
::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
||||
s.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ::std::fmt::Debug for siginfo_t {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
write ! (f , "siginfo_t {{ si_signo: {:?}, si_errno: {:?}, si_code: {:?}, __pad0: {:?}, _sifields: {:?} }}" , self . si_signo , self . si_errno , self . si_code , self . __pad0 , self . _sifields)
|
||||
}
|
||||
}
|
||||
pub type guint8 = ::std::os::raw::c_uchar;
|
||||
pub type gchar = ::std::os::raw::c_char;
|
||||
pub type guint = ::std::os::raw::c_uint;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* 1.83.0-nightly */
|
||||
/* qemu git hash: d6637939526f453c69f4c6bfe4635feb5dc5c0be */
|
||||
/* 1.84.0-nightly */
|
||||
/* qemu git hash: 805b14ffc44999952562e8f219d81c21a4fa50b9 */
|
||||
/* automatically generated by rust-bindgen 0.70.1 */
|
||||
|
||||
pub const LIBAFL_SYNC_EXIT_OPCODE: u32 = 1727150607;
|
||||
|
@ -35,9 +35,6 @@ use libafl_bolts::{
|
||||
use libafl_qemu_sys::libafl_exit_request_timeout;
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use libafl_qemu_sys::libafl_qemu_handle_crash;
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
use libafl_qemu_sys::siginfo_t;
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
use libc::siginfo_t;
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
@ -60,17 +57,26 @@ where
|
||||
///
|
||||
/// This should be used as a crash handler, and nothing else.
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
unsafe fn inproc_qemu_crash_handler(
|
||||
unsafe fn inproc_qemu_crash_handler<ET, S>(
|
||||
signal: Signal,
|
||||
info: &mut siginfo_t,
|
||||
mut context: Option<&mut ucontext_t>,
|
||||
_data: &mut InProcessExecutorHandlerData,
|
||||
) {
|
||||
) where
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
S: UsesInput + Unpin,
|
||||
{
|
||||
let puc = match &mut context {
|
||||
Some(v) => ptr::from_mut::<ucontext_t>(*v) as *mut c_void,
|
||||
None => ptr::null_mut(),
|
||||
};
|
||||
libafl_qemu_handle_crash(signal as i32, ptr::from_mut::<siginfo_t>(info), puc);
|
||||
|
||||
// run modules' crash callback
|
||||
if let Some(emulator_modules) = EmulatorModules::<ET, S>::emulator_modules_mut() {
|
||||
emulator_modules.modules_mut().on_crash_all();
|
||||
}
|
||||
|
||||
libafl_qemu_handle_crash(signal as i32, info, puc);
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
@ -79,8 +85,7 @@ pub(crate) static BREAK_ON_TMOUT: AtomicBool = AtomicBool::new(false);
|
||||
/// # Safety
|
||||
/// Can call through the `unix_signal_handler::inproc_timeout_handler`.
|
||||
/// Calling this method multiple times concurrently can lead to race conditions.
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
|
||||
pub unsafe fn inproc_qemu_timeout_handler<E, EM, ET, OF, S, Z>(
|
||||
signal: Signal,
|
||||
info: &mut siginfo_t,
|
||||
context: Option<&mut ucontext_t>,
|
||||
@ -88,16 +93,36 @@ pub unsafe fn inproc_qemu_timeout_handler<E, EM, OF, Z>(
|
||||
) where
|
||||
E: HasObservers + HasInProcessHooks<E::State> + Executor<EM, Z>,
|
||||
E::Observers: ObserversTuple<E::Input, E::State>,
|
||||
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
|
||||
OF: Feedback<EM, E::Input, E::Observers, E::State>,
|
||||
E::State: HasExecutions + HasSolutions + HasCorpus,
|
||||
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
OF: Feedback<EM, E::Input, E::Observers, E::State>,
|
||||
S: State + Unpin,
|
||||
Z: HasObjective<Objective = OF, State = E::State>,
|
||||
<<E as UsesState>::State as HasSolutions>::Solutions: Corpus<Input = E::Input>, //delete me
|
||||
<<<E as UsesState>::State as HasCorpus>::Corpus as Corpus>::Input: Clone, //delete me
|
||||
{
|
||||
if BREAK_ON_TMOUT.load(Ordering::Acquire) {
|
||||
libafl_exit_request_timeout();
|
||||
} else {
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
{
|
||||
if BREAK_ON_TMOUT.load(Ordering::Acquire) {
|
||||
libafl_exit_request_timeout();
|
||||
} else {
|
||||
libafl::executors::hooks::unix::unix_signal_handler::inproc_timeout_handler::<
|
||||
E,
|
||||
EM,
|
||||
OF,
|
||||
Z,
|
||||
>(signal, info, context, data);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
{
|
||||
// run modules' crash callback
|
||||
if let Some(emulator_modules) = EmulatorModules::<ET, S>::emulator_modules_mut() {
|
||||
emulator_modules.modules_mut().on_timeout_all();
|
||||
}
|
||||
|
||||
libafl::executors::hooks::unix::unix_signal_handler::inproc_timeout_handler::<E, EM, OF, Z>(
|
||||
signal, info, context, data,
|
||||
);
|
||||
@ -153,7 +178,8 @@ where
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
{
|
||||
inner.inprocess_hooks_mut().crash_handler = inproc_qemu_crash_handler as *const c_void;
|
||||
inner.inprocess_hooks_mut().crash_handler =
|
||||
inproc_qemu_crash_handler::<ET, S> as *const c_void;
|
||||
|
||||
let handler = |emulator_modules: &mut EmulatorModules<ET, S>, host_sig| {
|
||||
eprintln!("Crashed with signal {host_sig}");
|
||||
@ -175,15 +201,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
{
|
||||
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
|
||||
StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>>,
|
||||
EM,
|
||||
OF,
|
||||
Z,
|
||||
> as *const c_void;
|
||||
}
|
||||
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
|
||||
StatefulInProcessExecutor<'a, H, OT, S, Emulator<CM, ED, ET, S, SM>>,
|
||||
EM,
|
||||
ET,
|
||||
OF,
|
||||
S,
|
||||
Z,
|
||||
> as *const c_void;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
|
@ -108,6 +108,7 @@ pub struct DrCovModule<F> {
|
||||
full_trace: bool,
|
||||
drcov_len: usize,
|
||||
}
|
||||
|
||||
impl DrCovModule<NopAddressFilter> {
|
||||
#[must_use]
|
||||
pub fn builder() -> DrCovModuleBuilder<NopAddressFilter> {
|
||||
@ -119,11 +120,7 @@ impl DrCovModule<NopAddressFilter> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> DrCovModule<F>
|
||||
where
|
||||
F: AddressFilter,
|
||||
{
|
||||
impl<F> DrCovModule<F> {
|
||||
#[must_use]
|
||||
#[allow(clippy::let_underscore_untyped)]
|
||||
pub fn new(
|
||||
@ -146,84 +143,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn must_instrument(&self, addr: GuestAddr) -> bool {
|
||||
self.filter.allowed(&addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, S> EmulatorModule<S> for DrCovModule<F>
|
||||
where
|
||||
F: AddressFilter,
|
||||
S: Unpin + UsesInput + HasMetadata,
|
||||
{
|
||||
type ModuleAddressFilter = F;
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
type ModulePageFilter = NopPageFilter;
|
||||
|
||||
fn init_module<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||
where
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
{
|
||||
emulator_modules.blocks(
|
||||
Hook::Function(gen_unique_block_ids::<ET, F, S>),
|
||||
Hook::Function(gen_block_lengths::<ET, F, S>),
|
||||
Hook::Function(exec_trace_block::<ET, F, S>),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
||||
where
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
{
|
||||
if self.module_mapping.is_none() {
|
||||
log::info!("Auto-filling module mapping for DrCov module from QEMU mapping.");
|
||||
|
||||
let qemu = emulator_modules.qemu();
|
||||
|
||||
let mut module_mapping: RangeMap<usize, (u16, String)> = RangeMap::new();
|
||||
|
||||
for (i, (r, p)) in qemu
|
||||
.mappings()
|
||||
.filter_map(|m| {
|
||||
m.path()
|
||||
.map(|p| ((m.start() as usize)..(m.end() as usize), p.to_string()))
|
||||
.filter(|(_, p)| !p.is_empty())
|
||||
})
|
||||
.enumerate()
|
||||
{
|
||||
module_mapping.insert(r, (i as u16, p));
|
||||
}
|
||||
|
||||
self.module_mapping = Some(module_mapping);
|
||||
} else {
|
||||
log::info!("Using user-provided module mapping for DrCov module.");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
fn first_exec<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
||||
where
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
{
|
||||
assert!(
|
||||
self.module_mapping.is_some(),
|
||||
"DrCov should have a module mapping already set."
|
||||
);
|
||||
}
|
||||
|
||||
fn post_exec<OT, ET>(
|
||||
&mut self,
|
||||
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||
_state: &mut S,
|
||||
_input: &S::Input,
|
||||
_observers: &mut OT,
|
||||
_exit_kind: &mut ExitKind,
|
||||
) where
|
||||
OT: ObserversTuple<S::Input, S>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
{
|
||||
pub fn write(&mut self) {
|
||||
let lengths_opt = DRCOV_LENGTHS.lock().unwrap();
|
||||
let lengths = lengths_opt.as_ref().unwrap();
|
||||
if self.full_trace {
|
||||
@ -322,6 +242,100 @@ where
|
||||
self.drcov_len = DRCOV_MAP.lock().unwrap().as_ref().unwrap().len();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> DrCovModule<F>
|
||||
where
|
||||
F: AddressFilter,
|
||||
{
|
||||
#[must_use]
|
||||
pub fn must_instrument(&self, addr: GuestAddr) -> bool {
|
||||
self.filter.allowed(&addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, S> EmulatorModule<S> for DrCovModule<F>
|
||||
where
|
||||
F: AddressFilter,
|
||||
S: Unpin + UsesInput + HasMetadata,
|
||||
{
|
||||
type ModuleAddressFilter = F;
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
type ModulePageFilter = NopPageFilter;
|
||||
|
||||
fn init_module<ET>(&self, emulator_modules: &mut EmulatorModules<ET, S>)
|
||||
where
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
{
|
||||
emulator_modules.blocks(
|
||||
Hook::Function(gen_unique_block_ids::<ET, F, S>),
|
||||
Hook::Function(gen_block_lengths::<ET, F, S>),
|
||||
Hook::Function(exec_trace_block::<ET, F, S>),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
fn first_exec<ET>(&mut self, emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
||||
where
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
{
|
||||
if self.module_mapping.is_none() {
|
||||
log::info!("Auto-filling module mapping for DrCov module from QEMU mapping.");
|
||||
|
||||
let qemu = emulator_modules.qemu();
|
||||
|
||||
let mut module_mapping: RangeMap<usize, (u16, String)> = RangeMap::new();
|
||||
|
||||
for (i, (r, p)) in qemu
|
||||
.mappings()
|
||||
.filter_map(|m| {
|
||||
m.path()
|
||||
.map(|p| ((m.start() as usize)..(m.end() as usize), p.to_string()))
|
||||
.filter(|(_, p)| !p.is_empty())
|
||||
})
|
||||
.enumerate()
|
||||
{
|
||||
module_mapping.insert(r, (i as u16, p));
|
||||
}
|
||||
|
||||
self.module_mapping = Some(module_mapping);
|
||||
} else {
|
||||
log::info!("Using user-provided module mapping for DrCov module.");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
fn first_exec<ET>(&mut self, _emulator_modules: &mut EmulatorModules<ET, S>, _state: &mut S)
|
||||
where
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
{
|
||||
assert!(
|
||||
self.module_mapping.is_some(),
|
||||
"DrCov should have a module mapping already set."
|
||||
);
|
||||
}
|
||||
|
||||
fn post_exec<OT, ET>(
|
||||
&mut self,
|
||||
_emulator_modules: &mut EmulatorModules<ET, S>,
|
||||
_state: &mut S,
|
||||
_input: &S::Input,
|
||||
_observers: &mut OT,
|
||||
_exit_kind: &mut ExitKind,
|
||||
) where
|
||||
OT: ObserversTuple<S::Input, S>,
|
||||
ET: EmulatorModuleTuple<S>,
|
||||
{
|
||||
self.write();
|
||||
}
|
||||
|
||||
unsafe fn on_crash(&mut self) {
|
||||
self.write();
|
||||
}
|
||||
|
||||
unsafe fn on_timeout(&mut self) {
|
||||
self.write();
|
||||
}
|
||||
|
||||
fn address_filter(&self) -> &Self::ModuleAddressFilter {
|
||||
&self.filter
|
||||
|
@ -95,6 +95,16 @@ where
|
||||
{
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This is getting executed in a signal handler.
|
||||
unsafe fn on_crash(&mut self) {}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This is getting executed in a signal handler.
|
||||
unsafe fn on_timeout(&mut self) {}
|
||||
|
||||
fn address_filter(&self) -> &Self::ModuleAddressFilter;
|
||||
fn address_filter_mut(&mut self) -> &mut Self::ModuleAddressFilter;
|
||||
fn update_address_filter(&mut self, qemu: Qemu, filter: Self::ModuleAddressFilter) {
|
||||
@ -149,6 +159,16 @@ where
|
||||
OT: ObserversTuple<S::Input, S>,
|
||||
ET: EmulatorModuleTuple<S>;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This is getting executed in a signal handler.
|
||||
unsafe fn on_crash_all(&mut self);
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// This is getting executed in a signal handler.
|
||||
unsafe fn on_timeout_all(&mut self);
|
||||
|
||||
fn allow_address_range_all(&mut self, address_range: Range<GuestAddr>);
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
@ -196,6 +216,10 @@ where
|
||||
{
|
||||
}
|
||||
|
||||
unsafe fn on_crash_all(&mut self) {}
|
||||
|
||||
unsafe fn on_timeout_all(&mut self) {}
|
||||
|
||||
fn allow_address_range_all(&mut self, _address_range: Range<GuestAddr>) {}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
@ -255,6 +279,16 @@ where
|
||||
.post_exec_all(emulator_modules, state, input, observers, exit_kind);
|
||||
}
|
||||
|
||||
unsafe fn on_crash_all(&mut self) {
|
||||
self.0.on_crash();
|
||||
self.1.on_crash_all();
|
||||
}
|
||||
|
||||
unsafe fn on_timeout_all(&mut self) {
|
||||
self.0.on_timeout();
|
||||
self.1.on_timeout_all();
|
||||
}
|
||||
|
||||
fn allow_address_range_all(&mut self, address_range: Range<GuestAddr>) {
|
||||
self.0.address_filter_mut().register(address_range.clone());
|
||||
self.1.allow_address_range_all(address_range);
|
||||
|
Loading…
x
Reference in New Issue
Block a user