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
This commit is contained in:
Romain Malmain 2025-01-27 15:01:20 +01:00 committed by GitHub
parent 133a0ffe7a
commit b320a8dbab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 586 additions and 139 deletions

View File

@ -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

View File

@ -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()

View File

@ -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,

View File

@ -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<unsafe extern "C" fn(data: u64, pc: target_ulong)>;
#[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<unsafe extern "C" fn(data: u64, pc: target_ulong)>,
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<unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong)>,
exec2: ::std::option::Option<unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong)>,
exec4: ::std::option::Option<unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong)>,
exec8: ::std::option::Option<unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong)>,
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<unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong)>,
exec2: ::std::option::Option<unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong)>,
exec4: ::std::option::Option<unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong)>,
exec8: ::std::option::Option<unsafe extern "C" fn(data: u64, id: u64, addr: target_ulong)>,
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;
}

View File

@ -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;

View File

@ -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<u8>,
}
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
const _: () = {
["Size of kAFL_payload"][::std::mem::size_of::<kAFL_payload>() - 4usize];
["Alignment of kAFL_payload"][::std::mem::align_of::<kAFL_payload>() - 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::<kAFL_ranges>() - 72usize];
["Alignment of kAFL_ranges"][::std::mem::align_of::<kAFL_ranges>() - 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::<agent_config_t>() - 37usize];
["Alignment of agent_config_t"][::std::mem::align_of::<agent_config_t>() - 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 {

View File

@ -455,22 +455,42 @@ where
let exec1 = get_raw_hook!(
execution_hook_1,
read_0_exec_hook_wrapper::<ET, I, S>,
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::<ET, I, S>,
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::<ET, I, S>,
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::<ET, I, S>,
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::<ET, I, S>,
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::<ET, I, S>,
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::<ET, I, S>,
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::<ET, I, S>,
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,
)

View File

@ -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<AF, PF> {
calls: bool,
cmps: bool,
reads: bool,
writes: bool,
threads: bool,
syscalls: bool,
custom_insns: bool,
edges: bool,
blocks: bool,
instruction: Option<Vec<GuestAddr>>,
addr_filter: AF,
page_filter: PF,
}
impl Default for LoggerModuleBuilder<NopAddressFilter, NopPageFilter> {
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<AF, PF> LoggerModuleBuilder<AF, PF> {
#[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<AF2>(self, addr_filter: AF2) -> LoggerModuleBuilder<AF2, PF> {
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<PF2>(self, page_filter: PF2) -> LoggerModuleBuilder<AF, PF2> {
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<AF, PF> {
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<AF, PF> {
calls: bool,
cmps: bool,
reads: bool,
writes: bool,
threads: bool,
syscalls: bool,
custom_insns: bool,
edges: bool,
blocks: bool,
instruction: Option<Vec<GuestAddr>>,
addr_filter: AF,
page_filter: PF,
}
impl LoggerModule<NopAddressFilter, NopPageFilter> {
#[must_use]
pub fn builder() -> LoggerModuleBuilder<NopAddressFilter, NopPageFilter> {
LoggerModuleBuilder::default()
}
}
#[expect(clippy::unnecessary_wraps)]
fn gen_logger_rw<ET, I, S, const IS_WRITE: bool>(
_qemu: Qemu,
_emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
pc: GuestAddr,
_addr: *mut TCGTemp,
info: MemAccessInfo,
) -> Option<u64>
where
ET: EmulatorModuleTuple<I, S>,
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<ET, I, S, const IS_WRITE: bool, const N: usize>(
qemu: Qemu,
emulator_modules: &mut EmulatorModules<ET, I, S>,
state: Option<&mut S>,
id: u64,
pc: GuestAddr,
addr: GuestAddr,
) where
ET: EmulatorModuleTuple<I, S>,
I: Unpin,
S: Unpin,
{
exec_logger_rw_n::<ET, I, S, IS_WRITE>(qemu, emulator_modules, state, id, pc, addr, N);
}
fn exec_logger_rw_n<ET, I, S, const IS_WRITE: bool>(
_qemu: Qemu,
_emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
_id: u64,
pc: GuestAddr,
addr: GuestAddr,
size: usize,
) where
ET: EmulatorModuleTuple<I, S>,
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<AF, I, PF, S> EmulatorModule<I, S> for LoggerModule<AF, PF>
where
AF: AddressFilter,
PF: Debug + 'static,
I: Unpin,
S: Unpin,
{
fn first_exec<ET>(
&mut self,
_qemu: Qemu,
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: &mut S,
) where
ET: EmulatorModuleTuple<I, S>,
{
if self.reads {
emulator_modules.reads(
Hook::Function(gen_logger_rw::<ET, I, S, false>),
Hook::Function(exec_logger_rw::<ET, I, S, false, 1>),
Hook::Function(exec_logger_rw::<ET, I, S, false, 2>),
Hook::Function(exec_logger_rw::<ET, I, S, false, 4>),
Hook::Function(exec_logger_rw::<ET, I, S, false, 8>),
Hook::Function(exec_logger_rw_n::<ET, I, S, false>),
);
}
if self.writes {
emulator_modules.writes(
Hook::Function(gen_logger_rw::<ET, I, S, true>),
Hook::Function(exec_logger_rw::<ET, I, S, true, 1>),
Hook::Function(exec_logger_rw::<ET, I, S, true, 2>),
Hook::Function(exec_logger_rw::<ET, I, S, true, 4>),
Hook::Function(exec_logger_rw::<ET, I, S, true, 8>),
Hook::Function(exec_logger_rw_n::<ET, I, S, true>),
);
}
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!()
}
}
}

View File

@ -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

View File

@ -1008,6 +1008,7 @@ pub fn trace_read_asan<ET, I, S, const N: usize>(
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
id: u64,
_pc: GuestAddr,
addr: GuestAddr,
) where
ET: EmulatorModuleTuple<I, S>,
@ -1023,6 +1024,7 @@ pub fn trace_read_n_asan<ET, I, S>(
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
id: u64,
_pc: GuestAddr,
addr: GuestAddr,
size: usize,
) where
@ -1039,6 +1041,7 @@ pub fn trace_write_asan<ET, I, S, const N: usize>(
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
id: u64,
_pc: GuestAddr,
addr: GuestAddr,
) where
ET: EmulatorModuleTuple<I, S>,
@ -1054,6 +1057,7 @@ pub fn trace_write_n_asan<ET, I, S>(
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
id: u64,
_pc: GuestAddr,
addr: GuestAddr,
size: usize,
) where
@ -1091,6 +1095,7 @@ pub fn trace_write_asan_snapshot<ET, I, S, const N: usize>(
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
id: u64,
_pc: GuestAddr,
addr: GuestAddr,
) where
ET: EmulatorModuleTuple<I, S>,
@ -1110,6 +1115,7 @@ pub fn trace_write_n_asan_snapshot<ET, I, S>(
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
id: u64,
_pc: GuestAddr,
addr: GuestAddr,
size: usize,
) where

View File

@ -160,6 +160,7 @@ fn guest_trace_error_asan<ET, I, S>(
_emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
_id: u64,
_pc: GuestAddr,
_addr: GuestAddr,
) where
ET: EmulatorModuleTuple<I, S>,
@ -175,6 +176,7 @@ fn guest_trace_error_n_asan<ET, I, S>(
_emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
_id: u64,
_pc: GuestAddr,
_addr: GuestAddr,
_n: usize,
) where

View File

@ -753,6 +753,7 @@ pub fn trace_write_snapshot<ET, I, S, const SIZE: usize>(
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
_id: u64,
_pc: GuestAddr,
addr: GuestAddr,
) where
ET: EmulatorModuleTuple<I, S>,
@ -768,6 +769,7 @@ pub fn trace_write_n_snapshot<ET, I, S>(
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: Option<&mut S>,
_id: u64,
_pc: GuestAddr,
addr: GuestAddr,
size: usize,
) where

View File

@ -731,7 +731,14 @@ create_hook_types!(
);
create_hook_types!(
ReadExec,
fn(Qemu, &mut EmulatorModules<ET, I, S>, Option<&mut S>, id: u64, addr: GuestAddr),
fn(
Qemu,
&mut EmulatorModules<ET, I, S>,
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<ET, I, S>, Option<&mut S>, id: u64, addr: GuestAddr, size: usize),
fn(
Qemu,
&mut EmulatorModules<ET, I, S>,
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<ET, I, S>, Option<&mut S>, id: u64, addr: GuestAddr),
fn(
Qemu,
&mut EmulatorModules<ET, I, S>,
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<ET, I, S>, Option<&mut S>, id: u64, addr: GuestAddr, size: usize),
fn(
Qemu,
&mut EmulatorModules<ET, I, S>,
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<unsafe extern "C" fn(T, GuestAddr, *mut TCGTemp, MemAccessInfo) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, usize)>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr, usize)>,
) -> ReadHookId {
unsafe {
let data: u64 = data.into().0;
@ -1020,11 +1066,15 @@ impl QemuHooks {
libafl_qemu_sys::MemOpIdx,
) -> u64,
> = transmute(gen);
let exec1: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec8);
let exec_n: Option<unsafe extern "C" fn(u64, u64, GuestAddr, usize)> =
let exec1: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr)> =
transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr)> =
transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr)> =
transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr)> =
transmute(exec8);
let exec_n: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr, usize)> =
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<unsafe extern "C" fn(T, GuestAddr, *mut TCGTemp, MemAccessInfo) -> u64>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, usize)>,
exec1: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr)>,
exec2: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr)>,
exec4: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr)>,
exec8: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr)>,
exec_n: Option<unsafe extern "C" fn(T, u64, GuestAddr, GuestAddr, usize)>,
) -> WriteHookId {
unsafe {
let data: u64 = data.into().0;
@ -1054,11 +1104,15 @@ impl QemuHooks {
libafl_qemu_sys::MemOpIdx,
) -> u64,
> = transmute(gen);
let exec1: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, GuestAddr)> = transmute(exec8);
let exec_n: Option<unsafe extern "C" fn(u64, u64, GuestAddr, usize)> =
let exec1: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr)> =
transmute(exec1);
let exec2: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr)> =
transmute(exec2);
let exec4: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr)> =
transmute(exec4);
let exec8: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr)> =
transmute(exec8);
let exec_n: Option<unsafe extern "C" fn(u64, u64, GuestAddr, GuestAddr, usize)> =
transmute(exec_n);
let num = libafl_qemu_sys::libafl_add_write_hook(
gen, exec1, exec2, exec4, exec8, exec_n, data,