1
0
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:
fanzha02 2021-01-04 17:14:35 +08:00 committed by Ian Lance Taylor
parent 60c3069dd8
commit 85b3b4ee03
12 changed files with 58 additions and 10 deletions

View File

@ -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

View File

@ -67,6 +67,7 @@ var NoInstrumentPkgs = []string{
"runtime",
"runtime/race",
"runtime/msan",
"runtime/asan",
"internal/cpu",
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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
)

View File

@ -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

View File

@ -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 {

View File

@ -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", ""))
}

View File

@ -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)

View File

@ -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},

View File

@ -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)

View File

@ -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 {