| # Getting started with fuzzing in Chromium |
| |
| This document explains how to add fuzz tests to Chromium using [FuzzTest]. |
| |
| If you need to maintain an existing libFuzzer target, see |
| [Getting Started with libFuzzer]. libFuzzer is deprecated for new fuzz targets. |
| |
| [TOC] |
| |
| ## What to fuzz |
| |
| You should fuzz any function that takes input from an untrusted source, such as |
| the internet. If the code parses, decodes, or otherwise manipulates that input, |
| you should write a fuzz test for it. |
| |
| To view directories that currently lack fuzz coverage, see |
| [go/chrome-fuzzing-dashboard]. |
| |
| ## Write the FuzzTest code |
| |
| We recommend writing your fuzz tests in the same file as your standard unit tests. |
| Open your existing unit test file and include the FuzzTest header: |
| |
| ```c++ |
| #include "third_party/fuzztest/src/fuzztest/fuzztest.h"` |
| ``` |
| |
| Create a function with assertions that verify the expected behavior. For example: |
| |
| ```c++ |
| void MyApiCanSuccessfullyParseAnyString(std::string input) { |
| bool success = MyApi(input); |
| EXPECT_TRUE(success); |
| } |
| ``` |
| |
| In the same file, declare the `FUZZ_TEST` macro directly below your function: |
| |
| ```c++ |
| FUZZ_TEST(MyApiTest, MyApiCanSuccessfullyParseAnyString); |
| ``` |
| |
| The fuzzing infrastructure mutates the input based on code coverage to explore |
| branches within your API. Because generating all possible strings requires |
| infinite time, the fuzzer crafts inputs designed to maximize code coverage and |
| find bugs efficiently. |
| |
| Fuzz tests do not replace traditional unit tests. Use fuzzing as a supplementary |
| testing strategy to help prevent unexpected security flaws. |
| |
| For complex cases, specify the expected domains of valid input. For example: |
| |
| ```c++ |
| void MyApiAlwaysSucceedsOnPositiveIntegers(int i) { |
| bool success = MyApi(i); |
| EXPECT_TRUE(success); |
| } |
| FUZZ_TEST(MyApiTest, MyApiAlwaysSucceedsOnPositiveIntegers) |
| .WithDomains(/*i:*/fuzztest::Positive<int>()); |
| ``` |
| |
| For more information about configuring input domains, see the |
| [FuzzTest Domain Reference]. |
| |
| ## Configure the GN build target |
| |
| Now that your test is written, add it to your existing unit test target. Open |
| your `BUILD.gn` file, locate your existing `test` target, and add the |
| `fuzztests` list containing the name of your new test. |
| |
| ```gn |
| import("//testing/test.gni") |
| |
| test("my_component_unittests") { |
| sources = [ |
| "my_component_unittest.cc", # Your FUZZ_TEST macros are in this file. |
| ] |
| |
| # Add your FuzzTest names here: |
| fuzztests = [ "MyApiTest.MyApiCanSuccessfullyParseAnyString" ] |
| |
| deps = [ |
| ":my_component", |
| |
| # Chromium unit tests usually already include this dependency. |
| # It provides the standard test main() that initializes FuzzTest. |
| "//base/test:test_support", |
| ] |
| } |
| ``` |
| |
| Because Chromium's [`test` template] automatically injects FuzzTest dependencies |
| and initializes the framework, you do not need to add any specific FuzzTest |
| libraries to your `deps`. |
| |
| ## Update your DEPS file |
| |
| If your component's directory enforces include rules, you must add |
| `+third_party/fuzztest` to your local DEPS file so that your C++ files can |
| successfully include the FuzzTest headers. |
| |
| ## Run the FuzzTest locally |
| |
| In a local terminal, build and run your unit tests. If your platform supports |
| FuzzTest, the test runs for one second and produces the following output: |
| |
| ```shell |
| [==========] Running 1 test from 1 test suite. |
| [----------] Global test environment set-up. |
| [----------] 1 test from ScaleFuzz |
| [ RUN ] ApiTest.MyApiCanSuccessfullyParseAnyString |
| [ OK ] ApiTest.MyApiCanSuccessfullyParseAnyString (1000 ms) |
| [----------] 1 test from ScaleFuzz (1000 ms total) |
| |
| [----------] Global test environment tear-down |
| [==========] 1 test from 1 test suite ran. (1000 ms total) |
| [ PASSED ] 1 test. |
| ``` |
| |
| On unsupported platforms, the system ignores the test. |
| |
| To perform continuous fuzzing with FuzzTest locally, add the following to your |
| gn arguments: |
| |
| ```gn |
| enable_fuzztest_fuzz=true |
| is_component_build=false |
| ``` |
| |
| Run your unit test and append the `--fuzz=` argument. You can optionally specify |
| a target test name by using `--fuzz=TEST_NAME`. The command produces |
| the following output as it explores your code: |
| |
| ```shell |
| [*] Corpus size: 1 | Edges covered: 73 | Fuzzing time: 1.60482ms | Total runs: 1.00e+00 | Runs/secs: 623 | Max stack usage: 0 |
| [*] Corpus size: 2 | Edges covered: 103 | Fuzzing time: 1.844ms | Total runs: 2.00e+00 | Runs/secs: 1084 | Max stack usage: 0 |
| [*] Corpus size: 3 | Edges covered: 111 | Fuzzing time: 2.747931ms | Total runs: 3.00e+00 | Runs/secs: 1091 | Max stack usage: 0 |
| [*] Corpus size: 4 | Edges covered: 135 | Fuzzing time: 2.92305ms | Total runs: 4.00e+00 | Runs/secs: 1368 | Max stack usage: 0 |
| [*] Corpus size: 5 | Edges covered: 173 | Fuzzing time: 3.35237ms | Total runs: 5.00e+00 | Runs/secs: 1491 | Max stack usage: 0 |
| [*] Corpus size: 6 | Edges covered: 178 | Fuzzing time: 4.15666ms | Total runs: 6.00e+00 | Runs/secs: 1443 | Max stack usage: 0 |
| ``` |
| |
| The **Edges covered** metric indicates the number of unique code blocks the |
| fuzzer explores. Over time, the fuzzer explores more blocks until it exhausts |
| new edges. |
| |
| ## Submit the Fuzztest |
| |
| Submit your changelist by using standard Chromium contribution processes. |
| |
| [ClusterFuzz] typically begins running your new fuzzer within two days. To view |
| the fuzzer, go to [ClusterFuzz Fuzzer Stats] and search for your test target's |
| name. FuzzTest targets typically use the `centipede_` or `libfuzzer_` prefix in |
| the dashboard. |
| |
| Thanks very much for doing your part in making Chromium more secure! |
| |
| ## Get help with advanced cases |
| |
| If FuzzTest and libFuzzer do not meet your requirements, you can explore |
| advanced options, such as uploading blackbox fuzzers to ClusterFuzz or running |
| external fuzzers that upload results to ClusterFuzz for triage. To discuss these |
| options, contact the fuzzing team at chrome-fuzzing-core@google.com. |
| |
| [ClusterFuzz]: https://clusterfuzz.com/ |
| [FuzzTest Domain Reference]: https://github.com/google/fuzztest/blob/main/doc/domains-reference.md |
| [ClusterFuzz Fuzzer Stats]: https://clusterfuzz.com/fuzzer-stats/by-fuzzer/fuzzer/libFuzzer/job/libfuzzer_chrome_asan |
| [fuzzer builders]: https://ci.chromium.org/p/chromium/g/chromium.fuzz/console |
| [FuzzTest]: https://github.com/google/fuzztest |
| [Getting Started with libFuzzer]: getting_started_with_libfuzzer.md |
| [go/chrome-fuzzing-dashboard]: (https://analysis.chromium.org/coverage/p/chromium?platform=fuzz). |
| [`test` template]: https://source.chromium.org/chromium/chromium/src/+/main:testing/test.gni?q=test.gni |