From: Ian Lance Taylor Date: Wed, 21 Oct 2015 19:33:56 +0000 (-0700) Subject: cmd/go: add -msan option X-Git-Tag: go1.6beta1~756 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=e7ee268292a814fed6ebe7c81c808581f80d3416;p=gostls13.git cmd/go: add -msan option The -msan option compiles Go code to use the memory sanitizer. This is intended for use when linking with C/C++ code compiled with -fsanitize=memory. When memory blocks are passed back and forth between C/C++ and Go, code in both languages will agree as to whether the memory is correctly initialized or not, and will report errors for any use of uninitialized memory. Change-Id: I2dbdbd26951eacb7d84063cfc7297f88ffadd70c Reviewed-on: https://go-review.googlesource.com/16169 Reviewed-by: David Crawshaw --- diff --git a/misc/cgo/testsanitizers/msan.go b/misc/cgo/testsanitizers/msan.go index 1a95715ecd..263fb5a2f7 100644 --- a/misc/cgo/testsanitizers/msan.go +++ b/misc/cgo/testsanitizers/msan.go @@ -1,9 +1,6 @@ package main /* -#cgo CFLAGS: -fsanitize=memory -#cgo LDFLAGS: -fsanitize=memory - #include void f(int32_t *p, int n) { diff --git a/misc/cgo/testsanitizers/msan2.go b/misc/cgo/testsanitizers/msan2.go new file mode 100644 index 0000000000..d1da89c912 --- /dev/null +++ b/misc/cgo/testsanitizers/msan2.go @@ -0,0 +1,31 @@ +package main + +/* +#include +#include +#include + +void f(int32_t *p, int n) { + int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n); + memcpy(p, q, n * sizeof(*p)); + free(q); +} + +void g(int32_t *p, int n) { + if (p[4] != 1) { + abort(); + } +} +*/ +import "C" + +import ( + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + a[4] = 1 + C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) +} diff --git a/misc/cgo/testsanitizers/msan_fail.go b/misc/cgo/testsanitizers/msan_fail.go new file mode 100644 index 0000000000..3be656f0d0 --- /dev/null +++ b/misc/cgo/testsanitizers/msan_fail.go @@ -0,0 +1,31 @@ +package main + +/* +#include +#include +#include + +void f(int32_t *p, int n) { + int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n); + memcpy(p, q, n * sizeof(*p)); + free(q); +} + +void g(int32_t *p, int n) { + if (p[4] != 1) { + abort(); + } +} +*/ +import "C" + +import ( + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + a[3] = 1 + C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) +} diff --git a/misc/cgo/testsanitizers/test.bash b/misc/cgo/testsanitizers/test.bash index 44cf529603..cc50d386e3 100755 --- a/misc/cgo/testsanitizers/test.bash +++ b/misc/cgo/testsanitizers/test.bash @@ -31,4 +31,21 @@ if $CC --version | grep clang >& /dev/null; then fi fi -go run msan.go +status=0 + +if ! go run -msan msan.go; then + echo "FAIL: msan" + status=1 +fi + +if ! go run -msan msan2.go; then + echo "FAIL: msan2" + status=1 +fi + +if go run -msan msan_fail.go 2>/dev/null; then + echo "FAIL: msan_fail" + status=1 +fi + +exit $status diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 228ad19de4..f9aa20dee5 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -90,6 +90,9 @@ and test commands: -race enable data race detection. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. + -msan + enable interoperation with memory sanitizer. + Supported only on linux/amd64. -v print the names of packages as they are compiled. -work @@ -112,8 +115,9 @@ and test commands: a suffix to use in the name of the package installation directory, in order to keep output separate from default builds. If using the -race flag, the install suffix is automatically set to race - or, if set explicitly, has _race appended to it. Using a -buildmode - option that requires non-default compile flags has a similar effect. + or, if set explicitly, has _race appended to it. Likewise for the -msan + flag. Using a -buildmode option that requires non-default compile flags + has a similar effect. -ldflags 'flag list' arguments to pass on each go tool link invocation. -linkshared diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index d4aeb705e7..8df312ab63 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -69,6 +69,9 @@ and test commands: -race enable data race detection. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. + -msan + enable interoperation with memory sanitizer. + Supported only on linux/amd64. -v print the names of packages as they are compiled. -work @@ -91,8 +94,9 @@ and test commands: a suffix to use in the name of the package installation directory, in order to keep output separate from default builds. If using the -race flag, the install suffix is automatically set to race - or, if set explicitly, has _race appended to it. Using a -buildmode - option that requires non-default compile flags has a similar effect. + or, if set explicitly, has _race appended to it. Likewise for the -msan + flag. Using a -buildmode option that requires non-default compile flags + has a similar effect. -ldflags 'flag list' arguments to pass on each go tool link invocation. -linkshared @@ -166,6 +170,7 @@ var buildGcflags []string // -gcflags flag var buildLdflags []string // -ldflags flag var buildGccgoflags []string // -gccgoflags flag var buildRace bool // -race flag +var buildMSan bool // -msan flag var buildToolExec []string // -toolexec flag var buildBuildmode string // -buildmode flag var buildLinkshared bool // -linkshared flag @@ -225,6 +230,7 @@ func addBuildFlags(cmd *Command) { cmd.Flag.BoolVar(&buildLinkshared, "linkshared", false, "") cmd.Flag.StringVar(&buildPkgdir, "pkgdir", "", "") cmd.Flag.BoolVar(&buildRace, "race", false, "") + cmd.Flag.BoolVar(&buildMSan, "msan", false, "") cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") cmd.Flag.Var((*stringsFlag)(&buildToolExec), "toolexec", "") cmd.Flag.BoolVar(&buildWork, "work", false, "") @@ -422,7 +428,7 @@ func buildModeInit() { } func runBuild(cmd *Command, args []string) { - raceInit() + instrumentInit() buildModeInit() var b builder b.init() @@ -523,7 +529,7 @@ func runInstall(cmd *Command, args []string) { fatalf("cannot install, GOBIN must be an absolute path") } - raceInit() + instrumentInit() buildModeInit() pkgs := pkgsFilter(packagesForBuild(args)) @@ -877,7 +883,7 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha // using cgo, to make sure we do not overwrite the binary while // a package is using it. If this is a cross-build, then the cgo we // are writing is not the cgo we need to use. - if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace { + if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace && !buildMSan { if (len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo") && !buildLinkshared && buildBuildmode != "shared" { var stk importStack p1 := loadPackage("cmd/cgo", &stk) @@ -2907,7 +2913,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = append(cgoflags, "-import_runtime_cgo=false") } - if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/cgo") { + if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo") { cgoflags = append(cgoflags, "-import_syscall=false") } @@ -3355,23 +3361,38 @@ func (q *actionQueue) pop() *action { return heap.Pop(q).(*action) } -func raceInit() { - if !buildRace { +func instrumentInit() { + if !buildRace && !buildMSan { return } + if buildRace && buildMSan { + fmt.Fprintf(os.Stderr, "go %s: may not use -race and -msan simultaneously", flag.Args()[0]) + os.Exit(2) + } if goarch != "amd64" || goos != "linux" && goos != "freebsd" && goos != "darwin" && goos != "windows" { - fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) + fmt.Fprintf(os.Stderr, "go %s: -race and -msan are only supported on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) os.Exit(2) } if !buildContext.CgoEnabled { fmt.Fprintf(os.Stderr, "go %s: -race requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0]) os.Exit(2) } - buildGcflags = append(buildGcflags, "-race") - buildLdflags = append(buildLdflags, "-race") + if buildRace { + buildGcflags = append(buildGcflags, "-race") + buildLdflags = append(buildLdflags, "-race") + } else { + buildGcflags = append(buildGcflags, "-msan") + buildLdflags = append(buildLdflags, "-msan") + } if buildContext.InstallSuffix != "" { buildContext.InstallSuffix += "_" } - buildContext.InstallSuffix += "race" - buildContext.BuildTags = append(buildContext.BuildTags, "race") + + if buildRace { + buildContext.InstallSuffix += "race" + buildContext.BuildTags = append(buildContext.BuildTags, "race") + } else { + buildContext.InstallSuffix += "msan" + buildContext.BuildTags = append(buildContext.BuildTags, "msan") + } } diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index f7b18743de..78bd72f52b 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -710,6 +710,7 @@ func expandScanner(err error) error { var raceExclude = map[string]bool{ "runtime/race": true, + "runtime/msan": true, "runtime/cgo": true, "cmd/cgo": true, "syscall": true, @@ -723,6 +724,7 @@ var cgoExclude = map[string]bool{ var cgoSyscallExclude = map[string]bool{ "runtime/cgo": true, "runtime/race": true, + "runtime/msan": true, } // load populates p using information from bp, err, which should @@ -838,6 +840,10 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package if buildRace && (!p.Standard || !raceExclude[p.ImportPath]) { importPaths = append(importPaths, "runtime/race") } + // MSan uses runtime/msan. + if buildMSan && (!p.Standard || !raceExclude[p.ImportPath]) { + importPaths = append(importPaths, "runtime/msan") + } // On ARM with GOARM=5, everything depends on math for the link. if p.Name == "main" && goarch == "arm" { importPaths = append(importPaths, "math") diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go index f6da373e25..7ee067a003 100644 --- a/src/cmd/go/run.go +++ b/src/cmd/go/run.go @@ -64,7 +64,7 @@ func printStderr(args ...interface{}) (int, error) { } func runRun(cmd *Command, args []string) { - raceInit() + instrumentInit() buildModeInit() var b builder b.init() diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index f72c2272f0..ccbe4ac1e9 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -327,7 +327,7 @@ func runTest(cmd *Command, args []string) { findExecCmd() // initialize cached result - raceInit() + instrumentInit() buildModeInit() pkgs := packagesForBuild(pkgArgs) if len(pkgs) == 0 { @@ -395,7 +395,7 @@ func runTest(cmd *Command, args []string) { if deps["C"] { delete(deps, "C") deps["runtime/cgo"] = true - if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace { + if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace && !buildMSan { deps["cmd/cgo"] = true } } @@ -543,6 +543,9 @@ func runTest(cmd *Command, args []string) { if buildRace { extraOpts = "-race " } + if buildMSan { + extraOpts = "-msan " + } fmt.Fprintf(os.Stderr, "installing these packages with 'go test %s-i%s' will speed future tests.\n\n", extraOpts, args) }