1
0
mirror of https://github.com/golang/go synced 2024-11-23 09:00:04 -07:00

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 <crawshaw@golang.org>
This commit is contained in:
Ian Lance Taylor 2015-10-21 12:33:56 -07:00
parent d96b4c494f
commit e7ee268292
9 changed files with 132 additions and 22 deletions

View 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) {

View File

@ -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)))
}

View File

@ -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)))
}

View File

@ -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

View 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

View 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")
}
}

View 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")

View 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()

View 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)
}