mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[dev.fuzz] internal/fuzz: minimize inputs that expand coverage
When a fuzz worker discovers an input that activates coverage counters that weren't previously activated, it sends that input back to the coordinator, as before. If the coordinator also finds that input provides new coverage (that is, some other input hasn't won the race), the coordinator now sends the input back to workers for minimization. The minimization procedure now supports minimizing these interesting inputs. It attempts to find smaller inputs that preserve at least one new coverage bit. If minimization succeeds, the coordinator adds the smaller input to the corpus instead of the original. If minimization fails, the coordinator adds the original input. If minimization finds that the original input didn't provide new coverage after all (for example, a counter was activated by an unrelated background goroutine and was considered flaky), the input is ignored and not recorded. Change-Id: I81d98d6ec28abb0ac2a476f73480ceeaff674c08 Reviewed-on: https://go-review.googlesource.com/c/go/+/342997 Trust: Jay Conrod <jayconrod@google.com> Trust: Katie Hockman <katie@golang.org> Run-TryBot: Jay Conrod <jayconrod@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Katie Hockman <katie@golang.org>
This commit is contained in:
parent
f0668e7c8c
commit
5bc273aca5
7 changed files with 583 additions and 169 deletions
71
src/internal/fuzz/queue.go
Normal file
71
src/internal/fuzz/queue.go
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fuzz
|
||||
|
||||
// queue holds a growable sequence of inputs for fuzzing and minimization.
|
||||
//
|
||||
// For now, this is a simple ring buffer
|
||||
// (https://en.wikipedia.org/wiki/Circular_buffer).
|
||||
//
|
||||
// TODO(golang.org/issue/46224): use a priotization algorithm based on input
|
||||
// size, previous duration, coverage, and any other metrics that seem useful.
|
||||
type queue struct {
|
||||
// elems holds a ring buffer.
|
||||
// The queue is empty when begin = end.
|
||||
// The queue is full (until grow is called) when end = begin + N - 1 (mod N)
|
||||
// where N = cap(elems).
|
||||
elems []interface{}
|
||||
head, len int
|
||||
}
|
||||
|
||||
func (q *queue) cap() int {
|
||||
return len(q.elems)
|
||||
}
|
||||
|
||||
func (q *queue) grow() {
|
||||
oldCap := q.cap()
|
||||
newCap := oldCap * 2
|
||||
if newCap == 0 {
|
||||
newCap = 8
|
||||
}
|
||||
newElems := make([]interface{}, newCap)
|
||||
oldLen := q.len
|
||||
for i := 0; i < oldLen; i++ {
|
||||
newElems[i] = q.elems[(q.head+i)%oldCap]
|
||||
}
|
||||
q.elems = newElems
|
||||
q.head = 0
|
||||
}
|
||||
|
||||
func (q *queue) enqueue(e interface{}) {
|
||||
if q.len+1 > q.cap() {
|
||||
q.grow()
|
||||
}
|
||||
i := (q.head + q.len) % q.cap()
|
||||
q.elems[i] = e
|
||||
q.len++
|
||||
}
|
||||
|
||||
func (q *queue) dequeue() (interface{}, bool) {
|
||||
if q.len == 0 {
|
||||
return nil, false
|
||||
}
|
||||
e := q.elems[q.head]
|
||||
q.elems[q.head] = nil
|
||||
q.head = (q.head + 1) % q.cap()
|
||||
q.len--
|
||||
return e, true
|
||||
}
|
||||
|
||||
func (q *queue) peek() (interface{}, bool) {
|
||||
if q.len == 0 {
|
||||
return nil, false
|
||||
}
|
||||
return q.elems[q.head], true
|
||||
}
|
||||
|
||||
func (q *queue) clear() {
|
||||
*q = queue{}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue