Skip to content

Commit

Permalink
go/storage: Add backend api fuzzing
Browse files Browse the repository at this point in the history
  • Loading branch information
jberci committed Dec 13, 2019
1 parent 6a4a2e5 commit 57f6ea3
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 11 deletions.
27 changes: 16 additions & 11 deletions go/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,27 @@ test-vectors/staking: generate
@env -u GOPATH $(OASIS_GO) run ./staking/gen_vectors

# Fuzzing binaries.
build-fuzz: consensus/tendermint/fuzz/fuzz-fuzz.zip
consensus/tendermint/fuzz/fuzz-fuzz.zip: .FORCE
@echo "Building consensus fuzzer"
build-fuzz: consensus/tendermint/fuzz/fuzz-fuzz.zip storage/fuzz/fuzz-fuzz.zip
%/fuzz-fuzz.zip: .FORCE
@echo "Building $@"
@cd "$$(dirname "$@")"; go-fuzz-build
@cd "$$(dirname "$@")/gencorpus"; env -u GOPATH $(OASIS_GO) build -tags gofuzz

# Run fuzzing.
define canned-fuzz-run
set -x; cd "$$(dirname "$<")"; \
if ! [ -d corpus ]; then \
mkdir corpus; \
pushd corpus; \
../gencorpus/gencorpus; \
popd; \
fi; \
go-fuzz -bin=./fuzz-fuzz.zip -procs=1 -v=4
endef
fuzz-consensus: consensus/tendermint/fuzz/fuzz-fuzz.zip
set -x; cd "$$(dirname "$<")"; \
if ! [ -d corpus ]; then \
mkdir corpus; \
pushd corpus; \
../gencorpus/gencorpus; \
popd; \
fi; \
go-fuzz -bin=./fuzz-fuzz.zip -procs=1 -v=4
$(canned-fuzz-run)
fuzz-storage: storage/fuzz/fuzz-fuzz.zip
$(canned-fuzz-run)

# Clean.
clean:
Expand Down
74 changes: 74 additions & 0 deletions go/common/fuzz/fuzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,77 @@ func MakeSampleBlob(typ interface{}) []byte {

return source.GetTraceback()
}

// InterfaceFuzzer is a helper class for fuzzing methods in structs or interfaces.
type InterfaceFuzzer struct {
instance interface{}

typeObject reflect.Type
}

// DispatchBlob constructs a method call with arguments from the given blob and dispatches it.
func (i *InterfaceFuzzer) DispatchBlob(blob []byte) ([]reflect.Value, bool) {
if len(blob) < 1 {
return nil, false
}

meth := int(blob[0])
if meth >= i.typeObject.NumMethod() {
return nil, false
}
method := i.typeObject.Method(meth)

source := NewRandSource(blob[1:])
fuzzer := gofuzz.New()
fuzzer = fuzzer.RandSource(source)

in := []reflect.Value{}

for arg := 0; arg < method.Type.NumIn(); arg++ {
val := reflect.New(method.Type.In(arg))
if val.Interface() != nil {
fuzzer.Fuzz(val.Interface())
}
in = append(in, val)
}

return method.Func.Call(in), true
}

// MakeSampleBlobs returns an array of sample blobs for all methods in the interface.
func (i *InterfaceFuzzer) MakeSampleBlobs() [][]byte {
blobList := [][]byte{}
for meth := 0; meth < i.typeObject.NumMethod(); meth++ {
source := NewTrackingRandSource()
fuzzer := gofuzz.New()
fuzzer = fuzzer.RandSource(source)

method := i.typeObject.Method(meth).Type
blob := []byte{byte(meth)}
for arg := 1; arg < method.NumIn(); arg++ {
val := reflect.New(method.In(arg)).Interface()
if val != nil {
fuzzer.Fuzz(val)
}
}

blob = append(blob, source.GetTraceback()...)
blobList = append(blobList, blob)
}

return blobList
}

// Method returns the method object associated with the instance's index-th method.
func (i *InterfaceFuzzer) Method(method int) reflect.Method {
return i.typeObject.Method(method)
}

// NewInterfaceFuzzer creates a new InterfaceFuzzer for the given instance.
func NewInterfaceFuzzer(instance interface{}) *InterfaceFuzzer {
ret := &InterfaceFuzzer{
instance: instance,
typeObject: reflect.TypeOf(instance).Elem(),
}
return ret
}
5 changes: 5 additions & 0 deletions go/storage/fuzz/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
corpus/
crashers/
suppressions/
*.zip
gencorpus/gencorpus
22 changes: 22 additions & 0 deletions go/storage/fuzz/fuzz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// +build gofuzz

package fuzz

import (
"context"

commonFuzz "github.com/oasislabs/oasis-core/go/common/fuzz"
"github.com/oasislabs/oasis-core/go/storage"
)

func Fuzz(data []byte) int {
storage, err := storage.New(context.Background(), "/tmp/oasis-node-fuzz-storage", nil, nil, nil)
if err != nil {
panic(err)
}
fuzzer := commonFuzz.NewInterfaceFuzzer(storage)

fuzzer.DispatchBlob(data)

return 0
}
35 changes: 35 additions & 0 deletions go/storage/fuzz/gencorpus/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// +build gofuzz

// Gencorpus implements a simple utility to generate corpus files for the fuzzer.
// It has no command-line options and creates the files in the current working directory.
package main

import (
"context"
"fmt"
"io/ioutil"

commonFuzz "github.com/oasislabs/oasis-core/go/common/fuzz"
"github.com/oasislabs/oasis-core/go/common/identity"
"github.com/oasislabs/oasis-core/go/storage"
)

const (
samplesPerMethod int = 20
)

func main() {
storage, err := storage.New(context.Background(), "/tmp/oasis-node-fuzz-storage", &identity.Identity{}, nil, nil)
if err != nil {
panic(err)
}
fuzzer := commonFuzz.NewInterfaceFuzzer(storage)

for i := 0; i < samplesPerMethod; i++ {
blobs := fuzzer.MakeSampleBlobs()
for meth := 0; meth < len(blobs); meth++ {
fileName := fmt.Sprintf("%s_%02d.bin", fuzzer.Method(meth).Name, i)
_ = ioutil.WriteFile(fileName, blobs[meth], 0644)
}
}
}

0 comments on commit 57f6ea3

Please sign in to comment.