[dev.fuzz] internal/fuzz: implement coverage and trace instrumentation

This CL makes two main changes to allow internal/fuzz to support
-d=libfuzzer instrumentation:

1. It extends cmd/link to define _counters and _ecounters symbols so
internal/fuzz can find the coverage counters.

2. It adds "trace" stub functions that implement the ABI expected by
cmd/compile for comparison instrumentation.

N.B., that -tags=libfuzzer should *not* be set, so that
internal/fuzz's trace routines will be used instead of runtime's
libfuzzer trampolines.

Also, the current implementation doesn't support multi-module builds
(i.e., compiling a Go program that spans multiple .so/.dll files).
Presumably this isn't an issue, since "go test -fuzz" will need to
recompile the binary with instrumentation anyway so it can make sure
to always use a single-module build. But we can revisit this if
necessary.

Change-Id: I9b1619119ab7477bebcfd5988b4b60499a7ab0d7
Reviewed-on: https://go-review.googlesource.com/c/go/+/308289
Trust: Matthew Dempsky <mdempsky@google.com>
Trust: Katie Hockman <katie@golang.org>
Reviewed-by: Katie Hockman <katie@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Run-TryBot: Katie Hockman <katie@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
Matthew Dempsky 2021-04-07 17:11:48 -07:00
parent 2f3bc725fe
commit 74f49f3366
3 changed files with 67 additions and 9 deletions

View file

@ -1746,7 +1746,9 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
// Coverage instrumentation counters for libfuzzer. // Coverage instrumentation counters for libfuzzer.
if len(state.data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 { if len(state.data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 {
state.allocateNamedSectionAndAssignSyms(&Segdata, "__libfuzzer_extra_counters", sym.SLIBFUZZER_EXTRA_COUNTER, sym.Sxxx, 06) sect := state.allocateNamedSectionAndAssignSyms(&Segdata, "__libfuzzer_extra_counters", sym.SLIBFUZZER_EXTRA_COUNTER, sym.Sxxx, 06)
ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._counters", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("internal/fuzz._ecounters", 0), sect)
} }
if len(state.data[sym.STLSBSS]) > 0 { if len(state.data[sym.STLSBSS]) > 0 {
@ -2410,6 +2412,7 @@ func (ctxt *Link) address() []*sym.Segment {
var noptr *sym.Section var noptr *sym.Section
var bss *sym.Section var bss *sym.Section
var noptrbss *sym.Section var noptrbss *sym.Section
var fuzzCounters *sym.Section
for i, s := range Segdata.Sections { for i, s := range Segdata.Sections {
if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && s.Name == ".tbss" { if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && s.Name == ".tbss" {
continue continue
@ -2421,17 +2424,17 @@ func (ctxt *Link) address() []*sym.Segment {
s.Vaddr = va s.Vaddr = va
va += uint64(vlen) va += uint64(vlen)
Segdata.Length = va - Segdata.Vaddr Segdata.Length = va - Segdata.Vaddr
if s.Name == ".data" { switch s.Name {
case ".data":
data = s data = s
} case ".noptrdata":
if s.Name == ".noptrdata" {
noptr = s noptr = s
} case ".bss":
if s.Name == ".bss" {
bss = s bss = s
} case ".noptrbss":
if s.Name == ".noptrbss" {
noptrbss = s noptrbss = s
case "__libfuzzer_extra_counters":
fuzzCounters = s
} }
} }
@ -2548,6 +2551,11 @@ func (ctxt *Link) address() []*sym.Segment {
ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length)) ctxt.xdefine("runtime.enoptrbss", sym.SNOPTRBSS, int64(noptrbss.Vaddr+noptrbss.Length))
ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length)) ctxt.xdefine("runtime.end", sym.SBSS, int64(Segdata.Vaddr+Segdata.Length))
if fuzzCounters != nil {
ctxt.xdefine("internal/fuzz._counters", sym.SLIBFUZZER_EXTRA_COUNTER, int64(fuzzCounters.Vaddr))
ctxt.xdefine("internal/fuzz._ecounters", sym.SLIBFUZZER_EXTRA_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length))
}
if ctxt.IsSolaris() { if ctxt.IsSolaris() {
// On Solaris, in the runtime it sets the external names of the // On Solaris, in the runtime it sets the external names of the
// end symbols. Unset them and define separate symbols, so we // end symbols. Unset them and define separate symbols, so we

View file

@ -4,8 +4,29 @@
package fuzz package fuzz
import (
"internal/unsafeheader"
"unsafe"
)
// coverage returns a []byte containing unique 8-bit counters for each edge of // coverage returns a []byte containing unique 8-bit counters for each edge of
// the instrumented source code. This coverage data will only be generated if // the instrumented source code. This coverage data will only be generated if
// `-d=libfuzzer` is set at build time. This can be used to understand the code // `-d=libfuzzer` is set at build time. This can be used to understand the code
// coverage of a test execution. // coverage of a test execution.
func coverage() []byte { return nil } func coverage() []byte {
addr := unsafe.Pointer(&_counters)
size := uintptr(unsafe.Pointer(&_ecounters)) - uintptr(addr)
var res []byte
*(*unsafeheader.Slice)(unsafe.Pointer(&res)) = unsafeheader.Slice{
Data: addr,
Len: int(size),
Cap: int(size),
}
return res
}
// _counters and _ecounters mark the start and end, respectively, of where
// the 8-bit coverage counters reside in memory. They're known to cmd/link,
// which specially assigns their addresses for this purpose.
var _counters, _ecounters [0]byte

View file

@ -0,0 +1,29 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !libfuzzer
package fuzz
import _ "unsafe" // for go:linkname
//go:linkname libfuzzerTraceCmp1 runtime.libfuzzerTraceCmp1
//go:linkname libfuzzerTraceCmp2 runtime.libfuzzerTraceCmp2
//go:linkname libfuzzerTraceCmp4 runtime.libfuzzerTraceCmp4
//go:linkname libfuzzerTraceCmp8 runtime.libfuzzerTraceCmp8
//go:linkname libfuzzerTraceConstCmp1 runtime.libfuzzerTraceConstCmp1
//go:linkname libfuzzerTraceConstCmp2 runtime.libfuzzerTraceConstCmp2
//go:linkname libfuzzerTraceConstCmp4 runtime.libfuzzerTraceConstCmp4
//go:linkname libfuzzerTraceConstCmp8 runtime.libfuzzerTraceConstCmp8
func libfuzzerTraceCmp1(arg0, arg1 uint8) {}
func libfuzzerTraceCmp2(arg0, arg1 uint16) {}
func libfuzzerTraceCmp4(arg0, arg1 uint32) {}
func libfuzzerTraceCmp8(arg0, arg1 uint64) {}
func libfuzzerTraceConstCmp1(arg0, arg1 uint8) {}
func libfuzzerTraceConstCmp2(arg0, arg1 uint16) {}
func libfuzzerTraceConstCmp4(arg0, arg1 uint32) {}
func libfuzzerTraceConstCmp8(arg0, arg1 uint64) {}