Skip to content

Commit bd71dee

Browse files
rolandshoemakergopherbot
authored andcommitted
internal/fuzz: don't use dirty coverage maps during minimization
When minimizing a value, if the value cannot be minimized (i.e. it is the final value is the same value as was sent for minimization) return the initial coverage map, rather than the coverageSnapshot, which is actually the coverage map for the final minimization step and may not accurately reflect whether the input actually expands the coverage set or not. Updates #48326 Change-Id: I01f0eebe5841e808b6799647d2e5fe3aa45cd2e0 Reviewed-on: https://go-review.googlesource.com/c/go/+/391614 Reviewed-by: Bryan Mills <[email protected]> Trust: Roland Shoemaker <[email protected]> Run-TryBot: Roland Shoemaker <[email protected]> Auto-Submit: Roland Shoemaker <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 914195c commit bd71dee

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Test that minimization doesn't use dirty coverage snapshots when it
2+
# is unable to actually minimize the input. We do this by checking that
3+
# a expected value appears in the cache. If a dirty coverage map is used
4+
# (i.e. the coverage map generated during the last minimization step,
5+
# rather than the map provided with the initial input) then this value
6+
# is unlikely to appear in the cache, since the map generated during
7+
# the last minimization step should not increase the coverage.
8+
9+
[short] skip
10+
[!fuzz-instrumented] skip
11+
12+
env GOCACHE=$WORK/gocache
13+
go test -fuzz=FuzzCovMin -fuzztime=25s -test.fuzzcachedir=$GOCACHE/fuzz
14+
go run check_file/main.go $GOCACHE/fuzz/FuzzCovMin abcd
15+
16+
-- go.mod --
17+
module test
18+
19+
-- covmin_test.go --
20+
package covmin
21+
22+
import "testing"
23+
24+
func FuzzCovMin(f *testing.F) {
25+
f.Fuzz(func(t *testing.T, data []byte) {
26+
if len(data) >= 4 && data[0] == 'a' && data[1] == 'b' && data[2] == 'c' && data[3] == 'd' {
27+
return
28+
}
29+
})
30+
}
31+
32+
-- check_file/main.go --
33+
package main
34+
35+
import (
36+
"bytes"
37+
"fmt"
38+
"os"
39+
"path/filepath"
40+
"regexp"
41+
"strconv"
42+
)
43+
44+
func checkFile(name, expected string) (bool, error) {
45+
data, err := os.ReadFile(name)
46+
if err != nil {
47+
return false, err
48+
}
49+
for _, line := range bytes.Split(data, []byte("\n")) {
50+
m := valRe.FindSubmatch(line)
51+
if m == nil {
52+
continue
53+
}
54+
fmt.Println(strconv.Unquote(string(m[1])))
55+
if s, err := strconv.Unquote(string(m[1])); err != nil {
56+
return false, err
57+
} else if s == expected {
58+
return true, nil
59+
}
60+
}
61+
return false, nil
62+
}
63+
64+
var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
65+
66+
func main() {
67+
dir, expected := os.Args[1], os.Args[2]
68+
ents, err := os.ReadDir(dir)
69+
if err != nil {
70+
fmt.Fprintln(os.Stderr, err)
71+
os.Exit(1)
72+
}
73+
for _, ent := range ents {
74+
name := filepath.Join(dir, ent.Name())
75+
if good, err := checkFile(name, expected); err != nil {
76+
fmt.Fprintln(os.Stderr, err)
77+
os.Exit(1)
78+
} else if good {
79+
os.Exit(0)
80+
}
81+
}
82+
fmt.Fprintln(os.Stderr, "input over minimized")
83+
os.Exit(1)
84+
}

src/internal/fuzz/worker.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,7 @@ func (ws *workerServer) minimize(ctx context.Context, args minimizeArgs) (resp m
800800
if err != nil {
801801
panic(err)
802802
}
803+
inpHash := sha256.Sum256(mem.valueCopy())
803804
if args.Timeout != 0 {
804805
var cancel func()
805806
ctx, cancel = context.WithTimeout(ctx, args.Timeout)
@@ -811,12 +812,22 @@ func (ws *workerServer) minimize(ctx context.Context, args minimizeArgs) (resp m
811812
success, err := ws.minimizeInput(ctx, vals, mem, args)
812813
if success {
813814
writeToMem(vals, mem)
815+
outHash := sha256.Sum256(mem.valueCopy())
814816
mem.header().rawInMem = false
815817
resp.WroteToMem = true
816818
if err != nil {
817819
resp.Err = err.Error()
818820
} else {
819-
resp.CoverageData = coverageSnapshot
821+
// If the values didn't change during minimization then coverageSnapshot is likely
822+
// a dirty snapshot which represents the very last step of minimization, not the
823+
// coverage for the initial input. In that case just return the coverage we were
824+
// given initially, since it more accurately represents the coverage map for the
825+
// input we are returning.
826+
if outHash != inpHash {
827+
resp.CoverageData = coverageSnapshot
828+
} else {
829+
resp.CoverageData = args.KeepCoverage
830+
}
820831
}
821832
}
822833
return resp

0 commit comments

Comments
 (0)