Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix qemu cov #2875

Merged
merged 3 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzzers/binary_only/qemu_coverage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This folder contains an example fuzzer which runs each entry in the input corpus and collects
the cumuative coverage data in drcov format. This fuzzer also distributes the test cases in
the input corupus evenly across the selected cores.
the input corpus evenly across the selected cores.

The following architectures are supported:
* arm
Expand Down
33 changes: 20 additions & 13 deletions fuzzers/binary_only/qemu_coverage/src/fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ use libafl_bolts::{
AsSlice,
};
use libafl_qemu::{
elf::EasyElf,
modules::{drcov::DrCovModule, utils::filters::StdAddressFilter},
ArchExtras, CallingConvention, Emulator, GuestAddr, GuestReg, MmapPerms, QemuExecutor,
QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
elf::EasyElf, modules::drcov::DrCovModule, ArchExtras, CallingConvention, Emulator, GuestAddr,
GuestReg, MmapPerms, Qemu, QemuExecutor, QemuExitReason, QemuRWError, QemuShutdownCause, Regs,
};

#[derive(Default)]
Expand Down Expand Up @@ -127,9 +125,14 @@ pub fn fuzz() {
mut mgr: LlmpRestartingEventManager<_, _, _, _, _>,
client_description: ClientDescription| {
let mut cov_path = options.coverage_path.clone();
let core_id = client_description.core_id();

let coverage_name = cov_path.file_stem().unwrap().to_str().unwrap();
let coverage_extension = cov_path.extension().unwrap_or_default().to_str().unwrap();
let core = core_id.0;
cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));

let emulator_modules = tuple_list!(DrCovModule::builder()
.filter(StdAddressFilter::default())
.filename(cov_path.clone())
.full_trace(false)
.build());
Expand Down Expand Up @@ -175,7 +178,7 @@ pub fn fuzz() {

let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap();

let reset = |buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> {
let reset = |qemu: Qemu, buf: &[u8], len: GuestReg| -> Result<(), QemuRWError> {
unsafe {
let _ = qemu.write_mem(input_addr, buf);
rmalmain marked this conversation as resolved.
Show resolved Hide resolved
qemu.write_reg(Regs::Pc, test_one_input_ptr)?;
Expand All @@ -197,7 +200,9 @@ pub fn fuzz() {
};

let mut harness =
|_emulator: &mut Emulator<_, _, _, _, _, _, _>, _state: &mut _, input: &BytesInput| {
|emulator: &mut Emulator<_, _, _, _, _, _, _>, state: &mut _, input: &BytesInput| {
let qemu = emulator.qemu();

let target = input.target_bytes();
let mut buf = target.as_slice();
let mut len = buf.len();
Expand All @@ -206,7 +211,13 @@ pub fn fuzz() {
len = MAX_INPUT_SIZE;
}
let len = len as GuestReg;
reset(buf, len).unwrap();
reset(qemu, buf, len).unwrap();

unsafe {
let ret = emulator.run(state, input);
log::warn!("ret = {ret:?}");
}

ExitKind::Ok
};

Expand All @@ -215,6 +226,7 @@ pub fn fuzz() {
.cores
.position(core_id)
.expect("Failed to get core index");

let files = corpus_files
.iter()
.skip(files_per_core * core_idx)
Expand Down Expand Up @@ -245,11 +257,6 @@ pub fn fuzz() {
let scheduler = QueueScheduler::new();
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);

let coverage_name = cov_path.file_stem().unwrap().to_str().unwrap();
let coverage_extension = cov_path.extension().unwrap_or_default().to_str().unwrap();
let core = core_id.0;
cov_path.set_file_name(format!("{coverage_name}-{core:03}.{coverage_extension}"));

let mut executor = QemuExecutor::new(
emulator,
&mut harness,
Expand Down
2 changes: 1 addition & 1 deletion libafl_qemu/src/modules/cmplog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ where
return None;
}
}
let state = state.expect("The gen_unique_cmp_ids hook works only for in-process fuzzing");
let state = state.expect("The gen_unique_cmp_ids hook works only for in-process fuzzing. Is the Executor initialized?");
if state.metadata_map().get::<QemuCmpsMapMetadata>().is_none() {
state.add_metadata(QemuCmpsMapMetadata::new());
}
Expand Down
17 changes: 9 additions & 8 deletions libafl_qemu/src/modules/drcov.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,26 +265,27 @@ where
I: Unpin,
S: Unpin + HasMetadata,
{
fn post_qemu_init<ET>(&mut self, _qemu: Qemu, emulator_modules: &mut EmulatorModules<ET, I, S>)
fn post_qemu_init<ET>(&mut self, _qemu: Qemu, _emulator_modules: &mut EmulatorModules<ET, I, S>)
where
ET: EmulatorModuleTuple<I, S>,
{
emulator_modules.blocks(
Hook::Function(gen_unique_block_ids::<ET, F, I, S>),
Hook::Function(gen_block_lengths::<ET, F, I, S>),
Hook::Function(exec_trace_block::<ET, F, I, S>),
);
}

#[cfg(feature = "usermode")]
fn first_exec<ET>(
&mut self,
qemu: Qemu,
_emulator_modules: &mut EmulatorModules<ET, I, S>,
emulator_modules: &mut EmulatorModules<ET, I, S>,
_state: &mut S,
) where
ET: EmulatorModuleTuple<I, S>,
{
emulator_modules.blocks(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make sense / look nicer from an API side to have an extra method that gets called to set up hooks? It'd be called right before this one but have a name that's clearly for setup

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so, in that case, i guess what you want is to rename first_exec into fuzzing_setup or something similar?
some hooks can be set just after qemu gets initialized or just before the first fuzzing run, depending on the module. so i don't think we can just have a single method to set hooks up

Hook::Function(gen_unique_block_ids::<ET, F, I, S>),
Hook::Function(gen_block_lengths::<ET, F, I, S>),
Hook::Function(exec_trace_block::<ET, F, I, S>),
);

if self.module_mapping.is_none() {
log::info!("Auto-filling module mapping for DrCov module from QEMU mapping.");

Expand Down Expand Up @@ -392,7 +393,7 @@ where
return None;
}

let state = state.expect("The gen_unique_block_ids hook works only for in-process fuzzing");
let state = state.expect("The gen_unique_block_ids hook works only for in-process fuzzing. Is the Executor initialized?");
if state
.metadata_map_mut()
.get_mut::<DrCovMetadata>()
Expand Down
2 changes: 1 addition & 1 deletion libafl_qemu/src/modules/edges/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ mod generators {

let mask: usize = get_mask::<IS_CONST_MAP, MAP_SIZE>();

let state = state.expect("The gen_unique_edge_ids hook works only for in-process fuzzing");
let state = state.expect("The gen_unique_edge_ids hook works only for in-process fuzzing. Is the Executor initialized?");
let meta = state.metadata_or_insert_with(QemuEdgesMapMetadata::new);

match meta.map.entry((src, dest)) {
Expand Down
Loading