Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full libfuzzer shimming (for cargo-fuzz libfuzzer alternative and other use cases) #981

Merged
merged 92 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
8568bb4
squash libfuzzer edits
addisoncrump May 4, 2023
1caf157
mergeup; make compat with libfuzzer-sys
addisoncrump May 9, 2023
8882d5e
fixup: compat with custom mutators
addisoncrump May 10, 2023
e95ff89
Merge branch 'main' of github.com:AFLplusplus/LibAFL into libfuzzer
addisoncrump May 10, 2023
2c5428a
use tui flag
addisoncrump May 10, 2023
8fd5474
add introspection support
addisoncrump May 10, 2023
3d2a4e9
use libfuzzer dep now that we've merged
addisoncrump May 12, 2023
016cadf
force input loading
addisoncrump Jun 6, 2023
2157790
some fixes
addisoncrump Jun 6, 2023
89ec678
mergeup
addisoncrump Jun 7, 2023
89ac90c
begin docs, impl shrink
addisoncrump Jun 7, 2023
629422b
make whole-archive conditional and not default
addisoncrump Jun 7, 2023
5286103
make more copies of counters maps
addisoncrump Jun 7, 2023
f652f77
lol, remember to add the observer
addisoncrump Jun 7, 2023
ce0be40
make size edge map observer an observer
addisoncrump Jun 7, 2023
a1d059d
fixup: make def of run driver conditional
addisoncrump Jun 8, 2023
856e969
add sanity checks for insertion
addisoncrump Jun 8, 2023
f87f2c4
revert silencing of forks
addisoncrump Jun 8, 2023
9d976e5
add experimental tmin support; add default asan flags
addisoncrump Jun 9, 2023
9d32627
Merge branch 'main' of github.com:AFLplusplus/LibAFL into libfuzzer
addisoncrump Jun 9, 2023
e51d3df
use default options instead of specifying our own
addisoncrump Jun 9, 2023
b750c3d
implement lockless mode
addisoncrump Jun 9, 2023
5711017
fix merge
addisoncrump Jun 9, 2023
ce33aad
fixup lockless corpus
addisoncrump Jun 10, 2023
807b1be
fixup for generalisation
addisoncrump Jun 10, 2023
25cc95b
Merge branch 'main' of github.com:AFLplusplus/LibAFL into libfuzzer
addisoncrump Jun 10, 2023
538f9fa
remove erroneous drop_in_place
addisoncrump Jun 14, 2023
ee12bd2
improve error logging in the case of corpus loading failure
addisoncrump Jun 14, 2023
874b426
ok, use lock files :pensive:
addisoncrump Jun 14, 2023
dc9cf00
fix tmin
addisoncrump Jun 16, 2023
f2778e1
implement merge (again); fix rare cases with maps being too small
addisoncrump Jun 16, 2023
580f8d0
implement a scheduler for removing excess
addisoncrump Jun 16, 2023
03afd7d
implement a walking strategy for corpus loading for large corpora
addisoncrump Jun 16, 2023
9f07169
revert filename parameter; rename and remove duplicates
addisoncrump Jun 17, 2023
0e742c7
Merge branch 'main' into libfuzzer
addisoncrump Jul 8, 2023
89735d1
various cleanup and clippy satisfaction
addisoncrump Jul 8, 2023
5a8c7c5
fix no_std tests
addisoncrump Jul 8, 2023
6d8e4ea
clang-format
addisoncrump Jul 8, 2023
4a06217
expand and satisfy the clippy gods
addisoncrump Jul 8, 2023
3af961a
fix sanitizer_ifaces bindgen for no_std
addisoncrump Jul 8, 2023
64facff
fix wasm fuzzer
addisoncrump Jul 8, 2023
9d2589d
fixup clippy script
addisoncrump Jul 14, 2023
f8a6142
rename and provide a small amount of explanation for sanitizer_interf…
addisoncrump Jul 14, 2023
86f29a9
mergeup
addisoncrump Jul 14, 2023
a45e04d
fixup: HasLastReportTime
addisoncrump Jul 15, 2023
3aad5e9
fix clippy oddities
addisoncrump Jul 20, 2023
5f6f8c9
restrict clippy checks to linux-only for libafl_libfuzzer_runtime
addisoncrump Jul 20, 2023
d3c32ce
Merge branch 'main' of github.com:AFLplusplus/LibAFL into libfuzzer
addisoncrump Jul 20, 2023
84a9d3a
Merge branch 'main' of github.com:AFLplusplus/LibAFL into libfuzzer
addisoncrump Aug 2, 2023
d4b0140
name the mutators
addisoncrump Aug 2, 2023
81f2020
format
addisoncrump Aug 2, 2023
067d7e4
fix clippy warning
addisoncrump Aug 2, 2023
7ed73a2
hope docker is fixed
addisoncrump Aug 2, 2023
bec635b
Merge branch 'main' into libfuzzer
domenukk Aug 2, 2023
51824bd
fix cmin lint
addisoncrump Aug 2, 2023
14287d0
clippy pass
domenukk Aug 2, 2023
80ba77a
more docs
domenukk Aug 2, 2023
e363c40
more clippy
domenukk Aug 2, 2023
a41cab1
Merge branch 'main' into libfuzzer
domenukk Aug 2, 2023
7c77e55
fix remaining clippy complaints
addisoncrump Aug 2, 2023
6b1c16c
fix import
addisoncrump Aug 2, 2023
381c834
miri fixes (no constructors executed)
addisoncrump Aug 2, 2023
537bd1a
exclude libafl_libfuzzer from cargo-hack
addisoncrump Aug 2, 2023
29fa6de
fix clippy check for sanitizer_interfaces
addisoncrump Aug 2, 2023
e23c81b
fmt
addisoncrump Aug 2, 2023
74f3c02
fix CI (?)
domenukk Aug 2, 2023
89143dd
Merge branch 'main' into libfuzzer
domenukk Aug 2, 2023
11165cf
Merge branch 'main' into libfuzzer
domenukk Aug 3, 2023
37a2175
Merge branch 'main' into libfuzzer
domenukk Aug 3, 2023
fd3bc07
deduplicate sancov 8bit for improved perf on ASAN
addisoncrump Aug 9, 2023
aaff794
merge 8bit coverage regions + comment out insane deduplication
addisoncrump Aug 9, 2023
368815c
no erroring out on free hooks
addisoncrump Aug 9, 2023
d49d8fc
fixup for non-forking merge
addisoncrump Aug 9, 2023
476200d
skip the corpus dir if we use it
addisoncrump Aug 9, 2023
6f0c7be
Merge branch 'main' of github.com:AFLplusplus/LibAFL into libfuzzer
addisoncrump Aug 22, 2023
d05916c
fixup: recent libafl changes and feature flags
addisoncrump Aug 22, 2023
69ace6c
libafl_libfuzzer: use rust-lld for whole-archive feature
Mrmaxmeier Aug 12, 2023
3e0118f
clarify cause of failure
addisoncrump Aug 22, 2023
dbea434
mark unsafe
addisoncrump Aug 22, 2023
ae4686c
clippy :cursed_cowboy:
addisoncrump Aug 22, 2023
d5b39e8
attempt to fix wasm
addisoncrump Aug 22, 2023
cf0e1ef
spooky unknowable bug :ghost:
addisoncrump Aug 22, 2023
ebe961d
more clippy lints
addisoncrump Aug 22, 2023
0122a03
clippy fix for merge
addisoncrump Aug 22, 2023
f6d225c
use the version pin
addisoncrump Aug 22, 2023
d5d654e
Merge branch 'main' into libfuzzer
domenukk Aug 23, 2023
b7e2752
add unsafe to ::register
domenukk Aug 23, 2023
9020704
Serdeany autoreg fix
domenukk Aug 23, 2023
ff23e2b
make type assert actionable
domenukk Aug 23, 2023
672137c
miri fixes
addisoncrump Aug 23, 2023
49718c8
Merge branch 'main' into libfuzzer
domenukk Aug 24, 2023
aefcb82
Merge branch 'main' into libfuzzer
domenukk Aug 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ perf.data.old
.vscode
test.dict

.idea/

# Ignore all built fuzzers
fuzzer_*
AFLplusplus
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"libafl_tinyinst",
"libafl_sugar",
"libafl_nyx",
"libafl_libfuzzer",
"libafl_concolic/symcc_runtime",
"libafl_concolic/symcc_libafl",
"libafl_concolic/test/dump_constraints",
Expand Down
7 changes: 5 additions & 2 deletions fuzzers/baby_fuzzer_wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
mod utils;

use libafl::{
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
bolts::{
current_nanos, rands::StdRand, serdeany::RegistryBuilder, tuples::tuple_list, AsSlice,
},
corpus::{Corpus, InMemoryCorpus},
events::SimpleEventManager,
executors::{ExitKind, InProcessExecutor},
feedbacks::{CrashFeedback, MaxMapFeedback},
feedbacks::{CrashFeedback, MapFeedbackMetadata, MaxMapFeedback},
generators::RandPrintablesGenerator,
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
Expand Down Expand Up @@ -36,6 +38,7 @@ pub extern "C" fn external_current_millis() -> u64 {
#[wasm_bindgen]
pub fn fuzz() {
set_panic_hook();
RegistryBuilder::register::<MapFeedbackMetadata<u8>>();

let mut signals = [0u8; 64];
let signals_ptr = signals.as_mut_ptr();
Expand Down
30 changes: 27 additions & 3 deletions libafl/src/bolts/serdeany.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,7 @@ macro_rules! create_serde_registry_for_trait {
where
T: $trait_name,
{
self.map
.insert(unpack_type_id(TypeId::of::<T>()), Box::new(t));
self.insert_boxed(Box::new(t));
}

/// Insert a boxed element into the map.
Expand All @@ -256,7 +255,20 @@ macro_rules! create_serde_registry_for_trait {
where
T: $trait_name,
{
self.map.insert(unpack_type_id(TypeId::of::<T>()), t);
let id = unpack_type_id(TypeId::of::<T>());
assert!(
domenukk marked this conversation as resolved.
Show resolved Hide resolved
unsafe {
REGISTRY
.deserializers
.as_ref()
.expect("Empty types registry")
.get(&id)
.is_some()
},
"type {} was inserted without registration!",
core::any::type_name::<T>()
);
self.map.insert(id, t);
}

/// Returns the count of elements in this map.
Expand Down Expand Up @@ -512,6 +524,18 @@ macro_rules! create_serde_registry_for_trait {
T: $trait_name,
{
let id = unpack_type_id(TypeId::of::<T>());
assert!(
unsafe {
REGISTRY
.deserializers
.as_ref()
.expect("Empty types registry")
.get(&id)
.is_some()
},
"type {} was inserted without registration!",
core::any::type_name::<T>()
);
if !self.map.contains_key(&id) {
self.map.insert(id, HashMap::default());
}
Expand Down
29 changes: 27 additions & 2 deletions libafl/src/corpus/cached.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! The [`CachedOnDiskCorpus`] stores [`Testcase`]s to disk, keeping a subset of them in memory/cache, evicting in a FIFO manner.

use alloc::collections::vec_deque::VecDeque;
use alloc::{collections::vec_deque::VecDeque, string::String};
use core::cell::RefCell;
use std::path::Path;

Expand Down Expand Up @@ -193,7 +193,7 @@ where
pub fn with_meta_format<P>(
dir_path: P,
cache_max_len: usize,
meta_format: OnDiskMetadataFormat,
meta_format: Option<OnDiskMetadataFormat>,
domenukk marked this conversation as resolved.
Show resolved Hide resolved
) -> Result<Self, Error>
where
P: AsRef<Path>,
Expand All @@ -204,6 +204,31 @@ where
)
}

/// Creates the [`CachedOnDiskCorpus`] specifying the metadata format and the prefix to prepend
/// to each testcase.
///
/// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`.
pub fn with_meta_format_and_prefix<P>(
dir_path: P,
cache_max_len: usize,
meta_format: Option<OnDiskMetadataFormat>,
prefix: Option<String>,
locking: bool,
) -> Result<Self, Error>
where
P: AsRef<Path>,
{
Self::_new(
InMemoryOnDiskCorpus::with_meta_format_and_prefix(
dir_path,
meta_format,
prefix,
locking,
)?,
cache_max_len,
)
}

/// Internal constructor `fn`
fn _new(on_disk_corpus: InMemoryOnDiskCorpus<I>, cache_max_len: usize) -> Result<Self, Error> {
if cache_max_len == 0 {
Expand Down
99 changes: 70 additions & 29 deletions libafl/src/corpus/inmemory_ondisk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ where
inner: InMemoryCorpus<I>,
dir_path: PathBuf,
meta_format: Option<OnDiskMetadataFormat>,
prefix: Option<String>,
locking: bool,
}

impl<I> UsesInput for InMemoryOnDiskCorpus<I>
Expand Down Expand Up @@ -209,20 +211,41 @@ where
where
P: AsRef<Path>,
{
Self::_new(dir_path.as_ref(), Some(OnDiskMetadataFormat::JsonPretty))
Self::_new(
dir_path.as_ref(),
Some(OnDiskMetadataFormat::JsonPretty),
None,
true,
)
}

/// Creates the [`InMemoryOnDiskCorpus`] specifying the format in which `Metadata` will be saved to disk.
///
/// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`.
pub fn with_meta_format<P>(
dir_path: P,
meta_format: OnDiskMetadataFormat,
meta_format: Option<OnDiskMetadataFormat>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think having an Option in an API is good design, maybe it's time for a builder?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is... 😔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now or later? :D

) -> Result<Self, Error>
where
P: AsRef<Path>,
{
Self::_new(dir_path.as_ref(), Some(meta_format))
Self::_new(dir_path.as_ref(), meta_format, None, true)
}

/// Creates the [`InMemoryOnDiskCorpus`] specifying the format in which `Metadata` will be saved to disk
/// and the prefix for the filenames.
///
/// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`.
pub fn with_meta_format_and_prefix<P>(
dir_path: P,
meta_format: Option<OnDiskMetadataFormat>,
prefix: Option<String>,
locking: bool,
) -> Result<Self, Error>
where
P: AsRef<Path>,
{
Self::_new(dir_path.as_ref(), meta_format, prefix, locking)
}

/// Creates an [`InMemoryOnDiskCorpus`] that will not store .metadata files
Expand All @@ -232,16 +255,27 @@ where
where
P: AsRef<Path>,
{
Self::_new(dir_path.as_ref(), None)
Self::_new(dir_path.as_ref(), None, None, true)
}

/// Private fn to crate a new corpus at the given (non-generic) path with the given optional `meta_format`
fn _new(dir_path: &Path, meta_format: Option<OnDiskMetadataFormat>) -> Result<Self, Error> {
fs::create_dir_all(dir_path)?;
fn _new(
dir_path: &Path,
meta_format: Option<OnDiskMetadataFormat>,
prefix: Option<String>,
locking: bool,
) -> Result<Self, Error> {
match fs::create_dir_all(dir_path) {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {}
Err(e) => return Err(e.into()),
}
Ok(InMemoryOnDiskCorpus {
inner: InMemoryCorpus::new(),
dir_path: dir_path.into(),
meta_format,
prefix,
locking,
})
}

Expand All @@ -265,19 +299,21 @@ where
return Ok(());
}

let new_lock_filename = format!(".{new_filename}.lafl_lock");
if self.locking {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would you not be locking? Are you using one folder per node?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you aren't forking, you probably don't want to lock.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fork as in multicore? Then I agree. Although you can also restart, you wouldn't have to fork :P

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does libafl_libfuzzer support a non-forking multiprocess mode btw? Might be relevant for some targets

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does libafl_libfuzzer support a non-forking multiprocess mode btw? Might be relevant for some targets

How do you mean? With e.g. threads instead of forks?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LibAFL launcher can start new processes instead of fork, yes

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, it should just use whatever's appropriate for the system. I don't specify fork/process explicitly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to specifically not use fork for some targets like browsers - they don't cope well with forking since all vital threads die in the forked process.

let new_lock_filename = format!(".{new_filename}.lafl_lock");

// Try to create lock file for new testcases
if OpenOptions::new()
.create(true)
.write(true)
.open(self.dir_path.join(new_lock_filename))
.is_err()
{
*testcase.filename_mut() = Some(old_filename);
return Err(Error::illegal_state(
"unable to create lock file for new testcase",
));
// Try to create lock file for new testcases
if OpenOptions::new()
.create(true)
.write(true)
.open(self.dir_path.join(new_lock_filename))
.is_err()
{
*testcase.filename_mut() = Some(old_filename);
return Err(Error::illegal_state(
"unable to create lock file for new testcase",
));
}
}

let new_file_path = self.dir_path.join(&new_filename);
Expand Down Expand Up @@ -311,18 +347,15 @@ where
fn save_testcase(&self, testcase: &mut Testcase<I>, idx: CorpusId) -> Result<(), Error> {
let file_name_orig = testcase.filename_mut().take().unwrap_or_else(|| {
// TODO walk entry metadata to ask for pieces of filename (e.g. :havoc in AFL)

testcase.input().as_ref().unwrap().generate_name(idx.0)
});
if testcase.file_path().is_some() {
// We already have a valid path, no need to do calculate anything
*testcase.filename_mut() = Some(file_name_orig);
} else {
// New testcase, we need to save it.
let mut file_name = file_name_orig.clone();

let mut ctr = 2;
let file_name = loop {
// New testcase, we need to save it.
let mut file_name = file_name_orig.clone();

let mut ctr = 2;
let file_name = if self.locking {
loop {
let lockfile_name = format!(".{file_name}.lafl_lock");
let lockfile_path = self.dir_path.join(lockfile_name);

Expand All @@ -337,11 +370,19 @@ where

file_name = format!("{file_name_orig}-{ctr}");
ctr += 1;
};
}
} else {
file_name
};

if testcase
.file_path()
.as_ref()
.map_or(true, |path| !path.starts_with(&self.dir_path))
{
*testcase.file_path_mut() = Some(self.dir_path.join(&file_name));
*testcase.filename_mut() = Some(file_name);
}
*testcase.filename_mut() = Some(file_name);

if self.meta_format.is_some() {
let metafile_name = format!(".{}.metadata", testcase.filename().as_ref().unwrap());
Expand Down
Loading