go-fuzz-utils
is a helper package for use with go-fuzz or other fuzzing utilities. It provides a simple interface to produce random values for various data types and can recursively populate complex structures from raw fuzz data generated by go-fuzz
. Spend more time writing property tests, and less time with ugly data type conversions, edge cases supporting full value ranges, nil
cases, etc. Simply feed go-fuzz
data into go-fuzz-utils
to produce fuzzed objects and use them in your property tests as needed.
When populating variables, you can configure a number of parameters:
- Minimum/maximum sizes of strings, maps, slices
- Probability of
nil
for maps, slices, pointers - Depth limit for nested structures
- Toggle for filling unexported fields in structures
- Probability of skipping a field when filling (to randomly fuzz over valid structure fields)
Import this package into your go-fuzz
tests:
import "github.com/trailofbits/go-fuzz-utils"
Construct a new TypeProvider
using NewTypeProvider(...)
.
func Fuzz(data []byte) int {
// Create a new type provider
tp, err := go_fuzz_utils.NewTypeProvider(data)
if err != nil {
return 0 // not enough data was supplied, exit gracefully for the next fuzzing iteration
}
[...]
Note: the data go-fuzz
generates on some runs may be too small to construct the TypeProvider
or derive all the values needed for your test. Ensure errors are handled appropriately. If one is encountered, exit gracefully to continue to the next run where more data may be produced. Fill parameters such as mapping/slice/string length and nil
probability can be set using the SetParams[...]
methods.
You can obtain the necessary type of data with exported functions such as:
// Obtain a byte
b, err := tp.GetByte()
...
// Obtain a bool
bl, err := tp.GetBool()
...
// Obtain an int16
i16, err := tp.GetInt16()
...
// Obtain a float32
f32, err := tp.GetFloat32()
...
// Obtain a fixed-length string
strFixed, err := tp.GetFixedString(7)
...
// Obtain a dynamic-length string
strDynamic, err := tp.GetString() // uses TypeProvider parameters to determine length/nil possibility
...
// Obtain a fixed-length byte array
bytesFixed, err := tp.GetNBytes(2)
...
// Obtain a dynamic-length byte array
bytesDynamic, err := tp.GetBytes() // uses TypeProvider parameters to determine length/nil possibility
go-fuzz-utils
exposes a generic Fill(...)
method which can populate simple data types, mappings, arrays, and arbitrary structures recursively via reflection.
For example, given the following structure:
type Person struct {
ID uint64
Name string
Photo []byte
Employed bool
EmergencyContact *Person
}
You can simply perform a Fill
call to populate it with the fuzz data. Even though Person
has a circular reference in EmergencyContact
, you can configure depth limits and nil
bias settings to prevent infinite loops while giving us various deeply nested structures.
// Create a person struct and fill it recursively.
var p Person
err := tp.Fill(&p)
Similarly, you can fill other data types as needed:
// Create an array of mappings and fill them
mappingArr := make([]map[string]int, 15)
err = tp.Fill(&mappingArr)