• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Getting started with fuzzing in Chromium
2
3This document walks through how to get started adding fuzz tests to Chromium.
4
5It guides you how to use our latest fuzzing technology, called [FuzzTest]. This
6replaces earlier technology called [libfuzzer]. See the section at the end
7for reasons why you might sometimes still want to use libfuzzer.
8
9[TOC]
10
11## What to fuzz
12
13You should fuzz any function which takes input from any
14untrusted source, such as the internet. If the code parses, decodes, or
15otherwise manipulates that input, it definitely should be fuzzed!
16
17## How to fuzz
18
191. Find your existing unit test target. Create a new similar target
20   alongside. (In the future, you'll be able to add them right into your
21   unit test code directly.)
222. Add a gn target definition a lot like a normal unit test, but with
23   `enable_fuzztest = true`. See below for details. Create a `.cc` file.
243. In the unit tests code, `#include "third_party/fuzztest/src/fuzztest/fuzztest.h"`
254. Add a `FUZZ_TEST` macro, which might be as simple as `FUZZ_TEST(MyApiTest, ExistingFunctionWhichTakesUntrustedInput)`
26   (though you may wish to structure things differently, see below)
275. Run the unit tests and ensure they pass.
286. Land the CL.
29
30That's it!
31
32This fuzzer will be built automatically, using various [sanitizers], and run
33on our distributed fuzzing infrastructure [ClusterFuzz]. If it finds bugs,
34they'll be reported back to you.
35
36More detail in all the following sections.
37
38## Creating a new `FUZZ_TEST` target
39
40*** note
41**Note:** Fuzztests don't yet build on Windows component builds.
42We recommend wrapping these new targets in `if (fuzztest_supported) { }`
43blocks in your `gn` file for now. We'll remove these in future when it works on
44all platforms.
45***
46
47```
48import("//build/config/sanitizers/sanitizers.gni")
49import("//testing/test.gni")
50
51if (fuzztest_supported) {
52  test("hypothetical_fuzztests") {
53    sources = [ "hypothetical_fuzztests.cc" ]
54
55    enable_fuzztest = true
56
57    deps = [
58      ":hypothetical_component",
59      "//third_party/fuzztest:fuzztest_gtest_main",
60    ]
61  }
62}
63```
64
65## Adding `FUZZ_TEST` support to a target
66
67*** note
68**Note:** Currently, you must create a **new** unit test target.
69While the FuzzTest framework supports mixed unit and fuzz tests,
70we don't yet support this option in Chromium.
71***
72
73In the near future we'll support adding `FUZZ_TEST`s alongside existing
74unit tests, even in the same .cc file. You will add an extra
75`enable_fuzztest = true` line:
76
77```
78if (is_linux) {
79  test("existing_unit_tests") {
80    sources = [ "existing_unit_tests.cc" ] # add FUZZ_TESTs here
81
82    enable_fuzztest = true   # add this!
83
84    deps = [
85      ":existing_component",
86      # Other stuff
87    ]
88  }
89}
90```
91
92This will:
93* add a dependency on the appropriate fuzztest libraries;
94* cause the target to be built on all our [fuzzer builders]
95* construct metadata so that [ClusterFuzz] knows how to run the resulting
96  binary.
97
98This relies on something, somewhere, calling `base::LaunchUnitTests` within
99your executable to initialize FuzzTest. This should be the case already.
100
101(If you have other code targets, such as `source_set`s, contributing to your
102unit test target they may need to explicitly depend upon `//third_party/fuzztest`
103too.)
104
105*** note
106**Note:** Again, this is not yet supported!
107***
108
109## Adding `FUZZ_TEST`s in the code
110
111First, `#include "third_party/fuzztest/src/fuzztest/fuzztest.h"`.
112
113Then, it's normal to create a function named after the thing you're trying to
114prove, with assertions to prove it.
115
116For instance,
117
118```
119void MyApiCanSuccessfullyParseAnyString(std::string input) {
120    bool success = MyApi(input);
121    EXPECT_TRUE(success);
122}
123```
124
125Then, declare the `FUZZ_TEST` macro:
126
127```
128FUZZ_TEST(MyApiTest, MyApiCanSuccessfullyParseAnyString);
129```
130
131Our fuzzing infrastructure will generate all possible strings and prove it works.
132Obviously, that takes infinite time, so instead our fuzzing infrastructure will
133carefully craft strings to explore more and more branches within `MyApi`,
134mutating the input according to code coverage, so there's a good chance bugs
135will be found quickly.
136
137Fuzzing should always be alongside traditional unit testing - never rely on it
138to find all the bugs! It should be a backstop to prevent unexpected security
139flaws sneaking past your regular testing.
140
141In more complex cases, you'll need to tell FuzzTest about the expected domains
142of valid input. For example:
143
144```
145void MyApiAlwaysSucceedsOnPositiveIntegers(int i) {
146  bool success = MyApi(i);
147  EXPECT_TRUE(success);
148}
149FUZZ_TEST(MyApiTest, MyApiAlwaysSucceedsOnPositiveIntegers)
150    .WithDomains(/*i:*/fuzztest::Positive<int>());
151```
152
153See the [FuzzTest reference] for all your options here.
154
155## Running this locally
156
157Simply build and run your unit tests as normal. `FUZZ_TEST`s are supported only
158on some platforms. If you're on such a platform, you'll see your fuzz test
159run for one second:
160
161```
162[==========] Running 1 test from 1 test suite.
163[----------] Global test environment set-up.
164[----------] 1 test from ScaleFuzz
165[ RUN      ] ApiTest.MyApiCanSuccessfullyParseAnyString
166[       OK ] ApiTest.MyApiCanSuccessfullyParseAnyString (1000 ms)
167[----------] 1 test from ScaleFuzz (1000 ms total)
168
169[----------] Global test environment tear-down
170[==========] 1 test from 1 test suite ran. (1000 ms total)
171[  PASSED  ] 1 test.
172```
173
174On other platforms, the test will be ignored.
175
176If you want to try actually fuzzing with FuzzTest, add the gn argument
177`enable_fuzztest_fuzz = true`. You can then run your unit test
178with the extra command line argument `--fuzz=`, optionally specifying a test
179name. You'll see lots of output as it explores your code:
180
181```
182[*] Corpus size:     1 | Edges covered:     73 | Fuzzing time:        1.60482ms | Total runs:  1.00e+00 | Runs/secs:   623 | Max stack usage:        0
183[*] Corpus size:     2 | Edges covered:    103 | Fuzzing time:          1.844ms | Total runs:  2.00e+00 | Runs/secs:  1084 | Max stack usage:        0
184[*] Corpus size:     3 | Edges covered:    111 | Fuzzing time:       2.747931ms | Total runs:  3.00e+00 | Runs/secs:  1091 | Max stack usage:        0
185[*] Corpus size:     4 | Edges covered:    135 | Fuzzing time:        2.92305ms | Total runs:  4.00e+00 | Runs/secs:  1368 | Max stack usage:        0
186[*] Corpus size:     5 | Edges covered:    173 | Fuzzing time:        3.35237ms | Total runs:  5.00e+00 | Runs/secs:  1491 | Max stack usage:        0
187[*] Corpus size:     6 | Edges covered:    178 | Fuzzing time:        4.15666ms | Total runs:  6.00e+00 | Runs/secs:  1443 | Max stack usage:        0
188```
189
190("Edges covered") is how many different code blocks have been explored (that is,
191sections between branches). Over time, you'll see it explore more and more until
192it runs out of new edges to explore.
193
194## Landing the CL
195
196Nothing special is required here!
197
198After a day or two, we should see [ClusterFuzz] starting to run your new fuzzer,
199and it should be visible on [ClusterFuzz Fuzzer Stats]. Look for fuzzers starting
200with `centipede_` and your test target's name.
201
202*** note
203**Note:** This is all very new, and ClusterFuzz isn't reliably spotting these
204new fuzztests yet. We're working on it!
205***
206
207Thanks very much for doing your part in making Chromium more secure!
208
209## Unusual cases
210
211There are some situations where FuzzTests may not work. For example:
212
213* You need to run on platforms not currently supported by FuzzTest
214* You need more structured input
215* You need to mutate the input in a more precise way
216
217In these cases, you may be best off creating a standalone fuzzer using our
218older fuzzing technology, [libfuzzer]. There are further options beyond
219that, e.g. uploading "black box" fuzzers to ClusterFuzz, or even running
220fuzzers outside of ClusterFuzz which then upload results to ClusterFuzz
221for triage and diagnosis. To explore any of those options, please discuss
222with the fuzzing team (email security@chromium.org if you're outside Google).
223
224
225[FuzzTest]: https://github.com/google/fuzztest#how-do-i-use-it
226[libfuzzer]: getting_started_with_libfuzzer.md
227[`test` template]: https://source.chromium.org/chromium/chromium/src/+/main:testing/test.gni?q=test.gni
228[fuzzer builders]: https://ci.chromium.org/p/chromium/g/chromium.fuzz/console
229[ClusterFuzz]: https://clusterfuzz.com/
230[FuzzTest reference]: https://github.com/google/fuzztest#how-do-i-use-it
231[ClusterFuzz Fuzzer Stats]: https://clusterfuzz.com/fuzzer-stats/by-fuzzer/fuzzer/libFuzzer/job/libfuzzer_chrome_asan
232