]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: in asan mode call __lsan_do_leak_check when exiting
authorIan Lance Taylor <iant@golang.org>
Sat, 22 Feb 2025 01:13:20 +0000 (17:13 -0800)
committerGopher Robot <gobot@golang.org>
Thu, 6 Mar 2025 02:23:46 +0000 (18:23 -0800)
This enables the ASAN default behavior of reporting C memory leaks.
It can be disabled with ASAN_OPTIONS=detect_leaks=0.

Fixes #67833

Change-Id: I420da1b5d79cf70d8cf134eaf97bf0a22f61ffd0
Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-asan-clang15,gotip-linux-arm64-asan-clang15
Reviewed-on: https://go-review.googlesource.com/c/go/+/651755
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

20 files changed:
doc/next/3-tools.md
src/cmd/cgo/internal/test/issue4029.go
src/cmd/cgo/internal/test/test.go
src/cmd/cgo/internal/testsanitizers/cc_test.go
src/cmd/cgo/internal/testsanitizers/lsan_test.go [new file with mode: 0644]
src/cmd/cgo/internal/testsanitizers/testdata/lsan1.go [new file with mode: 0644]
src/cmd/cgo/internal/testsanitizers/testdata/lsan2.go [new file with mode: 0644]
src/cmd/cgo/internal/testsanitizers/testdata/lsan3.go [new file with mode: 0644]
src/runtime/asan.go
src/runtime/asan/asan.go
src/runtime/asan0.go
src/runtime/asan_amd64.s
src/runtime/asan_arm64.s
src/runtime/asan_loong64.s
src/runtime/asan_ppc64le.s
src/runtime/asan_riscv64.s
src/runtime/mem.go
src/runtime/proc.go
src/runtime/vdso_test.go
src/syscall/exec_linux_test.go

index 5638f240a5b12744564781b99e1ce4878df6ca41..5b52fe200df088d5a666215c551a7ac54d2f4a62 100644 (file)
@@ -2,5 +2,13 @@
 
 ### Go command {#go-command}
 
+The `go build` `-asan` option now defaults to doing leak detection at
+program exit.
+This will report an error if memory allocated by C is not freed and is
+not referenced by any other memory allocated by either C or Go.
+These new error reports may be disabled by setting
+`ASAN_OPTIONS=detect_leaks=0` in the environment when running the
+program.
+
 ### Cgo {#cgo}
 
index 506c999bdb4984627e752beaf87d1bd12bfd6594..702af3f531ec2591f50c6a8b53e8c06aea924a16 100644 (file)
@@ -11,6 +11,7 @@ package cgotest
 
 /*
 #include <stdint.h>
+#include <stdlib.h>
 #include <dlfcn.h>
 #cgo linux LDFLAGS: -ldl
 
@@ -24,6 +25,7 @@ import "C"
 
 import (
        "testing"
+       "unsafe"
 )
 
 var callbacks int
@@ -66,7 +68,9 @@ func loadThySelf(t *testing.T, symbol string) {
        }
        defer C.dlclose4029(this_process)
 
-       symbol_address := C.dlsym4029(this_process, C.CString(symbol))
+       symCStr := C.CString(symbol)
+       defer C.free(unsafe.Pointer(symCStr))
+       symbol_address := C.dlsym4029(this_process, symCStr)
        if symbol_address == 0 {
                t.Error("dlsym:", C.GoString(C.dlerror()))
                return
index fcac076225324735c6c75d22175aa590610655cb..844b2dd42c8cbf1a14bbe9c1db1bf8ce5772cd74 100644 (file)
@@ -1098,6 +1098,7 @@ func testErrno(t *testing.T) {
 func testMultipleAssign(t *testing.T) {
        p := C.CString("234")
        n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10)
+       defer C.free(unsafe.Pointer(p))
        if runtime.GOOS == "openbsd" {
                // Bug in OpenBSD strtol(3) - base > 36 succeeds.
                if (n != 0 && n != 239089) || m != 234 {
@@ -1106,7 +1107,6 @@ func testMultipleAssign(t *testing.T) {
        } else if n != 0 || m != 234 {
                t.Fatal("Strtol x2: ", n, m)
        }
-       C.free(unsafe.Pointer(p))
 }
 
 var (
@@ -1632,7 +1632,9 @@ func testNaming(t *testing.T) {
 
 func test6907(t *testing.T) {
        want := "yarn"
-       if got := C.GoString(C.Issue6907CopyString(want)); got != want {
+       s := C.Issue6907CopyString(want)
+       defer C.free(unsafe.Pointer(s))
+       if got := C.GoString(s); got != want {
                t.Errorf("C.GoString(C.Issue6907CopyString(%q)) == %q, want %q", want, got, want)
        }
 }
@@ -1881,6 +1883,7 @@ func test17537(t *testing.T) {
        }
 
        p := (*C.char)(C.malloc(1))
+       defer C.free(unsafe.Pointer(p))
        *p = 17
        if got, want := C.F17537(&p), C.int(17); got != want {
                t.Errorf("got %d, want %d", got, want)
index 96a9e71cd7921372626486d97fcbfa5534217a2f..193d24d52cfd59eda28558f46f122c9ff221ee39 100644 (file)
@@ -328,6 +328,12 @@ func compilerRequiredAsanVersion(goos, goarch string) bool {
        }
 }
 
+// compilerRequiredLsanVersion reports whether the compiler is the
+// version required by Lsan.
+func compilerRequiredLsanVersion(goos, goarch string) bool {
+       return compilerRequiredAsanVersion(goos, goarch)
+}
+
 type compilerCheck struct {
        once sync.Once
        err  error
@@ -377,7 +383,7 @@ func configure(sanitizer string) *config {
                        c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan")
                }
 
-       case "address":
+       case "address", "leak":
                c.goFlags = append(c.goFlags, "-asan")
                // Set the debug mode to print the C stack trace.
                c.cFlags = append(c.cFlags, "-g")
diff --git a/src/cmd/cgo/internal/testsanitizers/lsan_test.go b/src/cmd/cgo/internal/testsanitizers/lsan_test.go
new file mode 100644 (file)
index 0000000..4dde3d2
--- /dev/null
@@ -0,0 +1,102 @@
+// Copyright 2025 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 linux || (freebsd && amd64)
+
+package sanitizers_test
+
+import (
+       "internal/platform"
+       "internal/testenv"
+       "strings"
+       "testing"
+)
+
+func TestLSAN(t *testing.T) {
+       config := mustHaveLSAN(t)
+
+       t.Parallel()
+       mustRun(t, config.goCmd("build", "std"))
+
+       cases := []struct {
+               src           string
+               leakError     string
+               errorLocation string
+       }{
+               {src: "lsan1.go", leakError: "detected memory leaks", errorLocation: "lsan1.go:11"},
+               {src: "lsan2.go"},
+               {src: "lsan3.go"},
+       }
+       for _, tc := range cases {
+               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.leakError == "" {
+                               mustRun(t, cmd)
+                       } else {
+                               outb, err := cmd.CombinedOutput()
+                               out := string(outb)
+                               if err != nil || len(out) > 0 {
+                                       t.Logf("%s\n%v\n%s", cmd, err, out)
+                               }
+                               if err != nil && strings.Contains(out, tc.leakError) {
+                                       // This string is output if the
+                                       // sanitizer library needs a
+                                       // symbolizer program and can't find it.
+                                       const noSymbolizer = "external symbolizer"
+                                       if tc.errorLocation != "" &&
+                                               !strings.Contains(out, tc.errorLocation) &&
+                                               !strings.Contains(out, noSymbolizer) &&
+                                               compilerSupportsLocation() {
+
+                                               t.Errorf("output does not contain expected location of the error %q", tc.errorLocation)
+                                       }
+                               } else {
+                                       t.Errorf("output does not contain expected leak error %q", tc.leakError)
+                               }
+
+                               // Make sure we can disable the leak check.
+                               cmd = hangProneCmd(outPath)
+                               replaceEnv(cmd, "ASAN_OPTIONS", "detect_leaks=0")
+                               mustRun(t, cmd)
+                       }
+               })
+       }
+}
+
+func mustHaveLSAN(t *testing.T) *config {
+       testenv.MustHaveGoBuild(t)
+       testenv.MustHaveCGO(t)
+       goos, err := goEnv("GOOS")
+       if err != nil {
+               t.Fatal(err)
+       }
+       goarch, err := goEnv("GOARCH")
+       if err != nil {
+               t.Fatal(err)
+       }
+       // LSAN is a subset of ASAN, so just check for ASAN support.
+       if !platform.ASanSupported(goos, goarch) {
+               t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch)
+       }
+
+       if !compilerRequiredLsanVersion(goos, goarch) {
+               t.Skipf("skipping on %s/%s: too old version of compiler", goos, goarch)
+       }
+
+       requireOvercommit(t)
+
+       config := configure("leak")
+       config.skipIfCSanitizerBroken(t)
+
+       return config
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/lsan1.go b/src/cmd/cgo/internal/testsanitizers/testdata/lsan1.go
new file mode 100644 (file)
index 0000000..5f99bd4
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright 2025 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>
+
+int* test() {
+  return malloc(sizeof(int));
+}
+
+void clearStack(int n) {
+  if (n > 0) {
+    clearStack(n - 1);
+  }
+}
+
+*/
+import "C"
+
+//go:noinline
+func F() {
+       C.test()
+}
+
+func clearStack(n int) {
+       if n > 0 {
+               clearStack(n - 1)
+       }
+}
+
+func main() {
+       // Test should fail: memory allocated by C is leaked.
+       F()
+       clearStack(100)
+       C.clearStack(100)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/lsan2.go b/src/cmd/cgo/internal/testsanitizers/testdata/lsan2.go
new file mode 100644 (file)
index 0000000..b904d13
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright 2025 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>
+
+int* test() {
+   return malloc(sizeof(int));
+}
+
+void clearStack(int n) {
+  if (n > 0) {
+    clearStack(n - 1);
+  }
+}
+
+*/
+import "C"
+
+var p *C.int
+
+//go:noinline
+func F() {
+       p = C.test()
+}
+
+func clearStack(n int) {
+       if n > 0 {
+               clearStack(n - 1)
+       }
+}
+
+func main() {
+       // Test should pass: memory allocated by C does not leak
+       // because a Go global variable points to it.
+       F()
+       clearStack(100)
+       C.clearStack(100)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/lsan3.go b/src/cmd/cgo/internal/testsanitizers/testdata/lsan3.go
new file mode 100644 (file)
index 0000000..824e153
--- /dev/null
@@ -0,0 +1,46 @@
+// Copyright 2025 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>
+
+int* test() {
+   return malloc(sizeof(int));
+}
+
+void clearStack(int n) {
+  if (n > 0) {
+    clearStack(n - 1);
+  }
+}
+
+*/
+import "C"
+
+type S struct {
+       p *C.int
+}
+
+var p *S
+
+//go:noinline
+func F() {
+       p = &S{p: C.test()}
+}
+
+func clearStack(n int) {
+       if n > 0 {
+               clearStack(n - 1)
+       }
+}
+
+func main() {
+       // Test should pass: memory allocated by C does not leak
+       // because a Go global variable points to it.
+       F()
+       clearStack(100)
+       C.clearStack(100)
+}
index 6fb1d00c3c9cb182649a7b2cab1ddff4fbc7d49e..adef8fa7bf6f15aefb71d5d7940871475ae66468 100644 (file)
@@ -61,6 +61,11 @@ func asanpoison(addr unsafe.Pointer, sz uintptr)
 //go:noescape
 func asanregisterglobals(addr unsafe.Pointer, n uintptr)
 
+//go:noescape
+func lsanregisterrootregion(addr unsafe.Pointer, n uintptr)
+
+func lsandoleakcheck()
+
 // These are called from asan_GOARCH.s
 //
 //go:cgo_import_static __asan_read_go
@@ -68,3 +73,5 @@ func asanregisterglobals(addr unsafe.Pointer, n uintptr)
 //go:cgo_import_static __asan_unpoison_go
 //go:cgo_import_static __asan_poison_go
 //go:cgo_import_static __asan_register_globals_go
+//go:cgo_import_static __lsan_register_root_region_go
+//go:cgo_import_static __lsan_do_leak_check_go
index ef70b0145b7f88c3446136a56877f7bffab17724..efdd911f2b10c47c4d61eeed2ebfc20156153cb2 100644 (file)
@@ -13,6 +13,7 @@ package asan
 #include <stdbool.h>
 #include <stdint.h>
 #include <sanitizer/asan_interface.h>
+#include <sanitizer/lsan_interface.h>
 
 void __asan_read_go(void *addr, uintptr_t sz, void *sp, void *pc) {
        if (__asan_region_is_poisoned(addr, sz)) {
@@ -34,6 +35,14 @@ void __asan_poison_go(void *addr, uintptr_t sz) {
        __asan_poison_memory_region(addr, sz);
 }
 
+void __lsan_register_root_region_go(void *addr, uintptr_t sz) {
+       __lsan_register_root_region(addr, sz);
+}
+
+void __lsan_do_leak_check_go(void) {
+       __lsan_do_leak_check();
+}
+
 // Keep in sync with the definition in compiler-rt
 // https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/asan/asan_interface_internal.h#L41
 // This structure is used to describe the source location of
index bcfd96f1ab9160fc91dc90b673a1b66d2e511d13..eb70367a2997a749dd57e2dc903b19d1f54241d0 100644 (file)
@@ -21,3 +21,5 @@ 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") }
 func asanregisterglobals(addr unsafe.Pointer, sz uintptr) { throw("asan") }
+func lsanregisterrootregion(unsafe.Pointer, uintptr)      { throw("asan") }
+func lsandoleakcheck()                                    { throw("asan") }
index 195faf4e6da1b4cc7bece42955ecd3244ff9d800..3f9df4fec8b657536f022a977dce33920893b436 100644 (file)
@@ -69,6 +69,20 @@ TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16
        MOVQ    $__asan_register_globals_go(SB), AX
        JMP     asancall<>(SB)
 
+// func runtime·lsanregisterrootregion(addr unsafe.Pointer, n uintptr)
+TEXT   runtime·lsanregisterrootregion(SB), NOSPLIT, $0-16
+       MOVQ    addr+0(FP), RARG0
+       MOVQ    n+8(FP), RARG1
+       // void __lsan_register_root_region_go(void *addr, uintptr_t sz)
+       MOVQ    $__lsan_register_root_region_go(SB), AX
+       JMP     asancall<>(SB)
+
+// func runtime·lsandoleakcheck()
+TEXT   runtime·lsandoleakcheck(SB), NOSPLIT, $0-0
+       // void __lsan_do_leak_check_go(void);
+       MOVQ    $__lsan_do_leak_check_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)
index dfa3f81bf23f8e6827bc55d49188b68d8ad6a0e3..5447d210e56adcc1a7cfa0f33f829c7ac81994c8 100644 (file)
@@ -58,6 +58,20 @@ TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16
        MOVD    $__asan_register_globals_go(SB), FARG
        JMP     asancall<>(SB)
 
+// func runtime·lsanregisterrootregion(addr unsafe.Pointer, n uintptr)
+TEXT   runtime·lsanregisterrootregion(SB), NOSPLIT, $0-16
+       MOVD    addr+0(FP), RARG0
+       MOVD    n+8(FP), RARG1
+       // void __lsan_register_root_region_go(void *addr, uintptr_t n);
+       MOVD    $__lsan_register_root_region_go(SB), FARG
+       JMP     asancall<>(SB)
+
+// func runtime·lsandoleakcheck()
+TEXT   runtime·lsandoleakcheck(SB), NOSPLIT, $0-0
+       // void __lsan_do_leak_check_go(void);
+       MOVD    $__lsan_do_leak_check_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
index 0034a316876ee39787e213d1efc5935eb238ba4a..3abcf889b8eae7123f44be922de42b0e99e20f49 100644 (file)
@@ -58,6 +58,20 @@ TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16
        MOVV    $__asan_register_globals_go(SB), FARG
        JMP     asancall<>(SB)
 
+// func runtime·lsanregisterrootregion(addr unsafe.Pointer, n uintptr)
+TEXT   runtime·lsanregisterrootregion(SB), NOSPLIT, $0-16
+       MOVV    addr+0(FP), RARG0
+       MOVV    n+8(FP), RARG1
+       // void __lsan_register_root_region_go(void *addr, uintptr_t n);
+       MOVV    $__lsan_register_root_region_go(SB), FARG
+       JMP     asancall<>(SB)
+
+// func runtime·lsandoleakcheck()
+TEXT   runtime·lsandoleakcheck(SB), NOSPLIT, $0-0
+       // void __lsan_do_leak_check_go(void);
+       MOVV    $__lsan_do_leak_check_go(SB), FARG
+       JMP     asancall<>(SB)
+
 // Switches SP to g0 stack and calls (FARG). Arguments already set.
 TEXT   asancall<>(SB), NOSPLIT, $0-0
        MOVV    R3, R23         // callee-saved
index d13301a1b1d249a51863120adb398b9a97ec942c..2fc5772a28cd5e15fa834748f9f67448cffc2b94 100644 (file)
@@ -58,6 +58,20 @@ TEXT runtime·asanregisterglobals(SB),NOSPLIT|NOFRAME,$0-16
        MOVD    $__asan_register_globals_go(SB), FARG
        BR      asancall<>(SB)
 
+// func runtime·lsanregisterrootregion(addr unsafe.Pointer, n uintptr)
+TEXT   runtime·lsanregisterrootregion(SB),NOSPLIT|NOFRAME,$0-16
+       MOVD    addr+0(FP), RARG0
+       MOVD    n+8(FP), RARG1
+       // void __lsan_register_root_region_go(void *addr, uintptr_t n);
+       MOVD    $__lsan_register_root_region_go(SB), FARG
+       BR      asancall<>(SB)
+
+// func runtime·lsandoleakcheck()
+TEXT   runtime·lsandoleakcheck(SB), NOSPLIT|NOFRAME, $0-0
+       // void __lsan_do_leak_check_go(void);
+       MOVD    $__lsan_do_leak_check_go(SB), FARG
+       BR      asancall<>(SB)
+
 // Switches SP to g0 stack and calls (FARG). Arguments already set.
 TEXT   asancall<>(SB), NOSPLIT, $0-0
        // LR saved in generated prologue
index 6fcd94d4b1fcf083eccb8ad405c010601a69345d..f5ddb21a25cbd53c5c88ba640fe203617ba81702 100644 (file)
@@ -52,6 +52,20 @@ TEXT runtime·asanregisterglobals(SB), NOSPLIT, $0-16
        MOV     $__asan_register_globals_go(SB), X14
        JMP     asancall<>(SB)
 
+// func runtime·lsanregisterrootregion(addr unsafe.Pointer, n uintptr)
+TEXT   runtime·lsanregisterrootregion(SB), NOSPLIT, $0-16
+       MOV     addr+0(FP), X10
+       MOV     n+8(FP), X11
+       // void __lsan_register_root_region_go(void *addr, uintptr_t n);
+       MOV     $__lsan_register_root_region_go(SB), X14
+       JMP     asancall<>(SB)
+
+// func runtime·lsandoleakcheck()
+TEXT   runtime·lsandoleakcheck(SB), NOSPLIT, $0-0
+       // void __lsan_do_leak_check_go(void);
+       MOV     $__lsan_do_leak_check_go(SB), X14
+       JMP     asancall<>(SB)
+
 // Switches SP to g0 stack and calls (X14). Arguments already set.
 TEXT   asancall<>(SB), NOSPLIT, $0-0
        MOV     X2, X8          // callee-saved
index 6bb91b371a34ac6af4c3fc27e5471e26aafaf7ea..d45a0ccfb864a1e401b708e3600f2e657b7217b6 100644 (file)
@@ -49,7 +49,15 @@ import "unsafe"
 func sysAlloc(n uintptr, sysStat *sysMemStat, vmaName string) unsafe.Pointer {
        sysStat.add(int64(n))
        gcController.mappedReady.Add(int64(n))
-       return sysAllocOS(n, vmaName)
+       p := sysAllocOS(n, vmaName)
+
+       // When using ASAN leak detection, we must tell ASAN about
+       // cases where we store pointers in mmapped memory.
+       if asanenabled {
+               lsanregisterrootregion(p, n)
+       }
+
+       return p
 }
 
 // sysUnused transitions a memory region from Ready to Prepared. It notifies the
@@ -143,7 +151,15 @@ func sysFault(v unsafe.Pointer, n uintptr) {
 // may use larger alignment, so the caller must be careful to realign the
 // memory obtained by sysReserve.
 func sysReserve(v unsafe.Pointer, n uintptr, vmaName string) unsafe.Pointer {
-       return sysReserveOS(v, n, vmaName)
+       p := sysReserveOS(v, n, vmaName)
+
+       // When using ASAN leak detection, we must tell ASAN about
+       // cases where we store pointers in mmapped memory.
+       if asanenabled {
+               lsanregisterrootregion(p, n)
+       }
+
+       return p
 }
 
 // sysMap transitions a memory region from Reserved to Prepared. It ensures the
index c467f4c49de9ef1a3e111d44499a16446577ebe6..74c19e9e435bf8cf4666656f4718fd8486f46769 100644 (file)
@@ -281,11 +281,27 @@ func main() {
        }
        fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
        fn()
+
+       exitHooksRun := false
        if raceenabled {
                runExitHooks(0) // run hooks now, since racefini does not return
+               exitHooksRun = true
                racefini()
        }
 
+       // Check for C memory leaks if using ASAN and we've made cgo calls,
+       // or if we are running as a library in a C program.
+       // We always make one cgo call, above, to notify_runtime_init_done,
+       // so we ignore that one.
+       // No point in leak checking if no cgo calls, since leak checking
+       // just looks for objects allocated using malloc and friends.
+       // Just checking iscgo doesn't help because asan implies iscgo.
+       if asanenabled && (isarchive || islibrary || NumCgoCall() > 1) {
+               runExitHooks(0) // lsandoleakcheck may not return
+               exitHooksRun = true
+               lsandoleakcheck()
+       }
+
        // Make racy client program work: if panicking on
        // another goroutine at the same time as main returns,
        // let the other goroutine finish printing the panic trace.
@@ -302,7 +318,9 @@ func main() {
        if panicking.Load() != 0 {
                gopark(nil, nil, waitReasonPanicWait, traceBlockForever, 1)
        }
-       runExitHooks(0)
+       if !exitHooksRun {
+               runExitHooks(0)
+       }
 
        exit(0)
        for {
@@ -319,6 +337,11 @@ func os_beforeExit(exitCode int) {
        if exitCode == 0 && raceenabled {
                racefini()
        }
+
+       // See comment in main, above.
+       if exitCode == 0 && asanenabled && (isarchive || islibrary || NumCgoCall() > 1) {
+               lsandoleakcheck()
+       }
 }
 
 func init() {
index d025ba50c212acaab35037916c28c8fd9a8d7ff1..b0f5fbe72882a808733b52995affe3ef25f7bb35 100644 (file)
@@ -8,6 +8,7 @@ package runtime_test
 
 import (
        "bytes"
+       "internal/asan"
        "internal/testenv"
        "os"
        "os/exec"
@@ -20,6 +21,10 @@ import (
 // TestUsingVDSO tests that we are actually using the VDSO to fetch
 // the time.
 func TestUsingVDSO(t *testing.T) {
+       if asan.Enabled {
+               t.Skip("test fails with ASAN beause the ASAN leak checker won't run under strace")
+       }
+
        const calls = 100
 
        if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
index 04973dc9ad306b63b5bf7df00ba9588b8a335713..69d49169446d1ccf2dbdead3a282f36a8f02b3f9 100644 (file)
@@ -11,6 +11,7 @@ import (
        "errors"
        "flag"
        "fmt"
+       "internal/asan"
        "internal/platform"
        "internal/syscall/unix"
        "internal/testenv"
@@ -334,6 +335,10 @@ func TestUnshareMountNameSpaceChroot(t *testing.T) {
 
 // Test for Issue 29789: unshare fails when uid/gid mapping is specified
 func TestUnshareUidGidMapping(t *testing.T) {
+       if asan.Enabled {
+               t.Skip("test fails with ASAN beause the ASAN leak checker fails finding memory regions")
+       }
+
        if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
                defer os.Exit(0)
                if err := syscall.Chroot(os.TempDir()); err != nil {