diff options
-rw-r--r-- | misc/cgo/testsanitizers/asan_test.go | 8 | ||||
-rw-r--r-- | misc/cgo/testsanitizers/cc_test.go | 16 | ||||
-rw-r--r-- | src/cmd/compile/internal/base/base.go | 1 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/obj.go | 17 | ||||
-rw-r--r-- | src/cmd/compile/internal/noder/noder.go | 2 | ||||
-rw-r--r-- | src/cmd/compile/internal/noder/object.go | 2 | ||||
-rw-r--r-- | src/cmd/compile/internal/noder/reader.go | 2 | ||||
-rw-r--r-- | src/cmd/compile/internal/pkginit/init.go | 53 | ||||
-rw-r--r-- | src/cmd/compile/internal/pkginit/initAsanGlobals.go | 241 | ||||
-rw-r--r-- | src/cmd/go/alldocs.go | 2 | ||||
-rw-r--r-- | src/cmd/go/internal/work/build.go | 2 | ||||
-rw-r--r-- | src/cmd/go/internal/work/init.go | 57 | ||||
-rw-r--r-- | src/runtime/asan.go | 4 | ||||
-rw-r--r-- | src/runtime/asan/asan.go | 38 | ||||
-rw-r--r-- | src/runtime/asan0.go | 9 | ||||
-rw-r--r-- | src/runtime/asan_amd64.s | 8 | ||||
-rw-r--r-- | src/runtime/asan_arm64.s | 8 |
17 files changed, 461 insertions, 9 deletions
diff --git a/misc/cgo/testsanitizers/asan_test.go b/misc/cgo/testsanitizers/asan_test.go index ff578ac63e1..23392c00f6d 100644 --- a/misc/cgo/testsanitizers/asan_test.go +++ b/misc/cgo/testsanitizers/asan_test.go @@ -22,6 +22,14 @@ func TestASAN(t *testing.T) { if !aSanSupported(goos, goarch) { t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch) } + // The current implementation is only compatible with the ASan library from version + // v7 to v9 (See the description in src/runtime/asan/asan.go). Therefore, using the + // -asan option must use a compatible version of ASan library, which requires that + // the gcc version is not less than 7 and the clang version is not less than 4, + // otherwise a segmentation fault will occur. + if !compilerRequiredAsanVersion() { + t.Skipf("skipping: too old version of compiler") + } t.Parallel() requireOvercommit(t) diff --git a/misc/cgo/testsanitizers/cc_test.go b/misc/cgo/testsanitizers/cc_test.go index 05b77932b4c..ee3c3bf28cc 100644 --- a/misc/cgo/testsanitizers/cc_test.go +++ b/misc/cgo/testsanitizers/cc_test.go @@ -235,6 +235,22 @@ func compilerSupportsLocation() bool { } } +// compilerRequiredAsanVersion reports whether the compiler is the version required by Asan. +func compilerRequiredAsanVersion() bool { + compiler, err := compilerVersion() + if err != nil { + return false + } + switch compiler.name { + case "gcc": + return compiler.major >= 7 + case "clang": + return true + default: + return false + } +} + type compilerCheck struct { once sync.Once err error diff --git a/src/cmd/compile/internal/base/base.go b/src/cmd/compile/internal/base/base.go index 39ce8e66f73..5e1493e275a 100644 --- a/src/cmd/compile/internal/base/base.go +++ b/src/cmd/compile/internal/base/base.go @@ -70,6 +70,7 @@ var NoInstrumentPkgs = []string{ "runtime/msan", "runtime/asan", "internal/cpu", + "buildcfg", } // Don't insert racefuncenter/racefuncexit into the following packages. diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index fe8b6e9d458..fea2df85e5f 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -9,6 +9,7 @@ import ( "cmd/compile/internal/ir" "cmd/compile/internal/noder" "cmd/compile/internal/objw" + "cmd/compile/internal/pkginit" "cmd/compile/internal/reflectdata" "cmd/compile/internal/staticdata" "cmd/compile/internal/typecheck" @@ -110,7 +111,6 @@ func dumpCompilerObj(bout *bio.Writer) { func dumpdata() { numExterns := len(typecheck.Target.Externs) numDecls := len(typecheck.Target.Decls) - dumpglobls(typecheck.Target.Externs) reflectdata.CollectPTabs() numExports := len(typecheck.Target.Exports) @@ -287,7 +287,20 @@ func ggloblnod(nam *ir.Name) { if nam.Type() != nil && !nam.Type().HasPointers() { flags |= obj.NOPTR } - base.Ctxt.Globl(s, nam.Type().Size(), flags) + size := nam.Type().Size() + linkname := nam.Sym().Linkname + name := nam.Sym().Name + + // We've skipped linkname'd globals's instrument, so we can skip them here as well. + if base.Flag.ASan && linkname == "" && pkginit.InstrumentGlobalsMap[name] != nil { + // Write the new size of instrumented global variables that have + // trailing redzones into object file. + rzSize := pkginit.GetRedzoneSizeForGlobal(size) + sizeWithRZ := rzSize + size + base.Ctxt.Globl(s, sizeWithRZ, flags) + } else { + base.Ctxt.Globl(s, size, flags) + } if nam.LibfuzzerExtraCounter() { s.Type = objabi.SLIBFUZZER_EXTRA_COUNTER } diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index 9a42b5afd17..c4c2db5f78a 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -442,7 +442,7 @@ func parseGoEmbed(args string) ([]string, error) { // the name, normally "pkg.init", is altered to "pkg.init.0". var renameinitgen int -func renameinit() *types.Sym { +func Renameinit() *types.Sym { s := typecheck.LookupNum("init.", renameinitgen) renameinitgen++ return s diff --git a/src/cmd/compile/internal/noder/object.go b/src/cmd/compile/internal/noder/object.go index e8dbaac1613..ee9e0e26800 100644 --- a/src/cmd/compile/internal/noder/object.go +++ b/src/cmd/compile/internal/noder/object.go @@ -104,7 +104,7 @@ func (g *irgen) obj(obj types2.Object) *ir.Name { var typ *types.Type if recv := sig.Recv(); recv == nil { if obj.Name() == "init" { - sym = renameinit() + sym = Renameinit() } else { sym = g.sym(obj) } diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 1350c22467f..10861717f3d 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -643,7 +643,7 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node case pkgbits.ObjFunc: if sym.Name == "init" { - sym = renameinit() + sym = Renameinit() } name := do(ir.ONAME, true) setType(name, r.signature(sym.Pkg, nil)) diff --git a/src/cmd/compile/internal/pkginit/init.go b/src/cmd/compile/internal/pkginit/init.go index 32e95bedc23..d94482a962d 100644 --- a/src/cmd/compile/internal/pkginit/init.go +++ b/src/cmd/compile/internal/pkginit/init.go @@ -7,6 +7,7 @@ package pkginit import ( "cmd/compile/internal/base" "cmd/compile/internal/ir" + "cmd/compile/internal/noder" "cmd/compile/internal/objw" "cmd/compile/internal/staticinit" "cmd/compile/internal/typecheck" @@ -83,6 +84,58 @@ func Task() *ir.Name { } deps = append(deps, n.(*ir.Name).Linksym()) } + if base.Flag.ASan { + // Make an initialization function to call runtime.asanregisterglobals to register an + // array of instrumented global variables when -asan is enabled. An instrumented global + // variable is described by a structure. + // See the _asan_global structure declared in src/runtime/asan/asan.go. + // + // func init { + // var globals []_asan_global {...} + // asanregisterglobals(&globals[0], len(globals)) + // } + for _, n := range typecheck.Target.Externs { + if canInstrumentGlobal(n) { + name := n.Sym().Name + InstrumentGlobalsMap[name] = n + InstrumentGlobalsSlice = append(InstrumentGlobalsSlice, n) + } + } + ni := len(InstrumentGlobalsMap) + if ni != 0 { + // Make an init._ function. + base.Pos = base.AutogeneratedPos + typecheck.DeclContext = ir.PEXTERN + name := noder.Renameinit() + fnInit := typecheck.DeclFunc(name, ir.NewFuncType(base.Pos, nil, nil, nil)) + + // Get an array of intrumented global variables. + globals := instrumentGlobals(fnInit) + + // Call runtime.asanregisterglobals function to poison redzones. + // runtime.asanregisterglobals(unsafe.Pointer(&globals[0]), ni) + asanf := typecheck.NewName(ir.Pkgs.Runtime.Lookup("asanregisterglobals")) + ir.MarkFunc(asanf) + asanf.SetType(types.NewSignature(types.NoPkg, nil, nil, []*types.Field{ + types.NewField(base.Pos, nil, types.Types[types.TUNSAFEPTR]), + types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]), + }, nil)) + asancall := ir.NewCallExpr(base.Pos, ir.OCALL, asanf, nil) + asancall.Args.Append(typecheck.ConvNop(typecheck.NodAddr( + ir.NewIndexExpr(base.Pos, globals, ir.NewInt(0))), types.Types[types.TUNSAFEPTR])) + asancall.Args.Append(typecheck.ConvNop(ir.NewInt(int64(ni)), types.Types[types.TUINTPTR])) + + fnInit.Body.Append(asancall) + typecheck.FinishFuncBody() + typecheck.Func(fnInit) + ir.CurFunc = fnInit + typecheck.Stmts(fnInit.Body) + ir.CurFunc = nil + + typecheck.Target.Decls = append(typecheck.Target.Decls, fnInit) + typecheck.Target.Inits = append(typecheck.Target.Inits, fnInit) + } + } // Record user init functions. for _, fn := range typecheck.Target.Inits { diff --git a/src/cmd/compile/internal/pkginit/initAsanGlobals.go b/src/cmd/compile/internal/pkginit/initAsanGlobals.go new file mode 100644 index 00000000000..7276791d6e9 --- /dev/null +++ b/src/cmd/compile/internal/pkginit/initAsanGlobals.go @@ -0,0 +1,241 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkginit + +import ( + "strings" + + "cmd/compile/internal/base" + "cmd/compile/internal/ir" + "cmd/compile/internal/typecheck" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// instrumentGlobals declares a global array of _asan_global structures and initializes it. +func instrumentGlobals(fn *ir.Func) *ir.Name { + asanGlobalStruct, asanLocationStruct, defStringstruct := createtypes() + lname := typecheck.Lookup + tconv := typecheck.ConvNop + // Make a global array of asanGlobalStruct type. + // var asanglobals []asanGlobalStruct + arraytype := types.NewArray(asanGlobalStruct, int64(len(InstrumentGlobalsMap))) + symG := lname(".asanglobals") + globals := typecheck.NewName(symG) + globals.SetType(arraytype) + globals.Class = ir.PEXTERN + symG.Def = globals + typecheck.Target.Externs = append(typecheck.Target.Externs, globals) + // Make a global array of asanLocationStruct type. + // var asanL []asanLocationStruct + arraytype = types.NewArray(asanLocationStruct, int64(len(InstrumentGlobalsMap))) + symL := lname(".asanL") + asanlocation := typecheck.NewName(symL) + asanlocation.SetType(arraytype) + asanlocation.Class = ir.PEXTERN + symL.Def = asanlocation + typecheck.Target.Externs = append(typecheck.Target.Externs, asanlocation) + // Make three global string variables to pass the global name and module name + // and the name of the source file that defines it. + // var asanName string + // var asanModulename string + // var asanFilename string + symL = lname(".asanName") + asanName := typecheck.NewName(symL) + asanName.SetType(types.Types[types.TSTRING]) + asanName.Class = ir.PEXTERN + symL.Def = asanName + typecheck.Target.Externs = append(typecheck.Target.Externs, asanName) + + symL = lname(".asanModulename") + asanModulename := typecheck.NewName(symL) + asanModulename.SetType(types.Types[types.TSTRING]) + asanModulename.Class = ir.PEXTERN + symL.Def = asanModulename + typecheck.Target.Externs = append(typecheck.Target.Externs, asanModulename) + + symL = lname(".asanFilename") + asanFilename := typecheck.NewName(symL) + asanFilename.SetType(types.Types[types.TSTRING]) + asanFilename.Class = ir.PEXTERN + symL.Def = asanFilename + typecheck.Target.Externs = append(typecheck.Target.Externs, asanFilename) + + var init ir.Nodes + var c ir.Node + // globals[i].odrIndicator = 0 is the default, no need to set it explicitly here. + for i, n := range InstrumentGlobalsSlice { + setField := func(f string, val ir.Node, i int) { + r := ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, + ir.NewIndexExpr(base.Pos, globals, ir.NewInt(int64(i))), lname(f)), val) + init.Append(typecheck.Stmt(r)) + } + // globals[i].beg = uintptr(unsafe.Pointer(&n)) + c = tconv(typecheck.NodAddr(n), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.Types[types.TUINTPTR]) + setField("beg", c, i) + // Assign globals[i].size. + g := n.(*ir.Name) + size := g.Type().Size() + c = tconv(ir.NewInt(size), types.Types[types.TUINTPTR]) + setField("size", c, i) + // Assign globals[i].sizeWithRedzone. + rzSize := GetRedzoneSizeForGlobal(size) + sizeWithRz := rzSize + size + c = tconv(ir.NewInt(sizeWithRz), types.Types[types.TUINTPTR]) + setField("sizeWithRedzone", c, i) + // The C string type is terminated by a null charactor "\0", Go should use three-digit + // octal "\000" or two-digit hexadecimal "\x00" to create null terminated string. + // asanName = symbol's linkname + "\000" + // globals[i].name = (*defString)(unsafe.Pointer(&asanName)).data + name := g.Linksym().Name + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanName, ir.NewString(name+"\000")))) + c = tconv(typecheck.NodAddr(asanName), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.NewPtr(defStringstruct)) + c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data")) + setField("name", c, i) + + // Set the name of package being compiled as a unique identifier of a module. + // asanModulename = pkgName + "\000" + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanModulename, ir.NewString(types.LocalPkg.Name+"\000")))) + c = tconv(typecheck.NodAddr(asanModulename), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.NewPtr(defStringstruct)) + c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data")) + setField("moduleName", c, i) + // Assign asanL[i].filename, asanL[i].line, asanL[i].column + // and assign globals[i].location = uintptr(unsafe.Pointer(&asanL[i])) + asanLi := ir.NewIndexExpr(base.Pos, asanlocation, ir.NewInt(int64(i))) + filename := ir.NewString(base.Ctxt.PosTable.Pos(n.Pos()).Filename() + "\000") + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, asanFilename, filename))) + c = tconv(typecheck.NodAddr(asanFilename), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.NewPtr(defStringstruct)) + c = ir.NewSelectorExpr(base.Pos, ir.ODOT, c, lname("data")) + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("filename")), c))) + line := ir.NewInt(int64(n.Pos().Line())) + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("line")), line))) + col := ir.NewInt(int64(n.Pos().Col())) + init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, ir.NewSelectorExpr(base.Pos, ir.ODOT, asanLi, lname("column")), col))) + c = tconv(typecheck.NodAddr(asanLi), types.Types[types.TUNSAFEPTR]) + c = tconv(c, types.Types[types.TUINTPTR]) + setField("sourceLocation", c, i) + } + fn.Body.Append(init...) + return globals +} + +// createtypes creates the asanGlobal, asanLocation and defString struct type. +// Go compiler does not refer to the C types, we represent the struct field +// by a uintptr, then use type conversion to make copies of the data. +// E.g., (*defString)(asanGlobal.name).data to C string. +// +// Keep in sync with src/runtime/asan/asan.go. +// type asanGlobal struct { +// beg uintptr +// size uintptr +// size_with_redzone uintptr +// name uintptr +// moduleName uintptr +// hasDynamicInit uintptr +// sourceLocation uintptr +// odrIndicator uintptr +// } +// +// type asanLocation struct { +// filename uintptr +// line int32 +// column int32 +// } +// +// defString is synthesized struct type meant to capture the underlying +// implementations of string. +// type defString struct { +// data uintptr +// len uintptr +// } + +func createtypes() (*types.Type, *types.Type, *types.Type) { + up := types.Types[types.TUINTPTR] + i32 := types.Types[types.TINT32] + fname := typecheck.Lookup + nxp := src.NoXPos + nfield := types.NewField + asanGlobal := types.NewStruct(types.NoPkg, []*types.Field{ + nfield(nxp, fname("beg"), up), + nfield(nxp, fname("size"), up), + nfield(nxp, fname("sizeWithRedzone"), up), + nfield(nxp, fname("name"), up), + nfield(nxp, fname("moduleName"), up), + nfield(nxp, fname("hasDynamicInit"), up), + nfield(nxp, fname("sourceLocation"), up), + nfield(nxp, fname("odrIndicator"), up), + }) + types.CalcSize(asanGlobal) + + asanLocation := types.NewStruct(types.NoPkg, []*types.Field{ + nfield(nxp, fname("filename"), up), + nfield(nxp, fname("line"), i32), + nfield(nxp, fname("column"), i32), + }) + types.CalcSize(asanLocation) + + defString := types.NewStruct(types.NoPkg, []*types.Field{ + types.NewField(nxp, fname("data"), up), + types.NewField(nxp, fname("len"), up), + }) + types.CalcSize(defString) + + return asanGlobal, asanLocation, defString +} + +// Calculate redzone for globals. +func GetRedzoneSizeForGlobal(size int64) int64 { + maxRZ := int64(1 << 18) + minRZ := int64(32) + redZone := (size / minRZ / 4) * minRZ + switch { + case redZone > maxRZ: + redZone = maxRZ + case redZone < minRZ: + redZone = minRZ + } + // Round up to multiple of minRZ. + if size%minRZ != 0 { + redZone += minRZ - (size % minRZ) + } + return redZone +} + +// InstrumentGlobalsMap contains only package-local (and unlinknamed from somewhere else) +// globals. +// And the key is the object name. For example, in package p, a global foo would be in this +// map as "foo". +// Consider range over maps is nondeterministic, make a slice to hold all the values in the +// InstrumentGlobalsMap and iterate over the InstrumentGlobalsSlice. +var InstrumentGlobalsMap = make(map[string]ir.Node) +var InstrumentGlobalsSlice = make([]ir.Node, 0, 0) + +func canInstrumentGlobal(g ir.Node) bool { + if g.Op() != ir.ONAME { + return false + } + n := g.(*ir.Name) + if n.Class == ir.PFUNC { + return false + } + if n.Sym().Pkg != types.LocalPkg { + return false + } + // Do not instrument any _cgo_ related global variables, because they are declared in C code. + if strings.Contains(n.Sym().Name, "cgo") { + return false + } + + // Do not instrument globals that are linknamed, because their home package will do the work. + if n.Sym().Linkname != "" { + return false + } + + return true +} diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 6fdb4f93a33..7193ab6c7ed 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -123,6 +123,8 @@ // -asan // enable interoperation with address sanitizer. // Supported only on linux/arm64, linux/amd64. +// Supported only on linux/amd64 or linux/arm64 and only with GCC 7 and higher +// or Clang/LLVM 4 and higher. // -v // print the names of packages as they are compiled. // -work diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index e9a8ee6cb3a..d69eb7a9bf7 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -79,6 +79,8 @@ and test commands: -asan enable interoperation with address sanitizer. Supported only on linux/arm64, linux/amd64. + Supported only on linux/amd64 or linux/arm64 and only with GCC 7 and higher + or Clang/LLVM 4 and higher. -v print the names of packages as they are compiled. -work diff --git a/src/cmd/go/internal/work/init.go b/src/cmd/go/internal/work/init.go index 26192ecaed1..22e29e87f40 100644 --- a/src/cmd/go/internal/work/init.go +++ b/src/cmd/go/internal/work/init.go @@ -14,9 +14,13 @@ import ( "cmd/internal/quoted" "cmd/internal/sys" "fmt" + exec "internal/execabs" "os" "path/filepath" + "regexp" "runtime" + "strconv" + "strings" ) func BuildInit() { @@ -107,6 +111,15 @@ func instrumentInit() { base.SetExitStatus(2) base.Exit() } + // The current implementation is only compatible with the ASan library from version + // v7 to v9 (See the description in src/runtime/asan/asan.go). Therefore, using the + // -asan option must use a compatible version of ASan library, which requires that + // the gcc version is not less than 7 and the clang version is not less than 4, + // otherwise a segmentation fault will occur. + if cfg.BuildASan { + compilerRequiredAsanVersion() + } + mode := "race" if cfg.BuildMSan { mode = "msan" @@ -310,3 +323,47 @@ func buildModeInit() { } } } + +// compilerRequiredAsanVersion checks whether the compiler is the version required by Asan. +func compilerRequiredAsanVersion() { + cc := os.Getenv("CC") + isgcc := false + if strings.HasPrefix(cc, "gcc") { + isgcc = true + } else if !strings.HasPrefix(cc, "clang") { + fmt.Fprintf(os.Stderr, "-asan requires C compiler is gcc or clang, not %s", cc) + base.SetExitStatus(2) + base.Exit() + } + out, err := exec.Command(cc, "-v").CombinedOutput() + if err != nil { + fmt.Fprintf(os.Stderr, "-asan fails to check C compiler %s version: %v", cc, err) + base.SetExitStatus(2) + base.Exit() + } + re := regexp.MustCompile(`version ([0-9]+)\.([0-9]+)\.([0-9]+)`) + matches := re.FindSubmatch(out) + if len(matches) < 3 { + fmt.Fprintf(os.Stderr, "-asan fails to check C compiler %s version: %s", cc, out) + } + major, err1 := strconv.Atoi(string(matches[1])) + minor, err2 := strconv.Atoi(string(matches[2])) + if err1 != nil || err2 != nil { + fmt.Fprintf(os.Stderr, "-asan fails to check C compiler %s version: %v, %v", cc, err1, err2) + base.SetExitStatus(2) + base.Exit() + } + if isgcc { + if major < 7 { + fmt.Fprintf(os.Stderr, "-asan is not supported with gcc %d.%d; requires gcc 7 or later", major, minor) + base.SetExitStatus(2) + base.Exit() + } + } else { + if major < 4 { + fmt.Fprintf(os.Stderr, "-asan is not supported with clang %d.%d; requires clang 4 or later", major, minor) + base.SetExitStatus(2) + base.Exit() + } + } +} diff --git a/src/runtime/asan.go b/src/runtime/asan.go index 8c41e418f7c..25b83277e6d 100644 --- a/src/runtime/asan.go +++ b/src/runtime/asan.go @@ -55,9 +55,13 @@ func asanunpoison(addr unsafe.Pointer, sz uintptr) //go:noescape func asanpoison(addr unsafe.Pointer, sz uintptr) +//go:noescape +func asanregisterglobals(addr unsafe.Pointer, n uintptr) + // These are called from asan_GOARCH.s // //go:cgo_import_static __asan_read_go //go:cgo_import_static __asan_write_go //go:cgo_import_static __asan_unpoison_go //go:cgo_import_static __asan_poison_go +//go:cgo_import_static __asan_register_globals_go diff --git a/src/runtime/asan/asan.go b/src/runtime/asan/asan.go index bab2362c51e..3e41d60d93c 100644 --- a/src/runtime/asan/asan.go +++ b/src/runtime/asan/asan.go @@ -34,5 +34,43 @@ void __asan_poison_go(void *addr, uintptr_t sz) { __asan_poison_memory_region(addr, sz); } +// Keep in sync with the defination in compiler-rt +// https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/asan/asan_interface_internal.h#L41 +// This structure is used to describe the source location of +// a place where global was defined. +struct _asan_global_source_location { + const char *filename; + int line_no; + int column_no; +}; + +// Keep in sync with the defination in compiler-rt +// https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/asan/asan_interface_internal.h#L48 +// So far, the current implementation is only compatible with the ASan library from version v7 to v9. +// https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/asan/asan_init_version.h +// This structure describes an instrumented global variable. +// +// TODO: If a later version of the ASan library changes __asan_global or __asan_global_source_location +// structure, we need to make the same changes. +struct _asan_global { + uintptr_t beg; + uintptr_t size; + uintptr_t size_with_redzone; + const char *name; + const char *module_name; + uintptr_t has_dynamic_init; + struct _asan_global_source_location *location; + uintptr_t odr_indicator; +}; + + +extern void __asan_register_globals(void*, long int); + +// Register global variables. +// The 'globals' is an array of structures describing 'n' globals. +void __asan_register_globals_go(void *addr, uintptr_t n) { + struct _asan_global *globals = (struct _asan_global *)(addr); + __asan_register_globals(globals, n); +} */ import "C" diff --git a/src/runtime/asan0.go b/src/runtime/asan0.go index d5478d6bee8..0948786200a 100644 --- a/src/runtime/asan0.go +++ b/src/runtime/asan0.go @@ -16,7 +16,8 @@ const asanenabled = false // Because asanenabled is false, none of these functions should be called. -func asanread(addr unsafe.Pointer, sz uintptr) { throw("asan") } -func asanwrite(addr unsafe.Pointer, sz uintptr) { throw("asan") } -func asanunpoison(addr unsafe.Pointer, sz uintptr) { throw("asan") } -func asanpoison(addr unsafe.Pointer, sz uintptr) { throw("asan") } +func asanread(addr unsafe.Pointer, sz uintptr) { throw("asan") } +func asanwrite(addr unsafe.Pointer, sz uintptr) { throw("asan") } +func asanunpoison(addr unsafe.Pointer, sz uintptr) { throw("asan") } +func asanpoison(addr unsafe.Pointer, sz uintptr) { throw("asan") } +func asanregisterglobals(addr unsafe.Pointer, sz uintptr) { throw("asan") } diff --git a/src/runtime/asan_amd64.s b/src/runtime/asan_amd64.s index 38573500202..0489aa86dd4 100644 --- a/src/runtime/asan_amd64.s +++ b/src/runtime/asan_amd64.s @@ -61,6 +61,14 @@ TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 MOVQ $__asan_poison_go(SB), AX JMP asancall<>(SB) +// func runtime·asanregisterglobals(addr unsafe.Pointer, n uintptr) +TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16 + MOVD addr+0(FP), RARG0 + MOVD size+8(FP), RARG1 + // void __asan_register_globals_go(void *addr, uintptr_t n); + MOVD $__asan_register_globals_go(SB), AX + JMP asancall<>(SB) + // Switches SP to g0 stack and calls (AX). Arguments already set. TEXT asancall<>(SB), NOSPLIT, $0-0 get_tls(R12) diff --git a/src/runtime/asan_arm64.s b/src/runtime/asan_arm64.s index 5ed03c932bd..697c98206ea 100644 --- a/src/runtime/asan_arm64.s +++ b/src/runtime/asan_arm64.s @@ -50,6 +50,14 @@ TEXT runtime·asanpoison(SB), NOSPLIT, $0-16 MOVD $__asan_poison_go(SB), FARG JMP asancall<>(SB) +// func runtime·asanregisterglobals(addr unsafe.Pointer, n uintptr) +TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16 + MOVD addr+0(FP), RARG0 + MOVD size+8(FP), RARG1 + // void __asan_register_globals_go(void *addr, uintptr_t n); + MOVD $__asan_register_globals_go(SB), FARG + JMP asancall<>(SB) + // Switches SP to g0 stack and calls (FARG). Arguments already set. TEXT asancall<>(SB), NOSPLIT, $0-0 MOVD RSP, R19 // callee-saved |