From b320a8dbab543a9da1b0fa53109e0ec048f90e2e Mon Sep 17 00:00:00 2001 From: Romain Malmain Date: Mon, 27 Jan 2025 15:01:20 +0100 Subject: [PATCH] Add PC to QEMU's read / write callbacks + logger module (#2896) * Logger module (only read/write for now) * add pc to rw callbacks * regen bindings --- .../binary_only/qemu_coverage/Makefile.toml | 23 +- .../binary_only/qemu_coverage/src/fuzzer.rs | 2 +- libafl_qemu/libafl_qemu_build/src/build.rs | 2 +- .../src/bindings/x86_64_stub_bindings.rs | 67 ++-- .../runtime/libafl_qemu_stub_bindings.rs | 4 +- libafl_qemu/runtime/nyx_stub_bindings.rs | 81 ++-- libafl_qemu/src/emu/hooks.rs | 58 ++- libafl_qemu/src/modules/logger.rs | 345 ++++++++++++++++++ libafl_qemu/src/modules/mod.rs | 3 + libafl_qemu/src/modules/usermode/asan.rs | 6 + .../src/modules/usermode/asan_guest.rs | 2 + libafl_qemu/src/modules/usermode/snapshot.rs | 2 + libafl_qemu/src/qemu/hooks.rs | 130 +++++-- 13 files changed, 586 insertions(+), 139 deletions(-) create mode 100644 libafl_qemu/src/modules/logger.rs diff --git a/fuzzers/binary_only/qemu_coverage/Makefile.toml b/fuzzers/binary_only/qemu_coverage/Makefile.toml index 362cc94645..c7756a8b6d 100644 --- a/fuzzers/binary_only/qemu_coverage/Makefile.toml +++ b/fuzzers/binary_only/qemu_coverage/Makefile.toml @@ -241,15 +241,16 @@ mac_alias = "unsupported" windows_alias = "unsupported" [tasks.run_unix] -command = "${TARGET_DIR}/${PROFILE_DIR}/qemu_coverage-${CARGO_MAKE_PROFILE}" -args = [ - "--coverage-path", - "${TARGET_DIR}/cov.drcov", - "--input-dir", - "./corpus", - "--", - "${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE}", -] +script_runner = "@shell" +script = ''' +${TARGET_DIR}/${PROFILE_DIR}/qemu_coverage-${CARGO_MAKE_PROFILE} \ + --coverage-path \ + ${TARGET_DIR}/cov.drcov \ + --input-dir \ + ./corpus \ + -- \ + ${TARGET_DIR}/libpng-harness-${CARGO_MAKE_PROFILE} +''' dependencies = ["harness", "fuzzer"] [tasks.test] @@ -297,11 +298,9 @@ cargo run --manifest-path ../../../utils/drcov_utils/Cargo.toml --bin drcov_merg -i ${TARGET_DIR}/cov-000.drcov ${TARGET_DIR}/cov-001.drcov ${TARGET_DIR}/cov-002.drcov ${TARGET_DIR}/cov-003.drcov \ --output ${TARGET_DIR}/cov-merged.drcov || exit 1 -TMP=$(cargo run --manifest-path ../../../utils/drcov_utils/Cargo.toml --bin drcov_dump_addrs -- \ +NB_BLOCKS=$(cargo run --manifest-path ../../../utils/drcov_utils/Cargo.toml --bin drcov_dump_addrs -- \ -i ${TARGET_DIR}/cov-merged.drcov -a | wc -l || exit 1) -NB_BLOCKS=$((TMP - 1)) - echo "Nb blocks found: $NB_BLOCKS" if [ $NB_BLOCKS -ge 1700 ]; then diff --git a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs index 3307ef7222..8744dc48b4 100644 --- a/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs +++ b/fuzzers/binary_only/qemu_coverage/src/fuzzer.rs @@ -136,7 +136,7 @@ pub fn fuzz() { let emulator_modules = tuple_list!( DrCovModule::builder().filename(cov_path.clone()).build(), - SnapshotModule::new() + SnapshotModule::new(), ); let emulator = Emulator::empty() diff --git a/libafl_qemu/libafl_qemu_build/src/build.rs b/libafl_qemu/libafl_qemu_build/src/build.rs index c7233fefb4..b990affcdf 100644 --- a/libafl_qemu/libafl_qemu_build/src/build.rs +++ b/libafl_qemu/libafl_qemu_build/src/build.rs @@ -11,7 +11,7 @@ use crate::cargo_add_rpath; pub const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge"; pub const QEMU_DIRNAME: &str = "qemu-libafl-bridge"; -pub const QEMU_REVISION: &str = "2b5e4bfcff875571b2813a9494de8b2e4c56120e"; +pub const QEMU_REVISION: &str = "7e0dc68430c509ad50c6b0c9887f7e642a4bba2d"; pub struct BuildResult { pub qemu_path: PathBuf, diff --git a/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs b/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs index b9664a601c..60e453775e 100644 --- a/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs +++ b/libafl_qemu/libafl_qemu_sys/src/bindings/x86_64_stub_bindings.rs @@ -1,5 +1,5 @@ -/* 1.85.0-nightly */ -/* qemu git hash: 81e52dc60f83c3ae191c1a07b39bb255e633c234 */ +/* 1.86.0-nightly */ +/* qemu git hash: 3f7b2d86635aaf03818aaec1d285dba88255f831 */ /* automatically generated by rust-bindgen 0.71.1 */ use libc::siginfo_t; @@ -8025,6 +8025,8 @@ unsafe extern "C" { unsafe extern "C" { pub fn libafl_qemu_hook_edge_run(); } +pub type libafl_instruction_cb = + ::std::option::Option; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct libafl_instruction_hook { @@ -8063,7 +8065,7 @@ impl Default for libafl_instruction_hook { unsafe extern "C" { pub fn libafl_qemu_add_instruction_hooks( pc: target_ulong, - callback: ::std::option::Option, + callback: libafl_instruction_cb, data: u64, invalidate: ::std::os::raw::c_int, ) -> usize; @@ -8086,12 +8088,19 @@ unsafe extern "C" { unsafe extern "C" { pub fn libafl_qemu_hook_instruction_run(pc_next: vaddr); } +pub type libafl_rw_gen_cb = ::std::option::Option< + unsafe extern "C" fn(data: u64, pc: target_ulong, addr: *mut TCGTemp, oi: MemOpIdx) -> u64, +>; +pub type libafl_rw_exec_cb = ::std::option::Option< + unsafe extern "C" fn(data: u64, id: u64, pc: target_ulong, addr: target_ulong), +>; +pub type libafl_rw_execN_cb = ::std::option::Option< + unsafe extern "C" fn(data: u64, id: u64, pc: target_ulong, addr: target_ulong, size: usize), +>; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct libafl_rw_hook { - pub gen: ::std::option::Option< - unsafe extern "C" fn(data: u64, pc: target_ulong, addr: *mut TCGTemp, oi: MemOpIdx) -> u64, - >, + pub gen: libafl_rw_gen_cb, pub data: u64, pub num: usize, pub helper_info1: TCGHelperInfo, @@ -8132,48 +8141,30 @@ impl Default for libafl_rw_hook { } } unsafe extern "C" { - pub fn libafl_gen_read(addr: *mut TCGTemp, oi: MemOpIdx); + pub fn libafl_gen_read(pc: *mut TCGTemp, addr: *mut TCGTemp, oi: MemOpIdx); } unsafe extern "C" { - pub fn libafl_gen_write(addr: *mut TCGTemp, oi: MemOpIdx); + pub fn libafl_gen_write(pc: *mut TCGTemp, addr: *mut TCGTemp, oi: MemOpIdx); } unsafe extern "C" { pub fn libafl_add_read_hook( - gen: ::std::option::Option< - unsafe extern "C" fn( - data: u64, - pc: target_ulong, - addr: *mut TCGTemp, - oi: MemOpIdx, - ) -> u64, - >, - exec1: ::std::option::Option, - exec2: ::std::option::Option, - exec4: ::std::option::Option, - exec8: ::std::option::Option, - execN: ::std::option::Option< - unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong, size: usize), - >, + gen: libafl_rw_gen_cb, + exec1: libafl_rw_exec_cb, + exec2: libafl_rw_exec_cb, + exec4: libafl_rw_exec_cb, + exec8: libafl_rw_exec_cb, + execN: libafl_rw_execN_cb, data: u64, ) -> usize; } unsafe extern "C" { pub fn libafl_add_write_hook( - gen: ::std::option::Option< - unsafe extern "C" fn( - data: u64, - pc: target_ulong, - addr: *mut TCGTemp, - oi: MemOpIdx, - ) -> u64, - >, - exec1: ::std::option::Option, - exec2: ::std::option::Option, - exec4: ::std::option::Option, - exec8: ::std::option::Option, - execN: ::std::option::Option< - unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong, size: usize), - >, + gen: libafl_rw_gen_cb, + exec1: libafl_rw_exec_cb, + exec2: libafl_rw_exec_cb, + exec4: libafl_rw_exec_cb, + exec8: libafl_rw_exec_cb, + execN: libafl_rw_execN_cb, data: u64, ) -> usize; } diff --git a/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs b/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs index 7752e9c2bc..350f4cb434 100644 --- a/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs +++ b/libafl_qemu/runtime/libafl_qemu_stub_bindings.rs @@ -1,5 +1,5 @@ -/* 1.85.0-nightly */ -/* qemu git hash: 81e52dc60f83c3ae191c1a07b39bb255e633c234 */ +/* 1.86.0-nightly */ +/* qemu git hash: 3f7b2d86635aaf03818aaec1d285dba88255f831 */ /* automatically generated by rust-bindgen 0.71.1 */ pub const LIBAFL_SYNC_EXIT_OPCODE: u32 = 1727150607; diff --git a/libafl_qemu/runtime/nyx_stub_bindings.rs b/libafl_qemu/runtime/nyx_stub_bindings.rs index 34700e8f41..63ad8771ea 100644 --- a/libafl_qemu/runtime/nyx_stub_bindings.rs +++ b/libafl_qemu/runtime/nyx_stub_bindings.rs @@ -1,5 +1,5 @@ -/* 1.85.0-nightly */ -/* qemu git hash: 81e52dc60f83c3ae191c1a07b39bb255e633c234 */ +/* 1.86.0-nightly */ +/* qemu git hash: 3f7b2d86635aaf03818aaec1d285dba88255f831 */ /* automatically generated by rust-bindgen 0.71.1 */ #[repr(C)] @@ -1078,33 +1078,35 @@ pub type intmax_t = __intmax_t; pub type uintmax_t = __uintmax_t; #[repr(C)] #[derive(Debug, Default)] -pub struct kAFL_payload { +pub struct _bindgen_ty_1 { pub size: i32, pub data: __IncompleteArrayField, } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of kAFL_payload"][::std::mem::size_of::() - 4usize]; - ["Alignment of kAFL_payload"][::std::mem::align_of::() - 4usize]; - ["Offset of field: kAFL_payload::size"][::std::mem::offset_of!(kAFL_payload, size) - 0usize]; - ["Offset of field: kAFL_payload::data"][::std::mem::offset_of!(kAFL_payload, data) - 4usize]; + ["Size of _bindgen_ty_1"][::std::mem::size_of::<_bindgen_ty_1>() - 4usize]; + ["Alignment of _bindgen_ty_1"][::std::mem::align_of::<_bindgen_ty_1>() - 4usize]; + ["Offset of field: _bindgen_ty_1::size"][::std::mem::offset_of!(_bindgen_ty_1, size) - 0usize]; + ["Offset of field: _bindgen_ty_1::data"][::std::mem::offset_of!(_bindgen_ty_1, data) - 4usize]; }; +pub type kAFL_payload = _bindgen_ty_1; #[repr(C)] #[derive(Debug, Default, Copy, Clone)] -pub struct kAFL_ranges { +pub struct _bindgen_ty_2 { pub ip: [u64; 4usize], pub size: [u64; 4usize], pub enabled: [u8; 4usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of kAFL_ranges"][::std::mem::size_of::() - 72usize]; - ["Alignment of kAFL_ranges"][::std::mem::align_of::() - 8usize]; - ["Offset of field: kAFL_ranges::ip"][::std::mem::offset_of!(kAFL_ranges, ip) - 0usize]; - ["Offset of field: kAFL_ranges::size"][::std::mem::offset_of!(kAFL_ranges, size) - 32usize]; - ["Offset of field: kAFL_ranges::enabled"] - [::std::mem::offset_of!(kAFL_ranges, enabled) - 64usize]; + ["Size of _bindgen_ty_2"][::std::mem::size_of::<_bindgen_ty_2>() - 72usize]; + ["Alignment of _bindgen_ty_2"][::std::mem::align_of::<_bindgen_ty_2>() - 8usize]; + ["Offset of field: _bindgen_ty_2::ip"][::std::mem::offset_of!(_bindgen_ty_2, ip) - 0usize]; + ["Offset of field: _bindgen_ty_2::size"][::std::mem::offset_of!(_bindgen_ty_2, size) - 32usize]; + ["Offset of field: _bindgen_ty_2::enabled"] + [::std::mem::offset_of!(_bindgen_ty_2, enabled) - 64usize]; }; +pub type kAFL_ranges = _bindgen_ty_2; #[repr(C, packed)] #[derive(Debug, Default, Copy, Clone)] pub struct host_config_t { @@ -1134,7 +1136,7 @@ const _: () = { }; #[repr(C, packed)] #[derive(Debug, Default, Copy, Clone)] -pub struct agent_config_t { +pub struct _bindgen_ty_3 { pub agent_magic: u32, pub agent_version: u32, pub agent_timeout_detection: u8, @@ -1149,31 +1151,32 @@ pub struct agent_config_t { } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { - ["Size of agent_config_t"][::std::mem::size_of::() - 37usize]; - ["Alignment of agent_config_t"][::std::mem::align_of::() - 1usize]; - ["Offset of field: agent_config_t::agent_magic"] - [::std::mem::offset_of!(agent_config_t, agent_magic) - 0usize]; - ["Offset of field: agent_config_t::agent_version"] - [::std::mem::offset_of!(agent_config_t, agent_version) - 4usize]; - ["Offset of field: agent_config_t::agent_timeout_detection"] - [::std::mem::offset_of!(agent_config_t, agent_timeout_detection) - 8usize]; - ["Offset of field: agent_config_t::agent_tracing"] - [::std::mem::offset_of!(agent_config_t, agent_tracing) - 9usize]; - ["Offset of field: agent_config_t::agent_ijon_tracing"] - [::std::mem::offset_of!(agent_config_t, agent_ijon_tracing) - 10usize]; - ["Offset of field: agent_config_t::agent_non_reload_mode"] - [::std::mem::offset_of!(agent_config_t, agent_non_reload_mode) - 11usize]; - ["Offset of field: agent_config_t::trace_buffer_vaddr"] - [::std::mem::offset_of!(agent_config_t, trace_buffer_vaddr) - 12usize]; - ["Offset of field: agent_config_t::ijon_trace_buffer_vaddr"] - [::std::mem::offset_of!(agent_config_t, ijon_trace_buffer_vaddr) - 20usize]; - ["Offset of field: agent_config_t::coverage_bitmap_size"] - [::std::mem::offset_of!(agent_config_t, coverage_bitmap_size) - 28usize]; - ["Offset of field: agent_config_t::input_buffer_size"] - [::std::mem::offset_of!(agent_config_t, input_buffer_size) - 32usize]; - ["Offset of field: agent_config_t::dump_payloads"] - [::std::mem::offset_of!(agent_config_t, dump_payloads) - 36usize]; + ["Size of _bindgen_ty_3"][::std::mem::size_of::<_bindgen_ty_3>() - 37usize]; + ["Alignment of _bindgen_ty_3"][::std::mem::align_of::<_bindgen_ty_3>() - 1usize]; + ["Offset of field: _bindgen_ty_3::agent_magic"] + [::std::mem::offset_of!(_bindgen_ty_3, agent_magic) - 0usize]; + ["Offset of field: _bindgen_ty_3::agent_version"] + [::std::mem::offset_of!(_bindgen_ty_3, agent_version) - 4usize]; + ["Offset of field: _bindgen_ty_3::agent_timeout_detection"] + [::std::mem::offset_of!(_bindgen_ty_3, agent_timeout_detection) - 8usize]; + ["Offset of field: _bindgen_ty_3::agent_tracing"] + [::std::mem::offset_of!(_bindgen_ty_3, agent_tracing) - 9usize]; + ["Offset of field: _bindgen_ty_3::agent_ijon_tracing"] + [::std::mem::offset_of!(_bindgen_ty_3, agent_ijon_tracing) - 10usize]; + ["Offset of field: _bindgen_ty_3::agent_non_reload_mode"] + [::std::mem::offset_of!(_bindgen_ty_3, agent_non_reload_mode) - 11usize]; + ["Offset of field: _bindgen_ty_3::trace_buffer_vaddr"] + [::std::mem::offset_of!(_bindgen_ty_3, trace_buffer_vaddr) - 12usize]; + ["Offset of field: _bindgen_ty_3::ijon_trace_buffer_vaddr"] + [::std::mem::offset_of!(_bindgen_ty_3, ijon_trace_buffer_vaddr) - 20usize]; + ["Offset of field: _bindgen_ty_3::coverage_bitmap_size"] + [::std::mem::offset_of!(_bindgen_ty_3, coverage_bitmap_size) - 28usize]; + ["Offset of field: _bindgen_ty_3::input_buffer_size"] + [::std::mem::offset_of!(_bindgen_ty_3, input_buffer_size) - 32usize]; + ["Offset of field: _bindgen_ty_3::dump_payloads"] + [::std::mem::offset_of!(_bindgen_ty_3, dump_payloads) - 36usize]; }; +pub type agent_config_t = _bindgen_ty_3; #[repr(C, packed)] #[derive(Debug, Default, Copy, Clone)] pub struct kafl_dump_file_t { diff --git a/libafl_qemu/src/emu/hooks.rs b/libafl_qemu/src/emu/hooks.rs index f561d78463..040ecf61b7 100644 --- a/libafl_qemu/src/emu/hooks.rs +++ b/libafl_qemu/src/emu/hooks.rs @@ -455,22 +455,42 @@ where let exec1 = get_raw_hook!( execution_hook_1, read_0_exec_hook_wrapper::, - unsafe extern "C" fn(&mut TcgHookState<5, ReadHookId>, id: u64, addr: GuestAddr) + unsafe extern "C" fn( + &mut TcgHookState<5, ReadHookId>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ) ); let exec2 = get_raw_hook!( execution_hook_2, read_1_exec_hook_wrapper::, - unsafe extern "C" fn(&mut TcgHookState<5, ReadHookId>, id: u64, addr: GuestAddr) + unsafe extern "C" fn( + &mut TcgHookState<5, ReadHookId>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ) ); let exec4 = get_raw_hook!( execution_hook_4, read_2_exec_hook_wrapper::, - unsafe extern "C" fn(&mut TcgHookState<5, ReadHookId>, id: u64, addr: GuestAddr) + unsafe extern "C" fn( + &mut TcgHookState<5, ReadHookId>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ) ); let exec8 = get_raw_hook!( execution_hook_8, read_3_exec_hook_wrapper::, - unsafe extern "C" fn(&mut TcgHookState<5, ReadHookId>, id: u64, addr: GuestAddr) + unsafe extern "C" fn( + &mut TcgHookState<5, ReadHookId>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ) ); let execn = get_raw_hook!( execution_hook_n, @@ -478,6 +498,7 @@ where unsafe extern "C" fn( &mut TcgHookState<5, ReadHookId>, id: u64, + pc: GuestAddr, addr: GuestAddr, size: usize, ) @@ -547,22 +568,42 @@ where let exec1 = get_raw_hook!( execution_hook_1, write_0_exec_hook_wrapper::, - unsafe extern "C" fn(&mut TcgHookState<5, WriteHookId>, id: u64, addr: GuestAddr) + unsafe extern "C" fn( + &mut TcgHookState<5, WriteHookId>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ) ); let exec2 = get_raw_hook!( execution_hook_2, write_1_exec_hook_wrapper::, - unsafe extern "C" fn(&mut TcgHookState<5, WriteHookId>, id: u64, addr: GuestAddr) + unsafe extern "C" fn( + &mut TcgHookState<5, WriteHookId>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ) ); let exec4 = get_raw_hook!( execution_hook_4, write_2_exec_hook_wrapper::, - unsafe extern "C" fn(&mut TcgHookState<5, WriteHookId>, id: u64, addr: GuestAddr) + unsafe extern "C" fn( + &mut TcgHookState<5, WriteHookId>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ) ); let exec8 = get_raw_hook!( execution_hook_8, write_3_exec_hook_wrapper::, - unsafe extern "C" fn(&mut TcgHookState<5, WriteHookId>, id: u64, addr: GuestAddr) + unsafe extern "C" fn( + &mut TcgHookState<5, WriteHookId>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ) ); let execn = get_raw_hook!( execution_hook_n, @@ -570,6 +611,7 @@ where unsafe extern "C" fn( &mut TcgHookState<5, WriteHookId>, id: u64, + pc: GuestAddr, addr: GuestAddr, size: usize, ) diff --git a/libafl_qemu/src/modules/logger.rs b/libafl_qemu/src/modules/logger.rs new file mode 100644 index 0000000000..195dfd98c2 --- /dev/null +++ b/libafl_qemu/src/modules/logger.rs @@ -0,0 +1,345 @@ +//! Logger Module +//! +//! It's a simple module logging selected events to the logger with the `info` level. +//! It must be built through [`LoggerModuleBuilder`]. + +use std::fmt::Debug; + +use libafl_qemu_sys::TCGTemp; + +use crate::{ + modules::{ + utils::filters::{AddressFilter, NopAddressFilter, NopPageFilter}, + EmulatorModule, EmulatorModuleTuple, + }, + qemu::Qemu, + EmulatorModules, GuestAddr, Hook, MemAccessInfo, +}; + +/// A builder for [`LoggerModule`]. +/// +/// By default, no event is logged. +#[expect(clippy::struct_excessive_bools)] +pub struct LoggerModuleBuilder { + calls: bool, + cmps: bool, + reads: bool, + writes: bool, + threads: bool, + syscalls: bool, + custom_insns: bool, + edges: bool, + blocks: bool, + instruction: Option>, + + addr_filter: AF, + page_filter: PF, +} + +impl Default for LoggerModuleBuilder { + fn default() -> Self { + Self { + calls: false, + cmps: false, + reads: false, + writes: false, + threads: false, + syscalls: false, + custom_insns: false, + edges: false, + blocks: false, + instruction: None, + addr_filter: NopAddressFilter, + page_filter: NopPageFilter, + } + } +} + +impl LoggerModuleBuilder { + #[must_use] + pub fn calls(mut self, calls: bool) -> Self { + self.calls = calls; + + self + } + + #[must_use] + pub fn cmps(mut self, cmps: bool) -> Self { + self.cmps = cmps; + + self + } + + #[must_use] + pub fn reads(mut self, reads: bool) -> Self { + self.reads = reads; + + self + } + + #[must_use] + pub fn writes(mut self, writes: bool) -> Self { + self.writes = writes; + + self + } + + #[must_use] + pub fn threads(mut self, threads: bool) -> Self { + self.threads = threads; + + self + } + + #[must_use] + pub fn syscalls(mut self, syscalls: bool) -> Self { + self.syscalls = syscalls; + + self + } + + #[must_use] + pub fn custom_insns(mut self, custom_insns: bool) -> Self { + self.custom_insns = custom_insns; + + self + } + + #[must_use] + pub fn edges(mut self, edges: bool) -> Self { + self.edges = edges; + + self + } + + #[must_use] + pub fn blocks(mut self, blocks: bool) -> Self { + self.blocks = blocks; + + self + } + + #[must_use] + pub fn instruction(mut self, instruction: GuestAddr) -> Self { + let instructions = if let Some(insns) = &mut self.instruction { + insns + } else { + self.instruction = Some(Vec::new()); + + self.instruction.as_mut().unwrap() + }; + + instructions.push(instruction); + + self + } + + #[must_use] + pub fn addr_filter(self, addr_filter: AF2) -> LoggerModuleBuilder { + LoggerModuleBuilder { + calls: self.calls, + cmps: self.cmps, + reads: self.reads, + writes: self.writes, + threads: self.threads, + syscalls: self.syscalls, + custom_insns: self.custom_insns, + edges: self.edges, + blocks: self.blocks, + instruction: self.instruction, + addr_filter, + page_filter: self.page_filter, + } + } + + #[must_use] + pub fn page_filter(self, page_filter: PF2) -> LoggerModuleBuilder { + LoggerModuleBuilder { + calls: self.calls, + cmps: self.cmps, + reads: self.reads, + writes: self.writes, + threads: self.threads, + syscalls: self.syscalls, + custom_insns: self.custom_insns, + edges: self.edges, + blocks: self.blocks, + instruction: self.instruction, + addr_filter: self.addr_filter, + page_filter, + } + } + + #[must_use] + pub fn build(self) -> LoggerModule { + LoggerModule { + calls: self.calls, + cmps: self.cmps, + reads: self.reads, + writes: self.writes, + threads: self.threads, + syscalls: self.syscalls, + custom_insns: self.custom_insns, + edges: self.edges, + blocks: self.blocks, + instruction: self.instruction, + addr_filter: self.addr_filter, + page_filter: self.page_filter, + } + } +} + +/// Module used to log events in QEMU. +/// It basically logs whatever goes through QEMU's hooks. +/// It can be configured through [`LoggerModuleBuilder`]. +#[derive(Debug)] +#[expect(dead_code)] +#[expect(clippy::struct_excessive_bools)] +pub struct LoggerModule { + calls: bool, + cmps: bool, + reads: bool, + writes: bool, + threads: bool, + syscalls: bool, + custom_insns: bool, + edges: bool, + blocks: bool, + instruction: Option>, + + addr_filter: AF, + page_filter: PF, +} + +impl LoggerModule { + #[must_use] + pub fn builder() -> LoggerModuleBuilder { + LoggerModuleBuilder::default() + } +} + +#[expect(clippy::unnecessary_wraps)] +fn gen_logger_rw( + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: Option<&mut S>, + pc: GuestAddr, + _addr: *mut TCGTemp, + info: MemAccessInfo, +) -> Option +where + ET: EmulatorModuleTuple, + I: Unpin, + S: Unpin, +{ + let kind = if IS_WRITE { "write" } else { "read" }; + + let size = info.size(); + log::info!("[PC {pc:#x}] gen {kind} of {size} bytes"); + + Some(0) +} + +fn exec_logger_rw( + qemu: Qemu, + emulator_modules: &mut EmulatorModules, + state: Option<&mut S>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, +) where + ET: EmulatorModuleTuple, + I: Unpin, + S: Unpin, +{ + exec_logger_rw_n::(qemu, emulator_modules, state, id, pc, addr, N); +} + +fn exec_logger_rw_n( + _qemu: Qemu, + _emulator_modules: &mut EmulatorModules, + _state: Option<&mut S>, + _id: u64, + pc: GuestAddr, + addr: GuestAddr, + size: usize, +) where + ET: EmulatorModuleTuple, + I: Unpin, + S: Unpin, +{ + let kind = if IS_WRITE { "write" } else { "read" }; + + log::info!("[PC {pc:#x}] exec {kind} of {size} bytes @addr {addr:#x}"); +} + +impl EmulatorModule for LoggerModule +where + AF: AddressFilter, + PF: Debug + 'static, + I: Unpin, + S: Unpin, +{ + fn first_exec( + &mut self, + _qemu: Qemu, + emulator_modules: &mut EmulatorModules, + _state: &mut S, + ) where + ET: EmulatorModuleTuple, + { + if self.reads { + emulator_modules.reads( + Hook::Function(gen_logger_rw::), + Hook::Function(exec_logger_rw::), + Hook::Function(exec_logger_rw::), + Hook::Function(exec_logger_rw::), + Hook::Function(exec_logger_rw::), + Hook::Function(exec_logger_rw_n::), + ); + } + + if self.writes { + emulator_modules.writes( + Hook::Function(gen_logger_rw::), + Hook::Function(exec_logger_rw::), + Hook::Function(exec_logger_rw::), + Hook::Function(exec_logger_rw::), + Hook::Function(exec_logger_rw::), + Hook::Function(exec_logger_rw_n::), + ); + } + + if self.calls { + todo!() + } + + if self.blocks { + todo!() + } + + if self.cmps { + todo!() + } + + if self.custom_insns { + todo!() + } + + if self.edges { + todo!() + } + + if self.syscalls { + todo!() + } + + if self.threads { + todo!() + } + + if self.instruction.is_some() { + todo!() + } + } +} diff --git a/libafl_qemu/src/modules/mod.rs b/libafl_qemu/src/modules/mod.rs index 71e403a727..667a0223fa 100644 --- a/libafl_qemu/src/modules/mod.rs +++ b/libafl_qemu/src/modules/mod.rs @@ -44,6 +44,9 @@ pub mod drcov; #[cfg(not(cpu_target = "hexagon"))] pub use drcov::{DrCovMetadata, DrCovModule, DrCovModuleBuilder}; +pub mod logger; +pub use logger::LoggerModule; + pub mod utils; /// [`EmulatorModule`] is a trait designed to define modules that interact with the QEMU emulator diff --git a/libafl_qemu/src/modules/usermode/asan.rs b/libafl_qemu/src/modules/usermode/asan.rs index 6cb45f094c..23b2e83adf 100644 --- a/libafl_qemu/src/modules/usermode/asan.rs +++ b/libafl_qemu/src/modules/usermode/asan.rs @@ -1008,6 +1008,7 @@ pub fn trace_read_asan( emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, + _pc: GuestAddr, addr: GuestAddr, ) where ET: EmulatorModuleTuple, @@ -1023,6 +1024,7 @@ pub fn trace_read_n_asan( emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, + _pc: GuestAddr, addr: GuestAddr, size: usize, ) where @@ -1039,6 +1041,7 @@ pub fn trace_write_asan( emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, + _pc: GuestAddr, addr: GuestAddr, ) where ET: EmulatorModuleTuple, @@ -1054,6 +1057,7 @@ pub fn trace_write_n_asan( emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, + _pc: GuestAddr, addr: GuestAddr, size: usize, ) where @@ -1091,6 +1095,7 @@ pub fn trace_write_asan_snapshot( emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, + _pc: GuestAddr, addr: GuestAddr, ) where ET: EmulatorModuleTuple, @@ -1110,6 +1115,7 @@ pub fn trace_write_n_asan_snapshot( emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, id: u64, + _pc: GuestAddr, addr: GuestAddr, size: usize, ) where diff --git a/libafl_qemu/src/modules/usermode/asan_guest.rs b/libafl_qemu/src/modules/usermode/asan_guest.rs index f2df21a3a8..f1dfcfa5c5 100644 --- a/libafl_qemu/src/modules/usermode/asan_guest.rs +++ b/libafl_qemu/src/modules/usermode/asan_guest.rs @@ -160,6 +160,7 @@ fn guest_trace_error_asan( _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, + _pc: GuestAddr, _addr: GuestAddr, ) where ET: EmulatorModuleTuple, @@ -175,6 +176,7 @@ fn guest_trace_error_n_asan( _emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, + _pc: GuestAddr, _addr: GuestAddr, _n: usize, ) where diff --git a/libafl_qemu/src/modules/usermode/snapshot.rs b/libafl_qemu/src/modules/usermode/snapshot.rs index a0e020145a..8a403b1272 100644 --- a/libafl_qemu/src/modules/usermode/snapshot.rs +++ b/libafl_qemu/src/modules/usermode/snapshot.rs @@ -753,6 +753,7 @@ pub fn trace_write_snapshot( emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, + _pc: GuestAddr, addr: GuestAddr, ) where ET: EmulatorModuleTuple, @@ -768,6 +769,7 @@ pub fn trace_write_n_snapshot( emulator_modules: &mut EmulatorModules, _state: Option<&mut S>, _id: u64, + _pc: GuestAddr, addr: GuestAddr, size: usize, ) where diff --git a/libafl_qemu/src/qemu/hooks.rs b/libafl_qemu/src/qemu/hooks.rs index f55b8caf04..fcd8dab375 100644 --- a/libafl_qemu/src/qemu/hooks.rs +++ b/libafl_qemu/src/qemu/hooks.rs @@ -731,7 +731,14 @@ create_hook_types!( ); create_hook_types!( ReadExec, - fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), + fn( + Qemu, + &mut EmulatorModules, + Option<&mut S>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ), Box< dyn for<'a> FnMut( Qemu, @@ -739,13 +746,22 @@ create_hook_types!( Option<&'a mut S>, u64, GuestAddr, + GuestAddr, ), >, - unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr) + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, pc: GuestAddr, addr: GuestAddr) ); create_hook_types!( ReadExecN, - fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + fn( + Qemu, + &mut EmulatorModules, + Option<&mut S>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + size: usize, + ), Box< dyn for<'a> FnMut( Qemu, @@ -753,20 +769,27 @@ create_hook_types!( Option<&'a mut S>, u64, GuestAddr, + GuestAddr, usize, ), >, - unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr, size: usize) + unsafe extern "C" fn( + libafl_qemu_opaque: *const (), + id: u64, + pc: GuestAddr, + addr: GuestAddr, + size: usize, + ) ); create_hook_id!(Read, libafl_qemu_remove_read_hook, true); create_gen_wrapper!(read, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, ReadHookId); -create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 0, 5, ReadHookId); -create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 1, 5, ReadHookId); -create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 2, 5, ReadHookId); -create_exec_wrapper!(read, (id: u64, addr: GuestAddr), 3, 5, ReadHookId); +create_exec_wrapper!(read, (id: u64, pc: GuestAddr, addr: GuestAddr), 0, 5, ReadHookId); +create_exec_wrapper!(read, (id: u64, pc: GuestAddr, addr: GuestAddr), 1, 5, ReadHookId); +create_exec_wrapper!(read, (id: u64, pc: GuestAddr, addr: GuestAddr), 2, 5, ReadHookId); +create_exec_wrapper!(read, (id: u64, pc: GuestAddr, addr: GuestAddr), 3, 5, ReadHookId); create_exec_wrapper!( read, - (id: u64, addr: GuestAddr, size: usize), + (id: u64, addr: GuestAddr, pc: GuestAddr, size: usize), 4, 5, ReadHookId @@ -802,7 +825,14 @@ create_hook_types!( ); create_hook_types!( WriteExec, - fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr), + fn( + Qemu, + &mut EmulatorModules, + Option<&mut S>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + ), Box< dyn for<'a> FnMut( Qemu, @@ -810,13 +840,22 @@ create_hook_types!( Option<&'a mut S>, u64, GuestAddr, + GuestAddr, ), >, - unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr) + unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, pc: GuestAddr, addr: GuestAddr) ); create_hook_types!( WriteExecN, - fn(Qemu, &mut EmulatorModules, Option<&mut S>, id: u64, addr: GuestAddr, size: usize), + fn( + Qemu, + &mut EmulatorModules, + Option<&mut S>, + id: u64, + pc: GuestAddr, + addr: GuestAddr, + size: usize, + ), Box< dyn for<'a> FnMut( Qemu, @@ -824,20 +863,27 @@ create_hook_types!( Option<&'a mut S>, u64, GuestAddr, + GuestAddr, usize, ), >, - unsafe extern "C" fn(libafl_qemu_opaque: *const (), id: u64, addr: GuestAddr, size: usize) + unsafe extern "C" fn( + libafl_qemu_opaque: *const (), + id: u64, + pc: GuestAddr, + addr: GuestAddr, + size: usize, + ) ); create_hook_id!(Write, libafl_qemu_remove_write_hook, true); create_gen_wrapper!(write, (pc: GuestAddr, addr: *mut TCGTemp, info: MemAccessInfo), u64, 5, WriteHookId); -create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 0, 5, WriteHookId); -create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 1, 5, WriteHookId); -create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 2, 5, WriteHookId); -create_exec_wrapper!(write, (id: u64, addr: GuestAddr), 3, 5, WriteHookId); +create_exec_wrapper!(write, (id: u64, pc: GuestAddr, addr: GuestAddr), 0, 5, WriteHookId); +create_exec_wrapper!(write, (id: u64, pc: GuestAddr, addr: GuestAddr), 1, 5, WriteHookId); +create_exec_wrapper!(write, (id: u64, pc: GuestAddr, addr: GuestAddr), 2, 5, WriteHookId); +create_exec_wrapper!(write, (id: u64, pc: GuestAddr, addr: GuestAddr), 3, 5, WriteHookId); create_exec_wrapper!( write, - (id: u64, addr: GuestAddr, size: usize), + (id: u64, pc: GuestAddr, addr: GuestAddr, size: usize), 4, 5, WriteHookId @@ -1004,11 +1050,11 @@ impl QemuHooks { &self, data: T, gen: Option u64>, - exec1: Option, - exec2: Option, - exec4: Option, - exec8: Option, - exec_n: Option, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + exec_n: Option, ) -> ReadHookId { unsafe { let data: u64 = data.into().0; @@ -1020,11 +1066,15 @@ impl QemuHooks { libafl_qemu_sys::MemOpIdx, ) -> u64, > = transmute(gen); - let exec1: Option = transmute(exec1); - let exec2: Option = transmute(exec2); - let exec4: Option = transmute(exec4); - let exec8: Option = transmute(exec8); - let exec_n: Option = + let exec1: Option = + transmute(exec1); + let exec2: Option = + transmute(exec2); + let exec4: Option = + transmute(exec4); + let exec8: Option = + transmute(exec8); + let exec_n: Option = transmute(exec_n); let num = libafl_qemu_sys::libafl_add_read_hook( gen, exec1, exec2, exec4, exec8, exec_n, data, @@ -1038,11 +1088,11 @@ impl QemuHooks { &self, data: T, gen: Option u64>, - exec1: Option, - exec2: Option, - exec4: Option, - exec8: Option, - exec_n: Option, + exec1: Option, + exec2: Option, + exec4: Option, + exec8: Option, + exec_n: Option, ) -> WriteHookId { unsafe { let data: u64 = data.into().0; @@ -1054,11 +1104,15 @@ impl QemuHooks { libafl_qemu_sys::MemOpIdx, ) -> u64, > = transmute(gen); - let exec1: Option = transmute(exec1); - let exec2: Option = transmute(exec2); - let exec4: Option = transmute(exec4); - let exec8: Option = transmute(exec8); - let exec_n: Option = + let exec1: Option = + transmute(exec1); + let exec2: Option = + transmute(exec2); + let exec4: Option = + transmute(exec4); + let exec8: Option = + transmute(exec8); + let exec_n: Option = transmute(exec_n); let num = libafl_qemu_sys::libafl_add_write_hook( gen, exec1, exec2, exec4, exec8, exec_n, data,