Romain Malmain d48a7d508d
Centralize clippy lints in workspace (#2606)
* 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
2024-10-15 13:31:01 +02:00

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