testing/libfuzzer/getting_started.md
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]
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.
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:
#include "third_party/fuzztest/src/fuzztest/fuzztest.h"`
Create a function with assertions that verify the expected behavior. For example:
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:
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:
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.
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.
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.
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.
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:
[==========] 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:
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:
[*] 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 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!
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 [email protected].