mirror of
https://github.com/golang/go
synced 2024-11-17 14:04:48 -07:00
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>
This commit is contained in:
parent
60c3069dd8
commit
85b3b4ee03
@ -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
|
||||
|
@ -67,6 +67,7 @@ var NoInstrumentPkgs = []string{
|
||||
"runtime",
|
||||
"runtime/race",
|
||||
"runtime/msan",
|
||||
"runtime/asan",
|
||||
"internal/cpu",
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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", ""))
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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},
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user