
* centralize clippy definition * fmt * add update bindings script * add a checked and unchecked version of memory read to qemu stuff also, a lot of clippy thing * update binding position * rm old script, new one is a bit better * update doc * macos clippy * adapt fuzzers * windows clippy * fix fuzzer * windows clippy * remove old allowed clippy * remove some allowed clippy * use default features for serde_json in gramatron * better error handler for failed rw to memory
133 lines
4.1 KiB
Rust
133 lines
4.1 KiB
Rust
use libafl::{
|
|
executors::ExitKind,
|
|
inputs::{BytesInput, HasTargetBytes},
|
|
Error,
|
|
};
|
|
use libafl_bolts::AsSlice;
|
|
use libafl_qemu::{
|
|
elf::EasyElf, ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, Regs,
|
|
};
|
|
|
|
pub struct Harness {
|
|
qemu: Qemu,
|
|
input_addr: GuestAddr,
|
|
pc: GuestAddr,
|
|
stack_ptr: GuestAddr,
|
|
ret_addr: GuestAddr,
|
|
}
|
|
|
|
pub const MAX_INPUT_SIZE: usize = 1_048_576; // 1MB
|
|
|
|
impl Harness {
|
|
/// 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:}")))?;
|
|
|
|
let pc: GuestReg = qemu
|
|
.read_reg(Regs::Pc)
|
|
.map_err(|e| Error::unknown(format!("Failed to read PC: {e:?}")))?;
|
|
|
|
let stack_ptr: GuestAddr = qemu
|
|
.read_reg(Regs::Sp)
|
|
.map_err(|e| Error::unknown(format!("Failed to read stack pointer: {e:?}")))?;
|
|
|
|
let ret_addr: GuestAddr = qemu
|
|
.read_return_address()
|
|
.map_err(|e| Error::unknown(format!("Failed to read return address: {e:?}")))?;
|
|
|
|
Ok(Harness {
|
|
qemu,
|
|
input_addr,
|
|
pc,
|
|
stack_ptr,
|
|
ret_addr,
|
|
})
|
|
}
|
|
|
|
/// 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
|
|
}
|
|
|
|
fn reset(&self, input: &BytesInput) -> Result<(), Error> {
|
|
let target = input.target_bytes();
|
|
let mut buf = target.as_slice();
|
|
let mut len = buf.len();
|
|
if len > MAX_INPUT_SIZE {
|
|
buf = &buf[0..MAX_INPUT_SIZE];
|
|
len = MAX_INPUT_SIZE;
|
|
}
|
|
let len = len as GuestReg;
|
|
|
|
self.qemu.write_mem(self.input_addr, buf).map_err(|e| {
|
|
Error::unknown(format!(
|
|
"Failed to write to memory@{:#x}: {e:?}",
|
|
self.input_addr
|
|
))
|
|
})?;
|
|
|
|
self.qemu
|
|
.write_reg(Regs::Pc, self.pc)
|
|
.map_err(|e| Error::unknown(format!("Failed to write PC: {e:?}")))?;
|
|
|
|
self.qemu
|
|
.write_reg(Regs::Sp, self.stack_ptr)
|
|
.map_err(|e| Error::unknown(format!("Failed to write SP: {e:?}")))?;
|
|
|
|
self.qemu
|
|
.write_return_address(self.ret_addr)
|
|
.map_err(|e| Error::unknown(format!("Failed to write return address: {e:?}")))?;
|
|
|
|
self.qemu
|
|
.write_function_argument(CallingConvention::Cdecl, 0, self.input_addr)
|
|
.map_err(|e| Error::unknown(format!("Failed to write argument 0: {e:?}")))?;
|
|
|
|
self.qemu
|
|
.write_function_argument(CallingConvention::Cdecl, 1, len)
|
|
.map_err(|e| Error::unknown(format!("Failed to write argument 1: {e:?}")))?;
|
|
unsafe {
|
|
let _ = self.qemu.run();
|
|
};
|
|
Ok(())
|
|
}
|
|
}
|