Home

Awesome

fuzzit.dev was acquired by GitLab and the new home for this repo is here

Build Status

Continuous Fuzzing for Golang Example

This is an example of how to integrate your go-fuzz targets with the Fuzzit Continuous Fuzzing Platform (Go support is currently in Beta).

This example will show the following steps:

Result:

Fuzzing for go can help find both complex bugs, as well as correctness bugs. Go is a safe language so memory corruption bugs are very unlikely to happen, but some bugs can still have security implications.

This tutorial focuses less on how to build go-fuzz targets and more on how to integrate the targets with Fuzzit. A lot of great information is available at the go-fuzz repository.

Building go-fuzz Target

The targets that are currently supported on Fuzzit are targets that utilize the libFuzzer engine. This is why we will use the -libfuzzer flag of go-fuzz and compile it on a Linux machine (should also be supported on mac in the future)

Understanding the bug

The bug is located at parser_complex.go in the following code

package parser

func ParseComplex(data [] byte) bool {
	if len(data) == 5 {
		if data[0] == 'F' && data[1] == 'U' && data[2] == 'Z' && data[3] == 'Z' && data[4] == 'I' && data[5] == 'T' {
			return true
		}
	}
	return false
}

This is the simplest example to demonstrate a classic off-by-one/out-of-bounds error which causes the program to crash. Instead of len(data) == 5 the correct code will be len(data) == 6.

Understanding the fuzzer

the fuzzer is located at parse_complex_fuzz.go in the following code:

// +build gofuzz

package parser

func Fuzz(data []byte) int {
	ParseComplex(data)
	return 0
}

Setting up the development environment

docker run -it gcr.io/fuzzit-public/buster-golang12:2dc7875 /bin/bash

# Download this example
go get github.com/fuzzitdev/example-go

Building the fuzzer

cd /go/src/github.com/fuzzitdev/example-go
go-fuzz-build -libfuzzer -o parse-complex.a .
clang -fsanitize=fuzzer parse-complex.a -o parse-complex

Running the fuzzer

./parse-complex

Will print the following output and stacktrace:

INFO: Seed: 3709860458
INFO: 65536 Extra Counters
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED ft: 4 corp: 1/1b exec/s: 0 rss: 25Mb
#213    NEW    ft: 6 corp: 2/6b lim: 6 exec/s: 0 rss: 25Mb L: 5/5 MS: 1 CMP- DE: "\x01\x00\x00\x00"-
#13142  NEW    ft: 7 corp: 3/11b lim: 128 exec/s: 0 rss: 25Mb L: 5/5 MS: 4 EraseBytes-ChangeByte-ShuffleBytes-InsertByte-
#104833 NEW    ft: 8 corp: 4/16b lim: 1030 exec/s: 104833 rss: 25Mb L: 5/5 MS: 1 ChangeByte-
#262144 pulse  ft: 8 corp: 4/16b lim: 2589 exec/s: 87381 rss: 25Mb
#524288 pulse  ft: 8 corp: 4/16b lim: 4096 exec/s: 74898 rss: 25Mb
#1048576        pulse  ft: 8 corp: 4/16b lim: 4096 exec/s: 74898 rss: 25Mb
#1275694        NEW    ft: 9 corp: 5/21b lim: 4096 exec/s: 75040 rss: 25Mb L: 5/5 MS: 1 ChangeByte-
#1293550        NEW    ft: 10 corp: 6/26b lim: 4096 exec/s: 76091 rss: 25Mb L: 5/5 MS: 1 CopyPart-
panic: runtime error: index out of range

goroutine 17 [running, locked to thread]:
github.com/fuzzitdev/example-go/pkg/parser.ParseComplex.func5(...)
        /go/src/github.com/fuzzitdev/example-go/pkg/parser/parse_complex.go:5
github.com/fuzzitdev/example-go/pkg/parser.ParseComplex(0x2aabb20, 0x5, 0x5, 0xc00001e040)
        /go/src/github.com/fuzzitdev/example-go/pkg/parser/parse_complex.go:5 +0x1b2
github.com/fuzzitdev/example-go/pkg/parser.Fuzz(...)
        /go/src/github.com/fuzzitdev/example-go/pkg/parser/parse_complex_fuzz.go:6
main.LLVMFuzzerTestOneInput(0x2aabb20, 0x5, 0x545b78)
        /tmp/go-fuzz-build316206684/gopath/src/github.com/fuzzitdev/example-go/pkg/parser/go.fuzz.main/main.go:35 +0x84
main._cgoexpwrap_90699947e885_LLVMFuzzerTestOneInput(0x2aabb20, 0x5, 0x2aaab10)
        _cgo_gotypes.go:64 +0x37
==4262== ERROR: libFuzzer: deadly signal
    #0 0x45c110 in __sanitizer_print_stack_trace (/go/src/github.com/fuzzitdev/example-go/parser-fuzz.libfuzzer+0x45c110)
    #1 0x43b79b in fuzzer::PrintStackTrace() (/go/src/github.com/fuzzitdev/example-go/parser-fuzz.libfuzzer+0x43b79b)
    #2 0x422123 in fuzzer::Fuzzer::CrashCallback() (/go/src/github.com/fuzzitdev/example-go/parser-fuzz.libfuzzer+0x422123)
    #3 0x7f0ba60ff72f  (/lib/x86_64-linux-gnu/libpthread.so.0+0x1272f)
    #4 0x4acc70 in runtime.raise /tmp/go-fuzz-build316206684/goroot/src/runtime/sys_linux_amd64.s:149

NOTE: libFuzzer has rudimentary signal handlers.
      Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal
MS: 1 ChangeByte-; base unit: 89b92cdd9bcb9b861c47c0179eff7b3a9baafcde
0x46,0x55,0x5a,0x5a,0x49,
FUZZI
artifact_prefix='./'; Test unit written to ./crash-df779ced6b712c5fca247e465de2de474d1d23b9
Base64: RlVaWkk=

Integrating with Fuzzit from CI

The best way to integrate with Fuzzit is by adding a two stages in your Continuous Build system (like Travis CI or Circle CI).

Fuzzing stage:

Regression stage

Here is the relevant snippet from the fuzzit.sh which is being run by .travis.yml

wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/latest/download/fuzzit_Linux_x86_64
chmod a+x fuzzit

## upload fuzz target for long fuzz testing on fuzzit.dev server or run locally for regression
./fuzzit create job --type ${1} fuzzitdev/parse-complex parse-complex

In production it is advised to download a pinned version of the CLI like in the example. In development you can use the latest version: https://github.com/fuzzitdev/fuzzit/releases/latest/download/fuzzit_${OS}_${ARCH}. Valid values for ${OS} are: Linux, Darwin, Windows. Valid values for ${ARCH} are: x86_64 and i386.