From 3109188ecd4dc6939f9e03ee2581075a0e9c77c5 Mon Sep 17 00:00:00 2001 From: dergoegge Date: Mon, 5 Aug 2024 15:06:21 +0100 Subject: [PATCH] Bump LibAFL to 0.13.2 --- Cargo.lock | 28 ++++++------- Cargo.toml | 6 +-- src/main.rs | 100 ++++++++++++++------------------------------ src/qemu_harness.rs | 87 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 86 deletions(-) create mode 100644 src/qemu_harness.rs diff --git a/Cargo.lock b/Cargo.lock index 5d9b12b..7ed889e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -658,9 +658,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libafl" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef11306fe131871480b650648aaf81db0c0c30068f886f2de5cb9b407b2ffac" +checksum = "9e42c22bae397bf94fbc321fae00f4aa9914d641b4f332c15f1d1fd46adafcf3" dependencies = [ "ahash", "backtrace", @@ -694,9 +694,9 @@ dependencies = [ [[package]] name = "libafl_bolts" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd2aed6c2f83d6be5f243e7c4ed5f5b87538b9868d70a483e0eaaa05fea782d" +checksum = "728dca49b0c000d62b94e1d92dffd76379ed313f28da14f45a7ddf2604d137f9" dependencies = [ "ahash", "backtrace", @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "libafl_derive" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c566e831be2d55db7938ee506f8a9cfac7d9b65a8ca58c3c1ac2795b0a0bfbf" +checksum = "fb28be024e17736dc77eca670c85e0bca03dd7a142b5908076e6b394c9942566" dependencies = [ "quote", "syn 2.0.71", @@ -737,9 +737,9 @@ dependencies = [ [[package]] name = "libafl_qemu" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9e2ba39a2193fa728f87a20fd96353a47ff737e8121d8ce1714d74a4f21378a" +checksum = "78877d135f7fe36011bc74bfb1d5db573cb4dd8837f9106eab0cc88a1d2c37ff" dependencies = [ "addr2line 0.23.0", "bindgen", @@ -778,9 +778,9 @@ dependencies = [ [[package]] name = "libafl_qemu_build" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d9a5fba7df07b1d67bf78dd2d7014f8e7a8585d87898fb499b9a15d1f58a277" +checksum = "478f36dd8cc7f91bc6df5dc5407f0ff7a699a83200834a8e6cd618bf15595993" dependencies = [ "bindgen", "cc", @@ -795,9 +795,9 @@ dependencies = [ [[package]] name = "libafl_qemu_sys" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e861dde713271340b8859a043a69cbd69dd8ed75262b3200a4fd44fe2a84bb" +checksum = "676a6a748332c39ec1ba89097217332d8a7eef99ffe9f335d9674a816ce2c125" dependencies = [ "libafl_qemu_build", "libc", @@ -810,9 +810,9 @@ dependencies = [ [[package]] name = "libafl_targets" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e07f5f7fcae18ec1143762c832e3c900c7f6d0f96d62c21e7517245344951" +checksum = "b29b5e4c6dc4be19b04602d0e420678569e59c1701f4562cf2f2d0a1df9f03b4" dependencies = [ "bindgen", "cc", diff --git a/Cargo.toml b/Cargo.toml index af8f506..a2e2f6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,9 @@ qemu_arm = ["qemu", "libafl_qemu/arm", "dep:libafl_qemu"] [dependencies] clap = { version = "4.3.0", features = ["derive", "string"] } -libafl = "0.13.1" -libafl_bolts = "0.13.1" -libafl_qemu = { version = "0.13.1", features = ["usermode"], optional = true } +libafl = "0.13.2" +libafl_bolts = "0.13.2" +libafl_qemu = { version = "0.13.2", features = ["usermode"], optional = true } libc = "0.2.155" serde = "1.0.200" diff --git a/src/main.rs b/src/main.rs index fd64c8a..3295504 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,8 @@ mod corpus_syncer; mod dlsym; mod observers; mod options; +#[cfg(feature = "qemu")] +mod qemu_harness; use clap::Parser; @@ -13,12 +15,12 @@ use std::time::Duration; use libafl::{ corpus::{Corpus, HasTestcase, InMemoryCorpus, OnDiskCorpus, Testcase}, events::{ProgressReporter, SimpleEventManager}, - executors::{DiffExecutor, ExitKind, ForkserverExecutor}, + executors::{DiffExecutor, ForkserverExecutor}, feedbacks::{ differential::{DiffFeedback, DiffResult}, MaxMapFeedback, }, - inputs::{BytesInput, HasMutatorBytes, HasTargetBytes, Input}, + inputs::{BytesInput, HasMutatorBytes, Input}, monitors::SimplePrintingMonitor, mutators::{ havoc_mutations, havoc_mutations_no_crossover, StdMOptMutator, StdScheduledMutator, @@ -30,7 +32,7 @@ use libafl::{ }, stages::{CalibrationStage, StdPowerMutationalStage, StdTMinMutationalStage}, state::{HasCorpus, HasSolutions, StdState}, - Fuzzer, StdFuzzer, + Error, Fuzzer, StdFuzzer, }; use libafl_bolts::{ ownedref::OwnedMutSlice, @@ -41,8 +43,8 @@ use libafl_bolts::{ }; #[cfg(feature = "qemu")] use libafl_qemu::{ - edges::QemuEdgeCoverageClassicHelper, elf::EasyElf, ArchExtras, CallingConvention, GuestAddr, - GuestReg, MmapPerms, Qemu, QemuExecutor, QemuHooks, Regs, + command::NopCommandManager, elf::EasyElf, modules::edges::EdgeCoverageClassicModule, + ArchExtras, Emulator, GuestAddr, NopEmulatorExitHandler, Qemu, QemuExecutor, }; use corpus_syncer::CorpusSyncer; @@ -55,11 +57,7 @@ const MAX_CHARACTERIZATION_SHMEM_SIZE: usize = 32; const MAX_INPUT_SIZE: usize = 1_048_576; #[cfg(feature = "qemu")] -fn setup_qemu( - entry: &str, - qemu_binary: &str, - args: Vec, -) -> (Qemu, GuestReg, GuestAddr, GuestAddr, GuestAddr) { +fn setup_qemu(entry: &str, qemu_binary: &str, args: Vec) -> Qemu { let mut qemu_args = vec![String::from("semsan"), String::from(qemu_binary)]; qemu_args.extend(args); @@ -67,21 +65,22 @@ fn setup_qemu( let mut env: HashMap = std::env::vars().collect(); env.remove("LD_LIBRARY_PATH"); let env: Vec<(String, String)> = env.drain().collect(); - let emu = Qemu::init(qemu_args.as_slice(), env.as_slice()).unwrap(); + let qemu = Qemu::init(qemu_args.as_slice(), env.as_slice()).unwrap(); let mut elf_buffer = Vec::new(); - let elf = EasyElf::from_file(emu.binary_path(), &mut elf_buffer).unwrap(); + let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap(); let test_one_input_ptr = elf - .resolve_symbol(entry, emu.load_addr()) + .resolve_symbol(entry, qemu.load_addr()) .expect(&format!("Symbol {} not found", entry)); // Emulate until `LLVMFuzzerTestOneInput` is hit - emu.entry_break(test_one_input_ptr); + qemu.entry_break(test_one_input_ptr); - let pc: GuestReg = emu.read_reg(Regs::Pc).unwrap(); - let stack_ptr: GuestAddr = emu.read_reg(Regs::Sp).unwrap(); - let ret_addr: GuestAddr = emu.read_return_address().unwrap(); + let ret_addr: GuestAddr = qemu + .read_return_address() + .map_err(|e| Error::unknown(format!("Failed to read return address: {e:?}"))) + .unwrap(); let mut breakpoint = ret_addr; #[cfg(feature = "qemu_arm")] @@ -89,13 +88,9 @@ fn setup_qemu( // Arm32 thumb state detected, subtract one for the breakpoint breakpoint -= 1; } - emu.set_breakpoint(breakpoint); + qemu.set_breakpoint(breakpoint); - let input_addr = emu - .map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite) - .unwrap(); - - (emu, pc, stack_ptr, ret_addr, input_addr) + qemu } fn main() -> std::process::ExitCode { @@ -141,16 +136,7 @@ fn main() -> std::process::ExitCode { secondary_args.extend(opts.shared_args.clone()); #[cfg(feature = "qemu")] - let (emulator, pc, stack_ptr, ret_addr, input_addr) = - setup_qemu(&opts.qemu_entry, &opts.secondary, secondary_args.clone()); - - #[cfg(feature = "qemu")] - if opts.debug { - println!( - "Successfully setup qemu: pc={:?} stack_ptr={:?} ret_addr={:?} input_addr={:?}", - pc, stack_ptr, ret_addr, input_addr - ); - } + let qemu = setup_qemu(&opts.qemu_entry, &opts.secondary, secondary_args.clone()); dlsym! { fn semsan_custom_comparator(*const u8, usize, *const u8, usize) -> bool } let custom_comparator = semsan_custom_comparator.get(); @@ -293,41 +279,17 @@ fn main() -> std::process::ExitCode { .unwrap(); #[cfg(feature = "qemu")] - let mut secondary_qemu_harness = |input: &BytesInput| { - 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; - - unsafe { - emulator.write_mem(input_addr, buf); - emulator.write_reg(Regs::Pc, pc).unwrap(); - emulator.write_reg(Regs::Sp, stack_ptr).unwrap(); - emulator.write_return_address(ret_addr).unwrap(); - emulator - .write_function_argument(CallingConvention::Cdecl, 0, input_addr) - .unwrap(); - emulator - .write_function_argument(CallingConvention::Cdecl, 1, len) - .unwrap(); - // TODO handle emulation results? siwtch to QemuForkExecutor to warn about exit(_) - // usage in the target - let _ = emulator.run(); - } - - ExitKind::Ok - }; - + let secondary_qemu_harness = qemu_harness::Harness::new(&qemu).unwrap(); + #[cfg(feature = "qemu")] + let mut secondary_qemu_harness = |input: &BytesInput| secondary_qemu_harness.run(input); #[cfg(feature = "qemu")] - let mut hooks = QemuHooks::new( - emulator.clone(), - // TODO: The classic helper is needed for StdMapObserver. - tuple_list!(QemuEdgeCoverageClassicHelper::default(),), - ); + let mut emulator = Emulator::new_with_qemu( + qemu, + tuple_list!(EdgeCoverageClassicModule::default(),), + NopEmulatorExitHandler, + NopCommandManager, + ) + .unwrap(); match &opts.command { Command::Fuzz(fuzz_opts) => { @@ -385,7 +347,7 @@ fn main() -> std::process::ExitCode { #[cfg(feature = "qemu")] let secondary_executor = QemuExecutor::new( - &mut hooks, + &mut emulator, &mut secondary_qemu_harness, tuple_list!(secondary_map_observer, secondary_diff_value_observer), &mut fuzzer, @@ -468,7 +430,7 @@ fn main() -> std::process::ExitCode { #[cfg(feature = "qemu")] let secondary_executor = QemuExecutor::new( - &mut hooks, + &mut emulator, &mut secondary_qemu_harness, tuple_list!(secondary_map_observer, secondary_diff_value_observer), &mut fuzzer, diff --git a/src/qemu_harness.rs b/src/qemu_harness.rs new file mode 100644 index 0000000..946796b --- /dev/null +++ b/src/qemu_harness.rs @@ -0,0 +1,87 @@ +use libafl::{ + executors::ExitKind, + inputs::{BytesInput, HasTargetBytes}, + Error, +}; +use libafl_bolts::AsSlice; +use libafl_qemu::{ArchExtras, CallingConvention, GuestAddr, GuestReg, MmapPerms, Qemu, Regs}; + +pub struct Harness<'a> { + qemu: &'a Qemu, + input_addr: GuestAddr, + pc: GuestAddr, + stack_ptr: GuestAddr, + ret_addr: GuestAddr, +} + +pub const MAX_INPUT_SIZE: usize = 1_048_576; // 1MB + +impl<'a> Harness<'a> { + pub fn new(qemu: &Qemu) -> Result { + 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, + }) + } + + 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; + + unsafe { self.qemu.write_mem(self.input_addr, buf) }; + + 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(()) + } +}