Qemu signal refactoring (#2920)
* qemu signal refactoring * udpate qemu * clippy, moving things around * update bindings * nostd * cfg * fmt * nostd * clippy * fmt * aaa * windowsssssss * systemmode * reimport fix * remove llmp from replay mode * lol * fixer --------- Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
This commit is contained in:
parent
defb475d28
commit
8398f8f99a
@ -48,19 +48,21 @@ mac_alias = "run_unix"
|
|||||||
windows_alias = "unsupported"
|
windows_alias = "unsupported"
|
||||||
|
|
||||||
[tasks.run_unix]
|
[tasks.run_unix]
|
||||||
command = "cargo"
|
script_runner = "@shell"
|
||||||
args = [
|
script = '''
|
||||||
"run",
|
cargo build \
|
||||||
"--profile",
|
--profile \
|
||||||
"${PROFILE}",
|
${PROFILE}
|
||||||
"./${FUZZER_NAME}",
|
|
||||||
"--",
|
${TARGET_DIR}/${PROFILE_DIR}/fuzzbench_qemu \
|
||||||
"--libafl-in",
|
--libafl-in \
|
||||||
"../../inprocess/libfuzzer_libpng/corpus",
|
../../inprocess/libfuzzer_libpng/corpus \
|
||||||
"--libafl-out",
|
--libafl-out \
|
||||||
"./out",
|
./out \
|
||||||
"./${FUZZER_NAME}",
|
./${FUZZER_NAME} \
|
||||||
]
|
-- \
|
||||||
|
./${FUZZER_NAME}
|
||||||
|
'''
|
||||||
dependencies = ["harness"]
|
dependencies = ["harness"]
|
||||||
|
|
||||||
# Run the fuzzer
|
# Run the fuzzer
|
||||||
|
@ -51,6 +51,7 @@ use libafl_qemu::{
|
|||||||
// asan::{init_with_asan, QemuAsanHelper},
|
// asan::{init_with_asan, QemuAsanHelper},
|
||||||
modules::cmplog::{CmpLogModule, CmpLogObserver},
|
modules::cmplog::{CmpLogModule, CmpLogObserver},
|
||||||
modules::edges::StdEdgeCoverageModule,
|
modules::edges::StdEdgeCoverageModule,
|
||||||
|
modules::AsanModule,
|
||||||
Emulator,
|
Emulator,
|
||||||
GuestReg,
|
GuestReg,
|
||||||
//snapshot::QemuSnapshotHelper,
|
//snapshot::QemuSnapshotHelper,
|
||||||
|
@ -375,6 +375,9 @@ echo "Profile: ${PROFILE}"
|
|||||||
export QEMU_LAUNCHER=${TARGET_DIR}/${PROFILE_DIR}/qemu_launcher
|
export QEMU_LAUNCHER=${TARGET_DIR}/${PROFILE_DIR}/qemu_launcher
|
||||||
|
|
||||||
./tests/injection/test.sh || exit 1
|
./tests/injection/test.sh || exit 1
|
||||||
|
|
||||||
|
# complie again with simple mgr
|
||||||
|
cargo build --profile=${PROFILE} --features="simplemgr" --target-dir=${TARGET_DIR}
|
||||||
./tests/qasan/test.sh || exit 1
|
./tests/qasan/test.sh || exit 1
|
||||||
'''
|
'''
|
||||||
dependencies = ["build_unix"]
|
dependencies = ["build_unix"]
|
||||||
|
@ -86,7 +86,6 @@ impl Fuzzer {
|
|||||||
// The shared memory allocator
|
// The shared memory allocator
|
||||||
#[cfg(not(feature = "simplemgr"))]
|
#[cfg(not(feature = "simplemgr"))]
|
||||||
let mut shmem_provider = StdShMemProvider::new()?;
|
let mut shmem_provider = StdShMemProvider::new()?;
|
||||||
|
|
||||||
/* If we are running in verbose, don't provide a replacement stdout, otherwise, use /dev/null */
|
/* If we are running in verbose, don't provide a replacement stdout, otherwise, use /dev/null */
|
||||||
#[cfg(not(feature = "simplemgr"))]
|
#[cfg(not(feature = "simplemgr"))]
|
||||||
let stdout = if self.options.verbose {
|
let stdout = if self.options.verbose {
|
||||||
@ -97,34 +96,15 @@ impl Fuzzer {
|
|||||||
|
|
||||||
let client = Client::new(&self.options);
|
let client = Client::new(&self.options);
|
||||||
|
|
||||||
#[cfg(not(feature = "simplemgr"))]
|
#[cfg(feature = "simplemgr")]
|
||||||
if self.options.rerun_input.is_some() {
|
if self.options.rerun_input.is_some() {
|
||||||
// If we want to rerun a single input but we use a restarting mgr, we'll have to create a fake restarting mgr that doesn't actually restart.
|
// only for simplemgr
|
||||||
// It's not pretty but better than recompiling with simplemgr.
|
// DON'T USE LLMP HERE!!
|
||||||
|
// it doesn't work like that
|
||||||
|
|
||||||
// Just a random number, let's hope it's free :)
|
|
||||||
let broker_port = 13120;
|
|
||||||
let _fake_broker = LlmpBroker::create_attach_to_tcp(
|
|
||||||
shmem_provider.clone(),
|
|
||||||
tuple_list!(),
|
|
||||||
broker_port,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// To rerun an input, instead of using a launcher, we create dummy parameters and run the client directly.
|
|
||||||
return client.run(
|
return client.run(
|
||||||
None,
|
None,
|
||||||
MonitorTypedEventManager::<_, M>::new(
|
SimpleEventManager::new(monitor),
|
||||||
LlmpEventManagerBuilder::builder().build_on_port(
|
|
||||||
shmem_provider.clone(),
|
|
||||||
broker_port,
|
|
||||||
EventConfig::AlwaysUnique,
|
|
||||||
None,
|
|
||||||
Some(StateRestorer::new(
|
|
||||||
shmem_provider.new_shmem(0x1000).unwrap(),
|
|
||||||
)),
|
|
||||||
)?,
|
|
||||||
),
|
|
||||||
ClientDescription::new(0, 0, CoreId(0)),
|
ClientDescription::new(0, 0, CoreId(0)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ tests_expected=(
|
|||||||
"is 0 bytes to the right of the 10-byte chunk"
|
"is 0 bytes to the right of the 10-byte chunk"
|
||||||
"is 1 bytes to the left of the 10-byte chunk"
|
"is 1 bytes to the left of the 10-byte chunk"
|
||||||
"is 0 bytes inside the 10-byte chunk"
|
"is 0 bytes inside the 10-byte chunk"
|
||||||
"is 0 bytes to the right of the 10-byte chunk"
|
"Invalid 11 bytes write at"
|
||||||
"is 0 bytes inside the 10-byte chunk"
|
"is 0 bytes inside the 10-byte chunk"
|
||||||
"Test-Limits - No Error"
|
"Test-Limits - No Error"
|
||||||
)
|
)
|
||||||
|
@ -12,6 +12,8 @@ use core::{
|
|||||||
|
|
||||||
#[cfg(all(target_os = "linux", feature = "std"))]
|
#[cfg(all(target_os = "linux", feature = "std"))]
|
||||||
use libafl_bolts::current_time;
|
use libafl_bolts::current_time;
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
use libafl_bolts::minibsod::{generate_minibsod_to_vec, BsodInfo};
|
||||||
#[cfg(all(unix, feature = "std", not(miri)))]
|
#[cfg(all(unix, feature = "std", not(miri)))]
|
||||||
use libafl_bolts::os::unix_signals::setup_signal_handler;
|
use libafl_bolts::os::unix_signals::setup_signal_handler;
|
||||||
#[cfg(all(windows, feature = "std"))]
|
#[cfg(all(windows, feature = "std"))]
|
||||||
@ -21,8 +23,6 @@ use windows::Win32::System::Threading::{CRITICAL_SECTION, PTP_TIMER};
|
|||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use crate::executors::hooks::timer::TimerStruct;
|
use crate::executors::hooks::timer::TimerStruct;
|
||||||
#[cfg(all(unix, feature = "std"))]
|
|
||||||
use crate::executors::hooks::unix::unix_signal_handler;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::{EventFirer, EventRestarter},
|
events::{EventFirer, EventRestarter},
|
||||||
executors::{hooks::ExecutorHook, inprocess::HasInProcessHooks, Executor, HasObservers},
|
executors::{hooks::ExecutorHook, inprocess::HasInProcessHooks, Executor, HasObservers},
|
||||||
@ -30,6 +30,13 @@ use crate::{
|
|||||||
state::{HasExecutions, HasSolutions},
|
state::{HasExecutions, HasSolutions},
|
||||||
Error, HasObjective,
|
Error, HasObjective,
|
||||||
};
|
};
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
use crate::{
|
||||||
|
executors::{
|
||||||
|
hooks::unix::unix_signal_handler, inprocess::run_observers_and_save_state, ExitKind,
|
||||||
|
},
|
||||||
|
state::HasCorpus,
|
||||||
|
};
|
||||||
#[cfg(any(unix, windows))]
|
#[cfg(any(unix, windows))]
|
||||||
use crate::{inputs::Input, observers::ObserversTuple, state::HasCurrentTestcase};
|
use crate::{inputs::Input, observers::ObserversTuple, state::HasCurrentTestcase};
|
||||||
|
|
||||||
@ -386,52 +393,111 @@ unsafe impl Sync for InProcessExecutorHandlerData {}
|
|||||||
impl InProcessExecutorHandlerData {
|
impl InProcessExecutorHandlerData {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Only safe if not called twice and if the executor is not used from another borrow after this.
|
/// Only safe if not called twice and if the executor is not used from another borrow after this.
|
||||||
#[cfg(any(unix, feature = "std"))]
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
pub(crate) unsafe fn executor_mut<'a, E>(&self) -> &'a mut E {
|
pub(crate) unsafe fn executor_mut<'a, E>(&self) -> &'a mut E {
|
||||||
unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() }
|
unsafe { (self.executor_ptr as *mut E).as_mut().unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Only safe if not called twice and if the state is not used from another borrow after this.
|
/// Only safe if not called twice and if the state is not used from another borrow after this.
|
||||||
#[cfg(any(unix, feature = "std"))]
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
pub(crate) unsafe fn state_mut<'a, S>(&self) -> &'a mut S {
|
pub(crate) unsafe fn state_mut<'a, S>(&self) -> &'a mut S {
|
||||||
unsafe { (self.state_ptr as *mut S).as_mut().unwrap() }
|
unsafe { (self.state_ptr as *mut S).as_mut().unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Only safe if not called twice and if the event manager is not used from another borrow after this.
|
/// Only safe if not called twice and if the event manager is not used from another borrow after this.
|
||||||
#[cfg(any(unix, feature = "std"))]
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
pub(crate) unsafe fn event_mgr_mut<'a, EM>(&self) -> &'a mut EM {
|
pub(crate) unsafe fn event_mgr_mut<'a, EM>(&self) -> &'a mut EM {
|
||||||
unsafe { (self.event_mgr_ptr as *mut EM).as_mut().unwrap() }
|
unsafe { (self.event_mgr_ptr as *mut EM).as_mut().unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Only safe if not called twice and if the fuzzer is not used from another borrow after this.
|
/// Only safe if not called twice and if the fuzzer is not used from another borrow after this.
|
||||||
#[cfg(any(unix, feature = "std"))]
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
pub(crate) unsafe fn fuzzer_mut<'a, Z>(&self) -> &'a mut Z {
|
pub(crate) unsafe fn fuzzer_mut<'a, Z>(&self) -> &'a mut Z {
|
||||||
unsafe { (self.fuzzer_ptr as *mut Z).as_mut().unwrap() }
|
unsafe { (self.fuzzer_ptr as *mut Z).as_mut().unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Only safe if not called concurrently.
|
/// Only safe if not called concurrently.
|
||||||
#[cfg(any(unix, feature = "std"))]
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
pub(crate) unsafe fn take_current_input<'a, I>(&mut self) -> &'a I {
|
pub(crate) unsafe fn take_current_input<'a, I>(&mut self) -> &'a I {
|
||||||
let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() };
|
let r = unsafe { (self.current_input_ptr as *const I).as_ref().unwrap() };
|
||||||
self.current_input_ptr = ptr::null();
|
self.current_input_ptr = ptr::null();
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(unix, feature = "std"))]
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
pub(crate) fn is_valid(&self) -> bool {
|
pub(crate) fn is_valid(&self) -> bool {
|
||||||
!self.current_input_ptr.is_null()
|
!self.current_input_ptr.is_null()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(unix, feature = "std"))]
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
pub(crate) fn set_in_handler(&mut self, v: bool) -> bool {
|
pub(crate) fn set_in_handler(&mut self, v: bool) -> bool {
|
||||||
let old = self.in_handler;
|
let old = self.in_handler;
|
||||||
self.in_handler = v;
|
self.in_handler = v;
|
||||||
old
|
old
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// if data is valid, safely report a crash and return true.
|
||||||
|
/// return false otherwise.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Should only be called to signal a crash in the target
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
pub unsafe fn maybe_report_crash<E, EM, I, OF, S, Z>(
|
||||||
|
&mut self,
|
||||||
|
bsod_info: Option<BsodInfo>,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
E: Executor<EM, I, S, Z> + HasObservers,
|
||||||
|
E::Observers: ObserversTuple<I, S>,
|
||||||
|
EM: EventFirer<I, S> + EventRestarter<S>,
|
||||||
|
OF: Feedback<EM, I, E::Observers, S>,
|
||||||
|
S: HasExecutions + HasSolutions<I> + HasCorpus<I> + HasCurrentTestcase<I>,
|
||||||
|
Z: HasObjective<Objective = OF>,
|
||||||
|
I: Input + Clone,
|
||||||
|
{
|
||||||
|
if self.is_valid() {
|
||||||
|
let executor = self.executor_mut::<E>();
|
||||||
|
// disarms timeout in case of timeout
|
||||||
|
let state = self.state_mut::<S>();
|
||||||
|
let event_mgr = self.event_mgr_mut::<EM>();
|
||||||
|
let fuzzer = self.fuzzer_mut::<Z>();
|
||||||
|
let input = self.take_current_input::<I>();
|
||||||
|
|
||||||
|
log::error!("Target crashed!");
|
||||||
|
|
||||||
|
if let Some(bsod_info) = bsod_info {
|
||||||
|
let bsod = generate_minibsod_to_vec(
|
||||||
|
bsod_info.signal,
|
||||||
|
&bsod_info.siginfo,
|
||||||
|
bsod_info.ucontext.as_ref(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Ok(bsod) = bsod {
|
||||||
|
if let Ok(r) = std::str::from_utf8(&bsod) {
|
||||||
|
log::error!("{}", r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run_observers_and_save_state::<E, EM, I, OF, S, Z>(
|
||||||
|
executor,
|
||||||
|
state,
|
||||||
|
input,
|
||||||
|
fuzzer,
|
||||||
|
event_mgr,
|
||||||
|
ExitKind::Crash,
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exception handling needs some nasty unsafe.
|
/// Exception handling needs some nasty unsafe.
|
||||||
|
@ -14,10 +14,6 @@ use core::{
|
|||||||
|
|
||||||
use libafl_bolts::tuples::{tuple_list, RefIndexable};
|
use libafl_bolts::tuples::{tuple_list, RefIndexable};
|
||||||
|
|
||||||
#[cfg(any(unix, feature = "std"))]
|
|
||||||
use crate::executors::hooks::inprocess::GLOBAL_STATE;
|
|
||||||
#[cfg(any(unix, feature = "std"))]
|
|
||||||
use crate::ExecutionProcessor;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, Testcase},
|
corpus::{Corpus, Testcase},
|
||||||
events::{Event, EventFirer, EventRestarter},
|
events::{Event, EventFirer, EventRestarter},
|
||||||
@ -436,45 +432,6 @@ pub fn run_observers_and_save_state<E, EM, I, OF, S, Z>(
|
|||||||
log::info!("Bye!");
|
log::info!("Bye!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO remove this after executor refactor and libafl qemu new executor
|
|
||||||
/// Expose a version of the crash handler that can be called from e.g. an emulator
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// This will directly access `GLOBAL_STATE` and related data pointers
|
|
||||||
#[cfg(any(unix, feature = "std"))]
|
|
||||||
pub unsafe fn generic_inproc_crash_handler<E, EM, I, OF, S, Z>()
|
|
||||||
where
|
|
||||||
E: Executor<EM, I, S, Z> + HasObservers,
|
|
||||||
E::Observers: ObserversTuple<I, S>,
|
|
||||||
EM: EventFirer<I, S> + EventRestarter<S>,
|
|
||||||
OF: Feedback<EM, I, E::Observers, S>,
|
|
||||||
S: HasExecutions + HasSolutions<I> + HasCurrentTestcase<I>,
|
|
||||||
I: Input + Clone,
|
|
||||||
Z: HasObjective<Objective = OF> + ExecutionProcessor<EM, I, E::Observers, S>,
|
|
||||||
{
|
|
||||||
let data = &raw mut GLOBAL_STATE;
|
|
||||||
let in_handler = (*data).set_in_handler(true);
|
|
||||||
|
|
||||||
if (*data).is_valid() {
|
|
||||||
let executor = (*data).executor_mut::<E>();
|
|
||||||
let state = (*data).state_mut::<S>();
|
|
||||||
let event_mgr = (*data).event_mgr_mut::<EM>();
|
|
||||||
let fuzzer = (*data).fuzzer_mut::<Z>();
|
|
||||||
let input = (*data).take_current_input::<I>();
|
|
||||||
|
|
||||||
run_observers_and_save_state::<E, EM, I, OF, S, Z>(
|
|
||||||
executor,
|
|
||||||
state,
|
|
||||||
input,
|
|
||||||
fuzzer,
|
|
||||||
event_mgr,
|
|
||||||
ExitKind::Crash,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
(*data).set_in_handler(in_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use libafl_bolts::{rands::XkcdRand, tuples::tuple_list};
|
use libafl_bolts::{rands::XkcdRand, tuples::tuple_list};
|
||||||
|
@ -6,6 +6,8 @@ use core::mem::size_of;
|
|||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::vec::Vec;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use libc::siginfo_t;
|
use libc::siginfo_t;
|
||||||
@ -24,6 +26,18 @@ use windows::Win32::System::Diagnostics::Debug::{CONTEXT, EXCEPTION_POINTERS};
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use crate::os::unix_signals::{ucontext_t, Signal};
|
use crate::os::unix_signals::{ucontext_t, Signal};
|
||||||
|
|
||||||
|
/// Necessary info to print a mini-BSOD.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub struct BsodInfo {
|
||||||
|
/// the signal
|
||||||
|
pub signal: Signal,
|
||||||
|
/// siginfo
|
||||||
|
pub siginfo: siginfo_t,
|
||||||
|
/// ucontext
|
||||||
|
pub ucontext: Option<ucontext_t>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Write the content of all important registers
|
/// Write the content of all important registers
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
any(target_os = "linux", target_os = "android"),
|
any(target_os = "linux", target_os = "android"),
|
||||||
@ -1110,6 +1124,24 @@ pub fn generate_minibsod<W: Write>(
|
|||||||
write_minibsod(writer)
|
write_minibsod(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a mini-BSOD given a signal and context and dump it to a [`Vec`]
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub fn generate_minibsod_to_vec(
|
||||||
|
signal: Signal,
|
||||||
|
siginfo: &siginfo_t,
|
||||||
|
ucontext: Option<&ucontext_t>,
|
||||||
|
) -> Result<Vec<u8>, std::io::Error> {
|
||||||
|
let mut bsod = Vec::new();
|
||||||
|
{
|
||||||
|
let mut writer = BufWriter::new(&mut bsod);
|
||||||
|
|
||||||
|
generate_minibsod(&mut writer, signal, siginfo, ucontext)?;
|
||||||
|
|
||||||
|
writer.flush()?;
|
||||||
|
}
|
||||||
|
Ok(bsod)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates a mini-BSOD given an `EXCEPTION_POINTERS` structure.
|
/// Generates a mini-BSOD given an `EXCEPTION_POINTERS` structure.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[expect(clippy::non_ascii_literal, clippy::not_unsafe_ptr_arg_deref)]
|
#[expect(clippy::non_ascii_literal, clippy::not_unsafe_ptr_arg_deref)]
|
||||||
|
@ -11,7 +11,7 @@ use crate::cargo_add_rpath;
|
|||||||
|
|
||||||
pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||||
pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||||
pub const QEMU_REVISION: &str = "7e0dc68430c509ad50c6b0c9887f7e642a4bba2d";
|
pub const QEMU_REVISION: &str = "695657e4f3f408c34b146d5191b102d5eb99b74b";
|
||||||
|
|
||||||
pub struct BuildResult {
|
pub struct BuildResult {
|
||||||
pub qemu_path: PathBuf,
|
pub qemu_path: PathBuf,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* 1.86.0-nightly */
|
/* 1.86.0-nightly */
|
||||||
/* qemu git hash: 3f7b2d86635aaf03818aaec1d285dba88255f831 */
|
/* qemu git hash: 695657e4f3f408c34b146d5191b102d5eb99b74b */
|
||||||
/* automatically generated by rust-bindgen 0.71.1 */
|
/* automatically generated by rust-bindgen 0.71.1 */
|
||||||
|
|
||||||
use libc::siginfo_t;
|
use libc::siginfo_t;
|
||||||
@ -6315,15 +6315,42 @@ impl Default for libafl_mapinfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe extern "C" {
|
#[repr(C)]
|
||||||
pub static mut libafl_dump_core_hook:
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
::std::option::Option<unsafe extern "C" fn(host_sig: ::std::os::raw::c_int)>;
|
pub struct libafl_qemu_sig_ctx {
|
||||||
|
pub in_qemu_sig_hdlr: bool,
|
||||||
|
pub is_target_signal: bool,
|
||||||
}
|
}
|
||||||
|
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
|
||||||
|
const _: () = {
|
||||||
|
["Size of libafl_qemu_sig_ctx"][::std::mem::size_of::<libafl_qemu_sig_ctx>() - 2usize];
|
||||||
|
["Alignment of libafl_qemu_sig_ctx"][::std::mem::align_of::<libafl_qemu_sig_ctx>() - 1usize];
|
||||||
|
["Offset of field: libafl_qemu_sig_ctx::in_qemu_sig_hdlr"]
|
||||||
|
[::std::mem::offset_of!(libafl_qemu_sig_ctx, in_qemu_sig_hdlr) - 0usize];
|
||||||
|
["Offset of field: libafl_qemu_sig_ctx::is_target_signal"]
|
||||||
|
[::std::mem::offset_of!(libafl_qemu_sig_ctx, is_target_signal) - 1usize];
|
||||||
|
};
|
||||||
unsafe extern "C" {
|
unsafe extern "C" {
|
||||||
pub static mut libafl_force_dfl: ::std::os::raw::c_int;
|
pub static mut libafl_force_dfl: ::std::os::raw::c_int;
|
||||||
}
|
}
|
||||||
unsafe extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_dump_core_exec(signal: ::std::os::raw::c_int);
|
pub fn libafl_qemu_native_signal_handler(
|
||||||
|
host_sig: ::std::os::raw::c_int,
|
||||||
|
info: *mut siginfo_t,
|
||||||
|
puc: *mut ::std::os::raw::c_void,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
unsafe extern "C" {
|
||||||
|
pub fn libafl_qemu_signal_context() -> *mut libafl_qemu_sig_ctx;
|
||||||
|
}
|
||||||
|
unsafe extern "C" {
|
||||||
|
pub fn libafl_set_in_target_signal_ctx();
|
||||||
|
}
|
||||||
|
unsafe extern "C" {
|
||||||
|
pub fn libafl_set_in_host_signal_ctx();
|
||||||
|
}
|
||||||
|
unsafe extern "C" {
|
||||||
|
pub fn libafl_unset_in_signal_ctx();
|
||||||
}
|
}
|
||||||
unsafe extern "C" {
|
unsafe extern "C" {
|
||||||
pub fn libafl_qemu_handle_crash(
|
pub fn libafl_qemu_handle_crash(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* 1.86.0-nightly */
|
/* 1.86.0-nightly */
|
||||||
/* qemu git hash: 3f7b2d86635aaf03818aaec1d285dba88255f831 */
|
/* qemu git hash: 695657e4f3f408c34b146d5191b102d5eb99b74b */
|
||||||
/* automatically generated by rust-bindgen 0.71.1 */
|
/* automatically generated by rust-bindgen 0.71.1 */
|
||||||
|
|
||||||
pub const LIBAFL_SYNC_EXIT_OPCODE: u32 = 1727150607;
|
pub const LIBAFL_SYNC_EXIT_OPCODE: u32 = 1727150607;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* 1.86.0-nightly */
|
/* 1.86.0-nightly */
|
||||||
/* qemu git hash: 3f7b2d86635aaf03818aaec1d285dba88255f831 */
|
/* qemu git hash: 695657e4f3f408c34b146d5191b102d5eb99b74b */
|
||||||
/* automatically generated by rust-bindgen 0.71.1 */
|
/* automatically generated by rust-bindgen 0.71.1 */
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -73,7 +73,7 @@ macro_rules! hook_to_repr {
|
|||||||
static mut EMULATOR_MODULES: *mut () = ptr::null_mut();
|
static mut EMULATOR_MODULES: *mut () = ptr::null_mut();
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
pub extern "C" fn crash_hook_wrapper<ET, I, S>(target_sig: i32)
|
pub extern "C" fn run_target_crash_hooks<ET, I, S>(target_sig: i32)
|
||||||
where
|
where
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
S: Unpin,
|
S: Unpin,
|
||||||
@ -974,8 +974,6 @@ where
|
|||||||
pub fn crash_function(&mut self, hook: CrashHookFn<ET, I, S>) {
|
pub fn crash_function(&mut self, hook: CrashHookFn<ET, I, S>) {
|
||||||
// # Safety
|
// # Safety
|
||||||
// Will cast the valid hook to a ptr.
|
// Will cast the valid hook to a ptr.
|
||||||
self.qemu_hooks
|
|
||||||
.set_crash_hook(crash_hook_wrapper::<ET, I, S>);
|
|
||||||
self.hook_collection
|
self.hook_collection
|
||||||
.crash_hooks
|
.crash_hooks
|
||||||
.push(HookRepr::Function(hook as *const libc::c_void));
|
.push(HookRepr::Function(hook as *const libc::c_void));
|
||||||
@ -985,8 +983,6 @@ where
|
|||||||
// # Safety
|
// # Safety
|
||||||
// Will cast the hook to a [`FatPtr`].
|
// Will cast the hook to a [`FatPtr`].
|
||||||
unsafe {
|
unsafe {
|
||||||
self.qemu_hooks
|
|
||||||
.set_crash_hook(crash_hook_wrapper::<ET, I, S>);
|
|
||||||
self.hook_collection
|
self.hook_collection
|
||||||
.crash_hooks
|
.crash_hooks
|
||||||
.push(HookRepr::Closure(transmute(hook)));
|
.push(HookRepr::Closure(transmute(hook)));
|
||||||
|
@ -4,11 +4,13 @@ use core::{
|
|||||||
fmt::{self, Debug, Formatter},
|
fmt::{self, Debug, Formatter},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "usermode")]
|
|
||||||
use std::ptr;
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
use std::{ptr, str};
|
||||||
|
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
use libafl::state::HasCorpus;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
events::{EventFirer, EventRestarter},
|
events::{EventFirer, EventRestarter},
|
||||||
executors::{
|
executors::{
|
||||||
@ -24,6 +26,8 @@ use libafl::{
|
|||||||
state::{HasCurrentTestcase, HasExecutions, HasSolutions},
|
state::{HasCurrentTestcase, HasExecutions, HasSolutions},
|
||||||
Error, ExecutionProcessor, HasScheduler,
|
Error, ExecutionProcessor, HasScheduler,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
use libafl_bolts::minibsod;
|
||||||
#[cfg(feature = "fork")]
|
#[cfg(feature = "fork")]
|
||||||
use libafl_bolts::shmem::ShMemProvider;
|
use libafl_bolts::shmem::ShMemProvider;
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
@ -32,15 +36,11 @@ use libafl_bolts::{
|
|||||||
};
|
};
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
use libafl_qemu_sys::libafl_exit_request_timeout;
|
use libafl_qemu_sys::libafl_exit_request_timeout;
|
||||||
#[cfg(feature = "usermode")]
|
|
||||||
use libafl_qemu_sys::libafl_qemu_handle_crash;
|
|
||||||
use libc::siginfo_t;
|
use libc::siginfo_t;
|
||||||
|
|
||||||
#[cfg(feature = "usermode")]
|
|
||||||
use crate::EmulatorModules;
|
|
||||||
#[cfg(feature = "usermode")]
|
|
||||||
use crate::Qemu;
|
|
||||||
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver};
|
use crate::{command::CommandManager, modules::EmulatorModuleTuple, Emulator, EmulatorDriver};
|
||||||
|
#[cfg(feature = "usermode")]
|
||||||
|
use crate::{run_target_crash_hooks, EmulatorModules, Qemu, QemuSignalContext};
|
||||||
|
|
||||||
type EmulatorInProcessExecutor<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, Z> =
|
type EmulatorInProcessExecutor<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, Z> =
|
||||||
StatefulInProcessExecutor<'a, EM, Emulator<C, CM, ED, ET, I, S, SM>, H, I, OT, S, Z>;
|
StatefulInProcessExecutor<'a, EM, Emulator<C, CM, ED, ET, I, S, SM>, H, I, OT, S, Z>;
|
||||||
@ -50,31 +50,103 @@ pub struct QemuExecutor<'a, C, CM, ED, EM, ET, H, I, OT, S, SM, Z> {
|
|||||||
first_exec: bool,
|
first_exec: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `LibAFL` QEMU main crash handler.
|
||||||
|
/// Be careful, it can come after QEMU's native crash handler if a signal is caught by QEMU but
|
||||||
|
/// not by `LibAFL`.
|
||||||
|
///
|
||||||
|
/// Signals handlers can be nested.
|
||||||
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This should be used as a crash handler, and nothing else.
|
/// This should be used as a crash handler, and nothing else.
|
||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
unsafe fn inproc_qemu_crash_handler<ET, I, S>(
|
unsafe fn inproc_qemu_crash_handler<E, EM, ET, I, OF, S, Z>(
|
||||||
signal: Signal,
|
signal: Signal,
|
||||||
info: &mut siginfo_t,
|
info: &mut siginfo_t,
|
||||||
mut context: Option<&mut ucontext_t>,
|
mut context: Option<&mut ucontext_t>,
|
||||||
_data: &mut InProcessExecutorHandlerData,
|
data: &mut InProcessExecutorHandlerData,
|
||||||
) where
|
) where
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
I: Unpin,
|
E: Executor<EM, I, S, Z> + HasObservers,
|
||||||
S: Unpin,
|
E::Observers: ObserversTuple<I, S>,
|
||||||
|
EM: EventFirer<I, S> + EventRestarter<S>,
|
||||||
|
OF: Feedback<EM, I, E::Observers, S>,
|
||||||
|
S: HasExecutions + HasSolutions<I> + HasCorpus<I> + HasCurrentTestcase<I> + Unpin,
|
||||||
|
Z: HasObjective<Objective = OF>,
|
||||||
|
I: Input + Clone + Unpin,
|
||||||
{
|
{
|
||||||
|
log::debug!("QEMU signal handler has been triggered (signal {signal})");
|
||||||
|
|
||||||
let puc = match &mut context {
|
let puc = match &mut context {
|
||||||
Some(v) => ptr::from_mut::<ucontext_t>(*v) as *mut c_void,
|
Some(v) => ptr::from_mut::<ucontext_t>(*v) as *mut c_void,
|
||||||
None => ptr::null_mut(),
|
None => ptr::null_mut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// run modules' crash callback
|
if let Some(qemu) = Qemu::get() {
|
||||||
if let Some(emulator_modules) = EmulatorModules::<ET, I, S>::emulator_modules_mut() {
|
// QEMU is already initialized, we have to route the signal to QEMU's handler or
|
||||||
emulator_modules.modules_mut().on_crash_all();
|
// consider it as a host (i.e. fuzzer) signal
|
||||||
|
|
||||||
|
if qemu.is_running() {
|
||||||
|
// QEMU is running, we must determine if we are coming from qemu's signal handler or not
|
||||||
|
log::debug!("Signal has been triggered while QEMU was running");
|
||||||
|
|
||||||
|
match qemu.signal_ctx() {
|
||||||
|
QemuSignalContext::OutOfQemuSignalHandler => {
|
||||||
|
// we did not run QEMU's signal handler, run it not
|
||||||
|
log::debug!("It's a simple signal, let QEMU handle it first");
|
||||||
|
|
||||||
|
qemu.run_signal_handler(signal.into(), info, puc);
|
||||||
|
|
||||||
|
// if we are there, we can safely to execution
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QemuSignalContext::InQemuSignalHandlerHost => {
|
||||||
|
// we are running in a nested signal handling
|
||||||
|
// and the signal is a host QEMU signal
|
||||||
|
|
||||||
|
let si_addr = { info.si_addr() as usize };
|
||||||
|
log::error!("QEMU Host crash crashed at addr 0x{si_addr:x}... Bug in QEMU or Emulator modules? Exiting.\n");
|
||||||
|
|
||||||
|
if let Some(cpu) = qemu.current_cpu() {
|
||||||
|
eprint!("QEMU Context:\n{}", cpu.display_context());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QemuSignalContext::InQemuSignalHandlerTarget => {
|
||||||
|
// we are running in a nested signal handler and the signal is a target signal.
|
||||||
|
// run qemu hooks then report the crash.
|
||||||
|
|
||||||
|
log::debug!("QEMU Target signal received that should be handled by host. Most likely a target crash.");
|
||||||
|
|
||||||
|
log::debug!("Running crash hooks.");
|
||||||
|
run_target_crash_hooks::<ET, I, S>(signal.into());
|
||||||
|
|
||||||
|
assert!(data.maybe_report_crash::<E, EM, I, OF, S, Z>(None));
|
||||||
|
|
||||||
|
if let Some(cpu) = qemu.current_cpu() {
|
||||||
|
eprint!("QEMU Context:\n{}", cpu.display_context());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// qemu is not running, it is a bug in LibAFL
|
||||||
|
let si_addr = { info.si_addr() as usize };
|
||||||
|
log::error!("The fuzzer crashed at addr 0x{si_addr:x}... Bug in the fuzzer? Exiting.");
|
||||||
|
|
||||||
|
let bsod = minibsod::generate_minibsod_to_vec(signal, info, context.as_deref());
|
||||||
|
|
||||||
|
if let Ok(bsod) = bsod {
|
||||||
|
if let Ok(bsod_str) = str::from_utf8(&bsod) {
|
||||||
|
log::error!("\n{}", bsod_str);
|
||||||
|
} else {
|
||||||
|
log::error!("convert minibsod to string failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::error!("generate_minibsod failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
libafl_qemu_handle_crash(signal as i32, info, puc);
|
libc::_exit(128 + (signal as i32));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "systemmode")]
|
#[cfg(feature = "systemmode")]
|
||||||
@ -178,34 +250,7 @@ where
|
|||||||
#[cfg(feature = "usermode")]
|
#[cfg(feature = "usermode")]
|
||||||
{
|
{
|
||||||
inner.inprocess_hooks_mut().crash_handler =
|
inner.inprocess_hooks_mut().crash_handler =
|
||||||
inproc_qemu_crash_handler::<ET, I, S> as *const c_void;
|
inproc_qemu_crash_handler::<Self, EM, ET, I, OF, S, Z> as *const c_void;
|
||||||
|
|
||||||
let handler =
|
|
||||||
|qemu: Qemu, _emulator_modules: &mut EmulatorModules<ET, I, S>, host_sig| {
|
|
||||||
eprintln!("Crashed with signal {host_sig}");
|
|
||||||
unsafe {
|
|
||||||
libafl::executors::inprocess::generic_inproc_crash_handler::<
|
|
||||||
Self,
|
|
||||||
EM,
|
|
||||||
I,
|
|
||||||
OF,
|
|
||||||
S,
|
|
||||||
Z,
|
|
||||||
>();
|
|
||||||
}
|
|
||||||
if let Some(cpu) = qemu.current_cpu() {
|
|
||||||
eprint!("Context:\n{}", cpu.display_context());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// # Safety
|
|
||||||
// We assume our crash handlers to be safe/quit after execution.
|
|
||||||
unsafe {
|
|
||||||
inner
|
|
||||||
.exposed_executor_state_mut()
|
|
||||||
.modules_mut()
|
|
||||||
.crash_closure(Box::new(handler));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
|
inner.inprocess_hooks_mut().timeout_handler = inproc_qemu_timeout_handler::<
|
||||||
|
@ -9,12 +9,12 @@ use std::{
|
|||||||
fs,
|
fs,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
process,
|
|
||||||
sync::Mutex,
|
sync::Mutex,
|
||||||
};
|
};
|
||||||
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use libafl::{executors::ExitKind, observers::ObserversTuple};
|
use libafl::{executors::ExitKind, observers::ObserversTuple};
|
||||||
|
use libafl_bolts::os::unix_signals::Signal;
|
||||||
use libafl_qemu_sys::GuestAddr;
|
use libafl_qemu_sys::GuestAddr;
|
||||||
use libc::{
|
use libc::{
|
||||||
c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE,
|
c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE,
|
||||||
@ -73,6 +73,17 @@ pub struct AsanGiovese {
|
|||||||
pub dirty_shadow: Mutex<HashSet<GuestAddr>>,
|
pub dirty_shadow: Mutex<HashSet<GuestAddr>>,
|
||||||
pub saved_shadow: HashMap<GuestAddr, Vec<i8>>,
|
pub saved_shadow: HashMap<GuestAddr, Vec<i8>>,
|
||||||
pub snapshot_shadow: bool,
|
pub snapshot_shadow: bool,
|
||||||
|
pub target_crash: AsanTargetCrash,
|
||||||
|
pub error_found: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AsanModuleBuilder {
|
||||||
|
env: Vec<(String, String)>,
|
||||||
|
detect_leaks: bool,
|
||||||
|
snapshot: bool,
|
||||||
|
filter: StdAddressFilter,
|
||||||
|
error_callback: Option<AsanErrorCallback>,
|
||||||
|
target_crash: AsanTargetCrash,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy)]
|
||||||
@ -132,12 +143,11 @@ pub enum AsanError {
|
|||||||
Signal(i32),
|
Signal(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AsanModuleBuilder {
|
#[derive(Clone, Debug)]
|
||||||
env: Vec<(String, String)>,
|
pub enum AsanTargetCrash {
|
||||||
detect_leaks: bool,
|
Never,
|
||||||
snapshot: bool,
|
OnFirstError,
|
||||||
filter: StdAddressFilter,
|
OnTargetStop,
|
||||||
error_callback: Option<AsanErrorCallback>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -239,6 +249,7 @@ impl AsanModuleBuilder {
|
|||||||
snapshot: bool,
|
snapshot: bool,
|
||||||
filter: StdAddressFilter,
|
filter: StdAddressFilter,
|
||||||
error_callback: Option<AsanErrorCallback>,
|
error_callback: Option<AsanErrorCallback>,
|
||||||
|
target_crash: AsanTargetCrash,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
env,
|
env,
|
||||||
@ -246,6 +257,7 @@ impl AsanModuleBuilder {
|
|||||||
snapshot,
|
snapshot,
|
||||||
filter,
|
filter,
|
||||||
error_callback,
|
error_callback,
|
||||||
|
target_crash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,6 +269,7 @@ impl AsanModuleBuilder {
|
|||||||
self.snapshot,
|
self.snapshot,
|
||||||
self.filter,
|
self.filter,
|
||||||
self.error_callback,
|
self.error_callback,
|
||||||
|
self.target_crash,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,6 +281,7 @@ impl AsanModuleBuilder {
|
|||||||
self.snapshot,
|
self.snapshot,
|
||||||
self.filter,
|
self.filter,
|
||||||
self.error_callback,
|
self.error_callback,
|
||||||
|
self.target_crash,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,6 +293,7 @@ impl AsanModuleBuilder {
|
|||||||
snapshot,
|
snapshot,
|
||||||
self.filter,
|
self.filter,
|
||||||
self.error_callback,
|
self.error_callback,
|
||||||
|
self.target_crash,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,6 +305,7 @@ impl AsanModuleBuilder {
|
|||||||
self.snapshot,
|
self.snapshot,
|
||||||
filter,
|
filter,
|
||||||
self.error_callback,
|
self.error_callback,
|
||||||
|
self.target_crash,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,6 +317,7 @@ impl AsanModuleBuilder {
|
|||||||
self.snapshot,
|
self.snapshot,
|
||||||
self.filter,
|
self.filter,
|
||||||
Some(callback),
|
Some(callback),
|
||||||
|
self.target_crash,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,6 +335,19 @@ impl AsanModuleBuilder {
|
|||||||
self.snapshot,
|
self.snapshot,
|
||||||
self.filter,
|
self.filter,
|
||||||
Some(AsanErrorCallback::report()),
|
Some(AsanErrorCallback::report()),
|
||||||
|
self.target_crash,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn target_crash(self, target_crash: AsanTargetCrash) -> Self {
|
||||||
|
Self::new(
|
||||||
|
self.env,
|
||||||
|
self.detect_leaks,
|
||||||
|
self.snapshot,
|
||||||
|
self.filter,
|
||||||
|
self.error_callback,
|
||||||
|
target_crash,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,6 +359,7 @@ impl AsanModuleBuilder {
|
|||||||
self.snapshot,
|
self.snapshot,
|
||||||
self.filter,
|
self.filter,
|
||||||
self.error_callback,
|
self.error_callback,
|
||||||
|
self.target_crash,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,8 +369,14 @@ impl Default for AsanModuleBuilder {
|
|||||||
let env = env::vars()
|
let env = env::vars()
|
||||||
.filter(|(k, _v)| k != "LD_LIBRARY_PATH")
|
.filter(|(k, _v)| k != "LD_LIBRARY_PATH")
|
||||||
.collect::<Vec<(String, String)>>();
|
.collect::<Vec<(String, String)>>();
|
||||||
|
Self::new(
|
||||||
Self::new(env, false, true, StdAddressFilter::default(), None)
|
env,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
StdAddressFilter::default(),
|
||||||
|
None,
|
||||||
|
AsanTargetCrash::OnFirstError,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,6 +393,7 @@ impl AsanModule {
|
|||||||
snapshot: bool,
|
snapshot: bool,
|
||||||
filter: StdAddressFilter,
|
filter: StdAddressFilter,
|
||||||
error_callback: Option<AsanErrorCallback>,
|
error_callback: Option<AsanErrorCallback>,
|
||||||
|
target_crash: AsanTargetCrash,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut rt = AsanGiovese::new();
|
let mut rt = AsanGiovese::new();
|
||||||
|
|
||||||
@ -364,6 +402,8 @@ impl AsanModule {
|
|||||||
rt.set_error_callback(cb);
|
rt.set_error_callback(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rt.set_target_crash(target_crash);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
env: env.to_vec(),
|
env: env.to_vec(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@ -488,6 +528,8 @@ impl AsanGiovese {
|
|||||||
dirty_shadow: Mutex::new(HashSet::default()),
|
dirty_shadow: Mutex::new(HashSet::default()),
|
||||||
saved_shadow: HashMap::default(),
|
saved_shadow: HashMap::default(),
|
||||||
snapshot_shadow: true, // By default, track the dirty shadow pages
|
snapshot_shadow: true, // By default, track the dirty shadow pages
|
||||||
|
target_crash: AsanTargetCrash::OnFirstError,
|
||||||
|
error_found: false,
|
||||||
};
|
};
|
||||||
Box::pin(res)
|
Box::pin(res)
|
||||||
}
|
}
|
||||||
@ -551,6 +593,10 @@ impl AsanGiovese {
|
|||||||
self.snapshot_shadow = snapshot_shadow;
|
self.snapshot_shadow = snapshot_shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_target_crash(&mut self, target_crash: AsanTargetCrash) {
|
||||||
|
self.target_crash = target_crash;
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_invalid_access<const N: usize>(qemu: Qemu, addr: GuestAddr) -> bool {
|
pub fn is_invalid_access<const N: usize>(qemu: Qemu, addr: GuestAddr) -> bool {
|
||||||
@ -708,8 +754,12 @@ impl AsanGiovese {
|
|||||||
if let Some(mut cb) = self.error_callback.take() {
|
if let Some(mut cb) = self.error_callback.take() {
|
||||||
cb.call(self, qemu, pc, error);
|
cb.call(self, qemu, pc, error);
|
||||||
self.error_callback = Some(cb);
|
self.error_callback = Some(cb);
|
||||||
} else {
|
}
|
||||||
process::abort();
|
|
||||||
|
if let AsanTargetCrash::OnFirstError = self.target_crash {
|
||||||
|
unsafe {
|
||||||
|
qemu.target_signal(Signal::SigSegmentationFault);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1037,6 +1087,8 @@ where
|
|||||||
) where
|
) where
|
||||||
ET: EmulatorModuleTuple<I, S>,
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
{
|
{
|
||||||
|
self.rt.error_found = false;
|
||||||
|
|
||||||
if self.empty {
|
if self.empty {
|
||||||
self.rt.snapshot(qemu);
|
self.rt.snapshot(qemu);
|
||||||
self.empty = false;
|
self.empty = false;
|
||||||
@ -1055,6 +1107,12 @@ where
|
|||||||
ET: EmulatorModuleTuple<I, S>,
|
ET: EmulatorModuleTuple<I, S>,
|
||||||
OT: ObserversTuple<I, S>,
|
OT: ObserversTuple<I, S>,
|
||||||
{
|
{
|
||||||
|
if let AsanTargetCrash::OnTargetStop = self.rt.target_crash {
|
||||||
|
unsafe {
|
||||||
|
qemu.target_signal(Signal::SigSegmentationFault);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.reset(qemu) == AsanRollback::HasLeaks {
|
if self.reset(qemu) == AsanRollback::HasLeaks {
|
||||||
*exit_kind = ExitKind::Crash;
|
*exit_kind = ExitKind::Crash;
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
use core::{ffi::c_void, fmt::Debug, mem::transmute, ptr};
|
use core::{ffi::c_void, fmt::Debug, mem::transmute, ptr};
|
||||||
|
|
||||||
use libafl::executors::hooks::inprocess::inprocess_get_state;
|
use libafl::executors::hooks::inprocess::inprocess_get_state;
|
||||||
#[cfg(feature = "usermode")]
|
|
||||||
use libafl_qemu_sys::libafl_dump_core_hook;
|
|
||||||
use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsize};
|
use libafl_qemu_sys::{CPUArchStatePtr, CPUStatePtr, FatPtr, GuestAddr, GuestUsize};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::{pyclass, pymethods, FromPyObject};
|
use pyo3::{pyclass, pymethods, FromPyObject};
|
||||||
@ -1242,13 +1240,6 @@ impl QemuHooks {
|
|||||||
PostSyscallHookId(num)
|
PostSyscallHookId(num)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::unused_self)]
|
|
||||||
pub(crate) fn set_crash_hook(self, callback: extern "C" fn(i32)) {
|
|
||||||
unsafe {
|
|
||||||
libafl_dump_core_hook = Some(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
|
@ -54,6 +54,7 @@ pub use hooks::*;
|
|||||||
use libafl_bolts::{vec_init, AsSliceMut};
|
use libafl_bolts::{vec_init, AsSliceMut};
|
||||||
|
|
||||||
static mut QEMU_IS_INITIALIZED: bool = false;
|
static mut QEMU_IS_INITIALIZED: bool = false;
|
||||||
|
static mut QEMU_IS_RUNNING: bool = false;
|
||||||
|
|
||||||
pub(super) static QEMU_CONFIG: OnceLock<QemuConfig> = OnceLock::new();
|
pub(super) static QEMU_CONFIG: OnceLock<QemuConfig> = OnceLock::new();
|
||||||
|
|
||||||
@ -631,7 +632,9 @@ impl Qemu {
|
|||||||
/// Should, in general, be safe to call.
|
/// Should, in general, be safe to call.
|
||||||
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
/// Of course, the emulated target is not contained securely and can corrupt state or interact with the operating system.
|
||||||
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitError> {
|
pub unsafe fn run(&self) -> Result<QemuExitReason, QemuExitError> {
|
||||||
|
QEMU_IS_RUNNING = true;
|
||||||
self.run_inner();
|
self.run_inner();
|
||||||
|
QEMU_IS_RUNNING = false;
|
||||||
|
|
||||||
let exit_reason = unsafe { libafl_get_exit_reason() };
|
let exit_reason = unsafe { libafl_get_exit_reason() };
|
||||||
if exit_reason.is_null() {
|
if exit_reason.is_null() {
|
||||||
@ -902,6 +905,11 @@ impl Qemu {
|
|||||||
pub fn host_page_size(&self) -> usize {
|
pub fn host_page_size(&self) -> usize {
|
||||||
unsafe { libafl_qemu_sys::libafl_qemu_host_page_size() }
|
unsafe { libafl_qemu_sys::libafl_qemu_host_page_size() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_running(&self) -> bool {
|
||||||
|
unsafe { QEMU_IS_RUNNING }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArchExtras for Qemu {
|
impl ArchExtras for Qemu {
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
use std::{
|
use std::{
|
||||||
mem::MaybeUninit, ops::Range, ptr::copy_nonoverlapping, slice::from_raw_parts_mut,
|
ffi::c_void, mem::MaybeUninit, ops::Range, ptr::copy_nonoverlapping, slice::from_raw_parts_mut,
|
||||||
str::from_utf8_unchecked_mut,
|
str::from_utf8_unchecked_mut,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use libafl_bolts::os::unix_signals::Signal;
|
||||||
use libafl_qemu_sys::{
|
use libafl_qemu_sys::{
|
||||||
exec_path, free_self_maps, guest_base, libafl_force_dfl, libafl_get_brk,
|
exec_path, free_self_maps, guest_base, libafl_force_dfl, libafl_get_brk,
|
||||||
libafl_get_initial_brk, libafl_load_addr, libafl_maps_first, libafl_maps_next, libafl_qemu_run,
|
libafl_get_initial_brk, libafl_load_addr, libafl_maps_first, libafl_maps_next, libafl_qemu_run,
|
||||||
libafl_set_brk, mmap_next_start, pageflags_get_root, read_self_maps, GuestAddr, GuestUsize,
|
libafl_set_brk, mmap_next_start, pageflags_get_root, read_self_maps, GuestAddr, GuestUsize,
|
||||||
IntervalTreeNode, IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess,
|
IntervalTreeNode, IntervalTreeRoot, MapInfo, MmapPerms, VerifyAccess,
|
||||||
};
|
};
|
||||||
use libc::{c_int, c_uchar, strlen};
|
use libc::{c_int, c_uchar, siginfo_t, strlen};
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
use pyo3::{pyclass, pymethods, IntoPyObject, Py, PyRef, PyRefMut, Python};
|
use pyo3::{pyclass, pymethods, IntoPyObject, Py, PyRef, PyRefMut, Python};
|
||||||
|
|
||||||
use crate::{Qemu, CPU};
|
use crate::{qemu::QEMU_IS_RUNNING, Qemu, CPU};
|
||||||
|
|
||||||
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
#[cfg_attr(feature = "python", pyclass(unsendable))]
|
||||||
pub struct GuestMaps {
|
pub struct GuestMaps {
|
||||||
@ -33,6 +34,15 @@ pub struct ImageInfo {
|
|||||||
pub exec_stack: bool,
|
pub exec_stack: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum QemuSignalContext {
|
||||||
|
/// We are not in QEMU's signal handler, no signal is being propagated.
|
||||||
|
OutOfQemuSignalHandler,
|
||||||
|
/// We are propagating a host signal from QEMU signal handler.
|
||||||
|
InQemuSignalHandlerHost,
|
||||||
|
/// We are propagating a target signal from QEMU signal handler
|
||||||
|
InQemuSignalHandlerTarget,
|
||||||
|
}
|
||||||
|
|
||||||
// Consider a private new only for Emulator
|
// Consider a private new only for Emulator
|
||||||
impl GuestMaps {
|
impl GuestMaps {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -296,6 +306,54 @@ impl Qemu {
|
|||||||
Err(format!("Failed to unmap {addr}"))
|
Err(format!("Failed to unmap {addr}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn signal_ctx(&self) -> QemuSignalContext {
|
||||||
|
unsafe {
|
||||||
|
let qemu_signal_ctx = *libafl_qemu_sys::libafl_qemu_signal_context();
|
||||||
|
|
||||||
|
if qemu_signal_ctx.in_qemu_sig_hdlr {
|
||||||
|
if qemu_signal_ctx.is_target_signal {
|
||||||
|
QemuSignalContext::InQemuSignalHandlerTarget
|
||||||
|
} else {
|
||||||
|
QemuSignalContext::InQemuSignalHandlerHost
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QemuSignalContext::OutOfQemuSignalHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs QEMU signal's handler
|
||||||
|
/// If it is already running, returns true.
|
||||||
|
/// In that case, it would most likely mean we are in a signal loop.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Run QEMU's native signal handler.
|
||||||
|
///
|
||||||
|
/// Needlessly to say, it should be used very carefully.
|
||||||
|
/// It will run QEMU's signal handler, and maybe propagate new signals.
|
||||||
|
pub(crate) unsafe fn run_signal_handler(
|
||||||
|
&self,
|
||||||
|
host_sig: c_int,
|
||||||
|
info: *mut siginfo_t,
|
||||||
|
puc: *mut c_void,
|
||||||
|
) {
|
||||||
|
libafl_qemu_sys::libafl_qemu_native_signal_handler(host_sig, info, puc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emulate a signal coming from the target
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This may raise a signal to host. Some signals could have a funky behaviour.
|
||||||
|
/// SIGSEGV is safe to use.
|
||||||
|
pub unsafe fn target_signal(&self, signal: Signal) {
|
||||||
|
QEMU_IS_RUNNING = true;
|
||||||
|
libafl_qemu_sys::libafl_set_in_target_signal_ctx();
|
||||||
|
libc::raise(signal.into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user