How to Fuzz Go Code with go-fuzz (Continuously)

What is fuzzing?

Fuzzing or fuzz testing is an automated software technique that involves providing semi-random data as input to the test program in order to uncover bugs and crashes.


Why fuzz Go Code?

Golang is a safe language and memory corruption issues are a thing of the past so we don’t need to fuzz our code, right? wrong:). Any code where stability quality and coverage are important is usually worth fuzzing. Also fuzzing can uncover logical bugs and Denial-of-service where in critical components can lead to security issues as well. Also, you can checkout our previous discussion about why fuzz go?

As a reference to almost infinite amount of bugs found with go-fuzz (only the documented one) you can look here


Enter go-fuzz

go-fuzz is the current de-facto standard fuzzer for go and was initially developed by Dmitry Vyukov. It is a coverage guided fuzzer which means it uses coverage instrumentation and feedback to generate test-cases which proved to be very successful both by go-fuzz and originally by fuzzers like AFL.

go-fuzz algorithm and in general coverage guided fuzzers works as follows:

For a bit more information about under-the-hood of go-fuzz you can watch the great presentation by Dmitry Vyuokov on go-fuzz as well as read the original white-paper for AFL by Michal Zalewski.


Building & Running

If you are already familiar with this part you can skip to Continuous Fuzzing section.

we will use https://github.com/fuzzitdev/example-go as a simple example.

For the sake of the example we have a simple function with an off-by-one bug:

our fuzz function will look like this and will be called by go-fuzz in a infinite loop with the generated data according to the coverage-guided algorithm

To run the fuzzer we need to build an instrumented version of the code together with the fuzz function. This is done with the following simple steps:

This find the bug in a few seconds, prints the “FUZZI” string that triggers the vulnerability and saves it to a file.


Continuous Fuzzing

From our experience integrating continuous fuzzing into OSS projects we believe the following two workflows brings the most value:

The fuzzing workflow helps find new bugs, generate new test-cases and guarantee that your most recent version of the code was fuzzed.

The regression workflow helps validate pull-requests and find bugs before merging. Essentially adding another (free!) layer of unit-tests.

Now let’s look at a concrete example of how .travis.yml looks like though the same logic can be implemented in any CI (Circle,GithubActions, Jenkins, etc…)

here you can see exactly the two workflows:

  • fuzzing – which builds the fuzzers and uploads them to fuzzit so they can run asynchrounsly without blocking the CI.
  • local regression – which run the fuzzers through all generated test-cases and the fixed crashes inline in your CI so it will fail the CI and immediately notify about the bugs.

the fuzzit.sh itself contains the bash script itself that build the fuzz targets and either uploads them to fuzzit or runs locally for regression via fuzzit open-source CLI.

Summary

Fuzzing is a great way to improve your code. Wanna join the fuzzing revolution? try out fuzzit for free for your OSS project.

Comments are closed.