-
Notifications
You must be signed in to change notification settings - Fork 5
/
fuzz.go
103 lines (86 loc) · 1.85 KB
/
fuzz.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package fastrlp
import (
"bytes"
"reflect"
fuzz "github.com/google/gofuzz"
)
type FuzzObject interface {
Marshaler
Unmarshaler
}
type FuzzError struct {
Source, Target interface{}
}
func (f *FuzzError) Error() string {
return "failed to encode fuzz object"
}
type FuzzOption func(f *Fuzzer)
func WithPostHook(fnc func(FuzzObject) error) FuzzOption {
return func(f *Fuzzer) {
f.postHook = fnc
}
}
func WithDefaults(fnc func(FuzzObject)) FuzzOption {
return func(f *Fuzzer) {
f.defaults = append(f.defaults, fnc)
}
}
func copyObj(obj interface{}) interface{} {
return reflect.New(reflect.TypeOf(obj).Elem()).Interface()
}
type Fuzzer struct {
*fuzz.Fuzzer
defaults []func(FuzzObject)
postHook func(FuzzObject) error
}
func (f *Fuzzer) applyDefaults(obj FuzzObject) FuzzObject {
for _, fn := range f.defaults {
fn(obj)
}
return obj
}
func Fuzz(num int, base FuzzObject, opts ...FuzzOption) error {
f := &Fuzzer{
Fuzzer: fuzz.New(),
defaults: []func(FuzzObject){},
}
for _, opt := range opts {
opt(f)
}
fuzzImpl := func() error {
// marshal object with the fuzzing
obj := copyObj(base).(FuzzObject)
f.Fuzz(obj)
f.applyDefaults(obj)
// unmarshal object
obj2 := f.applyDefaults(copyObj(obj).(FuzzObject))
data, err := obj.MarshalRLPTo(nil)
if err != nil {
return err
}
if err := obj2.UnmarshalRLP(data); err != nil {
return err
}
// instead of relying on DeepEqual and issues with zero arrays and so on
// we use the rlp marshal values to compare
data2, err := obj2.MarshalRLPTo(nil)
if err != nil {
return err
}
if !bytes.Equal(data, data2) {
return &FuzzError{Source: obj, Target: obj2}
}
if f.postHook != nil {
if err := f.postHook(obj2); err != nil {
return err
}
}
return nil
}
for i := 0; i < num; i++ {
if err := fuzzImpl(); err != nil {
return err
}
}
return nil
}