runtime, runtime/asan: add asan runtime support
These are the runtime support functions for letting Go code interoperate
with the C/C++ address sanitizer. Calls to asanread/asanwrite are now
inserted by the compiler with the -asan option. Calls to
asanunpoison/asanpoison will be from other runtime functions in a
subsequent CL.
Updates #44853.
Change-Id: I9e8fc0ce937828bc7f4a8b6637453ddc3862c47b
Reviewed-on: https://go-review.googlesource.com/c/go/+/298613
Trust: fannie zhang <Fannie.Zhang@arm.com>
Run-TryBot: fannie zhang <Fannie.Zhang@arm.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index a92bb38..64f0841 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -331,7 +331,7 @@
< C
< runtime/cgo
< CGO
- < runtime/race, runtime/msan;
+ < runtime/race, runtime/msan, runtime/asan;
# Bulk of the standard library must not use cgo.
# The prohibition stops at net and os/user.
diff --git a/src/runtime/asan.go b/src/runtime/asan.go
new file mode 100644
index 0000000..7ff5f26
--- /dev/null
+++ b/src/runtime/asan.go
@@ -0,0 +1,43 @@
+// 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.
+
+//go:build asan
+// +build asan
+
+package runtime
+
+import (
+ "unsafe"
+)
+
+// Public address sanitizer API.
+
+func ASanRead(addr unsafe.Pointer, len int) {
+ asanread(addr, uintptr(len))
+}
+
+func ASanWrite(addr unsafe.Pointer, len int) {
+ asanwrite(addr, uintptr(len))
+}
+
+// Private interface for the runtime.
+const asanenabled = true
+
+//go:noescape
+func asanread(addr unsafe.Pointer, sz uintptr)
+
+//go:noescape
+func asanwrite(addr unsafe.Pointer, sz uintptr)
+
+//go:noescape
+func asanunpoison(addr unsafe.Pointer, sz uintptr)
+
+//go:noescape
+func asanpoison(addr unsafe.Pointer, sz uintptr)
+
+// These are called from asan_GOARCH.s
+//go:cgo_import_static __asan_read_go
+//go:cgo_import_static __asan_write_go
+//go:cgo_import_static __asan_unpoison_go
+//go:cgo_import_static __asan_poison_go
diff --git a/src/runtime/asan/asan.go b/src/runtime/asan/asan.go
new file mode 100644
index 0000000..40ebf96
--- /dev/null
+++ b/src/runtime/asan/asan.go
@@ -0,0 +1,52 @@
+// 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.
+
+//go:build asan && linux && (arm64 || amd64)
+// +build asan
+// +build linux
+// +build arm64 amd64
+
+package asan
+
+/*
+#cgo CFLAGS: -fsanitize=address
+#cgo LDFLAGS: -fsanitize=address
+
+#include <stdint.h>
+#include <sanitizer/asan_interface.h>
+
+void __asan_read_go(void *addr, uintptr_t sz) {
+ if (__asan_region_is_poisoned(addr, sz)) {
+ switch (sz) {
+ case 1: __asan_report_load1(addr); break;
+ case 2: __asan_report_load2(addr); break;
+ case 4: __asan_report_load4(addr); break;
+ case 8: __asan_report_load8(addr); break;
+ default: __asan_report_load_n(addr, sz); break;
+ }
+ }
+}
+
+void __asan_write_go(void *addr, uintptr_t sz) {
+ if (__asan_region_is_poisoned(addr, sz)) {
+ switch (sz) {
+ case 1: __asan_report_store1(addr); break;
+ case 2: __asan_report_store2(addr); break;
+ case 4: __asan_report_store4(addr); break;
+ case 8: __asan_report_store8(addr); break;
+ default: __asan_report_store_n(addr, sz); break;
+ }
+ }
+}
+
+void __asan_unpoison_go(void *addr, uintptr_t sz) {
+ __asan_unpoison_memory_region(addr, sz);
+}
+
+void __asan_poison_go(void *addr, uintptr_t sz) {
+ __asan_poison_memory_region(addr, sz);
+}
+
+*/
+import "C"
diff --git a/src/runtime/asan0.go b/src/runtime/asan0.go
new file mode 100644
index 0000000..dad069b
--- /dev/null
+++ b/src/runtime/asan0.go
@@ -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.
+
+//go:build !asan
+// +build !asan
+
+// Dummy ASan support API, used when not built with -asan.
+
+package runtime
+
+import (
+ "unsafe"
+)
+
+const asanenabled = false
+
+// Because asanenabled is false, none of these functions should be called.
+
+func asanread(addr unsafe.Pointer, sz uintptr) { throw("asan") }
+func asanwrite(addr unsafe.Pointer, sz uintptr) { throw("asan") }
+func asanunpoison(addr unsafe.Pointer, sz uintptr) { throw("asan") }
+func asanpoison(addr unsafe.Pointer, sz uintptr) { throw("asan") }
diff --git a/src/runtime/asan_amd64.s b/src/runtime/asan_amd64.s
new file mode 100644
index 0000000..01bd612
--- /dev/null
+++ b/src/runtime/asan_amd64.s
@@ -0,0 +1,76 @@
+// 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.
+
+//go:build asan
+// +build asan
+
+#include "go_asm.h"
+#include "go_tls.h"
+#include "funcdata.h"
+#include "textflag.h"
+
+// This is like race_amd64.s, but for the asan calls.
+// See race_amd64.s for detailed comments.
+
+#ifdef GOOS_windows
+#define RARG0 CX
+#define RARG1 DX
+#else
+#define RARG0 DI
+#define RARG1 SI
+#endif
+
+// Called from intrumented code.
+// func runtime·asanread(addr unsafe.Pointer, sz uintptr)
+TEXT runtime·asanread(SB), NOSPLIT, $0-16
+ MOVQ addr+0(FP), RARG0
+ MOVQ size+8(FP), RARG1
+ // void __asan_read_go(void *addr, uintptr_t sz);
+ MOVQ $__asan_read_go(SB), AX
+ JMP asancall<>(SB)
+
+// func runtime·asanwrite(addr unsafe.Pointer, sz uintptr)
+TEXT runtime·asanwrite(SB), NOSPLIT, $0-16
+ MOVQ addr+0(FP), RARG0
+ MOVQ size+8(FP), RARG1
+ // void __asan_write_go(void *addr, uintptr_t sz);
+ MOVQ $__asan_write_go(SB), AX
+ JMP asancall<>(SB)
+
+// func runtime·asanunpoison(addr unsafe.Pointer, sz uintptr)
+TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16
+ MOVQ addr+0(FP), RARG0
+ MOVQ size+8(FP), RARG1
+ // void __asan_unpoison_go(void *addr, uintptr_t sz);
+ MOVQ $__asan_unpoison_go(SB), AX
+ JMP asancall<>(SB)
+
+// func runtime·asanpoison(addr unsafe.Pointer, sz uintptr)
+TEXT runtime·asanpoison(SB), NOSPLIT, $0-16
+ MOVQ addr+0(FP), RARG0
+ MOVQ size+8(FP), RARG1
+ // void __asan_poison_go(void *addr, uintptr_t sz);
+ MOVQ $__asan_poison_go(SB), AX
+ JMP asancall<>(SB)
+
+// Switches SP to g0 stack and calls (AX). Arguments already set.
+TEXT asancall<>(SB), NOSPLIT, $0-0
+ get_tls(R12)
+ MOVQ g(R12), R14
+ MOVQ SP, R12 // callee-saved, preserved across the CALL
+ CMPQ R14, $0
+ JE call // no g; still on a system stack
+
+ MOVQ g_m(R14), R13
+ // Switch to g0 stack.
+ MOVQ m_g0(R13), R10
+ CMPQ R10, R14
+ JE call // already on g0
+
+ MOVQ (g_sched+gobuf_sp)(R10), SP
+call:
+ ANDQ $~15, SP // alignment for gcc ABI
+ CALL AX
+ MOVQ R12, SP
+ RET
diff --git a/src/runtime/asan_arm64.s b/src/runtime/asan_arm64.s
new file mode 100644
index 0000000..eb0f9bd
--- /dev/null
+++ b/src/runtime/asan_arm64.s
@@ -0,0 +1,63 @@
+// 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.
+
+//go:build asan
+// +build asan
+
+#include "go_asm.h"
+#include "textflag.h"
+
+#define RARG0 R0
+#define RARG1 R1
+#define FARG R3
+
+// Called from instrumented code.
+// func runtime·asanread(addr unsafe.Pointer, sz uintptr)
+TEXT runtime·asanread(SB), NOSPLIT, $0-16
+ MOVD addr+0(FP), RARG0
+ MOVD size+8(FP), RARG1
+ // void __asan_read_go(void *addr, uintptr_t sz);
+ MOVD $__asan_read_go(SB), FARG
+ JMP asancall<>(SB)
+
+// func runtime·asanwrite(addr unsafe.Pointer, sz uintptr)
+TEXT runtime·asanwrite(SB), NOSPLIT, $0-16
+ MOVD addr+0(FP), RARG0
+ MOVD size+8(FP), RARG1
+ // void __asan_write_go(void *addr, uintptr_t sz);
+ MOVD $__asan_write_go(SB), FARG
+ JMP asancall<>(SB)
+
+// func runtime·asanunpoison(addr unsafe.Pointer, sz uintptr)
+TEXT runtime·asanunpoison(SB), NOSPLIT, $0-16
+ MOVD addr+0(FP), RARG0
+ MOVD size+8(FP), RARG1
+ // void __asan_unpoison_go(void *addr, uintptr_t sz);
+ MOVD $__asan_unpoison_go(SB), FARG
+ JMP asancall<>(SB)
+
+// func runtime·asanpoison(addr unsafe.Pointer, sz uintptr)
+TEXT runtime·asanpoison(SB), NOSPLIT, $0-16
+ MOVD addr+0(FP), RARG0
+ MOVD size+8(FP), RARG1
+ // void __asan_poison_go(void *addr, uintptr_t sz);
+ MOVD $__asan_poison_go(SB), FARG
+ JMP asancall<>(SB)
+
+// Switches SP to g0 stack and calls (FARG). Arguments already set.
+TEXT asancall<>(SB), NOSPLIT, $0-0
+ MOVD RSP, R19 // callee-saved
+ CBZ g, g0stack // no g, still on a system stack
+ MOVD g_m(g), R10
+ MOVD m_g0(R10), R11
+ CMP R11, g
+ BEQ g0stack
+
+ MOVD (g_sched+gobuf_sp)(R11), R4
+ MOVD R4, RSP
+
+g0stack:
+ BL (FARG)
+ MOVD R19, RSP
+ RET