diff options
Diffstat (limited to 'src/cmd/compile/internal')
| -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 |
7 files changed, 313 insertions, 5 deletions
diff --git a/src/cmd/compile/internal/base/base.go b/src/cmd/compile/internal/base/base.go index 39ce8e66f7..5e1493e275 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 fe8b6e9d45..fea2df85e5 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 9a42b5afd1..c4c2db5f78 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 e8dbaac161..ee9e0e2680 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 1350c22467..10861717f3 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 32e95bedc2..d94482a962 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 0000000000..7276791d6e --- /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 +} |
