Go Fuzzing

Mayur Wadekar
Towards Dev
Published in
4 min readAug 1, 2022

--

What is Fuzzing?

Fuzzing is the technique where you can find undetected defects in your software. In fuzz testing, there are so many random inputs given to the system so code crashes and information leak issues might do cover for your software. In cooperation with the Core Infrastructure Initiative and the OpenSSF, OSS-Fuzz aims to make common open source software more secure and stable by combining modern fuzzing techniques with scalable, distributed execution.

Currently, OSS-Fuzz supports C/C++, Rust, Go, Python and Java/JVM code. Other languages supported by LLVM may work too. OSS-Fuzz supports fuzzing x86_64 and i386 builds.

Go supports fuzzing in its standard toolchain beginning in Go 1.18. Native Go fuzz tests are supported by OSS-Fuzz.

Image Credit: https://towardsdatascience.com/

Writing Fuzz Tests in Go

As from the above we already know, Fuzz testing is a kind of automation testing where you can feed input to the program which automatically finds a bug if any. Go fuzzing using an intelligent approach to provide failure reports to users.

Some points which need to be understood while writing fuzz test cases

  1. As Fuzz is of testing part so Fuzz test cases should be written into *_test.go file.
  2. A fuzz test must be a function named like FuzzXxx, which accepts only a *testing.F, and has no return value.
  3. A fuzz target must be a method call to (*testing.F).Fuzz which accepts a *testing.T as the first parameter, followed by the fuzzing arguments. There is no return value.
  4. There must be exactly one fuzz target per fuzz test.
  5. 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

Let’s try things with code. So I have written a simple function which is doing the addition of two numbers.

add.go

Let’s write a test case for the above code.

add_test.go

Running Fuzz tests in Go

In the above test file, We have used the Fuzz function to fill the input to the function. Where no1 and no2 are the fuzzing arguments, the whole fuzz function is written in the FuzzAdd test case called Fuzz target.

We have simply written down that if the function generates an output of more than 400 then our test case will fail.

So to run fuzz test cases you have to pass -fuzz flag with go test

$ go test -fuzz=FuzzAdd

Above will run fuzz test. If the test pass then manually we have to stop the fuzz test from running, but if the test fails then it will show each seed corpus entry in the file in testdata/fuzz/{FuzzTestName} directory.

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.

mayurwadekar@Mayurs-MacBook-Pro fuzzTesting % go test -fuzz=FuzzAdd
fuzz: elapsed: 0s, gathering baseline coverage: 0/1 completed
fuzz: elapsed: 0s, gathering baseline coverage: 1/1 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 1051853 (350547/sec), new interesting: 0 (total: 1)
fuzz: elapsed: 6s, execs: 2130515 (359512/sec), new interesting: 0 (total: 1)
fuzz: elapsed: 9s, execs: 3195614 (355078/sec), new interesting: 0 (total: 1)
fuzz: elapsed: 12s, execs: 4235019 (346527/sec), new interesting: 0 (total: 1)
fuzz: elapsed: 15s, execs: 5295266 (353409/sec), new interesting: 0 (total: 1)
fuzz: elapsed: 18s, execs: 6355479 (353299/sec), new interesting: 0 (total: 1)
^C
PASS
ok Go_Exercise/fuzzTesting 18.030s

The above first line indicates that the “gathering baseline” is gathered before fuzzing.

elapsed indicates elapsed time since the process began. execs indicate the number of executions. new interesting the total number of “interesting” inputs that have been added to the generated corpus.

Failed execution

mayurwadekar@Mayurs-MacBook-Pro fuzzTesting % go test -fuzz=FuzzAdd
fuzz: elapsed: 0s, gathering baseline coverage: 0/1 completed
fuzz: elapsed: 0s, gathering baseline coverage: 1/1 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 1044848 (348174/sec), new interesting: 0 (total: 1)
fuzz: elapsed: 6s, execs: 2050565 (346278/sec), new interesting: 0 (total: 1)
--- FAIL: FuzzAdd (5.91s)
--- FAIL: FuzzAdd (0.00s)
add_test.go:13: 401

Failing input written to testdata/fuzz/FuzzAdd/5162d02948747beee18ea2f1c99a469af7808a934ac19a55bdc8f05479f028e5
To re-run:
go test -run=FuzzAdd/5162d02948747beee18ea2f1c99a469af7808a934ac19a55bdc8f05479f028e5
FAIL
exit status 1
FAIL Go_Exercise/fuzzTesting 6.014s

If the fuzz test fails then the fuzz engine tries to minimize input smallest and human-readable form which also might possibly cause an error.

In the above-failed case, it has generated a file on the location mentioned below

Failing input written to testdata/fuzz/FuzzAdd/5162d02948747beee18ea2f1c99a469af7808a934ac19a55bdc8f05479f028e5

Corpus File Format

go test fuzz v1
int(273)
int(128)

So in the above file input have provided to the fuzz function as below. The first line indicates the encoding version of a file.

f.Fuzz(func(t *testing.T, 273, 128)

Custom settings

  • -fuzztime: the total time or number of iterations that the fuzz target will be executed. Default it is indefinitely.
$ go test -fuzz=FuzzAdd -fuzztime=1s
  • -fuzzminimizetime: the time or number of iterations that the fuzz target will be executed during each minimization attempt, default 60sec. But you can disable it using set it to 0sec.
$ go test -fuzz=FuzzAdd -fuzzminimizetime=33s
  • -parallel: the number of fuzzing processes running at once, default $GOMAXPROCS.
$ go test -fuzz=FuzzAdd -parallel=10

So in conclusion I might say that by using fuzzing you can identify hidden bugs in your software that might miss using the unit tests. So fuzzing provides little surety that your code will perform well in unknown kinds of inputs and will not panic in a production environment. Also remember Fuzzing works on Go basic data types.

I hope you’ve enjoyed reading. :)

Resources:

  1. https://go.dev/doc/fuzz/
  2. https://google.github.io/oss-fuzz/
  3. https://www.synopsys.com/glossary/what-is-fuzz-testing.html
  4. https://go.dev/doc/tutorial/fuzz

--

--