Skip to content

Commit

Permalink
all: follow up on libfuzzer change
Browse files Browse the repository at this point in the history
Changes of note:

* Add instructions on README
* Switch from using 'var _ = _go_fuz_dep_.Main' to
  'var _ = _go_fuz_dep_.CoverTab' to avoid
  "imported but not used" errors in instrumented code.
  This is because CoverTab is always present,
  but libFuzzer code doesn't need Main.
* Switch from a format string to text/template to
  generate the main package.
* Rework the 'go build' command at the core of buildInstrumentedBinary.
  Passing an empty "extraBuildFlags" to go build does not work:
  go build interprets the empty string as a package name.
  Also, don't bother renaming outf; it gets renamed (for libfuzzer)
  or read and deleted (for go-fuzz) later.
* Extract shared code from go-fuzz-dep main.go/main_libFuzzer.go,
  so that the only code in each is truly specific to that target.
  • Loading branch information
josharian authored and dvyukov committed Mar 13, 2019
1 parent dea5d38 commit ee722ec
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 76 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,25 @@ value should be less than ~5000, otherwise fuzzer can miss new interesting input
due to hash collisions. And finally ```uptime``` is uptime of the process. This same
information is also served via http (see the ```-http``` flag).


## libFuzzer support

go-fuzz-build can also generate an archive file
that can be used with [libFuzzer](https://llvm.org/docs/LibFuzzer.html)
instead of go-fuzz. (Requires linux.)

Sample usage:

```
$ cd $GOPATH/src/github.com/dvyukov/go-fuzz-corpus/fmt
$ go-fuzz-build -libfuzzer # produces fmt.a
$ clang -fsanitize=fuzzer fmt.a -o fmt.libfuzzer
$ ./fmt.libfuzzer
```

When run with `-libfuzzer`, go-fuzz-build adds the additional build tag
`gofuzz_libfuzzer` when building code.

### Random Notes

go-fuzz-build builds the program with gofuzz build tag, this allows to put the
Expand Down
2 changes: 1 addition & 1 deletion go-fuzz-build/cover.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func instrument(pkg, fullName string, fset *token.FileSet, parsedFile *ast.File,
info: info,
}
if sonar == nil {
file.addImport("go-fuzz-dep", fuzzdepPkg, "Main")
file.addImport("go-fuzz-dep", fuzzdepPkg, "CoverTab")
ast.Walk(file, file.astFile)
} else {
s := &Sonar{
Expand Down
84 changes: 38 additions & 46 deletions go-fuzz-build/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"runtime"
"runtime/pprof"
"strings"
"text/template"

"golang.org/x/tools/go/packages"

Expand All @@ -37,11 +38,9 @@ var (

func makeTags() string {
tags := "gofuzz"

if *flagLibFuzzer {
tags += " " + "gofuzz_libfuzzer"
}

if len(*flagTag) > 0 {
tags += " " + *flagTag
}
Expand Down Expand Up @@ -72,6 +71,15 @@ func main() {
defer c.cleanup() // delete workdir as needed, etc.
c.populateWorkdir() // copy tools and packages to workdir as needed

if *flagOut == "" {
// TODO: Context method
ext := ".zip"
if *flagLibFuzzer {
ext = ".a"
}
*flagOut = c.pkgs[0].Name + "-fuzz" + ext
}

// Gather literals, instrument, and compile.
// Order matters here!
// buildInstrumentedBinary (and instrumentPackages) modify the AST.
Expand All @@ -93,11 +101,6 @@ func main() {

if *flagLibFuzzer {
archive := c.buildInstrumentedBinary(&blocks, nil)

if *flagOut == "" {
*flagOut = c.pkgs[0].Name + ".a"
}

err := os.Rename(archive, *flagOut)
if err != nil {
c.failf("failed to rename file: %v", err)
Expand All @@ -114,10 +117,6 @@ func main() {
os.Remove(metaData)
}()

if *flagOut == "" {
// TODO: Context method
*flagOut = c.pkgs[0].Name + "-fuzz.zip"
}
outf, err := os.Create(*flagOut)
if err != nil {
c.failf("failed to create output file: %v", err)
Expand Down Expand Up @@ -322,27 +321,16 @@ func (c *Context) createMeta(lits map[Literal]struct{}, blocks []CoverBlock, son
return f
}

func extraBuildFlags() string {
if *flagLibFuzzer {
return "-buildmode=c-archive"
}
return ""
}

func (c *Context) buildInstrumentedBinary(blocks *[]CoverBlock, sonar *[]CoverBlock) string {
c.instrumentPackages(blocks, sonar)
mainPkg := c.createFuzzMain(c.pkgpath)

mainPkg := c.createFuzzMain()
outf := c.tempFile()
os.Remove(outf)

if !*flagLibFuzzer {
outf += ".exe"
} else {
outf += ".a"
args := []string{"build", "-tags", makeTags()}
if *flagLibFuzzer {
args = append(args, "-buildmode=c-archive")
}

cmd := exec.Command("go", "build", "-tags", makeTags(), extraBuildFlags(), "-o", outf, mainPkg)
args = append(args, "-o", outf, mainPkg)
cmd := exec.Command("go", args...)
cmd.Env = append(os.Environ(),
"GOROOT="+filepath.Join(c.workdir, "goroot"),
"GOPATH="+filepath.Join(c.workdir, "gopath"),
Expand Down Expand Up @@ -421,20 +409,24 @@ func (c *Context) copyFuzzDep() {
}
}

func funcMain() string {
if !*flagLibFuzzer {
return mainSrc
func (c *Context) funcMain() []byte {
t := mainSrc
if *flagLibFuzzer {
t = mainSrcLibFuzzer
}

return mainSrcLibFuzzer
dot := map[string]string{"Pkg": c.pkgpath, "Func": *flagFunc}
buf := new(bytes.Buffer)
if err := t.Execute(buf, dot); err != nil {
c.failf("could not execute template: %v", err)
}
return buf.Bytes()
}

func (c *Context) createFuzzMain(pkg string) string {
mainPkg := filepath.Join(pkg, "go.fuzz.main")
func (c *Context) createFuzzMain() string {
mainPkg := filepath.Join(c.pkgpath, "go.fuzz.main")
path := filepath.Join(c.workdir, "gopath", "src", mainPkg)
c.mkdirAll(path)
src := fmt.Sprintf(funcMain(), pkg, *flagFunc)
c.writeFile(filepath.Join(path, "main.go"), []byte(src))
c.writeFile(filepath.Join(path, "main.go"), c.funcMain())
return mainPkg
}

Expand Down Expand Up @@ -613,27 +605,27 @@ func (c *Context) mkdirAll(dir string) {
}
}

var mainSrc = `
var mainSrc = template.Must(template.New("main").Parse(`
package main
import (
target "%v"
target "{{.Pkg}}"
dep "github.com/dvyukov/go-fuzz/go-fuzz-dep"
)
func main() {
dep.Main(target.%v)
dep.Main(target.{{.Func}})
}
`
`))

var mainSrcLibFuzzer = `
var mainSrcLibFuzzer = template.Must(template.New("main").Parse(`
package main
import (
"unsafe"
"reflect"
target "%v"
dep "go-fuzz-dep"
target "{{.Pkg}}"
dep "github.com/dvyukov/go-fuzz/go-fuzz-dep"
)
// #cgo CFLAGS: -Wall -Werror
Expand All @@ -660,11 +652,11 @@ func LLVMFuzzerTestOneInput(data uintptr, size uint64) int {
}
input := *(*[]byte)(unsafe.Pointer(sh))
target.%v(input)
target.{{.Func}}(input)
return 0
}
func main() {
}
`
`))
17 changes: 17 additions & 0 deletions go-fuzz-dep/cover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2019 go-fuzz project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// +build gofuzz

package gofuzzdep

import (
. "github.com/dvyukov/go-fuzz/go-fuzz-defs"
)

// Bool is just a bool.
// It is used by code autogenerated by go-fuzz-build
// to avoid compilation errors when a user's code shadows the built-in bool.
type Bool = bool

var CoverTab *[CoverSize]byte
11 changes: 1 addition & 10 deletions go-fuzz-dep/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,10 @@ import (
. "github.com/dvyukov/go-fuzz/go-fuzz-defs"
)

// Bool is just a bool.
// It is used by code autogenerated by go-fuzz-build
// to avoid compilation errors when a user's code shadows the built-in bool.
type Bool = bool

var (
inFD FD
outFD FD

CoverTab *[CoverSize]byte
input []byte
sonarRegion []byte
sonarPos uint32
input []byte
)

func init() {
Expand Down
19 changes: 0 additions & 19 deletions go-fuzz-dep/main_libFuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,9 @@ import (
. "github.com/dvyukov/go-fuzz/go-fuzz-defs"
)

// Bool is just a bool.
// It is used by code autogenerated by go-fuzz-build
// to avoid compilation errors when a user's code shadows the built-in bool.
type Bool = bool

var (
CoverTab *[CoverSize]byte
sonarRegion []byte
sonarPos uint32
CoverTabTmp [CoverSize]byte
)

func init() {
CoverTab = (*[CoverSize]byte)(unsafe.Pointer(&CoverTabTmp[0]))
}

func Initialize(coverTabPtr unsafe.Pointer, coverTabSize uint64) {
if coverTabSize != CoverSize {
panic("Incorrect cover tab size")
}
CoverTab = (*[CoverSize]byte)(coverTabPtr)
}

func Main(f func([]byte) int) {
}
5 changes: 5 additions & 0 deletions go-fuzz-dep/sonar.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import (
. "github.com/dvyukov/go-fuzz/go-fuzz-defs"
)

var (
sonarRegion []byte
sonarPos uint32
)

const failure = ^uint8(0)

type iface struct {
Expand Down

0 comments on commit ee722ec

Please sign in to comment.