blob: 3ba7a5bf61cf8ebda73c5b8d74a44c4e78b24b8f [file] [log] [blame] [view]
---
title: Go Fuzzing
layout: article
---
[Back to Go Security](/security)
Go supports fuzzing in its standard toolchain beginning in Go 1.18. Native Go fuzz tests are
[supported by OSS-Fuzz](https://google.github.io/oss-fuzz/getting-started/new-project-guide/go-lang/#native-go-fuzzing-support).
**Try out the [tutorial for fuzzing with Go](/doc/tutorial/fuzz).**
## Overview
Fuzzing is a type of automated testing which continuously manipulates inputs to
a program to find bugs. Go fuzzing uses coverage guidance to intelligently walk
through the code being fuzzed to find and report failures to the user. Since it
can reach edge cases which humans often miss, fuzz testing can be particularly
valuable for finding security exploits and vulnerabilities.
Below is an example of a [fuzz test](#glos-fuzz-test), highlighting its main
components.
<img alt="Example code showing the overall fuzz test, with a fuzz target within
it. Before the fuzz target is a corpus addition with f.Add, and the parameters
of the fuzz target are highlighted as the fuzzing arguments."
src="/security/fuzz/example.png" style="display: block; width: 600px; height:
auto;"/>
## Writing fuzz tests
### Requirements
Below are rules that fuzz tests must follow.
- A fuzz test must be a function named like `FuzzXxx`, which accepts only a
`*testing.F`, and has no return value.
- Fuzz tests must be in \*\_test.go files to run.
- A [fuzz target](#glos-fuzz-target) must be a method call to
<code>[(\*testing.F).Fuzz](https://pkg.go.dev/testing#F.Fuzz)</code> which
accepts a `*testing.T` as the first parameter, followed by the fuzzing
arguments. There is no return value.
- There must be exactly one fuzz target per fuzz test.
- All [seed corpus](#glos-seed-corpus) entries must have types which are
identical to the [fuzzing arguments](#glos-fuzzing-arguments), in the same order.
This is true for calls to
<code>[(\*testing.F).Add](https://pkg.go.dev/testing#F.Add)</code> and any
corpus files in the testdata/fuzz directory of the fuzz test.
- The fuzzing arguments can only be the following types:
- `string`, `[]byte`
- `int`, `int8`, `int16`, `int32`/`rune`, `int64`
- `uint`, `uint8`/`byte`, `uint16`, `uint32`, `uint64`
- `float32`, `float64`
- `bool`
### Suggestions {#suggestions}
Below are suggestions that will help you get the most out of fuzzing.
- Fuzz targets should be fast and deterministic so the fuzzing engine can work
efficiently, and new failures and code coverage can be easily reproduced.
- Since the fuzz target is invoked in parallel across multiple workers and in
nondeterministic order, the state of a fuzz target should not persist past the
end of each call, and the behavior of a fuzz target should not depend on
global state.
## Running fuzz tests
There are two modes of running your fuzz test: as a unit test (default `go test`), or
with fuzzing (`go test -fuzz=FuzzTestName`).
Fuzz tests are run much like a unit test by default. Each [seed corpus
entry](#glos-seed-corpus) will be tested against the fuzz target, reporting any
failures before exiting.
To enable fuzzing, run `go test` with the `-fuzz` flag, providing a regex
matching a single fuzz test. By default, all other tests in that package will
run before fuzzing begins. This is to ensure that fuzzing won't report any
issues that would already be caught by an existing test.
Note that it is up to you to decide how long to run fuzzing. It is very possible
that an execution of fuzzing could run indefinitely if it doesn't find any errors.
There will be support to run these fuzz tests continuously using tools like OSS-Fuzz
in the future, see [Issue #50192](https://github.com/golang/go/issues/50192).
**Note:** Fuzzing should be run on a platform that supports coverage
instrumentation (currently AMD64 and ARM64) so that the corpus can meaningfully
grow as it runs, and more code can be covered while fuzzing.
### Command line output
While fuzzing is in progress, the [fuzzing engine](#glos-fuzzing-engine)
generates new inputs and runs them against the provided fuzz target. By default,
it continues to run until a [failing input](#glos-failing-input) is found, or
the user cancels the process (e.g. with Ctrl^C).
The output will look something like this:
```
~ go test -fuzz FuzzFoo
fuzz: elapsed: 0s, gathering baseline coverage: 0/192 completed
fuzz: elapsed: 0s, gathering baseline coverage: 192/192 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 325017 (108336/sec), new interesting: 11 (total: 202)
fuzz: elapsed: 6s, execs: 680218 (118402/sec), new interesting: 12 (total: 203)
fuzz: elapsed: 9s, execs: 1039901 (119895/sec), new interesting: 19 (total: 210)
fuzz: elapsed: 12s, execs: 1386684 (115594/sec), new interesting: 21 (total: 212)
PASS
ok foo 12.692s
```
The first lines indicate that the "baseline coverage" is gathered before
fuzzing begins.
To gather baseline coverage, the fuzzing engine executes both the [seed
corpus](#glos-seed-corpus) and the [generated corpus](#glos-generated-corpus), to
ensure that no errors occurred and to understand the code coverage the existing
corpus already provides.
The lines following provide insight into the active fuzzing execution:
- elapsed: the amount of time that has elapsed since the process began
- execs: the total number of inputs that have been run against the fuzz target
(with an average execs/sec since the last log line)
- new interesting: the total number of "interesting" inputs that have been
added to the generated corpus during this fuzzing execution (with the total
size of the entire corpus)
For an input to be "interesting", it must expand the code coverage beyond what
the existing generated corpus can reach. It's typical for the number of new
interesting inputs to grow quickly at the start and eventually slow down, with
occasional bursts as new branches are discovered.
You should expect to see the "new interesting" number taper off over time as the
inputs in the corpus begin to cover more lines of the code, with occasional
bursts if the fuzzing engine finds a new code path.
### Failing input
A failure may occur while fuzzing for several reasons:
- A panic occurred in the code or the test.
- The fuzz target called `t.Fail`, either directly or through methods such as
`t.Error` or `t.Fatal`.
- A non-recoverable error occurred, such as an `os.Exit` or stack overflow.
- The fuzz target took too long to complete. Currently, the timeout for an
execution of a fuzz target is 1 second. This may fail due to a deadlock or
infinite loop, or from intended behavior in the code. This is one reason why
it is [suggested that your fuzz target be fast](#suggestions).
If an error occurs, the fuzzing engine will attempt to minimize the input to the
smallest possible and most human readable value which will still produce an
error. To configure this, see the [custom settings](#custom-settings) section.
Once minimization is complete, the error message will be logged, and the output
will end with something like this:
```
Failing input written to testdata/fuzz/FuzzFoo/a878c3134fe0404d44eb1e662e5d8d4a24beb05c3d68354903670ff65513ff49
To re-run:
go test -run=FuzzFoo/a878c3134fe0404d44eb1e662e5d8d4a24beb05c3d68354903670ff65513ff49
FAIL
exit status 1
FAIL foo 0.839s
```
The fuzzing engine wrote this [failing input](#glos-failing-input) to the seed
corpus for that fuzz test, and it will now be run by default with `go test`,
serving as a regression test once the bug has been fixed.
The next step for you will be to diagnose the problem, fix the bug, verify the
fix by re-running `go test`, and submit the patch with the new testdata file
acting as your regression test.
### Custom settings {#custom-settings}
The default go command settings should work for most use cases of fuzzing. So
typically, an execution of fuzzing on the command line should look like this:
```
$ go test -fuzz={FuzzTestName}
```
However, the `go` command does provide a few settings when running fuzzing.
These are documented in the [`cmd/go` package docs](https://pkg.go.dev/cmd/go).
To highlight a few:
- `-fuzztime`: the total time or number of iterations that the fuzz target
will be executed before exiting, default indefinitely.
- `-fuzzminimizetime`: the time or number of iterations that the fuzz target
will be executed during each minimization attempt, default 60sec. You can
completely disable minimization by setting `-fuzzminimizetime 0` when fuzzing.
- `-parallel`: the number of fuzzing processes running at once, default
`$GOMAXPROCS`. Currently, setting -cpu during fuzzing has no effect.
## Corpus file format
Corpus files are encoded in a special format. This is the same format for both
the [seed corpus](#glos-seed-corpus), and the [generated
corpus](#glos-generated-corpus).
Below is an example of a corpus file:
```
go test fuzz v1
[]byte("hello\\xbd\\xb2=\\xbc ⌘")
int64(572293)
```
The first line is used to inform the fuzzing engine of the file's encoding
version. Although no future versions of the encoding format are currently
planned, the design must support this possibility.
Each of the lines following are the values that make up the corpus entry, and
can be copied directly into Go code if desired.
In the example above, we have a `[]byte` followed by an `int64`. These types
must match the fuzzing arguments exactly, in that order. A fuzz target for these
types would look like this:
```
f.Fuzz(func(*testing.T, []byte, int64) {})
```
The easiest way to specify your own seed corpus values is to use the
`(*testing.F).Add` method. In the example above, that would look like this:
```
f.Add([]byte("hello\\xbd\\xb2=\\xbc ⌘"), int64(572293))
```
However, you may have large binary files that you'd prefer not to copy as code
into your test, and instead remain as individual seed corpus entries in the
testdata/fuzz/{FuzzTestName} directory. The
[`file2fuzz`](https://pkg.go.dev/golang.org/x/tools/cmd/file2fuzz) tool at
golang.org/x/tools/cmd/file2fuzz can be used to convert these binary files to
corpus files encoded for `[]byte`.
To use this tool:
```
$ go install golang.org/x/tools/cmd/file2fuzz@latest
$ file2fuzz
```
## Resources
- **Tutorial**:
- Try out the [tutorial for fuzzing with Go](/doc/tutorial/fuzz) for a deep
dive into the new concepts.
- For a shorter, introductory tutorial of fuzzing with Go, please see [the
blog post](/blog/fuzz-beta).
- **Documentation**:
- The [`testing`](https://pkg.go.dev//testing#hdr-Fuzzing) package docs
describes the `testing.F` type which is used when writing fuzz tests.
- The [`cmd/go`](https://pkg.go.dev/cmd/go) package docs describe the flags
associated with fuzzing.
- **Technical details**:
- [Design draft](https://golang.org/s/draft-fuzzing-design)
- [Proposal](https://golang.org/issue/44551)
## Glossary {#glossary}
<a id="glos-corpus-entry"></a>
**corpus entry:** An input in the corpus which can be used while fuzzing. This
can be a specially-formatted file, or a call to
<code>[(\*testing.F).Add](https://pkg.go.dev/testing#F.Add)</code>.
<a id="glos-coverage-guidance"></a>
**coverage guidance:** A method of fuzzing which uses expansions in code
coverage to determine which corpus entries are worth keeping for future use.
<a id="glos-failing-input"></a>
**failing input:** A failing input is a corpus entry that will cause an error
or panic when run against the [fuzz target](#glos-fuzz-target).
<a id="glos-fuzz-target"></a>
**fuzz target:** The function of the fuzz test which is executed for corpus
entries and generated values while fuzzing. It is provided to the fuzz test by
passing the function to
<code>[(\*testing.F).Fuzz](https://pkg.go.dev/testing#F.Fuzz)</code>.
<a id="glos-fuzz-test"></a>
**fuzz test:** A function in a test file of the form `func FuzzXxx(*testing.F)`
which can be used for fuzzing.
<a id="glos-fuzzing"></a>
**fuzzing:** A type of automated testing which continuously manipulates inputs
to a program to find issues such as bugs or
[vulnerabilities](#glos-vulnerability) to which the code may be susceptible.
<a id="glos-fuzzing-arguments"></a>
**fuzzing arguments:** The types which will be passed to the fuzz target, and
mutated by the [mutator](#glos-mutator).
<a id="glos-fuzzing-engine"></a>
**fuzzing engine:** A tool that manages fuzzing, including maintaining the
corpus, invoking the mutator, identifying new coverage, and reporting failures.
<a id="glos-generated-corpus"></a>
**generated corpus:** A corpus which is maintained by the fuzzing engine over
time while fuzzing to keep track of progress. It is stored in `$GOCACHE`/fuzz.
These entries are only used while fuzzing.
<a id="glos-mutator"></a>
**mutator:** A tool used while fuzzing which randomly manipulates corpus entries
before passing them to a fuzz target.
<a id="glos-package"></a>
**package:** A collection of source files in the same directory that are
compiled together. See the [Packages section](/ref/spec#Packages) in the Go
Language Specification.
<a id="glos-seed-corpus"></a>
**seed corpus:** A user-provided corpus for a fuzz test which can be used to
guide the fuzzing engine. It is composed of the corpus entries provided by f.Add
calls within the fuzz test, and the files in the testdata/fuzz/{FuzzTestName}
directory within the package. These entries are run by default with `go test`,
whether fuzzing or not.
<a id="glos-test-file"></a>
**test file:** A file of the format xxx_test.go that may contain tests,
benchmarks, examples and fuzz tests.
<a id="glos-vulnerability"></a>
**vulnerability:** A security-sensitive weakness in code which can be exploited
by an attacker.
## Feedback
If you experience any problems or have an idea for a feature, please [file an
issue](https://github.com/golang/go/issues/new?&labels=fuzz).
For discussion and general feedback about the feature, you can also participate
in the [#fuzzing channel](https://gophers.slack.com/archives/CH5KV1AKE) in
Gophers Slack.