package module
Version: v0.0.0-...-f39289b Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2021 License: MIT Imports: 0 Imported by: 0


Continuous Fuzzing for Golang Example

Fuzzing for go can help find both complex bugs and 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 GitLab. A lot of great information is available at the go-fuzz repository.

This is an example of how to integrate your go-fuzz targets into GitLab Ci/CD

This example will show the following steps:


  • go-fuzz targets will run continuously on the master branch
  • go-fuzz targets will run regression tests on every pull-request (and every other branch) with the generated corpus and crashes to catch bugs early on.

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) == 6 {
		if data[0] == 'F' && data[1] == 'U' && data[2] == 'Z' && data[3] == 'Z' && data[4] == 'I' && data[5] == 'N' && data[6] == 'G' {
			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) == 6 the correct code will be len(data) == 7.

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 {
	return 0
Building go-fuzz Target

The targets that are currently supported on GitLab are targets built in a GitLab pipeline 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 be supported on mac via brew install clang)

Running go-fuzz from CI

The best way to integrate go-fuzz fuzzing with Gitlab CI/CD is by adding additional stage & step to your .gitlab-ci.yml.

  - template: Coverage-Fuzzing.gitlab-ci.yml

    extends: .fuzz_base
    image: golang:latest
      CI_SEED_CORPUS: './seed_corpus'
        - apt update && apt install -y clang
        - go get -u
        - go-fuzz-build -libfuzzer -o my_fuzz_target.a .
        - clang -fsanitize=fuzzer my_fuzz_target.a -o my_fuzz_target
        - ./gitlab-cov-fuzz run --regression=$REGRESSION -- ./my_fuzz_target || true

For each fuzz target you will have to create a step which extends .fuzz_base that runs the following:

  • Builds the fuzz target
  • Runs the fuzz target via gitlab-cov-fuzz CLI.
  • For $CI_DEFAULT_BRANCH (can be override by $COV_FUZZING_BRANCH) will run fully fledged fuzzing sessions. For everything else including MRs will run fuzzing regression with the accumlated corpus and fixed crashes.

How to run fuzz testing locally

If you want to try running fuzz testing on your local machine rather than as part of a pipeline, you can follow the steps below to build the fuzz target and then run the fuzz target.

Building the fuzzer
cd /go/src/
go-fuzz-build -libfuzzer -o parse-complex.a .
clang -fsanitize=fuzzer parse-complex.a -o parse-complex
Running the fuzzer

Will print the following output and stacktrace:

INFO: Seed: 1994114449
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
#203    NEW    ft: 6 corp: 2/7b lim: 6 exec/s: 0 rss: 25Mb L: 6/6 MS: 1 CrossOver-
#114009 NEW    ft: 7 corp: 3/13b lim: 1130 exec/s: 38003 rss: 25Mb L: 6/6 MS: 1 ChangeByte-
#131072 pulse  ft: 7 corp: 3/13b lim: 1290 exec/s: 43690 rss: 25Mb
#262144 pulse  ft: 7 corp: 3/13b lim: 2600 exec/s: 37449 rss: 25Mb
#364834 NEW    ft: 8 corp: 4/19b lim: 3623 exec/s: 36483 rss: 25Mb L: 6/6 MS: 5 ChangeByte-EraseBytes-ChangeByte-ShuffleBytes-InsertByte-
#432956 NEW    ft: 9 corp: 5/25b lim: 4096 exec/s: 36079 rss: 25Mb L: 6/6 MS: 2 ChangeByte-ChangeByte-
#435537 NEW    ft: 10 corp: 6/31b lim: 4096 exec/s: 36294 rss: 25Mb L: 6/6 MS: 1 CopyPart-
#524288 pulse  ft: 10 corp: 6/31b lim: 4096 exec/s: 37449 rss: 25Mb
#1048576        pulse  ft: 10 corp: 6/31b lim: 4096 exec/s: 36157 rss: 25Mb
#1669798        NEW    ft: 11 corp: 7/37b lim: 4096 exec/s: 36299 rss: 25Mb L: 6/6 MS: 1 ChangeByte-
panic: runtime error: index out of range

goroutine 17 [running, locked to thread]:
        /go/src/, 0x6, 0x6, 0xc000056000)
        /go/src/ +0x1e4
main.LLVMFuzzerTestOneInput(0x13dab00, 0x6, 0x545b58)
        /tmp/go-fuzz-build673188302/gopath/src/ +0x84
main._cgoexpwrap_98ba7f745c88_LLVMFuzzerTestOneInput(0x13dab00, 0x6, 0x13d9ab0)
        _cgo_gotypes.go:64 +0x37
==857== ERROR: libFuzzer: deadly signal
    #0 0x45bf30 in __sanitizer_print_stack_trace (/go/src/
    #1 0x43b5cb in fuzzer::PrintStackTrace() (/go/src/
    #2 0x4220c3 in fuzzer::Fuzzer::CrashCallback() (/go/src/
    #3 0x7f76d88ed72f  (/lib/x86_64-linux-gnu/
    #4 0x4aca90 in runtime.raise /tmp/go-fuzz-build673188302/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: d0735caef6f40321673b1ce14e49a21aa86b2410
artifact_prefix='./'; Test unit written to ./crash-14b5f09dd74fe15430d803af773ba09a0524670d
Base64: RlVaWklO




This section is empty.


This section is empty.


func ParseComplex

func ParseComplex(data []byte) bool


This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL