//
// -fuzz regexp
// Run the fuzz target matching the regular expression. When specified,
-// the command line argument must match exactly one package, and regexp
-// must match exactly one fuzz target within that package. After tests,
-// benchmarks, seed corpora of other fuzz targets, and examples have
-// completed, the matching target will be fuzzed. See the Fuzzing section
-// of the testing package documentation for details.
+// the command line argument must match exactly one package within the
+// main module, and regexp must match exactly one fuzz target within
+// that package. After tests, benchmarks, seed corpora of other fuzz
+// targets, and examples have completed, the matching target will be
+// fuzzed. See the Fuzzing section of the testing package documentation
+// for details.
//
// -fuzztime t
// Run enough iterations of the fuzz test to take t, specified as a
"cmd/go/internal/work"
"cmd/internal/sys"
"cmd/internal/test2json"
+
+ "golang.org/x/mod/module"
)
// Break init loop.
-fuzz regexp
Run the fuzz target matching the regular expression. When specified,
- the command line argument must match exactly one package, and regexp
- must match exactly one fuzz target within that package. After tests,
- benchmarks, seed corpora of other fuzz targets, and examples have
- completed, the matching target will be fuzzed. See the Fuzzing section
- of the testing package documentation for details.
+ the command line argument must match exactly one package within the
+ main module, and regexp must match exactly one fuzz target within
+ that package. After tests, benchmarks, seed corpora of other fuzz
+ targets, and examples have completed, the matching target will be
+ fuzzed. See the Fuzzing section of the testing package documentation
+ for details.
-fuzztime t
Run enough iterations of the fuzz test to take t, specified as a
if len(pkgs) != 1 {
base.Fatalf("cannot use -fuzz flag with multiple packages")
}
+
+ // Reject the '-fuzz' flag if the package is outside the main module.
+ // Otherwise, if fuzzing identifies a failure it could corrupt checksums in
+ // the module cache (or permanently alter the behavior of std tests for all
+ // users) by writing the failing input to the package's testdata directory.
+ // (See https://golang.org/issue/48495 and test_fuzz_modcache.txt.)
+ mainMods := modload.MainModules
+ if m := pkgs[0].Module; m != nil && m.Path != "" {
+ if !mainMods.Contains(m.Path) {
+ base.Fatalf("cannot use -fuzz flag on package outside the main module")
+ }
+ } else if pkgs[0].Standard && modload.Enabled() {
+ // Because packages in 'std' and 'cmd' are part of the standard library,
+ // they are only treated as part of a module in 'go mod' subcommands and
+ // 'go get'. However, we still don't want to accidentally corrupt their
+ // testdata during fuzzing, nor do we want to fail with surprising errors
+ // if GOROOT isn't writable (as is often the case for Go toolchains
+ // installed through package managers).
+ //
+ // If the user is requesting to fuzz a standard-library package, ensure
+ // that they are in the same module as that package (just like when
+ // fuzzing any other package).
+ if strings.HasPrefix(pkgs[0].ImportPath, "cmd/") {
+ if !mainMods.Contains("cmd") || !mainMods.InGorootSrc(module.Version{Path: "cmd"}) {
+ base.Fatalf("cannot use -fuzz flag on package outside the main module")
+ }
+ } else {
+ if !mainMods.Contains("std") || !mainMods.InGorootSrc(module.Version{Path: "std"}) {
+ base.Fatalf("cannot use -fuzz flag on package outside the main module")
+ }
+ }
+ }
}
if testProfile() != "" && len(pkgs) != 1 {
base.Fatalf("cannot use %s flag with multiple packages", testProfile())
--- /dev/null
+-- .mod --
+module example.com/fuzzfail
+
+go 1.18
+-- .info --
+{"Version":"v0.1.0"}
+-- go.mod --
+module example.com/fuzzfail
+
+go 1.18
+-- fuzzfail_test.go --
+package fuzzfail
+
+import "testing"
+
+func FuzzFail(f *testing.F) {
+ f.Fuzz(func(t *testing.T, b []byte) {
+ t.Fatalf("oops: %q", b)
+ })
+}
--- /dev/null
+-- .mod --
+module example.com/fuzzfail
+
+go 1.18
+-- .info --
+{"Version":"v0.2.0"}
+-- go.mod --
+module example.com/fuzzfail
+
+go 1.18
+-- fuzzfail_test.go --
+package fuzzfail
+
+import "testing"
+
+func FuzzFail(f *testing.F) {
+ f.Fuzz(func(t *testing.T, b []byte) {
+ t.Fatalf("oops: %q", b)
+ })
+}
+-- testdata/fuzz/FuzzFail/bbb0c2d22aa1a24617301566dc7486f8b625d38024603ba62757c1124013b49a --
+go test fuzz v1
+[]byte("\x05")
--- /dev/null
+# This test demonstrates the fuzz corpus behavior for packages outside of the main module.
+# (See https://golang.org/issue/48495.)
+
+[short] skip
+
+# Set -modcacherw so that the test behaves the same regardless of whether the
+# module cache is writable. (For example, on some platforms it can always be
+# written if the user is running as root.) At one point, a failing fuzz test
+# in a writable module cache would corrupt module checksums in the cache.
+env GOFLAGS=-modcacherw
+
+
+# When the upstream module has no test corpus, running 'go test' should succeed,
+# but 'go test -fuzz=.' should error out before running the test.
+# (It should NOT corrupt the module cache by writing out new fuzz inputs,
+# even if the cache is writable.)
+
+go get -t example.com/fuzzfail@v0.1.0
+go test example.com/fuzzfail
+
+! go test -fuzz=. example.com/fuzzfail
+! stdout .
+stderr '^cannot use -fuzz flag on package outside the main module$'
+
+go mod verify
+
+
+# If the module does include a test corpus, 'go test' (without '-fuzz') should
+# load that corpus and run the fuzz tests against it, but 'go test -fuzz=.'
+# should continue to be rejected.
+
+go get -t example.com/fuzzfail@v0.2.0
+
+! go test example.com/fuzzfail
+stdout '^\s*fuzzfail_test\.go:7: oops:'
+
+! go test -fuzz=. example.com/fuzzfail
+! stdout .
+stderr '^cannot use -fuzz flag on package outside the main module$'
+
+go mod verify
+
+
+# Packages in 'std' cannot be fuzzed when the corresponding GOROOT module is not
+# the main module — either the failures would not be recorded or the behavior of
+# the 'std' tests would change globally.
+
+! go test -fuzz . encoding/json
+stderr '^cannot use -fuzz flag on package outside the main module$'
+
+! go test -fuzz . cmd/buildid
+stderr '^cannot use -fuzz flag on package outside the main module$'
+
+
+-- go.mod --
+module example.com/m
+
+go 1.18