Skip to content
This repository was archived by the owner on Jul 22, 2020. It is now read-only.

Commit 123ed2b

Browse files
author
Yevgeny Pats
committed
Example of go-fuzz target integration with Fuzzit
1 parent 8c4fbfb commit 123ed2b

7 files changed

+244
-2
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@
1010

1111
# Output of the go coverage tool, specifically when used with LiteIDE
1212
*.out
13+
14+
# Goland Jetbrains
15+
.idea

.travis.yml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
language: go
2+
go:
3+
- "1.12.x"
4+
5+
env:
6+
global:
7+
secure: "Z79th1hv63cvfrAdtndyxae7nhMXVlTNzVrV5F521RmdZlgDbLfD14ScxaHFp9apAmfTl+hH+nTnY9dnZKv+ZTMxuQ2ocaWH1+PK3WWxloHckqfjBoVWNYXWVyXCf0uJ1//tr8DMjVnwGnlfskDqEgjmKZfer59J+aOYUQx5fLEbxPEvZgK2vbeP/iNFGUlkw1l3VF0zx/MbEBvtYjPq79Jhi080NvR//PXMiJFnON3F9kpGafW/+igUS+KCreLVhwb1OC/uubeFMsbizpBb0aFfAnx6cYXB9MAzlSlptrkoGpHHKnI3AQJOKIvaNi3DJlNgZ/IQ8VBiW5ReeAIxUa0exs79ufFl23en5FqZOCV7ExZBmWtRvS3XJ/S3ixgM3kncv7P8HCjWv3mj/yRSrgtXx/DRlcKHbZ3ellu1YYAbGhMJqqJi9pGepF5OKYm4fRqRoH45ilNTLV/XuEEZFmjXaVuyUA/siF/J16TIQe5USE9aaDD14MpU9iiXUhgaP4gmlJ0c+KSpJZKAW+Nc2vt3QF38ZMVtUK0VLsBIzCIbg1rA9P/YsaBofIYeHfVfgoa624gG51wShrAYv5lqX5FFihSy1OTnA6nr3Kqm4NQQVTi3x4qoq1sBqm3WJky7MQJ4dXpuBXC3qUzcE1zCbWkjIAvsvZfESVLp7dgRBWg="
8+
9+
jobs:
10+
include:
11+
- stage: Build & test
12+
go: 1.12.x
13+
script:
14+
- go build ./...
15+
- go test ./...
16+
17+
- stage: Fuzzit (Fuzzing)
18+
if: branch = master AND type IN (push)
19+
go: 1.12.x
20+
script:
21+
- ./ci/fuzzit.sh fuzzing
22+
23+
- stage: Fuzzit (Sanity)
24+
if: type IN (pull_request)
25+
go: 1.12.x
26+
script:
27+
- ./ci/fuzzit.sh sanity

README.md

+170-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,170 @@
1-
# example-go
2-
Go Fuzzit Example
1+
[![Build Status](https://travis-ci.org/fuzzitdev/example-go.svg?branch=master)](https://travis-ci.org/fuzzitdev/example-go)
2+
![fuzzit](https://app.fuzzit.dev/badge?org_id=hP8u8bNAda91Cnj0mKPX&branch=master)
3+
4+
# Continuous Fuzzing for Golang Example
5+
6+
This is an example of how to integrate your [go-fuzz](https://github.com/dvyukov/go-fuzz) targets with
7+
[Fuzzit](https://fuzzit.dev) Continuous Fuzzing Platform (Go support is currently in Alpha).
8+
9+
This example will show the following steps:
10+
* [Building and running locally a simple go-fuzz target](#building-go-fuzz-target)
11+
* [Integrate the go-fuzz target with Fuzzit via Travis-CI](#integrating-with-fuzzit)
12+
13+
Result:
14+
* Fuzzit will run the fuzz targets continuously on daily basis with the latest release.
15+
* Fuzzit will run sanity tests on every pull-request with the generated corpus and crashes to catch bugs early on.
16+
17+
Fuzzing for go can both help find complex bugs as well as correctness bugs. Go is a safe language so memory corruption bugs
18+
are very unlikely to happen but some bugs can still have security [implications](https://blog.cloudflare.com/dns-parser-meet-go-fuzzer/).
19+
20+
This tutorial is less about how to build go-fuzz targets but more about how to integrate the targets with Fuzzit. A lot of
21+
great information is available at the [go-fuzz](https://github.com/dvyukov/go-fuzz) repository.
22+
23+
## Building go-fuzz Target
24+
25+
The targets that are currently supported on Fuzzit is targets that utilize the libFuzzer engine. This is why we will
26+
use the `-libfuzzer` flag of go-fuzz and compile it on Linux machine (should be also supported on mac in the future)
27+
28+
### Understanding the bug
29+
30+
The bug is located at `parser_complex.go` with the following code
31+
32+
```go
33+
package parser
34+
35+
func ParseComplex(data [] byte) bool {
36+
if len(data) == 5 {
37+
if data[0] == 'F' && data[1] == 'U' && data[2] == 'Z' && data[3] == 'Z' && data[4] == 'I' && data[5] == 'T' {
38+
return true
39+
}
40+
}
41+
return false
42+
}
43+
```
44+
45+
This is the simplest example to demonstrate a classic off-by-one/out-of-bound error which causes the program to crash.
46+
Instead of `len(data) == 5` the correct code will be `len(data) == 6`.
47+
48+
### Understanding the fuzzer
49+
50+
the fuzzer is located at `parse_complex_fuzz.go` with the following code:
51+
52+
```go
53+
// +build gofuzz
54+
55+
package parser
56+
57+
func Fuzz(data []byte) int {
58+
ParseComplex(data)
59+
return 0
60+
}
61+
```
62+
63+
### Setting up the development environment
64+
65+
```bash
66+
docker run -it golang:1.12.7-buster /bin/bash
67+
68+
# Download Clang-9
69+
echo "deb http://apt.llvm.org/buster/ llvm-toolchain-buster main" >> /etc/apt/sources.list
70+
echo "deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster main" >> /etc/apt/sources.list
71+
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key| apt-key add -
72+
apt update && apt install -y clang-9 lldb-9 lld-9
73+
74+
# Download go-fuzz and go-fuzz-build
75+
go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
76+
77+
# Download this example
78+
go get github.com/fuzzitdev/example-go
79+
```
80+
81+
### Building the fuzzer
82+
83+
```bash
84+
cd /go/src/github.com/fuzzitdev/example-go
85+
go-fuzz-build -libfuzzer ./...
86+
clang-9 -fsanitize=fuzzer parser-fuzz.a -o parser-fuzz.libfuzzer
87+
```
88+
89+
### Running the fuzzer
90+
91+
```bash
92+
./parser-fuzz.libfuzzer
93+
```
94+
95+
Will print the following output and stacktrace:
96+
97+
```text
98+
INFO: Seed: 3709860458
99+
INFO: 65536 Extra Counters
100+
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
101+
INFO: A corpus is not provided, starting from an empty corpus
102+
#2 INITED ft: 4 corp: 1/1b exec/s: 0 rss: 25Mb
103+
#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"-
104+
#13142 NEW ft: 7 corp: 3/11b lim: 128 exec/s: 0 rss: 25Mb L: 5/5 MS: 4 EraseBytes-ChangeByte-ShuffleBytes-InsertByte-
105+
#104833 NEW ft: 8 corp: 4/16b lim: 1030 exec/s: 104833 rss: 25Mb L: 5/5 MS: 1 ChangeByte-
106+
#262144 pulse ft: 8 corp: 4/16b lim: 2589 exec/s: 87381 rss: 25Mb
107+
#524288 pulse ft: 8 corp: 4/16b lim: 4096 exec/s: 74898 rss: 25Mb
108+
#1048576 pulse ft: 8 corp: 4/16b lim: 4096 exec/s: 74898 rss: 25Mb
109+
#1275694 NEW ft: 9 corp: 5/21b lim: 4096 exec/s: 75040 rss: 25Mb L: 5/5 MS: 1 ChangeByte-
110+
#1293550 NEW ft: 10 corp: 6/26b lim: 4096 exec/s: 76091 rss: 25Mb L: 5/5 MS: 1 CopyPart-
111+
panic: runtime error: index out of range
112+
113+
goroutine 17 [running, locked to thread]:
114+
github.com/fuzzitdev/example-go/pkg/parser.ParseComplex.func5(...)
115+
/go/src/github.com/fuzzitdev/example-go/pkg/parser/parse_complex.go:5
116+
github.com/fuzzitdev/example-go/pkg/parser.ParseComplex(0x2aabb20, 0x5, 0x5, 0xc00001e040)
117+
/go/src/github.com/fuzzitdev/example-go/pkg/parser/parse_complex.go:5 +0x1b2
118+
github.com/fuzzitdev/example-go/pkg/parser.Fuzz(...)
119+
/go/src/github.com/fuzzitdev/example-go/pkg/parser/parse_complex_fuzz.go:6
120+
main.LLVMFuzzerTestOneInput(0x2aabb20, 0x5, 0x545b78)
121+
/tmp/go-fuzz-build316206684/gopath/src/github.com/fuzzitdev/example-go/pkg/parser/go.fuzz.main/main.go:35 +0x84
122+
main._cgoexpwrap_90699947e885_LLVMFuzzerTestOneInput(0x2aabb20, 0x5, 0x2aaab10)
123+
_cgo_gotypes.go:64 +0x37
124+
==4262== ERROR: libFuzzer: deadly signal
125+
#0 0x45c110 in __sanitizer_print_stack_trace (/go/src/github.com/fuzzitdev/example-go/parser-fuzz.libfuzzer+0x45c110)
126+
#1 0x43b79b in fuzzer::PrintStackTrace() (/go/src/github.com/fuzzitdev/example-go/parser-fuzz.libfuzzer+0x43b79b)
127+
#2 0x422123 in fuzzer::Fuzzer::CrashCallback() (/go/src/github.com/fuzzitdev/example-go/parser-fuzz.libfuzzer+0x422123)
128+
#3 0x7f0ba60ff72f (/lib/x86_64-linux-gnu/libpthread.so.0+0x1272f)
129+
#4 0x4acc70 in runtime.raise /tmp/go-fuzz-build316206684/goroot/src/runtime/sys_linux_amd64.s:149
130+
131+
NOTE: libFuzzer has rudimentary signal handlers.
132+
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
133+
SUMMARY: libFuzzer: deadly signal
134+
MS: 1 ChangeByte-; base unit: 89b92cdd9bcb9b861c47c0179eff7b3a9baafcde
135+
0x46,0x55,0x5a,0x5a,0x49,
136+
FUZZI
137+
artifact_prefix='./'; Test unit written to ./crash-df779ced6b712c5fca247e465de2de474d1d23b9
138+
Base64: RlVaWkk=
139+
```
140+
141+
142+
## Integrating with Fuzzit
143+
144+
The integration with fuzzit is easy and consists of adding a travis stage, downloading the fuzzit cli,
145+
authenticating and uploading the fuzzer to fuzzit.
146+
147+
here is the relevant snippet from the [./ci/fuzzit.sh](https://github.com/fuzzitdev/example-go/blob/master/ci/fuzzit.sh)
148+
which is being run by [.travis.yml](https://github.com/fuzzitdev/example-go/blob/master/.travis.yml)
149+
150+
```bash
151+
wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v1.2.7/fuzzit_Linux_x86_64
152+
chmod a+x fuzzit
153+
./fuzzit auth ${FUZZIT_API_KEY}
154+
export TARGET_ID=2n6hO2dQzylLxX5GGhRG
155+
./fuzzit create job --type $1 --branch $TRAVIS_BRANCH --revision $TRAVIS_COMMIT $TARGET_ID ./fuzzer
156+
```
157+
158+
NOTE: In production it is advised to download a pinned version of the [CLI](https://github.com/fuzzitdev/fuzzit)
159+
like in the example. In development you can use latest with the following link:
160+
https://github.com/fuzzitdev/fuzzit/releases/latest/download/fuzzit_\<Os\>_\<Arch\>
161+
162+
* Authenticate with the API key (you should keep this secret) you can find in the fuzzit settings dashboard.
163+
* Upload the fuzzer via create job command and create the fuzzing job. In This example we use two type of jobs:
164+
* Fuzzing job which is run on every push to master which continuous the previous job just with the new release.
165+
Continuous means all the current corpus is kept and the fuzzer will try to find new paths in the newly added code
166+
* In a Pull-Request the fuzzer will run a quick "sanity" test running the fuzzer through all the generated corpus
167+
and crashes to see if the Pull-Request doesnt introduce old or new crashes. This will be alred via the configured
168+
channel in the dashboard
169+
* The Target is not a secret. This ID can be retrieved from the dashboard after your create the appropriate target in the dashboard.
170+
Each target has it's own corpus and crashes.

ci/fuzzit.sh

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
set -xe
2+
3+
## Install go-fuzz
4+
go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
5+
6+
## build and send to fuzzit
7+
go build ./...
8+
go-fuzz-build -libfuzzer -o fuzzer.a ./...
9+
clang -fsanitize=fuzzer fuzzer.a -o fuzzer
10+
11+
wget -q -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v1.2.7/fuzzit_Linux_x86_64
12+
chmod a+x fuzzit
13+
./fuzzit auth ${FUZZIT_API_KEY}
14+
export TARGET_ID=2n6hO2dQzylLxX5GGhRG
15+
./fuzzit create job --type $1 --branch $TRAVIS_BRANCH --revision $TRAVIS_COMMIT $TARGET_ID ./fuzzer

pkg/parser/parse_complex.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package parser
2+
3+
func ParseComplex(data [] byte) bool {
4+
if len(data) == 5 {
5+
if data[0] == 'F' && data[1] == 'U' && data[2] == 'Z' && data[3] == 'Z' && data[4] == 'I' && data[5] == 'T' {
6+
return true
7+
}
8+
}
9+
return false
10+
}

pkg/parser/parse_complex_fuzz.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// +build gofuzz
2+
3+
package parser
4+
5+
func Fuzz(data []byte) int {
6+
ParseComplex(data)
7+
return 0
8+
}
9+

pkg/parser/parse_complex_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package parser
2+
3+
import "testing"
4+
5+
func TestParseComplex(t *testing.T) {
6+
res := ParseComplex([]byte("Incorrect Data"))
7+
if res {
8+
t.Fail()
9+
}
10+
}

0 commit comments

Comments
 (0)