mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-18 04:38:09 +00:00
verify libfuzzer targets at the start of the task (#752)
This commit is contained in:
@ -170,7 +170,15 @@ pub fn get_synced_dirs(
|
|||||||
fn register_cleanup(job_id: Uuid) -> Result<()> {
|
fn register_cleanup(job_id: Uuid) -> Result<()> {
|
||||||
let path = std::env::current_dir()?.join(job_id.to_string());
|
let path = std::env::current_dir()?.join(job_id.to_string());
|
||||||
atexit::register(move || {
|
atexit::register(move || {
|
||||||
remove_dir_all(&path).expect("cleanup failed");
|
// only cleaing up if the path exists upon exit
|
||||||
|
if std::fs::metadata(&path).is_ok() {
|
||||||
|
let result = remove_dir_all(&path);
|
||||||
|
|
||||||
|
// don't panic if the remove failed but the path is gone
|
||||||
|
if result.is_err() && std::fs::metadata(&path).is_ok() {
|
||||||
|
result.expect("cleanup failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -38,10 +38,9 @@ pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
|||||||
.expect("invalid crash dir remote location");
|
.expect("invalid crash dir remote location");
|
||||||
|
|
||||||
let fuzzer = LibFuzzerFuzzTask::new(fuzz_config)?;
|
let fuzzer = LibFuzzerFuzzTask::new(fuzz_config)?;
|
||||||
fuzzer.check_libfuzzer().await?;
|
|
||||||
let mut task_handles = vec![];
|
let mut task_handles = vec![];
|
||||||
|
|
||||||
let fuzz_task = spawn(async move { fuzzer.managed_run().await });
|
let fuzz_task = spawn(async move { fuzzer.run().await });
|
||||||
|
|
||||||
wait_for_dir(&crash_dir).await?;
|
wait_for_dir(&crash_dir).await?;
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ impl Config {
|
|||||||
match self {
|
match self {
|
||||||
Config::LibFuzzerFuzz(config) => {
|
Config::LibFuzzerFuzz(config) => {
|
||||||
fuzz::libfuzzer_fuzz::LibFuzzerFuzzTask::new(config)?
|
fuzz::libfuzzer_fuzz::LibFuzzerFuzzTask::new(config)?
|
||||||
.managed_run()
|
.run()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Config::LibFuzzerReport(config) => {
|
Config::LibFuzzerReport(config) => {
|
||||||
|
@ -93,22 +93,19 @@ impl CoverageTask {
|
|||||||
Self { config, poller }
|
Self { config, poller }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_libfuzzer(&self) -> Result<()> {
|
pub async fn verify(&self) -> Result<()> {
|
||||||
if self.config.check_fuzzer_help {
|
|
||||||
let fuzzer = LibFuzzer::new(
|
let fuzzer = LibFuzzer::new(
|
||||||
&self.config.target_exe,
|
&self.config.target_exe,
|
||||||
&self.config.target_options,
|
&self.config.target_options,
|
||||||
&self.config.target_env,
|
&self.config.target_env,
|
||||||
&self.config.common.setup_dir,
|
&self.config.common.setup_dir,
|
||||||
);
|
);
|
||||||
fuzzer.check_help().await?;
|
fuzzer.verify(self.config.check_fuzzer_help, None).await
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn managed_run(&mut self) -> Result<()> {
|
pub async fn managed_run(&mut self) -> Result<()> {
|
||||||
info!("starting libFuzzer coverage task");
|
info!("starting libFuzzer coverage task");
|
||||||
self.check_libfuzzer().await?;
|
self.verify().await?;
|
||||||
self.config.coverage.init_pull().await?;
|
self.config.coverage.init_pull().await?;
|
||||||
self.process().await
|
self.process().await
|
||||||
}
|
}
|
||||||
|
@ -79,13 +79,9 @@ impl LibFuzzerFuzzTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn managed_run(&self) -> Result<()> {
|
|
||||||
self.check_libfuzzer().await?;
|
|
||||||
self.run().await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run(&self) -> Result<()> {
|
pub async fn run(&self) -> Result<()> {
|
||||||
self.init_directories().await?;
|
self.init_directories().await?;
|
||||||
|
self.verify().await?;
|
||||||
|
|
||||||
let hb_client = self.config.common.init_heartbeat().await?;
|
let hb_client = self.config.common.init_heartbeat().await?;
|
||||||
|
|
||||||
@ -102,17 +98,22 @@ impl LibFuzzerFuzzTask {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_libfuzzer(&self) -> Result<()> {
|
pub async fn verify(&self) -> Result<()> {
|
||||||
if self.config.check_fuzzer_help {
|
let mut directories = vec![self.config.inputs.path.clone()];
|
||||||
|
if let Some(readonly_inputs) = &self.config.readonly_inputs {
|
||||||
|
let mut dirs = readonly_inputs.iter().map(|x| x.path.clone()).collect();
|
||||||
|
directories.append(&mut dirs);
|
||||||
|
}
|
||||||
|
|
||||||
let fuzzer = LibFuzzer::new(
|
let fuzzer = LibFuzzer::new(
|
||||||
&self.config.target_exe,
|
&self.config.target_exe,
|
||||||
&self.config.target_options,
|
&self.config.target_options,
|
||||||
&self.config.target_env,
|
&self.config.target_env,
|
||||||
&self.config.common.setup_dir,
|
&self.config.common.setup_dir,
|
||||||
);
|
);
|
||||||
fuzzer.check_help().await?;
|
fuzzer
|
||||||
}
|
.verify(self.config.check_fuzzer_help, Some(directories))
|
||||||
Ok(())
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_fuzzers(&self, stats_sender: Option<&StatsSender>) -> Result<()> {
|
pub async fn run_fuzzers(&self, stats_sender: Option<&StatsSender>) -> Result<()> {
|
||||||
@ -256,11 +257,11 @@ impl LibFuzzerFuzzTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn init_directories(&self) -> Result<()> {
|
async fn init_directories(&self) -> Result<()> {
|
||||||
self.config.inputs.init().await?;
|
self.config.inputs.init_pull().await?;
|
||||||
self.config.crashes.init().await?;
|
self.config.crashes.init().await?;
|
||||||
if let Some(readonly_inputs) = &self.config.readonly_inputs {
|
if let Some(readonly_inputs) = &self.config.readonly_inputs {
|
||||||
for dir in readonly_inputs {
|
for dir in readonly_inputs {
|
||||||
dir.init().await?;
|
dir.init_pull().await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -47,15 +47,13 @@ pub struct Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn spawn(config: Arc<Config>) -> Result<()> {
|
pub async fn spawn(config: Arc<Config>) -> Result<()> {
|
||||||
if config.check_fuzzer_help {
|
let fuzzer = LibFuzzer::new(
|
||||||
let target = LibFuzzer::new(
|
|
||||||
&config.target_exe,
|
&config.target_exe,
|
||||||
&config.target_options,
|
&config.target_options,
|
||||||
&config.target_env,
|
&config.target_env,
|
||||||
&config.common.setup_dir,
|
&config.common.setup_dir,
|
||||||
);
|
);
|
||||||
target.check_help().await?;
|
fuzzer.verify(config.check_fuzzer_help, None).await?;
|
||||||
}
|
|
||||||
|
|
||||||
config.unique_inputs.init().await?;
|
config.unique_inputs.init().await?;
|
||||||
if let Some(queue) = config.input_queue.clone() {
|
if let Some(queue) = config.input_queue.clone() {
|
||||||
|
@ -62,8 +62,19 @@ impl ReportTask {
|
|||||||
Self { config, poller }
|
Self { config, poller }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn verify(&self) -> Result<()> {
|
||||||
|
let fuzzer = LibFuzzer::new(
|
||||||
|
&self.config.target_exe,
|
||||||
|
&self.config.target_options,
|
||||||
|
&self.config.target_env,
|
||||||
|
&self.config.common.setup_dir,
|
||||||
|
);
|
||||||
|
fuzzer.verify(self.config.check_fuzzer_help, None).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn managed_run(&mut self) -> Result<()> {
|
pub async fn managed_run(&mut self) -> Result<()> {
|
||||||
info!("Starting libFuzzer crash report task");
|
info!("Starting libFuzzer crash report task");
|
||||||
|
self.verify().await?;
|
||||||
|
|
||||||
if let Some(unique_reports) = &self.config.unique_reports {
|
if let Some(unique_reports) = &self.config.unique_reports {
|
||||||
unique_reports.init().await?;
|
unique_reports.init().await?;
|
||||||
|
@ -4,15 +4,19 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
env::{get_path_with_directory, LD_LIBRARY_PATH, PATH},
|
env::{get_path_with_directory, LD_LIBRARY_PATH, PATH},
|
||||||
expand::Expand,
|
expand::Expand,
|
||||||
|
fs::{list_files, write_file},
|
||||||
input_tester::{TestResult, Tester},
|
input_tester::{TestResult, Tester},
|
||||||
};
|
};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::thread_rng;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::Stdio,
|
process::Stdio,
|
||||||
};
|
};
|
||||||
|
use tempfile::tempdir;
|
||||||
use tokio::process::{Child, Command};
|
use tokio::process::{Child, Command};
|
||||||
|
|
||||||
const DEFAULT_MAX_TOTAL_SECONDS: i32 = 10 * 60;
|
const DEFAULT_MAX_TOTAL_SECONDS: i32 = 10 * 60;
|
||||||
@ -45,18 +49,20 @@ impl<'a> LibFuzzer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_help(&self) -> Result<()> {
|
fn build_command(
|
||||||
// Verify -help=1 exits with a zero return code, which validates the
|
&self,
|
||||||
// libfuzzer works as we expect.
|
fault_dir: Option<&Path>,
|
||||||
|
corpus_dir: Option<&Path>,
|
||||||
|
extra_corpus_dirs: Option<&[&Path]>,
|
||||||
|
) -> Result<Command> {
|
||||||
let mut cmd = Command::new(&self.exe);
|
let mut cmd = Command::new(&self.exe);
|
||||||
|
|
||||||
cmd.kill_on_drop(true)
|
cmd.kill_on_drop(true)
|
||||||
.env(PATH, get_path_with_directory(PATH, &self.setup_dir)?)
|
.env(PATH, get_path_with_directory(PATH, &self.setup_dir)?)
|
||||||
.env_remove("RUST_LOG")
|
.env_remove("RUST_LOG")
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::piped())
|
.stderr(Stdio::piped())
|
||||||
.arg("-help=1");
|
.arg("-workers=1");
|
||||||
|
|
||||||
if cfg!(target_family = "unix") {
|
if cfg!(target_family = "unix") {
|
||||||
cmd.env(
|
cmd.env(
|
||||||
@ -68,7 +74,11 @@ impl<'a> LibFuzzer<'a> {
|
|||||||
let expand = Expand::new()
|
let expand = Expand::new()
|
||||||
.target_exe(&self.exe)
|
.target_exe(&self.exe)
|
||||||
.target_options(&self.options)
|
.target_options(&self.options)
|
||||||
.setup_dir(&self.setup_dir);
|
.setup_dir(&self.setup_dir)
|
||||||
|
.set_optional(corpus_dir, |tester, corpus_dir| {
|
||||||
|
tester.input_corpus(&corpus_dir)
|
||||||
|
})
|
||||||
|
.set_optional(fault_dir, |tester, fault_dir| tester.crashes(&fault_dir));
|
||||||
|
|
||||||
for (k, v) in self.env {
|
for (k, v) in self.env {
|
||||||
cmd.env(k, expand.evaluate_value(v)?);
|
cmd.env(k, expand.evaluate_value(v)?);
|
||||||
@ -79,6 +89,103 @@ impl<'a> LibFuzzer<'a> {
|
|||||||
cmd.arg(o);
|
cmd.arg(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the read/written main corpus directory.
|
||||||
|
if let Some(corpus_dir) = corpus_dir {
|
||||||
|
cmd.arg(corpus_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set extra corpus directories that will be periodically rescanned.
|
||||||
|
if let Some(extra_corpus_dirs) = extra_corpus_dirs {
|
||||||
|
for dir in extra_corpus_dirs {
|
||||||
|
cmd.arg(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a max_time is already set
|
||||||
|
if self
|
||||||
|
.options
|
||||||
|
.iter()
|
||||||
|
.find(|o| o.starts_with("-max_total_time"))
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
cmd.arg(format!("-max_total_time={}", DEFAULT_MAX_TOTAL_SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn verify(
|
||||||
|
&self,
|
||||||
|
check_fuzzer_help: bool,
|
||||||
|
inputs: Option<Vec<PathBuf>>,
|
||||||
|
) -> Result<()> {
|
||||||
|
if check_fuzzer_help {
|
||||||
|
self.check_help().await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut seen_inputs = false;
|
||||||
|
|
||||||
|
if let Some(inputs) = inputs {
|
||||||
|
// check the 5 files at random from the input directories
|
||||||
|
for input_dir in inputs {
|
||||||
|
if tokio::fs::metadata(&input_dir).await.is_ok() {
|
||||||
|
let mut files = list_files(&input_dir).await?;
|
||||||
|
{
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
files.shuffle(&mut rng);
|
||||||
|
}
|
||||||
|
for file in files.iter().take(5) {
|
||||||
|
self.check_input(&file).await.with_context(|| {
|
||||||
|
format!("checking input corpus: {}", file.display())
|
||||||
|
})?;
|
||||||
|
seen_inputs = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("input dir doesn't exist: {:?}", input_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !seen_inputs {
|
||||||
|
let temp_dir = tempdir()?;
|
||||||
|
let empty = temp_dir.path().join("empty-file.txt");
|
||||||
|
write_file(&empty, "").await?;
|
||||||
|
self.check_input(&empty)
|
||||||
|
.await
|
||||||
|
.context("checking libFuzzer with empty input")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_input(&self, input: &Path) -> Result<()> {
|
||||||
|
// Verify that the libfuzzer exits with a zero return code with a known
|
||||||
|
// good input, which libfuzzer works as we expect.
|
||||||
|
|
||||||
|
let mut cmd = self.build_command(None, None, None)?;
|
||||||
|
cmd.arg(&input);
|
||||||
|
|
||||||
|
let result = cmd
|
||||||
|
.spawn()
|
||||||
|
.with_context(|| format_err!("libfuzzer failed to start: {}", self.exe.display()))?
|
||||||
|
.wait_with_output()
|
||||||
|
.await
|
||||||
|
.with_context(|| format_err!("libfuzzer failed to run: {}", self.exe.display()))?;
|
||||||
|
if !result.status.success() {
|
||||||
|
bail!(
|
||||||
|
"libFuzzer failed when parsing an initial seed {:?}: stdout:{:?} stderr:{:?}",
|
||||||
|
input.file_name().unwrap_or_else(|| input.as_ref()),
|
||||||
|
String::from_utf8_lossy(&result.stdout),
|
||||||
|
String::from_utf8_lossy(&result.stderr),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_help(&self) -> Result<()> {
|
||||||
|
let mut cmd = self.build_command(None, None, None)?;
|
||||||
|
cmd.arg("-help=1");
|
||||||
|
|
||||||
let result = cmd
|
let result = cmd
|
||||||
.spawn()
|
.spawn()
|
||||||
.with_context(|| format_err!("libfuzzer failed to start: {}", self.exe.display()))?
|
.with_context(|| format_err!("libfuzzer failed to start: {}", self.exe.display()))?
|
||||||
@ -97,70 +204,23 @@ impl<'a> LibFuzzer<'a> {
|
|||||||
corpus_dir: impl AsRef<Path>,
|
corpus_dir: impl AsRef<Path>,
|
||||||
extra_corpus_dirs: &[impl AsRef<Path>],
|
extra_corpus_dirs: &[impl AsRef<Path>],
|
||||||
) -> Result<Child> {
|
) -> Result<Child> {
|
||||||
let corpus_dir = corpus_dir.as_ref();
|
let extra_corpus_dirs: Vec<&Path> = extra_corpus_dirs.iter().map(|x| x.as_ref()).collect();
|
||||||
let fault_dir = fault_dir.as_ref();
|
let mut cmd = self.build_command(
|
||||||
|
Some(fault_dir.as_ref()),
|
||||||
let expand = Expand::new()
|
Some(corpus_dir.as_ref()),
|
||||||
.target_exe(&self.exe)
|
Some(&extra_corpus_dirs),
|
||||||
.target_options(&self.options)
|
)?;
|
||||||
.input_corpus(&corpus_dir)
|
|
||||||
.crashes(&fault_dir)
|
|
||||||
.setup_dir(&self.setup_dir);
|
|
||||||
|
|
||||||
let mut cmd = Command::new(&self.exe);
|
|
||||||
cmd.kill_on_drop(true)
|
|
||||||
.env(PATH, get_path_with_directory(PATH, &self.setup_dir)?)
|
|
||||||
.env_remove("RUST_LOG")
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::piped());
|
|
||||||
|
|
||||||
// Set the environment.
|
|
||||||
for (k, v) in self.env {
|
|
||||||
cmd.env(k, expand.evaluate_value(v)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass custom option arguments.
|
|
||||||
for o in expand.evaluate(self.options)? {
|
|
||||||
cmd.arg(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg!(target_family = "unix") {
|
|
||||||
cmd.env(
|
|
||||||
LD_LIBRARY_PATH,
|
|
||||||
get_path_with_directory(LD_LIBRARY_PATH, &self.setup_dir)?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if a max_time is already set
|
|
||||||
if self
|
|
||||||
.options
|
|
||||||
.iter()
|
|
||||||
.find(|o| o.starts_with("-max_total_time"))
|
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
cmd.arg(format!("-max_total_time={}", DEFAULT_MAX_TOTAL_SECONDS));
|
|
||||||
}
|
|
||||||
|
|
||||||
// When writing a new faulting input, the libFuzzer runtime _exactly_
|
// When writing a new faulting input, the libFuzzer runtime _exactly_
|
||||||
// prepends the value of `-artifact_prefix` to the new file name. To
|
// prepends the value of `-artifact_prefix` to the new file name. To
|
||||||
// specify that a new file `crash-<digest>` should be written to a
|
// specify that a new file `crash-<digest>` should be written to a
|
||||||
// _directory_ `<corpus_dir>`, we must ensure that the prefix includes a
|
// _directory_ `<corpus_dir>`, we must ensure that the prefix includes a
|
||||||
// trailing path separator.
|
// trailing path separator.
|
||||||
let artifact_prefix: OsString = format!("-artifact_prefix={}/", fault_dir.display()).into();
|
let artifact_prefix: OsString =
|
||||||
|
format!("-artifact_prefix={}/", fault_dir.as_ref().display()).into();
|
||||||
|
|
||||||
cmd.arg(&artifact_prefix);
|
cmd.arg(&artifact_prefix);
|
||||||
|
|
||||||
// Force a single worker, so we can manage workers ourselves.
|
|
||||||
cmd.arg("-workers=1");
|
|
||||||
|
|
||||||
// Set the read/written main corpus directory.
|
|
||||||
cmd.arg(corpus_dir);
|
|
||||||
|
|
||||||
// Set extra corpus directories that will be periodically rescanned.
|
|
||||||
for dir in extra_corpus_dirs {
|
|
||||||
cmd.arg(dir.as_ref());
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = cmd
|
let child = cmd
|
||||||
.spawn()
|
.spawn()
|
||||||
.with_context(|| format_err!("libfuzzer failed to start: {}", self.exe.display()))?;
|
.with_context(|| format_err!("libfuzzer failed to start: {}", self.exe.display()))?;
|
||||||
@ -193,44 +253,12 @@ impl<'a> LibFuzzer<'a> {
|
|||||||
pub async fn merge(
|
pub async fn merge(
|
||||||
&self,
|
&self,
|
||||||
corpus_dir: impl AsRef<Path>,
|
corpus_dir: impl AsRef<Path>,
|
||||||
corpus_dirs: &[impl AsRef<Path>],
|
extra_corpus_dirs: &[impl AsRef<Path>],
|
||||||
) -> Result<LibFuzzerMergeOutput> {
|
) -> Result<LibFuzzerMergeOutput> {
|
||||||
let expand = Expand::new()
|
let extra_corpus_dirs: Vec<&Path> = extra_corpus_dirs.iter().map(|x| x.as_ref()).collect();
|
||||||
.target_exe(&self.exe)
|
let mut cmd =
|
||||||
.target_options(&self.options)
|
self.build_command(None, Some(corpus_dir.as_ref()), Some(&extra_corpus_dirs))?;
|
||||||
.input_corpus(&corpus_dir)
|
cmd.arg("-merge=1");
|
||||||
.setup_dir(&self.setup_dir);
|
|
||||||
|
|
||||||
let mut cmd = Command::new(&self.exe);
|
|
||||||
|
|
||||||
cmd.kill_on_drop(true)
|
|
||||||
.env(PATH, get_path_with_directory(PATH, &self.setup_dir)?)
|
|
||||||
.env_remove("RUST_LOG")
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.stderr(Stdio::piped())
|
|
||||||
.arg("-merge=1")
|
|
||||||
.arg(corpus_dir.as_ref());
|
|
||||||
|
|
||||||
if cfg!(target_family = "unix") {
|
|
||||||
cmd.env(
|
|
||||||
LD_LIBRARY_PATH,
|
|
||||||
get_path_with_directory(LD_LIBRARY_PATH, &self.setup_dir)?,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for dir in corpus_dirs {
|
|
||||||
cmd.arg(dir.as_ref());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the environment.
|
|
||||||
for (k, v) in self.env {
|
|
||||||
cmd.env(k, expand.evaluate_value(v)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass custom option arguments.
|
|
||||||
for o in expand.evaluate(self.options)? {
|
|
||||||
cmd.arg(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = cmd
|
let output = cmd
|
||||||
.spawn()
|
.spawn()
|
||||||
@ -317,4 +345,64 @@ mod tests {
|
|||||||
assert!(execs_sec.is_finite());
|
assert!(execs_sec.is_finite());
|
||||||
assert!((execs_sec - expected).abs() < f64::EPSILON);
|
assert!((execs_sec - expected).abs() < f64::EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
async fn verify_initial_inputs() -> Result<()> {
|
||||||
|
let bad_bin = PathBuf::from("/bin/false");
|
||||||
|
let good_bin = PathBuf::from("/bin/echo");
|
||||||
|
let temp_setup_dir = tempdir()?;
|
||||||
|
let options = vec![];
|
||||||
|
let env = HashMap::new();
|
||||||
|
|
||||||
|
let input_file = temp_setup_dir.path().join("input.txt");
|
||||||
|
write_file(&input_file, "input").await?;
|
||||||
|
|
||||||
|
let fuzzer = LibFuzzer::new(bad_bin, &options, &env, &temp_setup_dir.path());
|
||||||
|
|
||||||
|
// verify catching bad exits with -help=1
|
||||||
|
assert!(
|
||||||
|
fuzzer.verify(true, None).await.is_err(),
|
||||||
|
"checking false with -help=1"
|
||||||
|
);
|
||||||
|
|
||||||
|
// verify catching bad exits with inputs
|
||||||
|
assert!(
|
||||||
|
fuzzer
|
||||||
|
.verify(false, Some(vec!(temp_setup_dir.path().to_path_buf())))
|
||||||
|
.await
|
||||||
|
.is_err(),
|
||||||
|
"checking false with basic input"
|
||||||
|
);
|
||||||
|
|
||||||
|
// verify catching bad exits with no inputs
|
||||||
|
assert!(
|
||||||
|
fuzzer.verify(false, None).await.is_err(),
|
||||||
|
"checking false without inputs"
|
||||||
|
);
|
||||||
|
|
||||||
|
let fuzzer = LibFuzzer::new(good_bin, &options, &env, &temp_setup_dir.path());
|
||||||
|
// verify good exits with -help=1
|
||||||
|
assert!(
|
||||||
|
fuzzer.verify(true, None).await.is_ok(),
|
||||||
|
"checking true with -help=1"
|
||||||
|
);
|
||||||
|
|
||||||
|
// verify good exits with inputs
|
||||||
|
assert!(
|
||||||
|
fuzzer
|
||||||
|
.verify(false, Some(vec!(temp_setup_dir.path().to_path_buf())))
|
||||||
|
.await
|
||||||
|
.is_ok(),
|
||||||
|
"checking true with basic inputs"
|
||||||
|
);
|
||||||
|
|
||||||
|
// verify good exits with no inputs
|
||||||
|
assert!(
|
||||||
|
fuzzer.verify(false, None).await.is_ok(),
|
||||||
|
"checking true without inputs"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user