Skip to content

Commit

Permalink
multiple corpus locations as input to fuzzing, and change default out…
Browse files Browse the repository at this point in the history
…put corpus location (#7)

See PR #7 comment for details.

* fuzzing_rich_signatures.txt: multiple corpus locations as input to fuzzing, change default dest

* main.go: multiple corpus locations as input to fuzzing, change default dest

* cache.go: multiple corpus locations as input to fuzzing, change default dest

* richsig.go: multiple corpus locations as input to fuzzing, change default dest

* richsig_test.go: multiple corpus locations as input to fuzzing, change default dest

* exec.go: multiple corpus locations as input to fuzzing, change default dest

* packages.go: multiple corpus locations as input to fuzzing, change default dest
  • Loading branch information
thepudds authored Aug 3, 2019
1 parent 603cd5f commit 709a788
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 76 deletions.
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)
}
*/
})
}
}
Loading

0 comments on commit 709a788

Please sign in to comment.