cmd/go: add -asan option

The -asan option compiles Go code to use the address sanitizer.  This is
intended for use when linking with C/C++ code compiled with -fsanitize=address.
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 validly allocated or not,
and will report errors for any use of invalid memory.

Updates #44853.

Change-Id: I0209002ef795cc1c823daae557fb80c906158db3
Reviewed-on: https://go-review.googlesource.com/c/go/+/298612
Trust: fannie zhang <Fannie.Zhang@arm.com>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index d04ba04..8178073 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -121,6 +121,9 @@
 // 		Supported only on linux/amd64, linux/arm64
 // 		and only with Clang/LLVM as the host C compiler.
 // 		On linux/arm64, pie build mode will be used.
+// 	-asan
+// 		enable interoperation with address sanitizer.
+// 		Supported only on linux/arm64, linux/amd64.
 // 	-v
 // 		print the names of packages as they are compiled.
 // 	-work
@@ -155,8 +158,8 @@
 // 		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. Likewise for the -msan
-// 		flag. Using a -buildmode option that requires non-default compile flags
-// 		has a similar effect.
+// 		and -asan flags. Using a -buildmode option that requires non-default compile
+// 		flags has a similar effect.
 // 	-ldflags '[pattern=]arg list'
 // 		arguments to pass on each go tool link invocation.
 // 	-linkshared
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index d67d01a..339014e94 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -46,6 +46,7 @@
 	canRace = false // whether we can run the race detector
 	canCgo  = false // whether we can use cgo
 	canMSan = false // whether we can run the memory sanitizer
+	canASan = false // whether we can run the address sanitizer
 )
 
 var exeSuffix string = func() string {
@@ -197,6 +198,7 @@
 		testGOCACHE = strings.TrimSpace(string(out))
 
 		canMSan = canCgo && sys.MSanSupported(runtime.GOOS, runtime.GOARCH)
+		canASan = canCgo && sys.ASanSupported(runtime.GOOS, runtime.GOARCH)
 		canRace = canCgo && sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH)
 		// The race detector doesn't work on Alpine Linux:
 		// golang.org/issue/14481
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index 37e9b26..351c3ee 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -35,6 +35,7 @@
 	BuildI                 bool                    // -i flag
 	BuildLinkshared        bool                    // -linkshared flag
 	BuildMSan              bool                    // -msan flag
+	BuildASan              bool                    // -asan flag
 	BuildN                 bool                    // -n flag
 	BuildO                 string                  // -o flag
 	BuildP                 = runtime.GOMAXPROCS(0) // -p flag
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index 99c4a9c..a5be48a 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -1625,6 +1625,7 @@
 	"runtime/cgo":  true,
 	"runtime/race": true,
 	"runtime/msan": true,
+	"runtime/asan": true,
 }
 
 var foldPath = make(map[string]string)
@@ -2415,6 +2416,10 @@
 	if cfg.BuildMSan {
 		deps = append(deps, "runtime/msan")
 	}
+	// Using address sanitizer forces an import of runtime/asan.
+	if cfg.BuildASan {
+		deps = append(deps, "runtime/asan")
+	}
 
 	return deps
 }
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index e10f647..ffe33bf 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -75,6 +75,9 @@
 		Supported only on linux/amd64, linux/arm64
 		and only with Clang/LLVM as the host C compiler.
 		On linux/arm64, pie build mode will be used.
+	-asan
+		enable interoperation with address sanitizer.
+		Supported only on linux/arm64, linux/amd64.
 	-v
 		print the names of packages as they are compiled.
 	-work
@@ -109,8 +112,8 @@
 		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. Likewise for the -msan
-		flag. Using a -buildmode option that requires non-default compile flags
-		has a similar effect.
+		and -asan flags. Using a -buildmode option that requires non-default compile
+		flags has a similar effect.
 	-ldflags '[pattern=]arg list'
 		arguments to pass on each go tool link invocation.
 	-linkshared
@@ -309,6 +312,7 @@
 	cmd.Flag.StringVar(&cfg.BuildPkgdir, "pkgdir", "", "")
 	cmd.Flag.BoolVar(&cfg.BuildRace, "race", false, "")
 	cmd.Flag.BoolVar(&cfg.BuildMSan, "msan", false, "")
+	cmd.Flag.BoolVar(&cfg.BuildASan, "asan", false, "")
 	cmd.Flag.Var((*tagsFlag)(&cfg.BuildContext.BuildTags), "tags", "")
 	cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildToolexec), "toolexec", "")
 	cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "")
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index d4e24d4..62d8143 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -2736,6 +2736,10 @@
 		cgoCFLAGS = append([]string{"-fsanitize=memory"}, cgoCFLAGS...)
 		cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...)
 	}
+	if cfg.BuildASan {
+		cgoCFLAGS = append([]string{"-fsanitize=address"}, cgoCFLAGS...)
+		cgoLDFLAGS = append([]string{"-fsanitize=address"}, cgoLDFLAGS...)
+	}
 
 	// Allows including _cgo_export.h, as well as the user's .h files,
 	// from .[ch] files in the package.
@@ -2757,7 +2761,7 @@
 	if p.Standard && p.ImportPath == "runtime/cgo" {
 		cgoflags = append(cgoflags, "-import_runtime_cgo=false")
 	}
-	if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo") {
+	if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/msan" || p.ImportPath == "runtime/cgo" || p.ImportPath == "runtime/asan") {
 		cgoflags = append(cgoflags, "-import_syscall=false")
 	}
 
diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go
index 6a29abb..56e39f8 100644
--- a/src/cmd/go/internal/work/init.go
+++ b/src/cmd/go/internal/work/init.go
@@ -87,7 +87,7 @@
 }
 
 func instrumentInit() {
-	if !cfg.BuildRace && !cfg.BuildMSan {
+	if !cfg.BuildRace && !cfg.BuildMSan && !cfg.BuildASan {
 		return
 	}
 	if cfg.BuildRace && cfg.BuildMSan {
@@ -95,17 +95,30 @@
 		base.SetExitStatus(2)
 		base.Exit()
 	}
+	if cfg.BuildRace && cfg.BuildASan {
+		fmt.Fprintf(os.Stderr, "go: may not use -race and -asan simultaneously\n")
+		base.SetExitStatus(2)
+		base.Exit()
+	}
+	if cfg.BuildMSan && cfg.BuildASan {
+		fmt.Fprintf(os.Stderr, "go: may not use -msan and -asan simultaneously\n")
+		base.SetExitStatus(2)
+		base.Exit()
+	}
 	if cfg.BuildMSan && !sys.MSanSupported(cfg.Goos, cfg.Goarch) {
 		fmt.Fprintf(os.Stderr, "-msan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
 		base.SetExitStatus(2)
 		base.Exit()
 	}
-	if cfg.BuildRace {
-		if !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) {
-			fmt.Fprintf(os.Stderr, "go: -race is only supported on linux/amd64, linux/ppc64le, linux/arm64, freebsd/amd64, netbsd/amd64, darwin/amd64, darwin/arm64, and windows/amd64\n")
-			base.SetExitStatus(2)
-			base.Exit()
-		}
+	if cfg.BuildRace && !sys.RaceDetectorSupported(cfg.Goos, cfg.Goarch) {
+		fmt.Fprintf(os.Stderr, "-race is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
+		base.SetExitStatus(2)
+		base.Exit()
+	}
+	if cfg.BuildASan && !sys.ASanSupported(cfg.Goos, cfg.Goarch) {
+		fmt.Fprintf(os.Stderr, "-asan is not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
+		base.SetExitStatus(2)
+		base.Exit()
 	}
 	mode := "race"
 	if cfg.BuildMSan {
@@ -116,6 +129,9 @@
 			cfg.BuildBuildmode = "pie"
 		}
 	}
+	if cfg.BuildASan {
+		mode = "asan"
+	}
 	modeFlag := "-" + mode
 
 	if !cfg.BuildContext.CgoEnabled {
diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go
index ac9764d..acb1f91 100644
--- a/src/cmd/go/script_test.go
+++ b/src/cmd/go/script_test.go
@@ -353,6 +353,8 @@
 				ok = canCgo
 			case "msan":
 				ok = canMSan
+			case "asan":
+				ok = canASan
 			case "race":
 				ok = canRace
 			case "net":
diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README
index 48e4055..2b88e88 100644
--- a/src/cmd/go/testdata/script/README
+++ b/src/cmd/go/testdata/script/README
@@ -79,7 +79,7 @@
  - Compiler names, like [gccgo], [gc].
  - Test environment details:
    - [short] for testing.Short()
-   - [cgo], [msan], [race] for whether cgo, msan, and the race detector can be used
+   - [cgo], [msan], [asan], [race] for whether cgo, msan, asan, and the race detector can be used
    - [net] for whether the external network can be used
    - [link] for testenv.HasLink()
    - [root] for os.Geteuid() == 0
diff --git a/src/cmd/go/testdata/script/goflags.txt b/src/cmd/go/testdata/script/goflags.txt
index 686d113..f4872ff 100644
--- a/src/cmd/go/testdata/script/goflags.txt
+++ b/src/cmd/go/testdata/script/goflags.txt
@@ -9,7 +9,7 @@
 
 env GOFLAGS=-race OLDGOARCH=$GOARCH OLDGOOS=$GOOS GOARCH=386 GOOS=linux
 ! go list runtime
-stderr 'race is only supported on'
+stderr 'race is not supported on linux/386'
 
 env GOARCH=$OLDGOARCH GOOS=$OLDGOOS
 
diff --git a/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt b/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt
index 7985cd2..5e88f7b 100644
--- a/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt
+++ b/src/cmd/go/testdata/script/install_msan_and_race_require_cgo.txt
@@ -1,7 +1,5 @@
 # Tests Issue #21895
 
-[!msan] [!race] skip 'skipping because both msan and the race detector are not supported'
-
 env CGO_ENABLED=0
 
 [race] ! go install -race triv.go
@@ -12,6 +10,10 @@
 [msan] stderr '-msan requires cgo'
 [msan] ! stderr '-race'
 
+[asan] ! go install -asan triv.go
+[asan] stderr '-asan requires cgo'
+[asan] ! stderr '-msan'
+
 -- triv.go --
 package main