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::{ use crate::local::{
common::add_common_config, generic_analysis, generic_crash_report, generic_generator, common::add_common_config, generic_analysis, generic_crash_report, generic_generator,
libfuzzer, libfuzzer_coverage, libfuzzer_crash_report, libfuzzer_fuzz, libfuzzer_merge, 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"; const RADAMSA: &str = "radamsa";
@ -20,6 +20,7 @@ const LIBFUZZER_CRASH_REPORT: &str = "libfuzzer-crash-report";
const LIBFUZZER_COVERAGE: &str = "libfuzzer-coverage"; const LIBFUZZER_COVERAGE: &str = "libfuzzer-coverage";
const LIBFUZZER_MERGE: &str = "libfuzzer-merge"; const LIBFUZZER_MERGE: &str = "libfuzzer-merge";
const LIBFUZZER_TEST_INPUT: &str = "libfuzzer-test-input"; const LIBFUZZER_TEST_INPUT: &str = "libfuzzer-test-input";
const LIBFUZZER_REGRESSION: &str = "libfuzzer-regression";
const GENERIC_CRASH_REPORT: &str = "crash-report"; const GENERIC_CRASH_REPORT: &str = "crash-report";
const GENERIC_GENERATOR: &str = "generator"; const GENERIC_GENERATOR: &str = "generator";
const GENERIC_ANALYSIS: &str = "analysis"; 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_GENERATOR, Some(sub)) => generic_generator::run(sub).await,
(GENERIC_TEST_INPUT, Some(sub)) => test_input::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_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()); anyhow::bail!("missing subcommand\nUSAGE: {}", args.usage());
} }
@ -78,6 +80,9 @@ pub fn args(name: &str) -> App<'static, 'static> {
LIBFUZZER_COVERAGE, LIBFUZZER_COVERAGE,
))) )))
.subcommand(add_common_config(libfuzzer_merge::args(LIBFUZZER_MERGE))) .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( .subcommand(add_common_config(libfuzzer_crash_report::args(
LIBFUZZER_CRASH_REPORT, 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 RENAME_OUTPUT: &str = "rename_output";
pub const CHECK_FUZZER_HELP: &str = "check_fuzzer_help"; pub const CHECK_FUZZER_HELP: &str = "check_fuzzer_help";
pub const DISABLE_CHECK_DEBUGGER: &str = "disable_check_debugger"; 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_EXE: &str = "target_exe";
pub const TARGET_ENV: &str = "target_env"; pub const TARGET_ENV: &str = "target_env";

View File

@ -5,17 +5,20 @@ use crate::{
local::{ local::{
common::{ common::{
build_common_config, wait_for_dir, DirectoryMonitorQueue, ANALYZER_EXE, COVERAGE_DIR, 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}, 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_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_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_fuzz::{build_fuzz_config, build_shared_args as build_fuzz_args},
libfuzzer_regression::{
build_regression_config, build_shared_args as build_regression_args,
},
}, },
tasks::{ tasks::{
analysis::generic::run as run_analysis, config::CommonConfig, analysis::generic::run as run_analysis, config::CommonConfig,
coverage::libfuzzer_coverage::CoverageTask, fuzz::libfuzzer_fuzz::LibFuzzerFuzzTask, coverage::libfuzzer_coverage::CoverageTask, fuzz::libfuzzer_fuzz::LibFuzzerFuzzTask,
report::libfuzzer_report::ReportTask, regression::libfuzzer::LibFuzzerRegressionTask, report::libfuzzer_report::ReportTask,
}, },
}; };
use anyhow::Result; use anyhow::Result;
@ -87,7 +90,7 @@ pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
Some(analysis_input_monitor.queue_client), Some(analysis_input_monitor.queue_client),
CommonConfig { CommonConfig {
task_id: Uuid::new_v4(), task_id: Uuid::new_v4(),
..common ..common.clone()
}, },
)?; )?;
let analysis_task = spawn(async move { run_analysis(analysis_config).await }); 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); 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?; try_wait_all_join_handles(task_handles).await?;
Ok(()) Ok(())
@ -110,6 +126,7 @@ pub fn args(name: &'static str) -> App<'static, 'static> {
build_crash_args(), build_crash_args(),
build_analysis_args(false), build_analysis_args(false),
build_coverage_args(true), build_coverage_args(true),
build_regression_args(false),
] { ] {
for arg in args { for arg in args {
if used.contains(arg.b.name) { 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_crash_report;
pub mod libfuzzer_fuzz; pub mod libfuzzer_fuzz;
pub mod libfuzzer_merge; pub mod libfuzzer_merge;
pub mod libfuzzer_regression;
pub mod libfuzzer_test_input; pub mod libfuzzer_test_input;
pub mod radamsa; pub mod radamsa;
pub mod test_input; pub mod test_input;

View File

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

View File

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