aboutsummaryrefslogtreecommitdiff
path: root/src/internal/fuzz
diff options
context:
space:
mode:
authorKatie Hockman <katie@golang.org>2021-06-07 17:51:51 -0400
committerKatie Hockman <katie@golang.org>2021-06-15 19:32:21 +0000
commit413c125da38990720744c0d98ab65c0d5b1602da (patch)
tree0e8e9e63f364abc3c00380e092311c621b4a1ce7 /src/internal/fuzz
parent965b1147549ef28a407bd4e8f5efe5e7b7616f80 (diff)
downloadgo-413c125da38990720744c0d98ab65c0d5b1602da.tar.xz
[dev.fuzz] testing: convert seed corpus values where possible
The types provided in f.Fuzz will be viewed as the canonical types for fuzzing. If the type is different for a seed corpus entry, then the testing package will attempt to convert it. If it can't convert it, f.Fuzz will fail. Currently, this allows converting types that may result in precision loss or a semantically different value. For example, an int(-1) can be converted to uint even though the value could be math.MaxUint64. There is a TODO to consider improving this in the future. Updates golang/go#45593 Change-Id: I2e752119662f46b68445d42b1ffa46dd30e9faea Reviewed-on: https://go-review.googlesource.com/c/go/+/325702 Trust: Katie Hockman <katie@golang.org> Run-TryBot: Katie Hockman <katie@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org>
Diffstat (limited to 'src/internal/fuzz')
-rw-r--r--src/internal/fuzz/fuzz.go45
1 files changed, 41 insertions, 4 deletions
diff --git a/src/internal/fuzz/fuzz.go b/src/internal/fuzz/fuzz.go
index 929f78bb17..9ffa8beb16 100644
--- a/src/internal/fuzz/fuzz.go
+++ b/src/internal/fuzz/fuzz.go
@@ -727,15 +727,52 @@ func readCorpusData(data []byte, types []reflect.Type) ([]interface{}, error) {
if err != nil {
return nil, fmt.Errorf("unmarshal: %v", err)
}
+ if err = CheckCorpus(vals, types); err != nil {
+ return nil, err
+ }
+ return vals, nil
+}
+
+// CheckCorpus verifies that the types in vals match the expected types
+// provided. If not, attempt to convert them. If that's not possible, return an
+// error.
+func CheckCorpus(vals []interface{}, types []reflect.Type) error {
if len(vals) != len(types) {
- return nil, fmt.Errorf("wrong number of values in corpus file: %d, want %d", len(vals), len(types))
+ return fmt.Errorf("wrong number of values in corpus file: %d, want %d", len(vals), len(types))
}
for i := range types {
- if reflect.TypeOf(vals[i]) != types[i] {
- return nil, fmt.Errorf("mismatched types in corpus file: %v, want %v", vals, types)
+ orig := reflect.ValueOf(vals[i])
+ origType := orig.Type()
+ wantType := types[i]
+ if origType == wantType {
+ continue // already the same type
+ }
+ // Attempt to convert the corpus value to the expected type
+ if !origType.ConvertibleTo(wantType) {
+ return fmt.Errorf("cannot convert %v to %v", origType, wantType)
}
+ convertedVal, ok := convertToType(orig, wantType)
+ if !ok {
+ return fmt.Errorf("error converting %v to %v", origType, wantType)
+ }
+ // TODO: Check that the value didn't change.
+ // e.g. val went from int64(-1) -> uint(0) -> int64(0) which should fail
+
+ // Updates vals to use the newly converted value of the expected type.
+ vals[i] = convertedVal.Interface()
}
- return vals, nil
+ return nil
+}
+
+func convertToType(orig reflect.Value, t reflect.Type) (converted reflect.Value, ok bool) {
+ // Convert might panic even if ConvertibleTo returns true, so catch
+ // that panic and return false.
+ defer func() {
+ if r := recover(); r != nil {
+ ok = false
+ }
+ }()
+ return orig.Convert(t), true
}
// writeToCorpus atomically writes the given bytes to a new file in testdata.