| // 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) |
| } |
| } |