cmd/compile: add -asan option
The -asan option causes the compiler to add instrumentation for the
C/C++ address sanitizer. Every memory read/write will be replaced
by a call to asanread/asanwrite.
This CL also inserts asan instrumentation during SSA building.
This CL passes tests but is not usable by itself. The actual
implementation of asanread/asanwrite in the runtime package, and
support for -asan in the go tool and tests, will follow in subsequent
CLs.
Updates #44853.
Change-Id: Ia18c9c5d5c351857420d2f6835f0daec2ad31096
Reviewed-on: https://go-review.googlesource.com/c/go/+/298611
Trust: fannie zhang <Fannie.Zhang@arm.com>
Run-TryBot: fannie zhang <Fannie.Zhang@arm.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go
index b68ef27..ef7fa86 100644
--- a/src/cmd/compile/doc.go
+++ b/src/cmd/compile/doc.go
@@ -44,6 +44,8 @@
Print compiler version and exit.
-asmhdr file
Write assembly header to file.
+ -asan
+ Insert calls to C/C++ address sanitizer.
-buildid id
Record id as the build id in the export metadata.
-blockprofile file
diff --git a/src/cmd/compile/internal/base/base.go b/src/cmd/compile/internal/base/base.go
index 4c2516f..be6d49f 100644
--- a/src/cmd/compile/internal/base/base.go
+++ b/src/cmd/compile/internal/base/base.go
@@ -67,6 +67,7 @@
"runtime",
"runtime/race",
"runtime/msan",
+ "runtime/asan",
"internal/cpu",
}
diff --git a/src/cmd/compile/internal/base/flag.go b/src/cmd/compile/internal/base/flag.go
index 241f5da..51938e8 100644
--- a/src/cmd/compile/internal/base/flag.go
+++ b/src/cmd/compile/internal/base/flag.go
@@ -84,6 +84,7 @@
// Longer names
AsmHdr string "help:\"write assembly header to `file`\""
+ ASan bool "help:\"build code compatible with C/C++ address sanitizer\""
Bench string "help:\"append benchmark times to `file`\""
BlockProfile string "help:\"write block profile to `file`\""
BuildID string "help:\"record `id` as the build id in the export metadata\""
@@ -177,6 +178,9 @@
if Flag.MSan && !sys.MSanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
log.Fatalf("%s/%s does not support -msan", buildcfg.GOOS, buildcfg.GOARCH)
}
+ if Flag.ASan && !sys.ASanSupported(buildcfg.GOOS, buildcfg.GOARCH) {
+ log.Fatalf("%s/%s does not support -asan", buildcfg.GOOS, buildcfg.GOARCH)
+ }
if Flag.Race && !sys.RaceDetectorSupported(buildcfg.GOOS, buildcfg.GOARCH) {
log.Fatalf("%s/%s does not support -race", buildcfg.GOOS, buildcfg.GOARCH)
}
@@ -217,12 +221,16 @@
}
Flag.LowerO = p + suffix
}
-
- if Flag.Race && Flag.MSan {
+ switch {
+ case Flag.Race && Flag.MSan:
log.Fatal("cannot use both -race and -msan")
+ case Flag.Race && Flag.ASan:
+ log.Fatal("cannot use both -race and -asan")
+ case Flag.MSan && Flag.ASan:
+ log.Fatal("cannot use both -msan and -asan")
}
- if Flag.Race || Flag.MSan {
- // -race and -msan imply -d=checkptr for now.
+ if Flag.Race || Flag.MSan || Flag.ASan {
+ // -race, -msan and -asan imply -d=checkptr for now.
if Debug.Checkptr == -1 { // if not set explicitly
Debug.Checkptr = 1
}
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 74b2157..ed81ef7 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -107,7 +107,7 @@
// Record flags that affect the build result. (And don't
// record flags that don't, since that would cause spurious
// changes in the binary.)
- dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "shared", "dynlink", "dwarf", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
+ dwarfgen.RecordFlags("B", "N", "l", "msan", "race", "asan", "shared", "dynlink", "dwarf", "dwarflocationlists", "dwarfbasentries", "smallframes", "spectre")
if !base.EnableTrace && base.Flag.LowerT {
log.Fatalf("compiler not built with support for -t")
@@ -149,11 +149,12 @@
if base.Compiling(base.NoInstrumentPkgs) {
base.Flag.Race = false
base.Flag.MSan = false
+ base.Flag.ASan = false
}
ssagen.Arch.LinkArch.Init(base.Ctxt)
startProfile()
- if base.Flag.Race || base.Flag.MSan {
+ if base.Flag.Race || base.Flag.MSan || base.Flag.ASan {
base.Flag.Cfg.Instrumenting = true
}
if base.Flag.Dwarf {
diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go
index 18d0b02..41c9607 100644
--- a/src/cmd/compile/internal/ir/func.go
+++ b/src/cmd/compile/internal/ir/func.go
@@ -201,7 +201,7 @@
funcNilCheckDisabled // disable nil checks when compiling this function
funcInlinabilityChecked // inliner has already determined whether the function is inlinable
funcExportInline // include inline body in export data
- funcInstrumentBody // add race/msan instrumentation during SSA construction
+ funcInstrumentBody // add race/msan/asan instrumentation during SSA construction
funcOpenCodedDeferDisallowed // can't do open-coded defers
funcClosureCalled // closure is only immediately called; used by escape analysis
)
diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go
index 1435e43..b204a1d 100644
--- a/src/cmd/compile/internal/ir/symtab.go
+++ b/src/cmd/compile/internal/ir/symtab.go
@@ -15,6 +15,8 @@
AssertE2I2 *obj.LSym
AssertI2I *obj.LSym
AssertI2I2 *obj.LSym
+ Asanread *obj.LSym
+ Asanwrite *obj.LSym
CheckPtrAlignment *obj.LSym
Deferproc *obj.LSym
DeferprocStack *obj.LSym
diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go
index 0aaf894..58dffba 100644
--- a/src/cmd/compile/internal/noder/import.go
+++ b/src/cmd/compile/internal/noder/import.go
@@ -127,6 +127,8 @@
suffix = "_race"
} else if base.Flag.MSan {
suffix = "_msan"
+ } else if base.Flag.ASan {
+ suffix = "_asan"
}
if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil {
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index b4ed96c..ce41b8c 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -1413,6 +1413,9 @@
if base.Flag.MSan {
dimportpath(types.NewPkg("runtime/msan", ""))
}
+ if base.Flag.ASan {
+ dimportpath(types.NewPkg("runtime/asan", ""))
+ }
dimportpath(types.NewPkg("main", ""))
}
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index 5a958a5..6b595ea 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -108,6 +108,8 @@
ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread")
ir.Syms.Msanwrite = typecheck.LookupRuntimeFunc("msanwrite")
ir.Syms.Msanmove = typecheck.LookupRuntimeFunc("msanmove")
+ ir.Syms.Asanread = typecheck.LookupRuntimeFunc("asanread")
+ ir.Syms.Asanwrite = typecheck.LookupRuntimeFunc("asanwrite")
ir.Syms.Newobject = typecheck.LookupRuntimeFunc("newobject")
ir.Syms.Newproc = typecheck.LookupRuntimeFunc("newproc")
ir.Syms.Panicdivide = typecheck.LookupRuntimeFunc("panicdivide")
@@ -1245,10 +1247,10 @@
}
// instrumentFields instruments a read/write operation on addr.
-// If it is instrumenting for MSAN and t is a struct type, it instruments
+// If it is instrumenting for MSAN or ASAN and t is a struct type, it instruments
// operation for each field, instead of for the whole struct.
func (s *state) instrumentFields(t *types.Type, addr *ssa.Value, kind instrumentKind) {
- if !base.Flag.MSan || !t.IsStruct() {
+ if !(base.Flag.MSan || base.Flag.ASan) || !t.IsStruct() {
s.instrument(t, addr, kind)
return
}
@@ -1327,6 +1329,16 @@
default:
panic("unreachable")
}
+ } else if base.Flag.ASan {
+ switch kind {
+ case instrumentRead:
+ fn = ir.Syms.Asanread
+ case instrumentWrite:
+ fn = ir.Syms.Asanwrite
+ default:
+ panic("unreachable")
+ }
+ needWidth = true
} else {
panic("unreachable")
}
@@ -3002,7 +3014,7 @@
}
// If n is addressable and can't be represented in
// SSA, then load just the selected field. This
- // prevents false memory dependencies in race/msan
+ // prevents false memory dependencies in race/msan/asan
// instrumentation.
if ir.IsAddressable(n) && !s.canSSA(n) {
p := s.addr(n)
diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go
index 524360e..67597ce 100644
--- a/src/cmd/compile/internal/typecheck/builtin.go
+++ b/src/cmd/compile/internal/typecheck/builtin.go
@@ -192,6 +192,8 @@
{"msanread", funcTag, 140},
{"msanwrite", funcTag, 140},
{"msanmove", funcTag, 141},
+ {"asanread", funcTag, 140},
+ {"asanwrite", funcTag, 140},
{"checkptrAlignment", funcTag, 142},
{"checkptrArithmetic", funcTag, 144},
{"libfuzzerTraceCmp1", funcTag, 145},
diff --git a/src/cmd/compile/internal/typecheck/builtin/runtime.go b/src/cmd/compile/internal/typecheck/builtin/runtime.go
index 66641fb..04ae4f2 100644
--- a/src/cmd/compile/internal/typecheck/builtin/runtime.go
+++ b/src/cmd/compile/internal/typecheck/builtin/runtime.go
@@ -250,6 +250,10 @@
func msanwrite(addr, size uintptr)
func msanmove(dst, src, size uintptr)
+// address sanitizer
+func asanread(addr, size uintptr)
+func asanwrite(addr, size uintptr)
+
func checkptrAlignment(unsafe.Pointer, *byte, uintptr)
func checkptrArithmetic(unsafe.Pointer, []unsafe.Pointer)
diff --git a/src/cmd/internal/sys/supported.go b/src/cmd/internal/sys/supported.go
index 0d2bad9..e6c56fb 100644
--- a/src/cmd/internal/sys/supported.go
+++ b/src/cmd/internal/sys/supported.go
@@ -34,6 +34,17 @@
}
}
+// ASanSupported reports whether goos/goarch supports the address
+// sanitizer option.
+func ASanSupported(goos, goarch string) bool {
+ switch goos {
+ case "linux":
+ return goarch == "arm64" || goarch == "amd64"
+ default:
+ return false
+ }
+}
+
// MustLinkExternal reports whether goos/goarch requires external linking.
// (This is the opposite of internal/testenv.CanInternalLink. Keep them in sync.)
func MustLinkExternal(goos, goarch string) bool {