Skip to content

Commit c379c3d

Browse files
committed
cmd/compile: set conversions to unsafe.Pointer as an escaping operation 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]>
1 parent 599d539 commit c379c3d

File tree

6 files changed

+88
-3
lines changed

6 files changed

+88
-3
lines changed

misc/cgo/testsanitizers/asan_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ func TestASAN(t *testing.T) {
4141
{src: "asan4_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan4_fail.go:13"},
4242
{src: "asan5_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan5_fail.go:18"},
4343
{src: "asan_useAfterReturn.go"},
44+
{src: "asan_unsafe_fail1.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail1.go:25"},
45+
{src: "asan_unsafe_fail2.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail2.go:25"},
46+
{src: "asan_unsafe_fail3.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail3.go:18"},
4447
}
4548
for _, tc := range cases {
4649
tc := tc
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"fmt"
9+
"unsafe"
10+
)
11+
12+
func main() {
13+
a := 1
14+
b := 2
15+
c := add(a, b)
16+
d := a + b
17+
fmt.Println(c, d)
18+
}
19+
20+
//go:noinline
21+
func add(a1, b1 int) int {
22+
// The arguments.
23+
// When -asan is enabled, unsafe.Pointer(&a1) conversion is escaping.
24+
var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a1), 1*unsafe.Sizeof(int(1))))
25+
*p = 10 // BOOM
26+
return a1 + b1
27+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"fmt"
9+
"unsafe"
10+
)
11+
12+
func main() {
13+
a := 1
14+
b := 2
15+
c := add(a, b)
16+
d := a + b
17+
fmt.Println(c, d)
18+
}
19+
20+
//go:noinline
21+
func add(a1, b1 int) (ret int) {
22+
// The return value
23+
// When -asan is enabled, the unsafe.Pointer(&ret) conversion is escaping.
24+
var p *int = (*int)(unsafe.Add(unsafe.Pointer(&ret), 1*unsafe.Sizeof(int(1))))
25+
*p = 123 // BOOM
26+
ret = a1 + b1
27+
return
28+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"fmt"
9+
"unsafe"
10+
)
11+
12+
func main() {
13+
a := 1
14+
b := 2
15+
// The local variables.
16+
// When -asan is enabled, the unsafe.Pointer(&a) conversion is escaping.
17+
var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a), 1*unsafe.Sizeof(int(1))))
18+
*p = 20 // BOOM
19+
d := a + b
20+
fmt.Println(d)
21+
}

src/cmd/compile/internal/escape/expr.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
100100

101101
case ir.OCONV, ir.OCONVNOP:
102102
n := n.(*ir.ConvExpr)
103-
if ir.ShouldCheckPtr(e.curfn, 2) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() {
104-
// When -d=checkptr=2 is enabled, treat
105-
// conversions to unsafe.Pointer as an
103+
if (ir.ShouldCheckPtr(e.curfn, 2) || ir.ShouldAsanCheckPtr(e.curfn)) && n.Type().IsUnsafePtr() && n.X.Type().IsPtr() {
104+
// When -d=checkptr=2 or -asan is enabled,
105+
// treat conversions to unsafe.Pointer as an
106106
// escaping operation. This allows better
107107
// runtime instrumentation, since we can more
108108
// easily detect object boundaries on the heap

src/cmd/compile/internal/ir/expr.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,12 @@ func ShouldCheckPtr(fn *Func, level int) bool {
10361036
return base.Debug.Checkptr >= level && fn.Pragma&NoCheckPtr == 0
10371037
}
10381038

1039+
// ShouldAsanCheckPtr reports whether pointer checking should be enabled for
1040+
// function fn when -asan is enabled.
1041+
func ShouldAsanCheckPtr(fn *Func) bool {
1042+
return base.Flag.ASan && fn.Pragma&NoCheckPtr == 0
1043+
}
1044+
10391045
// IsReflectHeaderDataField reports whether l is an expression p.Data
10401046
// where p has type reflect.SliceHeader or reflect.StringHeader.
10411047
func IsReflectHeaderDataField(l Node) bool {

0 commit comments

Comments
 (0)