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
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:
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.
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.
Fuzzing is a great way to improve your code. Wanna join the fuzzing revolution? try out fuzzit for free for your OSS project.