Skip to content

Commit

Permalink
Feature/libafl fuzz misc (AFLplusplus#2430)
Browse files Browse the repository at this point in the history
* libafl-fuzz: add cmplog to CI
libafl-fuzz: add option to specify custom rng sed
libafl-fuzz: add help messages to CLI, add file extension support
libafl-fuzz: adhere to AFL++ cmplog bin path format
libafl-fuzz: avoid races when writing to fuzzer_stats
libafl-fuzz: add time tracking for CalibrationStage, MutationalStage and SyncFromDiskStage

* libafl-fuzz: fix libafl paths

* libafl-fuzz: remove redundant cmplog check

* libafl-fuzz: ingore UnstableMapEntries when using queue scheduler in afl_stats.rs
libafl-fuzz: track max_depth for QueueScheduler
libafl-fuzz: fix custom input file

* libafl-fuzz: fix Makefile

* clippy
  • Loading branch information
R9295 authored Jul 22, 2024
1 parent 6951841 commit e6b94f3
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 72 deletions.
37 changes: 27 additions & 10 deletions fuzzers/others/libafl-fuzz/Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,34 @@ script_runner="@shell"
script='''
cargo build --profile ${PROFILE}
AFL_PATH=${AFL_DIR_NAME} ${AFL_CC_PATH} ./test/test-instr.c -o ./test/out-instr
AFL_CORES=1 AFL_STATS_INTERVAL=1 timeout 5 ${FUZZER} -i ./test/seeds -o ./test/output ./test/out-instr || true
test -n "$( ls ./test/output/fuzzer_main/queue/id:000002* 2>/dev/null )" || exit 1
test -n "$( ls ./test/output/fuzzer_main/fuzzer_stats 2>/dev/null )" || exit 1
test -n "$( ls ./test/output/fuzzer_main/plot_data 2>/dev/null )" || exit 1
test -d "./test/output/fuzzer_main/hangs" || exit 1
test -d "./test/output/fuzzer_main/crashes" || exit 1
LIBAFL_DEBUG_OUTPUT=1 AFL_CORES=1 AFL_STATS_INTERVAL=1 timeout 5 ${FUZZER} -i ./test/seeds -o ./test/output ./test/out-instr || true
test -n "$( ls ./test/output/fuzzer_main/queue/id:000002* 2>/dev/null )" || {
echo "No new corpus entries found"
exit 1
}
test -n "$( ls ./test/output/fuzzer_main/fuzzer_stats 2>/dev/null )" || {
echo "No fuzzer_stats file found"
exit 1
}
test -n "$( ls ./test/output/fuzzer_main/plot_data 2>/dev/null )" || {
echo "No plot_data found"
exit 1
}
test -d "./test/output/fuzzer_main/hangs" || {
echo "No hangs directory found"
exit 1
}
test -d "./test/output/fuzzer_main/crashes" || {
echo "No crashes directory found"
exit 1
}
# cmplog TODO: AFL_BENCH_UNTIL_CRASH=1 instead of timeout 15s
#AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR_NAME} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog
#AFL_CORES=1 timeout 15 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/cmplog-output -c ./test/out-cmplog ./test/out-cmplog >>errors 2>&1
#test -n "$( ls ./test/cmplog-output/fuzzer_main/crashes/id:000000* ./test/cmplog-output/hangs/id:000000* 2>/dev/null )" || exit 1
AFL_LLVM_CMPLOG=1 AFL_PATH=${AFL_DIR_NAME} ${AFL_CC_PATH} ./test/test-cmplog.c -o ./test/out-cmplog
AFL_CORES=1 timeout 15 ${FUZZER} -Z -l 3 -m 0 -V30 -i ./test/seeds_cmplog -o ./test/cmplog-output -c 0 ./test/out-cmplog >>errors 2>&1
test -n "$( ls ./test/cmplog-output/fuzzer_main/crashes/id:000000* ./test/cmplog-output/hangs/id:000000* 2>/dev/null )" || {
echo "no crashes found when running cmplog"
exit 1
}
'''
dependencies = ["build_afl"]

Expand Down
6 changes: 3 additions & 3 deletions fuzzers/others/libafl-fuzz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ Rewrite of afl-fuzz in Rust.
- [x] AFL_FUZZER_STATS_UPDATE_INTERVAL
- [x] AFL_DEFER_FORKSRV
- [x] AFL_NO_WARN_INSTABILITY (we don't warn anyways, we should maybe?)
- [x] AFL_IMPORT_FIRST (implicit)
- [x] AFL_SYNC_TIME
- [ ] AFL_FINAL_SYNC
- [x] AFL_AUTORESUME
- [x] AFL_PERSISTENT_RECORD
- [ ] AFL_FINAL_SYNC
- [ ] AFL_CRASHING_SEEDS_AS_NEW_CRASH
- [ ] AFL_IGNORE_UNKNOWN_ENVS
- [ ] AFL_NO_UI
Expand All @@ -44,7 +46,6 @@ Rewrite of afl-fuzz in Rust.
- [ ] AFL_FAST_CAL
- [ ] AFL_NO_CRASH_README
- [ ] AFL_KEEP_TIMEOUTS
- [x] AFL_PERSISTENT_RECORD
- [ ] AFL_TESTCACHE_SIZE
- [ ] AFL_NO_ARITH
- [ ] AFL_DISABLE_TRIM
Expand All @@ -56,7 +57,6 @@ Rewrite of afl-fuzz in Rust.
- [ ] AFL_STATSD_PORT
- [ ] AFL_STATSD_HOST
- [ ] AFL_IMPORT
- [x] AFL_IMPORT_FIRST (implicit)
- [ ] AFL_SHUFFLE_QUEUE
- [ ] AFL_CUSTOM_QEMU_BIN
- [ ] AFL_PATH
Expand Down
112 changes: 87 additions & 25 deletions fuzzers/others/libafl-fuzz/src/afl_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,47 @@ use libafl::{
events::EventFirer,
executors::HasObservers,
inputs::UsesInput,
mutators::Tokens,
observers::MapObserver,
schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles, Scheduler},
stages::{calibrate::UnstableEntriesMetadata, Stage},
state::{HasCorpus, HasExecutions, HasImported, HasStartTime, Stoppable, UsesState},
Error, HasMetadata, HasNamedMetadata, HasScheduler,
Error, HasMetadata, HasNamedMetadata, HasScheduler, SerdeAny,
};
use libafl_bolts::{
core_affinity::CoreId,
current_time,
os::peak_rss_mb_child_processes,
tuples::{Handle, Handled, MatchNameRef},
Named,
};
use serde::{Deserialize, Serialize};

use crate::{fuzzer::fuzzer_target_mode, Opt};

#[derive(Debug, SerdeAny, Serialize, Deserialize)]
pub struct CalibrationTime(pub Duration);
impl From<Duration> for CalibrationTime {
fn from(value: Duration) -> Self {
Self(value)
}
}
#[derive(Debug, SerdeAny, Serialize, Deserialize)]
pub struct SyncTime(pub Duration);
impl From<Duration> for SyncTime {
fn from(value: Duration) -> Self {
Self(value)
}
}

#[derive(Debug, SerdeAny, Serialize, Deserialize)]
pub struct FuzzTime(pub Duration);
impl From<Duration> for FuzzTime {
fn from(value: Duration) -> Self {
Self(value)
}
}

/// The [`AflStatsStage`] is a Stage that calculates and writes
/// AFL++'s `fuzzer_stats` and `plot_data` information.
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -63,6 +89,12 @@ pub struct AflStatsStage<C, O, E, EM, Z> {
target_mode: Cow<'static, str>,
/// full command line used for the fuzzing session
command_line: Cow<'static, str>,
/// Amount of tokens provided by the user. Used to determine autotokens count.
provided_tokens: usize,
/// autotokens are enabled
autotokens_enabled: bool,
/// The core we are bound to
core_id: CoreId,
phantom: PhantomData<(C, O, E, EM, Z)>,
}

Expand All @@ -82,11 +114,12 @@ pub struct AFLFuzzerStats<'a> {
cycles_wo_find: u64,
/// longest time in seconds no new path was found
time_wo_finds: u64,
/// TODO
/// Time spent fuzzing
fuzz_time: u64,
/// TODO
/// Time spent calibrating inputs
calibration_time: u64,
/// TODO
/// Time spent syncing with foreign fuzzers
/// NOTE: Syncing between our own instances is not counted.
sync_time: u64,
/// TODO
trim_time: u64,
Expand Down Expand Up @@ -137,16 +170,16 @@ pub struct AFLFuzzerStats<'a> {
/// max rss usage reached during fuzzing in MB
peak_rss_mb: i64,
/// TODO
cpu_affinity: i64,
cpu_affinity: usize,
/// how many edges have been found
edges_found: u64,
/// TODO:
/// Size of our edges map
total_edges: u64,
/// how many edges are non-deterministic
var_byte_count: usize,
/// TODO:
havoc_expansion: usize,
/// TODO:
/// Amount of automatic dict entries found
auto_dict_entries: usize,
/// TODO:
testcache_size: usize,
Expand Down Expand Up @@ -258,24 +291,40 @@ where
.as_ref();
let filled_entries_in_map = map_observer.count_bytes();
let map_size = map_observer.usable_count();

let unstable_entries_metadata = state
// Since we do not calibrate when using `QueueScheduler`; we cannot calculate unstable entries.
let unstable_entries_in_map = state
.metadata_map()
.get::<UnstableEntriesMetadata>()
.unwrap();
let unstable_entries_in_map = unstable_entries_metadata.unstable_entries().len();
.map_or(0, |m| m.unstable_entries().len());

let auto_dict_entries = if self.autotokens_enabled {
state
.metadata::<Tokens>()?
.len()
.saturating_sub(self.provided_tokens)
} else {
0
};
let stats = AFLFuzzerStats {
start_time: self.start_time,
last_update: self.last_report_time.as_secs(),
run_time: self.last_report_time.as_secs() - self.start_time,
fuzzer_pid: self.pid,
cycles_done: queue_cycles,
cycles_wo_find: self.cycles_wo_finds,
fuzz_time: 0, // TODO
calibration_time: 0, // TODO
sync_time: 0, // TODO
trim_time: 0, // TODO
fuzz_time: state
.metadata::<FuzzTime>()
.map_or(Duration::from_secs(0), |d| d.0)
.as_secs(),
calibration_time: state
.metadata::<CalibrationTime>()
.map_or(Duration::from_secs(0), |d| d.0)
.as_secs(),
sync_time: state
.metadata::<SyncTime>()
.map_or(Duration::from_secs(0), |d| d.0)
.as_secs(),
trim_time: 0, // TODO
execs_done: total_executions,
execs_per_sec: *state.executions(), // TODO
execs_ps_last_min: *state.executions(), // TODO
Expand All @@ -298,15 +347,15 @@ where
last_hang: self.last_hang,
last_crash: self.last_crash,
execs_since_crash: total_executions - self.execs_at_last_objective,
exec_timeout: self.exec_timeout, // TODO
exec_timeout: self.exec_timeout,
slowest_exec_ms: self.slowest_exec.as_millis(),
peak_rss_mb: peak_rss_mb_child_processes()?,
cpu_affinity: 0, // TODO
cpu_affinity: self.core_id.0,
total_edges: map_size as u64,
edges_found: filled_entries_in_map,
var_byte_count: unstable_entries_metadata.unstable_entries().len(),
havoc_expansion: 0, // TODO
auto_dict_entries: 0, // TODO
var_byte_count: unstable_entries_in_map,
havoc_expansion: 0, // TODO
auto_dict_entries,
testcache_size: 0,
testcache_count: 0,
testcache_evict: 0,
Expand Down Expand Up @@ -354,7 +403,14 @@ where
/// create a new instance of the [`AflStatsStage`]
#[allow(clippy::too_many_arguments)]
#[must_use]
pub fn new(opt: &Opt, fuzzer_dir: PathBuf, map_observer: &C) -> Self {
pub fn new(
opt: &Opt,
fuzzer_dir: PathBuf,
map_observer: &C,
provided_tokens: usize,
autotokens_enabled: bool,
core_id: CoreId,
) -> Self {
Self::create_plot_data_file(&fuzzer_dir).unwrap();
Self::create_fuzzer_stats_file(&fuzzer_dir).unwrap();
Self {
Expand All @@ -381,6 +437,9 @@ where
afl_version: Cow::Borrowed("libafl-fuzz-0.0.1"),
command_line: get_run_cmdline(),
fuzzer_dir,
provided_tokens,
core_id,
autotokens_enabled,
phantom: PhantomData,
}
}
Expand Down Expand Up @@ -408,14 +467,17 @@ where
}

fn write_fuzzer_stats(&self, stats: &AFLFuzzerStats) -> Result<(), Error> {
std::fs::write(self.fuzzer_dir.join("fuzzer_stats"), stats.to_string())?;
let tmp_file = self.fuzzer_dir.join(".fuzzer_stats_tmp");
let stats_file = self.fuzzer_dir.join("fuzzer_stats");
std::fs::write(&tmp_file, stats.to_string())?;
std::fs::copy(&tmp_file, &stats_file)?;
std::fs::remove_file(tmp_file)?;
Ok(())
}

fn write_plot_data(&self, plot_data: &AFLPlotData) -> Result<(), Error> {
let mut file = OpenOptions::new()
.append(true)
.open(self.fuzzer_dir.join("plot_data"))?;
let plot_file = self.fuzzer_dir.join("plot_data");
let mut file = OpenOptions::new().append(true).open(&plot_file)?;
writeln!(file, "{plot_data}")?;
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion fuzzers/others/libafl-fuzz/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub fn check_binary(opt: &mut Opt, shmem_env_var: &str) -> Result<(), Error> {
)));
}
if opt.skip_bin_check
|| opt.use_wine
|| opt.wine_mode
|| opt.unicorn_mode
|| (opt.qemu_mode && opt.qemu_custom_bin)
|| (opt.forkserver_cs && opt.cs_custom_bin)
Expand Down
4 changes: 2 additions & 2 deletions fuzzers/others/libafl-fuzz/src/feedback/persistent_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ where
{
fn create_feedback(&self, _ctx: &T) -> PersitentRecordFeedback<I, S> {
Self {
record_size: self.record_size.clone(),
record_size: self.record_size,
record: self.record.clone(),
phantomm: self.phantomm,
}
Expand Down Expand Up @@ -156,6 +156,6 @@ where
S: State<Input = I>,
{
fn should_run(&self) -> bool {
return self.record_size > 0;
self.record_size > 0
}
}
Loading

0 comments on commit e6b94f3

Please sign in to comment.