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
This commit is contained in:
Dmitry Vyukov 2020-07-04 12:41:40 +02:00
parent c7d7f10bdf
commit 74cb4e09a5
19 changed files with 2483 additions and 7 deletions

View File

@ -401,8 +401,7 @@ install_prerequisites:
sudo apt-get install -y -q g++-s390x-linux-gnu || true
sudo apt-get install -y -q g++-riscv64-linux-gnu || true
sudo apt-get install -y -q ragel clang-format
go get -u golang.org/x/tools/cmd/goyacc \
github.com/dvyukov/go-fuzz/go-fuzz-build
go get -u golang.org/x/tools/cmd/goyacc
check_copyright:
./tools/check-copyright.sh

1
go.mod
View File

@ -6,6 +6,7 @@ require (
cloud.google.com/go v0.60.0 // indirect
cloud.google.com/go/storage v1.10.0
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.4.2 // indirect
github.com/golangci/golangci-lint v1.27.0

2
go.sum
View File

@ -71,6 +71,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813 h1:NgO45/5mBLRVfiXerEFzH6ikcZ7DNRPS639xFg3ENzU=
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=

View File

@ -9,5 +9,7 @@
package tools
import (
_ "github.com/dvyukov/go-fuzz/go-fuzz-build"
_ "github.com/dvyukov/go-fuzz/go-fuzz-dep"
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
)

View File

@ -2,7 +2,7 @@
# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
# The image provides dev environment suitable for syzkaller development/testing.
# It includes Go toolchain, C/C++ cross-compilers and go-fuzz.
# It includes Go toolchain and C/C++ cross-compilers.
# The image is available as gcr.io/syzkaller/env.
@ -36,9 +36,6 @@ RUN curl https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz | tar -C /usr/loca
ENV PATH /usr/local/go/bin:/gopath/bin:$PATH
ENV GOPATH /gopath
# For go-fuzz we also need sources (go-fuzz-dep).
RUN go get github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
# Pre-create dirs for syz-dock.
# This is necessary to make docker work with the current user,
# otherwise --volume will create these dirs under root and then

View File

@ -7,13 +7,16 @@
set -eux
export TYPE="${1}"
export GOBIN=$(realpath .)/bin
function target {
go-fuzz-build -libfuzzer -func $3 -o fuzzer.a $2
bin/go-fuzz-build -libfuzzer -func $3 -o fuzzer.a $2
clang -fsanitize=fuzzer fuzzer.a -o fuzzer
./fuzzit create job --type "${TYPE}" --branch ${GITHUB_REF#refs/heads/} --revision ${GITHUB_SHA} syzkaller/$1 ./fuzzer
}
go install github.com/dvyukov/go-fuzz/go-fuzz-build
curl -L --output fuzzit https://github.com/fuzzitdev/fuzzit/releases/download/v2.4.77/fuzzit_Linux_x86_64
chmod a+x fuzzit

174
vendor/github.com/dvyukov/go-fuzz/LICENSE generated vendored Normal file
View File

@ -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.

View File

@ -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
}

882
vendor/github.com/dvyukov/go-fuzz/go-fuzz-build/main.go generated vendored Normal file
View File

@ -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() {
}
`))

36
vendor/github.com/dvyukov/go-fuzz/go-fuzz-defs/defs.go generated vendored Normal file
View File

@ -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
)

22
vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/cover.go generated vendored Normal file
View File

@ -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)

10
vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/doc.go generated vendored Normal file
View File

@ -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

99
vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/main.go generated vendored Normal file
View File

@ -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
}
}

View File

@ -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)
}

201
vendor/github.com/dvyukov/go-fuzz/go-fuzz-dep/sonar.go generated vendored Normal file
View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

6
vendor/modules.txt vendored
View File

@ -21,6 +21,12 @@ github.com/OpenPeeDeeP/depguard
github.com/bombsimon/wsl/v3
# github.com/davecgh/go-spew v1.1.1
github.com/davecgh/go-spew/spew
# github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813
## explicit
github.com/dvyukov/go-fuzz/go-fuzz-build
github.com/dvyukov/go-fuzz/go-fuzz-defs
github.com/dvyukov/go-fuzz/go-fuzz-dep
github.com/dvyukov/go-fuzz/internal/go-fuzz-types
# github.com/fatih/color v1.7.0
github.com/fatih/color
# github.com/fsnotify/fsnotify v1.4.7