add libfuzzer regression tasks to local fuzzing (#744)

This commit is contained in:
bmc-msft
2021-03-30 16:58:07 -04:00
committed by GitHub
parent cf1051bad1
commit 5055bf6c38
7 changed files with 177 additions and 11 deletions

View File

@ -10,7 +10,7 @@ use tokio::time::timeout;
use crate::local::{
common::add_common_config, generic_analysis, generic_crash_report, generic_generator,
libfuzzer, libfuzzer_coverage, libfuzzer_crash_report, libfuzzer_fuzz, libfuzzer_merge,
libfuzzer_test_input, radamsa, test_input,
libfuzzer_regression, libfuzzer_test_input, radamsa, test_input,
};
const RADAMSA: &str = "radamsa";
@ -20,6 +20,7 @@ const LIBFUZZER_CRASH_REPORT: &str = "libfuzzer-crash-report";
const LIBFUZZER_COVERAGE: &str = "libfuzzer-coverage";
const LIBFUZZER_MERGE: &str = "libfuzzer-merge";
const LIBFUZZER_TEST_INPUT: &str = "libfuzzer-test-input";
const LIBFUZZER_REGRESSION: &str = "libfuzzer-regression";
const GENERIC_CRASH_REPORT: &str = "crash-report";
const GENERIC_GENERATOR: &str = "generator";
const GENERIC_ANALYSIS: &str = "analysis";
@ -43,6 +44,7 @@ pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
(GENERIC_GENERATOR, Some(sub)) => generic_generator::run(sub).await,
(GENERIC_TEST_INPUT, Some(sub)) => test_input::run(sub).await,
(LIBFUZZER_TEST_INPUT, Some(sub)) => libfuzzer_test_input::run(sub).await,
(LIBFUZZER_REGRESSION, Some(sub)) => libfuzzer_regression::run(sub).await,
_ => {
anyhow::bail!("missing subcommand\nUSAGE: {}", args.usage());
}
@ -78,6 +80,9 @@ pub fn args(name: &str) -> App<'static, 'static> {
LIBFUZZER_COVERAGE,
)))
.subcommand(add_common_config(libfuzzer_merge::args(LIBFUZZER_MERGE)))
.subcommand(add_common_config(libfuzzer_regression::args(
LIBFUZZER_REGRESSION,
)))
.subcommand(add_common_config(libfuzzer_crash_report::args(
LIBFUZZER_CRASH_REPORT,
)))

View File

@ -33,6 +33,7 @@ pub const TOOLS_DIR: &str = "tools_dir";
pub const RENAME_OUTPUT: &str = "rename_output";
pub const CHECK_FUZZER_HELP: &str = "check_fuzzer_help";
pub const DISABLE_CHECK_DEBUGGER: &str = "disable_check_debugger";
pub const REGRESSION_REPORTS_DIR: &str = "regression_reports_dir";
pub const TARGET_EXE: &str = "target_exe";
pub const TARGET_ENV: &str = "target_env";

View File

@ -5,17 +5,20 @@ use crate::{
local::{
common::{
build_common_config, wait_for_dir, DirectoryMonitorQueue, ANALYZER_EXE, COVERAGE_DIR,
UNIQUE_REPORTS_DIR,
REGRESSION_REPORTS_DIR, UNIQUE_REPORTS_DIR,
},
generic_analysis::{build_analysis_config, build_shared_args as build_analysis_args},
libfuzzer_coverage::{build_coverage_config, build_shared_args as build_coverage_args},
libfuzzer_crash_report::{build_report_config, build_shared_args as build_crash_args},
libfuzzer_fuzz::{build_fuzz_config, build_shared_args as build_fuzz_args},
libfuzzer_regression::{
build_regression_config, build_shared_args as build_regression_args,
},
},
tasks::{
analysis::generic::run as run_analysis, config::CommonConfig,
coverage::libfuzzer_coverage::CoverageTask, fuzz::libfuzzer_fuzz::LibFuzzerFuzzTask,
report::libfuzzer_report::ReportTask,
regression::libfuzzer::LibFuzzerRegressionTask, report::libfuzzer_report::ReportTask,
},
};
use anyhow::Result;
@ -87,7 +90,7 @@ pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
Some(analysis_input_monitor.queue_client),
CommonConfig {
task_id: Uuid::new_v4(),
..common
..common.clone()
},
)?;
let analysis_task = spawn(async move { run_analysis(analysis_config).await });
@ -96,6 +99,19 @@ pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
task_handles.push(analysis_input_monitor.handle);
}
if args.is_present(REGRESSION_REPORTS_DIR) {
let regression_config = build_regression_config(
args,
CommonConfig {
task_id: Uuid::new_v4(),
..common
},
)?;
let regression = LibFuzzerRegressionTask::new(regression_config);
let regression_task = spawn(async move { regression.run().await });
task_handles.push(regression_task);
}
try_wait_all_join_handles(task_handles).await?;
Ok(())
@ -110,6 +126,7 @@ pub fn args(name: &'static str) -> App<'static, 'static> {
build_crash_args(),
build_analysis_args(false),
build_coverage_args(true),
build_regression_args(false),
] {
for arg in args {
if used.contains(arg.b.name) {

View File

@ -0,0 +1,138 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use crate::{
local::common::{
build_common_config, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir, CmdType,
CHECK_FUZZER_HELP, CHECK_RETRY_COUNT, COVERAGE_DIR, CRASHES_DIR, NO_REPRO_DIR,
REGRESSION_REPORTS_DIR, REPORTS_DIR, TARGET_ENV, TARGET_EXE, TARGET_OPTIONS,
TARGET_TIMEOUT, UNIQUE_REPORTS_DIR,
},
tasks::{
config::CommonConfig,
regression::libfuzzer::{Config, LibFuzzerRegressionTask},
},
};
use anyhow::Result;
use clap::{App, Arg, SubCommand};
const REPORT_NAMES: &str = "report_names";
pub fn build_regression_config(
args: &clap::ArgMatches<'_>,
common: CommonConfig,
) -> Result<Config> {
let target_exe = get_cmd_exe(CmdType::Target, args)?.into();
let target_env = get_cmd_env(CmdType::Target, args)?;
let target_options = get_cmd_arg(CmdType::Target, args);
let target_timeout = value_t!(args, TARGET_TIMEOUT, u64).ok();
let crashes = get_synced_dir(CRASHES_DIR, common.job_id, common.task_id, args)?;
let regression_reports =
get_synced_dir(REGRESSION_REPORTS_DIR, common.job_id, common.task_id, args)?;
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
let reports = get_synced_dir(REPORTS_DIR, common.job_id, common.task_id, args).ok();
let no_repro = get_synced_dir(NO_REPRO_DIR, common.job_id, common.task_id, args).ok();
let unique_reports =
get_synced_dir(UNIQUE_REPORTS_DIR, common.job_id, common.task_id, args).ok();
let report_list = if args.is_present(REPORT_NAMES) {
Some(values_t!(args, REPORT_NAMES, String)?)
} else {
None
};
let check_fuzzer_help = args.is_present(CHECK_FUZZER_HELP);
let config = Config {
target_exe,
target_env,
target_options,
target_timeout,
check_fuzzer_help,
check_retry_count,
crashes,
regression_reports,
reports,
no_repro,
unique_reports,
readonly_inputs: None,
report_list,
minimized_stack_depth: None,
common,
};
Ok(config)
}
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
let common = build_common_config(args, true)?;
let config = build_regression_config(args, common)?;
LibFuzzerRegressionTask::new(config).run().await
}
pub fn build_shared_args(local_job: bool) -> Vec<Arg<'static, 'static>> {
let mut args = vec![
Arg::with_name(TARGET_EXE)
.long(TARGET_EXE)
.takes_value(true)
.required(true),
Arg::with_name(TARGET_ENV)
.long(TARGET_ENV)
.takes_value(true)
.multiple(true),
Arg::with_name(TARGET_OPTIONS)
.long(TARGET_OPTIONS)
.takes_value(true)
.value_delimiter(" ")
.help("Use a quoted string with space separation to denote multiple arguments"),
Arg::with_name(COVERAGE_DIR)
.takes_value(true)
.required(!local_job)
.long(COVERAGE_DIR),
Arg::with_name(CHECK_FUZZER_HELP)
.takes_value(false)
.long(CHECK_FUZZER_HELP),
Arg::with_name(TARGET_TIMEOUT)
.takes_value(true)
.long(TARGET_TIMEOUT),
Arg::with_name(CRASHES_DIR)
.long(CRASHES_DIR)
.takes_value(true)
.required(true),
Arg::with_name(REGRESSION_REPORTS_DIR)
.long(REGRESSION_REPORTS_DIR)
.takes_value(true)
.required(local_job),
Arg::with_name(REPORTS_DIR)
.long(REPORTS_DIR)
.takes_value(true)
.required(false),
Arg::with_name(NO_REPRO_DIR)
.long(NO_REPRO_DIR)
.takes_value(true)
.required(false),
Arg::with_name(UNIQUE_REPORTS_DIR)
.long(UNIQUE_REPORTS_DIR)
.takes_value(true)
.required(true),
Arg::with_name(CHECK_RETRY_COUNT)
.takes_value(true)
.long(CHECK_RETRY_COUNT)
.default_value("0"),
];
if local_job {
args.push(
Arg::with_name(REPORT_NAMES)
.long(REPORT_NAMES)
.takes_value(true)
.multiple(true),
)
}
args
}
pub fn args(name: &'static str) -> App<'static, 'static> {
SubCommand::with_name(name)
.about("execute a local-only libfuzzer regression task")
.args(&build_shared_args(false))
}

View File

@ -11,6 +11,7 @@ pub mod libfuzzer_coverage;
pub mod libfuzzer_crash_report;
pub mod libfuzzer_fuzz;
pub mod libfuzzer_merge;
pub mod libfuzzer_regression;
pub mod libfuzzer_test_input;
pub mod radamsa;
pub mod test_input;

View File

@ -32,7 +32,9 @@ pub async fn run(
readonly_inputs: &Option<SyncedDir>,
handler: &impl RegressionHandler,
) -> Result<()> {
info!("Starting generic regression task");
info!("starting regression task");
regression_reports.init().await?;
handle_crash_reports(
handler,
crashes,
@ -41,7 +43,8 @@ pub async fn run(
&regression_reports,
&heartbeat_client,
)
.await?;
.await
.context("handling crash reports")?;
if let Some(readonly_inputs) = &readonly_inputs {
handle_inputs(
@ -50,9 +53,11 @@ pub async fn run(
&regression_reports,
&heartbeat_client,
)
.await?;
.await
.context("handling inputs")?;
}
info!("regression task stopped");
Ok(())
}

View File

@ -7,7 +7,7 @@ use crate::tasks::{
utils::default_bool_true,
};
use anyhow::Result;
use anyhow::{Context, Result};
use reqwest::Url;
use super::common::{self, RegressionHandler};
@ -78,8 +78,6 @@ impl LibFuzzerRegressionTask {
}
pub async fn run(&self) -> Result<()> {
info!("Starting libfuzzer regression task");
let mut report_dirs = vec![];
for dir in &[
&self.config.reports,
@ -101,7 +99,8 @@ impl LibFuzzerRegressionTask {
&self.config.readonly_inputs,
self,
)
.await?;
.await
.context("libfuzzer regression")?;
Ok(())
}
}