aizatsky | 9c8c5b0 | 2016-03-30 22:09:09 | [diff] [blame] | 1 | # Getting Started with libFuzzer in Chrome |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 2 | |
| 3 | *** note |
mmoroz | 27ea9c2 | 2016-04-07 21:17:48 | [diff] [blame] | 4 | **Prerequisites:** libFuzzer in Chrome is supported with GN on Linux only. |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 5 | *** |
| 6 | |
| 7 | This document will walk you through: |
| 8 | |
| 9 | * setting up your build enviroment. |
| 10 | * creating your first fuzzer. |
| 11 | * running the fuzzer and verifying its vitals. |
| 12 | |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 13 | ## Configure Build |
| 14 | |
| 15 | Use `use_libfuzzer` GN argument together with sanitizer to generate build files: |
| 16 | |
mmoroz | db4e66a1 | 2016-11-04 22:14:24 | [diff] [blame^] | 17 | *Notice*: current implementation also supports `use_afl` argument, but it is |
| 18 | recommended to use libFuzzer for development. Running libFuzzer locally doesn't |
| 19 | require any special configuration and quickly gives meaningful output for speed, |
| 20 | coverage and other parameters. |
| 21 | |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 22 | ```bash |
| 23 | # With address sanitizer |
aizatsky | 36f50994 | 2016-10-13 23:05:17 | [diff] [blame] | 24 | gn gen out/libfuzzer '--args=use_libfuzzer=true is_asan=true is_debug=false enable_nacl=false' --check |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 25 | ``` |
| 26 | |
| 27 | Supported sanitizer configurations are: |
| 28 | |
| 29 | | GN Argument | Description | |
| 30 | |--------------|----| |
aizatsky | 8f19edd | 2016-09-01 20:54:50 | [diff] [blame] | 31 | | `is_asan=true` | enables [Address Sanitizer] to catch problems like buffer overruns. | |
| 32 | | `is_msan=true` | enables [Memory Sanitizer] to catch problems like uninitialed reads. | |
| 33 | | `is_ubsan_security=true` | enables [Undefined Behavior Sanitizer] to catch<sup>\[[1](#Notes)\]</sup> undefined behavior like integer overflow. | |
| 34 | | | it is possible to run libfuzzer without any sanitizers; *probably not what you want*.| |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 35 | |
| 36 | |
| 37 | ## Write Fuzzer Function |
| 38 | |
| 39 | Create a new .cc file and define a `LLVMFuzzerTestOneInput` function: |
| 40 | |
| 41 | ```cpp |
reillyg | 21fe4450 | 2016-05-20 22:28:50 | [diff] [blame] | 42 | #include <stddef.h> |
| 43 | #include <stdint.h> |
| 44 | |
| 45 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 46 | // put your fuzzing code here and use data+size as input. |
| 47 | return 0; |
| 48 | } |
| 49 | ``` |
| 50 | |
| 51 | [url_parse_fuzzer.cc] is a simple example of real-world fuzzer. |
| 52 | |
| 53 | ## Define GN Target |
| 54 | |
| 55 | Define `fuzzer_test` GN target: |
| 56 | |
aizatsky | 00a0620 | 2016-10-10 21:58:52 | [diff] [blame] | 57 | ```python |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 58 | import("//testing/libfuzzer/fuzzer_test.gni") |
| 59 | fuzzer_test("my_fuzzer") { |
| 60 | sources = [ "my_fuzzer.cc" ] |
| 61 | deps = [ ... ] |
| 62 | } |
| 63 | ``` |
| 64 | |
| 65 | ## Build and Run Fuzzer Locally |
| 66 | |
| 67 | Build with ninja as usual and run: |
| 68 | |
| 69 | ```bash |
| 70 | ninja -C out/libfuzzer url_parse_fuzzer |
mmoroz | 27ea9c2 | 2016-04-07 21:17:48 | [diff] [blame] | 71 | ./out/libfuzzer/url_parse_fuzzer |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 72 | ``` |
| 73 | |
| 74 | Your fuzzer should produce output like this: |
| 75 | |
| 76 | ``` |
| 77 | INFO: Seed: 1787335005 |
| 78 | INFO: -max_len is not provided, using 64 |
| 79 | INFO: PreferSmall: 1 |
| 80 | #0 READ units: 1 exec/s: 0 |
| 81 | #1 INITED cov: 2361 bits: 95 indir: 29 units: 1 exec/s: 0 |
| 82 | #2 NEW cov: 2710 bits: 359 indir: 36 units: 2 exec/s: 0 L: 64 MS: 0 |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 83 | ``` |
| 84 | |
aizatsky | 9c8c5b0 | 2016-03-30 22:09:09 | [diff] [blame] | 85 | The `... NEW ...` line appears when libFuzzer finds new and interesting input. The |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 86 | efficient fuzzer should be able to finds lots of them rather quickly. |
aizatsky | 9b70cf1b | 2016-03-18 19:33:50 | [diff] [blame] | 87 | The `... pulse ...` line will appear periodically to show the current status. |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 88 | |
aizatsky | 00a0620 | 2016-10-10 21:58:52 | [diff] [blame] | 89 | ## Improving Your Fuzzer |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 90 | |
aizatsky | 00a0620 | 2016-10-10 21:58:52 | [diff] [blame] | 91 | Your fuzzer may immediately discover interesting (i.e. crashing) inputs. |
| 92 | To make it more efficient, several small steps can take you really far: |
mmoroz | 9e07559 | 2016-05-06 08:37:53 | [diff] [blame] | 93 | |
aizatsky | 00a0620 | 2016-10-10 21:58:52 | [diff] [blame] | 94 | * Create seed corpus. Add `seed_corpus = "src/fuzz-testcases/"` attribute |
| 95 | to your fuzzer targets and add example files in appropriate folder. Read more |
| 96 | in [Seed Corpus] section of efficient fuzzer guide. |
| 97 | *Make sure corpus files are appropriately licensed.* |
| 98 | * Create mutation dictionary. With a `dict = "protocol.dict"` attribute and |
| 99 | `key=value` dicitionary file format, mutations can be more effective. |
| 100 | See [Fuzzer Dictionary]. |
| 101 | * Specify maximum testcase length. By default libFuzzer uses `-max_len=64` |
| 102 | (or takes the longest testcase in a corpus). ClusterFuzz takes |
mmoroz | 9e07559 | 2016-05-06 08:37:53 | [diff] [blame] | 103 | random value in range from `1` to `10000` for each fuzzing session and passes |
| 104 | that value to libFuzzers. If corpus contains testcases of size greater than |
aizatsky | 00a0620 | 2016-10-10 21:58:52 | [diff] [blame] | 105 | `max_len`, libFuzzer will use only first `max_len` bytes of such testcases. |
| 106 | See [Maximum Testcase Length]. |
mmoroz | 9e07559 | 2016-05-06 08:37:53 | [diff] [blame] | 107 | |
robert.bradford | 160c598 | 2016-08-30 17:04:30 | [diff] [blame] | 108 | ## Disable noisy error message logging |
| 109 | |
| 110 | If the code that you are a fuzzing generates error messages when encountering |
| 111 | incorrect or invalid data then you need to silence those errors in the fuzzer. |
| 112 | |
| 113 | If the target uses the Chromium logging APIs, the best way to do that is to |
| 114 | override the environment used for logging in your fuzzer: |
| 115 | |
| 116 | ```cpp |
| 117 | struct Environment { |
| 118 | Environment() { |
| 119 | logging::SetMinLogLevel(logging::LOG_FATAL); |
| 120 | } |
| 121 | }; |
| 122 | |
| 123 | Environment* env = new Environment(); |
| 124 | ``` |
| 125 | |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 126 | ## Submitting Fuzzer to ClusterFuzz |
| 127 | |
| 128 | ClusterFuzz builds and executes all `fuzzer_test` targets in the source tree. |
| 129 | The only thing you should do is to submit a fuzzer into Chrome. |
| 130 | |
| 131 | ## Next Steps |
| 132 | |
| 133 | * After your fuzzer is submitted, you should check its [ClusterFuzz status] in |
| 134 | a day or two. |
| 135 | * Check the [Efficient Fuzzer Guide] to better understand your fuzzer |
| 136 | performance and for optimization hints. |
| 137 | |
| 138 | |
mmoroz | 5a2604d4 | 2016-04-21 10:08:13 | [diff] [blame] | 139 | ## Notes |
| 140 | [1] By default UBSan doesn't crash once undefined behavior has been detected. |
| 141 | To make it crash the following additional option should be provided: |
| 142 | |
| 143 | ```bash |
| 144 | UBSAN_OPTIONS=halt_on_error=1 ./fuzzer <corpus_directory_or_single_testcase_path> |
| 145 | ``` |
| 146 | |
| 147 | Other useful options (used by ClusterFuzz) are: |
| 148 | ```bash |
| 149 | UBSAN_OPTIONS=symbolize=1:halt_on_error=1:print_stacktrace=1 ./fuzzer <corpus_directory_or_single_testcase_path> |
| 150 | ``` |
| 151 | |
| 152 | |
aizatsky | a6f8629 | 2016-03-18 00:22:24 | [diff] [blame] | 153 | [Address Sanitizer]: http://clang.llvm.org/docs/AddressSanitizer.html |
mmoroz | 9e07559 | 2016-05-06 08:37:53 | [diff] [blame] | 154 | [ClusterFuzz status]: clusterfuzz.md#Status-Links |
mmoroz | 9e07559 | 2016-05-06 08:37:53 | [diff] [blame] | 155 | [Efficient Fuzzer Guide]: efficient_fuzzer.md |
aizatsky | 00a0620 | 2016-10-10 21:58:52 | [diff] [blame] | 156 | [Fuzzer Dictionary]: efficient_fuzzer.md#Fuzzer-Dictionary |
mmoroz | 9e07559 | 2016-05-06 08:37:53 | [diff] [blame] | 157 | [Maximum Testcase Length]: efficient_fuzzer.md#Maximum-Testcase-Length |
aizatsky | 00a0620 | 2016-10-10 21:58:52 | [diff] [blame] | 158 | [Memory Sanitizer]: http://clang.llvm.org/docs/MemorySanitizer.html |
| 159 | [Seed Corpus]: efficient_fuzzer.md#Seed-Corpus |
| 160 | [Undefined Behavior Sanitizer]: http://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html |
| 161 | [crbug/598448]: https://bugs.chromium.org/p/chromium/issues/detail?id=598448 |
aizatsky | 9c8c5b0 | 2016-03-30 22:09:09 | [diff] [blame] | 162 | [url_parse_fuzzer.cc]: https://code.google.com/p/chromium/codesearch#chromium/src/testing/libfuzzer/fuzzers/url_parse_fuzzer.cc |