]> Cypherpunks.ru repositories - gostls13.git/commitdiff
[dev.fuzz] all: merge master (7764ee5) into dev.fuzz
authorKatie Hockman <katie@golang.org>
Fri, 19 Feb 2021 14:18:36 +0000 (09:18 -0500)
committerKatie Hockman <katie@golang.org>
Fri, 19 Feb 2021 14:18:45 +0000 (09:18 -0500)
Conflicts:

- api/next.txt

Merge List:

+ 2021-02-19 7764ee5614 runtime: fix invalid nil g check for for mips64x
+ 2021-02-19 87f425da14 cmd/go/internal/mvs: split Reqs into narrower per-function interfaces
+ 2021-02-19 4da0188c6c cmd/go/internal/modget: split resolveCandidates into two methods
+ 2021-02-19 5f2e24efb3 cmd/internal/diff: skip over Cygwin warning in diff output
+ 2021-02-19 ee7038f6a5 net: disable Windows netsh tests when netsh won't run
+ 2021-02-19 40765ffa95 os/exec: disable failing LookPathTest on windows/arm64
+ 2021-02-19 b445d6ea34 runtime/pprof: expect tests to pass on macOS
+ 2021-02-19 b110a43628 runtime: delete gosave (dead code)
+ 2021-02-19 474d5f4f4d math: remove most 387 implementations
+ 2021-02-19 c7c6c113be runtime: convert windows/arm64 assembly
+ 2021-02-19 3527caa7d6 runtime: initial windows/arm64 implementation files
+ 2021-02-19 427bd7599d runtime: generate windows/arm64 callback asm
+ 2021-02-19 f6c4b4bf96 syscall: add windows/arm64 support
+ 2021-02-19 ac024a0c7b cmd/vendor: get golang.org/x/sys@beda7e5e158
+ 2021-02-19 a3b97e7628 test: disable nilptr on windows/arm64
+ 2021-02-19 985d087782 cmd/link: add windows/arm64 support
+ 2021-02-19 95a44d2409 cmd/internal/objfile: recognize Windows ARM64 executables
+ 2021-02-19 0ca0551f02 debug/pe: recognize arm64 executables
+ 2021-02-19 0c633125f2 cmd/dist: add windows/arm64 support
+ 2021-02-19 bb6efb9609 build: set GOPATH consistently in run.bash, run.bat, run.rc
+ 2021-02-19 a1222b7535 cmd/link: add debug print in deadcode
+ 2021-02-19 1c659f2525 cmd/link: clean up windows PE generation
+ 2021-02-19 b6379f190b syscall: clean up windows a bit
+ 2021-02-19 09e059afb1 runtime: enable framepointer on all arm64
+ 2021-02-19 b19e7b518e runtime: clean up windows a bit
+ 2021-02-19 5421c37a1d runtime: fix windows/arm externalthreadhandler
+ 2021-02-19 91cc484ea9 runtime: fix time on windows/arm under WINE
+ 2021-02-19 38672d3dcf runtime: crash earlier on windows for runtime.abort
+ 2021-02-19 a1e9148e3d runtime: print hex numbers with hex prefixes in traceback debug
+ 2021-02-19 75e273fc2c runtime: fix windows/arm CONTEXT_CONTROL
+ 2021-02-19 76ab626bfc runtime: factor common code out of defs_windows_*.go
+ 2021-02-19 ece954d8b8 runtime: find g in Windows profiler using SP
+ 2021-02-19 a54f7fc0fd runtime: do not treat asmcgocall as a topofstack on g0
+ 2021-02-19 776ee4079a runtime: do not treat morestack as a topofstack
+ 2021-02-19 5ecd9e34df runtime: do not treat mcall as a topofstack
+ 2021-02-19 54da3ab385 runtime: use TOPFRAME to identify top-of-frame functions
+ 2021-02-19 fbe74dbf42 runtime: use FuncInfo SPWRITE flag to identify untraceable profile samples
+ 2021-02-19 4dd77bdc91 cmd/asm, cmd/link, runtime: introduce FuncInfo flag bits
+ 2021-02-19 aa0388f2ed runtime: remove unnecessary writes to gp.sched.g
+ 2021-02-19 6fe8981620 cmd/internal/obj/riscv: fix JMP name<>(SB)
+ 2021-02-19 01f05d8ff1 runtime: unify asmcgocall and systemstack traceback setup
+ 2021-02-19 229695a283 runtime: clean up funcID assignment
+ 2021-02-19 c80da0a33a runtime: handle nil gp in cpuprof
+ 2021-02-19 a78879ac67 runtime: move sys.DefaultGoroot to runtime.defaultGOROOT
+ 2021-02-19 8ac23a1f15 runtime: document, clean up internal/sys
+ 2021-02-19 678568a5cf runtime: delete windows setlasterror (unused)
+ 2021-02-19 0d94f989d1 runtime: clean up system calls during cgo callback init
+ 2021-02-19 e7ee3c1fa8 os: report Windows exit status in hex
+ 2021-02-18 eb982727e3 cmd/go/internal/mvs: fix Downgrade to match Algorithm 4
+ 2021-02-18 3b7277d365 cmd/go: add a script test for artifacts resulting from 'go get -u'
+ 2021-02-18 f3c2208e2c cmd/go: add script tests for potential upgrades due to downgrades
+ 2021-02-18 a5c8a15f64 cmd/go/internal/mvs: clarify and annotate test cases
+ 2021-02-18 a76efea1fe cmd/go/internal/mvs: don't emit duplicates from Req
+ 2021-02-18 609d82b289 cmd/dist: set GOARM=7 for windows/arm
+ 2021-02-18 f0be3cc547 runtime: unbreak linux/riscv64 following regabi merge
+ 2021-02-18 07ef313525 runtime/cgo: add cast in C code to avoid C compiler warning
+ 2021-02-17 2f0da6d9e2 go/types: revert "no 'declared but not used' errors for invalid var decls"
+ 2021-02-17 70c37ee7d0 cmd/compile/internal/test: gofmt abiutils_test.go
+ 2021-02-16 84825599dc all: merge branch dev.regabi (d3cd4830ad) into master
+ 2021-02-16 d3cd4830ad [dev.regabi] test: run abi/regabipragma test with -c=1
+ 2021-02-16 03cea563d1 [dev.regabi] all: merge master (5faf941) into dev.regabi
+ 2021-02-16 b8fb049c7a [dev.regabi] cmd/go: copy internal/abi in TestNewReleaseRebuildsStalePackagesInGOPATH
+ 2021-02-16 5faf941df0 internal/goversion: update Version to 1.17
+ 2021-02-16 ed55da46ab [dev.regabi] go/types: overlapping embedded interfaces requires go1.14
+ 2021-02-16 7696c94334 [dev.regabi] go/types: type alias decl requires go1.9
+ 2021-02-16 c2358a1ae7 [dev.regabi] runtime: stub out spillArgs and unspillArgs
+ 2021-02-16 8cfbf34dd9 internal/abi: set register count constants to zero for regabi experiment
+ 2021-02-16 6f3da9d2f6 README: pull gopher image from website
+ 2021-02-16 d28aae26b0 [dev.regabi] cmd/link: recognize internal/abi as runtime package
+ 2021-02-16 098504c73f cmd/link: generate trampoline for inter-dependent packages
+ 2021-02-16 1004a7cb31 runtime/metrics: update documentation to current interface
+ 2021-02-16 6530f2617f doc/go1.16: remove draft notice
+ 2021-02-16 353e111455 doc/go1.16: fix mismatched id attribute
+ 2021-02-16 e0215315f5 [dev.regabi] reflect: support for register ABI on amd64 for reflect.(Value).Call
+ 2021-02-16 f0d23c9dbb internal/poll: netpollcheckerr before sendfile
+ 2021-02-16 0cb3415154 doc: remove all docs not tied to distribution
+ 2021-02-16 626ef08127 doc: remove install.html and install-source.html
+ 2021-02-16 30641e36aa internal/poll: if copy_file_range returns 0, assume it failed
+ 2021-02-15 33d72fd412 doc/faq: update generics entry to reflect accepted proposal
+ 2021-02-15 852ce7c212 cmd/go: provide a more helpful suggestion for "go vet -?"
+ 2021-02-13 66c27093d0 cmd/link: fix typo in link_test.go
+ 2021-02-13 b81efb7ec4 [dev.regabi] go/types: add support for language version checking
+ 2021-02-13 a7e9b4b948 [dev.regabi] go/types: untyped shift counts must fit into uint
+ 2021-02-13 060fa49bd2 [dev.regabi] go/types: refuse excessively long constants
+ 2021-02-12 baa6c75dce [dev.regabi] internal/abi: add new internal/abi package for ABI constants
+ 2021-02-12 d1fd9a8863 [dev.regabi] all: merge master (ff0e93e) into dev.regabi
+ 2021-02-12 ff0e93ea31 doc/go1.16: note that package path elements beginning with '.' are disallowed
+ 2021-02-11 249da7ec02 CONTRIBUTORS: update for the Go 1.16 release
+ 2021-02-11 864d4f1c6b cmd/go: multiple small 'go help' fixes
+ 2021-02-11 26ceae85a8 spec: More precise wording in section on function calls.
+ 2021-02-11 930c2c9a68 cmd/go: reject embedded files that can't be packed into modules
+ 2021-02-11 e5b08e6d5c io/fs: allow backslash in ValidPath, reject in os.DirFS.Open
+ 2021-02-10 ed8079096f cmd/compile: mark concrete call of reflect.(*rtype).Method as REFLECTMETHOD
+ 2021-02-10 59703d53e2 [dev.regabi] cmd/link: stop using ABI aliases if wrapper is enabled
+ 2021-02-09 e9c9683597 cmd/go: suppress errors from 'go get -d' for packages that only conditionally exist
+ 2021-02-09 168d6a49a5 [dev.regabi] go/types: use 512 bits as max. integer precision
+ 2021-02-09 0a62067708 [dev.regabi] go/types: adjust importer to match compiler importer
+ 2021-02-09 1c58fcf7ed [dev.regabi] go/types: handle untyped constant arithmetic overflow
+ 2021-02-09 493363ccff [dev.regabi] go/types: must not import a package called "init"
+ 2021-02-09 e0ac989cf3 archive/tar: detect out of bounds accesses in PAX records resulting from padded lengths
+ 2021-02-09 c48d1503ba [dev.regabi] go/types: report unused packages in source order
+ 2021-02-09 813958f13c [dev.regabi] go/types: factor out sorting of methods
+ 2021-02-09 11d15c171b [dev.regabi] go/types: convert untyped arguments to delete
+ 2021-02-09 c9d6f45fec runtime/metrics: fix a couple of documentation typpos
+ 2021-02-09 cea4e21b52 io/fs: backslash is always a glob meta character
+ 2021-02-08 dc725bfb3c doc/go1.16: mention new vet check for asn1.Unmarshal
+ 2021-02-08 618e3c15bd [dev.regabi] go/types: consistently report nil type as "untyped nil"
+ 2021-02-08 50449de66a [dev.regabi] all: merge master (1901853) into dev.regabi
+ 2021-02-08 7b0dfb177f [dev.regabi] runtime: use g register in some assembly functions on AMD64
+ 2021-02-08 2e60c00f56 [dev.regabi] cmd/internal/obj/x86: use g register in stack bounds check
+ 2021-02-08 22f9e1ccbc [dev.regabi] runtime: initialize special registers before sigpanic
+ 2021-02-08 5d7dc53888 [dev.regabi] cmd/compile, runtime: reserve R14 as g registers on AMD64
+ 2021-02-08 1901853098 runtime/metrics: fix panic in readingAllMetric example
+ 2021-02-08 ed3e4afa12 syscall/plan9: remove spooky fd action at a distance
+ 2021-02-08 a21de9ec73 [dev.regabi] cmd/link: resolve symbol ABI in shared linkage
+ 2021-02-05 724d0720b3 doc/go1.16: add missed heading tag in vet section
+ 2021-02-05 b54cd94d47 embed, io/fs: clarify that leading and trailing slashes are disallowed
+ 2021-02-05 4516afebed testing/fstest: avoid symlink-induced failures in tester
+ 2021-02-05 8fa84772ba [dev.regabi] runtime: delete gosave function
+ 2021-02-05 946351d5a2 [dev.regabi] runtime: zero X15 in racecall
+ 2021-02-05 397a46a10a [dev.regabi] cmd/asm: define g register on AMD64
+ 2021-02-05 e79c2fd428 [dev.regabi] runtime: mark racecallbackthunk as ABIInternal
+ 2021-02-05 7cc6de59f2 [dev.regabi] runtime: don't mark rt0_go ABIInternal
+ 2021-02-05 63de211014 [dev.regabi] runtime/cgo: call setg_gcc in crosscall_amd64
+ 2021-02-04 120b819f45 [dev.regabi] go/types: report error for invalid main function signature
+ 2021-02-04 52d5cb2822 [dev.regabi] cmd/internal/obj: access Attribute atomically
+ 2021-02-04 bc451b5770 [dev.regabi] go/types: port check_test.go ergonomics from dev.typeparams
+ 2021-02-04 afd67f3334 [dev.regabi] go/types: no "declared but not used" errors for invalid var decls
+ 2021-02-04 8869086d8f runtime: fix typo in histogram.go
+ 2021-02-03 401d7e5a24 [dev.regabi] cmd/compile: reserve X15 as zero register on AMD64
+ 2021-02-03 bfc7418e6d [dev.regabi] runtime, syscall, etc.: mark Darwin syscall wrappers as ABIInternal
+ 2021-02-03 e491c6eea9 math/big: fix comment in divRecursiveStep
+ 2021-02-02 23b0c1f76e [dev.regabi] all: merge master (fca94ab) into dev.regabi
+ 2021-02-02 fca94ab3ab spec: improve the example in Type assertions section
+ 2021-02-02 98f8454a73 cmd/link: don't decode type symbol in shared library in deadcode
+ 2021-02-02 1426a571b7 cmd/link: fix off-by-1 error in findShlibSection
+ 2021-02-01 32e789f4fb test: fix incorrectly laid out instructions in issue11656.go
+ 2021-02-01 ca6999e27c [dev.regabi] test: add a test for inlining closures
+ 2021-02-01 0b6cfea634 doc/go1.16: document that on OpenBSD syscalls are now made through libc
+ 2021-02-01 26e29aa15a cmd/link: disable TestPIESize if CGO isn't enabled
+ 2021-02-01 6ac91e460c doc/go1.16: minor markup fixes
+ 2021-01-29 44361140c0 embed: update docs for proposal tweaks
+ 2021-01-29 68058edc39 runtime: document pointer write atomicity for memclrNoHeapPointers
+ 2021-01-28 c8bd8010ff syscall: generate readlen/writelen for openbsd libc
+ 2021-01-28 41bb49b878 cmd/go: revert TestScript/build_trimpath to use ioutil.ReadFile
+ 2021-01-28 725a642c2d runtime: correct syscall10/syscall10X on openbsd/amd64
+ 2021-01-28 4b068cafb5 doc/go1.16: document go/build/constraint package
+ 2021-01-28 376518d77f runtime,syscall: convert syscall on openbsd/arm64 to libc
+ 2021-01-27 aca22bddf2 [dev.regabi] cmd/compile: remove nested functions from expands_calls.go
+ 2021-01-27 667e08ba8c [dev.regabi] cmd/go: Use GOMAXPROCS to limit default build, compile parallelism
+ 2021-01-27 00f2ff5c94 api/go1.16: add go/build/constraint APIs
+ 2021-01-27 35334caf18 crypto/x509: remove leftover CertificateRequest field
+ 2021-01-27 a5a5e2c968 runtime: make sure to remove open-coded defer entries in all cases after a recover
+ 2021-01-27 8cfa01943a runtime: block console ctrlhandler when the signal is handled
+ 2021-01-27 ff9e8364c6 cmd/go: skip issue33139 when the 'cc' script command is unavailable
+ 2021-01-27 cd176b3615 runtime: switch runtime to libc for openbsd/arm64
+ 2021-01-27 6c8fbfbdcf runtime: convert openbsd/arm64 locking to libc
+ 2021-01-27 5cdf0da1bf syscall: clean up mkasm related changes
+ 2021-01-27 210f70e298 doc/go1.16: fix closing brace in .Export format
+ 2021-01-27 0f797f168d math: fix typo in sqrt.go code comment
+ 2021-01-26 9b636feafe [dev.regabi] cmd/compile: missing last patch set for cl286013
+ 2021-01-26 f7dad5eae4 [dev.regabi] cmd/compile: remove leftover code form late call lowering work
+ 2021-01-26 8634a234df runtime,syscall: convert syscall on openbsd/amd64 to libc
+ 2021-01-26 1d5e14632e os: further document limitations around naked file descriptors
+ 2021-01-25 5e4a0cdde3 [dev.regabi] all: merge master (bf0f7c9) into dev.regabi
+ 2021-01-26 cf263e9f77 os: correct names in CreateTemp and MkdirTemp doc comments
+ 2021-01-26 ce8b318624 net/http/fcgi: remove locking added to prevent a test-only race
+ 2021-01-25 bf0f7c9d78 doc/go1.16: mention os.DirFS in os section
+ 2021-01-25 deaf29a8a8 cmd/compile: fix order-of-assignment issue w/ defers
+ 2021-01-25 ad2ca26a52 doc/go1.16: mention os.DirEntry and types moved from os to io/fs
+ 2021-01-25 a51921fa5b doc/go1.16: mention new testing/iotest functions
+ 2021-01-25 e6b6d107f7 doc/go1.16: mention deprecation of io/ioutil
+ 2021-01-25 7eaaf28cae [dev.regabi] cmd/compile: disallow taking address of SSA'd values
+ 2021-01-25 96a276363b doc/go1.16: mention go/build changes
+ 2021-01-25 3d85c69a0b html/template: revert "avoid race when escaping updates template"
+ 2021-01-25 54514c6b28 cmd/go: fix TestScript/cgo_path, cgo_path_space when CC set
+ 2021-01-25 6f5e79f470 [dev.regabi] cmd/compile/internal: specify memory layout
+ 2021-01-25 cabffc199d [dev.regabi] cmd/compile/internal: add internal ABI specification
+ 2021-01-25 6de8443f3b doc/asm: add a section on go_asm.h, clean up go_tls.h section
+ 2021-01-25 6a4739ccc5 [dev.regabi] cmd/compile: enable rational constant arithmetic
+ 2021-01-25 be9612a832 [dev.regabi] os: disable TestDirFS until #42637 is fixed
+ 2021-01-25 8ee3d39838 [dev.regabi] cmd/go: workaround -race issue on ppc64le
+ 2021-01-25 54b251f542 lib/time, time/tzdata: update tzdata to 2021a
+ 2021-01-25 5a76c3d548 [dev.regabi] cmd/compile: modify abiutils for recently updated ABI
+ 2021-01-25 ff82cc971a os: force consistent mtime before running fstest on directory on Windows
+ 2021-01-25 044f937a73 doc/go1.16: fix WalkDir and Walk links
+ 2021-01-25 063c72f06d [dev.regabi] cmd/compile: backport changes from dev.typeparams (9456804)
+ 2021-01-23 b634f5d97a doc/go1.16: add crypto/x509 memory optimization
+ 2021-01-23 9897655c61 doc/go1.16: reword ambiguously parsable sentence
+ 2021-01-23 cd99385ff4 cmd/internal/obj/arm64: fix VMOVQ instruction encoding error
+ 2021-01-23 d05d6fab32 [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet for SSA 2
+ 2021-01-23 66ee8b158f runtime: restore cgo_import_dynamic for libc.so on openbsd
+ 2021-01-23 48badc5fa8 [dev.regabi] cmd/compile: fix escape analysis problem with closures
+ 2021-01-23 51e1819a8d [dev.regabi] cmd/compile: scan body of closure in tooHairy to check for disallowed nodes
+ 2021-01-22 25c39e4fb5 io/ioutil: fix example test for WriteFile to allow it to run in the playground
+ 2021-01-22 eb21b31e48 runtime: define dummy msanmove
+ 2021-01-22 3a778ff50f runtime: check for g0 stack last in signal handler
+ 2021-01-22 a2cef9b544 cmd/go: don't lookup the path for CC when invoking cgo
+ 2021-01-22 7e0a81d280 [dev.regabi] all: merge master (dab3e5a) into dev.regabi
+ 2021-01-22 dab3e5affe runtime: switch runtime to libc for openbsd/amd64
+ 2021-01-22 a1b53d85da cmd/go: add documentation for test and xtest fields output by go list
+ 2021-01-22 b268b60774 runtime: remove pthread_kill/pthread_self for openbsd
+ 2021-01-22 ec4051763d runtime: fix typo in mgcscavenge.go
+ 2021-01-22 7ece3a7b17 net/http: fix flaky TestDisableKeepAliveUpgrade
+ 2021-01-22 50cba0506f time: clarify Timer.Reset behavior on AfterFunc Timers
+ 2021-01-22 cf10e69f17 doc/go1.16: mention net/http.Transport.GetProxyConnectHeader
+ 2021-01-22 ec1b945265 doc/go1.16: mention path/filepath.WalkDir
+ 2021-01-22 11def3d40b doc/go1.16: mention syscall.AllThreadsSyscall
+ 2021-01-21 07b0235609 doc/go1.16: add notes about package-specific fs.FS changes
+ 2021-01-21 e2b4f1fea5 doc/go1.16: minor formatting fix
+ 2021-01-21 9f43a9e07b doc/go1.16: mention new debug/elf constants
+ 2021-01-21 3c2f11ba5b cmd/go: overwrite program name with full path
+ 2021-01-21 953d1feca9 all: introduce and use internal/execabs
+ 2021-01-21 b186e4d70d cmd/go: add test case for cgo CC setting
+ 2021-01-21 5a8a2265fb cmd/cgo: report exec errors a bit more clearly
+ 2021-01-21 46e2e2e9d9 cmd/go: pass resolved CC, GCCGO to cgo
+ 2021-01-21 3d40895e36 runtime: switch openbsd/arm64 to pthreads
+ 2021-01-21 d95ca91380 crypto/elliptic: fix P-224 field reduction
+ 2021-01-21 d7e71c01ad [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet for dwarf
+ 2021-01-21 5248f59a22 [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet for SSA
+ 2021-01-21 970d8b6cb2 [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet in inlining
+ 2021-01-21 68a4664475 [dev.regabi] cmd/compile: remove tempAssigns in walkCall1
+ 2021-01-21 fd9a391cdd [dev.regabi] cmd/compile: remove CallExpr.Rargs
+ 2021-01-21 19a6db6b63 [dev.regabi] cmd/compile: make sure mkcall* passed non-nil init
+ 2021-01-21 9f036844db [dev.regabi] cmd/compile: use ir.DoChildren directly in inlining
+ 2021-01-21 213c3905e9 [dev.regabi] cmd/compile: use node walked flag to prevent double walk for walkSelect
+ 2021-01-20 1760d736f6 [dev.regabi] cmd/compile: exporting, importing, and inlining functions with OCLOSURE
+ 2021-01-20 ecf4ebf100 cmd/internal/moddeps: check content of all modules in GOROOT
+ 2021-01-20 92cb157cf3 [dev.regabi] cmd/compile: late expansion of return values
+ 2021-01-20 d2d155d1ae runtime: don't adjust timer pp field in timerWaiting status
+ 2021-01-20 803d18fc6c cmd/go: set Incomplete field on go list output if no files match embed
+ 2021-01-20 6e243ce71d cmd/go: have go mod vendor copy embedded files in subdirs
+ 2021-01-20 be28e5abc5 cmd/go: fix mod_get_fallback test
+ 2021-01-20 928bda4f4a runtime: convert openbsd/amd64 locking to libc
+ 2021-01-19 824f2d635c cmd/go: allow go fmt to complete when embedded file is missing
+ 2021-01-19 0575e35e50 cmd/compile: require 'go 1.16' go.mod line for //go:embed
+ 2021-01-19 9423d50d53 [dev.regabi] cmd/compile: use '%q' for printing rune values less than 128
+ 2021-01-19 ccb2e90688 cmd/link: exit before Asmb2 if error
+ 2021-01-19 ca5774a5a5 embed: treat uninitialized FS as empty
+ 2021-01-19 d047c91a6c cmd/link,runtime: switch openbsd/amd64 to pthreads
+ 2021-01-19 61debffd97 runtime: factor out usesLibcall
+ 2021-01-19 9fed39d281 runtime: factor out mStackIsSystemAllocated
+ 2021-01-19 a2f825c542 [dev.regabi] cmd/compile: directly create go.map and go.track symbols
+ 2021-01-19 4a4212c0e5 [dev.regabi] cmd/compile: refactor Linksym creation
+ 2021-01-19 4f5c603c0f [dev.regabi] cmd/compile: cleanup callTargetLSym
+ 2021-01-18 dbab079835 runtime: free Windows event handles after last lock is dropped
+ 2021-01-18 5a8fbb0d2d os: do not close syscall.Stdin in TestReadStdin
+ 2021-01-18 422f38fb6c [dev.regabi] cmd/compile: move stack objects to liveness
+ 2021-01-18 6113db0bb4 [dev.regabi] cmd/compile: convert OPANIC argument to interface{} during typecheck
+ 2021-01-18 4c835f9169 [dev.regabi] cmd/compile: use LinksymOffsetExpr in TypePtr/ItabAddr
+ 2021-01-18 0ffa1ead6e [dev.regabi] cmd/compile: use *obj.LSym instead of *ir.Name for staticdata functions
+ 2021-01-17 7e0fa38aad [dev.regabi] cmd/compile: remove unneeded packages from ir.Pkgs
+ 2021-01-17 99a5db11ac [dev.regabi] cmd/compile: use LinksymOffsetExpr in walkConvInterface
+ 2021-01-17 87845d14f9 [dev.regabi] cmd/compile: add ir.TailCallStmt
+ 2021-01-17 e3027c6828 [dev.regabi] cmd/compile: fix linux-amd64-noopt builder
+ 2021-01-17 59ff93fe64 [dev.regabi] cmd/compile: rename NameOffsetExpr to LinksymOffsetExpr
+ 2021-01-17 82b9cae700 [dev.regabi] cmd/compile: change ir.NameOffsetExpr to use *obj.LSym instead of *Name
+ 2021-01-17 88956fc4b1 [dev.regabi] cmd/compile: stop analyze NameOffsetExpr.Name_ in escape analysis
+ 2021-01-17 7ce2a8383d [dev.regabi] cmd/compile: simplify stack temp initialization
+ 2021-01-17 ba0e8a92fa [dev.regabi] cmd/compile: refactor temp construction in walk
+ 2021-01-17 78e5aabcdb [dev.regabi] cmd/compile: replace Node.HasCall with walk.mayCall
+ 2021-01-16 6de9423445 [dev.regabi] cmd/compile: cleanup OAS2FUNC ordering
+ 2021-01-16 a956a0e909 [dev.regabi] cmd/compile, runtime: fix up comments/error messages from recent renames
+ 2021-01-16 ab3b67abfd [dev.regabi] cmd/compile: remove ONEWOBJ
+ 2021-01-16 c9b1445ac8 [dev.regabi] cmd/compile: remove TypeAssertExpr {Src,Dst}Type fields
+ 2021-01-15 682a1d2176 runtime: detect errors in DuplicateHandle
+ 2021-01-15 9f83418b83 cmd/link: remove GOROOT write in TestBuildForTvOS
+ 2021-01-15 ec9470162f cmd/compile: allow embed into any string or byte slice type
+ 2021-01-15 54198b04db cmd/compile: disallow embed of var inside func
+ 2021-01-15 b386c735e7 cmd/go: fix go generate docs
+ 2021-01-15 bb5075a525 syscall: remove RtlGenRandom and move it into internal/syscall
+ 2021-01-15 1deae0b597 os: invoke processKiller synchronously in testKillProcess
+ 2021-01-15 03a875137f [dev.regabi] cmd/compile: unexport reflectdata.WriteType
+ 2021-01-15 14537e6e54 [dev.regabi] cmd/compile: move stkobj symbol generation to SSA
+ 2021-01-15 ab523fc510 [dev.regabi] cmd/compile: don't promote Byval CaptureVars if Addrtaken
+ 2021-01-15 ff196c3e84 crypto/x509: update iOS bundled roots to version 55188.40.9
+ 2021-01-15 b7a698c73f [dev.regabi] test: disable test on windows because expected contains path separators.
+ 2021-01-15 4be7af23f9 [dev.regabi] cmd/compile: fix ICE during ir.Dump
+ 2021-01-14 e125ccd10e cmd/go: in 'go mod edit', validate versions given to -retract and -exclude
+ 2021-01-14 eb330020dc cmd/dist, cmd/go: pass -arch for C compilation on Darwin
+ 2021-01-14 84e8a06f62 cmd/cgo: remove unnecessary space in cgo export header
+ 2021-01-14 0c86b999c3 cmd/test2json: document passing -test.paniconexit0
+ 2021-01-14 9135795891 cmd/go/internal/load: report positions for embed errors
+ 2021-01-14 35b9c66601 [dev.regabi] cmd/compile,cmd/link: additional code review suggestions for CL 270863
+ 2021-01-14 d9b79e53bb cmd/compile: fix wrong complement for arm64 floating-point comparisons
+ 2021-01-14 c73232d08f cmd/go/internal/load: refactor setErrorPos to PackageError.setPos
+ 2021-01-14 6aa28d3e06 go/build: report positions for go:embed directives
+ 2021-01-14 9734fd482d [dev.regabi] cmd/compile: use node walked flag to prevent double walk for walkSwitch
+ 2021-01-14 f97983249a [dev.regabi] cmd/compile: move more PAUTOHEAP to SSA construction
+ 2021-01-14 4476300425 [dev.regabi] cmd/compile: use byte for CallExpr.Use
+ 2021-01-14 5a5ab24689 [dev.regabi] cmd/compile: do not rely on CallExpr.Rargs for detect already walked calls
+ 2021-01-14 983ac4b086 [dev.regabi] cmd/compile: fix ICE when initializing blank vars
+ 2021-01-13 7eb31d999c cmd/go: add hints to more missing sum error messages
+ 2021-01-13 d6d4673728 [dev.regabi] cmd/compile: fix GOEXPERIMENT=regabi builder
+ 2021-01-13 c41b999ad4 [dev.regabi] cmd/compile: refactor abiutils from "gc" into new "abi"
+ 2021-01-13 861707a8c8 [dev.regabi] cmd/compile: added limited //go:registerparams pragma for new ABI dev
+ 2021-01-13 c1370e918f [dev.regabi] cmd/compile: add code to support register ABI spills around morestack calls
+ 2021-01-13 2abd24f3b7 [dev.regabi] test: make run.go error messages slightly more informative
+ 2021-01-13 9a19481acb [dev.regabi] cmd/compile: make ordering for InvertFlags more stable
+ 2021-01-12 d9acf6f3a3 [dev.regabi] cmd/compile: remove Func.ClosureType
+ 2021-01-12 41352fd401 [dev.regabi] cmd/compile: transform closures during walk
+ 2021-01-12 d6ad88b4db [dev.regabi] cmd/compile: compile functions before closures
+ 2021-01-12 432f9ffb11 [dev.regabi] cmd/compile: unindent compileFunctions
+ 2021-01-12 cc90e7a51e [dev.regabi] cmd/compile: always use the compile queue
+ 2021-01-12 cd5b74d2df [dev.regabi] cmd/compile: call NeedFuncSym in InitLSym
+ 2021-01-12 ba76567bc2 cmd/go/internal/modload: delete unused *mvsReqs.next method
+ 2021-01-12 665def2c11 encoding/asn1: document unmarshaling behavior for IMPLICIT string fields
+ 2021-01-12 95acd8121b [dev.regabi] cmd/compile: remove Name.Typegen
+ 2021-01-12 12ee55ba7b [dev.regabi] cmd/compile: stop using Vargen for import/export
+ 2021-01-12 b4d2a0445b [dev.regabi] cmd/compile: refactor closure var setup/teardown
+ 2021-01-12 f57f484053 [dev.regabi] cmd/compile: decouple escape analysis from Name.Vargen
+ 2021-01-11 81ea89adf3 cmd/go: fix non-script staleness checks interacting badly with GOFLAGS
+ 2021-01-11 759309029f doc: update editors.html for Go 1.16
+ 2021-01-11 c3b4c7093a cmd/internal/objfile: don't require runtime.symtab symbol for XCOFF
+ 2021-01-10 7fd84c6e46 [dev.regabi] cmd/compile: remove OCLOSUREREAD
+ 2021-01-10 c9c26d7ffb [dev.regabi] cmd/compile: use ClosureVars for method value wrappers
+ 2021-01-10 950cf4d46c [dev.regabi] cmd/compile: bind closure vars during SSA constructions
+ 2021-01-10 8b2efa990b [dev.regabi] cmd/compile: deref PAUTOHEAPs during SSA construction
+ 2021-01-08 59bfc18e34 cmd/go: add hint to read 'go help vcs' to GOVCS errors
+ 2021-01-08 6ee9b118a2 [dev.regabi] cmd/compile: remove fmt_test code; it has outlived its usefulness
+ 2021-01-08 cd6f3a54e4 cmd/go: revise 'go help' documentation for modules
+ 2021-01-08 6192b98751 cmd/go: make hints in error messages more consistent
+ 2021-01-08 25886cf4bd cmd/go: preserve sums for indirect deps fetched by 'go mod download'
+ 2021-01-08 6250833911 runtime/metrics: mark histogram metrics as cumulative
+ 2021-01-08 8f6a9acbb3 runtime/metrics: remove unused StopTheWorld Description field
+ 2021-01-08 6598c65646 cmd/compile: fix exponential-time init-cycle reporting
+ 2021-01-08 fefad1dc85 test: fix timeout code for invoking compiler
+ 2021-01-08 6728118e0a cmd/go: pass signals forward during "go tool"
+ 2021-01-08 e65c543f3c go/build/constraint: add parser for build tag constraint expressions
+ 2021-01-08 0c5afc4fb7 testing/fstest,os: clarify racy behavior of TestFS
+ 2021-01-08 32afcc9436 runtime/metrics: change unit on *-by-size metrics to match bucket unit
+ 2021-01-08 c6513bca5a io/fs: minor corrections to Glob doc
+ 2021-01-08 b241938e04 [dev.regabi] cmd/compile: fix some methods error text
+ 2021-01-08 304f769ffc cmd/compile: don't short-circuit copies whose source is volatile
+ 2021-01-08 ae97717133 runtime,runtime/metrics: use explicit histogram boundaries
+ 2021-01-08 a9ccd2d795 go/build: skip string literal while findEmbed
+ 2021-01-08 d92f8add32 archive/tar: fix typo in comment
+ 2021-01-08 cab1202183 cmd/link: accept extra blocks in TestFallocate
+ 2021-01-08 ee4d32249b io/fs: minor corrections to Glob release date
+ 2021-01-08 54bd1ccce2 cmd: update to latest golang.org/x/tools
+ 2021-01-07 9ec21a8f34 Revert "reflect: support multiple keys in struct tags"
+ 2021-01-07 091414b5b7 io/fs: correct WalkDirFunc documentation
+ 2021-01-07 9b55088d6b doc/go1.16: add release note for disallowing non-ASCII import paths
+ 2021-01-07 fa90aaca7d cmd/compile: fix late expand_calls leaf type for OpStructSelect/OpArraySelect
+ 2021-01-07 7cee66d4cb cmd/go: add documentation for Embed fields in go list output
+ 2021-01-07 e60cffa4ca html/template: attach functions to namespace
+ 2021-01-07 6da2d3b7d7 cmd/link: fix typo in asm.go
+ 2021-01-07 df81a15819 runtime: check mips64 VDSO clock_gettime return code
+ 2021-01-06 4787e906cf crypto/x509: rollback new CertificateRequest fields
+ 2021-01-06 c9658bee93 cmd/go: make module suggestion more friendly
+ 2021-01-06 4c668b25c6 runtime/metrics: fix panic message for Float64Histogram
+ 2021-01-06 d2131704a6 net/http/httputil: fix deadlock in DumpRequestOut
+ 2021-01-05 3e1e13ce6d cmd/go: set cfg.BuildMod to "readonly" by default with no module root
+ 2021-01-05 0b0d004983 cmd/go: pass embedcfg to gccgo if supported
+ 2021-01-05 cb05a0aa6a [dev.regabi] cmd/compile: remove toolstash scaffolding
+ 2021-01-05 9821838832 [dev.regabi] cmd/compile: remove CaptureVars
+ 2021-01-05 fd43831f44 [dev.regabi] cmd/compile: reimplement capture analysis
+ 2021-01-05 fb69c67cad [dev.regabi] test: enable finalizer tests on !amd64
+ 2021-01-05 1b85e7c057 cmd/go: don't scan gccgo standard library packages for imports
+ 2021-01-05 81f4f0e912 [dev.regabi] cmd/compile: remove race-y check in Name.Canonical
+ 2021-01-05 6b37b15d95 runtime: don't take allglock in tracebackothers
+ 2021-01-05 4a9d9adea4 [dev.regabi] cmd/compile: remove initname function
+ 2021-01-05 77365c5ed7 [dev.regabi] cmd/compile: add Name.Canonical and move Byval
+ 2021-01-05 e09783cbc0 [dev.regabi] cmd/compile: make ir.StaticValue safer
+ 2021-01-05 9aa950c407 [dev.regabi] cmd/compile: make ir.OuterValue safer
+ 2021-01-05 eb626409d1 [dev.regabi] cmd/compile: simplify CaptureVars
+ 2021-01-05 c28ca67a96 [dev.regabi] cmd/compile: fix ir.Dump for []*CaseClause, etc
+ 2021-01-04 9eef49cfa6 math/rand: fix typo in comment
+ 2021-01-04 b01fb2af9e testing/fstest: fix typo in error message
+ 2021-01-04 f24e40c14a [dev.regabi] cmd/compile: remove Name.Class_ accessors
+ 2021-01-04 d89705e087 [dev.regabi] cmd/compile: fix re-export of parameters
+ 2021-01-04 290b4154b7 [dev.regabi] cmd/compile: fix ICE due to large uint64 constants
+ 2021-01-04 a30fd52884 [dev.regabi] cmd/compile: use ir.NewNameAt in SubstArgTypes
+ 2021-01-03 8fc44cf0fa [dev.regabi] cmd/compile: remove a couple CloneName calls
+ 2021-01-03 907a4bfdc7 [dev.regabi] cmd/compile: fix map assignment order
+ 2021-01-03 f2e6dab048 [dev.regabi] cmd/compile: remove walkReturn "common case" path
+ 2021-01-03 d36a6bf44d [dev.regabi] cmd/compile: improve walkReturn common case
+ 2021-01-03 a317067d65 [dev.regabi] cmd/compile: improve ascompatee
+ 2021-01-03 5d80a590a2 [dev.regabi] cmd/compile: simplify walkReturn
+ 2021-01-03 bb1b6c95c2 [dev.regabi] cmd/compile: remove Node.{,Set}Walkdef
+ 2021-01-03 57c426c9a5 [dev.regabi] cmd/compile: tighten typecheckdef to *ir.Name
+ 2021-01-03 b1747756e3 [dev.regabi] cmd/compile: reorganize escape analysis somewhat
+ 2021-01-02 f2538033c0 [dev.regabi] cmd/compile: remove Nodes.Set [generated]
+ 2021-01-02 2f2d4b4e68 [dev.regabi] cmd/compile: remove {Ptr,Set}Init from Node interface
+ 2021-01-01 3dd5867605 doc: 2021 is the Year of the Gopher
+ 2021-01-01 1544a03198 [dev.regabi] cmd/compile: refactor redundant type conversion [generated]
+ 2021-01-01 7958a23ea3 [dev.regabi] cmd/compile: use *ir.Name where possible in inl.go
+ 2021-01-01 bfa97ba48f [dev.regabi] test: add another closure test case
+ 2021-01-01 67ad695416 [dev.regabi] cmd/compile: split escape analysis state
+ 2021-01-01 fad9a8b528 [dev.regabi] cmd/compile: simplify inlining of closures
+ 2021-01-01 7d55669847 [dev.regabi] cmd/compile: simplify dwarfgen.declPos
+ 2021-01-01 9ed1577779 [dev.regabi] cmd/compile: remove Func.ClosureEnter
+ 2021-01-01 ece345aa69 [dev.regabi] cmd/compile: expand documentation for Func.Closure{Vars,Enter}
+ 2021-01-01 6ddbc75efd [dev.regabi] cmd/compile: earlier deadcode removal
+ 2021-01-01 68e6fa4f68 [dev.regabi] cmd/compile: fix package-initialization order
+ 2021-01-01 3a4474cdfd [dev.regabi] cmd/compile: some more manual shuffling
+ 2021-01-01 0f1d2129c4 [dev.regabi] cmd/compile: reshuffle type-checking code [generated]
+ 2021-01-01 b8fd3440cd [dev.regabi] cmd/compile: report unused variables during typecheck
+ 2021-01-01 fd22df9905 [dev.regabi] cmd/compile: remove idempotent Name() calls [generated]
+ 2020-12-31 dfbcff80c6 [dev.regabi] cmd/compile: make copyExpr return *ir.Name directly
+ 2020-12-31 77fd81a3e6 [dev.regabi] cmd/compile: use names for keep alive variables in function call
+ 2020-12-31 8fe1197654 [dev.regabi] cmd/compile: remove Name.orig
+ 2020-12-31 477b049060 [dev.regabi] cmd/compile: fix printing of method expressions
+ 2020-12-31 95ce805d14 io/fs: remove darwin/arm64 special condition
+ 2020-12-30 20d0991b86 lib/time, time/tzdata: update tzdata to 2020f
+ 2020-12-30 ed301733bb misc/cgo/testcarchive: remove special flags for Darwin/ARM
+ 2020-12-30 0ae2e032f2 misc/cgo/test: enable TestCrossPackageTests on darwin/arm64
+ 2020-12-30 178c667db2 [dev.regabi] cmd/compile: fix OSLICEARR comments
+ 2020-12-30 f0d99def5b [dev.regabi] cmd/compile: add newline to ir.Dump
+ 2020-12-30 451693af71 [dev.regabi] cmd/compile: simplify typecheckdef
+ 2020-12-30 0c1a899a6c [dev.regabi] cmd/compile: fix defined-pointer method call check
+ 2020-12-30 f9b67f76a5 [dev.regabi] cmd/compile: change ir.DoChildren to use bool result type
+ 2020-12-30 499851bac8 [dev.regabi] cmd/compile: generalize ir/mknode.go
+ 2020-12-30 82ab3d1448 [dev.regabi] cmd/compile: use *ir.Name for Decl.X
+ 2020-12-30 9958b7ed3e [dev.regabi] cmd/compile: unexport ir.FmtNode
+ 2020-12-29 780b4de16b misc/ios: fix wording for command line instructions
+ 2020-12-29 b4a71c95d2 doc/go1.16: reference misc/ios/README for how to build iOS programs
+ 2020-12-29 f83e0f6616 misc/ios: add to README how to build ios executables
+ 2020-12-29 f5816624cd [dev.regabi] cmd/compile: change AddrExpr.Alloc to AddrExpr.Prealloc
+ 2020-12-29 850aa7c60c [dev.regabi] cmd/compile: use *ir.Name instead of ir.Node for CaseClause.Var
+ 2020-12-29 37babc97bb [dev.regabi] cmd/compile: allow visitor visits *ir.Name
+ 2020-12-29 5cf3c87fa6 [dev.regabi] cmd/compile: generate case/comm clause functions in mknode.go
+ 2020-12-29 b3e1ec97fd [dev.regabi] cmd/compile: move new addrtaken bit back to the old name
+ 2020-12-29 0620c674dd [dev.regabi] cmd/compile: remove original addrtaken bit
+ 2020-12-29 0523d525ae [dev.regabi] cmd/compile: separate out address taken computation from typechecker
+ 2020-12-29 9ea272e5ec [dev.regabi] cmd/compile: simplify ir.Func somewhat
+ 2020-12-29 e40cb4d4ae [dev.regabi] cmd/compile: remove more unused code
+ 2020-12-29 6f30c95048 [dev.regabi] cmd/compile: remove unneeded indirection
+ 2020-12-29 171fc6f223 [dev.regabi] cmd/compile: remove workarounds for go/constant issues
+ 2020-12-29 33801cdc62 [dev.regabi] cmd/compile: use Ntype where possible
+ 2020-12-29 82ad3083f8 [dev.regabi] cmd/compile: remove typ from AssignOpStmt
+ 2020-12-29 e34c44a7c4 [dev.regabi] cmd/compile: refactoring typecheck arith
+ 2020-12-29 a5ec920160 [dev.regabi] cmd/compile: more Linksym cleanup
+ 2020-12-29 ec59b197d5 [dev.regabi] cmd/compile: rewrite to use linksym helpers [generated]
+ 2020-12-29 25c613c02d [dev.regabi] cmd/compile: add Linksym helpers
+ 2020-12-29 289da2b33e [dev.regabi] cmd/compile: move Node.Opt to Name
+ 2020-12-29 6acbae4fcc [dev.regabi] cmd/compile: address some ir TODOs
+ 2020-12-29 4629f6a51d [dev.regabi] cmd/compile: merge {Selector,CallPart,Method}Expr
+ 2020-12-29 e563715b30 [dev.regabi] cmd/compile: remove Sym.Importdef
+ 2020-12-29 3f370b75fb [dev.regabi] cmd/compile: cleanup //go:generate directives
+ 2020-12-28 4fd9455882 io/fs: fix typo in comment
+ 2020-12-28 07569dac4e [dev.regabi] all: merge master (1d78139) into dev.regabi
+ 2020-12-28 76136be027 [dev.regabi] cmd/compile: check for recursive import in ImportBody
+ 2020-12-28 fda7ec3a3f [dev.regabi] cmd/compile: remove Name.IsDDD, etc
+ 2020-12-28 098a6490b9 [dev.regabi] cmd/compile: remove Declare in makepartialcall
+ 2020-12-28 137f0d2e06 [dev.regabi] cmd/compile: remove unnecessary Name.Sym call
+ 2020-12-28 3383b5c74a [dev.regabi] cmd/compile: flatten dependency graph [generated]
+ 2020-12-28 f8afb8216a [dev.regabi] cmd/compile: rename CommStmt and CaseStmt [generated]
+ 2020-12-28 5f3bd59a0d [dev.regabi] cmd/compile: remove some unneeded code in package ir
+ 2020-12-28 3bdafb0d82 [dev.regabi] cmd/compile: remove CommStmt.List
+ 2020-12-28 2ecf52b841 [dev.regabi] cmd/compile: separate CommStmt from CaseStmt
+ 2020-12-28 ed9772e130 [dev.regabi] cmd/compile: add explicit file name in types generation
+ 2020-12-28 a59d26603f [dev.regabi] cmd/compile: use []*CaseStmt in {Select,Switch}Stmt
+ 2020-12-28 fbc4458c06 [dev.regabi] cmd/compile: simplify some tree traversal code
+ 2020-12-28 6c67677541 [dev.regabi] cmd/compile: simplify FuncName and PkgFuncName
+ 2020-12-28 676d794b81 [dev.regabi] cmd/compile: remove refersToCommonName
+ 2020-12-28 c98548e110 [dev.regabi] cmd/compile: merge ascompatee, ascompatee1, and reorder3
+ 2020-12-28 4c215c4fa9 [dev.regabi] cmd/compile: simplify and optimize reorder3
+ 2020-12-28 e6c973198d [dev.regabi] cmd/compile: stop mangling SelectorExpr.Sel for ODOTMETH
+ 2020-12-28 135ce1c485 [dev.regabi] cmd/compile: desugar OMETHEXPR into ONAME during walk
+ 2020-12-28 0f732f8c91 [dev.regabi] cmd/compile: minor walkExpr cleanups
+ 2020-12-28 0de8eafd98 [dev.regabi] cmd/compile: remove SelectorExpr.Offset field
+ 2020-12-28 a4f335f420 [dev.regabi] cmd/compile: always use a Field for ODOTPTR expressions
+ 2020-12-26 1d78139128 runtime/cgo: fix Android build with NDK 22
+ 2020-12-25 2018b68a65 net/mail: don't use MDT in test
+ 2020-12-25 e4f293d853 [dev.regabi] cmd/compile: fix OCALLMETH desugaring
+ 2020-12-25 1d9a1f67d5 [dev.regabi] cmd/compile: don't emit reflect data for method types
+ 2020-12-25 396b6c2e7c [dev.regabi] cmd/compile: cleanup assignment typechecking
+ 2020-12-25 e24d2f3d05 [dev.regabi] cmd/compile: remove typ from RangeStmt
+ 2020-12-25 2785c691c2 [dev.regabi] cmd/compile: cleanup devirtualization docs
+ 2020-12-25 4b1d0fe66f [dev.regabi] cmd/compile: new devirtualization pkg [generated]
+ 2020-12-24 082cc8b7d9 [dev.regabi] cmd/compile: change ir.IsAssignable -> ir.IsAddressable
+ 2020-12-24 27b248b307 [dev.regabi] cmd/compile: separate range stmt Vars to Key, Value nodes
+ 2020-12-23 40818038bf [dev.regabi] cmd/compile: change CaseStmt.Vars to Var
+ 2020-12-23 b116404444 runtime: shift timeHistogram buckets and allow negative durations
+ 2020-12-23 8db7e2fecd runtime: fix allocs-by-size and frees-by-size buckets
+ 2020-12-23 fb96f07e1a runtime: fix nStackRoots comment about stack roots
+ 2020-12-23 d1502b3c72 lib/time, time/tzdata: update tzdata to 2020e
+ 2020-12-23 30c99cbb7a cmd/go: add the Retract field to 'go help mod edit' definition of the GoMod struct
+ 2020-12-23 49d0b239cb doc: fix a typo in contribute.html
+ 2020-12-23 9eeed291bc [dev.regabi] cmd/compile: eliminate usage of ir.Node in liveness
+ 2020-12-23 d1d64e4cea [dev.regabi] cmd/compile: split SliceExpr.List into separate fields
+ 2020-12-23 98a73030b0 cmd/go: in 'go get', promote named implicit dependencies to explicit
+ 2020-12-23 d19018e8f1 [dev.regabi] cmd/compile: split SliceHeaderExpr.LenCap into separate fields
+ 2020-12-23 53f082b0ee [dev.regabi] cmd/compile: cleanup export code further
+ 2020-12-23 31267f82e1 [dev.regabi] cmd/compile: simplify function/interface/struct typechecking
+ 2020-12-23 addade2cce [dev.regabi] cmd/compile: prefer types constructors over typecheck
+ 2020-12-23 18ebfb49e9 [dev.regabi] cmd/compile: cleanup noder
+ 2020-12-23 87a592b356 [dev.regabi] cmd/compile: cleanup import/export code
+ 2020-12-23 5898025026 [dev.regabi] cmd/compile: update mkbuiltin.go to use new type constructors
+ 2020-12-23 63c96c2ee7 [dev.regabi] cmd/compile: update mkbuiltin.go and re-enable TestBuiltin
+ 2020-12-23 37f138df6b [dev.regabi] cmd/compile: split out package test [generated]
+ 2020-12-23 3d8a3cb06b [dev.regabi] cmd/compile: split out package pkginit [generated]
+ 2020-12-23 3f04d964ab [dev.regabi] cmd/compile: split up walkexpr1, walkstmt [generated]
+ 2020-12-23 e4895ab4c0 [dev.regabi] cmd/compile: split out package walk [generated]
+ 2020-12-23 01fd2d05c8 [dev.regabi] cmd/compile: split out package dwarfgen [generated]
+ 2020-12-23 6c34d2f420 [dev.regabi] cmd/compile: split out package ssagen [generated]
+ 2020-12-23 de65151e50 [dev.regabi] cmd/compile: split out package reflectdata [generated]
+ 2020-12-23 4dfb5d91a8 [dev.regabi] cmd/compile: split out package staticdata [generated]
+ 2020-12-23 fbc82f03b1 [dev.regabi] cmd/compile: split out package noder [generated]
+ 2020-12-23 de454eef5f [dev.regabi] cmd/compile: split out package escape [generated]
+ 2020-12-23 071ab0a14c [dev.regabi] cmd/compile: split out package liveness [generated]
+ 2020-12-23 0ced54062e [dev.regabi] cmd/compile: split out package objw [generated]
+ 2020-12-23 575fd6ff0a [dev.regabi] cmd/compile: split out package inline [generated]
+ 2020-12-23 0256ba99a8 [dev.regabi] cmd/compile: split up typecheck1 [generated]
+ 2020-12-23 b9693d7627 [dev.regabi] cmd/compile: split out package typecheck [generated]
+ 2020-12-23 dac0de3748 [dev.regabi] cmd/compile: move type size calculations into package types [generated]
+ 2020-12-23 527a1895d6 [dev.regabi] cmd/compile: move helpers into package ir [generated]
+ 2020-12-23 65c4c6dfb2 [dev.regabi] cmd/compile: group known symbols, packages, names [generated]
+ 2020-12-23 9ee309255a [dev.regabi] cmd/compile: move helpers into package types [generated]
+ 2020-12-23 ead4957892 [dev.regabi] cmd/compile: move helpers into package base [generated]
+ 2020-12-23 440308ffd7 [dev.regabi] cmd/compile: simplify Nodes usage [generated]
+ 2020-12-23 f9d373720e [dev.regabi] cmd/compile: remove Left, Right etc methods [generated]
+ 2020-12-23 14d667341f [dev.regabi] cmd/compile: remove Node.Left etc [generated]
+ 2020-12-23 6f27d29be0 [dev.regabi] cmd/compile: remove ir.Nod [generated]
+ 2020-12-23 fd6ba1c8a2 os/signal: fix a deadlock with syscall.AllThreadsSyscall() use
+ 2020-12-23 69cf39089f [dev.regabi] cmd/compile: do not die in early base.FlushErrors
+ 2020-12-23 6d03cde88a [dev.regabi] cmd/dist: automatically bootstrap cmd subdirs
+ 2020-12-23 b0b0d98283 runtime: linux iscgo support for not blocking nptl signals
+ 2020-12-23 d1d1099c91 [dev.regabi] cmd/compile: fixes for big rewrite
+ 2020-12-22 223331fc0c cmd/go/internal/modload: add hint for missing implicit dependency
+ 2020-12-22 ec741b0447 [dev.regabi] all: merge master (c9fb4eb) into dev.regabi
+ 2020-12-22 acc32ea124 [dev.regabi] codereview.cfg: add config for dev.regabi
+ 2020-12-22 c9fb4eb0a2 cmd/link: handle grouped resource sections
+ 2020-12-22 c40934b33d [dev.regabi] cmd/compile: adjust one case in walkexpr
+ 2020-12-22 280e7fd1ee [dev.regabi] cmd/compile: only access Func method on concrete types
+ 2020-12-22 51ba53f5c2 [dev.regabi] cmd/compile: separate misc for gc split
+ 2020-12-22 572f168ed2 [dev.regabi] cmd/compile: separate various from Main
+ 2020-12-22 3b12c6dc08 [dev.regabi] cmd/compile: separate typecheck more cleanly
+ 2020-12-22 7c8f5356ab [dev.regabi] cmd/compile: separate dowidth better
+ 2020-12-22 c06a354bcc test: trigger SIGSEGV instead of SIGTRAP in issue11656.go
+ 2020-12-22 0aa9b4709a cmd/pack: r command create output file if not exist
+ 2020-12-22 cb28c96be8 [dev.regabi] cmd/compile,cmd/link: initial support for ABI wrappers
+ 2020-12-22 c8610e4700 [dev.regabi] cmd/compile: add ir.BasicLit to represent literals
+ 2020-12-22 3512cde10a [dev.regabi] cmd/compile: stop reusing Ntype for OSLICELIT length
+ 2020-12-22 2755361e6a [dev.regabi] cmd/compile: change noder.declNames to returns ir.Names
+ 2020-12-22 301af2cb71 [dev.regabi] runtime/race: adjust test pattern match for ABI wrapper
+ 2020-12-22 4d27c4c223 runtime: correct error handling in several FreeBSD syscall wrappers
+ 2020-12-22 9b6147120a cmd/pack: treat compiler's -linkobj output as "compiler object"
+ 2020-12-22 306b2451c8 [dev.regabi] runtime: fix ABI targets in runtime.panic{Index,Slice} shims
+ 2020-12-21 bc7e4d9257 syscall: don't generate ptrace on iOS
+ 2020-12-21 94cfeca0a5 [dev.regabi] cmd/compile: stop using ONONAME with Name
+ 2020-12-21 cb4898a77d [dev.regabi] cmd/compile: simplify declaration importing
+ 2020-12-21 06915ac14d [dev.regabi] cmd/compile: move itabname call out of implements
+ 2020-12-21 6cff874c47 runtime/metrics: add Read examples
+ 2020-12-21 4e8f681eff Merge "[dev.regabi] all: merge master into dev.regabi" into dev.regabi
+ 2020-12-21 1a523c8ab0 [dev.regabi] cmd/compile: separate nowritebarrierrec from main
+ 2020-12-21 e999c17022 [dev.regabi] cmd/compile: separate ssa from other phases
+ 2020-12-21 4836e28ac0 [dev.regabi] cmd/compile: separate noder more cleanly
+ 2020-12-21 85ce6ecfe3 [dev.regabi] cmd/compile: separate exportsym more cleanly
+ 2020-12-21 1a3b036b83 [dev.regabi] cmd/compile: collect global compilation state
+ 2020-12-21 2153a99914 [dev.regabi] cmd/compile: setup to move Addrconst, Patch into cmd/internal/obj
+ 2020-12-21 0bb0baf683 [dev.regabi] cmd/compile: cleanup for concrete types - more
+ 2020-12-21 ca8e17164e [dev.regabi] all: merge master into dev.regabi
+ 2020-12-21 8438a5779b runtime: use _exit on darwin
+ 2020-12-21 cb95819cf6 runtime: detect netbsd netpoll overrun in sysmon
+ 2020-12-21 53c984d976 runtime: skip wakep call in wakeNetPoller on Plan 9
+ 2020-12-21 9abbe27710 test: skip issue11656.go on mips/mips64/ppc64
+ 2020-12-20 89b44b4e2b cmd/compile: recognize reassignments involving receives
+ 2020-12-19 55b58018f4 test: for issue11656 try to execute trap, not call it
+ 2020-12-18 626cc7c02d test: permit "exponent too large" error
+ 2020-12-18 139cd0e12f go/build: make TestDependencies work again
+ 2020-12-18 2de7866470 os: remove dependency on strings package
+ 2020-12-18 c45313bf45 [dev.regabi] cmd/compile: remove prealloc map
+ 2020-12-18 ffb0cb7044 [dev.regabi] cmd/compile: remove uses of Name.Offset, Name.copy
+ 2020-12-18 c76be2a24e [dev.regabi] cmd/compile: add ONAMEOFFSET, delete to-be-deleted fields
+ 2020-12-18 4e8f1e139f [dev.regabi] cmd/compile: cleanup for concrete types - sinit
+ 2020-12-18 27aba22651 [dev.regabi] cmd/compile: cleanup for concrete types - walk
+ 2020-12-18 0b9cb63b8d [dev.regabi] cmd/compile: rename ir.Find to ir.Any and update uses
+ 2020-12-18 ae652a4ac9 os/signal: fix flaky tests for NotifyContext.
+ 2020-12-18 740851baca cmd/link: avoid use of -T when linking with lld
+ 2020-12-18 f1778c28a9 test: recognize and use gc build tag
+ 2020-12-17 8fcf318123 api/go1.16: remove crypto/tls APIs that are moved to Go 1.17
+ 2020-12-17 520f3b72db crypto/tls: revert "add HandshakeContext method to Conn"
+ 2020-12-17 2ff33f5e44 api: promote next to go1.16
+ 2020-12-17 aeedc9f804 [dev.regabi] cmd/compile: remove OSELRECV
+ 2020-12-17 0328c3b660 [dev.regabi] cmd/compile: use OSELRECV2 for all <-c variants
+ 2020-12-17 88e1415d08 [dev.regabi] cmd/compile: add type assertion in regabi test
+ 2020-12-17 9c384e881e [dev.regabi] cmd/compile: cleanup for concrete types - mop-up
+ 2020-12-17 be64c8bece [dev.regabi] cmd/compile: cleanup for concrete types - noder
+ 2020-12-17 5024396563 [dev.regabi] cmd/compile: cleanup for concrete types - subr
+ 2020-12-17 dd67b13d07 [dev.regabi] cmd/compile: cleanup for concrete types - range, select, swt
+ 2020-12-17 42fec2ded4 [dev.regabi] cmd/compile: cleanup for concrete types - const
+ 2020-12-17 389ae3d5ba [dev.regabi] cmd/compile: cleanup for concrete types - inl
+ 2020-12-17 5fe64298a4 [dev.regabi] cmd/compile: cleanup for concrete types - import/export
+ 2020-12-17 aa55d4e54b [dev.regabi] cmd/compile: cleanup for concrete types - escape
+ 2020-12-17 846740c17f [dev.regabi] cmd/compile: cleanup for concrete types - ssa
+ 2020-12-17 bf9bbbd6ed [dev.regabi] cmd/compile: cleanup for concrete types - order
+ 2020-12-17 4ac6a6317b [dev.regabi] cmd/compile: cleanup for concrete types - typecheck
+ 2020-12-17 f6efa3d4a4 [dev.regabi] cmd/compile: simplify ir.Find, replace ir.Inspect with ir.Visit
+ 2020-12-17 f6d2834f8f [dev.regabi] cmd/compile: limit Implicit method to nodes where it is defined
+ 2020-12-17 7fde0d2b50 [dev.regabi] cmd/compile: remove use of Initorder, Offset Node fields for initorder
+ 2020-12-17 114af2a044 [dev.regabi] cmd/compile: change Nodes to be a slice
+ 2020-12-17 4dfc7333f4 [dev.regabi] cmd/compile: update ir/fmt for concrete types
+ 2020-12-17 a997543292 [dev.regabi] cmd/compile: fix potential closure waste in Order
+ 2020-12-17 578fbbe3aa [dev.regabi] cmd/compile: rewrite some generic ir.Nod calls
+ 2020-12-17 5ae70b85c6 [dev.regabi] cmd/compile: cleanup preparing for concrete types, 2
+ 2020-12-17 fa06894b36 [dev.regabi] cmd/compile: cleanup preparing for concrete types
+ 2020-12-17 5a4db102b2 html/template: avoid race when escaping updates template
+ 2020-12-16 b0f01e17f8 go/types: report error for invalid (but empty) expr switch
+ 2020-12-16 5abda2618b cmd/link: handle large relocation addend on darwin/arm64
+ 2020-12-16 a318d56c1e cmd/link: pass arch-specific flags to external linker when testing supported flag
+ 2020-12-16 f4e7a6b905 cmd/internal/goobj: fix buglet in object file reader
+ 2020-12-16 75e16f5127 doc/go1.16: add link to reflect.StructTag
+ 2020-12-16 08b5091d03 net: close connection in localServer teardown
+ 2020-12-16 8981092d71 cmd/link: ignore SEH marking on PE objects
+ 2020-12-15 731bb54038 test: update for gofrontend error message changes
+ 2020-12-15 129bb1917b doc/go1.15: mention 1.15.3 cgo restriction on empty structs
+ 2020-12-15 685a322fe4 test: match gofrontend error messages
+ 2020-12-15 3d6467824c test: only require issue11674 errors with gc compiler
+ 2020-12-15 7cdc84a15b test: remove bug429 (duplicates runtime.TestSimpleDeadlock)
+ 2020-12-15 412dc2f4d3 test: adjust issue11371 to fit in required precision
+ 2020-12-15 8e2d74b705 test: only check for issue11362 error with gc
+ 2020-12-15 f8ac237032 test: import file name for issue19028
+ 2020-12-15 a508840c67 doc/go1.16: fix path, path/filepath release notes
+ 2020-12-15 5046cb8a6e doc/go1.16: fix formatting in net, net/http and net/http/httputil sections
+ 2020-12-15 3298300ddf text/template: error on range over send channel
+ 2020-12-15 4c2d66f642 [dev.regabi] cmd/compile: use ir.Ident for imported identifiers
+ 2020-12-15 305d93ef84 [dev.regabi] cmd/compile: type check externdcl earlier
+ 2020-12-15 9f16620f46 [dev.regabi] cmd/compile: fix latent Sym.SetPkgDef issue
+ 2020-12-15 5a25a3fd1d test: recognize gofrontend error messages
+ 2020-12-14 fea898a4b0 [dev.regabi] cmd/compile: intercept the making of OADDR nodes
+ 2020-12-14 663cd862ed cmd/link: do not mark resource section as writable
+ 2020-12-14 48dfa2b2dc cmd/link: deal with ADDR32NB relocations the same way as ADDR32 on arm
+ 2020-12-14 033390d9ad cmd/link: recognize arm header of PE objects
+ 2020-12-14 48906a6d57 net/http/pprof: don't treat os.Args as format string in Cmdline handler
+ 2020-12-14 6e3cc5c56f go/types: report invalid ... in conversions
+ 2020-12-14 278b9a8a4a io/fs: fix package reference in FS godoc
+ 2020-12-14 617383377f [dev.regabi] cmd/compile: reorg generated array hash loop
+ 2020-12-14 d06794da4a doc/go1.16: add missing <code> tag
+ 2020-12-14 dea6d94a44 math/big: add test for recursive division panic
+ 2020-12-14 2f5b1a3974 test: make a race detector test robust to timing variations
+ 2020-12-14 c81343ce3a net/http: attempt deadlock fix in TestDisableKeepAliveUpgrade
+ 2020-12-14 828746ec57 debug/dwarf: don't try to parse addr/rnglists header
+ 2020-12-14 be10af7c4e test: match gofrontend error messages
+ 2020-12-14 89f38323fa [dev.regabi] cmd/compile: add register ABI analysis utilities
+ 2020-12-14 ce61ccca8f test: match gofrontend error messages
+ 2020-12-14 a58be734ea cmd/compile: fix incorrect shift count type with s390x rules
+ 2020-12-14 8ce37e4110 [dev.regabi] cmd/compile: fix noopt builder
+ 2020-12-14 7e17b46c58 [dev.regabi] cmd/compile/internal/types: add IsScalar query method
+ 2020-12-14 2b76429eb0 [dev.regabi] cmd/compile: refactor type initialization code into helper
+ 2020-12-14 9c5241e520 [dev.regabi] cmd/compile: remove unnecessary String methods
+ 2020-12-14 267975dc47 Merge branch 'master' into dev.regabi
+ 2020-12-14 64d8846aae cmd/go: print hint when 'go install' run without version outside module
+ 2020-12-14 451b6b38fd cmd/go: refactor error reporting in internal/load
+ 2020-12-09 63bc23b545 [dev.regabi] cmd/compile: first start towards using Ident
+ 2020-12-09 eae8fd519b [dev.regabi] cmd/compile: iexport debug crumbs for toolstash
+ 2020-12-09 837b35cc55 [dev.regabi] cmd/compile: adjust IR representations
+ 2020-12-09 0c49440664 [dev.regabi] cmd/compile: arrange for walkstmt, walkexpr, to return from switch cases
+ 2020-12-09 4090af83c5 [dev.regabi] cmd/compile: use reflection in ir.Dump
+ 2020-12-09 e2d278bfeb [dev.regabi] cmd/compile: two small fixes
+ 2020-12-08 dbf2fc8cff [dev.regabi] cmd/compile: replace many uses of ir.Node with *ir.Name
+ 2020-12-08 bb31c75343 [dev.regabi] cmd/compile: ir.Node is no longer an ssa.Aux
+ 2020-12-08 6db970e20a [dev.regabi] cmd/compile: rewrite Aux uses of ir.Node to *ir.Name [generated]
+ 2020-12-08 1c8943a6ad [dev.regabi] cmd/compile: introduce FwdRefAux for wrapping ir.Node as ssa.Aux
+ 2020-12-08 dcec658f6c [dev.regabi] cmd/compile: change LocalSlot.N to *ir.Name
+ 2020-12-08 1a98ab0e2d [dev.regabi] cmd/compile: add ssa.Aux tag interface for Value.Aux
+ 2020-12-07 63722da46b [dev.regabi] cmd/compile: fix comment
+ 2020-12-07 6d783e7440 [dev.regabi] cmd/compile: export all Node fields [generated]
+ 2020-12-07 2de0af3b1b [dev.regabi] cmd/compile: prepare mknode for rename of Func.body
+ 2020-12-07 724374f859 [dev.regabi] cmd/compile: rewrite stale format strings
+ 2020-12-07 61889ba680 [dev.regabi] cmd/compile: simplify fmtmap
+ 2020-12-07 6ea2b8c54c [dev.regabi] cmd/compile: clean up and document formatting
+ 2020-12-07 bb4a37bd93 [dev.regabi] cmd/compile: move Type, Sym printing to package types [generated]
+ 2020-12-07 70155cca81 [dev.regabi] cmd/compile: untangle FmtFlag, FmtMode
+ 2020-12-07 3904a62829 [dev.regabi] cmd/compile: remove mode.Sprintf etc in printer
+ 2020-12-07 fb17dfa43d [dev.regabi] cmd/compile: narrow interface between ir and types
+ 2020-12-07 3b25f3c150 [dev.regabi] cmd/compile: simplify Op, Node, Nodes printing
+ 2020-12-07 8ce2605c5b [dev.regabi] cmd/compile: untangle ir.Dump printing
+ 2020-12-07 158c9dd131 [dev.regabi] cmd/compile: reorganize ir/fmt.go
+ 2020-12-07 a79742f39a [dev.regabi] cmd/compile: remove "short" node header mode
+ 2020-12-07 ef5964dd6b [dev.regabi] cmd/compile: arrange for typecheck1 to end in switch
+ 2020-12-07 dcc640e839 [dev.regabi] test: add exhaustive test of evaluated but not used
+ 2020-12-07 2cec6c4a8c [dev.regabi] cmd/compile: generate Node methods using program
+ 2020-12-07 d90b199e9c [dev.regabi] cmd/compile: silence errors about missing blank methods
+ 2020-12-06 e885df2731 [dev.regabi] cmd/compile: change iexport to avoid map[ir.Node]
+ 2020-12-06 2d4c95565a [dev.regabi] cmd/compile: change nowritebarrierrec to use map[*ir.Func]
+ 2020-12-06 1b5eed8982 [dev.regabi] cmd/compile: replace NodeQueue with NameQueue
+ 2020-12-06 6c5967e528 [dev.regabi] cmd/compile: change NodeSet to NameSet
+ 2020-12-04 46b6e70e3b [dev.regabi] cmd/compile: replace ir.Node with *ir.Name in Order
+ 2020-12-04 b75f51c645 [dev.regabi] cmd/compile: replace ir.Node with *ir.Name in Liveness
+ 2020-12-04 133b03e1c3 [dev.regabi] cmd/compile: rewrite code to use DeclaredBy
+ 2020-12-04 d9cb84c84b [dev.regabi] cmd/compile: add SameSource, Uses, and DeclaredBy helpers
+ 2020-12-04 5dbd2e8e44 [dev.regabi] cmd/compile: remove DeepCopyNode interface
+ 2020-12-04 9ab3d854ad [dev.regabi] cmd/compile: avoid general traversal in deadcode
+ 2020-12-04 bb5aa2b664 [dev.regabi] cmd/compile: implement editChildren for nodes
+ 2020-12-04 4725c3ffd1 [dev.regabi] cmd/compile: implement doChildren for nodes
+ 2020-12-04 18f2df7e81 [dev.regabi] cmd/compile: implement copy for nodes
+ 2020-12-04 d855b30fe4 [dev.regabi] cmd/compile: use ir.EditChildren for inline rewriting
+ 2020-12-04 b9df26d7a8 [dev.regabi] cmd/compile: use ir.Find for "search" traversals
+ 2020-12-04 0d1b44c645 [dev.regabi] cmd/compile: introduce IR visitors
+ 2020-12-04 7fcf5b994c [dev.regabi] cmd/compile: replace inlcopy with ir.DeepCopy
+ 2020-12-04 989a3f5041 [dev.regabi] cmd/compile: adjustments to Copy and DeepCopy
+ 2020-12-04 99ecfcae31 [dev.regabi] cmd/compile: swap inlining order of if then vs else blocks
+ 2020-12-04 84cb51d7d7 [dev.regabi] cmd/compile: eliminate more SetOrig
+ 2020-12-03 351bc2f38c [dev.regabi] cmd/compile: store types.Field on {Selector,CallPart}Expr
+ 2020-12-03 a2058bac21 [dev.regabi] cmd/compile: add ConstExpr
+ 2020-12-03 beb5e05404 [dev.regabi] cmd/compile: refactoring prep for ConstExpr
+ 2020-12-03 7e81135be7 [dev.regabi] cmd/compile: rename addinit(n, init) to initExpr(init, n)
+ 2020-12-03 6e30fc10fc [dev.regabi] all: merge master (d0c0dc682c1f) into dev.regabi
+ 2020-12-03 59b8916d48 [dev.regabi] cmd/compile: handle OCONVNOP better in ssa
+ 2020-12-03 00e5727790 [dev.regabi] cmd/compile: remove okAs
+ 2020-12-03 5a3b6796cd [dev.regabi] cmd/compile: remove extra typ field in Name struct
+ 2020-12-02 64bc656aed [dev.regabi] cmd/compile: use explicit block statements for init
+ 2020-12-02 ecc8d15bc5 [dev.regabi] cmd/compile: delete OEMPTY
+ 2020-12-02 ec5f349b22 [dev.regabi] cmd/compile: merge OBLOCK and OEMPTY
+ 2020-12-02 c769d393de [dev.regabi] cmd/compile: add ir.NewDeclNameAt
+ 2020-12-02 c10b0ad628 [dev.regabi] cmd/compile: add Pkg parameter to type constructors
+ 2020-12-02 42e46f4ae0 [dev.regabi] cmd/compile: comment out //go:linkname warning
+ 2020-12-02 77a71e0057 [dev.regabi] cmd/compile: add Interface, Signature, and Struct constructors
+ 2020-12-02 15085f8974 [dev.regabi] cmd/compile: tweak hash bucket type descriptor
+ 2020-12-01 1408d26ccc [dev.regabi] cmd/compile: cleanup some leftover cruft
+ 2020-12-01 5ffa275f3c [dev.regabi] cmd/compile: first pass at abstracting Type
+ 2020-12-01 6ca23a45fe [dev.regabi] cmd/compile: only save ONAMEs on Curfn.Dcl
+ 2020-12-01 a17c5e2fce [dev.regabi] cmd/compile: add NewBasic and cleanup universe
+ 2020-12-01 f37aa5e4e2 [dev.regabi] cmd/compile: add NewNamed
+ 2020-12-01 63a6f08b39 [dev.regabi] cmd/compile: move setUnderlying to package types
+ 2020-12-01 f2311462ab [dev.regabi] cmd/compile: cleanup type-checking of defined types
+ 2020-12-01 2d6ff998ed [dev.regabi] cmd/compile: process //go:linknames after declarations
+ 2020-12-01 ecff7628ea [dev.regabi] cmd/compile: unexport Node.RawCopy
+ 2020-12-01 4da41fb3f8 [dev.regabi] cmd/compile: use ir.Copy instead of direct use of RawCopy
+ 2020-12-01 dadfc80bc1 [dev.regabi] cmd/compile: improve findTypeLoop
+ 2020-12-01 45f3b646d4 [dev.regabi] cmd/compile: add OSTMTEXPR Op
+ 2020-12-01 9a5a11adfa [dev.regabi] cmd/compile: add custom expression Node implementations
+ 2020-12-01 0f9f27287b [dev.regabi] cmd/compile: remove types.InitSyms
+ 2020-11-30 41ad4dec99 [dev.regabi] cmd/compile: fix -h
+ 2020-11-30 ffa68716a0 [dev.regabi] cmd/compile: add custom statement Node implementations
+ 2020-11-30 2bc814cd18 [dev.regabi] cmd/compile: clean up ONEW node
+ 2020-11-30 b7f67b75d2 [dev.regabi] cmd/compile: clean up in preparation for expression Nodes
+ 2020-11-30 5fc192af56 [dev.regabi] cmd/compile: clean up Order.copyExpr TODO
+ 2020-11-30 7c9b6b1ca2 [dev.regabi] cmd/compile: clean up in preparation for statement Nodes
+ 2020-11-30 c6de5d8d1f [dev.regabi] cmd/compile: simplify export data representation of nil
+ 2020-11-30 ae1a337809 [dev.regabi] cmd/compile: remove ODCLFIELD and ODDD ops
+ 2020-11-30 4e7685ef1a [dev.regabi] cmd/compile: add custom type syntax Node implementations
+ 2020-11-30 d40869fced [dev.regabi] cmd/compile: move gc.treecopy to ir.DeepCopy
+ 2020-11-30 f0001e8867 [dev.regabi] cmd/compile: add OTSLICE Op
+ 2020-11-30 1b84aabb01 [dev.regabi] cmd/compile: move typenod, typenodl to ir.TypeNode, ir.TypeNodeAt [generated]
+ 2020-11-30 e5c6463e20 [dev.regabi] cmd/compile: add ir.CallPartExpr
+ 2020-11-30 4eaef981b5 [dev.regabi] cmd/compile: add ir.Closure, ir.ClosureRead
+ 2020-11-30 e84b27bec5 [dev.regabi] cmd/compile: clean up Name and Func uses
+ 2020-11-30 c4bd0b7474 [dev.regabi] cmd/compile: make ir.Func the ODCLFUNC Node implementation
+ 2020-11-30 65ae15ac5d [dev.regabi] cmd/compile: move func code from node.go to func.go
+ 2020-11-30 862f638a89 [dev.regabi] cmd/compile: make ir.Name the ONAME Node implementation
+ 2020-11-30 f6106d195d [dev.regabi] cmd/compile: add ir.PkgName
+ 2020-11-30 420809ab08 [dev.regabi] cmd/compile: move name code from node.go to name.go
+ 2020-11-30 be3d8b40b5 [dev.regabi] cmd/compile: ir.BranchStmt, add ir.EmptyStmt, ir.LabelStmt
+ 2020-11-30 b09dbc6913 [dev.regabi] cmd/compile: remove SetOp(OEMPTY) calls
+ 2020-11-30 171787efcd [dev.regabi] cmd/compile: remove Orig, SetOrig from Node interface
+ 2020-11-30 79a3d5ce15 [dev.regabi] cmd/compile: setup for new Node implementations
+ 2020-11-30 0c65a2f317 [dev.regabi] cmd/compile: drop Node.HasOpt method
+ 2020-11-30 65f4ec2fae [dev.regabi] cmd/compile: cleanup label handling
+ 2020-11-25 88e33f6ecb [dev.regabi] cmd/compile: fix latent import/export issue with break/continue
+ 2020-11-25 40f5bc4d55 [dev.regabi] merge master 4481ad6eb6 into dev.regabi
+ 2020-11-25 41f3af9d04 [dev.regabi] cmd/compile: replace *Node type with an interface Node [generated]
+ 2020-11-25 4d0d9c2c5c [dev.regabi] cmd/compile: introduce ir.INode interface for *ir.Node
+ 2020-11-25 c26aead50c [dev.regabi] cmd/compile: convert types.Node (a pointer) to types.IRNode (an interface)
+ 2020-11-25 acb4d1cef1 [dev.regabi] cmd/compile: use Node getters and setters [generated]
+ 2020-11-25 41ab6689ed [dev.regabi] cmd/compile: rewrite a few ++/--/+=/-= to prep for getters/setters [generated]
+ 2020-11-25 048debb224 [dev.regabi] cmd/compile: remove gc ↔ ssa cycle hacks
+ 2020-11-25 84e2bd611f [dev.regabi] cmd/compile: introduce cmd/compile/internal/ir [generated]
+ 2020-11-25 331b8b4797 [dev.regabi] cmd/compile: move okforconst into its own declaration
+ 2020-11-25 26b66fd60b [dev.regabi] cmd/compile: introduce cmd/compile/internal/base [generated]
+ 2020-11-25 eb3086e5a8 [dev.regabi] cmd/compile: finish cleanup of Debug parsing
+ 2020-11-25 3c240f5d17 [dev.regabi] cmd/compile: clean up debug flag (-d) handling [generated]
+ 2020-11-25 756661c82a [dev.regabi] cmd/compile: finish cleanup of Flag initialization
+ 2020-11-25 259fd8adbb [dev.regabi] cmd/compile: fix reporting of overflow
+ 2020-11-25 18573aea3c [dev.regabi] cmd/compile: clean up flag handling [generated]
+ 2020-11-25 6e583d65ab [dev.regabi] cmd/compile: simplify fmt handling of Nodes
+ 2020-11-25 d166ef6876 [dev.regabi] cmd/compile: add Node field getters and setters
+ 2020-11-25 9262909764 [dev.regabi] cmd/compile: rewrite problematic use of Node fields
+ 2020-11-25 9e0e43d84d [dev.regabi] cmd/compile: remove uses of dummy
+ 2020-11-25 4a6b4fd139 [dev.regabi] add FatalfAt and fix Fatalf docs
+ 2020-11-25 484449c641 [dev.regabi] cmd/compile: remove file mistakenly added by CL 272248
+ 2020-11-25 7d72951229 [dev.regabi] cmd/compile: replace Val with go/constant.Value
+ 2020-11-24 6826287c6b [dev.regabi] cmd/compile: replace evconst with non-mutating version
+ 2020-11-24 c22bc745c3 [dev.regabi] cmd/compile: delete n.List after collapsing OADDSTR to OLITERAL
+ 2020-11-24 ee6132a698 [dev.regabi] cmd/compile: introduce OMETHEXPR instead of overloading ONAME
+ 2020-11-24 4f9d54e41d [dev.regabi] cmd/compile: add OMETHEXPR
+ 2020-11-24 fd11a32c92 [dev.regabi] cmd/compile: clean up Node.Func
+ 2020-11-24 8e2106327c [dev.regabi] cmd/compile: clean up tests to know less about Node
+ 2020-11-24 742c05e3bc [dev.regabi] cmd/compile: prep refactoring for switching to go/constant
+ 2020-11-24 015423a15b [dev.regabi] strconv: add to bootstrap packages
+ 2020-11-24 c767d73227 [dev.regabi] cmd/compile: remove CTRUNE
+ 2020-11-24 6dae48fb0b [dev.regabi] cmd/compile: refactor type/value assertions
+ 2020-11-24 88a9e2f9ad [dev.regabi] cmd/compile: replace CTNIL with ONIL
+ 2020-11-24 4af2decf30 [dev.regabi] cmd/compile: add (unused) ONIL constant
+ 2020-11-24 668e3a598f [dev.regabi] cmd/compile: cleanup type switch typechecking
+ 2020-11-24 96f3fb7244 [dev.regabi] go/constant: avoid heap allocations in match
+ 2020-11-24 1abb12fc97 [dev.regabi] go/constant: optimize BitLen
+ 2020-11-24 228b732ad9 [dev.regabi] cmd/compile: prepare for package ir
+ 2020-11-24 e37597f7f0 [dev.regabi] cmd/compile: rename a few 'base' identifiers
+ 2020-11-24 357c576878 [dev.regabi] cmd/compile: clean up error API
+ 2020-11-24 5fd949e4bd [dev.regabi] cmd/compile: initialize importMap lazily
+ 2020-11-24 7b144ed4f7 [dev.regabi] cmd/compile: rewrite concurrentFlagOk to be clearer
+ 2020-11-24 c754f25241 [dev.regabi] cmd/compile/internal/types: remove Func.Nname
+ 2020-11-24 c50c7a8c06 [dev.regabi] cmd/compile/internal/gc: refactor to use stop using Func.Nname
+ 2020-11-24 d5928847de [dev.regabi] cmd/compile/internal/gc: prep for Func.Nname removal refactoring
+ 2020-11-24 b30c7a8044 [dev.regabi] cmd/compile/internal/gc: add MethodName for getting referenced method
+ 2020-11-24 e1047302bd [dev.regabi] cmd/compile/internal/types: add pos/sym/typ params to NewField

Change-Id: I9c6085171cb95684cc2c71879b915fa650c31dab

36 files changed:
api/except.txt
api/next.txt
codereview.cfg [new file with mode: 0644]
src/cmd/go/alldocs.go
src/cmd/go/internal/cache/cache.go
src/cmd/go/internal/clean/clean.go
src/cmd/go/internal/load/test.go
src/cmd/go/internal/test/flagdefs.go
src/cmd/go/internal/test/flagdefs_test.go
src/cmd/go/internal/test/genflags.go
src/cmd/go/internal/test/test.go
src/cmd/go/internal/test/testflag.go
src/cmd/go/testdata/script/test_fuzz.txt [new file with mode: 0644]
src/cmd/go/testdata/script/test_fuzz_cache.txt [new file with mode: 0644]
src/cmd/go/testdata/script/test_fuzz_chatty.txt [new file with mode: 0644]
src/cmd/go/testdata/script/test_fuzz_cleanup.txt [new file with mode: 0644]
src/cmd/go/testdata/script/test_fuzz_fuzztime.txt [new file with mode: 0644]
src/cmd/go/testdata/script/test_fuzz_match.txt [new file with mode: 0644]
src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt [new file with mode: 0644]
src/cmd/go/testdata/script/test_fuzz_mutator.txt [new file with mode: 0644]
src/go/build/deps_test.go
src/go/doc/example.go
src/go/doc/example_test.go
src/internal/fuzz/encoding.go [new file with mode: 0644]
src/internal/fuzz/encoding_test.go [new file with mode: 0644]
src/internal/fuzz/fuzz.go [new file with mode: 0644]
src/internal/fuzz/mem.go [new file with mode: 0644]
src/internal/fuzz/mutator.go [new file with mode: 0644]
src/internal/fuzz/pcg.go [new file with mode: 0644]
src/internal/fuzz/sys_posix.go [new file with mode: 0644]
src/internal/fuzz/sys_unimplemented.go [new file with mode: 0644]
src/internal/fuzz/sys_windows.go [new file with mode: 0644]
src/internal/fuzz/worker.go [new file with mode: 0644]
src/testing/fuzz.go [new file with mode: 0644]
src/testing/internal/testdeps/deps.go
src/testing/testing.go

index 6f6f839ba604355c0841fac5d71e26d1937d5782..798b2c2f413694d28a8c1d7d4f597130f08a4f1b 100644 (file)
@@ -489,6 +489,7 @@ pkg syscall (windows-amd64), type CertRevocationInfo struct, OidSpecificInfo uin
 pkg syscall (windows-amd64), type CertSimpleChain struct, TrustListInfo uintptr
 pkg syscall (windows-amd64), type RawSockaddrAny struct, Pad [96]int8
 pkg testing, func MainStart(func(string, string) (bool, error), []InternalTest, []InternalBenchmark, []InternalExample) *M
+pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalExample) *M
 pkg testing, func RegisterCover(Cover)
 pkg text/scanner, const GoTokens = 1012
 pkg text/template/parse, type DotNode bool
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e7d6bee824fa92fb270cc1cf7b0394201ca0be1c 100644 (file)
@@ -0,0 +1,33 @@
+pkg testing, func Fuzz(func(*F)) FuzzResult
+pkg testing, func MainStart(testDeps, []InternalTest, []InternalBenchmark, []InternalFuzzTarget, []InternalExample) *M
+pkg testing, func RunFuzzTargets(func(string, string) (bool, error), []InternalFuzzTarget) bool
+pkg testing, func RunFuzzing(func(string, string) (bool, error), []InternalFuzzTarget) bool
+pkg testing, method (*F) Add(...interface{})
+pkg testing, method (*F) Cleanup(func())
+pkg testing, method (*F) Error(...interface{})
+pkg testing, method (*F) Errorf(string, ...interface{})
+pkg testing, method (*F) Fail()
+pkg testing, method (*F) FailNow()
+pkg testing, method (*F) Failed() bool
+pkg testing, method (*F) Fatal(...interface{})
+pkg testing, method (*F) Fatalf(string, ...interface{})
+pkg testing, method (*F) Fuzz(interface{})
+pkg testing, method (*F) Helper()
+pkg testing, method (*F) Log(...interface{})
+pkg testing, method (*F) Logf(string, ...interface{})
+pkg testing, method (*F) Name() string
+pkg testing, method (*F) Skip(...interface{})
+pkg testing, method (*F) SkipNow()
+pkg testing, method (*F) Skipf(string, ...interface{})
+pkg testing, method (*F) Skipped() bool
+pkg testing, method (*F) TempDir() string
+pkg testing, method (FuzzResult) String() string
+pkg testing, type F struct
+pkg testing, type FuzzResult struct
+pkg testing, type FuzzResult struct, Crasher entry
+pkg testing, type FuzzResult struct, Error error
+pkg testing, type FuzzResult struct, N int
+pkg testing, type FuzzResult struct, T time.Duration
+pkg testing, type InternalFuzzTarget struct
+pkg testing, type InternalFuzzTarget struct, Fn func(*F)
+pkg testing, type InternalFuzzTarget struct, Name string
diff --git a/codereview.cfg b/codereview.cfg
new file mode 100644 (file)
index 0000000..bed9bcf
--- /dev/null
@@ -0,0 +1,2 @@
+branch: dev.fuzz
+parent-branch: master
\ No newline at end of file
index db3f281ef35a3b37a7786d4fd26b29efb8806785..26bc5e785b3d42da2aded81030c930579ced14e3 100644 (file)
 // download cache, including unpacked source code of versioned
 // dependencies.
 //
+// The -fuzzcache flag causes clean to remove values used for fuzz testing.
+//
 // For more about build flags, see 'go help build'.
 //
 // For more about specifying packages, see 'go help packages'.
index 41f921641d499744a2a88224355333c4f76d2693..1a9762bdfba90fdd4b1ef237bcfa1a9f44952079 100644 (file)
@@ -522,3 +522,13 @@ func (c *Cache) copyFile(file io.ReadSeeker, out OutputID, size int64) error {
 
        return nil
 }
+
+// FuzzDir returns a subdirectory within the cache for storing fuzzing data.
+// The subdirectory may not exist.
+//
+// This directory is managed by the internal/fuzz package. Files in this
+// directory aren't removed by the 'go clean -cache' command or by Trim.
+// They may be removed with 'go clean -fuzzcache'.
+func (c *Cache) FuzzDir() string {
+       return filepath.Join(c.dir, "fuzz")
+}
index b1d40feb273b89d6604747ff8e2de6949572221a..788c4b19775a326d74571f9b6006e535e3bf6e4e 100644 (file)
@@ -75,6 +75,8 @@ The -modcache flag causes clean to remove the entire module
 download cache, including unpacked source code of versioned
 dependencies.
 
+The -fuzzcache flag causes clean to remove values used for fuzz testing.
+
 For more about build flags, see 'go help build'.
 
 For more about specifying packages, see 'go help packages'.
@@ -85,6 +87,7 @@ var (
        cleanI         bool // clean -i flag
        cleanR         bool // clean -r flag
        cleanCache     bool // clean -cache flag
+       cleanFuzzcache bool // clean -fuzzcache flag
        cleanModcache  bool // clean -modcache flag
        cleanTestcache bool // clean -testcache flag
 )
@@ -96,6 +99,7 @@ func init() {
        CmdClean.Flag.BoolVar(&cleanI, "i", false, "")
        CmdClean.Flag.BoolVar(&cleanR, "r", false, "")
        CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "")
+       CmdClean.Flag.BoolVar(&cleanFuzzcache, "fuzzcache", false, "")
        CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "")
        CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "")
 
@@ -206,6 +210,18 @@ func runClean(ctx context.Context, cmd *base.Command, args []string) {
                        }
                }
        }
+
+       if cleanFuzzcache {
+               fuzzDir := cache.Default().FuzzDir()
+               if cfg.BuildN || cfg.BuildX {
+                       b.Showcmd("", "rm -rf %s", fuzzDir)
+               }
+               if !cfg.BuildN {
+                       if err := os.RemoveAll(fuzzDir); err != nil {
+                               base.Errorf("go clean -fuzzcache: %v", err)
+                       }
+               }
+       }
 }
 
 var cleaned = map[*load.Package]bool{}
index eb8aef3ee28126d289e39a91f57ca285051e339e..f5d66290922ac977eac93305250013b3f9f839f8 100644 (file)
@@ -527,6 +527,7 @@ func formatTestmain(t *testFuncs) ([]byte, error) {
 type testFuncs struct {
        Tests       []testFunc
        Benchmarks  []testFunc
+       FuzzTargets []testFunc
        Examples    []testFunc
        TestMain    *testFunc
        Package     *Package
@@ -619,6 +620,13 @@ func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
                        }
                        t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
                        *doImport, *seen = true, true
+               case isTest(name, "Fuzz"):
+                       err := checkTestFunc(n, "F")
+                       if err != nil {
+                               return err
+                       }
+                       t.FuzzTargets = append(t.FuzzTargets, testFunc{pkg, name, "", false})
+                       *doImport, *seen = true, true
                }
        }
        ex := doc.Examples(f)
@@ -682,6 +690,12 @@ var benchmarks = []testing.InternalBenchmark{
 {{end}}
 }
 
+var fuzzTargets = []testing.InternalFuzzTarget{
+{{range .FuzzTargets}}
+       {"{{.Name}}", {{.Package}}.{{.Name}}},
+{{end}}
+}
+
 var examples = []testing.InternalExample{
 {{range .Examples}}
        {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
@@ -740,7 +754,7 @@ func main() {
                CoveredPackages: {{printf "%q" .Covered}},
        })
 {{end}}
-       m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
+       m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples)
 {{with .TestMain}}
        {{.Package}}.{{.Name}}(m)
        os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int()))
index 8a0a07683b7cc9778ee96d5011db4baefadc6f3a..c32b89430b56f69ebde457ab4324fa81742e6e26 100644 (file)
@@ -19,6 +19,8 @@ var passFlagToTest = map[string]bool{
        "cpu":                  true,
        "cpuprofile":           true,
        "failfast":             true,
+       "fuzz":                 true,
+       "fuzztime":             true,
        "list":                 true,
        "memprofile":           true,
        "memprofilerate":       true,
index ab5440b3801f15af1124d3ce1738035a894926a0..f238fc7d335e639a55e3dd466d927a9e87ed27b2 100644 (file)
@@ -17,7 +17,7 @@ func TestPassFlagToTestIncludesAllTestFlags(t *testing.T) {
                }
                name := strings.TrimPrefix(f.Name, "test.")
                switch name {
-               case "testlogfile", "paniconexit0":
+               case "testlogfile", "paniconexit0", "fuzzcachedir", "fuzzworker":
                        // These are internal flags.
                default:
                        if !passFlagToTest[name] {
index 30334b0f305ce495cc0475a43f6c08da063c90be..949d65ae808b5f25bb99ef25b77a2af89da3ecd6 100644 (file)
@@ -63,7 +63,7 @@ func testFlags() []string {
                name := strings.TrimPrefix(f.Name, "test.")
 
                switch name {
-               case "testlogfile", "paniconexit0":
+               case "testlogfile", "paniconexit0", "fuzzcachedir", "fuzzworker":
                        // These flags are only for use by cmd/go.
                default:
                        names = append(names, name)
index 7fc9e8fbdcda1afb5837d5b2932329890b49b6a2..c713394141e013922692b7375d0c74acc31cd235 100644 (file)
@@ -475,6 +475,7 @@ var (
        testCoverPaths   []string                          // -coverpkg flag
        testCoverPkgs    []*load.Package                   // -coverpkg flag
        testCoverProfile string                            // -coverprofile flag
+       testFuzz         string                            // -fuzz flag
        testJSON         bool                              // -json flag
        testList         string                            // -list flag
        testO            string                            // -o flag
@@ -1066,6 +1067,8 @@ func declareCoverVars(p *load.Package, files ...string) map[string]*load.CoverVa
 }
 
 var noTestsToRun = []byte("\ntesting: warning: no tests to run\n")
+var noTargetsToFuzz = []byte("\ntesting: warning: no targets to fuzz\n")
+var tooManyTargetsToFuzz = []byte("\ntesting: warning: -fuzz matches more than one target, won't fuzz\n")
 
 type runCache struct {
        disableCache bool // cache should be disabled for this run
@@ -1169,7 +1172,12 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work.
                testlogArg = []string{"-test.testlogfile=" + a.Objdir + "testlog.txt"}
        }
        panicArg := "-test.paniconexit0"
-       args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, testArgs)
+       fuzzArg := []string{}
+       if testFuzz != "" {
+               fuzzCacheDir := filepath.Join(cache.Default().FuzzDir(), a.Package.ImportPath)
+               fuzzArg = []string{"-test.fuzzcachedir=" + fuzzCacheDir}
+       }
+       args := str.StringList(execCmd, a.Deps[0].BuiltTarget(), testlogArg, panicArg, fuzzArg, testArgs)
 
        if testCoverProfile != "" {
                // Write coverage to temporary profile, for merging later.
@@ -1262,6 +1270,12 @@ func (c *runCache) builderRunTest(b *work.Builder, ctx context.Context, a *work.
                if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
                        norun = " [no tests to run]"
                }
+               if bytes.HasPrefix(out, noTargetsToFuzz[1:]) || bytes.Contains(out, noTargetsToFuzz) {
+                       norun = " [no targets to fuzz]"
+               }
+               if bytes.HasPrefix(out, tooManyTargetsToFuzz[1:]) || bytes.Contains(out, tooManyTargetsToFuzz) {
+                       norun = " [will not fuzz, -fuzz matches more than one target]"
+               }
                fmt.Fprintf(cmd.Stdout, "ok  \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun)
                c.saveOutput(a)
        } else {
index 10e6604da5f44b3b1c41f56f0653a667b7019196..e11b41ba76b2f68fadadf3d07d947ec2935139e4 100644 (file)
@@ -56,6 +56,7 @@ func init() {
        cf.String("cpu", "", "")
        cf.StringVar(&testCPUProfile, "cpuprofile", "", "")
        cf.Bool("failfast", false, "")
+       cf.StringVar(&testFuzz, "fuzz", "", "")
        cf.StringVar(&testList, "list", "", "")
        cf.StringVar(&testMemProfile, "memprofile", "", "")
        cf.String("memprofilerate", "", "")
@@ -66,6 +67,7 @@ func init() {
        cf.String("run", "", "")
        cf.Bool("short", false, "")
        cf.DurationVar(&testTimeout, "timeout", 10*time.Minute, "")
+       cf.Duration("fuzztime", 0, "")
        cf.StringVar(&testTrace, "trace", "", "")
        cf.BoolVar(&testV, "v", false, "")
 
diff --git a/src/cmd/go/testdata/script/test_fuzz.txt b/src/cmd/go/testdata/script/test_fuzz.txt
new file mode 100644 (file)
index 0000000..9870f71
--- /dev/null
@@ -0,0 +1,350 @@
+# TODO(jayconrod): support shared memory on more platforms.
+[!darwin] [!linux] [!windows] skip
+
+# Test that running a fuzz target that returns without failing or calling
+# f.Fuzz fails and causes a non-zero exit status.
+! go test noop_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that fuzzing a fuzz target that returns without failing or calling
+# f.Fuzz fails and causes a non-zero exit status.
+! go test -fuzz=Fuzz -fuzztime=5s -parallel=1 noop_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that calling f.Error in a fuzz target causes a non-zero exit status.
+! go test -fuzz=Fuzz -fuzztime=5s -parallel=1 error_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that calling f.Fatal in a fuzz target causes a non-zero exit status.
+! go test fatal_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that successful test exits cleanly.
+go test success_fuzz_test.go
+stdout ^ok
+! stdout FAIL
+
+# Test that successful fuzzing exits cleanly.
+go test -fuzz=Fuzz -fuzztime=5s -parallel=1 success_fuzz_test.go
+stdout ok
+! stdout FAIL
+
+# Test that calling f.Fatal while fuzzing causes a non-zero exit status.
+! go test -fuzz=Fuzz -fuzztime=5s -parallel=1 fatal_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test error with seed corpus in f.Fuzz
+! go test -run FuzzError fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'error here'
+
+[short] stop
+
+# Test that calling panic(nil) in a fuzz target causes a non-zero exit status.
+! go test panic_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that skipped test exits cleanly.
+go test skipped_fuzz_test.go
+stdout ok
+! stdout FAIL
+
+# Test that f.Fatal within f.Fuzz panics
+! go test fatal_fuzz_fn_fuzz_test.go
+! stdout ^ok
+! stdout 'fatal here'
+stdout FAIL
+stdout 'f.Fuzz function'
+
+# Test that f.Error within f.Fuzz panics
+! go test error_fuzz_fn_fuzz_test.go
+! stdout ^ok
+! stdout 'error here'
+stdout FAIL
+stdout 'f.Fuzz function'
+
+# Test that f.Skip within f.Fuzz panics
+! go test skip_fuzz_fn_fuzz_test.go
+! stdout ^ok
+! stdout 'skip here'
+stdout FAIL
+stdout 'f.Fuzz function'
+
+# Test that a call to f.Fatal after the Fuzz func is never executed.
+go test fatal_after_fuzz_func_fuzz_test.go
+stdout ok
+! stdout FAIL
+
+# Test that missing *T in f.Fuzz causes a non-zero exit status.
+! go test incomplete_fuzz_call_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test that a panic in the Cleanup func is executed.
+! go test cleanup_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'failed some precondition'
+
+# Test success with seed corpus in f.Fuzz
+go test -run FuzzPass fuzz_add_test.go
+stdout ok
+! stdout FAIL
+! stdout 'off by one error'
+
+# Test fatal with seed corpus in f.Fuzz
+! go test -run FuzzFatal fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'fatal here'
+
+# Test panic with seed corpus in f.Fuzz
+! go test -run FuzzPanic fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'off by one error'
+
+# Test panic(nil) with seed corpus in f.Fuzz
+! go test -run FuzzNilPanic fuzz_add_test.go
+! stdout ^ok
+stdout FAIL
+
+# Test fatal with testdata seed corpus
+! go test -run FuzzFail corpustesting/fuzz_testdata_corpus_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'fatal here'
+
+# Test pass with testdata seed corpus
+go test -run FuzzPass corpustesting/fuzz_testdata_corpus_test.go
+stdout ok
+! stdout FAIL
+! stdout 'fatal here'
+
+# Test pass with file in other nested testdata directory
+go test -run FuzzInNestedDir corpustesting/fuzz_testdata_corpus_test.go
+stdout ok
+! stdout FAIL
+! stdout 'fatal here'
+
+-- noop_fuzz_test.go --
+package noop_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {}
+
+-- error_fuzz_test.go --
+package error_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Error("error in target")
+}
+
+-- fatal_fuzz_test.go --
+package fatal_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fatal("fatal in target")
+}
+
+-- panic_fuzz_test.go --
+package panic_fuzz
+
+import "testing"
+
+func FuzzPanic(f *testing.F) {
+    panic(nil)
+}
+
+-- success_fuzz_test.go --
+package success_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fuzz(func (*testing.T, []byte) {})
+}
+
+-- skipped_fuzz_test.go --
+package skipped_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Skip()
+}
+
+-- fatal_fuzz_fn_fuzz_test.go --
+package fatal_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Add([]byte("aa"))
+    f.Fuzz(func(t *testing.T, b []byte) {
+        f.Fatal("fatal here")
+    })
+}
+
+-- error_fuzz_fn_fuzz_test.go --
+package error_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Add([]byte("aa"))
+    f.Fuzz(func(t *testing.T, b []byte) {
+        f.Error("error here")
+    })
+}
+
+-- skip_fuzz_fn_fuzz_test.go --
+package skip_fuzz_fn_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Add([]byte("aa"))
+    f.Fuzz(func(t *testing.T, b []byte) {
+        f.Skip("skip here")
+    })
+}
+
+-- fatal_after_fuzz_func_fuzz_test.go --
+package fatal_after_fuzz_func_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fuzz(func(t *testing.T, b []byte) {
+        // no-op
+    })
+    f.Fatal("this shouldn't be called")
+}
+
+-- incomplete_fuzz_call_fuzz_test.go --
+package incomplete_fuzz_call_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fuzz(func(b []byte) {
+        // this is missing *testing.T as the first param, so should panic
+    })
+}
+
+-- cleanup_fuzz_test.go --
+package cleanup_fuzz_test
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Cleanup(func() {
+        panic("failed some precondition")
+    })
+    f.Fuzz(func(t *testing.T, b []byte) {
+        // no-op
+    })
+}
+
+-- fuzz_add_test.go --
+package fuzz_add
+
+import "testing"
+
+func add(f *testing.F) {
+    f.Helper()
+    f.Add([]byte("123"))
+    f.Add([]byte("12345"))
+    f.Add([]byte(""))
+}
+
+func FuzzPass(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == -1 {
+            t.Fatal("fatal here") // will not be executed
+        }
+    })
+}
+
+func FuzzError(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == 3 {
+            t.Error("error here")
+        }
+    })
+}
+
+func FuzzFatal(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == 0 {
+            t.Fatal("fatal here")
+        }
+    })
+}
+
+func FuzzPanic(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == 5 {
+            panic("off by one error")
+        }
+    })
+}
+
+func FuzzNilPanic(f *testing.F) {
+    add(f)
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if len(b) == 3 {
+            panic(nil)
+        }
+    })
+}
+
+-- corpustesting/fuzz_testdata_corpus_test.go --
+package fuzz_testdata_corpus
+
+import "testing"
+
+func fuzzFn(f *testing.F) {
+    f.Helper()
+    f.Fuzz(func(t *testing.T, b []byte) {
+        if string(b) == "12345\n" {
+            t.Fatal("fatal here")
+        }
+    })
+}
+
+func FuzzFail(f *testing.F) {
+    fuzzFn(f)
+}
+
+func FuzzPass(f *testing.F) {
+    fuzzFn(f)
+}
+
+func FuzzInNestedDir(f *testing.F) {
+    fuzzFn(f)
+}
+
+-- corpustesting/testdata/corpus/FuzzFail/1 --
+12345
+-- corpustesting/testdata/corpus/FuzzPass/1 --
+00000
+-- corpustesting/testdata/corpus/FuzzInNestedDir/anotherdir/1 --
+12345
\ No newline at end of file
diff --git a/src/cmd/go/testdata/script/test_fuzz_cache.txt b/src/cmd/go/testdata/script/test_fuzz_cache.txt
new file mode 100644 (file)
index 0000000..b4f5927
--- /dev/null
@@ -0,0 +1,65 @@
+# TODO(jayconrod): support shared memory on more platforms.
+[!darwin] [!linux] [!windows] skip
+
+[short] skip
+env GOCACHE=$WORK/cache
+
+# Fuzz cache should not exist after a regular test run.
+go test .
+exists $GOCACHE
+! exists $GOCACHE/fuzz
+
+# Fuzzing should write interesting values to the cache.
+go test -fuzz=FuzzY -fuzztime=5s -parallel=1 .
+go run ./contains_files $GOCACHE/fuzz/example.com/y/FuzzY
+
+# 'go clean -cache' should not delete the fuzz cache.
+go clean -cache
+exists $GOCACHE/fuzz
+
+# 'go clean -fuzzcache' should delete the fuzz cache but not the build cache.
+go list -f {{.Stale}} ./empty
+stdout true
+go install ./empty
+go list -f {{.Stale}} ./empty
+stdout false
+go clean -fuzzcache
+! exists $GOCACHE/fuzz
+go list -f {{.Stale}} ./empty
+stdout false
+
+-- go.mod --
+module example.com/y
+
+go 1.16
+-- y_test.go --
+package y
+
+import "testing"
+
+func FuzzY(f *testing.F) {
+       f.Add([]byte("y"))
+       f.Fuzz(func(t *testing.T, b []byte) {})
+}
+-- empty/empty.go --
+package empty
+-- contains_files/contains_files.go --
+package main
+
+import (
+       "fmt"
+       "path/filepath"
+       "io/ioutil"
+       "os"
+)
+
+func main() {
+       infos, err := ioutil.ReadDir(filepath.Clean(os.Args[1]))
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       if len(infos) == 0 {
+               os.Exit(1)
+       }
+}
diff --git a/src/cmd/go/testdata/script/test_fuzz_chatty.txt b/src/cmd/go/testdata/script/test_fuzz_chatty.txt
new file mode 100644 (file)
index 0000000..aaf385f
--- /dev/null
@@ -0,0 +1,81 @@
+# TODO(jayconrod): support shared memory on more platforms.
+[!darwin] [!linux] [!windows] skip
+
+[short] skip
+
+# Run chatty fuzz targets with an error.
+! go test -v chatty_error_fuzz_test.go
+! stdout '^ok'
+stdout 'FAIL'
+stdout 'error in target'
+
+# Run chatty fuzz targets with a fatal.
+! go test -v chatty_fatal_fuzz_test.go
+! stdout '^ok'
+stdout 'FAIL'
+stdout 'fatal in target'
+
+# Run chatty fuzz target with a panic
+! go test -v chatty_panic_fuzz_test.go
+! stdout ^ok
+stdout FAIL
+stdout 'this is bad'
+
+# Run skipped chatty fuzz targets.
+go test -v chatty_skipped_fuzz_test.go
+stdout ok
+stdout SKIP
+! stdout FAIL
+
+# Run successful chatty fuzz targets.
+go test -v chatty_fuzz_test.go
+stdout ok
+stdout PASS
+stdout 'all good here'
+! stdout FAIL
+
+-- chatty_error_fuzz_test.go --
+package chatty_error_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Error("error in target")
+}
+
+-- chatty_fatal_fuzz_test.go --
+package chatty_fatal_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Fatal("fatal in target")
+}
+
+-- chatty_panic_fuzz_test.go --
+package chatty_panic_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    panic("this is bad")
+}
+
+-- chatty_skipped_fuzz_test.go --
+package chatty_skipped_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Skip()
+}
+
+-- chatty_fuzz_test.go --
+package chatty_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+    f.Log("all good here")
+    f.Fuzz(func(*testing.T, []byte) {})
+}
diff --git a/src/cmd/go/testdata/script/test_fuzz_cleanup.txt b/src/cmd/go/testdata/script/test_fuzz_cleanup.txt
new file mode 100644 (file)
index 0000000..8862591
--- /dev/null
@@ -0,0 +1,67 @@
+# TODO(jayconrod): support shared memory on more platforms.
+[!darwin] [!linux] [!windows] skip
+[short] skip
+
+# Cleanup should run after F.Skip.
+go test -run=FuzzTargetSkip
+stdout cleanup
+
+# Cleanup should run after F.Fatal.
+! go test -run=FuzzTargetFatal
+stdout cleanup
+
+# Cleanup should run after an unexpected runtime.Goexit.
+! go test -run=FuzzTargetGoexit
+stdout cleanup
+
+# Cleanup should run after panic.
+! go test -run=FuzzTargetPanic
+stdout cleanup
+
+# Cleanup should run in fuzz function on seed corpus.
+go test -v -run=FuzzFunction
+stdout '(?s)inner.*outer'
+
+# TODO(jayconrod): test cleanup while fuzzing. For now, the worker process's
+# stdout and stderr is connected to the coordinator's, but it should eventually
+# be connected to os.DevNull, so we wouldn't see t.Log output.
+
+-- go.mod --
+module cleanup
+
+go 1.15
+-- cleanup_test.go --
+package cleanup
+
+import (
+       "runtime"
+       "testing"
+)
+
+func FuzzTargetSkip(f *testing.F) {
+       f.Cleanup(func() { f.Log("cleanup") })
+       f.Skip()
+}
+
+func FuzzTargetFatal(f *testing.F) {
+       f.Cleanup(func() { f.Log("cleanup") })
+       f.Fatal()
+}
+
+func FuzzTargetGoexit(f *testing.F) {
+       f.Cleanup(func() { f.Log("cleanup") })
+       runtime.Goexit()
+}
+
+func FuzzTargetPanic(f *testing.F) {
+       f.Cleanup(func() { f.Log("cleanup") })
+       panic("oh no")
+}
+
+func FuzzFunction(f *testing.F) {
+       f.Add([]byte{0})
+       f.Cleanup(func() { f.Log("outer") })
+       f.Fuzz(func(t *testing.T, b []byte) {
+               t.Cleanup(func() { t.Logf("inner") })
+       })
+}
diff --git a/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt b/src/cmd/go/testdata/script/test_fuzz_fuzztime.txt
new file mode 100644 (file)
index 0000000..1da095f
--- /dev/null
@@ -0,0 +1,30 @@
+# TODO(jayconrod): support shared memory on more platforms.
+[!darwin] [!linux] [!windows] skip
+
+[short] skip
+
+# There are no seed values, so 'go test' should finish quickly.
+go test
+
+# Fuzzing should exit 0 when after fuzztime, even if timeout is short.
+go test -timeout=10ms -fuzz=FuzzFast -fuzztime=5s -parallel=1
+
+# We should see the same behavior when invoking the test binary directly.
+go test -c
+exec ./fuzz.test$GOEXE -test.timeout=10ms -test.fuzz=FuzzFast -test.fuzztime=5s -test.parallel=1 -test.fuzzcachedir=$WORK/cache
+
+# Timeout should not cause inputs to be written as crashers.
+! exists testdata/corpus
+
+-- go.mod --
+module fuzz
+
+go 1.16
+-- fuzz_test.go --
+package fuzz_test
+
+import "testing"
+
+func FuzzFast(f *testing.F) {
+       f.Fuzz(func (*testing.T, []byte) {})
+}
diff --git a/src/cmd/go/testdata/script/test_fuzz_match.txt b/src/cmd/go/testdata/script/test_fuzz_match.txt
new file mode 100644 (file)
index 0000000..5ead414
--- /dev/null
@@ -0,0 +1,60 @@
+# TODO(jayconrod): support shared memory on more platforms.
+[!darwin] [!linux] [!windows] skip
+
+# Matches only fuzz targets to test.
+go test standalone_fuzz_test.go
+! stdout '^ok.*\[no tests to run\]'
+stdout '^ok'
+
+# Matches only for fuzzing.
+go test -fuzz Fuzz -fuzztime 5s -parallel 1 standalone_fuzz_test.go
+! stdout '^ok.*\[no tests to run\]'
+stdout '^ok'
+
+# Matches none for fuzzing but will run the fuzz target as a test.
+go test -fuzz ThisWillNotMatch -fuzztime 5s -parallel 1 standalone_fuzz_test.go
+! stdout '^ok.*\[no tests to run\]'
+stdout ok
+stdout '\[no targets to fuzz\]'
+
+[short] stop
+
+# Matches only fuzz targets to test with -run.
+go test -run Fuzz standalone_fuzz_test.go
+! stdout '^ok.*\[no tests to run\]'
+stdout '^ok'
+
+# Matches no fuzz targets.
+go test -run ThisWillNotMatch standalone_fuzz_test.go
+stdout '^ok.*\[no tests to run\]'
+! stdout '\[no targets to fuzz\]'
+
+# Matches more than one fuzz target for fuzzing.
+go test -fuzz Fuzz -fuzztime 5s -parallel 1 multiple_fuzz_test.go
+# The tests should run, but not be fuzzed
+! stdout '\[no tests to run\]'
+! stdout '\[no targets to fuzz\]'
+stdout ok
+stdout '\[will not fuzz, -fuzz matches more than one target\]'
+
+-- standalone_fuzz_test.go --
+package standalone_fuzz
+
+import "testing"
+
+func Fuzz(f *testing.F) {
+       f.Fuzz(func (*testing.T, []byte) {})
+}
+
+-- multiple_fuzz_test.go --
+package multiple_fuzz
+
+import "testing"
+
+func FuzzA(f *testing.F) {
+       f.Fuzz(func (*testing.T, []byte) {})
+}
+
+func FuzzB(f *testing.F) {
+       f.Fuzz(func (*testing.T, []byte) {})
+}
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt b/src/cmd/go/testdata/script/test_fuzz_mutate_crash.txt
new file mode 100644 (file)
index 0000000..b45e7d7
--- /dev/null
@@ -0,0 +1,121 @@
+# TODO(jayconrod): support shared memory on more platforms.
+[!darwin] [!linux] [!windows] skip
+
+# Tests that a crash caused by a mutator-discovered input writes the bad input
+# to testdata, and fails+reports correctly. This tests the end-to-end behavior
+# of the mutator finding a crash while fuzzing, adding it as a regression test
+# to the seed corpus in testdata, and failing the next time the test is run.
+
+[short] skip
+
+# TODO: remove -parallel=1 once the races are fixed.
+
+# Running the seed corpus for all of the targets should pass the first
+# time, since nothing in the seed corpus will cause a crash.
+go test -parallel=1
+
+# Running the fuzzer should find a crashing input quickly.
+! go test -fuzz=FuzzWithBug -fuzztime=5s -parallel=1
+stdout 'testdata[/\\]corpus[/\\]FuzzWithBug[/\\]'
+stdout 'this input caused a crash!'
+go run check_testdata.go FuzzWithBug
+
+# Now, the failing bytes should have been added to the seed corpus for
+# the target, and should fail when run without fuzzing.
+! go test -parallel=1
+
+! go test -run=FuzzWithNilPanic -fuzz=FuzzWithNilPanic -fuzztime=5s -parallel=1
+stdout 'testdata[/\\]corpus[/\\]FuzzWithNilPanic[/\\]'
+stdout 'runtime.Goexit'
+go run check_testdata.go FuzzWithNilPanic
+
+! go test -run=FuzzWithBadExit -fuzz=FuzzWithBadExit -fuzztime=5s -parallel=1
+stdout 'testdata[/\\]corpus[/\\]FuzzWithBadExit[/\\]'
+stdout 'unexpectedly'
+go run check_testdata.go FuzzWithBadExit
+
+-- go.mod --
+module m
+
+go 1.16
+-- fuzz_crash_test.go --
+package fuzz_crash
+
+import (
+    "os"
+       "testing"
+)
+
+func FuzzWithBug(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       panic("this input caused a crash!")
+               }
+       })
+}
+
+func FuzzWithNilPanic(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       panic(nil)
+               }
+       })
+}
+
+func FuzzWithBadExit(f *testing.F) {
+       f.Add([]byte("aa"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               if string(b) != "aa" {
+                       os.Exit(1)
+               }
+       })
+}
+
+-- check_testdata.go --
+// +build ignore
+
+package main
+
+import (
+       "bytes"
+       "crypto/sha256"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+)
+
+func main() {
+       target := os.Args[1]
+       dir := filepath.Join("testdata/corpus", target)
+
+       files, err := ioutil.ReadDir(dir)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+
+       if len(files) != 1 {
+               fmt.Fprintln(os.Stderr, fmt.Errorf("expect only one new mutation to be written to testdata", len(files)))
+               os.Exit(1)
+       }
+
+       fname := files[0].Name()
+       contents, err := ioutil.ReadFile(filepath.Join(dir, fname))
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       if bytes.Equal(contents, []byte("aa")) {
+               fmt.Fprintln(os.Stderr, fmt.Errorf("newly written testdata entry was not mutated"))
+               os.Exit(1)
+       }
+       // The hash of the bytes in the file should match the filename.
+       h := []byte(fmt.Sprintf("%x", sha256.Sum256(contents)))
+       if !bytes.Equal([]byte(fname), h) {
+               fmt.Fprintln(os.Stderr, fmt.Errorf("hash of bytes %q does not match filename %q", h, fname))
+               os.Exit(1)
+       }
+}
\ No newline at end of file
diff --git a/src/cmd/go/testdata/script/test_fuzz_mutator.txt b/src/cmd/go/testdata/script/test_fuzz_mutator.txt
new file mode 100644 (file)
index 0000000..a84fc35
--- /dev/null
@@ -0,0 +1,185 @@
+# TODO(jayconrod): support shared memory on more platforms.
+[!darwin] [!linux] [!windows] skip
+
+# Test basic fuzzing mutator behavior.
+#
+# fuzz_test.go has two fuzz targets (FuzzA, FuzzB) which both add a seed value.
+# Each fuzz function writes the input to a log file. The coordinator and worker
+# use separate log files. check_logs.go verifies that the coordinator only
+# tests seed values and the worker tests mutated values on the fuzz target.
+
+[short] skip
+
+go test -fuzz=FuzzA -fuzztime=5s -parallel=1 -log=fuzz
+go run check_logs.go fuzz fuzz.worker
+
+# Test that the mutator is good enough to find several unique mutations.
+! go test -v -fuzz=Fuzz -parallel=1 -fuzztime=30s mutator_test.go
+! stdout ok
+stdout FAIL
+stdout 'mutator found enough unique mutations'
+
+-- go.mod --
+module m
+
+go 1.16
+-- fuzz_test.go --
+package fuzz_test
+
+import (
+       "flag"
+       "fmt"
+       "os"
+       "testing"
+)
+
+var (
+       logPath = flag.String("log", "", "path to log file")
+       logFile *os.File
+)
+
+func TestMain(m *testing.M) {
+       flag.Parse()
+       var err error
+       logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+       if os.IsExist(err) {
+               *logPath += ".worker"
+               logFile, err = os.OpenFile(*logPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+       }
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       os.Exit(m.Run())
+}
+
+func FuzzA(f *testing.F) {
+       f.Add([]byte("seed"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               fmt.Fprintf(logFile, "FuzzA %q\n", b)
+       })
+}
+
+func FuzzB(f *testing.F) {
+       f.Add([]byte("seed"))
+       f.Fuzz(func(t *testing.T, b []byte) {
+               fmt.Fprintf(logFile, "FuzzB %q\n", b)
+       })
+}
+
+-- check_logs.go --
+// +build ignore
+
+package main
+
+import (
+       "bufio"
+       "fmt"
+       "io"
+       "os"
+       "strings"
+)
+
+func main() {
+       coordPath, workerPath := os.Args[1], os.Args[2]
+
+       coordLog, err := os.Open(coordPath)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       defer coordLog.Close()
+       if err := checkCoordLog(coordLog); err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+
+       workerLog, err := os.Open(workerPath)
+       if err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+       defer workerLog.Close()
+       if err := checkWorkerLog(workerLog); err != nil {
+               fmt.Fprintln(os.Stderr, err)
+               os.Exit(1)
+       }
+}
+
+func checkCoordLog(r io.Reader) error {
+       scan := bufio.NewScanner(r)
+       var sawASeed, sawBSeed bool
+       for scan.Scan() {
+               line := scan.Text()
+               switch {
+               case line == `FuzzA "seed"`:
+                       if sawASeed {
+                               return fmt.Errorf("coordinator: tested FuzzA seed multiple times")
+                       }
+                       sawASeed = true
+
+               case line == `FuzzB "seed"`:
+                       if sawBSeed {
+                               return fmt.Errorf("coordinator: tested FuzzB seed multiple times")
+                       }
+                       sawBSeed = true
+
+               default:
+                       return fmt.Errorf("coordinator: tested something other than seeds: %s", line)
+               }
+       }
+       if err := scan.Err(); err != nil {
+               return err
+       }
+       if !sawASeed {
+               return fmt.Errorf("coordinator: did not test FuzzA seed")
+       }
+       if !sawBSeed {
+               return fmt.Errorf("coordinator: did not test FuzzB seed")
+       }
+       return nil
+}
+
+func checkWorkerLog(r io.Reader) error {
+       scan := bufio.NewScanner(r)
+       var sawAMutant bool
+       for scan.Scan() {
+               line := scan.Text()
+               if !strings.HasPrefix(line, "FuzzA ") {
+                       return fmt.Errorf("worker: tested something other than target: %s", line)
+               }
+               if strings.TrimPrefix(line, "FuzzA ") != `"seed"` {
+                       sawAMutant = true
+               }
+       }
+       if err := scan.Err(); err != nil && err != bufio.ErrTooLong {
+               return err
+       }
+       if !sawAMutant {
+               return fmt.Errorf("worker: did not test any mutants")
+       }
+       return nil
+}
+
+-- mutator_test.go --
+package fuzz_test
+
+import (
+       "testing"
+)
+
+// TODO(katiehockman): re-work this test once we have a better fuzzing engine
+// (ie. more mutations, and compiler instrumentation)
+func Fuzz(f *testing.F) {
+       // TODO(katiehockman): simplify this once we can dedupe crashes (e.g.
+       // replace map with calls to panic, and simply count the number of crashes
+       // that were added to testdata)
+       crashes := make(map[string]bool)
+       // No seed corpus initiated
+       f.Fuzz(func(t *testing.T, b []byte) {
+               crashes[string(b)] = true
+               if len(crashes) >= 1000 {
+                       panic("mutator found enough unique mutations")
+               }
+       })
+}
\ No newline at end of file
index e5c849e8f53ea318e22def37a46108ee14d057b5..3511cf41a6b5992673b2d67b79a3fdd68a10781a 100644 (file)
@@ -501,7 +501,10 @@ var depsRules = `
        FMT, flag, runtime/debug, runtime/trace, internal/sysinfo
        < testing;
 
-       internal/testlog, runtime/pprof, regexp
+       FMT, crypto/sha256, encoding/json, go/ast, go/parser, go/token, math/rand
+       < internal/fuzz;
+
+       internal/fuzz, internal/testlog, runtime/pprof, regexp
        < testing/internal/testdeps;
 
        OS, flag, testing, internal/cfg
index 125fd530b133d73c9f805c0041077ba5842a732a..094d7ba61bd98f9cf6531ef363e610730922035d 100644 (file)
@@ -44,13 +44,13 @@ type Example struct {
 //     identifiers from other packages (or predeclared identifiers, such as
 //     "int") and the test file does not include a dot import.
 //   - The entire test file is the example: the file contains exactly one
-//     example function, zero test or benchmark functions, and at least one
-//     top-level function, type, variable, or constant declaration other
-//     than the example function.
+//     example function, zero test, fuzz target, or benchmark function, and at
+//     least one top-level function, type, variable, or constant declaration
+//     other than the example function.
 func Examples(testFiles ...*ast.File) []*Example {
        var list []*Example
        for _, file := range testFiles {
-               hasTests := false // file contains tests or benchmarks
+               hasTests := false // file contains tests, fuzz targets, or benchmarks
                numDecl := 0      // number of non-import declarations in the file
                var flist []*Example
                for _, decl := range file.Decls {
@@ -64,7 +64,7 @@ func Examples(testFiles ...*ast.File) []*Example {
                        }
                        numDecl++
                        name := f.Name.Name
-                       if isTest(name, "Test") || isTest(name, "Benchmark") {
+                       if isTest(name, "Test") || isTest(name, "Benchmark") || isTest(name, "Fuzz") {
                                hasTests = true
                                continue
                        }
@@ -133,9 +133,9 @@ func exampleOutput(b *ast.BlockStmt, comments []*ast.CommentGroup) (output strin
        return "", false, false // no suitable comment found
 }
 
-// isTest tells whether name looks like a test, example, or benchmark.
-// It is a Test (say) if there is a character after Test that is not a
-// lower-case letter. (We don't want Testiness.)
+// isTest tells whether name looks like a test, example, fuzz target, or
+// benchmark. It is a Test (say) if there is a character after Test that is not
+// lower-case letter. (We don't want Testiness.)
 func isTest(name, prefix string) bool {
        if !strings.HasPrefix(name, prefix) {
                return false
index 7c96f0300a8fb5b0622debf805728f21a7ca3c2f..2d9b95803b1af5844c5289e41a4c60dc0b000319 100644 (file)
@@ -307,6 +307,9 @@ func (X) TestBlah() {
 func (X) BenchmarkFoo() {
 }
 
+func (X) FuzzFoo() {
+}
+
 func Example() {
        fmt.Println("Hello, world!")
        // Output: Hello, world!
@@ -326,6 +329,9 @@ func (X) TestBlah() {
 func (X) BenchmarkFoo() {
 }
 
+func (X) FuzzFoo() {
+}
+
 func main() {
        fmt.Println("Hello, world!")
 }
diff --git a/src/internal/fuzz/encoding.go b/src/internal/fuzz/encoding.go
new file mode 100644 (file)
index 0000000..f9403b3
--- /dev/null
@@ -0,0 +1,240 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuzz
+
+import (
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/parser"
+       "go/token"
+       "strconv"
+)
+
+// encVersion1 will be the first line of a file with version 1 encoding.
+var encVersion1 = "version 1"
+
+// marshalCorpusFile encodes an arbitrary number of arguments into the file format for the
+// corpus.
+func marshalCorpusFile(vals ...interface{}) []byte {
+       if len(vals) == 0 {
+               panic("must have at least one value to encode")
+       }
+       b := bytes.NewBuffer([]byte(encVersion1))
+       // TODO(katiehockman): keep uint8 and int32 encoding where applicable,
+       // instead of changing to byte and rune respectively.
+       for _, val := range vals {
+               switch t := val.(type) {
+               case int, int8, int16, int64, uint, uint16, uint32, uint64, uintptr, float32, float64, bool:
+                       fmt.Fprintf(b, "\n%T(%v)", t, t)
+               case string:
+                       fmt.Fprintf(b, "\nstring(%q)", t)
+               case rune: // int32
+                       fmt.Fprintf(b, "\nrune(%q)", t)
+               case byte: // uint8
+                       fmt.Fprintf(b, "\nbyte(%q)", t)
+               case []byte: // []uint8
+                       fmt.Fprintf(b, "\n[]byte(%q)", t)
+               default:
+                       panic(fmt.Sprintf("unsupported type: %T", t))
+               }
+       }
+       return b.Bytes()
+}
+
+// unmarshalCorpusFile decodes corpus bytes into their respective values.
+func unmarshalCorpusFile(b []byte) ([]interface{}, error) {
+       if len(b) == 0 {
+               return nil, fmt.Errorf("cannot decode empty string")
+       }
+       lines := bytes.Split(b, []byte("\n"))
+       if len(lines) < 2 {
+               return nil, fmt.Errorf("must include version and at least one value")
+       }
+       if string(lines[0]) != encVersion1 {
+               return nil, fmt.Errorf("unknown encoding version: %s", lines[0])
+       }
+       var vals []interface{}
+       for _, line := range lines[1:] {
+               line = bytes.TrimSpace(line)
+               if len(line) == 0 {
+                       continue
+               }
+               v, err := parseCorpusValue(line)
+               if err != nil {
+                       return nil, fmt.Errorf("malformed line %q: %v", line, err)
+               }
+               vals = append(vals, v)
+       }
+       return vals, nil
+}
+
+func parseCorpusValue(line []byte) (interface{}, error) {
+       fs := token.NewFileSet()
+       expr, err := parser.ParseExprFrom(fs, "(test)", line, 0)
+       if err != nil {
+               return nil, err
+       }
+       call, ok := expr.(*ast.CallExpr)
+       if !ok {
+               return nil, fmt.Errorf("expected call expression")
+       }
+       if len(call.Args) != 1 {
+               return nil, fmt.Errorf("expected call expression with 1 argument; got %d", len(call.Args))
+       }
+       arg := call.Args[0]
+
+       if arrayType, ok := call.Fun.(*ast.ArrayType); ok {
+               if arrayType.Len != nil {
+                       return nil, fmt.Errorf("expected []byte or primitive type")
+               }
+               elt, ok := arrayType.Elt.(*ast.Ident)
+               if !ok || elt.Name != "byte" {
+                       return nil, fmt.Errorf("expected []byte")
+               }
+               lit, ok := arg.(*ast.BasicLit)
+               if !ok || lit.Kind != token.STRING {
+                       return nil, fmt.Errorf("string literal required for type []byte")
+               }
+               s, err := strconv.Unquote(lit.Value)
+               if err != nil {
+                       return nil, err
+               }
+               return []byte(s), nil
+       }
+
+       idType, ok := call.Fun.(*ast.Ident)
+       if !ok {
+               return nil, fmt.Errorf("expected []byte or primitive type")
+       }
+       if idType.Name == "bool" {
+               id, ok := arg.(*ast.Ident)
+               if !ok {
+                       return nil, fmt.Errorf("malformed bool")
+               }
+               if id.Name == "true" {
+                       return true, nil
+               } else if id.Name == "false" {
+                       return false, nil
+               } else {
+                       return nil, fmt.Errorf("true or false required for type bool")
+               }
+       }
+       var (
+               val  string
+               kind token.Token
+       )
+       if op, ok := arg.(*ast.UnaryExpr); ok {
+               // Special case for negative numbers.
+               lit, ok := op.X.(*ast.BasicLit)
+               if !ok || (lit.Kind != token.INT && lit.Kind != token.FLOAT) {
+                       return nil, fmt.Errorf("expected operation on int or float type")
+               }
+               if op.Op != token.SUB {
+                       return nil, fmt.Errorf("unsupported operation on int: %v", op.Op)
+               }
+               val = op.Op.String() + lit.Value // e.g. "-" + "124"
+               kind = lit.Kind
+       } else {
+               lit, ok := arg.(*ast.BasicLit)
+               if !ok {
+                       return nil, fmt.Errorf("literal value required for primitive type")
+               }
+               val, kind = lit.Value, lit.Kind
+       }
+
+       switch typ := idType.Name; typ {
+       case "string":
+               if kind != token.STRING {
+                       return nil, fmt.Errorf("string literal value required for type string")
+               }
+               return strconv.Unquote(val)
+       case "byte", "rune":
+               if kind != token.CHAR {
+                       return nil, fmt.Errorf("character literal required for byte/rune types")
+               }
+               n := len(val)
+               if n < 2 {
+                       return nil, fmt.Errorf("malformed character literal, missing single quotes")
+               }
+               code, _, _, err := strconv.UnquoteChar(val[1:n-1], '\'')
+               if err != nil {
+                       return nil, err
+               }
+               if typ == "rune" {
+                       return code, nil
+               }
+               if code >= 256 {
+                       return nil, fmt.Errorf("can only encode single byte to a byte type")
+               }
+               return byte(code), nil
+       case "int", "int8", "int16", "int32", "int64":
+               if kind != token.INT {
+                       return nil, fmt.Errorf("integer literal required for int types")
+               }
+               return parseInt(val, typ)
+       case "uint", "uint8", "uint16", "uint32", "uint64":
+               if kind != token.INT {
+                       return nil, fmt.Errorf("integer literal required for uint types")
+               }
+               return parseUint(val, typ)
+       case "float32":
+               if kind != token.FLOAT {
+                       return nil, fmt.Errorf("float literal required for float32 type")
+               }
+               v, err := strconv.ParseFloat(val, 32)
+               return float32(v), err
+       case "float64":
+               if kind != token.FLOAT {
+                       return nil, fmt.Errorf("float literal required for float64 type")
+               }
+               return strconv.ParseFloat(val, 64)
+       default:
+               return nil, fmt.Errorf("expected []byte or primitive type")
+       }
+}
+
+// parseInt returns an integer of value val and type typ.
+func parseInt(val, typ string) (interface{}, error) {
+       switch typ {
+       case "int":
+               return strconv.Atoi(val)
+       case "int8":
+               i, err := strconv.ParseInt(val, 10, 8)
+               return int8(i), err
+       case "int16":
+               i, err := strconv.ParseInt(val, 10, 16)
+               return int16(i), err
+       case "int32":
+               i, err := strconv.ParseInt(val, 10, 32)
+               return int32(i), err
+       case "int64":
+               return strconv.ParseInt(val, 10, 64)
+       default:
+               panic("unreachable")
+       }
+}
+
+// parseInt returns an unsigned integer of value val and type typ.
+func parseUint(val, typ string) (interface{}, error) {
+       switch typ {
+       case "uint":
+               i, err := strconv.ParseUint(val, 10, 0)
+               return uint(i), err
+       case "uint8":
+               i, err := strconv.ParseUint(val, 10, 8)
+               return uint8(i), err
+       case "uint16":
+               i, err := strconv.ParseUint(val, 10, 16)
+               return uint16(i), err
+       case "uint32":
+               i, err := strconv.ParseUint(val, 10, 32)
+               return uint32(i), err
+       case "uint64":
+               return strconv.ParseUint(val, 10, 64)
+       default:
+               panic("unreachable")
+       }
+}
diff --git a/src/internal/fuzz/encoding_test.go b/src/internal/fuzz/encoding_test.go
new file mode 100644 (file)
index 0000000..98d3e21
--- /dev/null
@@ -0,0 +1,115 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuzz
+
+import (
+       "strings"
+       "testing"
+)
+
+func TestUnmarshalMarshal(t *testing.T) {
+       var tests = []struct {
+               in string
+               ok bool
+       }{
+               {
+                       in: "int(1234)",
+                       ok: false, // missing version
+               },
+               {
+                       in: `version 1
+string("a"bcad")`,
+                       ok: false, // malformed
+               },
+               {
+                       in: `version 1
+int()`,
+                       ok: false, // empty value
+               },
+               {
+                       in: `version 1
+uint(-32)`,
+                       ok: false, // invalid negative uint
+               },
+               {
+                       in: `version 1
+int8(1234456)`,
+                       ok: false, // int8 too large
+               },
+               {
+                       in: `version 1
+int(20*5)`,
+                       ok: false, // expression in int value
+               },
+               {
+                       in: `version 1
+int(--5)`,
+                       ok: false, // expression in int value
+               },
+               {
+                       in: `version 1
+bool(0)`,
+                       ok: false, // malformed bool
+               },
+               {
+                       in: `version 1
+byte('aa)`,
+                       ok: false, // malformed byte
+               },
+               {
+                       in: `version 1
+byte('☃')`,
+                       ok: false, // byte out of range
+               },
+               {
+                       in: `version 1
+string("extra")
+[]byte("spacing")  
+    `,
+                       ok: true,
+               },
+               {
+                       in: `version 1
+int(-23)
+int8(-2)
+int64(2342425)
+uint(1)
+uint16(234)
+uint32(352342)
+uint64(123)
+rune('œ')
+byte('K')
+byte('ÿ')
+[]byte("hello¿")
+[]byte("a")
+bool(true)
+string("hello\\xbd\\xb2=\\xbc ⌘")
+float64(-12.5)
+float32(2.5)`,
+                       ok: true,
+               },
+       }
+       for _, test := range tests {
+               t.Run(test.in, func(t *testing.T) {
+                       vals, err := unmarshalCorpusFile([]byte(test.in))
+                       if test.ok && err != nil {
+                               t.Fatalf("unmarshal unexpected error: %v", err)
+                       } else if !test.ok && err == nil {
+                               t.Fatalf("unmarshal unexpected success")
+                       }
+                       if !test.ok {
+                               return // skip the rest of the test
+                       }
+                       newB := marshalCorpusFile(vals...)
+                       if err != nil {
+                               t.Fatalf("marshal unexpected error: %v", err)
+                       }
+                       want := strings.TrimSpace(test.in)
+                       if want != string(newB) {
+                               t.Errorf("values changed after unmarshal then marshal\nbefore: %q\nafter:  %q", want, newB)
+                       }
+               })
+       }
+}
diff --git a/src/internal/fuzz/fuzz.go b/src/internal/fuzz/fuzz.go
new file mode 100644 (file)
index 0000000..ef00933
--- /dev/null
@@ -0,0 +1,327 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package fuzz provides common fuzzing functionality for tests built with
+// "go test" and for programs that use fuzzing functionality in the testing
+// package.
+package fuzz
+
+import (
+       "context"
+       "crypto/sha256"
+       "errors"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "runtime"
+       "sync"
+)
+
+// CoordinateFuzzing creates several worker processes and communicates with
+// them to test random inputs that could trigger crashes and expose bugs.
+// The worker processes run the same binary in the same directory with the
+// same environment variables as the coordinator process. Workers also run
+// with the same arguments as the coordinator, except with the -test.fuzzworker
+// flag prepended to the argument list.
+//
+// parallel is the number of worker processes to run in parallel. If parallel
+// is 0, CoordinateFuzzing will run GOMAXPROCS workers.
+//
+// seed is a list of seed values added by the fuzz target with testing.F.Add and
+// in testdata.
+//
+// corpusDir is a directory where files containing values that crash the
+// code being tested may be written.
+//
+// cacheDir is a directory containing additional "interesting" values.
+// The fuzzer may derive new values from these, and may write new values here.
+//
+// If a crash occurs, the function will return an error containing information
+// about the crash, which can be reported to the user.
+func CoordinateFuzzing(ctx context.Context, parallel int, seed []CorpusEntry, corpusDir, cacheDir string) (err error) {
+       if err := ctx.Err(); err != nil {
+               return err
+       }
+       if parallel == 0 {
+               parallel = runtime.GOMAXPROCS(0)
+       }
+
+       sharedMemSize := 100 << 20 // 100 MB
+       corpus, err := readCache(seed, cacheDir)
+       if err != nil {
+               return err
+       }
+       if len(corpus.entries) == 0 {
+               corpus.entries = []CorpusEntry{{Data: []byte{}}}
+       }
+
+       // TODO(jayconrod): do we want to support fuzzing different binaries?
+       dir := "" // same as self
+       binPath := os.Args[0]
+       args := append([]string{"-test.fuzzworker"}, os.Args[1:]...)
+       env := os.Environ() // same as self
+
+       c := &coordinator{
+               doneC:        make(chan struct{}),
+               inputC:       make(chan CorpusEntry),
+               interestingC: make(chan CorpusEntry),
+               crasherC:     make(chan crasherEntry),
+               errC:         make(chan error),
+       }
+
+       newWorker := func() (*worker, error) {
+               mem, err := sharedMemTempFile(sharedMemSize)
+               if err != nil {
+                       return nil, err
+               }
+               memMu := make(chan *sharedMem, 1)
+               memMu <- mem
+               return &worker{
+                       dir:         dir,
+                       binPath:     binPath,
+                       args:        args,
+                       env:         env,
+                       coordinator: c,
+                       memMu:       memMu,
+               }, nil
+       }
+
+       // Start workers.
+       workers := make([]*worker, parallel)
+       for i := range workers {
+               var err error
+               workers[i], err = newWorker()
+               if err != nil {
+                       return err
+               }
+       }
+
+       workerErrs := make([]error, len(workers))
+       var wg sync.WaitGroup
+       wg.Add(len(workers))
+       for i := range workers {
+               go func(i int) {
+                       defer wg.Done()
+                       workerErrs[i] = workers[i].runFuzzing()
+                       if cleanErr := workers[i].cleanup(); workerErrs[i] == nil {
+                               workerErrs[i] = cleanErr
+                       }
+               }(i)
+       }
+
+       // Before returning, signal workers to stop, wait for them to actually stop,
+       // and gather any errors they encountered.
+       defer func() {
+               close(c.doneC)
+               wg.Wait()
+               if err == nil || err == ctx.Err() {
+                       for _, werr := range workerErrs {
+                               if werr != nil {
+                                       // Return the first error found, replacing ctx.Err() if a more
+                                       // interesting error is found.
+                                       err = werr
+                                       break
+                               }
+                       }
+               }
+       }()
+
+       // Main event loop.
+       i := 0
+       for {
+               select {
+               case <-ctx.Done():
+                       // Interrupted, cancelled, or timed out.
+                       // TODO(jayconrod,katiehockman): On Windows, ^C only interrupts 'go test',
+                       // not the coordinator or worker processes. 'go test' will stop running
+                       // actions, but it won't interrupt its child processes. This makes it
+                       // difficult to stop fuzzing on Windows without a timeout.
+                       return ctx.Err()
+
+               case crasher := <-c.crasherC:
+                       // A worker found a crasher. Write it to testdata and return it.
+                       fileName, err := writeToCorpus(crasher.Data, corpusDir)
+                       if err == nil {
+                               err = &crashError{
+                                       name: filepath.Base(fileName),
+                                       err:  errors.New(crasher.errMsg),
+                               }
+                       }
+                       // TODO(jayconrod,katiehockman): if -keepfuzzing, report the error to
+                       // the user and restart the crashed worker.
+                       return err
+
+               case entry := <-c.interestingC:
+                       // Some interesting input arrived from a worker.
+                       // This is not a crasher, but something interesting that should
+                       // be added to the on disk corpus and prioritized for future
+                       // workers to fuzz.
+                       // TODO(jayconrod, katiehockman): Prioritize fuzzing these values which
+                       // expanded coverage.
+                       // TODO(jayconrod, katiehockman): Don't write a value that's already
+                       // in the corpus.
+                       corpus.entries = append(corpus.entries, entry)
+                       if cacheDir != "" {
+                               if _, err := writeToCorpus(entry.Data, cacheDir); err != nil {
+                                       return err
+                               }
+                       }
+
+               case err := <-c.errC:
+                       // A worker encountered a fatal error.
+                       return err
+
+               case c.inputC <- corpus.entries[i]:
+                       // Send the next input to any worker.
+                       // TODO(jayconrod,katiehockman): need a scheduling algorithm that chooses
+                       // which corpus value to send next (or generates something new).
+                       i = (i + 1) % len(corpus.entries)
+               }
+       }
+
+       // TODO(jayconrod,katiehockman): if a crasher can't be written to corpusDir,
+       // write to cacheDir instead.
+}
+
+// crashError wraps a crasher written to the seed corpus. It saves the name
+// of the file where the input causing the crasher was saved. The testing
+// framework uses this to report a command to re-run that specific input.
+type crashError struct {
+       name string
+       err  error
+}
+
+func (e *crashError) Error() string {
+       return e.err.Error()
+}
+
+func (e *crashError) Unwrap() error {
+       return e.err
+}
+
+func (e *crashError) CrashName() string {
+       return e.name
+}
+
+type corpus struct {
+       entries []CorpusEntry
+}
+
+// CorpusEntry represents an individual input for fuzzing.
+//
+// We must use an equivalent type in the testing and testing/internal/testdeps
+// packages, but testing can't import this package directly, and we don't want
+// to export this type from testing. Instead, we use the same struct type and
+// use a type alias (not a defined type) for convenience.
+type CorpusEntry = struct {
+       // Name is the name of the corpus file, if the entry was loaded from the
+       // seed corpus. It can be used with -run. For entries added with f.Add and
+       // entries generated by the mutator, Name is empty.
+       Name string
+
+       // Data is the raw data loaded from a corpus file.
+       Data []byte
+
+       // TODO(jayconrod,katiehockman): support multiple values of different types
+       // added with f.Add with a Values []interface{} field. We'll need marhsalling
+       // and unmarshalling functions, and we'll need to figure out what to do
+       // in the mutator.
+}
+
+type crasherEntry struct {
+       CorpusEntry
+       errMsg string
+}
+
+// coordinator holds channels that workers can use to communicate with
+// the coordinator.
+type coordinator struct {
+       // doneC is closed to indicate fuzzing is done and workers should stop.
+       // doneC may be closed due to a time limit expiring or a fatal error in
+       // a worker.
+       doneC chan struct{}
+
+       // inputC is sent values to fuzz by the coordinator. Any worker may receive
+       // values from this channel.
+       inputC chan CorpusEntry
+
+       // interestingC is sent interesting values by the worker, which is received
+       // by the coordinator. Values are usually interesting because they
+       // increase coverage.
+       interestingC chan CorpusEntry
+
+       // crasherC is sent values that crashed the code being fuzzed. These values
+       // should be saved in the corpus, and we may want to stop fuzzing after
+       // receiving one.
+       crasherC chan crasherEntry
+
+       // errC is sent internal errors encountered by workers. When the coordinator
+       // receives an error, it closes doneC and returns.
+       errC chan error
+}
+
+// readCache creates a combined corpus from seed values, values in the
+// corpus directory (in testdata), and values in the cache (in GOCACHE/fuzz).
+//
+// TODO(jayconrod,katiehockman): if a value in the cache has the wrong type,
+// ignore it instead of reporting an error. Cached values may be used for
+// the same package at a different version or in a different module.
+// TODO(jayconrod,katiehockman): need a mechanism that can remove values that
+// aren't useful anymore, for example, because they have the wrong type.
+func readCache(seed []CorpusEntry, cacheDir string) (corpus, error) {
+       var c corpus
+       c.entries = append(c.entries, seed...)
+       entries, err := ReadCorpus(cacheDir)
+       if err != nil {
+               return corpus{}, err
+       }
+       c.entries = append(c.entries, entries...)
+       return c, nil
+}
+
+// ReadCorpus reads the corpus from the testdata directory in this target's
+// package.
+func ReadCorpus(dir string) ([]CorpusEntry, error) {
+       files, err := ioutil.ReadDir(dir)
+       if os.IsNotExist(err) {
+               return nil, nil // No corpus to read
+       } else if err != nil {
+               return nil, fmt.Errorf("testing: reading seed corpus from testdata: %v", err)
+       }
+       var corpus []CorpusEntry
+       for _, file := range files {
+               // TODO(jayconrod,katiehockman): determine when a file is a fuzzing input
+               // based on its name. We should only read files created by writeToCorpus.
+               // If we read ALL files, we won't be able to change the file format by
+               // changing the extension. We also won't be able to add files like
+               // README.txt explaining why the directory exists.
+               if file.IsDir() {
+                       continue
+               }
+               bytes, err := ioutil.ReadFile(filepath.Join(dir, file.Name()))
+               if err != nil {
+                       return nil, fmt.Errorf("testing: failed to read corpus file: %v", err)
+               }
+               corpus = append(corpus, CorpusEntry{Name: file.Name(), Data: bytes})
+       }
+       return corpus, nil
+}
+
+// writeToCorpus atomically writes the given bytes to a new file in testdata.
+// If the directory does not exist, it will create one. If the file already
+// exists, writeToCorpus will not rewrite it. writeToCorpus returns the
+// file's name, or an error if it failed.
+func writeToCorpus(b []byte, dir string) (name string, err error) {
+       sum := fmt.Sprintf("%x", sha256.Sum256(b))
+       name = filepath.Join(dir, sum)
+       if err := os.MkdirAll(dir, 0777); err != nil {
+               return "", err
+       }
+       if err := ioutil.WriteFile(name, b, 0666); err != nil {
+               os.Remove(name) // remove partially written file
+               return "", err
+       }
+       return name, nil
+}
diff --git a/src/internal/fuzz/mem.go b/src/internal/fuzz/mem.go
new file mode 100644 (file)
index 0000000..bb30241
--- /dev/null
@@ -0,0 +1,126 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuzz
+
+import (
+       "fmt"
+       "io/ioutil"
+       "os"
+       "unsafe"
+)
+
+// sharedMem manages access to a region of virtual memory mapped from a file,
+// shared between multiple processes. The region includes space for a header and
+// a value of variable length.
+//
+// When fuzzing, the coordinator creates a sharedMem from a temporary file for
+// each worker. This buffer is used to pass values to fuzz between processes.
+// Care must be taken to manage access to shared memory across processes;
+// sharedMem provides no synchronization on its own. See workerComm for an
+// explanation.
+type sharedMem struct {
+       // f is the file mapped into memory.
+       f *os.File
+
+       // region is the mapped region of virtual memory for f. The content of f may
+       // be read or written through this slice.
+       region []byte
+
+       // removeOnClose is true if the file should be deleted by Close.
+       removeOnClose bool
+
+       // sys contains OS-specific information.
+       sys sharedMemSys
+}
+
+// sharedMemHeader stores metadata in shared memory.
+type sharedMemHeader struct {
+       length int
+}
+
+// sharedMemSize returns the size needed for a shared memory buffer that can
+// contain values of the given size.
+func sharedMemSize(valueSize int) int {
+       // TODO(jayconrod): set a reasonable maximum size per platform.
+       return int(unsafe.Sizeof(sharedMemHeader{})) + valueSize
+}
+
+// sharedMemTempFile creates a new temporary file of the given size, then maps
+// it into memory. The file will be removed when the Close method is called.
+func sharedMemTempFile(size int) (m *sharedMem, err error) {
+       // Create a temporary file.
+       f, err := ioutil.TempFile("", "fuzz-*")
+       if err != nil {
+               return nil, err
+       }
+       defer func() {
+               if err != nil {
+                       f.Close()
+                       os.Remove(f.Name())
+               }
+       }()
+
+       // Resize it to the correct size.
+       totalSize := sharedMemSize(size)
+       if err := f.Truncate(int64(totalSize)); err != nil {
+               return nil, err
+       }
+
+       // Map the file into memory.
+       removeOnClose := true
+       return sharedMemMapFile(f, totalSize, removeOnClose)
+}
+
+// header returns a pointer to metadata within the shared memory region.
+func (m *sharedMem) header() *sharedMemHeader {
+       return (*sharedMemHeader)(unsafe.Pointer(&m.region[0]))
+}
+
+// valueRef returns the value currently stored in shared memory. The returned
+// slice points to shared memory; it is not a copy.
+func (m *sharedMem) valueRef() []byte {
+       length := m.header().length
+       valueOffset := int(unsafe.Sizeof(sharedMemHeader{}))
+       return m.region[valueOffset : valueOffset+length]
+}
+
+// valueCopy returns a copy of the value stored in shared memory.
+func (m *sharedMem) valueCopy() []byte {
+       ref := m.valueRef()
+       b := make([]byte, len(ref))
+       copy(b, ref)
+       return b
+}
+
+// setValue copies the data in b into the shared memory buffer and sets
+// the length. len(b) must be less than or equal to the capacity of the buffer
+// (as returned by cap(m.value())).
+func (m *sharedMem) setValue(b []byte) {
+       v := m.valueRef()
+       if len(b) > cap(v) {
+               panic(fmt.Sprintf("value length %d larger than shared memory capacity %d", len(b), cap(v)))
+       }
+       m.header().length = len(b)
+       copy(v[:cap(v)], b)
+}
+
+// setValueLen sets the length of the shared memory buffer returned by valueRef
+// to n, which may be at most the cap of that slice.
+//
+// Note that we can only store the length in the shared memory header. The full
+// slice header contains a pointer, which is likely only valid for one process,
+// since each process can map shared memory at a different virtual address.
+func (m *sharedMem) setValueLen(n int) {
+       v := m.valueRef()
+       if n > cap(v) {
+               panic(fmt.Sprintf("length %d larger than shared memory capacity %d", n, cap(v)))
+       }
+       m.header().length = n
+}
+
+// TODO(jayconrod): add method to resize the buffer. We'll need that when the
+// mutator can increase input length. Only the coordinator will be able to
+// do it, since we'll need to send a message to the worker telling it to
+// remap the file.
diff --git a/src/internal/fuzz/mutator.go b/src/internal/fuzz/mutator.go
new file mode 100644 (file)
index 0000000..377491a
--- /dev/null
@@ -0,0 +1,247 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuzz
+
+import (
+       "encoding/binary"
+       "reflect"
+       "unsafe"
+)
+
+type mutator struct {
+       r *pcgRand
+}
+
+func newMutator() *mutator {
+       return &mutator{r: newPcgRand()}
+}
+
+func (m *mutator) rand(n int) int {
+       return m.r.intn(n)
+}
+
+func (m *mutator) randByteOrder() binary.ByteOrder {
+       if m.r.bool() {
+               return binary.LittleEndian
+       }
+       return binary.BigEndian
+}
+
+// chooseLen chooses length of range mutation in range [0,n]. It gives
+// preference to shorter ranges.
+func (m *mutator) chooseLen(n int) int {
+       switch x := m.rand(100); {
+       case x < 90:
+               return m.rand(min(8, n)) + 1
+       case x < 99:
+               return m.rand(min(32, n)) + 1
+       default:
+               return m.rand(n) + 1
+       }
+}
+
+func min(a, b int) int {
+       if a < b {
+               return a
+       }
+       return b
+}
+
+// mutate performs several mutations directly onto the provided byte slice.
+func (m *mutator) mutate(ptrB *[]byte) {
+       // TODO(jayconrod,katiehockman): make this use zero allocations
+       // TODO(katiehockman): pull some of these functions into helper methods
+       // and test that each case is working as expected.
+       // TODO(katiehockman): perform more types of mutations.
+       b := *ptrB
+       defer func() {
+               oldHdr := (*reflect.SliceHeader)(unsafe.Pointer(ptrB))
+               newHdr := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+               if oldHdr.Data != newHdr.Data {
+                       panic("data moved to new address")
+               }
+               *ptrB = b
+       }()
+
+       numIters := 1 + m.r.exp2()
+       for iter := 0; iter < numIters; iter++ {
+               switch m.rand(10) {
+               case 0:
+                       // Remove a range of bytes.
+                       if len(b) <= 1 {
+                               iter--
+                               continue
+                       }
+                       pos0 := m.rand(len(b))
+                       pos1 := pos0 + m.chooseLen(len(b)-pos0)
+                       copy(b[pos0:], b[pos1:])
+                       b = b[:len(b)-(pos1-pos0)]
+               case 1:
+                       // Insert a range of random bytes.
+                       pos := m.rand(len(b) + 1)
+                       n := m.chooseLen(10)
+                       if len(b)+n >= cap(b) {
+                               iter--
+                               continue
+                       }
+                       b = b[:len(b)+n]
+                       copy(b[pos+n:], b[pos:])
+                       for i := 0; i < n; i++ {
+                               b[pos+i] = byte(m.rand(256))
+                       }
+               case 2:
+                       // Duplicate a range of bytes.
+                       if len(b) <= 1 {
+                               iter--
+                               continue
+                       }
+                       src := m.rand(len(b))
+                       dst := m.rand(len(b))
+                       for dst == src {
+                               dst = m.rand(len(b))
+                       }
+                       n := m.chooseLen(len(b) - src)
+                       tmp := make([]byte, n)
+                       copy(tmp, b[src:])
+                       b = b[:len(b)+n]
+                       copy(b[dst+n:], b[dst:])
+                       copy(b[dst:], tmp)
+               case 3:
+                       // Copy a range of bytes.
+                       if len(b) <= 1 {
+                               iter--
+                               continue
+                       }
+                       src := m.rand(len(b))
+                       dst := m.rand(len(b))
+                       for dst == src {
+                               dst = m.rand(len(b))
+                       }
+                       n := m.chooseLen(len(b) - src)
+                       copy(b[dst:], b[src:src+n])
+               case 4:
+                       // Bit flip.
+                       if len(b) == 0 {
+                               iter--
+                               continue
+                       }
+                       pos := m.rand(len(b))
+                       b[pos] ^= 1 << uint(m.rand(8))
+               case 5:
+                       // Set a byte to a random value.
+                       if len(b) == 0 {
+                               iter--
+                               continue
+                       }
+                       pos := m.rand(len(b))
+                       b[pos] = byte(m.rand(256))
+               case 6:
+                       // Swap 2 bytes.
+                       if len(b) <= 1 {
+                               iter--
+                               continue
+                       }
+                       src := m.rand(len(b))
+                       dst := m.rand(len(b))
+                       for dst == src {
+                               dst = m.rand(len(b))
+                       }
+                       b[src], b[dst] = b[dst], b[src]
+               case 7:
+                       // Add/subtract from a byte.
+                       if len(b) == 0 {
+                               iter--
+                               continue
+                       }
+                       pos := m.rand(len(b))
+                       v := byte(m.rand(35) + 1)
+                       if m.r.bool() {
+                               b[pos] += v
+                       } else {
+                               b[pos] -= v
+                       }
+               case 8:
+                       // Add/subtract from a uint16.
+                       if len(b) < 2 {
+                               iter--
+                               continue
+                       }
+                       v := uint16(m.rand(35) + 1)
+                       if m.r.bool() {
+                               v = 0 - v
+                       }
+                       pos := m.rand(len(b) - 1)
+                       enc := m.randByteOrder()
+                       enc.PutUint16(b[pos:], enc.Uint16(b[pos:])+v)
+               case 9:
+                       // Add/subtract from a uint32.
+                       if len(b) < 4 {
+                               iter--
+                               continue
+                       }
+                       v := uint32(m.rand(35) + 1)
+                       if m.r.bool() {
+                               v = 0 - v
+                       }
+                       pos := m.rand(len(b) - 3)
+                       enc := m.randByteOrder()
+                       enc.PutUint32(b[pos:], enc.Uint32(b[pos:])+v)
+               case 10:
+                       // Add/subtract from a uint64.
+                       if len(b) < 8 {
+                               iter--
+                               continue
+                       }
+                       v := uint64(m.rand(35) + 1)
+                       if m.r.bool() {
+                               v = 0 - v
+                       }
+                       pos := m.rand(len(b) - 7)
+                       enc := m.randByteOrder()
+                       enc.PutUint64(b[pos:], enc.Uint64(b[pos:])+v)
+               case 11:
+                       // Replace a byte with an interesting value.
+                       if len(b) == 0 {
+                               iter--
+                               continue
+                       }
+                       pos := m.rand(len(b))
+                       b[pos] = byte(interesting8[m.rand(len(interesting8))])
+               case 12:
+                       // Replace a uint16 with an interesting value.
+                       if len(b) < 2 {
+                               iter--
+                               continue
+                       }
+                       pos := m.rand(len(b) - 1)
+                       v := uint16(interesting16[m.rand(len(interesting16))])
+                       m.randByteOrder().PutUint16(b[pos:], v)
+               case 13:
+                       // Replace a uint32 with an interesting value.
+                       if len(b) < 4 {
+                               iter--
+                               continue
+                       }
+                       pos := m.rand(len(b) - 3)
+                       v := uint32(interesting32[m.rand(len(interesting32))])
+                       m.randByteOrder().PutUint32(b[pos:], v)
+               }
+       }
+}
+
+var (
+       interesting8  = []int8{-128, -1, 0, 1, 16, 32, 64, 100, 127}
+       interesting16 = []int16{-32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767}
+       interesting32 = []int32{-2147483648, -100663046, -32769, 32768, 65535, 65536, 100663045, 2147483647}
+)
+
+func init() {
+       for _, v := range interesting8 {
+               interesting16 = append(interesting16, int16(v))
+       }
+       for _, v := range interesting16 {
+               interesting32 = append(interesting32, int32(v))
+       }
+}
diff --git a/src/internal/fuzz/pcg.go b/src/internal/fuzz/pcg.go
new file mode 100644 (file)
index 0000000..5f0c1c3
--- /dev/null
@@ -0,0 +1,104 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuzz
+
+import (
+       "math/bits"
+       "sync/atomic"
+       "time"
+)
+
+// The functions in pcg implement a 32 bit PRNG with a 64 bit period: pcg xsh rr
+// 64 32. See https://www.pcg-random.org/ for more information. This
+// implementation is geared specifically towards the needs of fuzzing: Simple
+// creation and use, no reproducibility, no concurrency safety, just the
+// necessary methods, optimized for speed.
+
+var globalInc uint64 // PCG stream
+
+const multiplier uint64 = 6364136223846793005
+
+// pcgRand is a PRNG. It should not be copied or shared. No Rand methods are
+// concurrency safe.
+type pcgRand struct {
+       noCopy noCopy // help avoid mistakes: ask vet to ensure that we don't make a copy
+       state  uint64
+       inc    uint64
+}
+
+// newPcgRand generates a new, seeded Rand, ready for use.
+func newPcgRand() *pcgRand {
+       r := new(pcgRand)
+       now := uint64(time.Now().UnixNano())
+       inc := atomic.AddUint64(&globalInc, 1)
+       r.state = now
+       r.inc = (inc << 1) | 1
+       r.step()
+       r.state += now
+       r.step()
+       return r
+}
+
+func (r *pcgRand) step() {
+       r.state *= multiplier
+       r.state += r.inc
+}
+
+// uint32 returns a pseudo-random uint32.
+func (r *pcgRand) uint32() uint32 {
+       x := r.state
+       r.step()
+       return bits.RotateLeft32(uint32(((x>>18)^x)>>27), -int(x>>59))
+}
+
+// intn returns a pseudo-random number in [0, n).
+// n must fit in a uint32.
+func (r *pcgRand) intn(n int) int {
+       if int(uint32(n)) != n {
+               panic("large Intn")
+       }
+       return int(r.uint32n(uint32(n)))
+}
+
+// uint32n returns a pseudo-random number in [0, n).
+//
+// For implementation details, see:
+// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction
+// https://lemire.me/blog/2016/06/30/fast-random-shuffling
+func (r *pcgRand) uint32n(n uint32) uint32 {
+       v := r.uint32()
+       prod := uint64(v) * uint64(n)
+       low := uint32(prod)
+       if low < n {
+               thresh := uint32(-int32(n)) % n
+               for low < thresh {
+                       v = r.uint32()
+                       prod = uint64(v) * uint64(n)
+                       low = uint32(prod)
+               }
+       }
+       return uint32(prod >> 32)
+}
+
+// exp2 generates n with probability 1/2^(n+1).
+func (r *pcgRand) exp2() int {
+       return bits.TrailingZeros32(r.uint32())
+}
+
+// bool generates a random bool.
+func (r *pcgRand) bool() bool {
+       return r.uint32()&1 == 0
+}
+
+// noCopy may be embedded into structs which must not be copied
+// after the first use.
+//
+// See https://golang.org/issues/8005#issuecomment-190753527
+// for details.
+type noCopy struct{}
+
+// lock is a no-op used by -copylocks checker from `go vet`.
+func (*noCopy) lock()   {}
+func (*noCopy) unlock() {}
diff --git a/src/internal/fuzz/sys_posix.go b/src/internal/fuzz/sys_posix.go
new file mode 100644 (file)
index 0000000..d29ff40
--- /dev/null
@@ -0,0 +1,91 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin linux
+
+package fuzz
+
+import (
+       "fmt"
+       "os"
+       "os/exec"
+       "syscall"
+)
+
+type sharedMemSys struct{}
+
+func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (*sharedMem, error) {
+       prot := syscall.PROT_READ | syscall.PROT_WRITE
+       flags := syscall.MAP_FILE | syscall.MAP_SHARED
+       region, err := syscall.Mmap(int(f.Fd()), 0, size, prot, flags)
+       if err != nil {
+               return nil, err
+       }
+
+       return &sharedMem{f: f, region: region, removeOnClose: removeOnClose}, nil
+}
+
+// Close unmaps the shared memory and closes the temporary file. If this
+// sharedMem was created with sharedMemTempFile, Close also removes the file.
+func (m *sharedMem) Close() error {
+       // Attempt all operations, even if we get an error for an earlier operation.
+       // os.File.Close may fail due to I/O errors, but we still want to delete
+       // the temporary file.
+       var errs []error
+       errs = append(errs,
+               syscall.Munmap(m.region),
+               m.f.Close())
+       if m.removeOnClose {
+               errs = append(errs, os.Remove(m.f.Name()))
+       }
+       for _, err := range errs {
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+// setWorkerComm configures communciation channels on the cmd that will
+// run a worker process.
+func setWorkerComm(cmd *exec.Cmd, comm workerComm) {
+       mem := <-comm.memMu
+       memFile := mem.f
+       comm.memMu <- mem
+       cmd.ExtraFiles = []*os.File{comm.fuzzIn, comm.fuzzOut, memFile}
+}
+
+// getWorkerComm returns communication channels in the worker process.
+func getWorkerComm() (comm workerComm, err error) {
+       fuzzIn := os.NewFile(3, "fuzz_in")
+       fuzzOut := os.NewFile(4, "fuzz_out")
+       memFile := os.NewFile(5, "fuzz_mem")
+       fi, err := memFile.Stat()
+       if err != nil {
+               return workerComm{}, err
+       }
+       size := int(fi.Size())
+       if int64(size) != fi.Size() {
+               return workerComm{}, fmt.Errorf("fuzz temp file exceeds maximum size")
+       }
+       removeOnClose := false
+       mem, err := sharedMemMapFile(memFile, size, removeOnClose)
+       if err != nil {
+               return workerComm{}, err
+       }
+       memMu := make(chan *sharedMem, 1)
+       memMu <- mem
+       return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, memMu: memMu}, nil
+}
+
+// isInterruptError returns whether an error was returned by a process that
+// was terminated by an interrupt signal (SIGINT).
+func isInterruptError(err error) bool {
+       exitErr, ok := err.(*exec.ExitError)
+       if !ok || exitErr.ExitCode() >= 0 {
+               return false
+       }
+       status := exitErr.Sys().(syscall.WaitStatus)
+       return status.Signal() == syscall.SIGINT
+}
diff --git a/src/internal/fuzz/sys_unimplemented.go b/src/internal/fuzz/sys_unimplemented.go
new file mode 100644 (file)
index 0000000..331b876
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// TODO(jayconrod): support more platforms.
+// +build !darwin,!linux,!windows
+
+package fuzz
+
+import (
+       "os"
+       "os/exec"
+)
+
+type sharedMemSys struct{}
+
+func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (*sharedMem, error) {
+       panic("not implemented")
+}
+
+func (m *sharedMem) Close() error {
+       panic("not implemented")
+}
+
+func setWorkerComm(cmd *exec.Cmd, comm workerComm) {
+       panic("not implemented")
+}
+
+func getWorkerComm() (comm workerComm, err error) {
+       panic("not implemented")
+}
+
+func isInterruptError(err error) bool {
+       panic("not implemented")
+}
diff --git a/src/internal/fuzz/sys_windows.go b/src/internal/fuzz/sys_windows.go
new file mode 100644 (file)
index 0000000..6d015c0
--- /dev/null
@@ -0,0 +1,143 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuzz
+
+import (
+       "fmt"
+       "os"
+       "os/exec"
+       "reflect"
+       "strconv"
+       "strings"
+       "syscall"
+       "unsafe"
+)
+
+type sharedMemSys struct {
+       mapObj syscall.Handle
+}
+
+func sharedMemMapFile(f *os.File, size int, removeOnClose bool) (*sharedMem, error) {
+       // Create a file mapping object. The object itself is not shared.
+       mapObj, err := syscall.CreateFileMapping(
+               syscall.Handle(f.Fd()), // fhandle
+               nil,                    // sa
+               syscall.PAGE_READWRITE, // prot
+               0,                      // maxSizeHigh
+               0,                      // maxSizeLow
+               nil,                    // name
+       )
+       if err != nil {
+               return nil, err
+       }
+
+       // Create a view from the file mapping object.
+       access := uint32(syscall.FILE_MAP_READ | syscall.FILE_MAP_WRITE)
+       addr, err := syscall.MapViewOfFile(
+               mapObj,        // handle
+               access,        // access
+               0,             // offsetHigh
+               0,             // offsetLow
+               uintptr(size), // length
+       )
+       if err != nil {
+               syscall.CloseHandle(mapObj)
+               return nil, err
+       }
+
+       var region []byte
+       header := (*reflect.SliceHeader)(unsafe.Pointer(&region))
+       header.Data = addr
+       header.Len = size
+       header.Cap = size
+       return &sharedMem{
+               f:             f,
+               region:        region,
+               removeOnClose: removeOnClose,
+               sys:           sharedMemSys{mapObj: mapObj},
+       }, nil
+}
+
+// Close unmaps the shared memory and closes the temporary file. If this
+// sharedMem was created with sharedMemTempFile, Close also removes the file.
+func (m *sharedMem) Close() error {
+       // Attempt all operations, even if we get an error for an earlier operation.
+       // os.File.Close may fail due to I/O errors, but we still want to delete
+       // the temporary file.
+       var errs []error
+       errs = append(errs,
+               syscall.UnmapViewOfFile(uintptr(unsafe.Pointer(&m.region[0]))),
+               syscall.CloseHandle(m.sys.mapObj),
+               m.f.Close())
+       if m.removeOnClose {
+               errs = append(errs, os.Remove(m.f.Name()))
+       }
+       for _, err := range errs {
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
+// setWorkerComm configures communciation channels on the cmd that will
+// run a worker process.
+func setWorkerComm(cmd *exec.Cmd, comm workerComm) {
+       mem := <-comm.memMu
+       memFD := mem.f.Fd()
+       comm.memMu <- mem
+       syscall.SetHandleInformation(syscall.Handle(comm.fuzzIn.Fd()), syscall.HANDLE_FLAG_INHERIT, 1)
+       syscall.SetHandleInformation(syscall.Handle(comm.fuzzOut.Fd()), syscall.HANDLE_FLAG_INHERIT, 1)
+       syscall.SetHandleInformation(syscall.Handle(memFD), syscall.HANDLE_FLAG_INHERIT, 1)
+       cmd.Env = append(cmd.Env, fmt.Sprintf("GO_TEST_FUZZ_WORKER_HANDLES=%x,%x,%x", comm.fuzzIn.Fd(), comm.fuzzOut.Fd(), memFD))
+}
+
+// getWorkerComm returns communication channels in the worker process.
+func getWorkerComm() (comm workerComm, err error) {
+       v := os.Getenv("GO_TEST_FUZZ_WORKER_HANDLES")
+       if v == "" {
+               return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES not set")
+       }
+       parts := strings.Split(v, ",")
+       if len(parts) != 3 {
+               return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES has invalid value")
+       }
+       base := 16
+       bitSize := 64
+       handles := make([]syscall.Handle, len(parts))
+       for i, s := range parts {
+               h, err := strconv.ParseInt(s, base, bitSize)
+               if err != nil {
+                       return workerComm{}, fmt.Errorf("GO_TEST_FUZZ_WORKER_HANDLES has invalid value: %v", err)
+               }
+               handles[i] = syscall.Handle(h)
+       }
+
+       fuzzIn := os.NewFile(uintptr(handles[0]), "fuzz_in")
+       fuzzOut := os.NewFile(uintptr(handles[1]), "fuzz_out")
+       tmpFile := os.NewFile(uintptr(handles[2]), "fuzz_mem")
+       fi, err := tmpFile.Stat()
+       if err != nil {
+               return workerComm{}, err
+       }
+       size := int(fi.Size())
+       if int64(size) != fi.Size() {
+               return workerComm{}, fmt.Errorf("fuzz temp file exceeds maximum size")
+       }
+       removeOnClose := false
+       mem, err := sharedMemMapFile(tmpFile, size, removeOnClose)
+       if err != nil {
+               return workerComm{}, err
+       }
+       memMu := make(chan *sharedMem, 1)
+       memMu <- mem
+
+       return workerComm{fuzzIn: fuzzIn, fuzzOut: fuzzOut, memMu: memMu}, nil
+}
+
+func isInterruptError(err error) bool {
+       // TODO(jayconrod): implement
+       return false
+}
diff --git a/src/internal/fuzz/worker.go b/src/internal/fuzz/worker.go
new file mode 100644 (file)
index 0000000..f9284db
--- /dev/null
@@ -0,0 +1,552 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fuzz
+
+import (
+       "context"
+       "encoding/json"
+       "errors"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "os/exec"
+       "runtime"
+       "sync"
+       "time"
+)
+
+const (
+       // workerFuzzDuration is the amount of time a worker can spend testing random
+       // variations of an input given by the coordinator.
+       workerFuzzDuration = 100 * time.Millisecond
+
+       // workerTimeoutDuration is the amount of time a worker can go without
+       // responding to the coordinator before being stopped.
+       workerTimeoutDuration = 1 * time.Second
+)
+
+// worker manages a worker process running a test binary. The worker object
+// exists only in the coordinator (the process started by 'go test -fuzz').
+// workerClient is used by the coordinator to send RPCs to the worker process,
+// which handles them with workerServer.
+type worker struct {
+       dir     string   // working directory, same as package directory
+       binPath string   // path to test executable
+       args    []string // arguments for test executable
+       env     []string // environment for test executable
+
+       coordinator *coordinator
+
+       memMu chan *sharedMem // mutex guarding shared memory with worker; persists across processes.
+
+       cmd     *exec.Cmd     // current worker process
+       client  *workerClient // used to communicate with worker process
+       waitErr error         // last error returned by wait, set before termC is closed.
+       termC   chan struct{} // closed by wait when worker process terminates
+}
+
+// cleanup releases persistent resources associated with the worker.
+func (w *worker) cleanup() error {
+       mem := <-w.memMu
+       if mem == nil {
+               return nil
+       }
+       close(w.memMu)
+       return mem.Close()
+}
+
+// runFuzzing runs the test binary to perform fuzzing.
+//
+// This function loops until w.coordinator.doneC is closed or some
+// fatal error is encountered. It receives inputs from w.coordinator.inputC,
+// then passes those on to the worker process.
+func (w *worker) runFuzzing() error {
+       // Start the process.
+       if err := w.start(); err != nil {
+               // We couldn't start the worker process. We can't do anything, and it's
+               // likely that other workers can't either, so give up.
+               w.coordinator.errC <- err
+               return err
+       }
+
+       inputC := w.coordinator.inputC // set to nil when processing input
+       fuzzC := make(chan struct{})   // sent when we finish processing an input.
+
+       // Main event loop.
+       for {
+               select {
+               case <-w.coordinator.doneC:
+                       // All workers were told to stop.
+                       err := w.stop()
+                       if isInterruptError(err) {
+                               // Worker interrupted by SIGINT. This can happen if the worker receives
+                               // SIGINT before installing the signal handler. That's likely if
+                               // TestMain or the fuzz target setup takes a long time.
+                               return nil
+                       }
+                       return err
+
+               case <-w.termC:
+                       // Worker process terminated unexpectedly.
+                       if isInterruptError(w.waitErr) {
+                               // Worker interrupted by SIGINT. See comment in doneC case.
+                               w.stop()
+                               return nil
+                       }
+                       if w.waitErr == nil {
+                               // Worker exited 0.
+                               w.stop()
+                               return fmt.Errorf("worker exited unexpectedly with status 0")
+                       }
+
+                       // Unexpected termination. Inform the coordinator about the crash.
+                       // TODO(jayconrod,katiehockman): if -keepfuzzing, restart worker.
+                       mem := <-w.memMu
+                       value := mem.valueCopy()
+                       w.memMu <- mem
+                       message := fmt.Sprintf("fuzzing process terminated unexpectedly: %v", w.waitErr)
+                       crasher := crasherEntry{
+                               CorpusEntry: CorpusEntry{Data: value},
+                               errMsg:      message,
+                       }
+                       w.coordinator.crasherC <- crasher
+                       return w.stop()
+
+               case input := <-inputC:
+                       // Received input from coordinator.
+                       inputC = nil // block new inputs until we finish with this one.
+                       go func() {
+                               args := fuzzArgs{Duration: workerFuzzDuration}
+                               value, resp, err := w.client.fuzz(input.Data, args)
+                               if err != nil {
+                                       // Error communicating with worker.
+                                       select {
+                                       case <-w.termC:
+                                               // Worker terminated, perhaps unexpectedly.
+                                               // We expect I/O errors due to partially sent or received RPCs,
+                                               // so ignore this error.
+                                       case <-w.coordinator.doneC:
+                                               // Timeout or interruption. Worker may also be interrupted.
+                                               // Again, ignore I/O errors.
+                                       default:
+                                               // TODO(jayconrod): if we get an error here, something failed between
+                                               // main and the call to testing.F.Fuzz. The error here won't
+                                               // be useful. Collect stderr, clean it up, and return that.
+                                               // TODO(jayconrod): we can get EPIPE if w.stop is called concurrently
+                                               // and it kills the worker process. Suppress this message in
+                                               // that case.
+                                               fmt.Fprintf(os.Stderr, "communicating with worker: %v\n", err)
+                                       }
+                                       // TODO(jayconrod): what happens if testing.F.Fuzz is never called?
+                                       // TODO(jayconrod): time out if the test process hangs.
+                               } else if resp.Err != "" {
+                                       // The worker found a crasher. Inform the coordinator.
+                                       crasher := crasherEntry{
+                                               CorpusEntry: CorpusEntry{Data: value},
+                                               errMsg:      resp.Err,
+                                       }
+                                       w.coordinator.crasherC <- crasher
+                               } else {
+                                       // Inform the coordinator that fuzzing found something
+                                       // interesting (i.e. new coverage).
+                                       if resp.Interesting {
+                                               w.coordinator.interestingC <- CorpusEntry{Data: value}
+                                       }
+
+                                       // Continue fuzzing.
+                                       fuzzC <- struct{}{}
+                               }
+                               // TODO(jayconrod,katiehockman): gather statistics.
+                       }()
+
+               case <-fuzzC:
+                       // Worker finished fuzzing and nothing new happened.
+                       inputC = w.coordinator.inputC // unblock new inputs
+               }
+       }
+}
+
+// start runs a new worker process.
+//
+// If the process couldn't be started, start returns an error. Start won't
+// return later termination errors from the process if they occur.
+//
+// If the process starts successfully, start returns nil. stop must be called
+// once later to clean up, even if the process terminates on its own.
+//
+// When the process terminates, w.waitErr is set to the error (if any), and
+// w.termC is closed.
+func (w *worker) start() (err error) {
+       if w.cmd != nil {
+               panic("worker already started")
+       }
+       w.waitErr = nil
+       w.termC = nil
+
+       cmd := exec.Command(w.binPath, w.args...)
+       cmd.Dir = w.dir
+       cmd.Env = w.env
+       // TODO(jayconrod): set stdout and stderr to nil or buffer. A large number
+       // of workers may be very noisy, but for now, this output is useful for
+       // debugging.
+       cmd.Stdout = os.Stdout
+       cmd.Stderr = os.Stderr
+
+       // TODO(jayconrod): set up shared memory between the coordinator and worker to
+       // transfer values and coverage data. If the worker crashes, we need to be
+       // able to find the value that caused the crash.
+
+       // Create the "fuzz_in" and "fuzz_out" pipes so we can communicate with
+       // the worker. We don't use stdin and stdout, since the test binary may
+       // do something else with those.
+       //
+       // Each pipe has a reader and a writer. The coordinator writes to fuzzInW
+       // and reads from fuzzOutR. The worker inherits fuzzInR and fuzzOutW.
+       // The coordinator closes fuzzInR and fuzzOutW after starting the worker,
+       // since we have no further need of them.
+       fuzzInR, fuzzInW, err := os.Pipe()
+       if err != nil {
+               return err
+       }
+       defer fuzzInR.Close()
+       fuzzOutR, fuzzOutW, err := os.Pipe()
+       if err != nil {
+               fuzzInW.Close()
+               return err
+       }
+       defer fuzzOutW.Close()
+       setWorkerComm(cmd, workerComm{fuzzIn: fuzzInR, fuzzOut: fuzzOutW, memMu: w.memMu})
+
+       // Start the worker process.
+       if err := cmd.Start(); err != nil {
+               fuzzInW.Close()
+               fuzzOutR.Close()
+               return err
+       }
+
+       // Worker started successfully.
+       // After this, w.client owns fuzzInW and fuzzOutR, so w.client.Close must be
+       // called later by stop.
+       w.cmd = cmd
+       w.termC = make(chan struct{})
+       w.client = newWorkerClient(workerComm{fuzzIn: fuzzInW, fuzzOut: fuzzOutR, memMu: w.memMu})
+
+       go func() {
+               w.waitErr = w.cmd.Wait()
+               close(w.termC)
+       }()
+
+       return nil
+}
+
+// stop tells the worker process to exit by closing w.client, then blocks until
+// it terminates. If the worker doesn't terminate after a short time, stop
+// signals it with os.Interrupt (where supported), then os.Kill.
+//
+// stop returns the error the process terminated with, if any (same as
+// w.waitErr).
+//
+// stop must be called once after start returns successfully, even if the
+// worker process terminates unexpectedly.
+func (w *worker) stop() error {
+       if w.termC == nil {
+               panic("worker was not started successfully")
+       }
+       select {
+       case <-w.termC:
+               // Worker already terminated, perhaps unexpectedly.
+               if w.client == nil {
+                       panic("worker already stopped")
+               }
+               w.client.Close()
+               w.cmd = nil
+               w.client = nil
+               return w.waitErr
+       default:
+               // Worker still running.
+       }
+
+       // Tell the worker to stop by closing fuzz_in. It won't actually stop until it
+       // finishes with earlier calls.
+       closeC := make(chan struct{})
+       go func() {
+               w.client.Close()
+               close(closeC)
+       }()
+
+       sig := os.Interrupt
+       if runtime.GOOS == "windows" {
+               // Per https://golang.org/pkg/os/#Signal, “Interrupt is not implemented on
+               // Windows; using it with os.Process.Signal will return an error.”
+               // Fall back to Kill instead.
+               sig = os.Kill
+       }
+
+       t := time.NewTimer(workerTimeoutDuration)
+       for {
+               select {
+               case <-w.termC:
+                       // Worker terminated.
+                       t.Stop()
+                       <-closeC
+                       w.cmd = nil
+                       w.client = nil
+                       return w.waitErr
+
+               case <-t.C:
+                       // Timer fired before worker terminated.
+                       switch sig {
+                       case os.Interrupt:
+                               // Try to stop the worker with SIGINT and wait a little longer.
+                               w.cmd.Process.Signal(sig)
+                               sig = os.Kill
+                               t.Reset(workerTimeoutDuration)
+
+                       case os.Kill:
+                               // Try to stop the worker with SIGKILL and keep waiting.
+                               w.cmd.Process.Signal(sig)
+                               sig = nil
+                               t.Reset(workerTimeoutDuration)
+
+                       case nil:
+                               // Still waiting. Print a message to let the user know why.
+                               fmt.Fprintf(os.Stderr, "go: waiting for fuzz worker to terminate...\n")
+                       }
+               }
+       }
+}
+
+// RunFuzzWorker is called in a worker process to communicate with the
+// coordinator process in order to fuzz random inputs. RunFuzzWorker loops
+// until the coordinator tells it to stop.
+//
+// fn is a wrapper on the fuzz function. It may return an error to indicate
+// a given input "crashed". The coordinator will also record a crasher if
+// the function times out or terminates the process.
+//
+// RunFuzzWorker returns an error if it could not communicate with the
+// coordinator process.
+func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error {
+       comm, err := getWorkerComm()
+       if err != nil {
+               return err
+       }
+       srv := &workerServer{workerComm: comm, fuzzFn: fn, m: newMutator()}
+       return srv.serve(ctx)
+}
+
+// call is serialized and sent from the coordinator on fuzz_in. It acts as
+// a minimalist RPC mechanism. Exactly one of its fields must be set to indicate
+// which method to call.
+type call struct {
+       Fuzz *fuzzArgs
+}
+
+// fuzzArgs contains arguments to workerServer.fuzz. The value to fuzz is
+// passed in shared memory.
+type fuzzArgs struct {
+       Duration time.Duration
+}
+
+// fuzzResponse contains results from workerServer.fuzz.
+type fuzzResponse struct {
+       // Interesting indicates the value in shared memory may be interesting to
+       // the coordinator (for example, because it expanded coverage).
+       Interesting bool
+
+       // Err is set if the value in shared memory caused a crash.
+       Err string
+}
+
+// workerComm holds pipes and shared memory used for communication
+// between the coordinator process (client) and a worker process (server).
+// These values are unique to each worker; they are shared only with the
+// coordinator, not with other workers.
+//
+// Access to shared memory is synchronized implicitly over the RPC protocol
+// implemented in workerServer and workerClient. During a call, the client
+// (worker) has exclusive access to shared memory; at other times, the server
+// (coordinator) has exclusive access.
+type workerComm struct {
+       fuzzIn, fuzzOut *os.File
+       memMu           chan *sharedMem // mutex guarding shared memory
+}
+
+// workerServer is a minimalist RPC server, run by fuzz worker processes.
+// It allows the coordinator process (using workerClient) to call methods in a
+// worker process. This system allows the coordinator to run multiple worker
+// processes in parallel and to collect inputs that caused crashes from shared
+// memory after a worker process terminates unexpectedly.
+type workerServer struct {
+       workerComm
+       m *mutator
+
+       // fuzzFn runs the worker's fuzz function on the given input and returns
+       // an error if it finds a crasher (the process may also exit or crash).
+       fuzzFn func(CorpusEntry) error
+}
+
+// serve reads serialized RPC messages on fuzzIn. When serve receives a message,
+// it calls the corresponding method, then sends the serialized result back
+// on fuzzOut.
+//
+// serve handles RPC calls synchronously; it will not attempt to read a message
+// until the previous call has finished.
+//
+// serve returns errors that occurred when communicating over pipes. serve
+// does not return errors from method calls; those are passed through serialized
+// responses.
+func (ws *workerServer) serve(ctx context.Context) error {
+       // Stop handling messages when ctx.Done() is closed. This normally happens
+       // when the worker process receives a SIGINT signal, which on POSIX platforms
+       // is sent to the process group when ^C is pressed.
+       //
+       // Ordinarily, the coordinator process may stop a worker by closing fuzz_in.
+       // We simulate that and interrupt a blocked read here.
+       doneC := make(chan struct{})
+       defer func() { close(doneC) }()
+       go func() {
+               select {
+               case <-ctx.Done():
+                       ws.fuzzIn.Close()
+               case <-doneC:
+               }
+       }()
+
+       enc := json.NewEncoder(ws.fuzzOut)
+       dec := json.NewDecoder(ws.fuzzIn)
+       for {
+               var c call
+               if err := dec.Decode(&c); err != nil {
+                       if ctx.Err() != nil {
+                               return ctx.Err()
+                       } else if err == io.EOF {
+                               return nil
+                       } else {
+                               return err
+                       }
+               }
+
+               var resp interface{}
+               switch {
+               case c.Fuzz != nil:
+                       resp = ws.fuzz(ctx, *c.Fuzz)
+               default:
+                       return errors.New("no arguments provided for any call")
+               }
+
+               if err := enc.Encode(resp); err != nil {
+                       return err
+               }
+       }
+}
+
+// fuzz runs the test function on random variations of a given input value for
+// a given amount of time. fuzz returns early if it finds an input that crashes
+// the fuzz function or an input that expands coverage.
+func (ws *workerServer) fuzz(ctx context.Context, args fuzzArgs) fuzzResponse {
+       ctx, cancel := context.WithTimeout(ctx, args.Duration)
+       defer cancel()
+       mem := <-ws.memMu
+       defer func() { ws.memMu <- mem }()
+
+       for {
+               select {
+               case <-ctx.Done():
+                       // TODO(jayconrod,katiehockman): this value is not interesting. Use a
+                       // real heuristic once we have one.
+                       return fuzzResponse{Interesting: true}
+               default:
+                       b := mem.valueRef()
+                       ws.m.mutate(&b)
+                       mem.setValueLen(len(b))
+                       if err := ws.fuzzFn(CorpusEntry{Data: b}); err != nil {
+                               return fuzzResponse{Err: err.Error()}
+                       }
+                       // TODO(jayconrod,katiehockman): return early if we find an
+                       // interesting value.
+               }
+       }
+}
+
+// workerClient is a minimalist RPC client. The coordinator process uses a
+// workerClient to call methods in each worker process (handled by
+// workerServer).
+type workerClient struct {
+       workerComm
+
+       mu  sync.Mutex
+       enc *json.Encoder
+       dec *json.Decoder
+}
+
+func newWorkerClient(comm workerComm) *workerClient {
+       return &workerClient{
+               workerComm: comm,
+               enc:        json.NewEncoder(comm.fuzzIn),
+               dec:        json.NewDecoder(comm.fuzzOut),
+       }
+}
+
+// Close shuts down the connection to the RPC server (the worker process) by
+// closing fuzz_in. Close drains fuzz_out (avoiding a SIGPIPE in the worker),
+// and closes it after the worker process closes the other end.
+func (wc *workerClient) Close() error {
+       wc.mu.Lock()
+       defer wc.mu.Unlock()
+
+       // Close fuzzIn. This signals to the server that there are no more calls,
+       // and it should exit.
+       if err := wc.fuzzIn.Close(); err != nil {
+               wc.fuzzOut.Close()
+               return err
+       }
+
+       // Drain fuzzOut and close it. When the server exits, the kernel will close
+       // its end of fuzzOut, and we'll get EOF.
+       if _, err := io.Copy(ioutil.Discard, wc.fuzzOut); err != nil {
+               wc.fuzzOut.Close()
+               return err
+       }
+       return wc.fuzzOut.Close()
+}
+
+// errSharedMemClosed is returned by workerClient methods that cannot access
+// shared memory because it was closed and unmapped by another goroutine. That
+// can happen when worker.cleanup is called in the worker goroutine while a
+// workerClient.fuzz call runs concurrently.
+//
+// This error should not be reported. It indicates the operation was
+// interrupted.
+var errSharedMemClosed = errors.New("internal error: shared memory was closed and unmapped")
+
+// fuzz tells the worker to call the fuzz method. See workerServer.fuzz.
+func (wc *workerClient) fuzz(valueIn []byte, args fuzzArgs) (valueOut []byte, resp fuzzResponse, err error) {
+       wc.mu.Lock()
+       defer wc.mu.Unlock()
+
+       mem, ok := <-wc.memMu
+       if !ok {
+               return nil, fuzzResponse{}, errSharedMemClosed
+       }
+       mem.setValue(valueIn)
+       wc.memMu <- mem
+
+       c := call{Fuzz: &args}
+       if err := wc.enc.Encode(c); err != nil {
+               return nil, fuzzResponse{}, err
+       }
+       err = wc.dec.Decode(&resp)
+
+       mem, ok = <-wc.memMu
+       if !ok {
+               return nil, fuzzResponse{}, errSharedMemClosed
+       }
+       valueOut = mem.valueCopy()
+       wc.memMu <- mem
+
+       return valueOut, resp, err
+}
diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go
new file mode 100644 (file)
index 0000000..6b2d910
--- /dev/null
@@ -0,0 +1,551 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testing
+
+import (
+       "errors"
+       "flag"
+       "fmt"
+       "os"
+       "path/filepath"
+       "runtime"
+       "sync/atomic"
+       "time"
+)
+
+func initFuzzFlags() {
+       matchFuzz = flag.String("test.fuzz", "", "run the fuzz target matching `regexp`")
+       fuzzDuration = flag.Duration("test.fuzztime", 0, "time to spend fuzzing; default (0) is to run indefinitely")
+       fuzzCacheDir = flag.String("test.fuzzcachedir", "", "directory where interesting fuzzing inputs are stored")
+       isFuzzWorker = flag.Bool("test.fuzzworker", false, "coordinate with the parent process to fuzz random values")
+}
+
+var (
+       matchFuzz    *string
+       fuzzDuration *time.Duration
+       fuzzCacheDir *string
+       isFuzzWorker *bool
+
+       // corpusDir is the parent directory of the target's seed corpus within
+       // the package.
+       corpusDir = "testdata/corpus"
+)
+
+// InternalFuzzTarget is an internal type but exported because it is cross-package;
+// it is part of the implementation of the "go test" command.
+type InternalFuzzTarget struct {
+       Name string
+       Fn   func(f *F)
+}
+
+// F is a type passed to fuzz targets for fuzz testing.
+type F struct {
+       common
+       fuzzContext *fuzzContext
+       testContext *testContext
+       inFuzzFn    bool          // set to true when fuzz function is running
+       corpus      []corpusEntry // corpus is the in-memory corpus
+       result      FuzzResult    // result is the result of running the fuzz target
+       fuzzCalled  bool
+}
+
+var _ TB = (*F)(nil)
+
+// corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry.
+// We use a type alias because we don't want to export this type, and we can't
+// importing internal/fuzz from testing.
+type corpusEntry = struct {
+       Name string
+       Data []byte
+}
+
+// Cleanup registers a function to be called when the test and all its
+// subtests complete. Cleanup functions will be called in last added,
+// first called order.
+func (f *F) Cleanup(fn func()) {
+       if f.inFuzzFn {
+               panic("testing: f.Cleanup was called inside the f.Fuzz function")
+       }
+       f.common.Cleanup(fn)
+}
+
+// Error is equivalent to Log followed by Fail.
+func (f *F) Error(args ...interface{}) {
+       if f.inFuzzFn {
+               panic("testing: f.Error was called inside the f.Fuzz function")
+       }
+       f.common.Error(args...)
+}
+
+// Errorf is equivalent to Logf followed by Fail.
+func (f *F) Errorf(format string, args ...interface{}) {
+       if f.inFuzzFn {
+               panic("testing: f.Errorf was called inside the f.Fuzz function")
+       }
+       f.common.Errorf(format, args...)
+}
+
+// Fail marks the function as having failed but continues execution.
+func (f *F) Fail() {
+       if f.inFuzzFn {
+               panic("testing: f.Fail was called inside the f.Fuzz function")
+       }
+       f.common.Fail()
+}
+
+// FailNow marks the function as having failed and stops its execution
+// by calling runtime.Goexit (which then runs all deferred calls in the
+// current goroutine).
+// Execution will continue at the next test or benchmark.
+// FailNow must be called from the goroutine running the
+// test or benchmark function, not from other goroutines
+// created during the test. Calling FailNow does not stop
+// those other goroutines.
+func (f *F) FailNow() {
+       if f.inFuzzFn {
+               panic("testing: f.FailNow was called inside the f.Fuzz function")
+       }
+       f.common.FailNow()
+}
+
+// Fatal is equivalent to Log followed by FailNow.
+func (f *F) Fatal(args ...interface{}) {
+       if f.inFuzzFn {
+               panic("testing: f.Fatal was called inside the f.Fuzz function")
+       }
+       f.common.Fatal(args...)
+}
+
+// Fatalf is equivalent to Logf followed by FailNow.
+func (f *F) Fatalf(format string, args ...interface{}) {
+       if f.inFuzzFn {
+               panic("testing: f.Fatalf was called inside the f.Fuzz function")
+       }
+       f.common.Fatalf(format, args...)
+}
+
+// Helper marks the calling function as a test helper function.
+// When printing file and line information, that function will be skipped.
+// Helper may be called simultaneously from multiple goroutines.
+func (f *F) Helper() {
+       if f.inFuzzFn {
+               panic("testing: f.Helper was called inside the f.Fuzz function")
+       }
+       f.common.Helper()
+}
+
+// Skip is equivalent to Log followed by SkipNow.
+func (f *F) Skip(args ...interface{}) {
+       if f.inFuzzFn {
+               panic("testing: f.Skip was called inside the f.Fuzz function")
+       }
+       f.common.Skip(args...)
+}
+
+// SkipNow marks the test as having been skipped and stops its execution
+// by calling runtime.Goexit.
+// If a test fails (see Error, Errorf, Fail) and is then skipped,
+// it is still considered to have failed.
+// Execution will continue at the next test or benchmark. See also FailNow.
+// SkipNow must be called from the goroutine running the test, not from
+// other goroutines created during the test. Calling SkipNow does not stop
+// those other goroutines.
+func (f *F) SkipNow() {
+       if f.inFuzzFn {
+               panic("testing: f.SkipNow was called inside the f.Fuzz function")
+       }
+       f.common.SkipNow()
+}
+
+// Skipf is equivalent to Logf followed by SkipNow.
+func (f *F) Skipf(format string, args ...interface{}) {
+       if f.inFuzzFn {
+               panic("testing: f.Skipf was called inside the f.Fuzz function")
+       }
+       f.common.Skipf(format, args...)
+}
+
+// TempDir returns a temporary directory for the test to use.
+// The directory is automatically removed by Cleanup when the test and
+// all its subtests complete.
+// Each subsequent call to t.TempDir returns a unique directory;
+// if the directory creation fails, TempDir terminates the test by calling Fatal.
+func (f *F) TempDir() string {
+       if f.inFuzzFn {
+               panic("testing: f.TempDir was called inside the f.Fuzz function")
+       }
+       return f.common.TempDir()
+}
+
+// Add will add the arguments to the seed corpus for the fuzz target. This will
+// be a no-op if called after or within the Fuzz function. The args must match
+// those in the Fuzz function.
+func (f *F) Add(args ...interface{}) {
+       if len(args) == 0 {
+               panic("testing: Add must have at least one argument")
+       }
+       if len(args) != 1 {
+               // TODO: support more than one argument
+               panic("testing: Add only supports one argument currently")
+       }
+       switch v := args[0].(type) {
+       case []byte:
+               f.corpus = append(f.corpus, corpusEntry{Data: v})
+       // TODO: support other types
+       default:
+               panic("testing: Add only supports []byte currently")
+       }
+}
+
+// Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of
+// arguments, those arguments will be added to the seed corpus.
+//
+// This is a terminal function which will terminate the currently running fuzz
+// target by calling runtime.Goexit. To run any code after this function, use
+// Cleanup.
+func (f *F) Fuzz(ff interface{}) {
+       if f.fuzzCalled {
+               panic("testing: F.Fuzz called more than once")
+       }
+       f.fuzzCalled = true
+
+       fn, ok := ff.(func(*T, []byte))
+       if !ok {
+               panic("testing: Fuzz function must have type func(*testing.T, []byte)")
+       }
+       f.Helper()
+
+       // Load seed corpus
+       c, err := f.fuzzContext.readCorpus(filepath.Join(corpusDir, f.name))
+       if err != nil {
+               f.Fatal(err)
+       }
+       f.corpus = append(f.corpus, c...)
+
+       // run calls fn on a given input, as a subtest with its own T.
+       // run is analogous to T.Run. The test filtering and cleanup works similarly.
+       // fn is called in its own goroutine.
+       //
+       // TODO(jayconrod,katiehockman): dedupe testdata corpus with entries from f.Add
+       // TODO(jayconrod,katiehockman): handle T.Parallel calls within fuzz function.
+       run := func(e corpusEntry) error {
+               testName, ok, _ := f.testContext.match.fullName(&f.common, e.Name)
+               if !ok || shouldFailFast() {
+                       return nil
+               }
+               // Record the stack trace at the point of this call so that if the subtest
+               // function - which runs in a separate stack - is marked as a helper, we can
+               // continue walking the stack into the parent test.
+               var pc [maxStackLen]uintptr
+               n := runtime.Callers(2, pc[:])
+               t := &T{
+                       common: common{
+                               barrier: make(chan bool),
+                               signal:  make(chan bool),
+                               name:    testName,
+                               parent:  &f.common,
+                               level:   f.level + 1,
+                               creator: pc[:n],
+                               chatty:  f.chatty,
+                       },
+                       context: f.testContext,
+               }
+               t.w = indenter{&t.common}
+               if t.chatty != nil {
+                       t.chatty.Updatef(t.name, "=== RUN  %s\n", t.name)
+               }
+               f.inFuzzFn = true
+               go tRunner(t, func(t *T) { fn(t, e.Data) })
+               <-t.signal
+               f.inFuzzFn = false
+               if t.Failed() {
+                       return errors.New(string(t.output))
+               }
+               return nil
+       }
+
+       switch {
+       case f.fuzzContext.coordinateFuzzing != nil:
+               // Fuzzing is enabled, and this is the test process started by 'go test'.
+               // Act as the coordinator process, and coordinate workers to perform the
+               // actual fuzzing.
+               corpusTargetDir := filepath.Join(corpusDir, f.name)
+               cacheTargetDir := filepath.Join(*fuzzCacheDir, f.name)
+               err := f.fuzzContext.coordinateFuzzing(*fuzzDuration, *parallel, f.corpus, corpusTargetDir, cacheTargetDir)
+               if err != nil {
+                       f.result = FuzzResult{Error: err}
+                       f.Error(err)
+                       if crashErr, ok := err.(fuzzCrashError); ok {
+                               crashName := crashErr.CrashName()
+                               f.Logf("Crash written to %s", filepath.Join("testdata/corpus", f.name, crashName))
+                               f.Logf("To re-run:\ngo test %s -run=%s/%s", f.fuzzContext.importPath(), f.name, crashName)
+                       }
+               }
+               // TODO(jayconrod,katiehockman): Aggregate statistics across workers
+               // and add to FuzzResult (ie. time taken, num iterations)
+
+       case f.fuzzContext.runFuzzWorker != nil:
+               // Fuzzing is enabled, and this is a worker process. Follow instructions
+               // from the coordinator.
+               if err := f.fuzzContext.runFuzzWorker(run); err != nil {
+                       // TODO(jayconrod,katiehockman): how should we handle a failure to
+                       // communicate with the coordinator? Might be caused by the coordinator
+                       // terminating early.
+                       f.Errorf("communicating with fuzzing coordinator: %v", err)
+               }
+
+       default:
+               // Fuzzing is not enabled. Only run the seed corpus.
+               for _, e := range f.corpus {
+                       run(e)
+               }
+       }
+
+       // Record that the fuzz function (or coordinateFuzzing or runFuzzWorker)
+       // returned normally. This is used to distinguish runtime.Goexit below
+       // from panic(nil).
+       f.finished = true
+
+       // Terminate the goroutine. F.Fuzz should not return.
+       // We cannot call runtime.Goexit from a deferred function: if there is a
+       // panic, that would replace the panic value with nil.
+       runtime.Goexit()
+}
+
+func (f *F) report() {
+       if *isFuzzWorker || f.parent == nil {
+               return
+       }
+       dstr := fmtDuration(f.duration)
+       format := "--- %s: %s (%s)\n"
+       if f.Failed() {
+               f.flushToParent(f.name, format, "FAIL", f.name, dstr)
+       } else if f.chatty != nil {
+               if f.Skipped() {
+                       f.flushToParent(f.name, format, "SKIP", f.name, dstr)
+               } else {
+                       f.flushToParent(f.name, format, "PASS", f.name, dstr)
+               }
+       }
+}
+
+// FuzzResult contains the results of a fuzz run.
+type FuzzResult struct {
+       N     int           // The number of iterations.
+       T     time.Duration // The total time taken.
+       Error error         // Error is the error from the crash
+}
+
+func (r FuzzResult) String() string {
+       s := ""
+       if r.Error == nil {
+               return s
+       }
+       s = fmt.Sprintf("%s", r.Error.Error())
+       return s
+}
+
+// fuzzCrashError is satisfied by a crash detected within the fuzz function.
+// These errors are written to the seed corpus and can be re-run with 'go test'.
+// Errors within the fuzzing framework (like I/O errors between coordinator
+// and worker processes) don't satisfy this interface.
+type fuzzCrashError interface {
+       error
+       Unwrap() error
+
+       // CrashName returns the name of the subtest that corresponds to the saved
+       // crash input file in the seed corpus. The test can be re-run with
+       // go test $pkg -run=$target/$name where $pkg is the package's import path,
+       // $target is the fuzz target name, and $name is the string returned here.
+       CrashName() string
+}
+
+// fuzzContext holds all fields that are common to all fuzz targets.
+type fuzzContext struct {
+       importPath        func() string
+       coordinateFuzzing func(time.Duration, int, []corpusEntry, string, string) error
+       runFuzzWorker     func(func(corpusEntry) error) error
+       readCorpus        func(string) ([]corpusEntry, error)
+}
+
+// runFuzzTargets runs the fuzz targets matching the pattern for -run. This will
+// only run the f.Fuzz function for each seed corpus without using the fuzzing
+// engine to generate or mutate inputs.
+func runFuzzTargets(deps testDeps, fuzzTargets []InternalFuzzTarget) (ran, ok bool) {
+       ok = true
+       if len(fuzzTargets) == 0 || *isFuzzWorker {
+               return ran, ok
+       }
+       m := newMatcher(deps.MatchString, *match, "-test.run")
+       tctx := newTestContext(*parallel, m)
+       fctx := &fuzzContext{
+               importPath: deps.ImportPath,
+               readCorpus: deps.ReadCorpus,
+       }
+       root := common{w: os.Stdout} // gather output in one place
+       if Verbose() {
+               root.chatty = newChattyPrinter(root.w)
+       }
+       for _, ft := range fuzzTargets {
+               if shouldFailFast() {
+                       break
+               }
+               testName, matched, _ := tctx.match.fullName(nil, ft.Name)
+               if !matched {
+                       continue
+               }
+               f := &F{
+                       common: common{
+                               signal: make(chan bool),
+                               name:   testName,
+                               parent: &root,
+                               level:  root.level + 1,
+                               chatty: root.chatty,
+                       },
+                       testContext: tctx,
+                       fuzzContext: fctx,
+               }
+               f.w = indenter{&f.common}
+               if f.chatty != nil {
+                       f.chatty.Updatef(f.name, "=== RUN  %s\n", f.name)
+               }
+
+               go fRunner(f, ft.Fn)
+               <-f.signal
+       }
+       return root.ran, !root.Failed()
+}
+
+// runFuzzing runs the fuzz target matching the pattern for -fuzz. Only one such
+// fuzz target must match. This will run the fuzzing engine to generate and
+// mutate new inputs against the f.Fuzz function.
+//
+// If fuzzing is disabled (-test.fuzz is not set), runFuzzing
+// returns immediately.
+func runFuzzing(deps testDeps, fuzzTargets []InternalFuzzTarget) (ran, ok bool) {
+       if len(fuzzTargets) == 0 || *matchFuzz == "" {
+               return false, true
+       }
+       m := newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz")
+       tctx := newTestContext(1, m)
+       fctx := &fuzzContext{
+               importPath: deps.ImportPath,
+               readCorpus: deps.ReadCorpus,
+       }
+       if *isFuzzWorker {
+               fctx.runFuzzWorker = deps.RunFuzzWorker
+       } else {
+               fctx.coordinateFuzzing = deps.CoordinateFuzzing
+       }
+       root := common{w: os.Stdout}
+       if Verbose() && !*isFuzzWorker {
+               root.chatty = newChattyPrinter(root.w)
+       }
+       var target *InternalFuzzTarget
+       var f *F
+       for i := range fuzzTargets {
+               ft := &fuzzTargets[i]
+               testName, matched, _ := tctx.match.fullName(nil, ft.Name)
+               if !matched {
+                       continue
+               }
+               if target != nil {
+                       fmt.Fprintln(os.Stderr, "testing: warning: -fuzz matches more than one target, won't fuzz")
+                       return false, true
+               }
+               target = ft
+               f = &F{
+                       common: common{
+                               signal: make(chan bool),
+                               name:   testName,
+                               parent: &root,
+                               level:  root.level + 1,
+                               chatty: root.chatty,
+                       },
+                       fuzzContext: fctx,
+                       testContext: tctx,
+               }
+               f.w = indenter{&f.common}
+       }
+       if target == nil {
+               return false, true
+       }
+       if f.chatty != nil {
+               f.chatty.Updatef(f.name, "=== FUZZ  %s\n", f.name)
+       }
+       go fRunner(f, target.Fn)
+       <-f.signal
+       return f.ran, !f.failed
+}
+
+// fRunner wraps a call to a fuzz target and ensures that cleanup functions are
+// called and status flags are set. fRunner should be called in its own
+// goroutine. To wait for its completion, receive f.signal.
+//
+// fRunner is analogous with tRunner, which wraps subtests started with T.Run.
+// Tests and fuzz targets work a little differently, so for now, these functions
+// aren't consoldiated.
+func fRunner(f *F, fn func(*F)) {
+       // When this goroutine is done, either because runtime.Goexit was called,
+       // a panic started, or fn returned normally, record the duration and send
+       // t.signal, indicating the fuzz target is done.
+       defer func() {
+               // Detect whether the fuzz target panicked or called runtime.Goexit without
+               // calling F.Fuzz, F.Fail, or F.Skip. If it did, panic (possibly replacing
+               // a nil panic value). Nothing should recover after fRunner unwinds,
+               // so this should crash the process with a stack. Unfortunately, recovering
+               // here adds stack frames, but the location of the original panic should
+               // still be clear.
+               if f.Failed() {
+                       atomic.AddUint32(&numFailed, 1)
+               }
+               err := recover()
+               f.mu.RLock()
+               ok := f.skipped || f.failed || (f.fuzzCalled && f.finished)
+               f.mu.RUnlock()
+               if err == nil && !ok {
+                       err = errNilPanicOrGoexit
+               }
+
+               // If we recovered a panic or inappropriate runtime.Goexit, fail the test,
+               // flush the output log up to the root, then panic.
+               if err != nil {
+                       f.Fail()
+                       for root := &f.common; root.parent != nil; root = root.parent {
+                               root.mu.Lock()
+                               root.duration += time.Since(root.start)
+                               d := root.duration
+                               root.mu.Unlock()
+                               root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d))
+                       }
+                       panic(err)
+               }
+
+               // No panic or inappropriate Goexit. Record duration and report the result.
+               f.duration += time.Since(f.start)
+               f.report()
+               f.done = true
+               f.setRan()
+
+               // Only report that the test is complete if it doesn't panic,
+               // as otherwise the test binary can exit before the panic is
+               // reported to the user. See issue 41479.
+               f.signal <- true
+       }()
+       defer func() {
+               f.runCleanup(normalPanic)
+       }()
+
+       f.start = time.Now()
+       fn(f)
+
+       // Code beyond this point is only executed if fn returned normally.
+       // That means fn did not call F.Fuzz or F.Skip. It should have called F.Fail.
+       f.mu.Lock()
+       defer f.mu.Unlock()
+       if !f.failed {
+               panic(f.name + " returned without calling F.Fuzz, F.Fail, or F.Skip")
+       }
+}
index 3608d332946e2ebee0558a35c35c7930a65e923a..3160cae7a4ea9c8468e97b73ea9779222c65d0c7 100644 (file)
@@ -12,12 +12,17 @@ package testdeps
 
 import (
        "bufio"
+       "context"
+       "internal/fuzz"
        "internal/testlog"
        "io"
+       "os"
+       "os/signal"
        "regexp"
        "runtime/pprof"
        "strings"
        "sync"
+       "time"
 )
 
 // TestDeps is an implementation of the testing.testDeps interface,
@@ -126,3 +131,43 @@ func (TestDeps) StopTestLog() error {
 func (TestDeps) SetPanicOnExit0(v bool) {
        testlog.SetPanicOnExit0(v)
 }
+
+func (TestDeps) CoordinateFuzzing(timeout time.Duration, parallel int, seed []fuzz.CorpusEntry, corpusDir, cacheDir string) (err error) {
+       // Fuzzing may be interrupted with a timeout or if the user presses ^C.
+       // In either case, we'll stop worker processes gracefully and save
+       // crashers and interesting values.
+       ctx, cancel := context.WithCancel(context.Background())
+       if timeout > 0 {
+               ctx, cancel = context.WithTimeout(ctx, timeout)
+       }
+       ctx, stop := signal.NotifyContext(ctx, os.Interrupt)
+       defer stop()
+       defer cancel()
+       err = fuzz.CoordinateFuzzing(ctx, parallel, seed, corpusDir, cacheDir)
+       if err == ctx.Err() {
+               return nil
+       }
+       return err
+}
+
+func (TestDeps) RunFuzzWorker(fn func(fuzz.CorpusEntry) error) error {
+       // Worker processes may or may not receive a signal when the user presses ^C
+       // On POSIX operating systems, a signal sent to a process group is delivered
+       // to all processes in that group. This is not the case on Windows.
+       // If the worker is interrupted, return quickly and without error.
+       // If only the coordinator process is interrupted, it tells each worker
+       // process to stop by closing its "fuzz_in" pipe.
+       ctx, cancel := context.WithCancel(context.Background())
+       ctx, stop := signal.NotifyContext(ctx, os.Interrupt)
+       defer stop()
+       defer cancel()
+       err := fuzz.RunFuzzWorker(ctx, fn)
+       if err == ctx.Err() {
+               return nil
+       }
+       return nil
+}
+
+func (TestDeps) ReadCorpus(dir string) ([]fuzz.CorpusEntry, error) {
+       return fuzz.ReadCorpus(dir)
+}
index 80354d5ce8390735d7409db722acc0a4079397c4..2e38898c9834690d3aacdedab8c239bc2925b72d 100644 (file)
@@ -301,6 +301,7 @@ func Init() {
        testlog = flag.String("test.testlogfile", "", "write test action log to `file` (for use only by cmd/go)")
 
        initBenchmarkFlags()
+       initFuzzFlags()
 }
 
 var (
@@ -1323,6 +1324,11 @@ func (f matchStringOnly) ImportPath() string                          { return "
 func (f matchStringOnly) StartTestLog(io.Writer)                      {}
 func (f matchStringOnly) StopTestLog() error                          { return errMain }
 func (f matchStringOnly) SetPanicOnExit0(bool)                        {}
+func (f matchStringOnly) CoordinateFuzzing(time.Duration, int, []corpusEntry, string, string) error {
+       return errMain
+}
+func (f matchStringOnly) RunFuzzWorker(func(corpusEntry) error) error { return errMain }
+func (f matchStringOnly) ReadCorpus(string) ([]corpusEntry, error)    { return nil, errMain }
 
 // Main is an internal function, part of the implementation of the "go test" command.
 // It was exported because it is cross-package and predates "internal" packages.
@@ -1331,15 +1337,16 @@ func (f matchStringOnly) SetPanicOnExit0(bool)                        {}
 // new functionality is added to the testing package.
 // Systems simulating "go test" should be updated to use MainStart.
 func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
-       os.Exit(MainStart(matchStringOnly(matchString), tests, benchmarks, examples).Run())
+       os.Exit(MainStart(matchStringOnly(matchString), tests, benchmarks, nil, examples).Run())
 }
 
 // M is a type passed to a TestMain function to run the actual tests.
 type M struct {
-       deps       testDeps
-       tests      []InternalTest
-       benchmarks []InternalBenchmark
-       examples   []InternalExample
+       deps        testDeps
+       tests       []InternalTest
+       benchmarks  []InternalBenchmark
+       fuzzTargets []InternalFuzzTarget
+       examples    []InternalExample
 
        timer     *time.Timer
        afterOnce sync.Once
@@ -1364,18 +1371,22 @@ type testDeps interface {
        StartTestLog(io.Writer)
        StopTestLog() error
        WriteProfileTo(string, io.Writer, int) error
+       CoordinateFuzzing(time.Duration, int, []corpusEntry, string, string) error
+       RunFuzzWorker(func(corpusEntry) error) error
+       ReadCorpus(string) ([]corpusEntry, error)
 }
 
 // MainStart is meant for use by tests generated by 'go test'.
 // It is not meant to be called directly and is not subject to the Go 1 compatibility document.
 // It may change signature from release to release.
-func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
+func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
        Init()
        return &M{
-               deps:       deps,
-               tests:      tests,
-               benchmarks: benchmarks,
-               examples:   examples,
+               deps:        deps,
+               tests:       tests,
+               benchmarks:  benchmarks,
+               fuzzTargets: fuzzTargets,
+               examples:    examples,
        }
 }
 
@@ -1402,9 +1413,21 @@ func (m *M) Run() (code int) {
                m.exitCode = 2
                return
        }
+       if *fuzzDuration < 0 {
+               fmt.Fprintln(os.Stderr, "testing: -fuzztime can only be given a positive duration, or zero to run indefinitely")
+               flag.Usage()
+               m.exitCode = 2
+               return
+       }
+       if *matchFuzz != "" && *fuzzCacheDir == "" {
+               fmt.Fprintln(os.Stderr, "testing: internal error: -test.fuzzcachedir must be set if -test.fuzz is set")
+               flag.Usage()
+               m.exitCode = 2
+               return
+       }
 
        if len(*matchList) != 0 {
-               listTests(m.deps.MatchString, m.tests, m.benchmarks, m.examples)
+               listTests(m.deps.MatchString, m.tests, m.benchmarks, m.fuzzTargets, m.examples)
                m.exitCode = 0
                return
        }
@@ -1416,19 +1439,32 @@ func (m *M) Run() (code int) {
        deadline := m.startAlarm()
        haveExamples = len(m.examples) > 0
        testRan, testOk := runTests(m.deps.MatchString, m.tests, deadline)
+       fuzzTargetsRan, fuzzTargetsOk := runFuzzTargets(m.deps, m.fuzzTargets)
        exampleRan, exampleOk := runExamples(m.deps.MatchString, m.examples)
        m.stopAlarm()
-       if !testRan && !exampleRan && *matchBenchmarks == "" {
+       if !testRan && !exampleRan && !fuzzTargetsRan && *matchBenchmarks == "" && *matchFuzz == "" {
                fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
        }
-       if !testOk || !exampleOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
+       if !testOk || !exampleOk || !fuzzTargetsOk || !runBenchmarks(m.deps.ImportPath(), m.deps.MatchString, m.benchmarks) || race.Errors() > 0 {
+               fmt.Println("FAIL")
+               m.exitCode = 1
+               return
+       }
+
+       fuzzingRan, fuzzingOk := runFuzzing(m.deps, m.fuzzTargets)
+       if *matchFuzz != "" && !fuzzingRan {
+               fmt.Fprintln(os.Stderr, "testing: warning: no targets to fuzz")
+       }
+       if !fuzzingOk && !*isFuzzWorker {
                fmt.Println("FAIL")
                m.exitCode = 1
                return
        }
 
-       fmt.Println("PASS")
        m.exitCode = 0
+       if !*isFuzzWorker {
+               fmt.Println("PASS")
+       }
        return
 }
 
@@ -1449,7 +1485,7 @@ func (t *T) report() {
        }
 }
 
-func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
+func listTests(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) {
        if _, err := matchString(*matchList, "non-empty"); err != nil {
                fmt.Fprintf(os.Stderr, "testing: invalid regexp in -test.list (%q): %s\n", *matchList, err)
                os.Exit(1)
@@ -1465,6 +1501,11 @@ func listTests(matchString func(pat, str string) (bool, error), tests []Internal
                        fmt.Println(bench.Name)
                }
        }
+       for _, fuzzTarget := range fuzzTargets {
+               if ok, _ := matchString(*matchList, fuzzTarget.Name); ok {
+                       fmt.Println(fuzzTarget.Name)
+               }
+       }
        for _, example := range examples {
                if ok, _ := matchString(*matchList, example.Name); ok {
                        fmt.Println(example.Name)