From 566253d58fd7516b31410ced709cd0a6e8398974 Mon Sep 17 00:00:00 2001 From: Addison Crump Date: Mon, 20 Feb 2023 14:40:34 +0100 Subject: [PATCH] improve how libafl_libfuzzer emits solution data, and exit when not ignoring --- .../libafl_libfuzzer_runtime/Cargo.toml | 3 +- .../libafl_libfuzzer_runtime/src/feedbacks.rs | 111 +++++++++++++++++- .../libafl_libfuzzer_runtime/src/fuzz.rs | 31 ++++- .../libafl_libfuzzer_runtime/src/lib.rs | 53 +++------ .../libafl_libfuzzer_runtime/src/report.rs | 1 + libafl_targets/Cargo.toml | 2 +- libafl_targets/src/libfuzzer/observers/oom.rs | 8 +- 7 files changed, 165 insertions(+), 44 deletions(-) diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml b/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml index ecb8dd5eed0..1c6bef2553e 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml @@ -17,11 +17,12 @@ path = "src/lib.rs" crate-type = ["staticlib", "rlib"] [dependencies] -rand = "0.8.5" libafl = { path = "../../libafl", default-features = false, features = ["std", "derive", "llmp_compression", "rand_trait", "fork", "errors_backtrace"] } libafl_targets = { path = "../../libafl_targets", features = ["sancov_8bit", "sancov_cmplog", "libfuzzer", "libfuzzer_oom"] } libc = "0.2.139" mimalloc = { version = "0.1.34", default-features = false } +rand = "0.8.5" +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } # serialization lib # for identifying if we can grimoire-ify utf8-chars = "2.0.3" diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/feedbacks.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/feedbacks.rs index 8be1e97a732..520d286b777 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/feedbacks.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/feedbacks.rs @@ -2,9 +2,20 @@ use alloc::rc::Rc; use core::{cell::RefCell, fmt::Debug}; use libafl::{ - alloc, bolts::tuples::Named, events::EventFirer, executors::ExitKind, feedbacks::Feedback, - inputs::UsesInput, observers::ObserversTuple, state::HasClientPerfMonitor, Error, + alloc, + bolts::tuples::Named, + corpus::Testcase, + events::EventFirer, + executors::ExitKind, + feedbacks::Feedback, + impl_serdeany, + inputs::UsesInput, + observers::ObserversTuple, + state::{HasClientPerfMonitor, HasMetadata}, + Error, }; +use libafl_targets::OOMFeedback; +use serde::{Deserialize, Serialize}; #[derive(Debug)] pub struct LibfuzzerKeepFeedback { @@ -48,3 +59,99 @@ where Ok(*self.keep.borrow()) } } + +#[derive(Deserialize, Serialize, Debug)] +pub struct LibfuzzerCrashCauseMetadata { + kind: ExitKind, +} + +impl_serdeany!(LibfuzzerCrashCauseMetadata); + +impl LibfuzzerCrashCauseMetadata { + pub fn kind(&self) -> ExitKind { + self.kind + } +} + +#[derive(Debug)] +pub struct LibfuzzerCrashCauseFeedback { + exit_kind: ExitKind, +} + +impl LibfuzzerCrashCauseFeedback { + pub fn new() -> Self { + Self { + exit_kind: ExitKind::Ok, + } + } +} + +impl Named for LibfuzzerCrashCauseFeedback { + fn name(&self) -> &str { + "crash-cause" + } +} + +impl Feedback for LibfuzzerCrashCauseFeedback +where + S: UsesInput + HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + _observers: &OT, + exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + self.exit_kind = *exit_kind; + Ok(false) + } + + fn append_metadata( + &mut self, + _state: &mut S, + _observers: &OT, + testcase: &mut Testcase, + ) -> Result<(), Error> + where + OT: ObserversTuple, + { + match self.exit_kind { + ExitKind::Crash | ExitKind::Oom if OOMFeedback::oomed() => { + if let Some(filename) = testcase.filename_mut() { + *filename = format!("oom-{}", filename); + } + testcase.metadata_mut().insert(LibfuzzerCrashCauseMetadata { + kind: ExitKind::Oom, + }); + } + ExitKind::Crash | ExitKind::Oom => { + if let Some(filename) = testcase.filename_mut() { + *filename = format!("crash-{}", filename); + } + testcase.metadata_mut().insert(LibfuzzerCrashCauseMetadata { + kind: ExitKind::Crash, + }); + } + ExitKind::Timeout => { + if let Some(filename) = testcase.filename_mut() { + *filename = format!("timeout-{}", filename); + } + testcase.metadata_mut().insert(LibfuzzerCrashCauseMetadata { + kind: ExitKind::Timeout, + }); + } + _ => { + testcase.metadata_mut().insert(LibfuzzerCrashCauseMetadata { + kind: self.exit_kind, + }); + } + } + Ok(()) + } +} diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs index bed4525c57d..6ec220f0b18 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/fuzz.rs @@ -7,17 +7,20 @@ use libafl::{ launcher::Launcher, shmem::{ShMemProvider, StdShMemProvider}, }, + corpus::Corpus, events::{EventConfig, ProgressReporter, SimpleEventManager, SimpleRestartingEventManager}, + executors::ExitKind, inputs::UsesInput, monitors::{tui::TuiMonitor, SimpleMonitor}, stages::StagesTuple, - state::{HasClientPerfMonitor, HasExecutions, HasMetadata, UsesState}, + state::{HasClientPerfMonitor, HasExecutions, HasMetadata, HasSolutions, UsesState}, Error, Fuzzer, }; -use crate::{fuzz_with, options::LibfuzzerOptions}; +use crate::{feedbacks::LibfuzzerCrashCauseMetadata, fuzz_with, options::LibfuzzerOptions}; fn do_fuzz( + options: &LibfuzzerOptions, fuzzer: &mut F, stages: &mut ST, executor: &mut E, @@ -26,11 +29,33 @@ fn do_fuzz( ) -> Result<(), Error> where F: Fuzzer, - S: HasClientPerfMonitor + HasMetadata + HasExecutions + UsesInput, + S: HasClientPerfMonitor + HasMetadata + HasExecutions + UsesInput + HasSolutions, E: UsesState, EM: ProgressReporter, ST: StagesTuple, { + if let Some(solution) = state.solutions().last() { + let kind = state + .solutions() + .get(solution) + .expect("Last solution was not available") + .borrow() + .metadata() + .get::() + .expect("Crash cause not attached to solution") + .kind(); + let mut halt = false; + match kind { + ExitKind::Oom if !options.ignore_ooms() => halt = true, + ExitKind::Crash if !options.ignore_crashes() => halt = true, + ExitKind::Timeout if !options.ignore_timeouts() => halt = true, + _ => {} + } + if halt { + eprintln!("Halting; the error on the next line is actually okay. :)"); + return Err(Error::shutting_down()); + } + } fuzzer.fuzz_loop(stages, executor, state, mgr)?; Ok(()) } diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs index c3e2d4d06bf..312703052ee 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs @@ -5,7 +5,6 @@ use libafl::{ inputs::{BytesInput, HasTargetBytes, Input}, Error, }; -use libafl_targets::libafl_cmplog_enabled; use crate::options::{LibfuzzerMode, LibfuzzerOptions}; @@ -62,7 +61,7 @@ macro_rules! fuzz_with { corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, executors::{ExitKind, InProcessExecutor, TimeoutExecutor}, feedback_and_fast, feedback_not, feedback_or, feedback_or_fast, - feedbacks::{ConstFeedback, CrashFeedback, MaxMapFeedback, NewHashFeedback, TimeFeedback, TimeoutFeedback}, + feedbacks::{CrashFeedback, MaxMapFeedback, NewHashFeedback, TimeFeedback, TimeoutFeedback}, generators::RandBytesGenerator, inputs::{BytesInput, HasTargetBytes}, mutators::{ @@ -88,7 +87,7 @@ macro_rules! fuzz_with { use crate::CustomMutationStatus; use crate::BACKTRACE; - use crate::feedbacks::LibfuzzerKeepFeedback; + use crate::feedbacks::{LibfuzzerCrashCauseFeedback, LibfuzzerKeepFeedback}; let edge_maker = &$edge_maker; @@ -137,10 +136,6 @@ macro_rules! fuzz_with { let edges_observer = edge_maker(); - let crashes = $options.forks().is_none() || !$options.ignore_crashes(); - let ooms = $options.forks().is_none() || !$options.ignore_ooms(); - let timeouts = $options.forks().is_none() || !$options.ignore_timeouts(); - let keep_observer = LibfuzzerKeepFeedback::new(); let keep = keep_observer.keep(); @@ -176,11 +171,7 @@ macro_rules! fuzz_with { // This one is composed by two Feedbacks in OR let mut feedback = feedback_and_fast!( feedback_not!( - feedback_or_fast!( - OOMFeedback, - CrashFeedback::new(), - TimeoutFeedback::new() - ) + CrashFeedback::new() ), keep_observer, feedback_or!( @@ -192,12 +183,13 @@ macro_rules! fuzz_with { // A feedback to choose if an input is a solution or not let mut objective = feedback_or_fast!( - feedback_and_fast!(ConstFeedback::new(ooms), OOMFeedback), + LibfuzzerCrashCauseFeedback::new(), + OOMFeedback, feedback_and_fast!( - feedback_and_fast!(ConstFeedback::new(crashes), feedback_not!(OOMFeedback), CrashFeedback::new()), + CrashFeedback::new(), NewHashFeedback::new(&backtrace_observer) ), - feedback_and_fast!(ConstFeedback::new(timeouts), TimeoutFeedback::new()) + TimeoutFeedback::new() ); let corpus_dir = if let Some(main) = $options.dirs().first() { @@ -444,7 +436,7 @@ macro_rules! fuzz_with { grimoire, ); - $operation(&mut fuzzer, &mut stages, &mut executor, &mut state, &mut mgr) + $operation(&$options, &mut fuzzer, &mut stages, &mut executor, &mut state, &mut mgr) }; $and_then(closure) @@ -496,11 +488,6 @@ extern "C" { fn libafl_targets_libfuzzer_init(argc: *mut c_int, argv: *mut *mut *const c_char) -> i32; } -unsafe extern "C" fn libafl_libfuzzer_asan_death_callback() { - libafl_cmplog_enabled = 0; - panic!("Received ASAN crash, time to go!"); -} - #[allow(non_snake_case)] #[no_mangle] pub extern "C" fn LLVMFuzzerRunDriver( @@ -512,12 +499,6 @@ pub extern "C" fn LLVMFuzzerRunDriver( .as_ref() .expect("Illegal harness provided to libafl."); - unsafe { - libafl_targets::sanitizer_ifaces::__sanitizer_set_death_callback(Some( - libafl_libfuzzer_asan_death_callback, - )) - } - unsafe { // it appears that no one, not even libfuzzer, uses this return value // https://github.com/llvm/llvm-project/blob/llvmorg-15.0.7/compiler-rt/lib/fuzzer/FuzzerDriver.cpp#L648 @@ -551,14 +532,14 @@ pub extern "C" fn LLVMFuzzerRunDriver( LibfuzzerMode::Cmin => unimplemented!(), LibfuzzerMode::Report => report::report(options, harness), }; - if res.is_err() { - let err = res.unwrap_err(); - eprintln!( - "Encountered error while performing libfuzzer shimming: {}", - err - ); - 1 - } else { - 0 + match res { + Ok(()) | Err(Error::ShuttingDown) => 0, + Err(err) => { + eprintln!( + "Encountered error while performing libfuzzer shimming: {}", + err + ); + 1 + } } } diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs index 33ef4b53a8e..040a678f4f6 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs @@ -14,6 +14,7 @@ use libafl::{ use crate::{fuzz_with, options::LibfuzzerOptions}; fn do_report( + _options: &LibfuzzerOptions, _fuzzer: &mut F, _stages: &mut ST, _executor: &mut E, diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index 0fa8f36f717..ba8784b0969 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -39,5 +39,5 @@ log = "0.4.17" libc = "0.2" rangemap = "1.0" -serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } # serialization lib # serde-big-array = "0.3.2" diff --git a/libafl_targets/src/libfuzzer/observers/oom.rs b/libafl_targets/src/libfuzzer/observers/oom.rs index 13def4d46c7..aa9bfac9a85 100644 --- a/libafl_targets/src/libfuzzer/observers/oom.rs +++ b/libafl_targets/src/libfuzzer/observers/oom.rs @@ -120,6 +120,12 @@ where #[derive(Debug, Serialize, Deserialize, Copy, Clone, Default)] pub struct OOMFeedback; +impl OOMFeedback { + pub fn oomed() -> bool { + OOMED.load(Ordering::Relaxed) + } +} + impl Named for OOMFeedback { fn name(&self) -> &str { "oom" @@ -142,6 +148,6 @@ where EM: EventFirer, OT: ObserversTuple, { - Ok(OOMED.load(Ordering::Relaxed)) + Ok(Self::oomed()) } }