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