Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

multiple corpus locations as input to fuzzing, and change default output corpus location #7

Merged
merged 7 commits into from
Aug 3, 2019
79 changes: 75 additions & 4 deletions fuzz/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ import (

// CacheDir returns <GOPATH>/pkg/fuzz/<GOOS_GOARCH>/<hash>/<package_fuzzfunc>/
func CacheDir(hash, pkgName, fuzzName string) string {
gp := os.Getenv("GOPATH")
if gp == "" {
gp = build.Default.GOPATH
}
gp := Gopath()
s := strings.Split(gp, string(os.PathListSeparator))
if len(s) > 1 {
gp = s[0]
Expand All @@ -31,6 +28,15 @@ func CacheDir(hash, pkgName, fuzzName string) string {
hash, fuzzName)
}

// Gopath returns the current effective GOPATH (from the GOPATH env, or the default if env var now set).
func Gopath() string {
gp := os.Getenv("GOPATH")
if gp == "" {
gp = build.Default.GOPATH
}
return gp
}

// Hash returns a string representing the hash of the files in a package, its dependencies,
// as well as the fuzz func name, the version of go and the go-fuzz-build binary.
func Hash(pkgPath, funcName, trimPrefix string, env []string, verbose bool) (string, error) {
Expand Down Expand Up @@ -167,3 +173,68 @@ func goListDeps(pkg string, env []string) ([]string, error) {
}
return results, nil
}

// CopyDir is a simple implementation of recursively copying a directory.
// The main use case is copying a corpus directory (which does not have symlinks, etc.).
// Files that already exist in the destination are left alone.
func CopyDir(dst string, src string) error {
report := func(err error) error {
return fmt.Errorf("copy dir failed from %s to %s: %v", dst, src, err)
}
files, err := ioutil.ReadDir(src)
if err != nil {
return report(err)
}
if err := os.MkdirAll(dst, 0700); err != nil {
return report(err)
}
for _, f := range files {
dstName := filepath.Join(dst, f.Name())
srcName := filepath.Join(src, f.Name())
if f.IsDir() {
if err := CopyDir(dstName, srcName); err != nil {
return report(err)
}
} else {
if err := CopyFile(dstName, srcName); err != nil {
return report(err)
}
}
}
return nil
}

// CopyFile copies a file. A dst file that already exists
// is left alone, and is not an error. The main use case
// is updating a corpus from GOPATH/pkg/fuzz/corpus/...,
// and we trust the destination if the file already exists.
func CopyFile(dst string, src string) error {
report := func(err error) error {
return fmt.Errorf("copy file failed from %s to %s: %v", dst, src, err)
}
if PathExists(dst) {
return nil
}
w, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700)
if err != nil {
return report(err)
}
defer w.Close()
r, err := os.Open(src)
if err != nil {
return report(err)
}
defer r.Close()
if _, err := io.Copy(w, r); err != nil {
return report(err)
}
return nil
}

// PathExists reports if a path is exists.
func PathExists(path string) bool {
if _, err := os.Stat(path); os.IsNotExist(err) {
return false
}
return true
}
21 changes: 13 additions & 8 deletions fuzz/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,28 @@ func Instrument(function Func, verbose bool) (Target, error) {
return report(fmt.Errorf("unexpected fuzz function: %#v", function))
}

// check if we have a plain data []byte signature, vs. a rich signature
plain, err := IsPlainSig(function.TypesFunc)
if err != nil {
return report(err)
}

var target Target
if !plain {
if plain {
// create our initial target struct using the actual func supplied by the user.
target = Target{UserFunc: function}
} else {
info("detected rich signature for %v.%v", function.PkgName, function.FuncName)
target, err = CreateWrapperFunc(function)
// create a wrapper function to handle the rich signature.
target, err = CreateRichSigWrapper(function)
if err != nil {
return report(err)
}
} else {
// create our initial target struct
target = Target{UserFunc: function}
// CreateRichSigWrapper was succesful, which means it populated the temp dir with the wrapper func.
// By the time we leave our current function, we are done with the temp dir
// that CreateRichSigWrapper created, so delete via a defer.
// (We can't delete it immediately because we haven't yet run go-fuzz-build on it).
defer os.RemoveAll(target.wrapperTempDir)
}

// Determine where our cacheDir is.
Expand All @@ -66,7 +73,7 @@ func Instrument(function Func, verbose bool) (Target, error) {
if _, err = os.Stat(finalZipPath); os.IsNotExist(err) {
// TODO: resume here ###########################################################################
// clean up the functions running around. switch to target.
// also, delete the temp dir. probably with a defer just after target returns succesfully
// also, delete the temp dir. probably with a defer just after target returns successfully
info("building instrumented binary for %v.%v", function.PkgName, function.FuncName)
outFile := filepath.Join(cacheDir, "fuzz.zip.partial")
var args []string
Expand Down Expand Up @@ -182,7 +189,6 @@ func (t *Target) cacheDir(verbose bool) (string, error) {
if t.savedCacheDir == "" {
// generate a hash covering the package, its dependencies, and some items like go-fuzz-build binary and go version
// TODO: pass verbose flag around?
// TODO: update packagedir for trimPrefix if / when target is introduced (want to trim TEMP dir)
var err error
var h string
if !t.hasWrapper {
Expand Down Expand Up @@ -219,7 +225,6 @@ func ExecGo(args []string, env []string) error {
}

// A maxDuration of 0 means no max time is enforced.
// TODO: added env. make public? put in fzgo/fuzz package?
func execCmd(name string, args []string, env []string, maxDuration time.Duration) error {
report := func(err error) error { return fmt.Errorf("exec %v error: %v", name, err) }

Expand Down
1 change: 0 additions & 1 deletion fuzz/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ func (f *Func) String() string {
// suggests not allowing something like 'go test -fuzz=. ./...' to match multiple fuzz functions.
// As an experiment, allowMultiFuzz flag allows that.
// FindFunc searches for a requested function to visit.
// TODO: from richsig
func FindFunc(pkgPattern, funcPattern string, env []string, allowMultiFuzz bool) ([]Func, error) {
report := func(err error) error {
return fmt.Errorf("error while loading packages for pattern %v: %v", pkgPattern, err)
Expand Down
17 changes: 8 additions & 9 deletions fuzz/richsig.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ func IsPlainSig(f *types.Func) (bool, error) {
return true, nil
}

// CreateWrapperFunc creates a temp working directory, then
// CreateRichSigWrapper creates a temp working directory, then
// creates a rich signature wrapping fuzz function.
func CreateWrapperFunc(function Func) (t Target, err error) {
func CreateRichSigWrapper(function Func) (t Target, err error) {
report := func(err error) (Target, error) {
return Target{}, fmt.Errorf("creating wrapper function for %s: %v", function.FuzzName(), err)
}
Expand All @@ -65,11 +65,10 @@ func CreateWrapperFunc(function Func) (t Target, err error) {
if err != nil {
return report(fmt.Errorf("create staging temp dir: %v", err))
}
// TODO: need to delete temp directory in non-error case.
defer func() {
// conditionally clean up. (this is a bit of an experiment to use named return err here).
if err != nil {
// on our our out, but encountered an error, so delete the temp dir
// on our way out, but encountered an error, so delete the temp dir
os.RemoveAll(tempDir)
}
}()
Expand Down Expand Up @@ -99,7 +98,10 @@ func CreateWrapperFunc(function Func) (t Target, err error) {

// write out temporary richsigwrapper.go file
var b bytes.Buffer
createWrapper(&b, function)
err = createWrapper(&b, function)
if err != nil {
return report(fmt.Errorf("failed constructing rich signature wrapper: %v", err))
}
err = ioutil.WriteFile(filepath.Join(wrapperDir, "richsigwrapper.go"), b.Bytes(), 0700)
if err != nil {
return report(fmt.Errorf("failed to create temporary richsigwrapper.go: %v", err))
Expand All @@ -121,8 +123,6 @@ func CreateWrapperFunc(function Func) (t Target, err error) {
// TODO: ########### resume finishing up here, also fuzz.Instrument, fuzz.Start ##########
// TODO: ##################################################################################

// TODO: need to delete temp directory in non-error case.

// Note: pkg patterns like 'fzgo/...' and 'fzgo/richsigwrapper' don't seem to work, but '.' does.
// (We cd'ed above to the working directory. Maybe a go/packages bug, not liking >1 GOPATH entry?)
functions, err := FindFunc(".", "FuzzRichSigWrapper", env, false)
Expand All @@ -149,8 +149,7 @@ func createWrapper(w io.Writer, function Func) error {
}

// start emitting the wrapper program!
// TODO: add in something like:
// fuzzer := gofuzz.New().NilChance(0.1).NumElements(0, 10).MaxDepth(10)
// TODO: add in something like: fuzzer := gofuzz.New().NilChance(0.1).NumElements(0, 10).MaxDepth(10)
fmt.Fprintf(w, "\npackage richsigwrapper\n")
fmt.Fprintf(w, "\nimport \"%s\"\n", function.PkgPath)
fmt.Fprintf(w, `
Expand Down
18 changes: 6 additions & 12 deletions fuzz/richsig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ func TestWrapperGeneration(t *testing.T) {
args args
wantOutput string
wantErr bool
// TODO: delete? update? not use currently.
// want []Func
}{
{
name: "only basic types: string, []byte, bool",
Expand Down Expand Up @@ -147,21 +145,17 @@ func fuzzOne (fuzzer *randparam.Fuzzer) {
var b bytes.Buffer
functions, err := FindFunc(tt.args.pkgPattern, tt.args.funcPattern, nil, tt.args.allowMultiFuzz)
if (err != nil) != tt.wantErr {
t.Errorf("FindFunc() error = %v, wantErr %v", err, tt.wantErr)
return
t.Fatalf("FindFunc() error = %v, wantErr %v", err, tt.wantErr)
}
err = createWrapper(&b, functions[0])
if err != nil {
t.Fatalf("createWrapper() error = %v", err)
}
createWrapper(&b, functions[0])
gotOutput := b.String()
diff := cmp.Diff(tt.wantOutput, gotOutput)
if diff != "" {
t.Fatalf("FindFunc() failed to match function output. diff:\n%s", diff)
}

/* TODO: delete? update?
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("FindFunc() = %v, want %v", got, tt.want)
t.Fatalf("createWrapper() failed to match function output. diff:\n%s", diff)
}
*/
})
}
}