aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/race.go
diff options
context:
space:
mode:
authorJames Chacon <jchacon@google.com>2016-05-06 11:51:56 -0700
committerRuss Cox <rsc@golang.org>2016-05-18 01:04:55 +0000
commit733162fd6c0df8bd700859974957b25045fe9ee4 (patch)
treeca6e940629076e93138d9217d922fbfb4fe72a27 /src/runtime/race.go
parentf744717d1924340b8f5e5a385e99078693ad9097 (diff)
downloadgo-733162fd6c0df8bd700859974957b25045fe9ee4.tar.gz
go-733162fd6c0df8bd700859974957b25045fe9ee4.zip
runtime: prevent racefini from being invoked more than once
racefini calls __tsan_fini which is C code and at the end of it invoked the standard C library exit(3) call. This has undefined behavior if invoked more than once. Specifically in C++ programs it caused static destructors to run twice. At least on glibc impls it also means the at_exit handlers list (where those are stored) also free's a list entry when it completes these. So invoking twice results in a double free at exit which trips debug memory allocation tracking. Fix all of this by using an atomic as a boolean barrier around calls to racefini being invoked > 1 time. Fixes #15578 Change-Id: I49222aa9b8ded77160931f46434c61a8379570fc Reviewed-on: https://go-review.googlesource.com/22882 Reviewed-by: Dmitry Vyukov <dvyukov@google.com> Run-TryBot: Dmitry Vyukov <dvyukov@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/runtime/race.go')
-rw-r--r--src/runtime/race.go8
1 files changed, 8 insertions, 0 deletions
diff --git a/src/runtime/race.go b/src/runtime/race.go
index ecd68d80ce..42da936ddb 100644
--- a/src/runtime/race.go
+++ b/src/runtime/race.go
@@ -283,8 +283,16 @@ func raceinit() (gctx, pctx uintptr) {
return
}
+var raceFiniLock mutex
+
//go:nosplit
func racefini() {
+ // racefini() can only be called once to avoid races.
+ // This eventually (via __tsan_fini) calls C.exit which has
+ // undefined behavior if called more than once. If the lock is
+ // already held it's assumed that the first caller exits the program
+ // so other calls can hang forever without an issue.
+ lock(&raceFiniLock)
racecall(&__tsan_fini, 0, 0, 0, 0)
}