]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/dist: add asan tests in misc/cgo/testsanitizers package
authorfanzha02 <fannie.zhang@arm.com>
Tue, 5 Jan 2021 09:52:43 +0000 (17:52 +0800)
committerIan Lance Taylor <iant@golang.org>
Tue, 2 Nov 2021 06:21:44 +0000 (06:21 +0000)
Add asan tests to check the use of Go with -asan option.

Currenly, the address sanitizer in Go only checks for error
memory access to heap objects.

TODO: Enable check for error memory access to global objects.

Updates #44853.

Change-Id: I83579f229f117b5684a369fc8f365f4dea140648
Reviewed-on: https://go-review.googlesource.com/c/go/+/298615
Trust: fannie zhang <Fannie.Zhang@arm.com>
Run-TryBot: fannie zhang <Fannie.Zhang@arm.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
misc/cgo/testsanitizers/asan_test.go [new file with mode: 0644]
misc/cgo/testsanitizers/cc_test.go
misc/cgo/testsanitizers/testdata/asan1_fail.go [new file with mode: 0644]
misc/cgo/testsanitizers/testdata/asan2_fail.go [new file with mode: 0644]
misc/cgo/testsanitizers/testdata/asan3_fail.go [new file with mode: 0644]
misc/cgo/testsanitizers/testdata/asan4_fail.go [new file with mode: 0644]
misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go [new file with mode: 0644]
src/cmd/internal/sys/supported.go

diff --git a/misc/cgo/testsanitizers/asan_test.go b/misc/cgo/testsanitizers/asan_test.go
new file mode 100644 (file)
index 0000000..dbcce2f
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright 2021 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 sanitizers_test
+
+import (
+       "strings"
+       "testing"
+)
+
+func TestASAN(t *testing.T) {
+       goos, err := goEnv("GOOS")
+       if err != nil {
+               t.Fatal(err)
+       }
+       goarch, err := goEnv("GOARCH")
+       if err != nil {
+               t.Fatal(err)
+       }
+       // The asan tests require support for the -asan option.
+       if !aSanSupported(goos, goarch) {
+               t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch)
+       }
+
+       t.Parallel()
+       requireOvercommit(t)
+       config := configure("address")
+       config.skipIfCSanitizerBroken(t)
+
+       mustRun(t, config.goCmd("build", "std"))
+
+       cases := []struct {
+               src               string
+               memoryAccessError string
+       }{
+               {src: "asan1_fail.go", memoryAccessError: "heap-use-after-free"},
+               {src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow"},
+               {src: "asan3_fail.go", memoryAccessError: "use-after-poison"},
+               {src: "asan4_fail.go", memoryAccessError: "use-after-poison"},
+               {src: "asan_useAfterReturn.go"},
+       }
+       for _, tc := range cases {
+               tc := tc
+               name := strings.TrimSuffix(tc.src, ".go")
+               t.Run(name, func(t *testing.T) {
+                       t.Parallel()
+
+                       dir := newTempDir(t)
+                       defer dir.RemoveAll(t)
+
+                       outPath := dir.Join(name)
+                       mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src)))
+
+                       cmd := hangProneCmd(outPath)
+                       if tc.memoryAccessError != "" {
+                               out, err := cmd.CombinedOutput()
+                               if err != nil && strings.Contains(string(out), tc.memoryAccessError) {
+                                       return
+                               }
+                               t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out)
+                       }
+                       mustRun(t, cmd)
+               })
+       }
+}
index 7af30ab5571f4934ab168cc179f620fdc0ad1674..b776afa3e63b319c0452e7ff307960b999690fc3 100644 (file)
@@ -267,6 +267,9 @@ func configure(sanitizer string) *config {
                        c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan")
                }
 
+       case "address":
+               c.goFlags = append(c.goFlags, "-asan")
+
        default:
                panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer))
        }
@@ -450,3 +453,14 @@ func mSanSupported(goos, goarch string) bool {
                return false
        }
 }
+
+// aSanSupported is a copy of the function cmd/internal/sys.ASanSupported,
+// because the internal pacakage can't be used here.
+func aSanSupported(goos, goarch string) bool {
+       switch goos {
+       case "linux":
+               return goarch == "amd64" || goarch == "arm64"
+       default:
+               return false
+       }
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan1_fail.go b/misc/cgo/testsanitizers/testdata/asan1_fail.go
new file mode 100644 (file)
index 0000000..e60db76
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2021 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
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+int *p;
+int* test() {
+ p = (int *)malloc(2 * sizeof(int));
+ free(p);
+ return p;
+}
+*/
+import "C"
+import "fmt"
+
+func main() {
+       // C passes Go an invalid pointer.
+       a := C.test()
+       // Use after free
+       *a = 2
+       // We shouldn't get here; asan should stop us first.
+       fmt.Println(*a)
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan2_fail.go b/misc/cgo/testsanitizers/testdata/asan2_fail.go
new file mode 100644 (file)
index 0000000..e35670c
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright 2021 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
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+int *p;
+int* f() {
+  int i;
+  p = (int *)malloc(5*sizeof(int));
+  for (i = 0; i < 5; i++) {
+    p[i] = i+10;
+  }
+  return p;
+}
+*/
+import "C"
+import (
+       "fmt"
+       "unsafe"
+)
+
+func main() {
+       a := C.f()
+       q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5))
+       // Access to C pointer out of bounds.
+       *q5 = 100
+       // We shouldn't get here; asan should stop us first.
+       fmt.Printf("q5: %d, %x\n", *q5, q5)
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan3_fail.go b/misc/cgo/testsanitizers/testdata/asan3_fail.go
new file mode 100644 (file)
index 0000000..9f6d26d
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2021 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
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+void test(int *a) {
+       // Access Go pointer out of bounds.
+       int c = a[5];        // BOOM
+       // We shouldn't get here; asan should stop us first.
+       printf("a[5]=%d\n", c);
+}
+*/
+import "C"
+
+func main() {
+       cIntSlice := []C.int{200, 201, 203, 203, 204}
+       C.test(&cIntSlice[0])
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan4_fail.go b/misc/cgo/testsanitizers/testdata/asan4_fail.go
new file mode 100644 (file)
index 0000000..1209845
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2021 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
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+void test(int* a) {
+       // Access Go pointer out of bounds.
+       a[3] = 300;          // BOOM
+       // We shouldn't get here; asan should stop us first.
+       printf("a[3]=%d\n", a[3]);
+}*/
+import "C"
+
+func main() {
+       var cIntArray [2]C.int
+       C.test(&cIntArray[0]) // cIntArray is moved to heap.
+}
diff --git a/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go b/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go
new file mode 100644 (file)
index 0000000..3d3d5a6
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2021 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
+
+// The -fsanitize=address option of C compier can detect stack-use-after-return bugs.
+// In the following program, the local variable 'local' was moved to heap by the Go
+// compiler because foo() is returning the reference to 'local', and return stack of
+// foo() will be invalid. Thus for main() to use the reference to 'local', the 'local'
+// must be available even after foo() has finished. Therefore, Go has no such issue.
+
+import "fmt"
+
+var ptr *int
+
+func main() {
+       foo()
+       fmt.Printf("ptr=%x, %v", *ptr, ptr)
+}
+
+func foo() {
+       var local int
+       local = 1
+       ptr = &local // local is moved to heap.
+}
index 4fa5aa495eeb547698f8fe74fe724861f7165e49..de2a3fd140d43c04b7a7058f5132c76913c2201b 100644 (file)
@@ -36,6 +36,7 @@ func MSanSupported(goos, goarch string) bool {
 
 // ASanSupported reports whether goos/goarch supports the address
 // sanitizer option.
+// There is a copy of this function in misc/cgo/testsanitizers/cc_test.go.
 func ASanSupported(goos, goarch string) bool {
        switch goos {
        case "linux":