]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile, cmd/link: add coverage instrumentation for libfuzzer
authorMatthew Dempsky <mdempsky@google.com>
Sat, 19 Oct 2019 00:39:39 +0000 (17:39 -0700)
committerMatthew Dempsky <mdempsky@google.com>
Tue, 5 Nov 2019 00:00:36 +0000 (00:00 +0000)
This CL adds experimental coverage instrumentation similar to what
github.com/dvyukov/go-fuzz produces in its -libfuzzer mode. The
coverage can be enabled by compiling with -d=libfuzzer. It's intended
to be used in conjunction with -buildmode=c-archive to produce an ELF
archive (.a) file that can be linked with libFuzzer. See #14565 for
example usage.

The coverage generates a unique 8-bit counter for each basic block in
the original source code, and emits an increment operation. These
counters are then collected into the __libfuzzer_extra_counters ELF
section for use by libFuzzer.

Updates #14565.

Change-Id: I239758cc0ceb9ca1220f2d9d3d23b9e761db9bf1
Reviewed-on: https://go-review.googlesource.com/c/go/+/202117
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/compile/internal/gc/gsubr.go
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/gc/order.go
src/cmd/compile/internal/gc/syntax.go
src/cmd/internal/objabi/symkind.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/elf.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/sym/symkind.go
src/cmd/link/internal/sym/symkind_string.go

index 2894d8d014be99ad56408ad3200ef9e7b8b00987..e0c43551782624f6f56101b26fe8d917a02a7154 100644 (file)
@@ -297,6 +297,9 @@ func ggloblnod(nam *Node) {
                flags |= obj.NOPTR
        }
        Ctxt.Globl(s, nam.Type.Width, flags)
+       if nam.Name.LibfuzzerExtraCounter() {
+               s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER
+       }
 }
 
 func ggloblsym(s *obj.LSym, width int32, flags int16) {
index ab616d4c9bec6f081163d54d22c08017aab3356d..2d427be5393f71418315f7ee7247758c3654d636 100644 (file)
@@ -44,6 +44,7 @@ var (
        Debug_closure      int
        Debug_compilelater int
        debug_dclstack     int
+       Debug_libfuzzer    int
        Debug_panic        int
        Debug_slice        int
        Debug_vlog         bool
@@ -73,6 +74,7 @@ var debugtab = []struct {
        {"disablenil", "disable nil checks", &disable_checknil},
        {"dclstack", "run internal dclstack check", &debug_dclstack},
        {"gcprog", "print dump of GC programs", &Debug_gcprog},
+       {"libfuzzer", "coverage instrumentation for libfuzzer", &Debug_libfuzzer},
        {"nil", "print information about nil checks", &Debug_checknil},
        {"panic", "do not hide any compiler panic", &Debug_panic},
        {"slice", "print information about slice compilation", &Debug_slice},
@@ -447,9 +449,12 @@ func Main(archInit func(*Arch)) {
                }
        }
 
-       // Runtime can't use -d=checkptr, at least not yet.
        if compiling_runtime {
+               // Runtime can't use -d=checkptr, at least not yet.
                Debug_checkptr = 0
+
+               // Fuzzing the runtime isn't interesting either.
+               Debug_libfuzzer = 0
        }
 
        // set via a -d flag
index 6822be413783e82d5745a53ffbdccb2c4b02981d..90d7baa602e0b8f3759e35286995319d11246ca2 100644 (file)
@@ -324,6 +324,25 @@ func (o *Order) stmtList(l Nodes) {
        }
 }
 
+// edge inserts coverage instrumentation for libfuzzer.
+func (o *Order) edge() {
+       if Debug_libfuzzer == 0 {
+               return
+       }
+
+       // Create a new uint8 counter to be allocated in section
+       // __libfuzzer_extra_counters.
+       counter := staticname(types.Types[TUINT8])
+       counter.Name.SetLibfuzzerExtraCounter(true)
+
+       // counter += 1
+       incr := nod(OASOP, counter, nodintconst(1))
+       incr.SetSubOp(OADD)
+       incr = typecheck(incr, ctxStmt)
+
+       o.out = append(o.out, incr)
+}
+
 // orderBlock orders the block of statements in n into a new slice,
 // and then replaces the old slice in n with the new slice.
 // free is a map that can be used to obtain temporary variables by type.
@@ -331,6 +350,7 @@ func orderBlock(n *Nodes, free map[string][]*Node) {
        var order Order
        order.free = free
        mark := order.markTemp()
+       order.edge()
        order.stmtList(*n)
        order.cleanTemp(mark)
        n.Set(order.out)
@@ -917,6 +937,11 @@ func (o *Order) stmt(n *Node) {
        // For now just clean all the temporaries at the end.
        // In practice that's fine.
        case OSWITCH:
+               if Debug_libfuzzer != 0 && !hasDefaultCase(n) {
+                       // Add empty "default:" case for instrumentation.
+                       n.List.Append(nod(OCASE, nil, nil))
+               }
+
                t := o.markTemp()
                n.Left = o.expr(n.Left, nil)
                for _, ncas := range n.List.Slice() {
@@ -934,6 +959,18 @@ func (o *Order) stmt(n *Node) {
        lineno = lno
 }
 
+func hasDefaultCase(n *Node) bool {
+       for _, ncas := range n.List.Slice() {
+               if ncas.Op != OCASE {
+                       Fatalf("expected case, found %v", ncas.Op)
+               }
+               if ncas.List.Len() == 0 {
+                       return true
+               }
+       }
+       return false
+}
+
 // exprList orders the expression list l into o.
 func (o *Order) exprList(l Nodes) {
        s := l.Slice()
@@ -1083,6 +1120,7 @@ func (o *Order) expr(n, lhs *Node) *Node {
                saveout := o.out
                o.out = nil
                t := o.markTemp()
+               o.edge()
                rhs := o.expr(n.Right, nil)
                o.out = append(o.out, typecheck(nod(OAS, r, rhs), ctxStmt))
                o.cleanTemp(t)
index 3f270addd6a0fe7c17ee985aaf1aaff3ed3848d1..57820f3810ba137a4a0d6bf3c34af149ff82edb9 100644 (file)
@@ -293,9 +293,10 @@ const (
        nameIsOutputParamHeapAddr // pointer to a result parameter's heap copy
        nameAssigned              // is the variable ever assigned to
        nameAddrtaken             // address taken, even if not moved to heap
-       nameInlFormal             // OPAUTO created by inliner, derived from callee formal
-       nameInlLocal              // OPAUTO created by inliner, derived from callee local
+       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
 )
 
 func (n *Name) Captured() bool              { return n.flags&nameCaptured != 0 }
@@ -312,6 +313,7 @@ func (n *Name) Addrtaken() bool             { return n.flags&nameAddrtaken != 0
 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) SetCaptured(b bool)              { n.flags.set(nameCaptured, b) }
 func (n *Name) SetReadonly(b bool)              { n.flags.set(nameReadonly, b) }
@@ -327,6 +329,7 @@ func (n *Name) SetAddrtaken(b bool)             { n.flags.set(nameAddrtaken, b)
 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) }
 
 type Param struct {
        Ntype    *Node
index f709c367cafdc87fd537cc0c28dfb5bb28af389e..69f15286cde67384601556648966479f7d514d20 100644 (file)
@@ -67,6 +67,8 @@ const (
        // TODO(austin): Remove this and all uses once the compiler
        // generates real ABI wrappers rather than symbol aliases.
        SABIALIAS
+       // Coverage instrumentation counter for libfuzzer.
+       SLIBFUZZER_EXTRA_COUNTER
        // Update cmd/link/internal/sym/AbiSymKindToSymKind for new SymKind values.
 
 )
index 89485b8be43932991d985a53caaeae575c92509e..f4aa78f45cb7562a9bf22c2244860091a4f807ca 100644 (file)
@@ -1467,11 +1467,26 @@ func (ctxt *Link) dodata() {
                s.Value = int64(uint64(datsize) - sect.Vaddr)
                datsize += s.Size
        }
-
        sect.Length = uint64(datsize) - sect.Vaddr
        ctxt.Syms.Lookup("runtime.end", 0).Sect = sect
        checkdatsize(ctxt, datsize, sym.SNOPTRBSS)
 
+       // Coverage instrumentation counters for libfuzzer.
+       if len(data[sym.SLIBFUZZER_EXTRA_COUNTER]) > 0 {
+               sect := addsection(ctxt.Arch, &Segdata, "__libfuzzer_extra_counters", 06)
+               sect.Align = dataMaxAlign[sym.SLIBFUZZER_EXTRA_COUNTER]
+               datsize = Rnd(datsize, int64(sect.Align))
+               sect.Vaddr = uint64(datsize)
+               for _, s := range data[sym.SLIBFUZZER_EXTRA_COUNTER] {
+                       datsize = aligndatsize(datsize, s)
+                       s.Sect = sect
+                       s.Value = int64(uint64(datsize) - sect.Vaddr)
+                       datsize += s.Size
+               }
+               sect.Length = uint64(datsize) - sect.Vaddr
+               checkdatsize(ctxt, datsize, sym.SLIBFUZZER_EXTRA_COUNTER)
+       }
+
        if len(data[sym.STLSBSS]) > 0 {
                var sect *sym.Section
                if (ctxt.IsELF || ctxt.HeadType == objabi.Haix) && (ctxt.LinkMode == LinkExternal || !*FlagD) {
index 5fc20f37a3c83b87b2a0b941a26ab289bf9619f5..91198efd279e8511446147074b981e0150fd7628 100644 (file)
@@ -1439,6 +1439,7 @@ func (ctxt *Link) doelf() {
        Addstring(shstrtab, ".data")
        Addstring(shstrtab, ".bss")
        Addstring(shstrtab, ".noptrbss")
+       Addstring(shstrtab, "__libfuzzer_extra_counters")
        Addstring(shstrtab, ".go.buildinfo")
 
        // generate .tbss section for dynamic internal linker or external
index 7f4d6412c7333ed459c266614cb69ea2602ad089..63987bb14a4b9edc93cab8accc8dbf294855e2a3 100644 (file)
@@ -2375,7 +2375,7 @@ func genasmsym(ctxt *Link, put func(*Link, *sym.Symbol, string, SymbolType, int6
                        }
                        put(ctxt, s, s.Name, DataSym, Symaddr(s), s.Gotype)
 
-               case sym.SBSS, sym.SNOPTRBSS:
+               case sym.SBSS, sym.SNOPTRBSS, sym.SLIBFUZZER_EXTRA_COUNTER:
                        if !s.Attr.Reachable() {
                                continue
                        }
index 5309e07ecff21940f9cc37b55eef4abc67863ec6..95311be0ccf08b35fea8fd6f77f25eb6b244b9d4 100644 (file)
@@ -94,6 +94,7 @@ const (
        SXCOFFTOC
        SBSS
        SNOPTRBSS
+       SLIBFUZZER_EXTRA_COUNTER
        STLSBSS
        SXREF
        SMACHOSYMSTR
@@ -133,6 +134,7 @@ var AbiSymKindToSymKind = [...]SymKind{
        SDWARFLOC,
        SDWARFLINES,
        SABIALIAS,
+       SLIBFUZZER_EXTRA_COUNTER,
 }
 
 // ReadOnly are the symbol kinds that form read-only sections. In some
index e48d90c511b26756dff82891690c4d42ccd350c5..97af9925d57583d523f3bd367342cc93b8aff8e1 100644 (file)
@@ -44,28 +44,29 @@ func _() {
        _ = x[SXCOFFTOC-33]
        _ = x[SBSS-34]
        _ = x[SNOPTRBSS-35]
-       _ = x[STLSBSS-36]
-       _ = x[SXREF-37]
-       _ = x[SMACHOSYMSTR-38]
-       _ = x[SMACHOSYMTAB-39]
-       _ = x[SMACHOINDIRECTPLT-40]
-       _ = x[SMACHOINDIRECTGOT-41]
-       _ = x[SFILEPATH-42]
-       _ = x[SCONST-43]
-       _ = x[SDYNIMPORT-44]
-       _ = x[SHOSTOBJ-45]
-       _ = x[SUNDEFEXT-46]
-       _ = x[SDWARFSECT-47]
-       _ = x[SDWARFINFO-48]
-       _ = x[SDWARFRANGE-49]
-       _ = x[SDWARFLOC-50]
-       _ = x[SDWARFLINES-51]
-       _ = x[SABIALIAS-52]
+       _ = x[SLIBFUZZER_EXTRA_COUNTER-36]
+       _ = x[STLSBSS-37]
+       _ = x[SXREF-38]
+       _ = x[SMACHOSYMSTR-39]
+       _ = x[SMACHOSYMTAB-40]
+       _ = x[SMACHOINDIRECTPLT-41]
+       _ = x[SMACHOINDIRECTGOT-42]
+       _ = x[SFILEPATH-43]
+       _ = x[SCONST-44]
+       _ = x[SDYNIMPORT-45]
+       _ = x[SHOSTOBJ-46]
+       _ = x[SUNDEFEXT-47]
+       _ = x[SDWARFSECT-48]
+       _ = x[SDWARFINFO-49]
+       _ = x[SDWARFRANGE-50]
+       _ = x[SDWARFLOC-51]
+       _ = x[SDWARFLINES-52]
+       _ = x[SABIALIAS-53]
 }
 
-const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSCONSTSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOCSDWARFLINESSABIALIAS"
+const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSFirstWritableSBUILDINFOSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASXCOFFTOCSBSSSNOPTRBSSSLIBFUZZER_EXTRA_COUNTERSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILEPATHSCONSTSDYNIMPORTSHOSTOBJSUNDEFEXTSDWARFSECTSDWARFINFOSDWARFRANGESDWARFLOCSDWARFLINESSABIALIAS"
 
-var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 320, 325, 337, 349, 366, 383, 392, 398, 408, 416, 425, 435, 445, 456, 465, 476, 485}
+var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 220, 230, 238, 244, 253, 261, 268, 278, 286, 291, 300, 304, 313, 337, 344, 349, 361, 373, 390, 407, 416, 422, 432, 440, 449, 459, 469, 480, 489, 500, 509}
 
 func (i SymKind) String() string {
        if i >= SymKind(len(_SymKind_index)-1) {