aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2020-07-04 12:41:40 +0200
committerDmitry Vyukov <dvyukov@google.com>2020-07-04 15:05:30 +0200
commit74cb4e09a50b0f8cc45fce9ac072d1079eb03b42 (patch)
treeaf9ed84de62047053e941c5e08d979fa200535b0 /vendor/github.com
parentc7d7f10bdff703e4a3c0414e8a33d4e45c91eb35 (diff)
go.mod: add go-fuzz-build
Otherwise go-fuzz-build now fails with: + go-fuzz-build -libfuzzer -func FuzzDeserialize -o fuzzer.a ./prog/test -: cannot find package "." in: /syzkaller/gopath/src/github.com/google/syzkaller/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/LICENSE174
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-build/cover.go906
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-build/main.go882
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-defs/defs.go36
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/cover.go22
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/doc.go10
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/main.go99
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/main_libFuzzer.go20
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sonar.go201
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sys_posix.go32
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sys_windows.go56
-rw-r--r--vendor/github.com/dvyukov/go-fuzz/internal/go-fuzz-types/types.go28
12 files changed, 2466 insertions, 0 deletions
diff --git a/vendor/github.com/dvyukov/go-fuzz/LICENSE b/vendor/github.com/dvyukov/go-fuzz/LICENSE
new file mode 100644
index 000000000..dd5b3a58a
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/LICENSE
@@ -0,0 +1,174 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-build/cover.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-build/cover.go
new file mode 100644
index 000000000..5c5d83efb
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-build/cover.go
@@ -0,0 +1,906 @@
+// Copyright 2015 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.
+
+package main
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/printer"
+ "go/token"
+ "go/types"
+ "io"
+ "strconv"
+ "strings"
+
+ . "github.com/dvyukov/go-fuzz/go-fuzz-defs"
+ . "github.com/dvyukov/go-fuzz/internal/go-fuzz-types"
+)
+
+const fuzzdepPkg = "_go_fuzz_dep_"
+
+func instrument(pkg, fullName string, fset *token.FileSet, parsedFile *ast.File, info *types.Info, out io.Writer, blocks *[]CoverBlock, sonar *[]CoverBlock) {
+ file := &File{
+ fset: fset,
+ pkg: pkg,
+ fullName: fullName,
+ astFile: parsedFile,
+ blocks: blocks,
+ info: info,
+ }
+ if sonar == nil {
+ file.addImport("go-fuzz-dep", fuzzdepPkg, "CoverTab")
+ ast.Walk(file, file.astFile)
+ } else {
+ s := &Sonar{
+ fset: fset,
+ fullName: fullName,
+ pkg: pkg,
+ blocks: sonar,
+ info: info,
+ }
+ ast.Walk(s, file.astFile)
+ }
+
+ file.print(out)
+}
+
+type Sonar struct {
+ fset *token.FileSet
+ fullName string
+ pkg string
+ blocks *[]CoverBlock
+ info *types.Info
+}
+
+var sonarSeq = 0
+
+func (s *Sonar) Visit(n ast.Node) ast.Visitor {
+ // TODO: detect "x&mask==0", emit sonar(x, x&^mask)
+ switch nn := n.(type) {
+ case *ast.BinaryExpr:
+ break
+
+ case *ast.GenDecl:
+ if nn.Tok != token.VAR {
+ return nil // constants and types are not interesting
+ }
+ return s
+
+ case *ast.SelectorExpr:
+ return nil
+
+ case *ast.SwitchStmt:
+ if nn.Tag == nil || nn.Body == nil {
+ return s // recurse
+ }
+ // Replace:
+ // switch a := foo(); bar(a) {
+ // case x: ...
+ // case y: ...
+ // }
+ // with:
+ // switch {
+ // default:
+ // a := foo()
+ // __tmp := bar(a)
+ // switch {
+ // case __tmp == x: ...
+ // case __tmp == y: ...
+ // }
+ // }
+ // The == comparisons will be instrumented later when we recurse.
+ sw := new(ast.SwitchStmt)
+ *sw = *nn
+ var stmts []ast.Stmt
+ if sw.Init != nil {
+ stmts = append(stmts, sw.Init)
+ sw.Init = nil
+ }
+ const tmpvar = "__go_fuzz_tmp"
+ tmp := ast.NewIdent(tmpvar)
+ typ := s.info.Types[sw.Tag]
+ s.info.Types[tmp] = typ
+ stmts = append(stmts, &ast.AssignStmt{Lhs: []ast.Expr{tmp}, Tok: token.DEFINE, Rhs: []ast.Expr{sw.Tag}})
+ stmts = append(stmts, &ast.AssignStmt{Lhs: []ast.Expr{ast.NewIdent("_")}, Tok: token.ASSIGN, Rhs: []ast.Expr{tmp}})
+ sw.Tag = nil
+ stmts = append(stmts, sw)
+ for _, cas1 := range sw.Body.List {
+ cas := cas1.(*ast.CaseClause)
+ for i, expr := range cas.List {
+ tmp := &ast.Ident{Name: tmpvar, NamePos: expr.Pos()}
+ s.info.Types[tmp] = typ
+ cas.List[i] = &ast.BinaryExpr{X: tmp, Op: token.EQL, Y: expr}
+ }
+ }
+ nn.Tag = nil
+ nn.Init = nil
+ nn.Body = &ast.BlockStmt{List: []ast.Stmt{&ast.CaseClause{Body: stmts}}}
+ return s // recurse
+
+ case *ast.ForStmt:
+ // For condition is usually uninteresting, but produces lots of samples.
+ // So we skip it if it looks boring.
+ if nn.Init != nil {
+ ast.Walk(s, nn.Init)
+ }
+ if nn.Post != nil {
+ ast.Walk(s, nn.Post)
+ }
+ ast.Walk(s, nn.Body)
+ if nn.Cond != nil {
+ // Look for the following pattern:
+ // for foo := ...; foo ? ...; ... { ... }
+ boring := false
+ if nn.Init != nil {
+ if init, ok1 := nn.Init.(*ast.AssignStmt); ok1 && init.Tok == token.DEFINE && len(init.Lhs) == 1 {
+ if id, ok2 := init.Lhs[0].(*ast.Ident); ok2 {
+ if bex, ok3 := nn.Cond.(*ast.BinaryExpr); ok3 {
+ if x, ok4 := bex.X.(*ast.Ident); ok4 && x.Name == id.Name {
+ boring = true
+ }
+ if x, ok4 := bex.Y.(*ast.Ident); ok4 && x.Name == id.Name {
+ boring = true
+ }
+ }
+ }
+ }
+ }
+ if !boring {
+ ast.Walk(s, nn.Cond)
+ }
+ }
+ return nil
+
+ default:
+ return s // recurse
+ }
+
+ // TODO: handle map index expressions (especially useful for strings).
+ // E.g. when code matches a read in identifier against a set of known identifiers.
+ // For the record, it looks as follows. However, it is tricky to distinguish
+ // from slice/array index and map assignments...
+ //. . . . . . . *ast.IndexExpr {
+ //. . . . . . . . X: *ast.Ident {
+ //. . . . . . . . . Name: "m"
+ //. . . . . . . . }
+ //. . . . . . . . Index: *ast.Ident {
+ //. . . . . . . . . Name: "s"
+ //. . . . . . . . }
+ //. . . . . . . }
+
+ // TODO: transform expressions so that lhs expression contains a variable
+ // and rhs contains all constant operands. For example, for (real code from vp8 codec):
+ // cf := (b[0]>>4)&7 == 5
+ // we would like to transform it to:
+ // b[0] & (7<<4) == 5<<4
+ // and then to:
+ // b[0] == 5<<4 | b & ^(7<<4)
+ // and emit:
+ // Sonar(b[0], 5<<4 | b & ^(7<<4), SonarEQL)
+ // This will allow the fuzzer to figure out what bytes it needs to replace
+ // with what bytes in order to crack this condition.
+ // Similarly, for:
+ // x/3 == 100
+ // we would like to emit:
+ // Sonar(x, 100*3, SonarEQL)
+
+ // TODO: intercept strings.Index/HasPrefix and similar functions.
+
+ nn := n.(*ast.BinaryExpr)
+ var flags uint8
+ switch nn.Op {
+ case token.EQL:
+ flags = SonarEQL
+ break
+ case token.NEQ:
+ flags = SonarNEQ
+ break
+ case token.LSS:
+ flags = SonarLSS
+ break
+ case token.GTR:
+ flags = SonarGTR
+ break
+ case token.LEQ:
+ flags = SonarLEQ
+ break
+ case token.GEQ:
+ flags = SonarGEQ
+ break
+ default:
+ return s // recurse
+ }
+ // Replace:
+ // x != y
+ // with:
+ // func() _go_fuzz_dep_.Bool { v1 := x; v2 := y; go-fuzz-dep.Sonar(v1, v2, flags); return v1 != v2 }() == true
+ // Using "== true" lets us modify the AST Node in-place.
+ v1 := nn.X
+ v2 := nn.Y
+ ast.Walk(s, v1)
+ ast.Walk(s, v2)
+ if isCap(v1) || isCap(v2) {
+ // Haven't seen useful cases yet.
+ return s
+ }
+ if isLen(v1) || isLen(v2) {
+ // TODO: we could pass both length value and the len argument.
+ // For example, if the code is:
+ // name := ... // obtained from input
+ // if len(name) > 5 { ... }
+ // If we would have the name value at runtime, we will know
+ // what part of the input to alter to affect len result.
+ flags |= SonarLength
+ }
+
+ checkType := func(tv types.TypeAndValue) bool {
+ // Comparisons of pointers, maps, chans and bool are not interesting.
+ if _, ok := tv.Type.(*types.Pointer); ok {
+ return false
+ }
+ if _, ok := tv.Type.(*types.Map); ok {
+ return false
+ }
+ if _, ok := tv.Type.(*types.Chan); ok {
+ return false
+ }
+ s := tv.Type.Underlying().String()
+ if s == "bool" || s == "ideal bool" || s == "error" ||
+ s == "untyped nil" || s == "unsafe.Pointer" {
+ return false
+ }
+ return true
+ }
+ if !checkType(s.info.Types[v1]) || !checkType(s.info.Types[v2]) {
+ return nil
+ }
+ var tv types.TypeAndValue
+ if isConstExpr(s.info, v1) {
+ flags |= SonarConst1
+ } else {
+ tv = s.info.Types[v1]
+ }
+ if isConstExpr(s.info, v2) {
+ flags |= SonarConst2
+ } else {
+ tv = s.info.Types[v2]
+ }
+ if flags&SonarConst1 != 0 && flags&SonarConst2 != 0 {
+ return nil
+ }
+ id := int(flags) | sonarSeq<<8
+ startPos := s.fset.Position(nn.Pos())
+ endPos := s.fset.Position(nn.End())
+ *s.blocks = append(*s.blocks, CoverBlock{sonarSeq, s.fullName, startPos.Line, startPos.Column, endPos.Line, endPos.Column, int(flags)})
+ sonarSeq++
+ block := &ast.BlockStmt{}
+
+ typstr := tv.Type.String()
+ if strings.HasPrefix(typstr, s.pkg+".") {
+ typstr = typstr[len(s.pkg)+1:]
+ }
+ if idx := strings.LastIndexByte(typstr, '/'); idx != -1 {
+ typstr = typstr[idx+1:]
+ }
+ conv := func(name string, v ast.Expr) ast.Expr {
+ // Convert const to the type of the other expr.
+ isConst := isConstExpr(s.info, v)
+ badConst := false
+ if isConst {
+ c := s.info.Types[v].Value
+ switch c.Kind() {
+ case constant.Int:
+ if v, ok := constant.Int64Val(c); !ok || int64(int(v)) != v {
+ // Such const can't be used outside of its current context,
+ // because it will be converted to int and that will fail.
+ badConst = true
+ }
+ case constant.Float:
+ badConst = true
+ }
+ }
+ if badConst || isWeirdShift(s.info, v) {
+ v = &ast.CallExpr{
+ Fun: ast.NewIdent(typstr),
+ Args: []ast.Expr{v},
+ }
+ s.info.Types[v] = tv
+ }
+ if !isConst {
+ // Assign to a temp to avoid double side-effects.
+ tmp := ast.NewIdent(name)
+ block.List = append(block.List, &ast.AssignStmt{Tok: token.DEFINE, Lhs: []ast.Expr{tmp}, Rhs: []ast.Expr{v}})
+ v = tmp
+ s.info.Types[v] = tv
+ }
+ return v
+ }
+ v1 = conv("__gofuzz_v1", v1)
+ v2 = conv("__gofuzz_v2", v2)
+
+ block.List = append(block.List,
+ &ast.ExprStmt{
+ X: &ast.CallExpr{
+ Fun: &ast.SelectorExpr{X: ast.NewIdent(fuzzdepPkg), Sel: ast.NewIdent("Sonar")},
+ Args: []ast.Expr{v1, v2, &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(id)}},
+ },
+ },
+ &ast.ReturnStmt{Results: []ast.Expr{&ast.BinaryExpr{Op: nn.Op, X: v1, Y: v2}}},
+ )
+ nn.X = &ast.CallExpr{
+ Fun: &ast.FuncLit{
+ Type: &ast.FuncType{Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent("_go_fuzz_dep_.Bool")}}}},
+ Body: block,
+ },
+ }
+ nn.Y = &ast.BasicLit{Kind: token.INT, Value: "true"}
+ nn.Op = token.EQL
+ return nil
+}
+
+func isWeirdShift(info *types.Info, n ast.Expr) bool {
+ w := &WeirdShiftWalker{info: info}
+ ast.Walk(w, n)
+ return w.found
+}
+
+type WeirdShiftWalker struct {
+ info *types.Info
+ found bool
+}
+
+func (w *WeirdShiftWalker) Visit(n ast.Node) ast.Visitor {
+ if bin, ok := n.(*ast.BinaryExpr); ok && (bin.Op == token.SHL || bin.Op == token.SHR) && isConstExpr(w.info, bin.X) {
+ w.found = true
+ }
+ return w
+}
+
+func isConstExpr(info *types.Info, n ast.Expr) bool {
+ tv := info.Types[n]
+ if tv.Type == nil && tv.Value == nil {
+ panic(fmt.Sprintf("untyped expression: %#v", n))
+ }
+ return tv.Value != nil
+}
+
+func isCap(n ast.Expr) bool {
+ if call, ok := n.(*ast.CallExpr); ok {
+ if id, ok2 := call.Fun.(*ast.Ident); ok2 {
+ return id.Name == "cap"
+ }
+ }
+ return false
+}
+
+func isLen(n ast.Expr) bool {
+ if call, ok := n.(*ast.CallExpr); ok {
+ if id, ok2 := call.Fun.(*ast.Ident); ok2 {
+ return id.Name == "len"
+ }
+ }
+ return false
+}
+
+type LiteralCollector struct {
+ ctxt *Context
+ lits map[Literal]struct{}
+}
+
+func (lc *LiteralCollector) Visit(n ast.Node) (w ast.Visitor) {
+ switch nn := n.(type) {
+ default:
+ return lc // recurse
+ case *ast.ImportSpec:
+ return nil
+ case *ast.Field:
+ return nil // ignore field tags
+ case *ast.CallExpr:
+ switch fn := nn.Fun.(type) {
+ case *ast.Ident:
+ if fn.Name == "panic" {
+ return nil
+ }
+ case *ast.SelectorExpr:
+ if id, ok := fn.X.(*ast.Ident); ok && (id.Name == "fmt" || id.Name == "errors") {
+ return nil
+ }
+ }
+ return lc
+ case *ast.BasicLit:
+ lit := nn.Value
+ switch nn.Kind {
+ case token.STRING:
+ lc.lits[Literal{lc.unquote(lit), true}] = struct{}{}
+ case token.CHAR:
+ lc.lits[Literal{lc.unquote(lit), false}] = struct{}{}
+ case token.INT:
+ if lit[0] < '0' || lit[0] > '9' {
+ lc.ctxt.failf("unsupported literal '%v'", lit)
+ }
+ v, err := strconv.ParseInt(lit, 0, 64)
+ if err != nil {
+ u, err := strconv.ParseUint(lit, 0, 64)
+ if err != nil {
+ lc.ctxt.failf("failed to parse int literal '%v': %v", lit, err)
+ }
+ v = int64(u)
+ }
+ var val []byte
+ if v >= -(1<<7) && v < 1<<8 {
+ val = append(val, byte(v))
+ } else if v >= -(1<<15) && v < 1<<16 {
+ val = append(val, byte(v), byte(v>>8))
+ } else if v >= -(1<<31) && v < 1<<32 {
+ val = append(val, byte(v), byte(v>>8), byte(v>>16), byte(v>>24))
+ } else {
+ val = append(val, byte(v), byte(v>>8), byte(v>>16), byte(v>>24), byte(v>>32), byte(v>>40), byte(v>>48), byte(v>>56))
+ }
+ lc.lits[Literal{string(val), false}] = struct{}{}
+ }
+ return nil
+ }
+}
+
+func trimComments(file *ast.File, fset *token.FileSet) []*ast.CommentGroup {
+ var comments []*ast.CommentGroup
+ for _, group := range file.Comments {
+ var list []*ast.Comment
+ for _, comment := range group.List {
+ if strings.HasPrefix(comment.Text, "//go:") && fset.Position(comment.Slash).Column == 1 {
+ list = append(list, comment)
+ }
+ }
+ if list != nil {
+ comments = append(comments, &ast.CommentGroup{List: list})
+ }
+ }
+ return comments
+}
+
+func initialComments(content []byte) []byte {
+ // Derived from go/build.Context.shouldBuild.
+ end := 0
+ p := content
+ for len(p) > 0 {
+ line := p
+ if i := bytes.IndexByte(line, '\n'); i >= 0 {
+ line, p = line[:i], p[i+1:]
+ } else {
+ p = p[len(p):]
+ }
+ line = bytes.TrimSpace(line)
+ if len(line) == 0 { // Blank line.
+ end = len(content) - len(p)
+ continue
+ }
+ if !bytes.HasPrefix(line, slashslash) { // Not comment line.
+ break
+ }
+ }
+ return content[:end]
+}
+
+type File struct {
+ fset *token.FileSet
+ pkg string
+ fullName string
+ astFile *ast.File
+ blocks *[]CoverBlock
+ info *types.Info
+}
+
+var slashslash = []byte("//")
+
+func (f *File) Visit(node ast.Node) ast.Visitor {
+ switch n := node.(type) {
+ case *ast.FuncDecl:
+ if n.Name.String() == "init" {
+ // Don't instrument init functions.
+ // They run regardless of what we do, so it is just noise.
+ return nil
+ }
+ case *ast.GenDecl:
+ if n.Tok != token.VAR {
+ return nil // constants and types are not interesting
+ }
+
+ case *ast.BlockStmt:
+ // If it's a switch or select, the body is a list of case clauses; don't tag the block itself.
+ if len(n.List) > 0 {
+ switch n.List[0].(type) {
+ case *ast.CaseClause: // switch
+ for _, n := range n.List {
+ clause := n.(*ast.CaseClause)
+ clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false)
+ }
+ return f
+ case *ast.CommClause: // select
+ for _, n := range n.List {
+ clause := n.(*ast.CommClause)
+ clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false)
+ }
+ return f
+ }
+ }
+ n.List = f.addCounters(n.Lbrace, n.Rbrace+1, n.List, true) // +1 to step past closing brace.
+ case *ast.IfStmt:
+ if n.Init != nil {
+ ast.Walk(f, n.Init)
+ }
+ if n.Cond != nil {
+ ast.Walk(f, n.Cond)
+ }
+ ast.Walk(f, n.Body)
+ if n.Else == nil {
+ // Add else because we want coverage for "not taken".
+ n.Else = &ast.BlockStmt{
+ Lbrace: n.Body.End(),
+ Rbrace: n.Body.End(),
+ }
+ }
+ // The elses are special, because if we have
+ // if x {
+ // } else if y {
+ // }
+ // we want to cover the "if y". To do this, we need a place to drop the counter,
+ // so we add a hidden block:
+ // if x {
+ // } else {
+ // if y {
+ // }
+ // }
+ switch stmt := n.Else.(type) {
+ case *ast.IfStmt:
+ block := &ast.BlockStmt{
+ Lbrace: n.Body.End(), // Start at end of the "if" block so the covered part looks like it starts at the "else".
+ List: []ast.Stmt{stmt},
+ Rbrace: stmt.End(),
+ }
+ n.Else = block
+ case *ast.BlockStmt:
+ stmt.Lbrace = n.Body.End() // Start at end of the "if" block so the covered part looks like it starts at the "else".
+ default:
+ panic("unexpected node type in if")
+ }
+ ast.Walk(f, n.Else)
+ return nil
+ case *ast.ForStmt:
+ // TODO: handle increment statement
+ case *ast.SelectStmt:
+ // Don't annotate an empty select - creates a syntax error.
+ if n.Body == nil || len(n.Body.List) == 0 {
+ return nil
+ }
+ case *ast.SwitchStmt:
+ hasDefault := false
+ if n.Body == nil {
+ n.Body = new(ast.BlockStmt)
+ }
+ for _, s := range n.Body.List {
+ if cas, ok := s.(*ast.CaseClause); ok && cas.List == nil {
+ hasDefault = true
+ break
+ }
+ }
+ if !hasDefault {
+ // Add default case to get additional coverage.
+ n.Body.List = append(n.Body.List, &ast.CaseClause{})
+ }
+
+ // Don't annotate an empty switch - creates a syntax error.
+ if n.Body == nil || len(n.Body.List) == 0 {
+ return nil
+ }
+ case *ast.TypeSwitchStmt:
+ // Don't annotate an empty type switch - creates a syntax error.
+ // TODO: add default case
+ if n.Body == nil || len(n.Body.List) == 0 {
+ return nil
+ }
+ case *ast.BinaryExpr:
+ if n.Op == token.LAND || n.Op == token.LOR {
+ // Replace:
+ // x && y
+ // with:
+ // x && func() T { return y }
+ // where T is a bool of the same type as n (and x and y).
+
+ // Spelling T correctly is a little tricky.
+ // go/types gives us a canonical name for T,
+ // but we can't always use that canonical name in the code directly;
+ // in the general case, it is of the form a/b/c/d.U.
+ // When U is the built-in bool, or defined in the current package,
+ // or defined in a dot-imported package, we want just U.
+ // When U is in another package, we want d.U.
+ // When U is in another package, imported under the name e, we want e.U.
+ // (And when the built-in bool type is shadowed, we're just screwed.)
+ // Handling all of these cases correctly is hard (it requires parsing the imports),
+ // so we handle just the common cases.
+
+ // types.Default maps untyped bools to typed bools.
+ typ := types.Default(f.info.Types[n].Type).String()
+ // If we're in the current package, strip the package path.
+ if strings.HasPrefix(typ, f.pkg+".") {
+ typ = typ[len(f.pkg)+1:]
+ }
+ // If we're still in a package, assume it was imported with a reasonable name.
+ if i := strings.LastIndexByte(typ, '/'); i >= 0 {
+ typ = typ[i+1:]
+ }
+
+ n.Y = &ast.CallExpr{
+ Fun: &ast.FuncLit{
+ Type: &ast.FuncType{Results: &ast.FieldList{List: []*ast.Field{{Type: ast.NewIdent(typ)}}}},
+ Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ReturnStmt{Results: []ast.Expr{n.Y}}}},
+ },
+ }
+ }
+ }
+ return f
+}
+
+func (f *File) addImport(path, name, anyIdent string) {
+ newImport := &ast.ImportSpec{
+ Name: ast.NewIdent(name),
+ Path: &ast.BasicLit{
+ Kind: token.STRING,
+ Value: fmt.Sprintf("%q", path),
+ },
+ }
+ impDecl := &ast.GenDecl{
+ Tok: token.IMPORT,
+ Specs: []ast.Spec{
+ newImport,
+ },
+ }
+ // Make the new import the first Decl in the file.
+ astFile := f.astFile
+ astFile.Decls = append(astFile.Decls, nil)
+ copy(astFile.Decls[1:], astFile.Decls[0:])
+ astFile.Decls[0] = impDecl
+ astFile.Imports = append(astFile.Imports, newImport)
+
+ // Now refer to the package, just in case it ends up unused.
+ // That is, append to the end of the file the declaration
+ // var _ = _cover_atomic_.AddUint32
+ reference := &ast.GenDecl{
+ Tok: token.VAR,
+ Specs: []ast.Spec{
+ &ast.ValueSpec{
+ Names: []*ast.Ident{
+ ast.NewIdent("_"),
+ },
+ Values: []ast.Expr{
+ &ast.SelectorExpr{
+ X: ast.NewIdent(name),
+ Sel: ast.NewIdent(anyIdent),
+ },
+ },
+ },
+ },
+ }
+ astFile.Decls = append(astFile.Decls, reference)
+}
+
+func (f *File) addCounters(pos, blockEnd token.Pos, list []ast.Stmt, extendToClosingBrace bool) []ast.Stmt {
+ // Special case: make sure we add a counter to an empty block. Can't do this below
+ // or we will add a counter to an empty statement list after, say, a return statement.
+ if len(list) == 0 {
+ return []ast.Stmt{f.newCounter(pos, blockEnd, 0)}
+ }
+ // We have a block (statement list), but it may have several basic blocks due to the
+ // appearance of statements that affect the flow of control.
+ var newList []ast.Stmt
+ for {
+ // Find first statement that affects flow of control (break, continue, if, etc.).
+ // It will be the last statement of this basic block.
+ var last int
+ end := blockEnd
+ for last = 0; last < len(list); last++ {
+ end = f.statementBoundary(list[last])
+ if f.endsBasicSourceBlock(list[last]) {
+ extendToClosingBrace = false // Block is broken up now.
+ last++
+ break
+ }
+ }
+ if extendToClosingBrace {
+ end = blockEnd
+ }
+ if pos != end { // Can have no source to cover if e.g. blocks abut.
+ newList = append(newList, f.newCounter(pos, end, last))
+ }
+ newList = append(newList, list[0:last]...)
+ list = list[last:]
+ if len(list) == 0 {
+ break
+ }
+ pos = list[0].Pos()
+ }
+ return newList
+}
+
+func (f *File) endsBasicSourceBlock(s ast.Stmt) bool {
+ switch s := s.(type) {
+ case *ast.BlockStmt:
+ // Treat blocks like basic blocks to avoid overlapping counters.
+ return true
+ case *ast.BranchStmt:
+ return true
+ case *ast.ForStmt:
+ return true
+ case *ast.IfStmt:
+ return true
+ case *ast.LabeledStmt:
+ return f.endsBasicSourceBlock(s.Stmt)
+ case *ast.RangeStmt:
+ return true
+ case *ast.SwitchStmt:
+ return true
+ case *ast.SelectStmt:
+ return true
+ case *ast.TypeSwitchStmt:
+ return true
+ case *ast.ExprStmt:
+ // Calls to panic change the flow.
+ // We really should verify that "panic" is the predefined function,
+ // but without type checking we can't and the likelihood of it being
+ // an actual problem is vanishingly small.
+ if call, ok := s.X.(*ast.CallExpr); ok {
+ if ident, ok := call.Fun.(*ast.Ident); ok && ident.Name == "panic" && len(call.Args) == 1 {
+ return true
+ }
+ }
+ }
+ found, _ := hasFuncLiteral(s)
+ return found
+}
+
+func (f *File) statementBoundary(s ast.Stmt) token.Pos {
+ // Control flow statements are easy.
+ switch s := s.(type) {
+ case *ast.BlockStmt:
+ // Treat blocks like basic blocks to avoid overlapping counters.
+ return s.Lbrace
+ case *ast.IfStmt:
+ found, pos := hasFuncLiteral(s.Init)
+ if found {
+ return pos
+ }
+ found, pos = hasFuncLiteral(s.Cond)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ case *ast.ForStmt:
+ found, pos := hasFuncLiteral(s.Init)
+ if found {
+ return pos
+ }
+ found, pos = hasFuncLiteral(s.Cond)
+ if found {
+ return pos
+ }
+ found, pos = hasFuncLiteral(s.Post)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ case *ast.LabeledStmt:
+ return f.statementBoundary(s.Stmt)
+ case *ast.RangeStmt:
+ found, pos := hasFuncLiteral(s.X)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ case *ast.SwitchStmt:
+ found, pos := hasFuncLiteral(s.Init)
+ if found {
+ return pos
+ }
+ found, pos = hasFuncLiteral(s.Tag)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ case *ast.SelectStmt:
+ return s.Body.Lbrace
+ case *ast.TypeSwitchStmt:
+ found, pos := hasFuncLiteral(s.Init)
+ if found {
+ return pos
+ }
+ return s.Body.Lbrace
+ }
+ found, pos := hasFuncLiteral(s)
+ if found {
+ return pos
+ }
+ return s.End()
+}
+
+var counterGen uint32
+
+func genCounter() int {
+ counterGen++
+ id := counterGen
+ buf := []byte{byte(id), byte(id >> 8), byte(id >> 16), byte(id >> 24)}
+ hash := sha1.Sum(buf)
+ return int(uint16(hash[0]) | uint16(hash[1])<<8)
+}
+
+func (f *File) newCounter(start, end token.Pos, numStmt int) ast.Stmt {
+ cnt := genCounter()
+
+ if f.blocks != nil {
+ s := f.fset.Position(start)
+ e := f.fset.Position(end)
+ *f.blocks = append(*f.blocks, CoverBlock{cnt, f.fullName, s.Line, s.Column, e.Line, e.Column, numStmt})
+ }
+
+ idx := &ast.BasicLit{
+ Kind: token.INT,
+ Value: strconv.Itoa(cnt),
+ }
+ counter := &ast.IndexExpr{
+ X: &ast.SelectorExpr{
+ X: ast.NewIdent(fuzzdepPkg),
+ Sel: ast.NewIdent("CoverTab"),
+ },
+ Index: idx,
+ }
+ return &ast.IncDecStmt{
+ X: counter,
+ Tok: token.INC,
+ }
+}
+
+func (f *File) print(w io.Writer) {
+ cfg := printer.Config{
+ Mode: printer.SourcePos,
+ Tabwidth: 8,
+ Indent: 0,
+ }
+ cfg.Fprint(w, f.fset, f.astFile)
+}
+
+type funcLitFinder token.Pos
+
+func (f *funcLitFinder) Visit(node ast.Node) (w ast.Visitor) {
+ if f.found() {
+ return nil // Prune search.
+ }
+ switch n := node.(type) {
+ case *ast.FuncLit:
+ *f = funcLitFinder(n.Body.Lbrace)
+ return nil // Prune search.
+ }
+ return f
+}
+
+func (f *funcLitFinder) found() bool {
+ return token.Pos(*f) != token.NoPos
+}
+
+func hasFuncLiteral(n ast.Node) (bool, token.Pos) {
+ if n == nil {
+ return false, 0
+ }
+ var literal funcLitFinder
+ ast.Walk(&literal, n)
+ return literal.found(), token.Pos(literal)
+}
+
+func (lc *LiteralCollector) unquote(s string) string {
+ t, err := strconv.Unquote(s)
+ if err != nil {
+ lc.ctxt.failf("cover: improperly quoted string %q\n", s)
+ }
+ return t
+}
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-build/main.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-build/main.go
new file mode 100644
index 000000000..dc43ac2b4
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-build/main.go
@@ -0,0 +1,882 @@
+// Copyright 2015 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.
+
+package main
+
+import (
+ "archive/zip"
+ "bytes"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "runtime/pprof"
+ "strings"
+ "text/template"
+ "unicode"
+ "unicode/utf8"
+
+ "golang.org/x/tools/go/packages"
+
+ . "github.com/dvyukov/go-fuzz/internal/go-fuzz-types"
+)
+
+var (
+ flagTag = flag.String("tags", "", "a space-separated list of build tags to consider satisfied during the build")
+ flagOut = flag.String("o", "", "output file")
+ flagFunc = flag.String("func", "", "preferred entry function")
+ flagWork = flag.Bool("work", false, "don't remove working directory")
+ flagRace = flag.Bool("race", false, "enable race detector")
+ flagCPU = flag.Bool("cpuprofile", false, "generate cpu profile in cpu.pprof")
+ flagLibFuzzer = flag.Bool("libfuzzer", false, "output static archive for use with libFuzzer")
+ flagBuildX = flag.Bool("x", false, "print the commands if build fails")
+ flagPreserve = flag.String("preserve", "", "a comma-separated list of import paths not to instrument")
+)
+
+func makeTags() string {
+ tags := "gofuzz"
+ if *flagLibFuzzer {
+ tags += " gofuzz_libfuzzer"
+ }
+ if *flagRace {
+ tags += " race"
+ }
+ if len(*flagTag) > 0 {
+ tags += " " + *flagTag
+ }
+ return tags
+}
+
+// basePackagesConfig returns a base golang.org/x/tools/go/packages.Config
+// that clients can then modify and use for calls to go/packages.
+func basePackagesConfig() *packages.Config {
+ cfg := new(packages.Config)
+
+ // Note that we do not set GO111MODULE here in order to respect any GO111MODULE
+ // setting by the user as we are finding dependencies. Note, however, that
+ // we are still setting up a GOPATH to build, so we later will force
+ // GO111MODULE to be off when building so that we are in GOPATH mode.
+ // If the user has not set GO111MODULE, the meaning here is
+ // left up to cmd/go (defaulting to 'auto' in Go 1.11-1.13,
+ // but likely defaulting to 'on' at some point during Go 1.14
+ // development cycle).
+ // Also note that we are leaving the overall cfg structure
+ // in place to support future experimentation, etc.
+ cfg.Env = os.Environ()
+ return cfg
+}
+
+// checkModVendor reports if the GOFLAGS env variable
+// contains -mod=vendor, which enables vendoring for modules.
+func checkModVendor() bool {
+ val := os.Getenv("GOFLAGS")
+ for _, s := range strings.Split(val, " ") {
+ if s == "-mod=vendor" {
+ return true
+ }
+ }
+ return false
+}
+
+// main copies the package with all dependent packages into a temp dir,
+// instruments Go source files there, and builds setting GOROOT to the temp dir.
+func main() {
+ flag.Parse()
+ c := new(Context)
+
+ if flag.NArg() > 1 {
+ c.failf("usage: go-fuzz-build [pkg]")
+ }
+
+ pkg := "."
+ if flag.NArg() == 1 {
+ pkg = flag.Arg(0)
+ }
+ if *flagFunc != "" && !isFuzzFuncName(*flagFunc) {
+ c.failf("provided -func=%v, but %v is not a fuzz function name", *flagFunc, *flagFunc)
+ }
+ if *flagLibFuzzer && *flagRace {
+ c.failf("-race and -libfuzzer are incompatible")
+ }
+ if checkModVendor() {
+ // We don't support -mod=vendor with modules.
+ // Part of the issue is go-fuzz-dep and go-fuzz-defs
+ // won't be in the user's vendor directory.
+ c.failf("GOFLAGS with -mod=vendor is not supported")
+ }
+
+ c.startProfiling() // start pprof as requested
+ c.loadPkg(pkg) // load and typecheck pkg
+ c.getEnv() // discover GOROOT, GOPATH
+ c.loadStd() // load standard library
+ c.calcIgnore() // calculate set of packages to ignore
+ c.makeWorkdir() // create workdir
+ defer c.cleanup() // delete workdir as needed, etc.
+ c.populateWorkdir() // copy tools and packages to workdir as needed
+
+ if *flagOut == "" {
+ 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.
+ // (We don't want to re-parse and re-typecheck every time, for performance.)
+ // So we gather literals first, while the AST is pristine.
+ // Then we add coverage and build.
+ // Then we add sonar and build.
+ // TODO: migrate to use cmd/internal/edit instead of AST modification.
+ // This has several benefits: (1) It is easier to work with.
+ // (2) 'go cover' has switched to it; we would get the benefit of
+ // upstream bug fixes, of which there has been at least one (around gotos and labels).
+ // (3) It leaves the AST intact, so we are less order-sensitive.
+ // The primary blocker is that we want good line numbers for when we find crashers.
+ // go/printer handles this automatically using Mode printer.SourcePos.
+ // We'd need to implement that support ourselves. (It's do-able but non-trivial.)
+ // See also https://golang.org/issue/29824.
+ lits := c.gatherLiterals()
+ var blocks, sonar []CoverBlock
+
+ if *flagLibFuzzer {
+ archive := c.buildInstrumentedBinary(&blocks, nil)
+ c.moveFile(archive, *flagOut)
+ return
+ }
+
+ coverBin := c.buildInstrumentedBinary(&blocks, nil)
+ sonarBin := c.buildInstrumentedBinary(nil, &sonar)
+ metaData := c.createMeta(lits, blocks, sonar)
+ defer func() {
+ os.Remove(coverBin)
+ os.Remove(sonarBin)
+ os.Remove(metaData)
+ }()
+
+ outf, err := os.Create(*flagOut)
+ if err != nil {
+ c.failf("failed to create output file: %v", err)
+ }
+ zipw := zip.NewWriter(outf)
+ zipFile := func(name, datafile string) {
+ w, err := zipw.Create(name)
+ if err != nil {
+ c.failf("failed to create zip file: %v", err)
+ }
+ f, err := os.Open(datafile)
+ if err != nil {
+ c.failf("failed to open data file %v", datafile)
+ }
+ if _, err := io.Copy(w, f); err != nil {
+ c.failf("failed to write %v to zip file: %v", datafile, err)
+ }
+ // best effort: close and remove our temp file
+ f.Close()
+ os.Remove(datafile)
+ }
+ zipFile("cover.exe", coverBin)
+ zipFile("sonar.exe", sonarBin)
+ zipFile("metadata", metaData)
+ if err := zipw.Close(); err != nil {
+ c.failf("failed to close zip file: %v", err)
+ }
+ if err := outf.Close(); err != nil {
+ c.failf("failed to close out file: %v", err)
+ }
+}
+
+// Context holds state for a go-fuzz-build run.
+type Context struct {
+ fuzzpkg *packages.Package // package containing Fuzz function
+ pkgs []*packages.Package // typechecked root packages
+
+ std map[string]bool // set of packages in the standard library
+ ignore map[string]bool // set of packages to ignore during instrumentation
+
+ allFuncs []string // all fuzz functions found in package
+
+ workdir string
+ GOROOT string
+ GOPATH string
+
+ cpuprofile *os.File
+
+ cmdGoHasTrimPath bool // does the active version of cmd/go have the -trimpath flag?
+}
+
+// getEnv determines GOROOT and GOPATH and updates c accordingly.
+func (c *Context) getEnv() {
+ env := map[string]string{
+ "GOROOT": "",
+ "GOPATH": "",
+ }
+ for k := range env {
+ v := os.Getenv(k)
+ if v != "" {
+ env[k] = v
+ continue
+ }
+ // TODO: make a single call ("go env GOROOT GOPATH") instead
+ out, err := exec.Command("go", "env", k).CombinedOutput()
+ if err != nil || len(out) == 0 {
+ c.failf("%s is not set and failed to locate it: 'go env %s' returned '%s' (%v)", k, k, out, err)
+ }
+ env[k] = strings.TrimSpace(string(out))
+ }
+ c.GOROOT = env["GOROOT"]
+ c.GOPATH = env["GOPATH"]
+
+ out, err := exec.Command("go", "list", "-f", "'{{context.ReleaseTags}}'", "runtime").CombinedOutput()
+ if err != nil || len(out) == 0 {
+ c.failf("go list -f '{{context.ReleaseTags}}' runtime returned '%s' (%v)", out, err)
+ }
+ c.cmdGoHasTrimPath = bytes.Contains(out, []byte("go1.13"))
+}
+
+// startProfiling starts pprof profiling, if requested.
+func (c *Context) startProfiling() {
+ if !*flagCPU {
+ return
+ }
+ var err error
+ c.cpuprofile, err = os.Create("cpu.pprof")
+ if err != nil {
+ c.failf("could not create cpu profile: %v", err)
+ }
+ pprof.StartCPUProfile(c.cpuprofile)
+}
+
+// loadPkg loads, parses, and typechecks pkg (the package containing the Fuzz function),
+// go-fuzz-dep, and their dependencies.
+func (c *Context) loadPkg(pkg string) {
+ // Resolve pkg.
+ // See https://golang.org/issue/30826 and https://golang.org/issue/30828.
+ rescfg := basePackagesConfig()
+ rescfg.Mode = packages.NeedName
+ rescfg.BuildFlags = []string{"-tags", makeTags()}
+ respkgs, err := packages.Load(rescfg, pkg)
+ if err != nil {
+ c.failf("could not resolve package %q: %v", pkg, err)
+ }
+ if len(respkgs) != 1 {
+ paths := make([]string, len(respkgs))
+ for i, p := range respkgs {
+ paths[i] = p.PkgPath
+ }
+ c.failf("cannot build multiple packages, but %q resolved to: %v", pkg, strings.Join(paths, ", "))
+ }
+ if respkgs[0].Name == "main" {
+ c.failf("cannot fuzz package main")
+ }
+ pkgpath := respkgs[0].PkgPath
+
+ // Load, parse, and type-check all packages.
+ // We'll use the type information later.
+ // This also provides better error messages in the case
+ // of invalid code than trying to compile instrumented code.
+ cfg := basePackagesConfig()
+ cfg.Mode = packages.LoadAllSyntax
+ cfg.BuildFlags = []string{"-tags", makeTags()}
+ // use custom ParseFile in order to get comments
+ cfg.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
+ return parser.ParseFile(fset, filename, src, parser.ParseComments)
+ }
+ // We need to load:
+ // * the target package, obviously
+ // * go-fuzz-dep, since we use it for instrumentation
+ // * reflect, if we are using libfuzzer, since its generated main function requires it
+ loadpkgs := []string{pkg, "github.com/dvyukov/go-fuzz/go-fuzz-dep"}
+ if *flagLibFuzzer {
+ loadpkgs = append(loadpkgs, "reflect")
+ }
+ initial, err := packages.Load(cfg, loadpkgs...)
+ if err != nil {
+ c.failf("could not load packages: %v", err)
+ }
+
+ // Stop if any package had errors.
+ if packages.PrintErrors(initial) > 0 {
+ c.failf("typechecking of %v failed", pkg)
+ }
+
+ c.pkgs = initial
+
+ // Find the fuzz package among c.pkgs.
+ for _, p := range initial {
+ if p.PkgPath == pkgpath {
+ c.fuzzpkg = p
+ break
+ }
+ }
+ if c.fuzzpkg == nil {
+ c.failf("internal error: failed to find fuzz package; please file an issue")
+ }
+
+ // Find all fuzz functions in fuzzpkg.
+ foundFlagFunc := false
+ s := c.fuzzpkg.Types.Scope()
+ for _, n := range s.Names() {
+ if !isFuzzFuncName(n) {
+ continue
+ }
+ // Check that n is a function with an appropriate signature.
+ typ := s.Lookup(n).Type()
+ sig, ok := typ.(*types.Signature)
+ if !ok || sig.Variadic() || !isFuzzSig(sig) {
+ if n == *flagFunc {
+ c.failf("provided -func=%v, but %v is not a fuzz function", *flagFunc, *flagFunc)
+ }
+ continue
+ }
+ // n is a fuzz function.
+ c.allFuncs = append(c.allFuncs, n)
+ foundFlagFunc = foundFlagFunc || n == *flagFunc
+ }
+
+ if len(c.allFuncs) == 0 {
+ c.failf("could not find any fuzz functions in %v", c.fuzzpkg.PkgPath)
+ }
+ if len(c.allFuncs) > 255 {
+ c.failf("go-fuzz-build supports a maximum of 255 fuzz functions, found %v; please file an issue", len(c.allFuncs))
+ }
+
+ if *flagFunc != "" {
+ // Specific fuzz function requested.
+ // If the requested function doesn't exist, fail.
+ if !foundFlagFunc {
+ c.failf("could not find fuzz function %v in %v", *flagFunc, c.fuzzpkg.PkgPath)
+ }
+ } else {
+ // No specific fuzz function requested.
+ // If there's only one fuzz function, mark it as preferred.
+ // If there's more than one...
+ // ...for go-fuzz, that's fine; one can be specified later on the command line.
+ // ...for libfuzzer, that's not fine, as there is no way to specify one later.
+ if len(c.allFuncs) == 1 {
+ *flagFunc = c.allFuncs[0]
+ } else if *flagLibFuzzer {
+ c.failf("must specify a fuzz function with -libfuzzer, found: %v", strings.Join(c.allFuncs, ", "))
+ }
+ }
+}
+
+// isFuzzSig reports whether sig is of the form
+// func FuzzFunc(data []byte) int
+func isFuzzSig(sig *types.Signature) bool {
+ return tupleHasTypes(sig.Params(), "[]byte") && tupleHasTypes(sig.Results(), "int")
+}
+
+// tupleHasTypes reports whether tuple is composed of
+// elements with exactly the types in types.
+func tupleHasTypes(tuple *types.Tuple, types ...string) bool {
+ if tuple.Len() != len(types) {
+ return false
+ }
+ for i, t := range types {
+ if tuple.At(i).Type().String() != t {
+ return false
+ }
+ }
+ return true
+}
+
+func isFuzzFuncName(name string) bool {
+ return isTest(name, "Fuzz")
+}
+
+// isTest is copied verbatim, along with its name,
+// from GOROOT/src/cmd/go/internal/load/test.go.
+// isTest tells whether name looks like a test (or benchmark, according to prefix).
+// It is a Test (say) if there is a character after Test that is not a lower-case letter.
+// We don't want TesticularCancer.
+func isTest(name, prefix string) bool {
+ if !strings.HasPrefix(name, prefix) {
+ return false
+ }
+ if len(name) == len(prefix) { // "Test" is ok
+ return true
+ }
+ rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
+ return !unicode.IsLower(rune)
+}
+
+// loadStd finds the set of standard library package paths.
+func (c *Context) loadStd() {
+ // Find out what packages are in the standard library.
+ cfg := basePackagesConfig()
+ cfg.Mode = packages.NeedName
+ stdpkgs, err := packages.Load(cfg, "std")
+ if err != nil {
+ c.failf("could not load standard library: %v", err)
+ }
+ c.std = make(map[string]bool, len(stdpkgs))
+ for _, p := range stdpkgs {
+ c.std[p.PkgPath] = true
+ }
+}
+
+// makeWorkdir creates the workdir, logging as requested.
+func (c *Context) makeWorkdir() {
+ // TODO: make workdir stable, so that we can use cmd/go's build cache?
+ // See https://github.com/golang/go/issues/29430.
+ var err error
+ c.workdir, err = ioutil.TempDir("", "go-fuzz-build")
+ if err != nil {
+ c.failf("failed to create temp dir: %v", err)
+ }
+ if *flagWork {
+ fmt.Printf("workdir: %v\n", c.workdir)
+ }
+}
+
+// cleanup ensures a clean exit. It should be called on all (controllable) exit paths.
+func (c *Context) cleanup() {
+ if !*flagWork && c.workdir != "" {
+ os.RemoveAll(c.workdir)
+ }
+ if c.cpuprofile != nil {
+ pprof.StopCPUProfile()
+ c.cpuprofile.Close()
+ }
+}
+
+// populateWorkdir prepares workdir for builds.
+func (c *Context) populateWorkdir() {
+ // TODO: instead of reconstructing the world,
+ // can we use a bunch of replace directives in a go.mod?
+
+ // TODO: make all this I/O concurrent (up to a limit).
+ // It's a non-trivial part of build time.
+ // Question: Do it here or in copyDir?
+
+ // TODO: See if we can avoid making toolchain copies,
+ // using some combination of env vars and toolexec.
+ if *flagLibFuzzer || *flagRace {
+ c.copyDir(filepath.Join(c.GOROOT, "src", "runtime", "cgo"), filepath.Join(c.workdir, "goroot", "src", "runtime", "cgo"))
+ }
+ if *flagRace {
+ c.copyDir(filepath.Join(c.GOROOT, "src", "runtime", "race"), filepath.Join(c.workdir, "goroot", "src", "runtime", "race"))
+ c.copyDir(filepath.Join(c.GOROOT, "src", "sync", "atomic"), filepath.Join(c.workdir, "goroot", "src", "sync", "atomic"))
+ }
+ c.copyDir(filepath.Join(c.GOROOT, "pkg", "tool"), filepath.Join(c.workdir, "goroot", "pkg", "tool"))
+ if _, err := os.Stat(filepath.Join(c.GOROOT, "pkg", "include")); err == nil {
+ c.copyDir(filepath.Join(c.GOROOT, "pkg", "include"), filepath.Join(c.workdir, "goroot", "pkg", "include"))
+ } else {
+ // Cross-compilation is not implemented.
+ c.copyDir(filepath.Join(c.GOROOT, "pkg", runtime.GOOS+"_"+runtime.GOARCH), filepath.Join(c.workdir, "goroot", "pkg", runtime.GOOS+"_"+runtime.GOARCH))
+ }
+
+ // Clone our package, go-fuzz-deps, and all dependencies.
+ // TODO: we might not need to do this for all packages.
+ // We know that we'll be writing out instrumented Go code later;
+ // we could instead just os.MkdirAll and copy non-Go files here.
+ // We'd still need to do a full package clone for packages that
+ // we aren't instrumenting (c.ignore).
+ packages.Visit(c.pkgs, nil, func(p *packages.Package) {
+ c.clonePackage(p)
+ })
+ c.copyFuzzDep()
+}
+
+func (c *Context) createMeta(lits map[Literal]struct{}, blocks []CoverBlock, sonar []CoverBlock) string {
+ meta := MetaData{Blocks: blocks, Sonar: sonar, Funcs: c.allFuncs, DefaultFunc: *flagFunc}
+ for k := range lits {
+ meta.Literals = append(meta.Literals, k)
+ }
+ data, err := json.Marshal(meta)
+ if err != nil {
+ c.failf("failed to serialize meta information: %v", err)
+ }
+ f := c.tempFile()
+ c.writeFile(f, data)
+ return f
+}
+
+func (c *Context) buildInstrumentedBinary(blocks *[]CoverBlock, sonar *[]CoverBlock) string {
+ c.instrumentPackages(blocks, sonar)
+ mainPkg := c.createFuzzMain()
+ outf := c.tempFile()
+ args := []string{"build", "-tags", makeTags()}
+ if *flagBuildX {
+ args = append(args, "-x")
+
+ if *flagWork {
+ args = append(args, "-work")
+ }
+ }
+ if *flagRace {
+ args = append(args, "-race")
+ }
+ if *flagLibFuzzer {
+ args = append(args, "-buildmode=c-archive")
+ }
+ if c.cmdGoHasTrimPath {
+ args = append(args, "-trimpath")
+ }
+ args = append(args, "-o", outf, mainPkg)
+ cmd := exec.Command("go", args...)
+
+ // We are constructing a GOPATH environment, so while building
+ // we force GOPATH mode here via GO111MODULE=off.
+ cmd.Env = append(os.Environ(),
+ "GOROOT="+filepath.Join(c.workdir, "goroot"),
+ "GOPATH="+filepath.Join(c.workdir, "gopath"),
+ "GO111MODULE=off",
+ )
+ if out, err := cmd.CombinedOutput(); err != nil {
+ c.failf("failed to execute go build: %v\n%v", err, string(out))
+ }
+ return outf
+}
+
+func (c *Context) calcIgnore() {
+ // No reason to instrument these.
+ c.ignore = map[string]bool{
+ "runtime/cgo": true,
+ "runtime/pprof": true,
+ "runtime/race": true,
+ }
+
+ // Roots: must not instrument these, nor any of their dependencies, to avoid import cycles.
+ // Fortunately, these are mostly packages that are non-deterministic,
+ // noisy (because they are low level), and/or not interesting.
+ // We could manually maintain this list, but that makes go-fuzz-build
+ // fragile in the face of internal standard library package changes.
+ roots := c.packagesNamed("runtime", "github.com/dvyukov/go-fuzz/go-fuzz-dep")
+ packages.Visit(roots, func(p *packages.Package) bool {
+ c.ignore[p.PkgPath] = true
+ return true
+ }, nil)
+
+ // Ignore any packages requested explicitly by the user.
+ paths := strings.Split(*flagPreserve, ",")
+ for _, path := range paths {
+ c.ignore[path] = true
+ }
+}
+
+func (c *Context) gatherLiterals() map[Literal]struct{} {
+ nolits := map[string]bool{
+ "math": true,
+ "os": true,
+ "unicode": true,
+ }
+
+ lits := make(map[Literal]struct{})
+ visit := func(pkg *packages.Package) {
+ if c.ignore[pkg.PkgPath] || nolits[pkg.PkgPath] {
+ return
+ }
+ for _, f := range pkg.Syntax {
+ ast.Walk(&LiteralCollector{lits: lits, ctxt: c}, f)
+ }
+ }
+
+ packages.Visit(c.pkgs, nil, visit)
+ return lits
+}
+
+func (c *Context) copyFuzzDep() {
+ // Standard library packages can't depend on non-standard ones.
+ // So we pretend that go-fuzz-dep is a standard one.
+ // go-fuzz-dep depends on go-fuzz-defs, which creates a problem.
+ // Fortunately (and intentionally), go-fuzz-defs contains only constants,
+ // which can be duplicated safely.
+ // So we eliminate the import statement and copy go-fuzz-defs/defs.go
+ // directly into the go-fuzz-dep package.
+ newDir := filepath.Join(c.workdir, "goroot", "src", "go-fuzz-dep")
+ c.mkdirAll(newDir)
+ dep := c.packageNamed("github.com/dvyukov/go-fuzz/go-fuzz-dep")
+ for _, f := range dep.GoFiles {
+ data := c.readFile(f)
+ // Eliminate the dot import.
+ data = bytes.Replace(data, []byte(`. "github.com/dvyukov/go-fuzz/go-fuzz-defs"`), nil, -1)
+ c.writeFile(filepath.Join(newDir, filepath.Base(f)), data)
+ }
+
+ defs := c.packageNamed("github.com/dvyukov/go-fuzz/go-fuzz-defs")
+ for _, f := range defs.GoFiles {
+ data := c.readFile(f)
+ // Adjust package name to match go-fuzz-deps.
+ data = bytes.Replace(data, []byte("\npackage base"), []byte("\npackage gofuzzdep"), -1)
+ c.writeFile(filepath.Join(newDir, "defs.go"), data)
+ }
+}
+
+func (c *Context) funcMain() []byte {
+ t := mainSrc
+ if *flagLibFuzzer {
+ t = mainSrcLibFuzzer
+ }
+ dot := map[string]interface{}{"Pkg": c.fuzzpkg.PkgPath, "AllFuncs": c.allFuncs, "DefaultFunc": *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() string {
+ mainPkg := filepath.Join(c.fuzzpkg.PkgPath, "go.fuzz.main")
+ path := filepath.Join(c.workdir, "gopath", "src", mainPkg)
+ c.mkdirAll(path)
+ c.writeFile(filepath.Join(path, "main.go"), c.funcMain())
+ return mainPkg
+}
+
+func (c *Context) clonePackage(p *packages.Package) {
+ root := "goroot"
+ if !c.std[p.PkgPath] {
+ root = "gopath"
+ }
+ newDir := filepath.Join(c.workdir, root, "src", p.PkgPath)
+ c.mkdirAll(newDir)
+
+ if p.PkgPath == "unsafe" {
+ // Write a dummy file. go/packages explicitly returns an empty GoFiles for it,
+ // for reasons that are unclear, but cmd/go wants there to be a Go file in the package.
+ c.writeFile(filepath.Join(newDir, "unsafe.go"), []byte(`package unsafe`))
+ return
+ }
+
+ // Copy all the source code.
+
+ // Use GoFiles instead of CompiledGoFiles here.
+ // If we use CompiledGoFiles, we end up with code that cmd/go won't compile.
+ // See https://golang.org/issue/30479 and Context.instrumentPackages.
+ for _, f := range p.GoFiles {
+ dst := filepath.Join(newDir, filepath.Base(f))
+ c.copyFile(f, dst)
+ }
+ for _, f := range p.OtherFiles {
+ dst := filepath.Join(newDir, filepath.Base(f))
+ c.copyFile(f, dst)
+ }
+
+ // TODO: do we need to look for and copy go.mod?
+}
+
+// packageNamed extracts the package listed in path.
+func (c *Context) packageNamed(path string) (pkgs *packages.Package) {
+ all := c.packagesNamed(path)
+ if len(all) != 1 {
+ c.failf("got multiple packages, requested only %v", path)
+ }
+ return all[0]
+}
+
+// packagesNamed extracts the packages listed in paths.
+func (c *Context) packagesNamed(paths ...string) (pkgs []*packages.Package) {
+ pre := func(p *packages.Package) bool {
+ for _, path := range paths {
+ if p.PkgPath == path {
+ pkgs = append(pkgs, p)
+ break
+ }
+ }
+ return len(pkgs) < len(paths) // continue only if we have not succeeded yet
+ }
+ packages.Visit(c.pkgs, pre, nil)
+ return pkgs
+}
+
+func (c *Context) instrumentPackages(blocks *[]CoverBlock, sonar *[]CoverBlock) {
+ visit := func(pkg *packages.Package) {
+ if c.ignore[pkg.PkgPath] {
+ return
+ }
+
+ root := "goroot"
+ if !c.std[pkg.PkgPath] {
+ root = "gopath"
+ }
+ path := filepath.Join(c.workdir, root, "src", pkg.PkgPath) // TODO: need filepath.FromSlash for pkg.PkgPath?
+
+ for i, fullName := range pkg.CompiledGoFiles {
+ fname := filepath.Base(fullName)
+ if !strings.HasSuffix(fname, ".go") {
+ // This is a cgo-generated file.
+ // Instrumenting it currently does not work.
+ // We copied the original Go file as part of copyPackageRewrite,
+ // so we can just skip this one.
+ // See https://golang.org/issue/30479.
+ continue
+ }
+ f := pkg.Syntax[i]
+
+ // TODO: rename trimComments?
+ f.Comments = trimComments(f, pkg.Fset)
+
+ buf := new(bytes.Buffer)
+ content := c.readFile(fullName)
+ buf.Write(initialComments(content)) // Retain '// +build' directives.
+ instrument(pkg.PkgPath, fullName, pkg.Fset, f, pkg.TypesInfo, buf, blocks, sonar)
+ tmp := c.tempFile()
+ c.writeFile(tmp, buf.Bytes())
+ outpath := filepath.Join(path, fname)
+ if runtime.GOOS == "windows" {
+ os.Remove(outpath)
+ }
+ c.moveFile(tmp, outpath)
+ }
+ }
+
+ packages.Visit(c.pkgs, nil, visit)
+}
+
+func (c *Context) copyDir(dir, newDir string) {
+ c.mkdirAll(newDir)
+ files, err := ioutil.ReadDir(dir)
+ if err != nil {
+ c.failf("failed to scan dir '%v': %v", dir, err)
+ }
+ for _, f := range files {
+ if f.IsDir() {
+ c.copyDir(filepath.Join(dir, f.Name()), filepath.Join(newDir, f.Name()))
+ continue
+ }
+ src := filepath.Join(dir, f.Name())
+ dst := filepath.Join(newDir, f.Name())
+ c.copyFile(src, dst)
+ }
+}
+
+func (c *Context) copyFile(src, dst string) {
+ r, err := os.Open(src)
+ if err != nil {
+ c.failf("copyFile: could not read %v", src, err)
+ }
+ w, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700)
+ if err != nil {
+ c.failf("copyFile: could not write %v: %v", dst, err)
+ }
+ if _, err := io.Copy(w, r); err != nil {
+ c.failf("copyFile: copying failed: %v", err)
+ }
+ if err := r.Close(); err != nil {
+ c.failf("copyFile: closing %v failed: %v", src, err)
+ }
+ if err := w.Close(); err != nil {
+ c.failf("copyFile: closing %v failed: %v", dst, err)
+ }
+}
+
+func (c *Context) moveFile(src, dst string) {
+ c.copyFile(src, dst)
+ err := os.Remove(src)
+ if err != nil {
+ c.failf("moveFile: removing %q failed: %v", src, err)
+ }
+}
+
+func (c *Context) failf(str string, args ...interface{}) {
+ c.cleanup()
+ fmt.Fprintf(os.Stderr, str+"\n", args...)
+ os.Exit(1)
+}
+
+// tempFile creates and deletes a temp file, and returns its path.
+// This is helpful when you need a temp path for an output file
+// that will be created by an external tool (go build) or by a call to writeFile.
+func (c *Context) tempFile() string {
+ outf, err := ioutil.TempFile("", "go-fuzz")
+ if err != nil {
+ c.failf("failed to create temp file: %v", err)
+ }
+ outf.Close()
+ os.Remove(outf.Name()) // necessary on Windows
+ return outf.Name()
+}
+
+func (c *Context) readFile(name string) []byte {
+ data, err := ioutil.ReadFile(name)
+ if err != nil {
+ c.failf("failed to read temp file: %v", err)
+ }
+ return data
+}
+
+func (c *Context) writeFile(name string, data []byte) {
+ if err := ioutil.WriteFile(name, data, 0700); err != nil {
+ c.failf("failed to write temp file: %v", err)
+ }
+}
+
+func (c *Context) mkdirAll(dir string) {
+ if err := os.MkdirAll(dir, 0700); err != nil {
+ c.failf("failed to create temp dir: %v", err)
+ }
+}
+
+var mainSrc = template.Must(template.New("main").Parse(`
+package main
+
+import (
+ target "{{.Pkg}}"
+ dep "go-fuzz-dep"
+)
+
+func main() {
+ fns := []func([]byte)int {
+ {{range .AllFuncs}}
+ target.{{.}},
+ {{end}}
+ }
+ dep.Main(fns)
+}
+`))
+
+var mainSrcLibFuzzer = template.Must(template.New("main").Parse(`
+package main
+
+import (
+ "unsafe"
+ "reflect"
+ target "{{.Pkg}}"
+ dep "go-fuzz-dep"
+)
+
+// #cgo CFLAGS: -Wall -Werror
+// #ifdef __linux__
+// __attribute__((weak, section("__libfuzzer_extra_counters")))
+// #else
+// #error Currently only Linux is supported
+// #endif
+// unsigned char GoFuzzCoverageCounters[65536];
+import "C"
+
+//export LLVMFuzzerInitialize
+func LLVMFuzzerInitialize(argc uintptr, argv uintptr) int {
+ dep.Initialize(unsafe.Pointer(&C.GoFuzzCoverageCounters[0]), 65536)
+ return 0
+}
+
+//export LLVMFuzzerTestOneInput
+func LLVMFuzzerTestOneInput(data uintptr, size uint64) int {
+ sh := &reflect.SliceHeader{
+ Data: data,
+ Len: int(size),
+ Cap: int(size),
+ }
+
+ input := *(*[]byte)(unsafe.Pointer(sh))
+ target.{{.DefaultFunc}}(input)
+
+ return 0
+}
+
+func main() {
+}
+`))
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-defs/defs.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-defs/defs.go
new file mode 100644
index 000000000..b66d5abdf
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-defs/defs.go
@@ -0,0 +1,36 @@
+// Copyright 2015 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.
+
+// Package defs provides constants required by go-fuzz-build, go-fuzz, and instrumented code.
+package base
+
+// This package has a special interaction with go-fuzz-dep:
+// It is copied into a package with it by go-fuzz-build.
+// Only things that can be safely duplicated without confusion,
+// like constants, should be added to this package.
+// And any additions should be tested carefully. :)
+
+const (
+ CoverSize = 64 << 10
+ MaxInputSize = 1 << 20
+ SonarRegionSize = 1 << 20
+)
+
+const (
+ SonarEQL = iota
+ SonarNEQ
+ SonarLSS
+ SonarGTR
+ SonarLEQ
+ SonarGEQ
+
+ SonarOpMask = 7
+ SonarLength = 1 << 3
+ SonarSigned = 1 << 4
+ SonarString = 1 << 5
+ SonarConst1 = 1 << 6
+ SonarConst2 = 1 << 7
+
+ SonarHdrLen = 6
+ SonarMaxLen = 20
+)
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/cover.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/cover.go
new file mode 100644
index 000000000..46604ad5a
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/cover.go
@@ -0,0 +1,22 @@
+// 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
+
+// CoverTab holds code coverage.
+// It is initialized to a new array so that instrumentation
+// executed during process initialization has somewhere to write to.
+// It is replaced by a newly initialized array when it is
+// time for actual instrumentation to commence.
+var CoverTab = new([CoverSize]byte)
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/doc.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/doc.go
new file mode 100644
index 000000000..c8adedb38
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/doc.go
@@ -0,0 +1,10 @@
+// Package gofuzzdep contains the business logic used to monitor the fuzzing.
+//
+// It is handled specially by go-fuzz-build; see the comments in package go-fuzz-defs.
+//
+// Be particularly careful about adding imports to go-fuzz-dep:
+// Any package imported by go-fuzz-dep cannot be instrumented (on pain of import cycles),
+// which reduces the effectiveness of go-fuzz on any other package that imports it.
+// That is why (e.g.) there are hand-rolled serialization functions instead of using encoding/binary,
+// and hand-rolled syscall-based communication instead of using package net or os.
+package gofuzzdep
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/main.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/main.go
new file mode 100644
index 000000000..09bb3c581
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/main.go
@@ -0,0 +1,99 @@
+// Copyright 2015 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
+// +build !gofuzz_libfuzzer
+
+package gofuzzdep
+
+import (
+ "runtime"
+ "sync/atomic"
+ "syscall"
+ "time"
+ "unsafe"
+
+ . "github.com/dvyukov/go-fuzz/go-fuzz-defs"
+)
+
+func Main(fns []func([]byte) int) {
+ mem, inFD, outFD := setupCommFile()
+ CoverTab = (*[CoverSize]byte)(unsafe.Pointer(&mem[0]))
+ input := mem[CoverSize : CoverSize+MaxInputSize]
+ sonarRegion = mem[CoverSize+MaxInputSize:]
+ runtime.GOMAXPROCS(1) // makes coverage more deterministic, we parallelize on higher level
+ for {
+ fnidx, n := read(inFD)
+ if n > uint64(len(input)) {
+ println("invalid input length")
+ syscall.Exit(1)
+ }
+ for i := range CoverTab {
+ CoverTab[i] = 0
+ }
+ atomic.StoreUint32(&sonarPos, 0)
+ t0 := time.Now()
+ res := fns[fnidx](input[:n:n])
+ ns := time.Since(t0)
+ write(outFD, uint64(res), uint64(ns), uint64(atomic.LoadUint32(&sonarPos)))
+ }
+}
+
+// read reads little-endian-encoded uint8+uint64 from fd.
+func read(fd FD) (uint8, uint64) {
+ rd := 0
+ var buf [9]byte
+ for rd != len(buf) {
+ n, err := fd.read(buf[rd:])
+ if err == syscall.EINTR {
+ continue
+ }
+ if n == 0 {
+ syscall.Exit(1)
+ }
+ if err != nil {
+ println("failed to read fd =", fd, "errno =", err.(syscall.Errno))
+ syscall.Exit(1)
+ }
+ rd += n
+ }
+ return buf[0], deserialize64(buf[1:])
+}
+
+// write writes little-endian-encoded vals... to fd.
+func write(fd FD, vals ...uint64) {
+ var tmp [3 * 8]byte
+ buf := tmp[:len(vals)*8]
+ for i, v := range vals {
+ serialize64(buf[i*8:], v)
+ }
+ wr := 0
+ for wr != len(buf) {
+ n, err := fd.write(buf[wr:])
+ if err == syscall.EINTR {
+ continue
+ }
+ if err != nil {
+ println("failed to read fd =", fd, "errno =", err.(syscall.Errno))
+ syscall.Exit(1)
+ }
+ wr += n
+ }
+}
+
+// writeStr writes strings s to fd.
+func writeStr(fd FD, s string) {
+ buf := []byte(s)
+ wr := 0
+ for wr != len(buf) {
+ n, err := fd.write(buf[wr:])
+ if err == syscall.EINTR {
+ continue
+ }
+ if err != nil {
+ println("failed to read fd =", fd, "errno =", err.(syscall.Errno))
+ syscall.Exit(1)
+ }
+ wr += n
+ }
+}
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/main_libFuzzer.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/main_libFuzzer.go
new file mode 100644
index 000000000..7a4911788
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/main_libFuzzer.go
@@ -0,0 +1,20 @@
+// Copyright 2015 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
+// +build gofuzz_libfuzzer
+
+package gofuzzdep
+
+import (
+ "unsafe"
+
+ . "github.com/dvyukov/go-fuzz/go-fuzz-defs"
+)
+
+func Initialize(coverTabPtr unsafe.Pointer, coverTabSize uint64) {
+ if coverTabSize != CoverSize {
+ panic("Incorrect cover tab size")
+ }
+ CoverTab = (*[CoverSize]byte)(coverTabPtr)
+}
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sonar.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sonar.go
new file mode 100644
index 000000000..e713c6d05
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sonar.go
@@ -0,0 +1,201 @@
+// Copyright 2015 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 (
+ "sync/atomic"
+ "unsafe"
+
+ . "github.com/dvyukov/go-fuzz/go-fuzz-defs"
+)
+
+var (
+ sonarRegion []byte
+ sonarPos uint32
+)
+
+const failure = ^uint8(0)
+
+type iface struct {
+ typ unsafe.Pointer
+ val unsafe.Pointer
+}
+
+// Sonar is called by instrumentation code to notify go-fuzz about comparisons.
+// Low 8 bits of id are flags, the rest is unique id of a comparison.
+func Sonar(v1, v2 interface{}, id uint32) {
+ var buf [SonarHdrLen + 2*SonarMaxLen]byte
+ n1, f1 := serialize(v1, v2, buf[SonarHdrLen:])
+ if n1 == failure {
+ return
+ }
+ n2, f2 := serialize(v2, v1, buf[SonarHdrLen+n1:])
+ if n2 == failure {
+ return
+ }
+ // Ideal const operands are converted to signed int,
+ // but it does not mean that the comparison is signed
+ // unless the other operand is signed.
+ if id&SonarConst1 != 0 {
+ f1 &^= SonarSigned
+ }
+ if id&SonarConst2 != 0 {
+ f2 &^= SonarSigned
+ }
+ id |= uint32(f1 | f2)
+ serialize32(buf[:], id)
+ buf[4] = n1
+ buf[5] = n2
+ n := uint32(SonarHdrLen + n1 + n2)
+ pos := atomic.LoadUint32(&sonarPos)
+ for {
+ if pos+n > uint32(len(sonarRegion)) {
+ return
+ }
+ if atomic.CompareAndSwapUint32(&sonarPos, pos, pos+n) {
+ break
+ }
+ pos = atomic.LoadUint32(&sonarPos)
+ }
+ copy(sonarRegion[pos:pos+n], buf[:])
+}
+
+func serialize(v, v2 interface{}, buf []byte) (n, flags uint8) {
+ switch vv := v.(type) {
+ case int8:
+ buf[0] = byte(vv)
+ return 1, SonarSigned
+ case uint8:
+ buf[0] = byte(vv)
+ return 1, 0
+ case int16:
+ return serialize16(buf, uint16(vv)), SonarSigned
+ case uint16:
+ return serialize16(buf, vv), 0
+ case int32:
+ return serialize32(buf, uint32(vv)), SonarSigned
+ case uint32:
+ return serialize32(buf, vv), 0
+ case int64:
+ return serialize64(buf, uint64(vv)), SonarSigned
+ case uint64:
+ return serialize64(buf, vv), 0
+ case int:
+ if unsafe.Sizeof(vv) == 4 {
+ return serialize32(buf, uint32(vv)), SonarSigned
+ } else {
+ return serialize64(buf, uint64(vv)), SonarSigned
+ }
+ case uint:
+ if unsafe.Sizeof(vv) == 4 {
+ return serialize32(buf, uint32(vv)), 0
+ } else {
+ return serialize64(buf, uint64(vv)), 0
+ }
+ case string:
+ if len(vv) > SonarMaxLen {
+ return failure, 0
+ }
+ return uint8(copy(buf, vv)), SonarString
+ case [1]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [2]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [3]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [4]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [5]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [6]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [7]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [8]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [9]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [10]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [11]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [12]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [13]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [14]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [15]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [16]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [17]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [18]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [19]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ case [20]byte:
+ return uint8(copy(buf, vv[:])), SonarString
+ default:
+ // Special case: string literal is compared with a variable of
+ // user type with string underlying type:
+ // type Name string
+ // var name Name
+ // if name == "foo" { ... }
+ if _, ok := v2.(string); ok {
+ s := *(*string)((*iface)(unsafe.Pointer(&v)).val)
+ if len(s) <= SonarMaxLen {
+ return uint8(copy(buf[:], s)), SonarString
+ }
+ }
+ return failure, 0
+ }
+}
+
+// The serialization routines here match those of encoding/binary.LittleEndian.
+// They are copied here because importing encoding/binary creates import cycles.
+
+func serialize16(buf []byte, v uint16) uint8 {
+ _ = buf[1]
+ buf[0] = byte(v >> 0)
+ buf[1] = byte(v >> 8)
+ return 2
+}
+
+func serialize32(buf []byte, v uint32) uint8 {
+ _ = buf[3]
+ buf[0] = byte(v >> 0)
+ buf[1] = byte(v >> 8)
+ buf[2] = byte(v >> 16)
+ buf[3] = byte(v >> 24)
+ return 4
+}
+
+func serialize64(buf []byte, v uint64) uint8 {
+ _ = buf[7]
+ buf[0] = byte(v >> 0)
+ buf[1] = byte(v >> 8)
+ buf[2] = byte(v >> 16)
+ buf[3] = byte(v >> 24)
+ buf[4] = byte(v >> 32)
+ buf[5] = byte(v >> 40)
+ buf[6] = byte(v >> 48)
+ buf[7] = byte(v >> 56)
+ return 8
+}
+
+func deserialize64(buf []byte) uint64 {
+ _ = buf[7]
+ return uint64(buf[0])<<0 |
+ uint64(buf[1])<<8 |
+ uint64(buf[2])<<16 |
+ uint64(buf[3])<<24 |
+ uint64(buf[4])<<32 |
+ uint64(buf[5])<<40 |
+ uint64(buf[6])<<48 |
+ uint64(buf[7])<<56
+}
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sys_posix.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sys_posix.go
new file mode 100644
index 000000000..938cf9324
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sys_posix.go
@@ -0,0 +1,32 @@
+// Copyright 2015 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 darwin linux freebsd dragonfly openbsd netbsd
+// +build gofuzz
+
+package gofuzzdep
+
+import (
+ "syscall"
+
+ . "github.com/dvyukov/go-fuzz/go-fuzz-defs"
+)
+
+type FD int
+
+func setupCommFile() ([]byte, FD, FD) {
+ mem, err := syscall.Mmap(3, 0, CoverSize+MaxInputSize+SonarRegionSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
+ if err != nil {
+ println("failed to mmap fd = 3 errno =", err.(syscall.Errno))
+ syscall.Exit(1)
+ }
+ return mem, 4, 5
+}
+
+func (fd FD) read(buf []byte) (int, error) {
+ return syscall.Read(int(fd), buf)
+}
+
+func (fd FD) write(buf []byte) (int, error) {
+ return syscall.Write(int(fd), buf)
+}
diff --git a/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sys_windows.go b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sys_windows.go
new file mode 100644
index 000000000..160301549
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sys_windows.go
@@ -0,0 +1,56 @@
+// Copyright 2015 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 (
+ "syscall"
+ "unsafe"
+
+ . "github.com/dvyukov/go-fuzz/go-fuzz-defs"
+)
+
+// Can't import reflect because of import cycles.
+type sliceHeader struct {
+ addr uintptr
+ l, c int
+}
+
+type FD syscall.Handle
+
+func setupCommFile() ([]byte, FD, FD) {
+ const (
+ size = CoverSize + MaxInputSize + SonarRegionSize
+ FILE_MAP_ALL_ACCESS = 0xF001F
+ )
+ mapping := readEnvParam("GO_FUZZ_COMM_FD")
+ addr, err := syscall.MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, size)
+ if err != nil {
+ println("failed to mmap comm file:", err.Error())
+ syscall.Exit(1)
+ }
+ hdr := sliceHeader{addr, size, size}
+ mem := *(*[]byte)(unsafe.Pointer(&hdr))
+ in := FD(readEnvParam("GO_FUZZ_IN_FD"))
+ out := FD(readEnvParam("GO_FUZZ_OUT_FD"))
+ return mem, in, out
+}
+
+func readEnvParam(name string) syscall.Handle {
+ v, _ := syscall.Getenv(name)
+ var x uintptr
+ for i := 0; i < len(v); i++ {
+ x = x*10 + uintptr(v[i]-'0')
+ }
+ return syscall.Handle(x)
+}
+
+func (fd FD) read(buf []byte) (int, error) {
+ return syscall.Read(syscall.Handle(fd), buf)
+}
+
+func (fd FD) write(buf []byte) (int, error) {
+ return syscall.Write(syscall.Handle(fd), buf)
+}
diff --git a/vendor/github.com/dvyukov/go-fuzz/internal/go-fuzz-types/types.go b/vendor/github.com/dvyukov/go-fuzz/internal/go-fuzz-types/types.go
new file mode 100644
index 000000000..360481ff2
--- /dev/null
+++ b/vendor/github.com/dvyukov/go-fuzz/internal/go-fuzz-types/types.go
@@ -0,0 +1,28 @@
+// Copyright 2015 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.
+
+// Package types provides types shared between go-fuzz-build and go-fuzz.
+package types
+
+type CoverBlock struct {
+ ID int
+ File string
+ StartLine int
+ StartCol int
+ EndLine int
+ EndCol int
+ NumStmt int
+}
+
+type Literal struct {
+ Val string
+ IsStr bool
+}
+
+type MetaData struct {
+ Literals []Literal
+ Blocks []CoverBlock
+ Sonar []CoverBlock
+ Funcs []string // fuzz function names; must have length > 0
+ DefaultFunc string // default function to fuzz
+}