]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: set conversions to unsafe.Pointer as an escaping operation when -asan...
authorfanzha02 <fannie.zhang@arm.com>
Mon, 7 Jun 2021 06:24:45 +0000 (14:24 +0800)
committerFannie Zhang <Fannie.Zhang@arm.com>
Wed, 16 Mar 2022 07:03:20 +0000 (07:03 +0000)
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.

CustomizedGitHooks: yes
Change-Id: I4e7fe46a3ce01f0d219e6a67dc50f4aff7d2ad87
Reviewed-on: https://go-review.googlesource.com/c/go/+/325629
Trust: Fannie Zhang <Fannie.Zhang@arm.com>
Reviewed-by: Keith Randall <khr@golang.org>
misc/cgo/testsanitizers/asan_test.go
misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go [new file with mode: 0644]
misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go [new file with mode: 0644]
misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go [new file with mode: 0644]
src/cmd/compile/internal/escape/expr.go
src/cmd/compile/internal/ir/expr.go

index 22dcf23c3ba76e31020b68cf21f2ed406f034e36..ff578ac63e1cdd26d95a52e87e5a747e078475a4 100644 (file)
@@ -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
diff --git a/misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go
new file mode 100644 (file)
index 0000000..e66387c
--- /dev/null
@@ -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.Pointer(uintptr(unsafe.Pointer(&a1)) + 1*unsafe.Sizeof(int(1))))
+       *p = 10 // BOOM
+       return a1 + b1
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go
new file mode 100644 (file)
index 0000000..4f25aac
--- /dev/null
@@ -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.Pointer(uintptr(unsafe.Pointer(&ret)) + 1*unsafe.Sizeof(int(1))))
+       *p = 123 // BOOM
+       ret = a1 + b1
+       return
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go
new file mode 100644 (file)
index 0000000..a05044f
--- /dev/null
@@ -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.Pointer(uintptr(unsafe.Pointer(&a)) + 1*unsafe.Sizeof(int(1))))
+       *p = 20 // BOOM
+       d := a + b
+       fmt.Println(d)
+}
index ced90a47bcb6632a4c8df5a0a1664ab5de409429..9c3e09d10da676757d489e2d9e43b7eaac168cba 100644 (file)
@@ -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
index 156fe9649381ee043e36b813f894b47ec0cc0002..ebb84ad78fbf009ddc5071437711c57d765ec4cd 100644 (file)
@@ -1035,6 +1035,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 {