Not just crashes and hangs
Thanks to it flexible fuzzing API, Go-fuzz lends itself nicely not only to the mere search for crashing inputs, but CA n is used to explore all scenarios where edge cases is troublesome. Useful applications range from checking output sanity by adding crashing assertions to your Fuzz() function, to comparing t He and ends of a unpack-pack chain and even comparing the behavior of a different versions or implementations of the SAM E functionality. For example, while preparing we DNSSEC engine for launch, I faced a weird bugs that would happen is on production or und ER stress tests: NSEC records, were supposed to only has 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, "pack and send" code pools []byte buffers to reduce GC and allocation churn, so buffers pas Sed to dns.msg.PackBuffer(buf []byte) can is "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, not being an array of zeroes is not buf handled by some Packers github.com/miekgs/dns , including the NSEC rdata one, that Woul D just OR present bits, without clearing ones that is 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 &NBSP;-&NBSP;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 is 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, 6 5535) 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'll be much faster than . |