Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.

Commit aa92dd7

Browse files
committed
Add go fuzzer in preparation for testing. Also gofmt a few files that needed it.
1 parent 8a5cc87 commit aa92dd7

File tree

6 files changed

+524
-4
lines changed

6 files changed

+524
-4
lines changed

pkg/util/fuzz.go

+263
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/*
2+
Copyright 2014 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package util
18+
19+
import (
20+
"fmt"
21+
"math/rand"
22+
"reflect"
23+
)
24+
25+
// Fuzzer knows how to fill any object with random fields.
26+
type Fuzzer struct {
27+
customFuzz map[reflect.Type]func(reflect.Value)
28+
}
29+
30+
// NewFuzzer returns a new Fuzzer with the given custom fuzzing functions.
31+
// Each entry in fuzzFuncs must be a function with one parameter, which will
32+
// be the variable we wish that function to fill with random data. For this
33+
// to be effective, the variable type should be either a pointer, map, slice,
34+
// etc. These functions are called sensibly, e.g., if you wanted custom string
35+
// fuzzing, the function `func(s *string)` would get called and passed the
36+
// address of strings.
37+
func NewFuzzer(fuzzFuncs ...interface{}) *Fuzzer {
38+
f := &Fuzzer{
39+
map[reflect.Type]func(reflect.Value){},
40+
}
41+
for i := range fuzzFuncs {
42+
v := reflect.ValueOf(fuzzFuncs[i])
43+
if v.Kind() != reflect.Func {
44+
panic("Need only funcs!")
45+
}
46+
t := v.Type()
47+
if t.NumIn() != 1 || t.NumOut() != 0 {
48+
panic("Need 1 in and 0 out params!")
49+
}
50+
argT := t.In(0)
51+
// fmt.Printf("Making entry for thing with '%v' type\n", argT)
52+
f.customFuzz[argT] = func(toFuzz reflect.Value) {
53+
if toFuzz.Type().AssignableTo(argT) {
54+
v.Call([]reflect.Value{toFuzz})
55+
} else if toFuzz.Type().ConvertibleTo(argT) {
56+
v.Call([]reflect.Value{toFuzz.Convert(argT)})
57+
} else {
58+
panic(fmt.Errorf("%#v neither ConvertibleTo nor AssignableTo %v",
59+
toFuzz.Interface(),
60+
argT))
61+
}
62+
}
63+
}
64+
return f
65+
}
66+
67+
// Fuzz recursively fills all of obj's fields with something random.
68+
// Not safe for cyclic or tree-like structs!
69+
// obj must be a pointer. Only exported (public) fields can be set (thanks, golang :/ )
70+
// Intended for tests, so will panic on bad input or unimplemented fields.
71+
func (f *Fuzzer) Fuzz(obj interface{}) {
72+
v := reflect.ValueOf(obj)
73+
if v.Kind() != reflect.Ptr {
74+
panic("needed ptr!")
75+
}
76+
v = v.Elem()
77+
f.doFuzz(v)
78+
}
79+
80+
func (f *Fuzzer) doFuzz(v reflect.Value) {
81+
if !v.CanSet() {
82+
return
83+
}
84+
// Check for both pointer and non-pointer custom functions.
85+
if v.CanAddr() && f.tryCustom(v.Addr()) {
86+
return
87+
}
88+
if f.tryCustom(v) {
89+
return
90+
}
91+
if fn, ok := fillFuncMap[v.Kind()]; ok {
92+
fn(v)
93+
return
94+
}
95+
switch v.Kind() {
96+
case reflect.Map:
97+
if rand.Intn(5) > 0 {
98+
v.Set(reflect.MakeMap(v.Type()))
99+
if f.tryCustom(v) {
100+
return
101+
}
102+
n := 1 + rand.Intn(10)
103+
for i := 0; i < n; i++ {
104+
key := reflect.New(v.Type().Key()).Elem()
105+
f.doFuzz(key)
106+
val := reflect.New(v.Type().Elem()).Elem()
107+
f.doFuzz(val)
108+
v.SetMapIndex(key, val)
109+
}
110+
return
111+
}
112+
v.Set(reflect.Zero(v.Type()))
113+
case reflect.Ptr:
114+
if rand.Intn(5) > 0 {
115+
v.Set(reflect.New(v.Type().Elem()))
116+
f.doFuzz(v.Elem())
117+
return
118+
}
119+
v.Set(reflect.Zero(v.Type()))
120+
case reflect.Slice:
121+
if rand.Intn(5) > 0 {
122+
n := 1 + rand.Intn(10)
123+
v.Set(reflect.MakeSlice(v.Type(), n, n))
124+
if f.tryCustom(v) {
125+
return
126+
}
127+
for i := 0; i < n; i++ {
128+
f.doFuzz(v.Index(i))
129+
}
130+
return
131+
}
132+
v.Set(reflect.Zero(v.Type()))
133+
case reflect.Struct:
134+
for i := 0; i < v.NumField(); i++ {
135+
f.doFuzz(v.Field(i))
136+
}
137+
case reflect.Array:
138+
fallthrough
139+
case reflect.Chan:
140+
fallthrough
141+
case reflect.Func:
142+
fallthrough
143+
case reflect.Interface:
144+
fallthrough
145+
default:
146+
panic(fmt.Sprintf("Can't handle %#v", v.Interface()))
147+
}
148+
}
149+
150+
// tryCustom searches for custom handlers and Randomizer implementations, and
151+
// returns true if it finds a match and successfully randomizes v.
152+
func (f Fuzzer) tryCustom(v reflect.Value) bool {
153+
// fmt.Printf("Trying thing with '%v' type\n", v.Type())
154+
switch v.Kind() {
155+
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Slice:
156+
if v.IsNil() {
157+
return false
158+
}
159+
case reflect.Ptr:
160+
if v.IsNil() {
161+
if !v.CanSet() {
162+
return false
163+
}
164+
v.Set(reflect.New(v.Type().Elem()))
165+
}
166+
case reflect.Map:
167+
if v.IsNil() {
168+
if !v.CanSet() {
169+
return false
170+
}
171+
v.Set(reflect.MakeMap(v.Type()))
172+
}
173+
}
174+
if f, ok := f.customFuzz[v.Type()]; ok {
175+
f(v)
176+
return true
177+
}
178+
return false
179+
}
180+
181+
func fuzzInt(v reflect.Value) {
182+
v.SetInt(int64(RandUint64()))
183+
}
184+
185+
func fuzzUint(v reflect.Value) {
186+
v.SetUint(RandUint64())
187+
}
188+
189+
var fillFuncMap = map[reflect.Kind]func(reflect.Value){
190+
reflect.Bool: func(v reflect.Value) {
191+
v.SetBool(RandBool())
192+
},
193+
reflect.Int: fuzzInt,
194+
reflect.Int8: fuzzInt,
195+
reflect.Int16: fuzzInt,
196+
reflect.Int32: fuzzInt,
197+
reflect.Int64: fuzzInt,
198+
reflect.Uint: fuzzUint,
199+
reflect.Uint8: fuzzUint,
200+
reflect.Uint16: fuzzUint,
201+
reflect.Uint32: fuzzUint,
202+
reflect.Uint64: fuzzUint,
203+
reflect.Uintptr: fuzzUint,
204+
reflect.Float32: func(v reflect.Value) {
205+
v.SetFloat(float64(rand.Float32()))
206+
},
207+
reflect.Float64: func(v reflect.Value) {
208+
v.SetFloat(rand.Float64())
209+
},
210+
reflect.Complex64: func(v reflect.Value) {
211+
panic("unimplemented")
212+
},
213+
reflect.Complex128: func(v reflect.Value) {
214+
panic("unimplemented")
215+
},
216+
reflect.String: func(v reflect.Value) {
217+
v.SetString(RandString())
218+
},
219+
reflect.UnsafePointer: func(v reflect.Value) {
220+
panic("unimplemented")
221+
},
222+
}
223+
224+
// RandBool returns true or false randomly.
225+
func RandBool() bool {
226+
if rand.Int()&1 == 1 {
227+
return true
228+
}
229+
return false
230+
}
231+
232+
type charRange struct {
233+
first, last rune
234+
}
235+
236+
// choose returns a random unicode character from the given range.
237+
func (r *charRange) choose() rune {
238+
count := int64(r.last - r.first)
239+
return r.first + rune(rand.Int63n(count))
240+
}
241+
242+
var unicodeRanges = []charRange{
243+
{' ', '~'}, // ASCII characters
244+
{'\u00a0', '\u02af'}, // Multi-byte encoded characters
245+
{'\u4e00', '\u9fff'}, // Common CJK (even longer encodings)
246+
}
247+
248+
// RandString makes a random string up to 20 characters long. The returned string
249+
// may include a variety of (valid) UTF-8 encodings. For testing.
250+
func RandString() string {
251+
n := rand.Intn(20)
252+
runes := make([]rune, n)
253+
for i := range runes {
254+
runes[i] = unicodeRanges[rand.Intn(len(unicodeRanges))].choose()
255+
}
256+
return string(runes)
257+
}
258+
259+
// RandUint64 makes random 64 bit numbers.
260+
// Weirdly, rand doesn't have a function that gives you 64 random bits.
261+
func RandUint64() uint64 {
262+
return uint64(rand.Uint32())<<32 | uint64(rand.Uint32())
263+
}

0 commit comments

Comments
 (0)