From: fanzha02 Date: Mon, 4 Jan 2021 09:14:35 +0000 (+0800) Subject: cmd/compile: add -asan option X-Git-Tag: go1.18beta1~766 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=85b3b4ee036e7460bf7621f64c6781e5dd0eed98;p=gostls13.git 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 Run-TryBot: fannie zhang TryBot-Result: Go Bot Reviewed-by: Ian Lance Taylor --- diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go index b68ef274f3..ef7fa86749 100644 --- a/src/cmd/compile/doc.go +++ b/src/cmd/compile/doc.go @@ -44,6 +44,8 @@ Flags: 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 4c2516f60e..be6d49fac7 100644 --- a/src/cmd/compile/internal/base/base.go +++ b/src/cmd/compile/internal/base/base.go @@ -67,6 +67,7 @@ var NoInstrumentPkgs = []string{ "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 241f5da185..51938e8fd5 100644 --- a/src/cmd/compile/internal/base/flag.go +++ b/src/cmd/compile/internal/base/flag.go @@ -84,6 +84,7 @@ type CmdFlags struct { // 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 @@ func ParseFlags() { 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 @@ func ParseFlags() { } 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 74b21571b3..ed81ef7bc0 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -107,7 +107,7 @@ func Main(archInit func(*ssagen.ArchInfo)) { // 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 @@ func Main(archInit func(*ssagen.ArchInfo)) { 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 18d0b023ad..41c96079f7 100644 --- a/src/cmd/compile/internal/ir/func.go +++ b/src/cmd/compile/internal/ir/func.go @@ -201,7 +201,7 @@ const ( 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 1435e4313e..b204a1d544 100644 --- a/src/cmd/compile/internal/ir/symtab.go +++ b/src/cmd/compile/internal/ir/symtab.go @@ -15,6 +15,8 @@ var Syms struct { 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 0aaf894641..58dffbad1e 100644 --- a/src/cmd/compile/internal/noder/import.go +++ b/src/cmd/compile/internal/noder/import.go @@ -127,6 +127,8 @@ func openPackage(path string) (*os.File, error) { 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 b4ed96c18a..ce41b8c934 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -1413,6 +1413,9 @@ func WriteBasicTypes() { 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 5a958a569d..6b595ea75d 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -108,6 +108,8 @@ func InitConfig() { 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 @@ func (s *state) instrument(t *types.Type, addr *ssa.Value, kind instrumentKind) } // 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 @@ func (s *state) instrument2(t *types.Type, addr, addr2 *ssa.Value, kind instrume 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 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value { } // 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 524360e8df..67597cebb4 100644 --- a/src/cmd/compile/internal/typecheck/builtin.go +++ b/src/cmd/compile/internal/typecheck/builtin.go @@ -192,6 +192,8 @@ var runtimeDecls = [...]struct { {"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 66641fb5aa..04ae4f23a3 100644 --- a/src/cmd/compile/internal/typecheck/builtin/runtime.go +++ b/src/cmd/compile/internal/typecheck/builtin/runtime.go @@ -250,6 +250,10 @@ func msanread(addr, size uintptr) 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 0d2bad9612..e6c56fbc56 100644 --- a/src/cmd/internal/sys/supported.go +++ b/src/cmd/internal/sys/supported.go @@ -34,6 +34,17 @@ func MSanSupported(goos, goarch string) bool { } } +// 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 {