Move all target-specific code to harness.rs (#2605)
This commit is contained in:
parent
830941ce3a
commit
2bfed2d488
@ -1,9 +1,13 @@
|
||||
# qemu_launcher_
|
||||
|
||||
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. It has been tested on Linux.
|
||||
This automatically spawns n child processes, and binds them to a free core.
|
||||
This folder contains an example fuzzer that will fuzz binary-only targets, cross-architecture, on Linux.
|
||||
It's using LLMP for fast multi-process fuzzing and crash detection.
|
||||
This automatically spawns `n` child processes, and binds them to a free core.
|
||||
|
||||
To adapt the fuzzer to your custom target, change [`harness.rs`](./src/harness.rs).
|
||||
|
||||
The following architectures are supported:
|
||||
|
||||
* arm
|
||||
* aarch64
|
||||
* i386
|
||||
@ -11,10 +15,10 @@ The following architectures are supported:
|
||||
* mips
|
||||
* ppc
|
||||
|
||||
Note that the injection feature `-j` is currently only supported on x86_64
|
||||
and aarch64.
|
||||
For usermode, this fuzzer supports injection fuzzing with `-j`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
sudo apt install \
|
||||
gcc-arm-linux-gnueabi \
|
||||
@ -32,7 +36,8 @@ sudo apt install \
|
||||
|
||||
## Run
|
||||
|
||||
Defaults to `x86_64` architecture
|
||||
Defaults to `x86_64` architecture. Change the architecture by
|
||||
|
||||
```bash
|
||||
cargo make run
|
||||
```
|
||||
|
@ -11,16 +11,16 @@ use libafl_bolts::{core_affinity::CoreId, rands::StdRand, tuples::tuple_list};
|
||||
#[cfg(feature = "injections")]
|
||||
use libafl_qemu::modules::injections::InjectionModule;
|
||||
use libafl_qemu::{
|
||||
elf::EasyElf,
|
||||
modules::{
|
||||
asan::{init_qemu_with_asan, AsanModule},
|
||||
asan_guest::{init_qemu_with_asan_guest, AsanGuestModule},
|
||||
cmplog::CmpLogModule,
|
||||
},
|
||||
ArchExtras, GuestAddr, Qemu,
|
||||
Qemu,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
harness::Harness,
|
||||
instance::{ClientMgr, Instance},
|
||||
options::FuzzerOptions,
|
||||
};
|
||||
@ -55,16 +55,7 @@ impl<'a> Client<'a> {
|
||||
.collect::<Vec<(String, String)>>()
|
||||
}
|
||||
|
||||
fn start_pc(qemu: Qemu) -> Result<GuestAddr, Error> {
|
||||
let mut elf_buffer = Vec::new();
|
||||
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?;
|
||||
|
||||
let start_pc = elf
|
||||
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||
.ok_or_else(|| Error::empty_optional("Symbol LLVMFuzzerTestOneInput not found"))?;
|
||||
Ok(start_pc)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn run<M: Monitor>(
|
||||
&self,
|
||||
state: Option<ClientState>,
|
||||
@ -72,9 +63,11 @@ impl<'a> Client<'a> {
|
||||
core_id: CoreId,
|
||||
) -> Result<(), Error> {
|
||||
let mut args = self.args()?;
|
||||
Harness::edit_args(&mut args);
|
||||
log::debug!("ARGS: {:#?}", args);
|
||||
|
||||
let mut env = self.env();
|
||||
Harness::edit_env(&mut env);
|
||||
log::debug!("ENV: {:#?}", env);
|
||||
|
||||
let is_asan = self.options.is_asan_core(core_id);
|
||||
@ -96,9 +89,6 @@ impl<'a> Client<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
let start_pc = Self::start_pc(qemu)?;
|
||||
log::debug!("start_pc @ {start_pc:#x}");
|
||||
|
||||
#[cfg(not(feature = "injections"))]
|
||||
let injection_module = None;
|
||||
|
||||
@ -118,13 +108,7 @@ impl<'a> Client<'a> {
|
||||
}
|
||||
});
|
||||
|
||||
qemu.entry_break(start_pc);
|
||||
|
||||
let ret_addr: GuestAddr = qemu
|
||||
.read_return_address()
|
||||
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:?}")))?;
|
||||
log::debug!("ret_addr = {ret_addr:#x}");
|
||||
qemu.set_breakpoint(ret_addr);
|
||||
let harness = Harness::init(qemu).expect("Error setting up harness.");
|
||||
|
||||
let is_cmplog = self.options.is_cmplog_core(core_id);
|
||||
|
||||
@ -136,6 +120,7 @@ impl<'a> Client<'a> {
|
||||
let instance_builder = Instance::builder()
|
||||
.options(self.options)
|
||||
.qemu(qemu)
|
||||
.harness(harness)
|
||||
.mgr(mgr)
|
||||
.core_id(core_id)
|
||||
.extra_tokens(extra_tokens);
|
||||
|
@ -4,7 +4,9 @@ use libafl::{
|
||||
Error,
|
||||
};
|
||||
use libafl_bolts::AsSlice;
|
||||
use libafl_qemu::{ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, Regs};
|
||||
use libafl_qemu::{
|
||||
elf::EasyElf, ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, Regs,
|
||||
};
|
||||
|
||||
pub struct Harness {
|
||||
qemu: Qemu,
|
||||
@ -17,7 +19,40 @@ pub struct Harness {
|
||||
pub const MAX_INPUT_SIZE: usize = 1_048_576; // 1MB
|
||||
|
||||
impl Harness {
|
||||
pub fn new(qemu: Qemu) -> Result<Harness, Error> {
|
||||
/// Change environment
|
||||
#[inline]
|
||||
#[allow(clippy::ptr_arg)]
|
||||
pub fn edit_env(_env: &mut Vec<(String, String)>) {}
|
||||
|
||||
/// Change arguments
|
||||
#[inline]
|
||||
#[allow(clippy::ptr_arg)]
|
||||
pub fn edit_args(_args: &mut Vec<String>) {}
|
||||
|
||||
/// Helper function to find the function we want to fuzz.
|
||||
fn start_pc(qemu: Qemu) -> Result<GuestAddr, Error> {
|
||||
let mut elf_buffer = Vec::new();
|
||||
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer)?;
|
||||
|
||||
let start_pc = elf
|
||||
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||
.ok_or_else(|| Error::empty_optional("Symbol LLVMFuzzerTestOneInput not found"))?;
|
||||
Ok(start_pc)
|
||||
}
|
||||
|
||||
/// Initialize the emulator, run to the entrypoint (or jump there) and return the [`Harness`] struct
|
||||
pub fn init(qemu: Qemu) -> Result<Harness, Error> {
|
||||
let start_pc = Self::start_pc(qemu)?;
|
||||
log::debug!("start_pc @ {start_pc:#x}");
|
||||
|
||||
qemu.entry_break(start_pc);
|
||||
|
||||
let ret_addr: GuestAddr = qemu
|
||||
.read_return_address()
|
||||
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:?}")))?;
|
||||
log::debug!("ret_addr = {ret_addr:#x}");
|
||||
qemu.set_breakpoint(ret_addr);
|
||||
|
||||
let input_addr = qemu
|
||||
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
||||
.map_err(|e| Error::unknown(format!("Failed to map input buffer: {e:}")))?;
|
||||
@ -43,6 +78,11 @@ impl Harness {
|
||||
})
|
||||
}
|
||||
|
||||
/// If we need to do extra work after forking, we can do that here.
|
||||
#[inline]
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn post_fork(&self) {}
|
||||
|
||||
pub fn run(&self, input: &BytesInput) -> ExitKind {
|
||||
self.reset(input).unwrap();
|
||||
ExitKind::Ok
|
||||
|
@ -61,6 +61,9 @@ pub type ClientMgr<M> =
|
||||
#[derive(TypedBuilder)]
|
||||
pub struct Instance<'a, M: Monitor> {
|
||||
options: &'a FuzzerOptions,
|
||||
/// The harness. We create it before forking, then `take()` it inside the client.
|
||||
#[builder(setter(strip_option))]
|
||||
harness: Option<Harness>,
|
||||
qemu: Qemu,
|
||||
mgr: ClientMgr<M>,
|
||||
core_id: CoreId,
|
||||
@ -186,7 +189,12 @@ impl<'a, M: Monitor> Instance<'a, M> {
|
||||
|
||||
state.add_metadata(tokens);
|
||||
|
||||
let harness = Harness::new(self.qemu)?;
|
||||
let harness = self
|
||||
.harness
|
||||
.take()
|
||||
.expect("The harness can never be None here!");
|
||||
harness.post_fork();
|
||||
|
||||
let mut harness = |_emulator: &mut Emulator<_, _, _, _, _>,
|
||||
_state: &mut _,
|
||||
input: &BytesInput| harness.run(input);
|
||||
|
Loading…
x
Reference in New Issue
Block a user