mirror of
https://github.com/golang/go
synced 2024-11-23 06:30:06 -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:
parent
d96b4c494f
commit
e7ee268292
@ -1,9 +1,6 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -fsanitize=memory
|
||||
#cgo LDFLAGS: -fsanitize=memory
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void f(int32_t *p, int n) {
|
||||
|
31
misc/cgo/testsanitizers/msan2.go
Normal file
31
misc/cgo/testsanitizers/msan2.go
Normal 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)))
|
||||
}
|
31
misc/cgo/testsanitizers/msan_fail.go
Normal file
31
misc/cgo/testsanitizers/msan_fail.go
Normal 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)))
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -64,7 +64,7 @@ func printStderr(args ...interface{}) (int, error) {
|
||||
}
|
||||
|
||||
func runRun(cmd *Command, args []string) {
|
||||
raceInit()
|
||||
instrumentInit()
|
||||
buildModeInit()
|
||||
var b builder
|
||||
b.init()
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user