]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.fuzz] internal/fuzz: small bug fixes and refactors to minimization
authorKatie Hockman <katie@golang.org>
Tue, 6 Apr 2021 19:33:55 +0000 (15:33 -0400)
committerKatie Hockman <katie@golang.org>
Wed, 7 Apr 2021 14:58:04 +0000 (14:58 +0000)
This fixes a few issues that were being masked since
log statements weren't being printed to stdout. Now
that they are, fix the bugs, and update the tests.

Also includes a few small refactors which will make
minimizing non-recoverable errors easier.

Change-Id: Ie2fd2e5534b3980317e1e1f3fd8e04750988c17f
Reviewed-on: https://go-review.googlesource.com/c/go/+/307810
Trust: Katie Hockman <katie@golang.org>
Run-TryBot: Katie Hockman <katie@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
src/cmd/go/testdata/script/test_fuzz_mutator.txt
src/internal/fuzz/worker.go

index c5a7a86b84f10aebae6a47bf3b05c5694032557f..8ec73bf35e029799bb6fdd0c573919ecbb737971 100644 (file)
@@ -19,15 +19,15 @@ go run check_logs.go fuzz fuzz.worker
 stdout FAIL
 stdout 'mutator found enough unique mutations'
 
-# Test that minimization is working.
-! go test -fuzz=FuzzMinimizer -run=FuzzMinimizer -parallel=1 -fuzztime=5s minimizer_test.go
-! stdout ok
-# TODO(jayconrod,katiehockman): Once logging is fixed, add this test in.
-# stdout 'got the minimum size!'
+# Test that minimization is working for recoverable errors.
+! go test -v -fuzz=FuzzMinimizerRecoverable -run=FuzzMinimizerRecoverable -parallel=1 -fuzztime=10s minimizer_test.go
+! stdout '^ok'
+stdout 'got the minimum size!'
+stdout 'contains a letter'
 stdout FAIL
 
 # Test that re-running the minimized value causes a crash.
-! go test -run=FuzzMinimizer minimizer_test.go
+! go test -run=FuzzMinimizerRecoverable minimizer_test.go
 
 -- go.mod --
 module m
@@ -85,7 +85,7 @@ import (
        "testing"
 )
 
-func FuzzMinimizer(f *testing.F) {
+func FuzzMinimizerRecoverable(f *testing.F) {
        f.Fuzz(func(t *testing.T, b []byte) {
                if len(b) < 100 {
                        // Make sure that b is large enough that it can be minimized
index 243a12baefe4f33833cd0c89b3946892ee3e7308..506a485f24c099f4309bb5eef52bb326d37441f5 100644 (file)
@@ -515,9 +515,12 @@ func (ws *workerServer) fuzz(ctx context.Context, args fuzzArgs) fuzzResponse {
                        ws.m.mutate(vals, cap(mem.valueRef()))
                        writeToMem(vals, mem)
                        if err := ws.fuzzFn(CorpusEntry{Values: vals}); err != nil {
-                               if minErr := ws.minimize(ctx, vals, mem); minErr != nil {
+                               // TODO(jayconrod,katiehockman): consider making the maximum minimization
+                               // time customizable with a go command flag.
+                               minCtx, minCancel := context.WithTimeout(ctx, time.Minute)
+                               defer minCancel()
+                               if minErr := ws.minimize(minCtx, vals, mem); minErr != nil {
                                        // Minimization found a different error, so use that one.
-                                       writeToMem(vals, mem)
                                        err = minErr
                                }
                                return fuzzResponse{Crashed: true, Err: err.Error()}
@@ -528,22 +531,20 @@ func (ws *workerServer) fuzz(ctx context.Context, args fuzzArgs) fuzzResponse {
        }
 }
 
-// minimizeInput applies a series of minimizing transformations on the provided
+// minimize applies a series of minimizing transformations on the provided
 // vals, ensuring that each minimization still causes an error in fuzzFn. Before
 // every call to fuzzFn, it marshals the new vals and writes it to the provided
 // mem just in case an unrecoverable error occurs. It runs for a maximum of one
 // minute, and returns the last error it found.
-func (ws *workerServer) minimize(ctx context.Context, vals []interface{}, mem *sharedMem) error {
-       // TODO(jayconrod,katiehockman): consider making the maximum minimization
-       // time customizable with a go command flag.
-       ctx, cancel := context.WithTimeout(ctx, time.Minute)
-       defer cancel()
-       var retErr error
+func (ws *workerServer) minimize(ctx context.Context, vals []interface{}, mem *sharedMem) (retErr error) {
+       // Make sure the last crashing value is written to mem.
+       defer writeToMem(vals, mem)
 
        // tryMinimized will run the fuzz function for the values in vals at the
        // time the function is called. If err is nil, then the minimization was
        // unsuccessful, since we expect an error to still occur.
        tryMinimized := func(i int, prevVal interface{}) error {
+               writeToMem(vals, mem) // write to mem in case a non-recoverable crash occurs
                err := ws.fuzzFn(CorpusEntry{Values: vals})
                if err == nil {
                        // The fuzz function succeeded, so return the value at index i back
@@ -565,11 +566,11 @@ func (ws *workerServer) minimize(ctx context.Context, vals []interface{}, mem *s
                        // First, try to cut the tail.
                        for n := 1024; n != 0; n /= 2 {
                                for len(v) > n {
-                                       if ctx.Done() != nil {
+                                       if ctx.Err() != nil {
                                                return retErr
                                        }
                                        vals[valI] = v[:len(v)-n]
-                                       if tryMinimized(valI, v) != nil {
+                                       if tryMinimized(valI, v) == nil {
                                                break
                                        }
                                        // Set v to the new value to continue iterating.
@@ -580,14 +581,14 @@ func (ws *workerServer) minimize(ctx context.Context, vals []interface{}, mem *s
                        // Then, try to remove each individual byte.
                        tmp := make([]byte, len(v))
                        for i := 0; i < len(v)-1; i++ {
-                               if ctx.Done() != nil {
+                               if ctx.Err() != nil {
                                        return retErr
                                }
                                candidate := tmp[:len(v)-1]
                                copy(candidate[:i], v[:i])
                                copy(candidate[i:], v[i+1:])
                                vals[valI] = candidate
-                               if tryMinimized(valI, v) != nil {
+                               if tryMinimized(valI, v) == nil {
                                        continue
                                }
                                // Update v to delete the value at index i.
@@ -602,13 +603,13 @@ func (ws *workerServer) minimize(ctx context.Context, vals []interface{}, mem *s
                        for i := 0; i < len(v)-1; i++ {
                                copy(tmp, v[:i])
                                for j := len(v); j > i+1; j-- {
-                                       if ctx.Done() != nil {
+                                       if ctx.Err() != nil {
                                                return retErr
                                        }
                                        candidate := tmp[:len(v)-j+i]
                                        copy(candidate[i:], v[j:])
                                        vals[valI] = candidate
-                                       if tryMinimized(valI, v) != nil {
+                                       if tryMinimized(valI, v) == nil {
                                                continue
                                        }
                                        // Update v and reset the loop with the new length.