|
1 |
| -# example-go |
2 |
| -Go Fuzzit Example |
| 1 | +[](https://travis-ci.org/fuzzitdev/example-go) |
| 2 | + |
| 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. |
0 commit comments