Skip to content

Commit

Permalink
cmd/compile: set conversions to unsafe.Pointer as an escaping operati…
Browse files Browse the repository at this point in the history
…on when -asan is enabled

When ASan is enabled, treat conversions to unsafe.Pointer as
an escaping operation. In this way, all pointer operations on
the stack objects will become operations on the escaped heap
objects. As we've already supported ASan detection of error
memory accesses to heap objects. With this trick, we can use
-asan option to report errors on bad stack operations.

Add test cases.

Updates #44853.

Change-Id: I6281e77f6ba581d7008d610f0b24316078b6e746
Reviewed-on: https://go-review.googlesource.com/c/go/+/393315
Trust: Fannie Zhang <[email protected]>
Run-TryBot: Fannie Zhang <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Reviewed-by: Eric Fang <[email protected]>
  • Loading branch information
zhangfannie committed Mar 17, 2022
1 parent 599d539 commit c379c3d
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 3 deletions.
3 changes: 3 additions & 0 deletions misc/cgo/testsanitizers/asan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ func TestASAN(t *testing.T) {
{src: "asan4_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan4_fail.go:13"},
{src: "asan5_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan5_fail.go:18"},
{src: "asan_useAfterReturn.go"},
{src: "asan_unsafe_fail1.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail1.go:25"},
{src: "asan_unsafe_fail2.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail2.go:25"},
{src: "asan_unsafe_fail3.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail3.go:18"},
}
for _, tc := range cases {
tc := tc
Expand Down
27 changes: 27 additions & 0 deletions misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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 main

import (
"fmt"
"unsafe"
)

func main() {
a := 1
b := 2
c := add(a, b)
d := a + b
fmt.Println(c, d)
}

//go:noinline
func add(a1, b1 int) int {
// The arguments.
// When -asan is enabled, unsafe.Pointer(&a1) conversion is escaping.
var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a1), 1*unsafe.Sizeof(int(1))))
*p = 10 // BOOM
return a1 + b1
}
28 changes: 28 additions & 0 deletions misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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 main

import (
"fmt"
"unsafe"
)

func main() {
a := 1
b := 2
c := add(a, b)
d := a + b
fmt.Println(c, d)
}

//go:noinline
func add(a1, b1 int) (ret int) {
// The return value
// When -asan is enabled, the unsafe.Pointer(&ret) conversion is escaping.
var p *int = (*int)(unsafe.Add(unsafe.Pointer(&ret), 1*unsafe.Sizeof(int(1))))
*p = 123 // BOOM
ret = a1 + b1
return
}
21 changes: 21 additions & 0 deletions misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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 main

import (
"fmt"
"unsafe"
)

func main() {
a := 1
b := 2
// The local variables.
// When -asan is enabled, the unsafe.Pointer(&a) conversion is escaping.
var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a), 1*unsafe.Sizeof(int(1))))
*p = 20 // BOOM
d := a + b
fmt.Println(d)
}
6 changes: 3 additions & 3 deletions src/cmd/compile/internal/escape/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {

case ir.OCONV, ir.OCONVNOP:
n := n.(*ir.ConvExpr)
if ir.ShouldCheckPtr(e.curfn, 2) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() {
// When -d=checkptr=2 is enabled, treat
// conversions to unsafe.Pointer as an
if (ir.ShouldCheckPtr(e.curfn, 2) || ir.ShouldAsanCheckPtr(e.curfn)) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() {
// When -d=checkptr=2 or -asan is enabled,
// treat conversions to unsafe.Pointer as an
// escaping operation. This allows better
// runtime instrumentation, since we can more
// easily detect object boundaries on the heap
Expand Down
6 changes: 6 additions & 0 deletions src/cmd/compile/internal/ir/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,12 @@ func ShouldCheckPtr(fn *Func, level int) bool {
return base.Debug.Checkptr >= level && fn.Pragma&NoCheckPtr == 0
}

// ShouldAsanCheckPtr reports whether pointer checking should be enabled for
// function fn when -asan is enabled.
func ShouldAsanCheckPtr(fn *Func) bool {
return base.Flag.ASan && fn.Pragma&NoCheckPtr == 0
}

// IsReflectHeaderDataField reports whether l is an expression p.Data
// where p has type reflect.SliceHeader or reflect.StringHeader.
func IsReflectHeaderDataField(l Node) bool {
Expand Down

0 comments on commit c379c3d

Please sign in to comment.