mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-17 20:38:06 +00:00
add libfuzzer regression tasks to local fuzzing (#744)
This commit is contained in:
@ -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,
|
||||||
)))
|
)))
|
||||||
|
@ -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";
|
||||||
|
@ -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) {
|
||||||
|
138
src/agent/onefuzz-agent/src/local/libfuzzer_regression.rs
Normal file
138
src/agent/onefuzz-agent/src/local/libfuzzer_regression.rs
Normal 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))
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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(
|
|||||||
®ression_reports,
|
®ression_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(
|
|||||||
®ression_reports,
|
®ression_reports,
|
||||||
&heartbeat_client,
|
&heartbeat_client,
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
|
.context("handling inputs")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("regression task stopped");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user