]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: add -msan option
authorIan Lance Taylor <iant@golang.org>
Wed, 21 Oct 2015 19:33:56 +0000 (12:33 -0700)
committerIan Lance Taylor <iant@golang.org>
Wed, 21 Oct 2015 20:30:23 +0000 (20:30 +0000)
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 <crawshaw@golang.org>
misc/cgo/testsanitizers/msan.go
misc/cgo/testsanitizers/msan2.go [new file with mode: 0644]
misc/cgo/testsanitizers/msan_fail.go [new file with mode: 0644]
misc/cgo/testsanitizers/test.bash
src/cmd/go/alldocs.go
src/cmd/go/build.go
src/cmd/go/pkg.go
src/cmd/go/run.go
src/cmd/go/test.go

index 1a95715ecda3dfed81de360a7c372387ea67208b..263fb5a2f771d19d096084cb5e18cab83a13803b 100644 (file)
@@ -1,9 +1,6 @@
 package main
 
 /*
-#cgo CFLAGS: -fsanitize=memory
-#cgo LDFLAGS: -fsanitize=memory
-
 #include <stdint.h>
 
 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 (file)
index 0000000..d1da89c
--- /dev/null
@@ -0,0 +1,31 @@
+package main
+
+/*
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+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 (file)
index 0000000..3be656f
--- /dev/null
@@ -0,0 +1,31 @@
+package main
+
+/*
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+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)))
+}
index 44cf529603ecf305f4a9cd2b77765cdee7290f21..cc50d386e33bbfe4ef22bc724499fda2c2f53e81 100755 (executable)
@@ -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
index 228ad19de4093d83945ae143dec3cd20040575cf..f9aa20dee51e6f7a5277a4e8cfb4557b8f3b603b 100644 (file)
@@ -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
index d4aeb705e76aa8855d817c54b077c96951f821fa..8df312ab63d1e28834efaad614c5f07ff5020b4a 100644 (file)
@@ -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")
+       }
 }
index f7b18743de4fe9fd8e63df49027fc0d71f8adf9b..78bd72f52b5c148926670979cfb005f58e227d8c 100644 (file)
@@ -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")
index f6da373e2522d4b491a58af224859e8d862f39d9..7ee067a003ded229eaae3d3201131280fd4ecfbe 100644 (file)
@@ -64,7 +64,7 @@ func printStderr(args ...interface{}) (int, error) {
 }
 
 func runRun(cmd *Command, args []string) {
-       raceInit()
+       instrumentInit()
        buildModeInit()
        var b builder
        b.init()
index f72c2272f04dae3b6ceb8932066d1270919847ea..ccbe4ac1e9ae618c29533ecb78c5b52cf2ddc129 100644 (file)
@@ -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)
        }