-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Continuous fuzzing on Fuzzit #432
Conversation
85c6651
to
1769825
Compare
Just pushed an improvement to this. I got it decoding the fuzz into a more meaningful HTTP response, including headers. I think it will give better coverage. Also split out raw byte stream fuzzing into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, thank you for this 🙇
The format detection fuzzer caught one crash in a local run. I'm attaching the data here.
Will look into this. Is the best way to reproduce this to just run the fuzz tests again?
If you sign in to set up an account I can ask them to add you to the org.
I have signed up. Will wait to be added to the org before doing the rest of the setup.
I am not able to reproduce the crash with this code. package main
import (
"os"
vegeta "github.com/tsenart/vegeta/lib"
)
func main() {
f, err := os.Open("2d8aaa305cd7b972396ef7c69da1f59204b0a255")
if err != nil {
panic(err)
}
defer f.Close()
vegeta.DecoderFor(f)
} |
Thanks for looking at it! Will go over all of this soon. |
I think you should be added to the org. |
Hm. It crashes for me. Maybe it just takes more memory than my system has. The same input crashes if I read it with a gob decoder. Same error. It doesn't crash until reading. No crash with CSV or JSON. package main
import (
"io"
"os"
vegeta "github.com/tsenart/vegeta/lib"
)
func main() {
f, err := os.Open("2d8aaa305cd7b972396ef7c69da1f59204b0a255")
if err != nil {
panic(err)
}
defer f.Close()
decoder := vegeta.NewDecoder(f)
readAllResults(decoder)
}
func readAllResults(decoder vegeta.Decoder) (ok bool) {
for {
result := &vegeta.Result{}
err := decoder.Decode(result)
if err == io.EOF {
return true
} else if err != nil {
return false
}
}
}
|
There's the hex of that crashing input.
|
2c0371a
to
dfd8adc
Compare
Enables automated fuzzing on continuous fuzzing platform Fuzzit. Fuzz regression tests run every build and PR. Full length fuzzing runs every push to master. Targets everything that does some parsing. Defined fuzzing targets are: * AttackerHTTP - Delivers fuzz as an HTTP response. Uses socket file. * AttackerTCP - Delivers fuzz as a TCP byte stream. Uses socket file. * HTTPTargeter - Fuzz decoding of a target list in HTTP format. * JSONTargeter - Fuzz decoding of a target list in JSON format. * ResultsFormatDetection - Fuzz result list format detection. * GobDecoder - Fuzz decoder of a result list in gob format. * CSVDecoder - Fuzz decoding of a result list in CSV format. * JSONDecoder - Fuzz decoding a result list in JSON format.
I think everything here is addressed. |
Thanks for addressing all the feedback!
I find this very strange. I just compiled and ran the code you posted above and the maximum resident set size is 131 MB. I'd like to solve this mystery before merging this in :-)
|
I'll do some investigation. There's what I get running with time. Seems like it's ~6MB memory usage before it crashes. [user@bookmoons vegeta]$ /usr/bin/time -vp ./test
fatal error: runtime: out of memory
runtime stack:
runtime.throw(0x70267c, 0x16)
/usr/share/go/src/runtime/panic.go:617 +0x72
runtime.sysMap(0xc004000000, 0xe8000000, 0x9702f8)
/usr/share/go/src/runtime/mem_linux.go:170 +0xc7
runtime.(*mheap).sysAlloc(0x957e80, 0xe4304000, 0x957e90, 0x72182)
/usr/share/go/src/runtime/malloc.go:633 +0x1cd
runtime.(*mheap).grow(0x957e80, 0x72182, 0x0)
/usr/share/go/src/runtime/mheap.go:1222 +0x42
runtime.(*mheap).allocSpanLocked(0x957e80, 0x72182, 0x970308, 0x957e88)
/usr/share/go/src/runtime/mheap.go:1150 +0x37f
runtime.(*mheap).alloc_m(0x957e80, 0x72182, 0x746abed50101, 0x746abed5fd98)
/usr/share/go/src/runtime/mheap.go:977 +0xc2
runtime.(*mheap).alloc.func1()
/usr/share/go/src/runtime/mheap.go:1048 +0x4c
runtime.(*mheap).alloc(0x957e80, 0x72182, 0x10101, 0x746abed5fd08)
/usr/share/go/src/runtime/mheap.go:1047 +0x8a
runtime.largeAlloc(0xe4303030, 0x970101, 0x746abed5fd08)
/usr/share/go/src/runtime/malloc.go:1055 +0x99
runtime.mallocgc.func1()
/usr/share/go/src/runtime/malloc.go:950 +0x46
runtime.systemstack(0x4567d9)
/usr/share/go/src/runtime/asm_amd64.s:351 +0x66
runtime.mstart()
/usr/share/go/src/runtime/proc.go:1153
goroutine 1 [running]:
runtime.systemstack_switch()
/usr/share/go/src/runtime/asm_amd64.s:311 fp=0xc0000b9c88 sp=0xc0000b9c80 pc=0x4568d0
runtime.mallocgc(0xe4303030, 0x694420, 0xc0000b9d01, 0x638363)
/usr/share/go/src/runtime/malloc.go:949 +0x872 fp=0xc0000b9d28 sp=0xc0000b9c88 pc=0x40d3e2
runtime.makeslice(0x694420, 0xe4303030, 0xe4303030, 0x4)
/usr/share/go/src/runtime/slice.go:49 +0x6c fp=0xc0000b9d58 sp=0xc0000b9d28 pc=0x44228c
encoding/gob.(*decBuffer).Size(...)
/usr/share/go/src/encoding/gob/decode.go:65
encoding/gob.(*Decoder).readMessage(0xc0000fe080, 0xe4303030)
/usr/share/go/src/encoding/gob/decoder.go:101 +0x16e fp=0xc0000b9db8 sp=0xc0000b9d58 pc=0x6417ce
encoding/gob.(*Decoder).recvMessage(0xc0000fe080, 0xc0000b9e38)
/usr/share/go/src/encoding/gob/decoder.go:90 +0xfe fp=0xc0000b9e10 sp=0xc0000b9db8 pc=0x64162e
encoding/gob.(*Decoder).decodeTypeSequence(0xc0000fe080, 0x712e00, 0xc0000fe080)
/usr/share/go/src/encoding/gob/decoder.go:143 +0x12c fp=0xc0000b9e30 sp=0xc0000b9e10 pc=0x641afc
encoding/gob.(*Decoder).DecodeValue(0xc0000fe080, 0x6ad380, 0xc0000fe100, 0x16, 0x0, 0x0)
/usr/share/go/src/encoding/gob/decoder.go:211 +0xf6 fp=0xc0000b9e70 sp=0xc0000b9e30 pc=0x641e46
encoding/gob.(*Decoder).Decode(0xc0000fe080, 0x6ad380, 0xc0000fe100, 0x6b8a01, 0xc0000fe100)
/usr/share/go/src/encoding/gob/decoder.go:188 +0x191 fp=0xc0000b9ed8 sp=0xc0000b9e70 pc=0x641cb1
github.com/tsenart/vegeta/lib.NewDecoder.func1(0xc0000fe100, 0xc0000fe100, 0xc00007de50)
/home/user/go/src/github.com/tsenart/vegeta/lib/results.go:116 +0x40 fp=0xc0000b9f10 sp=0xc0000b9ed8 pc=0x664bd0
github.com/tsenart/vegeta/lib.Decoder.Decode(...)
/home/user/go/src/github.com/tsenart/vegeta/lib/results.go:121
main.readAllResults(0xc00007de50, 0xc000088528)
/home/user/go/src/github.com/tsenart/vegeta/test.go:23 +0x45 fp=0xc0000b9f48 sp=0xc0000b9f10 pc=0x665025
main.main()
/home/user/go/src/github.com/tsenart/vegeta/test.go:17 +0xae fp=0xc0000b9f98 sp=0xc0000b9f48 pc=0x664f9e
runtime.main()
/usr/share/go/src/runtime/proc.go:200 +0x20c fp=0xc0000b9fe0 sp=0xc0000b9f98 pc=0x42eb2c
runtime.goexit()
/usr/share/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc0000b9fe8 sp=0xc0000b9fe0 pc=0x4589a1
Command exited with non-zero status 2
Command being timed: "./test"
User time (seconds): 0.00
System time (seconds): 0.00
Percent of CPU this job got: 76%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.01
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 6008
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 376
Voluntary context switches: 50
Involuntary context switches: 66
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 2 |
There's a reduced test case without the file read. Same crash. package main
import (
"bytes"
"io"
vegeta "github.com/tsenart/vegeta/lib"
)
func main() {
input := []byte{0xfc, 0xe4, 0x30, 0x30, 0x30}
decoder := vegeta.NewDecoder(bytes.NewReader(input))
readAllResults(decoder)
}
func readAllResults(decoder vegeta.Decoder) (ok bool) {
for {
result := &vegeta.Result{}
err := decoder.Decode(result)
if err == io.EOF {
return true
} else if err != nil {
return false
}
}
} |
What's your environment like? I'm running Fedora and Go 1.12. My Fedora version is old. Maybe the system has a bug. [user@bookmoons vegeta]$ cat /etc/fedora-release
Fedora release 28 (Twenty Eight)
[user@bookmoons vegeta]$ go version
go version go1.12.5 linux/amd64 |
Tried this with Go 1.12.9 and 1.13. Same crash on both. |
Removing anything from the input seems to prevent the crash. Adding to the beginning also prevents it. Adding to the end preserves. Crashing inputs:
Noncrashing inputs:
|
I think what's happening here is the gob decoder allocates a large amount then doesn't use it, so it never ends up in that resident set size. Your machine probably has enough to handle the allocation so it's accepted.
I see in the gob encoding a byte slice is sent as length followed by bytes. The length is prefixed with a byte count, encoded as a negated single byte. Ours is 0xfc which decodes to 4.
It shows up as That's the end of the fuzz so it just allocates then releases. But I guess if my system doesn't have that available it refuses to allocate. |
Thanks for the investigation!. I think there's nothing left to do here. |
Hooray:) feel free to RT https://twitter.com/fuzzitdev/status/1169170601623379968 |
Thanks a lot @tsenart. I think you can remove me from both orgs in Settings, then they're all yours. |
@bookmoons: I'm migrating to Github Actions for CI (away from Travis). Hitting this issue with our Fuzzit steps: https://github.com/tsenart/vegeta/pull/456/checks?check_run_id=276595658 Any clue how to fix this? |
Integrates with Fuzzit for automated bug discovery. Initially discussed in #429.
I've tried to fuzz everything that does some parsing. The main target
AttackerHTTP
decodes fuzz into a valid HTTP response. It stubs the targeter and communicates through a socket file.Other targets are:
AttackerTCP
- Fuzz response handling with unstructured byte stream.HTTPTargeter
- Fuzz decoding of a target list in HTTP format.JSONTargeter
- Fuzz decoding of a target list in JSON format.ResultsFormatDetection
- Fuzz result list format detection.GobDecoder
- Fuzz decoding of a result list in gob format.CSVDecoder
- Fuzz decoding of a result list in CSV format.JSONDecoder
- Fuzz decoding of a result list in JSON format.The format detection fuzzer caught one crash in a local run. I'm attaching the data here. The unsuffixed file contains the crashing input. Seems to have run out of memory, maybe a memory leak somewhere.
crashers.FormatDetection.zip
There's a
vegeta
org on Fuzzit all preconfigured and ready to go. If you sign in to set up an account I can ask them to add you to the org. There's a little setup to do:FUZZIT_API_KEY
.Including the "fuzz target parser" issue in the close list because I think this patch fulfills it.
Closes #429. Closes #140.
Background
Enables automated fuzzing. Proposed in #429.
Checklist