Commit e7ee2682 authored by Ian Lance Taylor's avatar Ian Lance Taylor

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/16169Reviewed-by: default avatarDavid Crawshaw <crawshaw@golang.org>
parent d96b4c49
package main package main
/* /*
#cgo CFLAGS: -fsanitize=memory
#cgo LDFLAGS: -fsanitize=memory
#include <stdint.h> #include <stdint.h>
void f(int32_t *p, int n) { void f(int32_t *p, int n) {
......
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)))
}
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)))
}
...@@ -31,4 +31,21 @@ if $CC --version | grep clang >& /dev/null; then ...@@ -31,4 +31,21 @@ if $CC --version | grep clang >& /dev/null; then
fi fi
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
...@@ -90,6 +90,9 @@ and test commands: ...@@ -90,6 +90,9 @@ and test commands:
-race -race
enable data race detection. enable data race detection.
Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
-msan
enable interoperation with memory sanitizer.
Supported only on linux/amd64.
-v -v
print the names of packages as they are compiled. print the names of packages as they are compiled.
-work -work
...@@ -112,8 +115,9 @@ and test commands: ...@@ -112,8 +115,9 @@ and test commands:
a suffix to use in the name of the package installation directory, a suffix to use in the name of the package installation directory,
in order to keep output separate from default builds. in order to keep output separate from default builds.
If using the -race flag, the install suffix is automatically set to race 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 or, if set explicitly, has _race appended to it. Likewise for the -msan
option that requires non-default compile flags has a similar effect. flag. Using a -buildmode option that requires non-default compile flags
has a similar effect.
-ldflags 'flag list' -ldflags 'flag list'
arguments to pass on each go tool link invocation. arguments to pass on each go tool link invocation.
-linkshared -linkshared
......
...@@ -69,6 +69,9 @@ and test commands: ...@@ -69,6 +69,9 @@ and test commands:
-race -race
enable data race detection. enable data race detection.
Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64. Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
-msan
enable interoperation with memory sanitizer.
Supported only on linux/amd64.
-v -v
print the names of packages as they are compiled. print the names of packages as they are compiled.
-work -work
...@@ -91,8 +94,9 @@ and test commands: ...@@ -91,8 +94,9 @@ and test commands:
a suffix to use in the name of the package installation directory, a suffix to use in the name of the package installation directory,
in order to keep output separate from default builds. in order to keep output separate from default builds.
If using the -race flag, the install suffix is automatically set to race 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 or, if set explicitly, has _race appended to it. Likewise for the -msan
option that requires non-default compile flags has a similar effect. flag. Using a -buildmode option that requires non-default compile flags
has a similar effect.
-ldflags 'flag list' -ldflags 'flag list'
arguments to pass on each go tool link invocation. arguments to pass on each go tool link invocation.
-linkshared -linkshared
...@@ -166,6 +170,7 @@ var buildGcflags []string // -gcflags flag ...@@ -166,6 +170,7 @@ var buildGcflags []string // -gcflags flag
var buildLdflags []string // -ldflags flag var buildLdflags []string // -ldflags flag
var buildGccgoflags []string // -gccgoflags flag var buildGccgoflags []string // -gccgoflags flag
var buildRace bool // -race flag var buildRace bool // -race flag
var buildMSan bool // -msan flag
var buildToolExec []string // -toolexec flag var buildToolExec []string // -toolexec flag
var buildBuildmode string // -buildmode flag var buildBuildmode string // -buildmode flag
var buildLinkshared bool // -linkshared flag var buildLinkshared bool // -linkshared flag
...@@ -225,6 +230,7 @@ func addBuildFlags(cmd *Command) { ...@@ -225,6 +230,7 @@ func addBuildFlags(cmd *Command) {
cmd.Flag.BoolVar(&buildLinkshared, "linkshared", false, "") cmd.Flag.BoolVar(&buildLinkshared, "linkshared", false, "")
cmd.Flag.StringVar(&buildPkgdir, "pkgdir", "", "") cmd.Flag.StringVar(&buildPkgdir, "pkgdir", "", "")
cmd.Flag.BoolVar(&buildRace, "race", false, "") cmd.Flag.BoolVar(&buildRace, "race", false, "")
cmd.Flag.BoolVar(&buildMSan, "msan", false, "")
cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "")
cmd.Flag.Var((*stringsFlag)(&buildToolExec), "toolexec", "") cmd.Flag.Var((*stringsFlag)(&buildToolExec), "toolexec", "")
cmd.Flag.BoolVar(&buildWork, "work", false, "") cmd.Flag.BoolVar(&buildWork, "work", false, "")
...@@ -422,7 +428,7 @@ func buildModeInit() { ...@@ -422,7 +428,7 @@ func buildModeInit() {
} }
func runBuild(cmd *Command, args []string) { func runBuild(cmd *Command, args []string) {
raceInit() instrumentInit()
buildModeInit() buildModeInit()
var b builder var b builder
b.init() b.init()
...@@ -523,7 +529,7 @@ func runInstall(cmd *Command, args []string) { ...@@ -523,7 +529,7 @@ func runInstall(cmd *Command, args []string) {
fatalf("cannot install, GOBIN must be an absolute path") fatalf("cannot install, GOBIN must be an absolute path")
} }
raceInit() instrumentInit()
buildModeInit() buildModeInit()
pkgs := pkgsFilter(packagesForBuild(args)) pkgs := pkgsFilter(packagesForBuild(args))
...@@ -877,7 +883,7 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha ...@@ -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 // 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 // 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. // 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" { if (len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo") && !buildLinkshared && buildBuildmode != "shared" {
var stk importStack var stk importStack
p1 := loadPackage("cmd/cgo", &stk) p1 := loadPackage("cmd/cgo", &stk)
...@@ -2907,7 +2913,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi ...@@ -2907,7 +2913,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
if p.Standard && p.ImportPath == "runtime/cgo" { if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_runtime_cgo=false") 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") cgoflags = append(cgoflags, "-import_syscall=false")
} }
...@@ -3355,23 +3361,38 @@ func (q *actionQueue) pop() *action { ...@@ -3355,23 +3361,38 @@ func (q *actionQueue) pop() *action {
return heap.Pop(q).(*action) return heap.Pop(q).(*action)
} }
func raceInit() { func instrumentInit() {
if !buildRace { if !buildRace && !buildMSan {
return 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" { 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) os.Exit(2)
} }
if !buildContext.CgoEnabled { if !buildContext.CgoEnabled {
fmt.Fprintf(os.Stderr, "go %s: -race requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0]) fmt.Fprintf(os.Stderr, "go %s: -race requires cgo; enable cgo by setting CGO_ENABLED=1\n", flag.Args()[0])
os.Exit(2) os.Exit(2)
} }
buildGcflags = append(buildGcflags, "-race") if buildRace {
buildLdflags = append(buildLdflags, "-race") buildGcflags = append(buildGcflags, "-race")
buildLdflags = append(buildLdflags, "-race")
} else {
buildGcflags = append(buildGcflags, "-msan")
buildLdflags = append(buildLdflags, "-msan")
}
if buildContext.InstallSuffix != "" { if buildContext.InstallSuffix != "" {
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")
}
} }
...@@ -710,6 +710,7 @@ func expandScanner(err error) error { ...@@ -710,6 +710,7 @@ func expandScanner(err error) error {
var raceExclude = map[string]bool{ var raceExclude = map[string]bool{
"runtime/race": true, "runtime/race": true,
"runtime/msan": true,
"runtime/cgo": true, "runtime/cgo": true,
"cmd/cgo": true, "cmd/cgo": true,
"syscall": true, "syscall": true,
...@@ -723,6 +724,7 @@ var cgoExclude = map[string]bool{ ...@@ -723,6 +724,7 @@ var cgoExclude = map[string]bool{
var cgoSyscallExclude = map[string]bool{ var cgoSyscallExclude = map[string]bool{
"runtime/cgo": true, "runtime/cgo": true,
"runtime/race": true, "runtime/race": true,
"runtime/msan": true,
} }
// load populates p using information from bp, err, which should // 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 ...@@ -838,6 +840,10 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
if buildRace && (!p.Standard || !raceExclude[p.ImportPath]) { if buildRace && (!p.Standard || !raceExclude[p.ImportPath]) {
importPaths = append(importPaths, "runtime/race") 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. // On ARM with GOARM=5, everything depends on math for the link.
if p.Name == "main" && goarch == "arm" { if p.Name == "main" && goarch == "arm" {
importPaths = append(importPaths, "math") importPaths = append(importPaths, "math")
......
...@@ -64,7 +64,7 @@ func printStderr(args ...interface{}) (int, error) { ...@@ -64,7 +64,7 @@ func printStderr(args ...interface{}) (int, error) {
} }
func runRun(cmd *Command, args []string) { func runRun(cmd *Command, args []string) {
raceInit() instrumentInit()
buildModeInit() buildModeInit()
var b builder var b builder
b.init() b.init()
......
...@@ -327,7 +327,7 @@ func runTest(cmd *Command, args []string) { ...@@ -327,7 +327,7 @@ func runTest(cmd *Command, args []string) {
findExecCmd() // initialize cached result findExecCmd() // initialize cached result
raceInit() instrumentInit()
buildModeInit() buildModeInit()
pkgs := packagesForBuild(pkgArgs) pkgs := packagesForBuild(pkgArgs)
if len(pkgs) == 0 { if len(pkgs) == 0 {
...@@ -395,7 +395,7 @@ func runTest(cmd *Command, args []string) { ...@@ -395,7 +395,7 @@ func runTest(cmd *Command, args []string) {
if deps["C"] { if deps["C"] {
delete(deps, "C") delete(deps, "C")
deps["runtime/cgo"] = true 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 deps["cmd/cgo"] = true
} }
} }
...@@ -543,6 +543,9 @@ func runTest(cmd *Command, args []string) { ...@@ -543,6 +543,9 @@ func runTest(cmd *Command, args []string) {
if buildRace { if buildRace {
extraOpts = "-race " 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) fmt.Fprintf(os.Stderr, "installing these packages with 'go test %s-i%s' will speed future tests.\n\n", extraOpts, args)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment