Not just crashes and hangs
Thanks to its flexible fuzzing API, go-fuzz lends itself nicely not only to the mere search of crashing inputs, but can be used to explore all scenarios where edge cases are troublesome. Useful applications range from checking output sanity by adding crashing assertions to your Fuzz() function, to comparing the two ends of a unpack-pack chain and even comparing the behavior of two different versions or implementations of the same functionality. For example, while preparing our DNSSEC engine for launch, I faced a weird bug that would happen only on production or under stress tests: NSEC records that were supposed to only have a couple bits set in their types bitmap would sometimes look like this deleg.filippo.io. IN NSEC 3600 \000.deleg.filippo.io. NS WKS HINFO TXT AAAA LOC SRV CERT SSHFP RRSIG NSEC TLSA HIP TYPE60 TYPE61 SPF The catch was that our "pack and send" code pools []byte buffers to reduce GC and allocation churn, so buffers passed to dns.msg.PackBuffer(buf []byte) can be "dirty" from previous uses. var bufpool = sync.Pool{ New: func() interface{} { return make([]byte, 0, 2048) },}[...] data := bufpool.Get().([]byte) defer bufpool.Put(data) if data, err = r.Response.PackBuffer(data); err != nil { However, buf not being an array of zeroes was not handled by some github.com/miekgs/dns packers, including the NSEC rdata one, that would just OR present bits, without clearing ones that are supposed to be absent. case `dns:"nsec"`: lastwindow := uint16(0) length := uint16(0) for j := 0; j < val.Field(i).Len(); j++ { t := uint16((fv.Index(j).Uint())) window := uint16(t / 256) if lastwindow != window { off += int(length) + 3 } length = (t - window*256) / 8 bit := t - (window * 256) - (length * 8) msg[off] = byte(window) // window # msg[off+1] = byte(length + 1) // octets length // Setting the bit value for the type in the right octet---> msg[off+2+int(length)] |= byte(1 << (7 - bit)) lastwindow = window } off += 2 + int(length) off++} The fix was clear and easy: we benchmarked a few different ways to zero a buffer and updated the code like this // zeroBuf is a big buffer of zero bytes, used to zero out the buffers passed// to PackBuffer.var zeroBuf = make([]byte, 65535)var bufpool = sync.Pool{ New: func() interface{} { return make([]byte, 0, 2048) },}[...] data := bufpool.Get().([]byte) defer bufpool.Put(data) copy(data[0:cap(data)], zeroBuf) if data, err = r.Response.PackBuffer(data); err != nil { Note: a recent optimization turns zeroing range loops into memclr calls, so once 1.5 lands that will be much faster than copy() . |