blob: 4200d15867d842ea87c82e530520d03d8562dc01 [file] [view]
# Reproducing Crashes from ClusterFuzz
## Introduction
This guide provides step-by-step instructions for reproducing crashes found by
ClusterFuzz. Reproducing a crash locally is the first step toward debugging and
fixing it.
*** note
**Requirements:** For Windows, you must convert the forward slashes (/) to
backslashes (\\) in the commands below and use `set` command instead of `export`
to set the environment variable (step 4). Note that these commands are intended
to be used with cmd.exe, not PowerShell. Also, you may find [these tips] on how
to debug an ASAN instrumented binary helpful.
***
[TOC]
## Which Workflow Should I Use?
The first step is to identify how the crash was found. Look at the ClusterFuzz
bug report:
* If the report specifies a "Fuzz Target" and mentions a fuzzing engine
(like libFuzzer, Centipede, etc.), you should follow the steps under
[Reproducing a Crash from a Fuzzing Engine](#reproducing-a-crash-from-a-fuzzing-engine).
* Otherwise, the crash was likely caused by a blackbox fuzzer that produced
a file to crash a larger application (like `content_shell`). You should
follow the steps under
[Reproducing a Crash from a Blackbox Fuzzer](#reproducing-a-crash-from-a-blackbox-fuzzer).
## Reproducing a Crash from a Fuzzing Engine
The majority of the bugs reported by ClusterFuzz have the **Reproducible**
label. This workflow applies to those cases.
1. Download the testcase from ClusterFuzz. If you are CCed on an issue filed by
ClusterFuzz, a link to it is next to "Reproducer testcase" in the bug
description.
For the rest of this section, we call the path of this
file: `$TESTCASE_PATH` and the fuzz target you want to reproduce a
crash on: `$FUZZER_NAME` (provided as "Fuzz Target" in the bug
description).
2. Generate gn build configuration:
```
gn args out/fuzz
```
This will open up an editor. Copy the gn configuration parameters from the
values provided in `GN Config` section in the ClusterFuzz testcase report.
3. Build the fuzzer:
```
autoninja -C out/fuzz $FUZZER_NAME
```
4. Set the `*SAN_OPTIONS` environment variable as provided in the
`Crash Stacktrace` section in the testcase report.
Here is an example value of `ASAN_OPTIONS` that is similar to its value on
ClusterFuzz:
```
export ASAN_OPTIONS=redzone=256:print_summary=1:handle_sigill=1:allocator_release_to_os_interval_ms=500:print_suppressions=0:strict_memcmp=1:allow_user_segv_handler=0:use_sigaltstack=1:handle_sigfpe=1:handle_sigbus=1:detect_stack_use_after_return=0:alloc_dealloc_mismatch=0:detect_leaks=0:print_scariness=1:allocator_may_return_null=1:handle_abort=1:check_malloc_usable_size=0:detect_container_overflow=0:quarantine_size_mb=256:detect_odr_violation=0:symbolize=1:handle_segv=1:fast_unwind_on_fatal=0
```
Note: The sanitizer options may reference suppressions files. You can find those
at go/crfuzz-clusterfuzz-suppressions (google-internal).
5. Run the fuzz target with the downloaded testcase. The `-runs=100` flag helps
ensure reproduction even if the crash has minor flakiness.
```
out/fuzz/$FUZZER_NAME -runs=100 $TESTCASE_PATH
```
[File a bug] if you run into any issues.
## Reproducing a Crash from a Blackbox Fuzzer
1. Get info from the report.
* Download the `Reproducer testcase`. We'll call its path `$TESTCASE_PATH`.
* Identify the target application from the `Job Type` (e.g.,
`linux_asan_content_shell_drt` means the target is `content_shell`).
2. Configure and build the application:
Generate the build configuration using the arguments from the “GN Config”
section.
```
gn args out/fuzz
```
Build the target application binary (e.g., content_shell).
```
autoninja -C out/fuzz content_shell
```
3. Set environment variables:
Export all variables listed under `[Environment]` in the stack trace of the
report.
```
export ASAN_OPTIONS=...
# Also export any other variables listed.
```
4. Construct and run the command:
In the stack trace, find the line beginning with `Command line:`.
Construct your local command by replacing the executable path (e.g.,
`/proc/self/exe` or `/mnt/.../content_shell`) with the path to your built
binary (e.g., `out/fuzz/content_shell`), copying all the flags, and appending
the `$TESTCASE_PATH` at the end.
```
# Example command based on a content_shell crash
out/fuzz/content_shell \
--run-web-tests \
--disable-in-process-stack-traces \
--autoplay-policy=no-user-gesture-required \
--lang=en-US \
$TESTCASE_PATH
```
## Crashes reported as Unreproducible
ClusterFuzz generally does not report issues that it cannot reliably reproduce,
unless the following condition is met. If a certain crash is occurring often
enough, such a crash might be reported with **Unreproducible** label and an
explicit clarification that there is no convenient way to reproduce it. There
are two ways to work with such crashes.
### Option 1: Attempt a Speculative Fix
Try a speculative fix based on the stacktrace. Once the fix is landed, wait a
couple days and then check Crash Statistics section on the ClusterFuzz
testcase report page. If the fix works out, you will see that the crash is
not happening anymore. If the crash does not occur again for a little while,
ClusterFuzz will automatically close the issue as Verified.
### Option 2: Replay the Fuzzing Session (libFuzzer Only)
Try to reproduce the whole fuzzing session. This workflow is very similar to the
one described above for reproducing a crash from a fuzzing engine. The only
differences are:
* Instead of downloading a single testcase, you need to download corpus backup.
This can be done using the following command:
```
gsutil cp gs://clusterfuzz-libfuzzer-backup/corpus/libfuzzer/$FUZZER_NAME/latest.zip .
```
* Alternatively, you can navigate to the following URL in your browser and
download the `latest.zip` file:
```
https://pantheon.corp.google.com/storage/browser/clusterfuzz-libfuzzer-backup/corpus/libfuzzer/$FUZZER_NAME
```
* Create an empty directory and unpack the corpus into it.
* Follow steps 2-4 in the reproducing a crash from a fuzzing engine section
above.
* On step 5, use the following command:
```
out/fuzz/$FUZZER_NAME -timeout=25 -rss_limit_mb=2048 -print_final_stats=1 $CORPUS_DIRECTORY_FROM_THE_PREVIOUS_STEP
```
* Wait and hope that the fuzzer will crash.
Waiting for a crash to occur may take some time (up to 1hr), but if it happens,
you will be able to test the fix locally and/or somehow debug the issue.
## Symbolizing stack traces
Stack traces from ASAN builds are not symbolized by default. However, you
can symbolize them by piping the output into:
```
src/tools/valgrind/asan/asan_symbolize.py
```
## Minimizing a crash input (optional)
ClusterFuzz does crash input minimization automatically, and a typical crash
report has two testcases available for downloading:
* An original testcase that has triggered the crash;
* A minimized testcase that is smaller than the original but triggers the same
crash.
If you would like to further minimize a testcase, run the fuzz target with the
two additional arguments:
* `-minimize_crash=1`
* `-exact_artifact_path=<output_filename_for_minimized_testcase>`
The full command would be:
```
out/fuzz/$FUZZER_NAME -minimize_crash=1 -exact_artifact_path=<minimized_testcase_path> $TESTCASE_PATH
```
This might be useful for large testcases that make it hard to identify a root
cause of a crash. You can leave the minimization running locally for a while
(e.g. overnight) for better results.
[File a bug]: https://bugs.chromium.org/p/chromium/issues/entry?components=Tools%3EStability%3ElibFuzzer&comment=What%20problem%20are%20you%20seeing
[these tips]: https://github.com/google/sanitizers/wiki/AddressSanitizerWindowsPort#debugging