Skip to content

Dev.libfuzzer #1

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/cmd/compile/internal/gc/obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@ func ggloblnod(nam *ir.Name) {
flags |= obj.NOPTR
}
base.Ctxt.Globl(s, nam.Type().Size(), flags)
if nam.LibfuzzerExtraCounter() {
s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
if nam.Libfuzzer8BitCounter() {
s.Type = objabi.SLIBFUZZER_8BIT_COUNTER
}
if nam.Sym().Linkname != "" {
// Make sure linkname'd symbol is non-package. When a symbol is
Expand Down
6 changes: 3 additions & 3 deletions src/cmd/compile/internal/ir/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ const (
nameInlFormal // PAUTO created by inliner, derived from callee formal
nameInlLocal // PAUTO created by inliner, derived from callee local
nameOpenDeferSlot // if temporary var storing info for open-coded defers
nameLibfuzzerExtraCounter // if PEXTERN should be assigned to __libfuzzer_extra_counters section
nameLibfuzzer8BitCounter // if PEXTERN should be assigned to __sancov_cntrs section
nameAlias // is type name an alias
)

Expand All @@ -281,7 +281,7 @@ func (n *Name) Addrtaken() bool { return n.flags&nameAddrtaken !=
func (n *Name) InlFormal() bool { return n.flags&nameInlFormal != 0 }
func (n *Name) InlLocal() bool { return n.flags&nameInlLocal != 0 }
func (n *Name) OpenDeferSlot() bool { return n.flags&nameOpenDeferSlot != 0 }
func (n *Name) LibfuzzerExtraCounter() bool { return n.flags&nameLibfuzzerExtraCounter != 0 }
func (n *Name) Libfuzzer8BitCounter() bool { return n.flags&nameLibfuzzer8BitCounter != 0 }

func (n *Name) setReadonly(b bool) { n.flags.set(nameReadonly, b) }
func (n *Name) SetNeedzero(b bool) { n.flags.set(nameNeedzero, b) }
Expand All @@ -294,7 +294,7 @@ func (n *Name) SetAddrtaken(b bool) { n.flags.set(nameAddrtaken,
func (n *Name) SetInlFormal(b bool) { n.flags.set(nameInlFormal, b) }
func (n *Name) SetInlLocal(b bool) { n.flags.set(nameInlLocal, b) }
func (n *Name) SetOpenDeferSlot(b bool) { n.flags.set(nameOpenDeferSlot, b) }
func (n *Name) SetLibfuzzerExtraCounter(b bool) { n.flags.set(nameLibfuzzerExtraCounter, b) }
func (n *Name) SetLibfuzzer8BitCounter(b bool) { n.flags.set(nameLibfuzzer8BitCounter, b) }

// OnStack reports whether variable n may reside on the stack.
func (n *Name) OnStack() bool {
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/ssa/writebarrier.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ func IsSanitizerSafeAddr(v *Value) bool {
// read-only once initialized.
return true
case OpAddr:
return v.Aux.(*obj.LSym).Type == objabi.SRODATA || v.Aux.(*obj.LSym).Type == objabi.SLIBFUZZER_EXTRA_COUNTER
return v.Aux.(*obj.LSym).Type == objabi.SRODATA || v.Aux.(*obj.LSym).Type == objabi.SLIBFUZZER_8BIT_COUNTER
}
return false
}
Expand Down
18 changes: 13 additions & 5 deletions src/cmd/compile/internal/typecheck/builtin.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 14 additions & 8 deletions src/cmd/compile/internal/typecheck/builtin/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,20 @@ func asanwrite(addr, size uintptr)
func checkptrAlignment(unsafe.Pointer, *byte, uintptr)
func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer)

func libfuzzerTraceCmp1(uint8, uint8)
func libfuzzerTraceCmp2(uint16, uint16)
func libfuzzerTraceCmp4(uint32, uint32)
func libfuzzerTraceCmp8(uint64, uint64)
func libfuzzerTraceConstCmp1(uint8, uint8)
func libfuzzerTraceConstCmp2(uint16, uint16)
func libfuzzerTraceConstCmp4(uint32, uint32)
func libfuzzerTraceConstCmp8(uint64, uint64)
func libfuzzerTraceCmp1(uint8, uint8, int)
func libfuzzerTraceCmp2(uint16, uint16, int)
func libfuzzerTraceCmp4(uint32, uint32, int)
func libfuzzerTraceCmp8(uint64, uint64, int)
func libfuzzerTraceConstCmp1(uint8, uint8, int)
func libfuzzerTraceConstCmp2(uint16, uint16, int)
func libfuzzerTraceConstCmp4(uint32, uint32, int)
func libfuzzerTraceConstCmp8(uint64, uint64, int)
func libfuzzerHookStrCmp(string, string, bool, int)
func libfuzzerHookEqualFold(string, string, int)
func libfuzzerIncrementCounter(*uint8)

// This function should be called by the fuzz target on start to register the 8bit counters with libfuzzer
func LibfuzzerInitializeCounters()

// architecture variants
var x86HasPOPCNT bool
Expand Down
38 changes: 33 additions & 5 deletions src/cmd/compile/internal/walk/compare.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,35 @@
package walk

import (
"go/constant"

"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/reflectdata"
"cmd/compile/internal/ssagen"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"encoding/binary"
"fmt"
"go/constant"
"hash/fnv"
"io"
)

func fakePC(n ir.Node) ir.Node {
// In order to get deterministic IDs, we include the package path, file index, line number, column number
// in the calculation of the fakePC for the IR node.
hash := fnv.New32()
// We ignore the errors here because the `io.Writer` in the `hash.Hash` interface never returns an error.
_, _ = io.WriteString(hash, base.Ctxt.Pkgpath)
_ = binary.Write(hash, binary.LittleEndian, n.Pos().FileIndex())
_ = binary.Write(hash, binary.LittleEndian, int64(n.Pos().Line()))
_ = binary.Write(hash, binary.LittleEndian, int64(n.Pos().Col()))
// We also include the string representation of the node to distinguish autogenerated expression since
// those get the same `src.XPos`
_, _ = io.WriteString(hash, fmt.Sprintf("%v", n))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These nodes seem to have AutoTmp() set and you may be able to get the name from .Name() directly. We should try not to rely on "%v".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nodes such as ir.BinaryExpr do not have a name and we would need to traverse the children to get the names. What would be the disadvantages of relying on "%v"

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, it's probably better than the alternatives then. Do we still have to hash in the other fields or are they also part of the string representation?


return ir.NewInt(int64(hash.Sum32()))
}

// The result of walkCompare MUST be assigned back to n, e.g.
// n.Left = walkCompare(n.Left, init)
func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
Expand Down Expand Up @@ -131,7 +150,8 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
default:
base.Fatalf("unexpected integer size %d for %v", t.Size(), t)
}
init.Append(mkcall(fn, nil, init, tracecmpArg(l, paramType, init), tracecmpArg(r, paramType, init)))

init.Append(mkcall(fn, nil, init, tracecmpArg(l, paramType, init), tracecmpArg(r, paramType, init), fakePC(n)))
}
return n
case types.TARRAY:
Expand Down Expand Up @@ -399,8 +419,16 @@ func walkCompareString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
r = mkcall("cmpstring", types.Types[types.TINT], init, typecheck.Conv(n.X, types.Types[types.TSTRING]), typecheck.Conv(n.Y, types.Types[types.TSTRING]))
r = ir.NewBinaryExpr(base.Pos, n.Op(), r, ir.NewInt(0))
}

return finishCompare(n, r, init)
result := finishCompare(n, r, init)
if base.Debug.Libfuzzer != 0 {
fn := "libfuzzerHookStrCmp"
x := cheapExpr(n.X, init)
y := cheapExpr(n.Y, init)
z := cheapExpr(result, init)
paramType := types.Types[types.TSTRING]
init.Append(mkcall(fn, nil, init, tracecmpArg(x, paramType, init), tracecmpArg(y, paramType, init), tracecmpArg(z, n.Type(), init), fakePC(n)))
}
return result
}

// The result of finishCompare MUST be assigned back to n, e.g.
Expand Down
21 changes: 21 additions & 0 deletions src/cmd/compile/internal/walk/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,16 @@ func walkAddString(n *ir.AddStringExpr, init *ir.Nodes) ir.Node {
return r1
}

type hookInfo struct {
paramType types.Kind
argsNum int
runtimeFunc string
}

var hooks = map[string]hookInfo{
"strings.EqualFold": {paramType: types.TSTRING, argsNum: 2, runtimeFunc: "libfuzzerHookEqualFold"},
}

// walkCall walks an OCALLFUNC or OCALLINTER node.
func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
if n.Op() == ir.OCALLMETH {
Expand Down Expand Up @@ -590,6 +600,17 @@ func walkCall1(n *ir.CallExpr, init *ir.Nodes) {
}

n.Args = args
funSym := n.X.Sym()
if base.Debug.Libfuzzer != 0 && funSym != nil {
if hook, found := hooks[funSym.Pkg.Path+"."+funSym.Name]; found && len(args) == hook.argsNum {
var hookArgs []ir.Node
for _, arg := range args {
hookArgs = append(hookArgs, tracecmpArg(arg, types.Types[hook.paramType], init))
}
hookArgs = append(hookArgs, fakePC(n))
init.Append(mkcall(hook.runtimeFunc, nil, init, hookArgs...))
}
}
}

// walkDivMod walks an ODIV or OMOD node.
Expand Down
23 changes: 12 additions & 11 deletions src/cmd/compile/internal/walk/order.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,21 +440,22 @@ func (o *orderState) edge() {
return
}

// Create a new uint8 counter to be allocated in section
// __libfuzzer_extra_counters.
// Create a new uint8 counter to be allocated in section __sancov_cntrs
counter := staticinit.StaticName(types.Types[types.TUINT8])
counter.SetLibfuzzerExtraCounter(true)
// As well as setting SetLibfuzzerExtraCounter, we preemptively set the
// symbol type to SLIBFUZZER_EXTRA_COUNTER so that the race detector
counter.SetLibfuzzer8BitCounter(true)
// As well as setting SetLibfuzzer8BitCounter, we preemptively set the
// symbol type to SLIBFUZZER_8BIT_COUNTER so that the race detector
// instrumentation pass (which does not have access to the flags set by
// SetLibfuzzerExtraCounter) knows to ignore them. This information is
// lost by the time it reaches the compile step, so SetLibfuzzerExtraCounter
// SetLibfuzzer8BitCounter) knows to ignore them. This information is
// lost by the time it reaches the compile step, so SetLibfuzzer8BitCounter
// is still necessary.
counter.Linksym().Type = objabi.SLIBFUZZER_EXTRA_COUNTER
counter.Linksym().Type = objabi.SLIBFUZZER_8BIT_COUNTER

// counter += 1
incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))
o.append(incr)
var init ir.Nodes
init.Append(mkcall("libfuzzerIncrementCounter", nil, &init, ir.NewAddrExpr(base.Pos, counter)))
for _, n := range init.Take() {
o.append(n)
}
}

// orderBlock orders the block of statements in n into a new slice,
Expand Down
2 changes: 2 additions & 0 deletions src/cmd/internal/goobj/builtinlist.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/cmd/internal/objabi/symkind.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const (
SDWARFLOC
SDWARFLINES
// Coverage instrumentation counter for libfuzzer.
SLIBFUZZER_EXTRA_COUNTER
SLIBFUZZER_8BIT_COUNTER
// Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values.

)
8 changes: 4 additions & 4 deletions src/cmd/internal/objabi/symkind_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions src/cmd/link/internal/ld/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -1780,10 +1780,10 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.end", 0), sect)

// Coverage instrumentation counters for libfuzzer.
if len(state.data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 {
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.SLIBFUZZER_8BIT_COUNTER]) > 0 {
sect := state.allocateNamedSectionAndAssignSyms(&Segdata, "__sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, sym.Sxxx, 06)
ldr.SetSymSect(ldr.LookupOrCreateSym("__start___sancov_cntrs", 0), sect)
ldr.SetSymSect(ldr.LookupOrCreateSym("__stop___sancov_cntrs", 0), sect)
}

if len(state.data[sym.STLSBSS]) > 0 {
Expand Down Expand Up @@ -2561,7 +2561,7 @@ func (ctxt *Link) address() []*sym.Segment {
bss = s
case ".noptrbss":
noptrbss = s
case "__libfuzzer_extra_counters":
case "__sancov_cntrs":
fuzzCounters = s
}
}
Expand Down Expand Up @@ -2680,8 +2680,8 @@ func (ctxt *Link) address() []*sym.Segment {
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))
ctxt.xdefine("__start___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr))
ctxt.xdefine("__stop___sancov_cntrs", sym.SLIBFUZZER_8BIT_COUNTER, int64(fuzzCounters.Vaddr+fuzzCounters.Length))
}

if ctxt.IsSolaris() {
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/link/internal/ld/elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,7 @@ func (ctxt *Link) doelf() {
shstrtab.Addstring(".data")
shstrtab.Addstring(".bss")
shstrtab.Addstring(".noptrbss")
shstrtab.Addstring("__libfuzzer_extra_counters")
shstrtab.Addstring("__sancov_cntrs")
shstrtab.Addstring(".go.buildinfo")
if ctxt.IsMIPS() {
shstrtab.Addstring(".MIPS.abiflags")
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/link/internal/ld/xcoff.go
Original file line number Diff line number Diff line change
Expand Up @@ -1117,7 +1117,7 @@ func (f *xcoffFile) asmaixsym(ctxt *Link) {
putaixsym(ctxt, s, TLSSym)
}

case st == sym.SBSS, st == sym.SNOPTRBSS, st == sym.SLIBFUZZER_EXTRA_COUNTER:
case st == sym.SBSS, st == sym.SNOPTRBSS, st == sym.SLIBFUZZER_8BIT_COUNTER:
if ldr.AttrReachable(s) {
data := ldr.Data(s)
if len(data) > 0 {
Expand Down
38 changes: 19 additions & 19 deletions src/cmd/link/internal/sym/symkind.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const (
SXCOFFTOC
SBSS
SNOPTRBSS
SLIBFUZZER_EXTRA_COUNTER
SLIBFUZZER_8BIT_COUNTER
STLSBSS
SXREF
SMACHOSYMSTR
Expand Down Expand Up @@ -125,24 +125,24 @@ const (
// AbiSymKindToSymKind maps values read from object files (which are
// of type cmd/internal/objabi.SymKind) to values of type SymKind.
var AbiSymKindToSymKind = [...]SymKind{
objabi.Sxxx: Sxxx,
objabi.STEXT: STEXT,
objabi.SRODATA: SRODATA,
objabi.SNOPTRDATA: SNOPTRDATA,
objabi.SDATA: SDATA,
objabi.SBSS: SBSS,
objabi.SNOPTRBSS: SNOPTRBSS,
objabi.STLSBSS: STLSBSS,
objabi.SDWARFCUINFO: SDWARFCUINFO,
objabi.SDWARFCONST: SDWARFCONST,
objabi.SDWARFFCN: SDWARFFCN,
objabi.SDWARFABSFCN: SDWARFABSFCN,
objabi.SDWARFTYPE: SDWARFTYPE,
objabi.SDWARFVAR: SDWARFVAR,
objabi.SDWARFRANGE: SDWARFRANGE,
objabi.SDWARFLOC: SDWARFLOC,
objabi.SDWARFLINES: SDWARFLINES,
objabi.SLIBFUZZER_EXTRA_COUNTER: SLIBFUZZER_EXTRA_COUNTER,
objabi.Sxxx: Sxxx,
objabi.STEXT: STEXT,
objabi.SRODATA: SRODATA,
objabi.SNOPTRDATA: SNOPTRDATA,
objabi.SDATA: SDATA,
objabi.SBSS: SBSS,
objabi.SNOPTRBSS: SNOPTRBSS,
objabi.STLSBSS: STLSBSS,
objabi.SDWARFCUINFO: SDWARFCUINFO,
objabi.SDWARFCONST: SDWARFCONST,
objabi.SDWARFFCN: SDWARFFCN,
objabi.SDWARFABSFCN: SDWARFABSFCN,
objabi.SDWARFTYPE: SDWARFTYPE,
objabi.SDWARFVAR: SDWARFVAR,
objabi.SDWARFRANGE: SDWARFRANGE,
objabi.SDWARFLOC: SDWARFLOC,
objabi.SDWARFLINES: SDWARFLINES,
objabi.SLIBFUZZER_8BIT_COUNTER: SLIBFUZZER_8BIT_COUNTER,
}

// ReadOnly are the symbol kinds that form read-only sections. In some
Expand Down
Loading