From 9413377c90e2a403a647f4185891e8c65edb9464 Mon Sep 17 00:00:00 2001 From: bmc-msft <41130664+bmc-msft@users.noreply.github.com> Date: Wed, 22 Sep 2021 13:16:35 -0400 Subject: [PATCH] handle fake crash reports generated by debugging tools in regression tasks (#1233) --- .../onefuzz-agent/data/fake-crash-report.json | 29 +++++++++ .../src/tasks/report/crash_report.rs | 60 +++++++++++++++---- src/cli/onefuzz/debug.py | 4 +- src/integration-tests/integration-test.py | 8 +++ 4 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 src/agent/onefuzz-agent/data/fake-crash-report.json diff --git a/src/agent/onefuzz-agent/data/fake-crash-report.json b/src/agent/onefuzz-agent/data/fake-crash-report.json new file mode 100644 index 0000000000..568ac56095 --- /dev/null +++ b/src/agent/onefuzz-agent/data/fake-crash-report.json @@ -0,0 +1,29 @@ +{ + "input_url": null, + "input_blob": { + "account": "fakestorageaccount", + "container": "fake-storage-container", + "name": "fake-crash-sample" + }, + "executable": "dds.exe", + "crash_type": "fake crash report", + "crash_site": "fake crash site", + "call_stack": [ + "#0 fake", + "#1 call", + "#2 stack" + ], + "call_stack_sha256": "0000000000000000000000000000000000000000000000000000000000000000", + "input_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "asan_log": "fake asan log", + "task_id": "2061fc8d-9f02-4d06-838a-87f59880e4e8", + "job_id": "510f8e4e-3c4d-4b54-968c-4da459d09f04", + "scariness_score": null, + "scariness_description": null, + "minimized_stack": [], + "minimized_stack_sha256": null, + "minimized_stack_function_names": [], + "minimized_stack_function_names_sha256": null, + "minimized_stack_function_lines": null, + "minimized_stack_function_lines_sha256": null +} diff --git a/src/agent/onefuzz-agent/src/tasks/report/crash_report.rs b/src/agent/onefuzz-agent/src/tasks/report/crash_report.rs index 6580615b63..ed07e81e70 100644 --- a/src/agent/onefuzz-agent/src/tasks/report/crash_report.rs +++ b/src/agent/onefuzz-agent/src/tasks/report/crash_report.rs @@ -40,8 +40,8 @@ pub struct CrashReport { #[serde(default, skip_serializing_if = "Option::is_none")] pub minimized_stack_function_names_sha256: Option, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub minimized_stack_function_lines: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub minimized_stack_function_lines: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub minimized_stack_function_lines_sha256: Option, @@ -206,6 +206,21 @@ impl CrashReport { } else { Some(crash_log.minimized_stack_function_names_sha256(minimized_stack_depth)) }; + + let minimized_stack_function_lines = if crash_log.minimized_stack_function_lines.is_empty() + { + None + } else { + Some(crash_log.minimized_stack_function_lines) + }; + + let minimized_stack_function_names = if crash_log.minimized_stack_function_names.is_empty() + { + None + } else { + Some(crash_log.minimized_stack_function_names) + }; + Self { input_sha256, input_blob, @@ -215,9 +230,9 @@ impl CrashReport { call_stack_sha256, minimized_stack: Some(crash_log.minimized_stack), minimized_stack_sha256, - minimized_stack_function_names: Some(crash_log.minimized_stack_function_names), + minimized_stack_function_names, minimized_stack_function_names_sha256, - minimized_stack_function_lines: crash_log.minimized_stack_function_lines, + minimized_stack_function_lines, minimized_stack_function_lines_sha256, call_stack: crash_log.call_stack, asan_log: crash_log.text, @@ -251,15 +266,25 @@ pub async fn parse_report_file(path: PathBuf) -> Result { .with_context(|| format_err!("invalid json: {} - {:?}", path.display(), raw))?; let report: Result = serde_json::from_value(json.clone()); - if let Ok(report) = report { - return Ok(CrashTestResult::CrashReport(report)); - } + + let report_err = match report { + Ok(report) => return Ok(CrashTestResult::CrashReport(report)), + Err(err) => err, + }; let no_repro: Result = serde_json::from_value(json); - if let Ok(no_repro) = no_repro { - return Ok(CrashTestResult::NoRepro(no_repro)); - } - bail!("unable to parse report: {} - {:?}", path.display(), raw) + let no_repro_err = match no_repro { + Ok(no_repro) => return Ok(CrashTestResult::NoRepro(no_repro)), + Err(err) => err, + }; + + bail!( + "unable to parse report: {} - {:?} - report error: {:?} no_repo error: {:?}", + path.display(), + raw, + report_err, + no_repro_err + ) } pub async fn monitor_reports( @@ -281,3 +306,16 @@ pub async fn monitor_reports( } Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use anyhow::Result; + + #[tokio::test] + async fn test_parse_fake_crash_report() -> Result<()> { + let path = std::path::PathBuf::from("data/fake-crash-report.json"); + parse_report_file(path).await?; + Ok(()) + } +} diff --git a/src/cli/onefuzz/debug.py b/src/cli/onefuzz/debug.py index 9c198b1bfa..c00a80b925 100644 --- a/src/cli/onefuzz/debug.py +++ b/src/cli/onefuzz/debug.py @@ -631,7 +631,7 @@ def _get_storage_account(self, container_name: Container) -> str: def job( self, - job_id: str, + job_id: UUID_EXPANSION, *, report_container_type: ContainerType = ContainerType.unique_reports, crash_name: str = "fake-crash-sample", @@ -655,7 +655,7 @@ def job( def task( self, - task_id: str, + task_id: UUID_EXPANSION, *, report_container_type: ContainerType = ContainerType.unique_reports, crash_name: str = "fake-crash-sample", diff --git a/src/integration-tests/integration-test.py b/src/integration-tests/integration-test.py index ef0fd418fb..51a8a03dae 100755 --- a/src/integration-tests/integration-test.py +++ b/src/integration-tests/integration-test.py @@ -69,6 +69,7 @@ class Integration(BaseModel): reboot_after_setup: Optional[bool] = Field(default=False) test_repro: Optional[bool] = Field(default=True) target_options: Optional[List[str]] + inject_fake_regression: bool = Field(default=False) TARGETS: Dict[str, Integration] = { @@ -90,6 +91,7 @@ class Integration(BaseModel): ContainerType.inputs: 2, }, reboot_after_setup=True, + inject_fake_regression=True, ), "linux-libfuzzer-with-options": Integration( template=TemplateType.libfuzzer, @@ -161,6 +163,7 @@ class Integration(BaseModel): target_exe="fuzz.exe", inputs="seeds", wait_for_files={ContainerType.unique_reports: 1}, + inject_fake_regression=True, ), "linux-trivial-crash-asan": Integration( template=TemplateType.radamsa, @@ -181,6 +184,7 @@ class Integration(BaseModel): ContainerType.unique_reports: 1, ContainerType.coverage: 1, }, + inject_fake_regression=True, ), "windows-libfuzzer-linked-library": Integration( template=TemplateType.libfuzzer, @@ -212,6 +216,7 @@ class Integration(BaseModel): target_exe="fuzz.exe", inputs="seeds", wait_for_files={ContainerType.unique_reports: 1}, + inject_fake_regression=True, ), } @@ -337,6 +342,9 @@ def launch( else: raise NotImplementedError + if config.inject_fake_regression and job is not None: + self.of.debug.notification.job(job.job_id) + if not job: raise Exception("missing job")