| // 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. | |
| //go:build darwin || linux || windows | |
| // +build darwin linux windows | |
| package fuzz | |
| import ( | |
| "bytes" | |
| "context" | |
| "errors" | |
| "fmt" | |
| "reflect" | |
| "testing" | |
| ) | |
| func TestMinimizeInput(t *testing.T) { | |
| type testcase struct { | |
| name string | |
| fn func(CorpusEntry) error | |
| input []interface{} | |
| expected []interface{} | |
| } | |
| cases := []testcase{ | |
| { | |
| name: "ones_byte", | |
| fn: func(e CorpusEntry) error { | |
| b := e.Values[0].([]byte) | |
| ones := 0 | |
| for _, v := range b { | |
| if v == 1 { | |
| ones++ | |
| } | |
| } | |
| if ones == 3 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{[]byte{0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, | |
| expected: []interface{}{[]byte{1, 1, 1}}, | |
| }, | |
| { | |
| name: "single_bytes", | |
| fn: func(e CorpusEntry) error { | |
| b := e.Values[0].([]byte) | |
| if len(b) < 2 { | |
| return nil | |
| } | |
| if len(b) == 2 && b[0] == 1 && b[1] == 2 { | |
| return nil | |
| } | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| }, | |
| input: []interface{}{[]byte{1, 2, 3, 4, 5}}, | |
| expected: []interface{}{[]byte{2, 3}}, | |
| }, | |
| { | |
| name: "set_of_bytes", | |
| fn: func(e CorpusEntry) error { | |
| b := e.Values[0].([]byte) | |
| if len(b) < 3 { | |
| return nil | |
| } | |
| if bytes.Equal(b, []byte{0, 1, 2, 3, 4, 5}) || bytes.Equal(b, []byte{0, 4, 5}) { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{[]byte{0, 1, 2, 3, 4, 5}}, | |
| expected: []interface{}{[]byte{0, 4, 5}}, | |
| }, | |
| { | |
| name: "ones_string", | |
| fn: func(e CorpusEntry) error { | |
| b := e.Values[0].(string) | |
| ones := 0 | |
| for _, v := range b { | |
| if v == '1' { | |
| ones++ | |
| } | |
| } | |
| if ones == 3 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{"001010001000000000000000000"}, | |
| expected: []interface{}{"111"}, | |
| }, | |
| { | |
| name: "int", | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(int) | |
| if i > 100 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{123456}, | |
| expected: []interface{}{123}, | |
| }, | |
| { | |
| name: "int8", | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(int8) | |
| if i > 10 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{int8(1<<7 - 1)}, | |
| expected: []interface{}{int8(12)}, | |
| }, | |
| { | |
| name: "int16", | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(int16) | |
| if i > 10 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{int16(1<<15 - 1)}, | |
| expected: []interface{}{int16(32)}, | |
| }, | |
| { | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(int32) | |
| if i > 10 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{int32(1<<31 - 1)}, | |
| expected: []interface{}{int32(21)}, | |
| }, | |
| { | |
| name: "int32", | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(uint) | |
| if i > 10 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{uint(123456)}, | |
| expected: []interface{}{uint(12)}, | |
| }, | |
| { | |
| name: "uint8", | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(uint8) | |
| if i > 10 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{uint8(1<<8 - 1)}, | |
| expected: []interface{}{uint8(25)}, | |
| }, | |
| { | |
| name: "uint16", | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(uint16) | |
| if i > 10 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{uint16(1<<16 - 1)}, | |
| expected: []interface{}{uint16(65)}, | |
| }, | |
| { | |
| name: "uint32", | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(uint32) | |
| if i > 10 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{uint32(1<<32 - 1)}, | |
| expected: []interface{}{uint32(42)}, | |
| }, | |
| { | |
| name: "float32", | |
| fn: func(e CorpusEntry) error { | |
| if i := e.Values[0].(float32); i == 1.23 { | |
| return nil | |
| } | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| }, | |
| input: []interface{}{float32(1.23456789)}, | |
| expected: []interface{}{float32(1.2)}, | |
| }, | |
| { | |
| name: "float64", | |
| fn: func(e CorpusEntry) error { | |
| if i := e.Values[0].(float64); i == 1.23 { | |
| return nil | |
| } | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| }, | |
| input: []interface{}{float64(1.23456789)}, | |
| expected: []interface{}{float64(1.2)}, | |
| }, | |
| } | |
| // If we are on a 64 bit platform add int64 and uint64 tests | |
| if v := int64(1<<63 - 1); int64(int(v)) == v { | |
| cases = append(cases, testcase{ | |
| name: "int64", | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(int64) | |
| if i > 10 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{int64(1<<63 - 1)}, | |
| expected: []interface{}{int64(92)}, | |
| }, testcase{ | |
| name: "uint64", | |
| fn: func(e CorpusEntry) error { | |
| i := e.Values[0].(uint64) | |
| if i > 10 { | |
| return fmt.Errorf("bad %v", e.Values[0]) | |
| } | |
| return nil | |
| }, | |
| input: []interface{}{uint64(1<<64 - 1)}, | |
| expected: []interface{}{uint64(18)}, | |
| }) | |
| } | |
| for _, tc := range cases { | |
| tc := tc | |
| t.Run(tc.name, func(t *testing.T) { | |
| t.Parallel() | |
| ws := &workerServer{ | |
| fuzzFn: tc.fn, | |
| } | |
| count := int64(0) | |
| vals := tc.input | |
| success, err := ws.minimizeInput(context.Background(), vals, &count, 0, nil) | |
| if !success { | |
| t.Errorf("minimizeInput did not succeed") | |
| } | |
| if err == nil { | |
| t.Fatal("minimizeInput didn't provide an error") | |
| } | |
| if expected := fmt.Sprintf("bad %v", tc.expected[0]); err.Error() != expected { | |
| t.Errorf("unexpected error: got %q, want %q", err, expected) | |
| } | |
| if !reflect.DeepEqual(vals, tc.expected) { | |
| t.Errorf("unexpected results: got %v, want %v", vals, tc.expected) | |
| } | |
| }) | |
| } | |
| } | |
| // TestMinimizeInputCoverageError checks that if we're minimizing an interesting | |
| // input (one that we don't expect to cause an error), and the fuzz function | |
| // returns an error, minimizing fails, and we return the error quickly. | |
| func TestMinimizeInputCoverageError(t *testing.T) { | |
| errOhNo := errors.New("ohno") | |
| ws := &workerServer{fuzzFn: func(e CorpusEntry) error { | |
| return errOhNo | |
| }} | |
| keepCoverage := make([]byte, len(coverageSnapshot)) | |
| count := int64(0) | |
| vals := []interface{}{[]byte(nil)} | |
| success, err := ws.minimizeInput(context.Background(), vals, &count, 0, keepCoverage) | |
| if success { | |
| t.Error("unexpected success") | |
| } | |
| if err != errOhNo { | |
| t.Errorf("unexpected error: %v", err) | |
| } | |
| if count != 1 { | |
| t.Errorf("count: got %d, want 1", count) | |
| } | |
| } |