Capture crash dumps from libfuzzer, when provided (#2793)

Enables capturing crashdumps generated by ASAN at point of failure.

This helps in several ways:
- provides a crash dump in the case that we cannot repro a failure later
- some people have stated that crash dumps would be more useful to their team than the repro VM
- we should be able to use these for automated submission to Watson or similar

---

Crash dumps are automatically collected (for libfuzzer) jobs, if we find any. They should be activated by enabling crash dumps in ASAN, via:

- On Linux: `ASAN_OPTIONS=disable_coredump=0:abort_on_error=1:unmap_shadow_on_exit=1`
  - OneFuzz will override the (Ubuntu) crash dump options to generate core dumps instead and then upload them.
- On Windows: `ASAN_SAVE_DUMPS=my_dump.dmp`
  - OneFuzz will look for any `*.dmp` files in the working directory and then upload them.

In both cases, the crash dump will be renamed to match the crashing input, if possible, and uploaded to a new `crashdumps` container.

---

Also updated: the “simple” LibFuzzer test has been updated to be compiled with `cl.exe` instead of `clang` on Windows, so that we are exercising the MSVC implementation of ASAN/LibFuzzer, and the CI image has been updated to `windows-2022`. The restriction to an old version of the Windows SDK has been removed.
This commit is contained in:
George Pollard
2023-08-10 09:55:27 +12:00
committed by GitHub
parent 74ae105074
commit a364051923
21 changed files with 437 additions and 168 deletions

View File

@ -87,6 +87,7 @@ class Integration(BaseModel):
target_class: Optional[str]
target_method: Optional[str]
setup_dir: Optional[str]
target_env: Optional[Dict[str, str]]
TARGETS: Dict[str, Integration] = {
@ -106,12 +107,21 @@ TARGETS: Dict[str, Integration] = {
ContainerType.unique_reports: 1,
ContainerType.coverage: 1,
ContainerType.inputs: 2,
# TODO: crashdumps are intermittently not captured
# during integration tests on Linux. This requires more
# investigation before we can fully enable this test.
# ContainerType.crashdumps: 1,
ContainerType.extra_output: 1,
},
reboot_after_setup=True,
inject_fake_regression=True,
target_env={
# same TODO
# "ASAN_OPTIONS": "disable_coredump=0:abort_on_error=1:unmap_shadow_on_exit=1"
},
fuzzing_target_options=[
"--test:{extra_setup_dir}",
"--only_asan_failures",
"--write_test_file={extra_output_dir}/test.txt",
],
),
@ -213,11 +223,15 @@ TARGETS: Dict[str, Integration] = {
ContainerType.inputs: 2,
ContainerType.unique_reports: 1,
ContainerType.coverage: 1,
ContainerType.crashdumps: 1,
ContainerType.extra_output: 1,
},
inject_fake_regression=True,
target_env={"ASAN_SAVE_DUMPS": "my_dump.dmp"},
# we should set unmap_shadow_on_exit=1 but it fails on Windows at the moment
fuzzing_target_options=[
"--test:{extra_setup_dir}",
"--only_asan_failures",
"--write_test_file={extra_output_dir}/test.txt",
],
),
@ -631,6 +645,7 @@ class TestOnefuzz:
fuzzing_target_options=config.fuzzing_target_options,
extra_setup_container=Container(extra_setup_container.name),
extra_output_container=Container(extra_output_container.name),
target_env=config.target_env,
)
elif config.template == TemplateType.libfuzzer_dotnet:
if setup is None:
@ -653,6 +668,7 @@ class TestOnefuzz:
fuzzing_target_options=config.target_options,
target_class=config.target_class,
target_method=config.target_method,
target_env=config.target_env,
)
elif config.template == TemplateType.libfuzzer_qemu_user:
return self.of.template.libfuzzer.qemu_user(
@ -665,6 +681,7 @@ class TestOnefuzz:
duration=duration,
vm_count=1,
target_options=config.target_options,
target_env=config.target_env,
)
elif config.template == TemplateType.radamsa:
return self.of.template.radamsa.basic(
@ -679,6 +696,7 @@ class TestOnefuzz:
disable_check_debugger=config.disable_check_debugger or False,
duration=duration,
vm_count=1,
target_env=config.target_env,
)
elif config.template == TemplateType.afl:
return self.of.template.afl.basic(
@ -692,6 +710,7 @@ class TestOnefuzz:
duration=duration,
vm_count=1,
target_options=config.target_options,
target_env=config.target_env,
)
else:
raise NotImplementedError
@ -798,14 +817,18 @@ class TestOnefuzz:
return (True, "timed out while checking jobs", False)
for job_id in check_containers:
job_name = jobs[job_id].config.name
finished_containers: Set[Container] = set()
for container_name, container_impl in check_containers[job_id].items():
container_client, count = container_impl
if len(container_client.list_blobs()) >= count:
container_client, required_count = container_impl
found_count = len(container_client.list_blobs())
if found_count >= required_count:
clear()
self.logger.info(
"found files for %s - %s",
jobs[job_id].config.name,
"found %d files (needed %d) for %s - %s",
found_count,
required_count,
job_name,
container_name,
)
finished_containers.add(container_name)
@ -813,6 +836,12 @@ class TestOnefuzz:
for container_name in finished_containers:
del check_containers[job_id][container_name]
to_check = check_containers[job_id].keys()
if len(to_check) > 0:
self.logger.info(
"%s - still waiting for %s", job_name, ", ".join(to_check)
)
scalesets = self.of.scalesets.list()
for job_id in job_tasks:
finished_tasks: Set[UUID] = set()