| # Getting Started with libFuzzer in Chrome |
| |
| *** note |
| **Prerequisites:** libFuzzer in Chrome is supported with GN on Linux only. |
| *** |
| |
| This document will walk you through: |
| |
| * setting up your build enviroment. |
| * creating your first fuzzer. |
| * running the fuzzer and verifying its vitals. |
| |
| ## Configure Build |
| |
| Use `use_libfuzzer` GN argument together with sanitizer to generate build files: |
| |
| ```bash |
| # With address sanitizer |
| gn gen out/libfuzzer '--args=use_libfuzzer=true is_asan=true enable_nacl=false' --check |
| ``` |
| |
| Supported sanitizer configurations are: |
| |
| | GN Argument | Description | |
| |--------------|----| |
| | is_asan=true | enables [Address Sanitizer] to catch problems like buffer overruns. | |
| | is_msan=true | enables [Memory Sanitizer] to catch problems like uninitialed reads. | |
| | is_ubsan_security=true | enables [Undefined Behavior Sanitizer] to catch<sup>\[[1](#Notes)\]</sup> undefined behavior like integer overflow. | |
| |
| |
| ## Write Fuzzer Function |
| |
| Create a new .cc file and define a `LLVMFuzzerTestOneInput` function: |
| |
| ```cpp |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| // put your fuzzing code here and use data+size as input. |
| return 0; |
| } |
| ``` |
| |
| [url_parse_fuzzer.cc] is a simple example of real-world fuzzer. |
| |
| ## Define GN Target |
| |
| Define `fuzzer_test` GN target: |
| |
| ``` |
| import("//testing/libfuzzer/fuzzer_test.gni") |
| fuzzer_test("my_fuzzer") { |
| sources = [ "my_fuzzer.cc" ] |
| deps = [ ... ] |
| } |
| ``` |
| |
| ## Build and Run Fuzzer Locally |
| |
| Build with ninja as usual and run: |
| |
| ```bash |
| ninja -C out/libfuzzer url_parse_fuzzer |
| ./out/libfuzzer/url_parse_fuzzer |
| ``` |
| |
| Your fuzzer should produce output like this: |
| |
| ``` |
| INFO: Seed: 1787335005 |
| INFO: -max_len is not provided, using 64 |
| INFO: PreferSmall: 1 |
| #0 READ units: 1 exec/s: 0 |
| #1 INITED cov: 2361 bits: 95 indir: 29 units: 1 exec/s: 0 |
| #2 NEW cov: 2710 bits: 359 indir: 36 units: 2 exec/s: 0 L: 64 MS: 0 |
| #3 NEW cov: 2715 bits: 371 indir: 37 units: 3 exec/s: 0 L: 64 MS: 1 ShuffleBytes- |
| #5 NEW cov: 2728 bits: 375 indir: 38 units: 4 exec/s: 0 L: 63 MS: 3 ShuffleBytes-ShuffleBytes-EraseByte- |
| #6 NEW cov: 2729 bits: 384 indir: 38 units: 5 exec/s: 0 L: 10 MS: 4 ShuffleBytes-ShuffleBytes-EraseByte-CrossOver- |
| #7 NEW cov: 2733 bits: 424 indir: 39 units: 6 exec/s: 0 L: 63 MS: 1 ShuffleBytes- |
| #8 NEW cov: 2733 bits: 426 indir: 39 units: 7 exec/s: 0 L: 63 MS: 2 ShuffleBytes-ChangeByte- |
| #11 NEW cov: 2733 bits: 447 indir: 39 units: 8 exec/s: 0 L: 33 MS: 5 ShuffleBytes-ChangeByte-ChangeASCIIInt-ChangeBit-CrossOver- |
| #12 NEW cov: 2733 bits: 451 indir: 39 units: 9 exec/s: 0 L: 62 MS: 1 CrossOver- |
| #16 NEW cov: 2733 bits: 454 indir: 39 units: 10 exec/s: 0 L: 61 MS: 5 CrossOver-ChangeBit-ChangeBit-EraseByte-ChangeBit- |
| #18 NEW cov: 2733 bits: 458 indir: 39 units: 11 exec/s: 0 L: 24 MS: 2 CrossOver-CrossOver- |
| ``` |
| |
| The `... NEW ...` line appears when libFuzzer finds new and interesting input. The |
| efficient fuzzer should be able to finds lots of them rather quickly. |
| |
| The `... pulse ...` line will appear periodically to show the current status. |
| |
| |
| ### Default value for maximum testcase length |
| |
| By default, when run manually, libFuzzer uses `-max_len=64` or takes the length |
| of the biggest testcase in corpus if corpus is not empty. ClusterFuzz takes |
| random value in range from `1` to `10000` for each fuzzing session and passes |
| that value to libFuzzers. If corpus contains testcases of size greater than |
| `max_len`, libFuzzer will use only first `max_len` bytes of such testcases. |
| |
| |
| You can specify custom `max_len` value to be used by ClusterFuzz. For more |
| information check out [Maximum Testcase Length] section of the [Efficient Fuzzer |
| Guide]. |
| |
| ## Submitting Fuzzer to ClusterFuzz |
| |
| ClusterFuzz builds and executes all `fuzzer_test` targets in the source tree. |
| The only thing you should do is to submit a fuzzer into Chrome. |
| |
| ## Next Steps |
| |
| * After your fuzzer is submitted, you should check its [ClusterFuzz status] in |
| a day or two. |
| * Check the [Efficient Fuzzer Guide] to better understand your fuzzer |
| performance and for optimization hints. |
| |
| |
| ## Notes |
| [1] By default UBSan doesn't crash once undefined behavior has been detected. |
| To make it crash the following additional option should be provided: |
| |
| ```bash |
| UBSAN_OPTIONS=halt_on_error=1 ./fuzzer <corpus_directory_or_single_testcase_path> |
| ``` |
| |
| Other useful options (used by ClusterFuzz) are: |
| ```bash |
| UBSAN_OPTIONS=symbolize=1:halt_on_error=1:print_stacktrace=1 ./fuzzer <corpus_directory_or_single_testcase_path> |
| ``` |
| |
| |
| [Address Sanitizer]: http://clang.llvm.org/docs/AddressSanitizer.html |
| [Memory Sanitizer]: http://clang.llvm.org/docs/MemorySanitizer.html |
| [Undefined Behavior Sanitizer]: http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html |
| [ClusterFuzz status]: clusterfuzz.md#Status-Links |
| [crbug/598448]: https://bugs.chromium.org/p/chromium/issues/detail?id=598448 |
| [Efficient Fuzzer Guide]: efficient_fuzzer.md |
| [Maximum Testcase Length]: efficient_fuzzer.md#Maximum-Testcase-Length |
| [url_parse_fuzzer.cc]: https://code.google.com/p/chromium/codesearch#chromium/src/testing/libfuzzer/fuzzers/url_parse_fuzzer.cc |