Improve the libafl_libfuzzer corpus (#1539)
* improved libfuzzer corpus * use .into() for converting ids to usize * oops * fix warning about unused arg * fix some lingering CI errors * actually save the last lmao
This commit is contained in:
parent
fd98eabfbf
commit
56b37bb4bd
@ -183,7 +183,7 @@ where
|
||||
/// Get the next id given a `CorpusId` (creation order)
|
||||
#[cfg(not(feature = "corpus_btreemap"))]
|
||||
#[must_use]
|
||||
fn next(&self, idx: CorpusId) -> Option<CorpusId> {
|
||||
pub fn next(&self, idx: CorpusId) -> Option<CorpusId> {
|
||||
if let Some(item) = self.map.get(&idx) {
|
||||
item.next
|
||||
} else {
|
||||
@ -194,7 +194,7 @@ where
|
||||
/// Get the next id given a `CorpusId` (creation order)
|
||||
#[cfg(feature = "corpus_btreemap")]
|
||||
#[must_use]
|
||||
fn next(&self, idx: CorpusId) -> Option<CorpusId> {
|
||||
pub fn next(&self, idx: CorpusId) -> Option<CorpusId> {
|
||||
// TODO see if using self.keys is faster
|
||||
let mut range = self
|
||||
.map
|
||||
@ -214,7 +214,7 @@ where
|
||||
/// Get the previous id given a `CorpusId` (creation order)
|
||||
#[cfg(not(feature = "corpus_btreemap"))]
|
||||
#[must_use]
|
||||
fn prev(&self, idx: CorpusId) -> Option<CorpusId> {
|
||||
pub fn prev(&self, idx: CorpusId) -> Option<CorpusId> {
|
||||
if let Some(item) = self.map.get(&idx) {
|
||||
item.prev
|
||||
} else {
|
||||
@ -225,7 +225,7 @@ where
|
||||
/// Get the previous id given a `CorpusId` (creation order)
|
||||
#[cfg(feature = "corpus_btreemap")]
|
||||
#[must_use]
|
||||
fn prev(&self, idx: CorpusId) -> Option<CorpusId> {
|
||||
pub fn prev(&self, idx: CorpusId) -> Option<CorpusId> {
|
||||
// TODO see if using self.keys is faster
|
||||
let mut range = self
|
||||
.map
|
||||
@ -245,28 +245,28 @@ where
|
||||
/// Get the first created id
|
||||
#[cfg(not(feature = "corpus_btreemap"))]
|
||||
#[must_use]
|
||||
fn first(&self) -> Option<CorpusId> {
|
||||
pub fn first(&self) -> Option<CorpusId> {
|
||||
self.first_idx
|
||||
}
|
||||
|
||||
/// Get the first created id
|
||||
#[cfg(feature = "corpus_btreemap")]
|
||||
#[must_use]
|
||||
fn first(&self) -> Option<CorpusId> {
|
||||
pub fn first(&self) -> Option<CorpusId> {
|
||||
self.map.iter().next().map(|x| *x.0)
|
||||
}
|
||||
|
||||
/// Get the last created id
|
||||
#[cfg(not(feature = "corpus_btreemap"))]
|
||||
#[must_use]
|
||||
fn last(&self) -> Option<CorpusId> {
|
||||
pub fn last(&self) -> Option<CorpusId> {
|
||||
self.last_idx
|
||||
}
|
||||
|
||||
/// Get the last created id
|
||||
#[cfg(feature = "corpus_btreemap")]
|
||||
#[must_use]
|
||||
fn last(&self) -> Option<CorpusId> {
|
||||
pub fn last(&self) -> Option<CorpusId> {
|
||||
self.map.iter().next_back().map(|x| *x.0)
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ use core::{fmt, fmt::Write, time::Duration};
|
||||
#[cfg(feature = "std")]
|
||||
pub use disk::{OnDiskJSONMonitor, OnDiskTOMLMonitor};
|
||||
use hashbrown::HashMap;
|
||||
use libafl_bolts::{current_time, format_duration_hms, ClientId};
|
||||
use libafl_bolts::{current_time, format_duration_hms, ClientId, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "afl_exec_sec")]
|
||||
@ -210,6 +210,7 @@ impl ClientStats {
|
||||
|
||||
/// Update the user-defined stat with name and value
|
||||
pub fn update_user_stats(&mut self, name: String, value: UserStats) {
|
||||
log::info!("{}", Error::unknown("dumping backtrace for monitoring"));
|
||||
self.user_monitor.insert(name, value);
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,6 @@ use alloc::vec::Vec;
|
||||
use core::hash::BuildHasher;
|
||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
||||
use core::hash::Hasher;
|
||||
use core::{iter::Iterator, time};
|
||||
#[cfg(feature = "std")]
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
@ -175,18 +174,23 @@ pub mod launcher {}
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate libafl_derive;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::{FromUtf8Error, String};
|
||||
use core::{
|
||||
array::TryFromSliceError,
|
||||
fmt::{self, Display},
|
||||
iter::Iterator,
|
||||
num::{ParseIntError, TryFromIntError},
|
||||
time,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use std::{env::VarError, io};
|
||||
|
||||
#[cfg(feature = "libafl_derive")]
|
||||
pub use libafl_derive::SerdeAny;
|
||||
#[cfg(feature = "alloc")]
|
||||
use {
|
||||
alloc::string::{FromUtf8Error, String},
|
||||
core::cell::{BorrowError, BorrowMutError},
|
||||
};
|
||||
|
||||
/// We need fixed names for many parts of this lib.
|
||||
pub trait Named {
|
||||
@ -444,6 +448,24 @@ impl Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<BorrowError> for Error {
|
||||
fn from(err: BorrowError) -> Self {
|
||||
Self::illegal_state(format!(
|
||||
"Couldn't borrow from a RefCell as immutable: {err:?}"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<BorrowMutError> for Error {
|
||||
fn from(err: BorrowMutError) -> Self {
|
||||
Self::illegal_state(format!(
|
||||
"Couldn't borrow from a RefCell as mutable: {err:?}"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Stringify the postcard serializer error
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<postcard::Error> for Error {
|
||||
|
@ -40,7 +40,7 @@ log = "0.4.17"
|
||||
mimalloc = { version = "0.1.34", default-features = false, optional = true }
|
||||
num-traits = "0.2.15"
|
||||
rand = "0.8.5"
|
||||
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } # serialization lib
|
||||
serde = { version = "1.0", features = ["derive"] } # serialization lib
|
||||
|
||||
# clippy-suggested optimised byte counter
|
||||
bytecount = "0.6.3"
|
||||
|
322
libafl_libfuzzer/libafl_libfuzzer_runtime/src/corpus.rs
Normal file
322
libafl_libfuzzer/libafl_libfuzzer_runtime/src/corpus.rs
Normal file
@ -0,0 +1,322 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{hash_map::Entry, BTreeMap, HashMap},
|
||||
io::ErrorKind,
|
||||
path::PathBuf,
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
};
|
||||
|
||||
use libafl::{
|
||||
corpus::{inmemory::TestcaseStorage, Corpus, CorpusId, Testcase},
|
||||
inputs::{Input, UsesInput},
|
||||
};
|
||||
use libafl_bolts::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A corpus which attempts to mimic the behaviour of libFuzzer.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(bound = "I: serde::de::DeserializeOwned")]
|
||||
pub struct LibfuzzerCorpus<I>
|
||||
where
|
||||
I: Input + Serialize,
|
||||
{
|
||||
corpus_dir: PathBuf,
|
||||
loaded_mapping: RefCell<HashMap<CorpusId, u64>>,
|
||||
loaded_entries: RefCell<BTreeMap<u64, CorpusId>>,
|
||||
mapping: TestcaseStorage<I>,
|
||||
max_len: usize,
|
||||
|
||||
current: Option<CorpusId>,
|
||||
next_recency: AtomicU64,
|
||||
}
|
||||
|
||||
impl<I> LibfuzzerCorpus<I>
|
||||
where
|
||||
I: Input + Serialize + for<'de> Deserialize<'de>,
|
||||
{
|
||||
pub fn new(corpus_dir: PathBuf, max_len: usize) -> Self {
|
||||
Self {
|
||||
corpus_dir,
|
||||
loaded_mapping: RefCell::new(HashMap::default()),
|
||||
loaded_entries: RefCell::new(BTreeMap::default()),
|
||||
mapping: TestcaseStorage::new(),
|
||||
max_len,
|
||||
current: None,
|
||||
next_recency: AtomicU64::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dir_path(&self) -> &PathBuf {
|
||||
&self.corpus_dir
|
||||
}
|
||||
|
||||
/// Touch this index and maybe evict an entry if we have touched an input which was unloaded.
|
||||
fn touch(&self, idx: CorpusId) -> Result<(), Error> {
|
||||
let mut loaded_mapping = self.loaded_mapping.borrow_mut();
|
||||
let mut loaded_entries = self.loaded_entries.borrow_mut();
|
||||
match loaded_mapping.entry(idx) {
|
||||
Entry::Occupied(mut e) => {
|
||||
let &old = e.get();
|
||||
let new = self.next_recency.fetch_add(1, Ordering::Relaxed);
|
||||
e.insert(new);
|
||||
loaded_entries.remove(&old);
|
||||
loaded_entries.insert(new, idx);
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
// new entry! send it in
|
||||
let new = self.next_recency.fetch_add(1, Ordering::Relaxed);
|
||||
e.insert(new);
|
||||
loaded_entries.insert(new, idx);
|
||||
}
|
||||
}
|
||||
if loaded_entries.len() > self.max_len {
|
||||
let idx = loaded_entries.pop_first().unwrap().1; // cannot panic
|
||||
let cell = self.mapping.get(idx).ok_or_else(|| {
|
||||
Error::key_not_found(format!("Tried to evict non-existent entry {idx}"))
|
||||
})?;
|
||||
let mut tc = cell.try_borrow_mut()?;
|
||||
let _ = tc.input_mut().take();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> UsesInput for LibfuzzerCorpus<I>
|
||||
where
|
||||
I: Input + Serialize + for<'de> Deserialize<'de>,
|
||||
{
|
||||
type Input = I;
|
||||
}
|
||||
|
||||
impl<I> Corpus for LibfuzzerCorpus<I>
|
||||
where
|
||||
I: Input + Serialize + for<'de> Deserialize<'de>,
|
||||
{
|
||||
fn count(&self) -> usize {
|
||||
self.mapping.map.len()
|
||||
}
|
||||
|
||||
fn add(&mut self, testcase: Testcase<Self::Input>) -> Result<CorpusId, Error> {
|
||||
let idx = self.mapping.insert(RefCell::new(testcase));
|
||||
let mut testcase = self.mapping.get(idx).unwrap().borrow_mut();
|
||||
|
||||
match testcase.file_path() {
|
||||
Some(path) if path.canonicalize()?.starts_with(&self.corpus_dir) => {
|
||||
// if it's already in the correct dir, we retain it
|
||||
}
|
||||
_ => {
|
||||
let input = testcase.input().as_ref().ok_or_else(|| {
|
||||
Error::empty(
|
||||
"The testcase, when added to the corpus, must have an input present!",
|
||||
)
|
||||
})?;
|
||||
let name = input.generate_name(idx.into());
|
||||
let path = self.corpus_dir.join(&name);
|
||||
|
||||
match input.to_file(&path) {
|
||||
Err(Error::File(e, _)) if e.kind() == ErrorKind::AlreadyExists => {
|
||||
// we do not care if the file already exists; in this case, we assume it is equal
|
||||
}
|
||||
res => res?,
|
||||
}
|
||||
|
||||
// we DO NOT save metadata!
|
||||
|
||||
testcase.filename_mut().replace(name);
|
||||
testcase.file_path_mut().replace(path);
|
||||
}
|
||||
};
|
||||
|
||||
self.touch(idx)?;
|
||||
Ok(idx)
|
||||
}
|
||||
|
||||
fn replace(
|
||||
&mut self,
|
||||
_idx: CorpusId,
|
||||
_testcase: Testcase<Self::Input>,
|
||||
) -> Result<Testcase<Self::Input>, Error> {
|
||||
unimplemented!("It is unsafe to use this corpus variant with replace!");
|
||||
}
|
||||
|
||||
fn remove(&mut self, _id: CorpusId) -> Result<Testcase<Self::Input>, Error> {
|
||||
unimplemented!("It is unsafe to use this corpus variant with replace!");
|
||||
}
|
||||
|
||||
fn get(&self, id: CorpusId) -> Result<&RefCell<Testcase<Self::Input>>, Error> {
|
||||
self.touch(id)?;
|
||||
self.mapping.map.get(&id).map(|item| &item.testcase).ok_or_else(|| Error::illegal_state("Nonexistent corpus entry {id} requested (present in loaded entries, but not the mapping?)"))
|
||||
}
|
||||
|
||||
fn current(&self) -> &Option<CorpusId> {
|
||||
&self.current
|
||||
}
|
||||
|
||||
fn current_mut(&mut self) -> &mut Option<CorpusId> {
|
||||
&mut self.current
|
||||
}
|
||||
|
||||
fn next(&self, id: CorpusId) -> Option<CorpusId> {
|
||||
self.mapping.next(id)
|
||||
}
|
||||
|
||||
fn prev(&self, id: CorpusId) -> Option<CorpusId> {
|
||||
self.mapping.prev(id)
|
||||
}
|
||||
|
||||
fn first(&self) -> Option<CorpusId> {
|
||||
self.mapping.first()
|
||||
}
|
||||
|
||||
fn last(&self) -> Option<CorpusId> {
|
||||
self.mapping.last()
|
||||
}
|
||||
|
||||
fn load_input_into(&self, testcase: &mut Testcase<Self::Input>) -> Result<(), Error> {
|
||||
// we don't need to update the loaded testcases because it must have already been loaded
|
||||
if testcase.input().is_none() {
|
||||
let path = testcase.file_path().as_ref().ok_or_else(|| {
|
||||
Error::empty("The testcase, when being saved, must have a file path!")
|
||||
})?;
|
||||
let input = I::from_file(path)?;
|
||||
testcase.input_mut().replace(input);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn store_input_from(&self, testcase: &Testcase<Self::Input>) -> Result<(), Error> {
|
||||
let input = testcase.input().as_ref().ok_or_else(|| {
|
||||
Error::empty("The testcase, when being saved, must have an input present!")
|
||||
})?;
|
||||
let path = testcase.file_path().as_ref().ok_or_else(|| {
|
||||
Error::empty("The testcase, when being saved, must have a file path!")
|
||||
})?;
|
||||
match input.to_file(path) {
|
||||
Err(Error::File(e, _)) if e.kind() == ErrorKind::AlreadyExists => {
|
||||
// we do not care if the file already exists; in this case, we assume it is equal
|
||||
Ok(())
|
||||
}
|
||||
res => res,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A corpus which attempts to mimic the behaviour of libFuzzer's crash output.
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(bound = "I: serde::de::DeserializeOwned")]
|
||||
pub struct ArtifactCorpus<I>
|
||||
where
|
||||
I: Input + Serialize,
|
||||
{
|
||||
last: Option<RefCell<Testcase<I>>>,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<I> ArtifactCorpus<I>
|
||||
where
|
||||
I: Input + Serialize + for<'de> Deserialize<'de>,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
last: None,
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> UsesInput for ArtifactCorpus<I>
|
||||
where
|
||||
I: Input + Serialize + for<'de> Deserialize<'de>,
|
||||
{
|
||||
type Input = I;
|
||||
}
|
||||
|
||||
impl<I> Corpus for ArtifactCorpus<I>
|
||||
where
|
||||
I: Input + Serialize + for<'de> Deserialize<'de>,
|
||||
{
|
||||
fn count(&self) -> usize {
|
||||
self.count
|
||||
}
|
||||
|
||||
fn add(&mut self, testcase: Testcase<Self::Input>) -> Result<CorpusId, Error> {
|
||||
let idx = self.count;
|
||||
self.count += 1;
|
||||
|
||||
let input = testcase.input().as_ref().ok_or_else(|| {
|
||||
Error::empty("The testcase, when added to the corpus, must have an input present!")
|
||||
})?;
|
||||
let path = testcase.file_path().as_ref().ok_or_else(|| {
|
||||
Error::illegal_state("Should have set the path in the LibfuzzerCrashCauseFeedback.")
|
||||
})?;
|
||||
match input.to_file(path) {
|
||||
Err(Error::File(e, _)) if e.kind() == ErrorKind::AlreadyExists => {
|
||||
// we do not care if the file already exists; in this case, we assume it is equal
|
||||
}
|
||||
res => res?,
|
||||
}
|
||||
|
||||
// we DO NOT save metadata!
|
||||
self.last = Some(RefCell::new(testcase));
|
||||
|
||||
Ok(CorpusId::from(idx))
|
||||
}
|
||||
|
||||
fn replace(
|
||||
&mut self,
|
||||
_idx: CorpusId,
|
||||
_testcase: Testcase<Self::Input>,
|
||||
) -> Result<Testcase<Self::Input>, Error> {
|
||||
unimplemented!("Artifact prefix is thin and cannot get, replace, or remove.")
|
||||
}
|
||||
|
||||
fn remove(&mut self, _id: CorpusId) -> Result<Testcase<Self::Input>, Error> {
|
||||
unimplemented!("Artifact prefix is thin and cannot get, replace, or remove.")
|
||||
}
|
||||
|
||||
fn get(&self, id: CorpusId) -> Result<&RefCell<Testcase<Self::Input>>, Error> {
|
||||
let maybe_last = if self
|
||||
.count
|
||||
.checked_sub(1)
|
||||
.map(CorpusId::from)
|
||||
.map_or(false, |last| last == id)
|
||||
{
|
||||
self.last.as_ref()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
maybe_last.ok_or_else(|| Error::illegal_argument("Can only get the last corpus ID."))
|
||||
}
|
||||
|
||||
fn current(&self) -> &Option<CorpusId> {
|
||||
unimplemented!("Artifact prefix is thin and cannot get, replace, or remove.")
|
||||
}
|
||||
|
||||
fn current_mut(&mut self) -> &mut Option<CorpusId> {
|
||||
unimplemented!("Artifact prefix is thin and cannot get, replace, or remove.")
|
||||
}
|
||||
|
||||
fn next(&self, _id: CorpusId) -> Option<CorpusId> {
|
||||
unimplemented!("Artifact prefix is thin and cannot get, replace, or remove.")
|
||||
}
|
||||
|
||||
fn prev(&self, _id: CorpusId) -> Option<CorpusId> {
|
||||
unimplemented!("Artifact prefix is thin and cannot get, replace, or remove.")
|
||||
}
|
||||
|
||||
fn first(&self) -> Option<CorpusId> {
|
||||
unimplemented!("Artifact prefix is thin and cannot get, replace, or remove.")
|
||||
}
|
||||
|
||||
fn last(&self) -> Option<CorpusId> {
|
||||
self.count.checked_sub(1).map(CorpusId::from)
|
||||
}
|
||||
|
||||
fn load_input_into(&self, _testcase: &mut Testcase<Self::Input>) -> Result<(), Error> {
|
||||
unimplemented!("Artifact prefix is thin and cannot get, replace, or remove.")
|
||||
}
|
||||
|
||||
fn store_input_from(&self, _testcase: &Testcase<Self::Input>) -> Result<(), Error> {
|
||||
unimplemented!("Artifact prefix is thin and cannot get, replace, or remove.")
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
use alloc::rc::Rc;
|
||||
use core::{cell::RefCell, fmt::Debug};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use libafl::{
|
||||
alloc,
|
||||
@ -77,12 +76,12 @@ impl LibfuzzerCrashCauseMetadata {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LibfuzzerCrashCauseFeedback {
|
||||
artifact_prefix: Option<ArtifactPrefix>,
|
||||
artifact_prefix: ArtifactPrefix,
|
||||
exit_kind: ExitKind,
|
||||
}
|
||||
|
||||
impl LibfuzzerCrashCauseFeedback {
|
||||
pub fn new(artifact_prefix: Option<ArtifactPrefix>) -> Self {
|
||||
pub fn new(artifact_prefix: ArtifactPrefix) -> Self {
|
||||
Self {
|
||||
artifact_prefix,
|
||||
exit_kind: ExitKind::Ok,
|
||||
@ -104,17 +103,10 @@ impl LibfuzzerCrashCauseFeedback {
|
||||
let name = testcase.input().as_ref().unwrap().generate_name(0);
|
||||
name
|
||||
};
|
||||
let file_path = if let Some(artifact_prefix) = self.artifact_prefix.as_ref() {
|
||||
if let Some(filename_prefix) = artifact_prefix.filename_prefix() {
|
||||
artifact_prefix
|
||||
.dir()
|
||||
.join(format!("{filename_prefix}{prefix}-{base}"))
|
||||
} else {
|
||||
artifact_prefix.dir().join(format!("{prefix}-{base}"))
|
||||
}
|
||||
} else {
|
||||
PathBuf::from(format!("{prefix}-{base}"))
|
||||
};
|
||||
let file_path = self.artifact_prefix.dir().join(format!(
|
||||
"{}{prefix}-{base}",
|
||||
self.artifact_prefix.filename_prefix()
|
||||
));
|
||||
*testcase.file_path_mut() = Some(file_path);
|
||||
}
|
||||
}
|
||||
|
@ -79,9 +79,11 @@ use libafl::{
|
||||
Error,
|
||||
};
|
||||
use libafl_bolts::AsSlice;
|
||||
use libc::_exit;
|
||||
|
||||
use crate::options::{LibfuzzerMode, LibfuzzerOptions};
|
||||
|
||||
mod corpus;
|
||||
mod feedbacks;
|
||||
mod fuzz;
|
||||
mod merge;
|
||||
@ -152,7 +154,7 @@ macro_rules! fuzz_with {
|
||||
AsSlice,
|
||||
};
|
||||
use libafl::{
|
||||
corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus},
|
||||
corpus::Corpus,
|
||||
executors::{ExitKind, InProcessExecutor, TimeoutExecutor},
|
||||
feedback_and_fast, feedback_not, feedback_or, feedback_or_fast,
|
||||
feedbacks::{ConstFeedback, CrashFeedback, MaxMapFeedback, NewHashFeedback, TimeFeedback, TimeoutFeedback},
|
||||
@ -179,6 +181,7 @@ macro_rules! fuzz_with {
|
||||
use std::{env::temp_dir, fs::create_dir, path::PathBuf};
|
||||
|
||||
use crate::{BACKTRACE, CustomMutationStatus};
|
||||
use crate::corpus::{ArtifactCorpus, LibfuzzerCorpus};
|
||||
use crate::feedbacks::{LibfuzzerCrashCauseFeedback, LibfuzzerKeepFeedback, ShrinkMapFeedback};
|
||||
use crate::misc::should_use_grimoire;
|
||||
use crate::observers::{MappedEdgeMapObserver, SizeValueObserver};
|
||||
@ -243,7 +246,7 @@ macro_rules! fuzz_with {
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let mut objective = feedback_or_fast!(
|
||||
LibfuzzerCrashCauseFeedback::new($options.artifact_prefix().cloned()),
|
||||
LibfuzzerCrashCauseFeedback::new($options.artifact_prefix().clone()),
|
||||
OomFeedback,
|
||||
feedback_and_fast!(
|
||||
CrashFeedback::new(),
|
||||
@ -269,13 +272,7 @@ macro_rules! fuzz_with {
|
||||
dir
|
||||
};
|
||||
|
||||
let crash_corpus = if let Some(prefix) = $options.artifact_prefix() {
|
||||
OnDiskCorpus::with_meta_format_and_prefix(prefix.dir(), None, prefix.filename_prefix().clone(), false)
|
||||
.unwrap()
|
||||
} else {
|
||||
OnDiskCorpus::with_meta_format_and_prefix(&std::env::current_dir().unwrap(), None, None, false)
|
||||
.unwrap()
|
||||
};
|
||||
let crash_corpus = ArtifactCorpus::new();
|
||||
|
||||
// If not restarting, create a State from scratch
|
||||
let mut state = state.unwrap_or_else(|| {
|
||||
@ -283,7 +280,7 @@ macro_rules! fuzz_with {
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
CachedOnDiskCorpus::with_meta_format_and_prefix(corpus_dir.clone(), 4096, None, None, true).unwrap(),
|
||||
LibfuzzerCorpus::new(corpus_dir.clone(), 4096),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
crash_corpus,
|
||||
@ -591,7 +588,21 @@ pub unsafe extern "C" fn LLVMFuzzerRunDriver(
|
||||
.unwrap();
|
||||
|
||||
if !options.unknown().is_empty() {
|
||||
println!("Unrecognised options: {:?}", options.unknown());
|
||||
eprintln!("Unrecognised options: {:?}", options.unknown());
|
||||
}
|
||||
|
||||
for folder in options
|
||||
.dirs()
|
||||
.iter()
|
||||
.chain(std::iter::once(options.artifact_prefix().dir()))
|
||||
{
|
||||
if !folder.try_exists().unwrap_or(false) {
|
||||
eprintln!(
|
||||
"Required folder {} did not exist; failing fast.",
|
||||
folder.to_string_lossy()
|
||||
);
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if *options.mode() != LibfuzzerMode::Tmin
|
||||
|
@ -8,7 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
use libafl::{
|
||||
corpus::{Corpus, OnDiskCorpus},
|
||||
corpus::Corpus,
|
||||
events::{EventRestarter, SimpleRestartingEventManager},
|
||||
executors::{ExitKind, InProcessExecutor, TimeoutExecutor},
|
||||
feedback_and_fast, feedback_or_fast,
|
||||
@ -29,6 +29,7 @@ use libafl_bolts::{
|
||||
use libafl_targets::{OomFeedback, OomObserver, COUNTERS_MAPS};
|
||||
|
||||
use crate::{
|
||||
corpus::{ArtifactCorpus, LibfuzzerCorpus},
|
||||
feedbacks::{LibfuzzerCrashCauseFeedback, LibfuzzerKeepFeedback},
|
||||
observers::{MappedEdgeMapObserver, SizeTimeValueObserver},
|
||||
options::LibfuzzerOptions,
|
||||
@ -44,23 +45,7 @@ pub fn merge(
|
||||
return Err(Error::illegal_argument("Missing corpora to minimize; you should provide one directory to minimize into and one-to-many from which the inputs are loaded."));
|
||||
}
|
||||
|
||||
let crash_corpus = if let Some(prefix) = options.artifact_prefix() {
|
||||
OnDiskCorpus::with_meta_format_and_prefix(
|
||||
prefix.dir(),
|
||||
None,
|
||||
prefix.filename_prefix().clone(),
|
||||
true,
|
||||
)
|
||||
.unwrap()
|
||||
} else {
|
||||
OnDiskCorpus::with_meta_format_and_prefix(
|
||||
&std::env::current_dir().unwrap(),
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
let crash_corpus = ArtifactCorpus::new();
|
||||
|
||||
let keep_observer = LibfuzzerKeepFeedback::new();
|
||||
let keep = keep_observer.keep();
|
||||
@ -130,7 +115,7 @@ pub fn merge(
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let mut objective = feedback_or_fast!(
|
||||
LibfuzzerCrashCauseFeedback::new(options.artifact_prefix().cloned()),
|
||||
LibfuzzerCrashCauseFeedback::new(options.artifact_prefix().clone()),
|
||||
OomFeedback,
|
||||
CrashFeedback::new(),
|
||||
TimeoutFeedback::new()
|
||||
@ -163,7 +148,7 @@ pub fn merge(
|
||||
// RNG
|
||||
StdRand::new(),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
OnDiskCorpus::with_meta_format_and_prefix(&corpus_dir, None, None, true).unwrap(),
|
||||
LibfuzzerCorpus::new(corpus_dir, 4096),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
crash_corpus,
|
||||
|
@ -2,6 +2,7 @@ use core::fmt::{Display, Formatter};
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
use libafl::mutators::Tokens;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::options::RawOption::{Directory, Flag};
|
||||
|
||||
@ -52,10 +53,10 @@ impl<'a> Display for OptionsParseError<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ArtifactPrefix {
|
||||
dir: PathBuf,
|
||||
filename_prefix: Option<String>,
|
||||
filename_prefix: String,
|
||||
}
|
||||
|
||||
impl ArtifactPrefix {
|
||||
@ -64,10 +65,10 @@ impl ArtifactPrefix {
|
||||
if path.ends_with(std::path::MAIN_SEPARATOR) {
|
||||
Self {
|
||||
dir,
|
||||
filename_prefix: None,
|
||||
filename_prefix: String::new(),
|
||||
}
|
||||
} else {
|
||||
let filename_prefix = dir.file_name().map(|s| {
|
||||
let filename_prefix = dir.file_name().map_or_else(String::new, |s| {
|
||||
s.to_os_string()
|
||||
.into_string()
|
||||
.expect("Provided artifact prefix is not usable")
|
||||
@ -84,17 +85,26 @@ impl ArtifactPrefix {
|
||||
&self.dir
|
||||
}
|
||||
|
||||
pub fn filename_prefix(&self) -> &Option<String> {
|
||||
pub fn filename_prefix(&self) -> &str {
|
||||
&self.filename_prefix
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ArtifactPrefix {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
dir: std::env::current_dir().expect("Must be able to get the current directory!"),
|
||||
filename_prefix: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct LibfuzzerOptions {
|
||||
fuzzer_name: String,
|
||||
mode: LibfuzzerMode,
|
||||
artifact_prefix: Option<ArtifactPrefix>,
|
||||
artifact_prefix: ArtifactPrefix,
|
||||
timeout: Duration,
|
||||
grimoire: Option<bool>,
|
||||
forks: Option<usize>,
|
||||
@ -140,8 +150,8 @@ impl LibfuzzerOptions {
|
||||
&self.mode
|
||||
}
|
||||
|
||||
pub fn artifact_prefix(&self) -> Option<&ArtifactPrefix> {
|
||||
self.artifact_prefix.as_ref()
|
||||
pub fn artifact_prefix(&self) -> &ArtifactPrefix {
|
||||
&self.artifact_prefix
|
||||
}
|
||||
|
||||
pub fn timeout(&self) -> Duration {
|
||||
@ -333,7 +343,10 @@ impl<'a> LibfuzzerOptionsBuilder<'a> {
|
||||
LibfuzzerOptions {
|
||||
fuzzer_name,
|
||||
mode: self.mode.unwrap_or(LibfuzzerMode::Fuzz),
|
||||
artifact_prefix: self.artifact_prefix.map(ArtifactPrefix::new),
|
||||
artifact_prefix: self
|
||||
.artifact_prefix
|
||||
.map(ArtifactPrefix::new)
|
||||
.unwrap_or_default(),
|
||||
timeout: self.timeout.unwrap_or(Duration::from_secs(1200)),
|
||||
grimoire: self.grimoire,
|
||||
forks: self.forks,
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::{
|
||||
ffi::c_int,
|
||||
fs::{read, write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use libafl::{
|
||||
@ -119,21 +118,10 @@ fn minimize_crash_with_mutator<M: Mutator<BytesInput, TMinState>>(
|
||||
options.dirs()[0].as_path().as_os_str().to_str().unwrap()
|
||||
);
|
||||
} else {
|
||||
let (mut dest, filename_prefix) = options.artifact_prefix().map_or_else(
|
||||
|| (PathBuf::default(), ""),
|
||||
|artifact_prefix| {
|
||||
(
|
||||
artifact_prefix.dir().clone(),
|
||||
artifact_prefix
|
||||
.filename_prefix()
|
||||
.as_ref()
|
||||
.map_or("", String::as_str),
|
||||
)
|
||||
},
|
||||
);
|
||||
let mut dest = options.artifact_prefix().dir().clone();
|
||||
dest.push(format!(
|
||||
"{}minimized-from-{}",
|
||||
filename_prefix,
|
||||
options.artifact_prefix().filename_prefix(),
|
||||
options.dirs()[0].file_name().unwrap().to_str().unwrap()
|
||||
));
|
||||
write(&dest, input)?;
|
||||
|
Loading…
x
Reference in New Issue
Block a user