mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-17 20:38:06 +00:00
add a single-shot crash report utility (#703)
Adds `test-input` and `test-input-libfuzzer`, which print the CrashTestResult in json form. While many of the existing tasks make sense running in a managed loop, crash report generation is something that having a single one-off is useful. Example: ``` $ onefuzz-agent local test-input /tmp/fuzz.exe /tmp/crash.txt { "crash_report": { "input_sha256": "a35b3ce1038750e9175a6dcd3f64c8d4e85720affb12cc11f5d0b6889274d06e", "executable": "/tmp/fuzz.exe", "crash_type": "SIGABRT", "crash_site": "0x7f0d9d4ad18b in gsignal+0xcb (/usr/lib/x86_64-linux-gnu/libc-2.31.so+0x4618b)", "call_stack": [ "#0 0x7f0d9d4ad18b in gsignal+0xcb (/usr/lib/x86_64-linux-gnu/libc-2.31.so+0x4618b)", "#1 0x7f0d9d48c859 in abort+0x12b (/usr/lib/x86_64-linux-gnu/libc-2.31.so+0x25859)", "#2 0x7f0d9d4f73ee in <unknown> (/usr/lib/x86_64-linux-gnu/libc-2.31.so+0x903ee)", "#3 0x7f0d9d599b4a in __fortify_fail+0x2a (/usr/lib/x86_64-linux-gnu/libc-2.31.so+0x132b4a)", "#4 0x7f0d9d5983e6 in __chk_fail+0x16 (/usr/lib/x86_64-linux-gnu/libc-2.31.so+0x1313e6)", "#5 0x7f0d9d597e09 in __strncpy_chk+0x19 (/usr/lib/x86_64-linux-gnu/libc-2.31.so+0x130e09)", "#6 0x400a54 in from_file+0xa4 (/tmp/fuzz.exe+0xa54)", "#7 0x7f0d9d48e0b3 in __libc_start_main+0xf3 (/usr/lib/x86_64-linux-gnu/libc-2.31.so+0x270b3)", "#8 0x40077a in _start+0x2a (/tmp/fuzz.exe+0x77a)" ], "call_stack_sha256": "6906234fb235690cc2843a1a55f49ff68b424e54bec55f9b8258415d97b3e638", "task_id": "00000000-0000-0000-0000-000000000000", "job_id": "00000000-0000-0000-0000-000000000000" } } $ ```
This commit is contained in:
@ -7,7 +7,7 @@ use clap::{App, SubCommand};
|
|||||||
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,
|
||||||
radamsa,
|
libfuzzer_test_input, radamsa, test_input,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RADAMSA: &str = "radamsa";
|
const RADAMSA: &str = "radamsa";
|
||||||
@ -16,9 +16,11 @@ const LIBFUZZER_FUZZ: &str = "libfuzzer-fuzz";
|
|||||||
const LIBFUZZER_CRASH_REPORT: &str = "libfuzzer-crash-report";
|
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 GENERIC_CRASH_REPORT: &str = "generic-crash-report";
|
const GENERIC_CRASH_REPORT: &str = "generic-crash-report";
|
||||||
const GENERIC_GENERATOR: &str = "generic-generator";
|
const GENERIC_GENERATOR: &str = "generic-generator";
|
||||||
const GENERIC_ANALYSIS: &str = "generic-analysis";
|
const GENERIC_ANALYSIS: &str = "generic-analysis";
|
||||||
|
const GENERIC_TEST_INPUT: &str = "generic-test-input";
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
match args.subcommand() {
|
match args.subcommand() {
|
||||||
@ -31,6 +33,8 @@ pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
|||||||
(GENERIC_ANALYSIS, Some(sub)) => generic_analysis::run(sub).await,
|
(GENERIC_ANALYSIS, Some(sub)) => generic_analysis::run(sub).await,
|
||||||
(GENERIC_CRASH_REPORT, Some(sub)) => generic_crash_report::run(sub).await,
|
(GENERIC_CRASH_REPORT, Some(sub)) => generic_crash_report::run(sub).await,
|
||||||
(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,
|
||||||
|
(LIBFUZZER_TEST_INPUT, Some(sub)) => libfuzzer_test_input::run(sub).await,
|
||||||
_ => {
|
_ => {
|
||||||
anyhow::bail!("missing subcommand\nUSAGE: {}", args.usage());
|
anyhow::bail!("missing subcommand\nUSAGE: {}", args.usage());
|
||||||
}
|
}
|
||||||
@ -57,4 +61,8 @@ pub fn args(name: &str) -> App<'static, 'static> {
|
|||||||
GENERIC_GENERATOR,
|
GENERIC_GENERATOR,
|
||||||
)))
|
)))
|
||||||
.subcommand(add_common_config(generic_analysis::args(GENERIC_ANALYSIS)))
|
.subcommand(add_common_config(generic_analysis::args(GENERIC_ANALYSIS)))
|
||||||
|
.subcommand(add_common_config(test_input::args(GENERIC_TEST_INPUT)))
|
||||||
|
.subcommand(add_common_config(libfuzzer_test_input::args(
|
||||||
|
LIBFUZZER_TEST_INPUT,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ pub const CHECK_ASAN_LOG: &str = "check_asan_log";
|
|||||||
pub const TOOLS_DIR: &str = "tools_dir";
|
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 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";
|
||||||
@ -176,9 +177,19 @@ pub fn get_synced_dir(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_common_config(args: &ArgMatches<'_>) -> Result<CommonConfig> {
|
// NOTE: generate_task_id is intended to change the default behavior for local
|
||||||
|
// fuzzing tasks from generating random task id to using UUID::nil(). This
|
||||||
|
// enables making the one-shot crash report generation, which isn't really a task,
|
||||||
|
// consistent across multiple runs.
|
||||||
|
pub fn build_common_config(args: &ArgMatches<'_>, generate_task_id: bool) -> Result<CommonConfig> {
|
||||||
let job_id = get_uuid("job_id", args).unwrap_or_else(|_| Uuid::nil());
|
let job_id = get_uuid("job_id", args).unwrap_or_else(|_| Uuid::nil());
|
||||||
let task_id = get_uuid("task_id", args).unwrap_or_else(|_| Uuid::new_v4());
|
let task_id = get_uuid("task_id", args).unwrap_or_else(|_| {
|
||||||
|
if generate_task_id {
|
||||||
|
Uuid::new_v4()
|
||||||
|
} else {
|
||||||
|
Uuid::nil()
|
||||||
|
}
|
||||||
|
});
|
||||||
let instance_id = get_uuid("instance_id", args).unwrap_or_else(|_| Uuid::nil());
|
let instance_id = get_uuid("instance_id", args).unwrap_or_else(|_| Uuid::nil());
|
||||||
|
|
||||||
let setup_dir = if args.is_present(SETUP_DIR) {
|
let setup_dir = if args.is_present(SETUP_DIR) {
|
||||||
|
@ -55,7 +55,7 @@ pub fn build_analysis_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
let common = build_common_config(args)?;
|
let common = build_common_config(args, true)?;
|
||||||
let config = build_analysis_config(args, None, common)?;
|
let config = build_analysis_config(args, None, common)?;
|
||||||
run_analysis(config).await
|
run_analysis(config).await
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
local::common::{
|
local::common::{
|
||||||
build_common_config, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir, CmdType,
|
build_common_config, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir, CmdType,
|
||||||
CHECK_ASAN_LOG, CHECK_RETRY_COUNT, CRASHES_DIR, DISABLE_CHECK_QUEUE, NO_REPRO_DIR,
|
CHECK_ASAN_LOG, CHECK_RETRY_COUNT, CRASHES_DIR, DISABLE_CHECK_DEBUGGER,
|
||||||
REPORTS_DIR, TARGET_ENV, TARGET_EXE, TARGET_OPTIONS, TARGET_TIMEOUT, UNIQUE_REPORTS_DIR,
|
DISABLE_CHECK_QUEUE, NO_REPRO_DIR, REPORTS_DIR, TARGET_ENV, TARGET_EXE, TARGET_OPTIONS,
|
||||||
|
TARGET_TIMEOUT, UNIQUE_REPORTS_DIR,
|
||||||
},
|
},
|
||||||
tasks::{
|
tasks::{
|
||||||
config::CommonConfig,
|
config::CommonConfig,
|
||||||
@ -46,7 +47,7 @@ pub fn build_report_config(
|
|||||||
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
|
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
|
||||||
let check_queue = !args.is_present(DISABLE_CHECK_QUEUE);
|
let check_queue = !args.is_present(DISABLE_CHECK_QUEUE);
|
||||||
let check_asan_log = args.is_present(CHECK_ASAN_LOG);
|
let check_asan_log = args.is_present(CHECK_ASAN_LOG);
|
||||||
let check_debugger = !args.is_present("disable_check_debugger");
|
let check_debugger = !args.is_present(DISABLE_CHECK_DEBUGGER);
|
||||||
|
|
||||||
let config = Config {
|
let config = Config {
|
||||||
target_exe,
|
target_exe,
|
||||||
@ -70,7 +71,7 @@ pub fn build_report_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
let common = build_common_config(args)?;
|
let common = build_common_config(args, true)?;
|
||||||
let config = build_report_config(args, None, common)?;
|
let config = build_report_config(args, None, common)?;
|
||||||
ReportTask::new(config).managed_run().await
|
ReportTask::new(config).managed_run().await
|
||||||
}
|
}
|
||||||
@ -121,9 +122,9 @@ pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
|
|||||||
Arg::with_name(CHECK_ASAN_LOG)
|
Arg::with_name(CHECK_ASAN_LOG)
|
||||||
.takes_value(false)
|
.takes_value(false)
|
||||||
.long(CHECK_ASAN_LOG),
|
.long(CHECK_ASAN_LOG),
|
||||||
Arg::with_name("disable_check_debugger")
|
Arg::with_name(DISABLE_CHECK_DEBUGGER)
|
||||||
.takes_value(false)
|
.takes_value(false)
|
||||||
.long("disable_check_debugger"),
|
.long(DISABLE_CHECK_DEBUGGER),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
local::common::{
|
local::common::{
|
||||||
build_common_config, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir,
|
build_common_config, get_cmd_arg, get_cmd_env, get_cmd_exe, get_synced_dir,
|
||||||
get_synced_dirs, CmdType, CHECK_ASAN_LOG, CHECK_RETRY_COUNT, CRASHES_DIR, GENERATOR_ENV,
|
get_synced_dirs, CmdType, CHECK_ASAN_LOG, CHECK_RETRY_COUNT, CRASHES_DIR,
|
||||||
GENERATOR_EXE, GENERATOR_OPTIONS, READONLY_INPUTS, RENAME_OUTPUT, TARGET_ENV, TARGET_EXE,
|
DISABLE_CHECK_DEBUGGER, GENERATOR_ENV, GENERATOR_EXE, GENERATOR_OPTIONS, READONLY_INPUTS,
|
||||||
TARGET_OPTIONS, TARGET_TIMEOUT, TOOLS_DIR,
|
RENAME_OUTPUT, TARGET_ENV, TARGET_EXE, TARGET_OPTIONS, TARGET_TIMEOUT, TOOLS_DIR,
|
||||||
},
|
},
|
||||||
tasks::{
|
tasks::{
|
||||||
config::CommonConfig,
|
config::CommonConfig,
|
||||||
@ -29,7 +29,7 @@ pub fn build_fuzz_config(args: &clap::ArgMatches<'_>, common: CommonConfig) -> R
|
|||||||
|
|
||||||
let rename_output = args.is_present(RENAME_OUTPUT);
|
let rename_output = args.is_present(RENAME_OUTPUT);
|
||||||
let check_asan_log = args.is_present(CHECK_ASAN_LOG);
|
let check_asan_log = args.is_present(CHECK_ASAN_LOG);
|
||||||
let check_debugger = !args.is_present("disable_check_debugger");
|
let check_debugger = !args.is_present(DISABLE_CHECK_DEBUGGER);
|
||||||
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
|
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
|
||||||
let target_timeout = Some(value_t!(args, TARGET_TIMEOUT, u64)?);
|
let target_timeout = Some(value_t!(args, TARGET_TIMEOUT, u64)?);
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ pub fn build_fuzz_config(args: &clap::ArgMatches<'_>, common: CommonConfig) -> R
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
let common = build_common_config(args)?;
|
let common = build_common_config(args, true)?;
|
||||||
let config = build_fuzz_config(args, common)?;
|
let config = build_fuzz_config(args, common)?;
|
||||||
GeneratorTask::new(config).run().await
|
GeneratorTask::new(config).run().await
|
||||||
}
|
}
|
||||||
@ -120,9 +120,9 @@ pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.long(TARGET_TIMEOUT)
|
.long(TARGET_TIMEOUT)
|
||||||
.default_value("30"),
|
.default_value("30"),
|
||||||
Arg::with_name("disable_check_debugger")
|
Arg::with_name(DISABLE_CHECK_DEBUGGER)
|
||||||
.takes_value(false)
|
.takes_value(false)
|
||||||
.long("disable_check_debugger"),
|
.long(DISABLE_CHECK_DEBUGGER),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ use tokio::task::spawn;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
let common = build_common_config(args)?;
|
let common = build_common_config(args, true)?;
|
||||||
let fuzz_config = build_fuzz_config(args, common.clone())?;
|
let fuzz_config = build_fuzz_config(args, common.clone())?;
|
||||||
let crash_dir = fuzz_config
|
let crash_dir = fuzz_config
|
||||||
.crashes
|
.crashes
|
||||||
|
@ -55,7 +55,7 @@ pub fn build_coverage_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
let common = build_common_config(args)?;
|
let common = build_common_config(args, true)?;
|
||||||
let config = build_coverage_config(args, false, None, common)?;
|
let config = build_coverage_config(args, false, None, common)?;
|
||||||
|
|
||||||
let mut task = CoverageTask::new(config);
|
let mut task = CoverageTask::new(config);
|
||||||
|
@ -63,7 +63,7 @@ pub fn build_report_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
let common = build_common_config(args)?;
|
let common = build_common_config(args, true)?;
|
||||||
let config = build_report_config(args, None, common)?;
|
let config = build_report_config(args, None, common)?;
|
||||||
ReportTask::new(config).managed_run().await
|
ReportTask::new(config).managed_run().await
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ pub fn build_fuzz_config(args: &clap::ArgMatches<'_>, common: CommonConfig) -> R
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
let common = build_common_config(args)?;
|
let common = build_common_config(args, true)?;
|
||||||
let config = build_fuzz_config(args, common)?;
|
let config = build_fuzz_config(args, common)?;
|
||||||
LibFuzzerFuzzTask::new(config)?.run().await
|
LibFuzzerFuzzTask::new(config)?.run().await
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ pub fn build_merge_config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
let common = build_common_config(args)?;
|
let common = build_common_config(args, true)?;
|
||||||
let config = build_merge_config(args, None, common)?;
|
let config = build_merge_config(args, None, common)?;
|
||||||
spawn(std::sync::Arc::new(config)).await
|
spawn(std::sync::Arc::new(config)).await
|
||||||
}
|
}
|
||||||
|
72
src/agent/onefuzz-agent/src/local/libfuzzer_test_input.rs
Normal file
72
src/agent/onefuzz-agent/src/local/libfuzzer_test_input.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
local::common::{
|
||||||
|
build_common_config, get_cmd_arg, get_cmd_env, CmdType, CHECK_RETRY_COUNT, TARGET_ENV,
|
||||||
|
TARGET_EXE, TARGET_OPTIONS, TARGET_TIMEOUT,
|
||||||
|
},
|
||||||
|
tasks::report::libfuzzer_report::{test_input, TestInputArgs},
|
||||||
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{App, Arg, SubCommand};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
|
let common = build_common_config(args, true)?;
|
||||||
|
|
||||||
|
let target_exe = value_t!(args, TARGET_EXE, PathBuf)?;
|
||||||
|
let target_env = get_cmd_env(CmdType::Target, args)?;
|
||||||
|
let target_options = get_cmd_arg(CmdType::Target, args);
|
||||||
|
let input = value_t!(args, "input", PathBuf)?;
|
||||||
|
let target_timeout = value_t!(args, TARGET_TIMEOUT, u64).ok();
|
||||||
|
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
|
||||||
|
|
||||||
|
let config = TestInputArgs {
|
||||||
|
target_exe: &target_exe.as_path(),
|
||||||
|
target_env: &target_env,
|
||||||
|
target_options: &target_options,
|
||||||
|
input_url: None,
|
||||||
|
input: input.as_path(),
|
||||||
|
job_id: common.job_id,
|
||||||
|
task_id: common.task_id,
|
||||||
|
target_timeout,
|
||||||
|
check_retry_count,
|
||||||
|
setup_dir: &common.setup_dir,
|
||||||
|
minimized_stack_depth: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = test_input(config).await?;
|
||||||
|
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
|
||||||
|
vec![
|
||||||
|
Arg::with_name(TARGET_EXE).takes_value(true).required(true),
|
||||||
|
Arg::with_name("input").takes_value(true).required(true),
|
||||||
|
Arg::with_name(TARGET_ENV)
|
||||||
|
.long(TARGET_ENV)
|
||||||
|
.takes_value(true)
|
||||||
|
.multiple(true),
|
||||||
|
Arg::with_name(TARGET_OPTIONS)
|
||||||
|
.default_value("{input}")
|
||||||
|
.long(TARGET_OPTIONS)
|
||||||
|
.takes_value(true)
|
||||||
|
.value_delimiter(" ")
|
||||||
|
.help("Use a quoted string with space separation to denote multiple arguments"),
|
||||||
|
Arg::with_name(TARGET_TIMEOUT)
|
||||||
|
.takes_value(true)
|
||||||
|
.long(TARGET_TIMEOUT),
|
||||||
|
Arg::with_name(CHECK_RETRY_COUNT)
|
||||||
|
.takes_value(true)
|
||||||
|
.long(CHECK_RETRY_COUNT)
|
||||||
|
.default_value("0"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn args(name: &'static str) -> App<'static, 'static> {
|
||||||
|
SubCommand::with_name(name)
|
||||||
|
.about("test a libfuzzer application with a specific input")
|
||||||
|
.args(&build_shared_args())
|
||||||
|
}
|
@ -11,4 +11,6 @@ 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_test_input;
|
||||||
pub mod radamsa;
|
pub mod radamsa;
|
||||||
|
pub mod test_input;
|
||||||
|
@ -17,7 +17,7 @@ use tokio::task::spawn;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
let common = build_common_config(args)?;
|
let common = build_common_config(args, true)?;
|
||||||
let fuzz_config = build_fuzz_config(args, common.clone())?;
|
let fuzz_config = build_fuzz_config(args, common.clone())?;
|
||||||
let crash_dir = fuzz_config
|
let crash_dir = fuzz_config
|
||||||
.crashes
|
.crashes
|
||||||
|
82
src/agent/onefuzz-agent/src/local/test_input.rs
Normal file
82
src/agent/onefuzz-agent/src/local/test_input.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
local::common::{
|
||||||
|
build_common_config, get_cmd_arg, get_cmd_env, CmdType, CHECK_ASAN_LOG, CHECK_RETRY_COUNT,
|
||||||
|
DISABLE_CHECK_DEBUGGER, TARGET_ENV, TARGET_EXE, TARGET_OPTIONS, TARGET_TIMEOUT,
|
||||||
|
},
|
||||||
|
tasks::report::generic::{test_input, TestInputArgs},
|
||||||
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
use clap::{App, Arg, SubCommand};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub async fn run(args: &clap::ArgMatches<'_>) -> Result<()> {
|
||||||
|
let common = build_common_config(args, false)?;
|
||||||
|
|
||||||
|
let target_exe = value_t!(args, TARGET_EXE, PathBuf)?;
|
||||||
|
let target_env = get_cmd_env(CmdType::Target, args)?;
|
||||||
|
let target_options = get_cmd_arg(CmdType::Target, args);
|
||||||
|
let input = value_t!(args, "input", PathBuf)?;
|
||||||
|
let target_timeout = value_t!(args, TARGET_TIMEOUT, u64).ok();
|
||||||
|
let check_retry_count = value_t!(args, CHECK_RETRY_COUNT, u64)?;
|
||||||
|
let check_asan_log = args.is_present(CHECK_ASAN_LOG);
|
||||||
|
let check_debugger = !args.is_present(DISABLE_CHECK_DEBUGGER);
|
||||||
|
|
||||||
|
let config = TestInputArgs {
|
||||||
|
target_exe: &target_exe.as_path(),
|
||||||
|
target_env: &target_env,
|
||||||
|
target_options: &target_options,
|
||||||
|
input_url: None,
|
||||||
|
input: input.as_path(),
|
||||||
|
job_id: common.job_id,
|
||||||
|
task_id: common.task_id,
|
||||||
|
target_timeout,
|
||||||
|
check_retry_count,
|
||||||
|
setup_dir: &common.setup_dir,
|
||||||
|
minimized_stack_depth: None,
|
||||||
|
check_asan_log,
|
||||||
|
check_debugger,
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = test_input(config).await?;
|
||||||
|
println!("{}", serde_json::to_string_pretty(&result)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_shared_args() -> Vec<Arg<'static, 'static>> {
|
||||||
|
vec![
|
||||||
|
Arg::with_name(TARGET_EXE).takes_value(true).required(true),
|
||||||
|
Arg::with_name("input").takes_value(true).required(true),
|
||||||
|
Arg::with_name(TARGET_ENV)
|
||||||
|
.long(TARGET_ENV)
|
||||||
|
.takes_value(true)
|
||||||
|
.multiple(true),
|
||||||
|
Arg::with_name(TARGET_OPTIONS)
|
||||||
|
.default_value("{input}")
|
||||||
|
.long(TARGET_OPTIONS)
|
||||||
|
.takes_value(true)
|
||||||
|
.value_delimiter(" ")
|
||||||
|
.help("Use a quoted string with space separation to denote multiple arguments"),
|
||||||
|
Arg::with_name(TARGET_TIMEOUT)
|
||||||
|
.takes_value(true)
|
||||||
|
.long(TARGET_TIMEOUT),
|
||||||
|
Arg::with_name(CHECK_RETRY_COUNT)
|
||||||
|
.takes_value(true)
|
||||||
|
.long(CHECK_RETRY_COUNT)
|
||||||
|
.default_value("0"),
|
||||||
|
Arg::with_name(CHECK_ASAN_LOG)
|
||||||
|
.takes_value(false)
|
||||||
|
.long(CHECK_ASAN_LOG),
|
||||||
|
Arg::with_name(DISABLE_CHECK_DEBUGGER)
|
||||||
|
.takes_value(false)
|
||||||
|
.long("disable_check_debugger"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn args(name: &'static str) -> App<'static, 'static> {
|
||||||
|
SubCommand::with_name(name)
|
||||||
|
.about("test an application with a specific input")
|
||||||
|
.args(&build_shared_args())
|
||||||
|
}
|
@ -32,24 +32,27 @@ pub struct CrashReport {
|
|||||||
pub call_stack: Vec<String>,
|
pub call_stack: Vec<String>,
|
||||||
pub call_stack_sha256: String,
|
pub call_stack_sha256: String,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub minimized_stack: Vec<String>,
|
pub minimized_stack: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub minimized_stack_sha256: Option<String>,
|
pub minimized_stack_sha256: Option<String>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub minimized_stack_function_names: Vec<String>,
|
pub minimized_stack_function_names: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub minimized_stack_function_names_sha256: Option<String>,
|
pub minimized_stack_function_names_sha256: Option<String>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub asan_log: Option<String>,
|
pub asan_log: Option<String>,
|
||||||
|
|
||||||
pub task_id: Uuid,
|
pub task_id: Uuid,
|
||||||
|
|
||||||
pub job_id: Uuid,
|
pub job_id: Uuid,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub scariness_score: Option<u32>,
|
pub scariness_score: Option<u32>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub scariness_description: Option<String>,
|
pub scariness_description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +65,7 @@ pub struct NoCrash {
|
|||||||
pub task_id: Uuid,
|
pub task_id: Uuid,
|
||||||
pub job_id: Uuid,
|
pub job_id: Uuid,
|
||||||
pub tries: u64,
|
pub tries: u64,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user