#![recursion_limit = "1024"]
extern crate phink_lib;
pub mod shared;
#[cfg(test)]
mod tests {
use super::*;
use crate::shared::{
ensure_while_fuzzing,
get_corpus_files,
instrument,
is_compiled,
is_instrumented,
samples::Sample,
with_modified_phink_config,
};
use anyhow::{
ensure,
Result,
};
use phink_lib::{
cli::config::Configuration,
instrumenter::path::InstrumentedPath,
};
use phink_lib::cli::ui::{
monitor::logs::AFLDashboard,
traits::FromPath,
};
use std::{
fs,
time::Duration,
};
use tempfile::tempdir;
#[test]
#[cfg_attr(target_os = "macos", ignore)]
fn test_fuzz_find_crash_before_three_minutes() -> Result<()> {
let fuzz_output = tempdir()?.into_path();
let config = Configuration {
instrumented_contract_path: Some(InstrumentedPath::from(tempdir()?.into_path())),
fuzz_output: Some(fuzz_output.clone()),
cores: Some(15),
show_ui: false,
verbose: false,
..Default::default()
};
const TIMEOUT: u64 = 180;
with_modified_phink_config(&config, || {
let _ = instrument(Sample::Dummy);
let fuzzing = ensure_while_fuzzing(&config, Duration::from_secs(TIMEOUT), || {
let fuzz_created = fs::metadata(fuzz_output.clone()).is_ok();
ensure!(
fuzz_created,
format!("Fuzz output directory wasn't created ({:?})", fuzz_output)
);
let dashboard = AFLDashboard::from_output(fuzz_output.clone())?;
if fuzz_created && dashboard.is_ready() {
let props = dashboard.read_properties()?;
ensure!(
props.crashed(),
"No crash detected within the {TIMEOUT} seconds, this should crash easily"
);
}
Ok(())
});
ensure!(
fuzzing.is_ok(),
"ensure_while_fuzzing returned an error: {:?}",
fuzzing.unwrap_err()
);
Ok(())
})
}
#[test]
fn test_fuzz_assert_output_created_when_fuzzing() -> Result<()> {
let fuzz_output = tempdir()?.into_path();
let instrumented_output = tempdir()?.into_path();
let config = Configuration {
instrumented_contract_path: Some(InstrumentedPath::from(instrumented_output)),
fuzz_output: Some(fuzz_output.clone()),
cores: Some(1),
show_ui: false,
verbose: true,
..Default::default()
};
assert!(fuzz_output.exists(), "output should be created by TempDir");
println!("OUT at {:?}", fuzz_output);
with_modified_phink_config(&config, || {
let _ = instrument(Sample::Dummy);
let mut initial_corpus_len = 0_usize;
let phink_output = fuzz_output.join("phink");
let fuzzing = ensure_while_fuzzing(
&config,
Duration::from_secs(60 * 4 ),
|| {
let fuzz_created = phink_output.exists();
ensure!(
fuzz_created,
"Fuzz output directory wasn't created ({fuzz_output:?})"
);
if fuzz_created {
let corp_dir = &phink_output.join("corpus");
println!("Maybe corpus dir at {:?}", corp_dir);
let corpus_res = get_corpus_files(corp_dir);
initial_corpus_len = match corpus_res {
Ok(_) => corpus_res.iter().len(),
_ => 0,
};
let path_contract = &config
.clone()
.instrumented_contract_path
.unwrap_or_default()
.path;
ensure!(is_instrumented(path_contract), "Dummy wasn't instrumented ");
ensure!(is_compiled(path_contract), "Dummy wasn't compiled properly");
ensure!(initial_corpus_len > 0,"Corpus directory is empty after creation: {initial_corpus_len:?} files");
let selector = phink_output.join("selectors.dict");
ensure!(
selector.exists(),
"selectors.dict doesn't exist ({selector:?})"
);
ensure!(
fs::read_to_string(selector).unwrap().lines().count() > 1,
"at least two entries inside the dict"
);
let dash = AFLDashboard::from_output(fuzz_output.clone())?;
ensure!(dash.is_ready(), "afl.log' didn't return a successfull dashboard, maybe there was an AFL++ bug ? The latest AFL log was: {dash:#?}");
if cfg!(not(target_os = "macos")) {
let allowlist = phink_output.join("allowlist.txt");
ensure!(allowlist.exists(), "allowlist.txt for AFL doesn't exist");
ensure!(
fs::read_to_string(allowlist).unwrap().lines().count() > 0,
"at least two entries inside the dict"
);
}
}
Ok(())
},
);
ensure!(
fuzzing.is_ok(),
"ensure_while_fuzzing returned an error: {:?}",
fuzzing.unwrap_err()
);
let corpus_len = get_corpus_files(&phink_output.join("corpus"))
.unwrap()
.len();
println!("After a bit of fuzzing, we get {corpus_len} corpus entries, when we had {initial_corpus_len} entries at the begining");
ensure!(
corpus_len > initial_corpus_len,
"There was no new corpus while fuzzing"
);
Ok(())
})
}
#[test]
fn test_fuzz_two_cores_work() -> Result<()> {
let fuzz_output = tempdir()?.into_path();
let config = Configuration {
instrumented_contract_path: Some(InstrumentedPath::from(tempdir()?.into_path())),
fuzz_output: Some(fuzz_output.clone()),
cores: Some(2),
show_ui: false,
verbose: false,
..Default::default()
};
with_modified_phink_config(&config, || {
let _ = instrument(Sample::Dummy);
let fuzzing = ensure_while_fuzzing(&config, Duration::from_secs(120), || {
let fuzz_created = fs::metadata(fuzz_output.clone()).is_ok();
ensure!(
fuzz_created,
format!("Fuzz output directory wasn't created ({:?})", fuzz_output)
);
if fuzz_created {
let log_path = config
.fuzz_output
.clone()
.unwrap_or_default()
.join("phink")
.join("logs");
ensure!(log_path.join("afl.log").exists(), "afl.log wasn't created",);
ensure!(
log_path.join("afl_1.log").exists(),
"afl_1.log wasn't created, even though we have 2 cores ! ",
);
}
Ok(())
});
ensure!(
fuzzing.is_ok(),
"ensure_while_fuzzing returned an error: {:?}",
fuzzing.unwrap_err()
);
Ok(())
})
}
}