diff --git a/misc/cgo/testsanitizers/testdata/tsan13.go b/misc/cgo/testsanitizers/testdata/tsan13.go new file mode 100644 index 0000000000..ebdf63581b --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan13.go @@ -0,0 +1,90 @@ +// Copyright 2023 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 + +// This program failed when run under the C/C++ ThreadSanitizer. +// There was no TSAN synchronization for the call to the cgo +// traceback routine. + +/* +#cgo CFLAGS: -g -fsanitize=thread +#cgo LDFLAGS: -g -fsanitize=thread + +#include +#include +#include +#include +#include + +struct tracebackArg { + uintptr_t Context; + uintptr_t SigContext; + uintptr_t* Buf; + uintptr_t Max; +}; + +void tsanTraceback(struct tracebackArg *arg) { + arg->Buf[0] = 0; +} + +static void* spin(void *arg) { + size_t n; + struct timeval tvstart, tvnow; + int diff; + void *prev; + void *cur; + + prev = NULL; + gettimeofday(&tvstart, NULL); + for (n = 0; n < 1<<20; n++) { + cur = malloc(n); + free(prev); + prev = cur; + + gettimeofday(&tvnow, NULL); + diff = (tvnow.tv_sec - tvstart.tv_sec) * 1000 * 1000 + (tvnow.tv_usec - tvstart.tv_usec); + + // Profile frequency is 100Hz so we should definitely + // get some signals in 50 milliseconds. + if (diff > 50 * 1000) { + break; + } + } + + free(prev); + + return NULL; +} + +static void runThreads(int n) { + pthread_t ids[64]; + int i; + + if (n > 64) { + n = 64; + } + for (i = 0; i < n; i++) { + pthread_create(&ids[i], NULL, spin, NULL); + } + for (i = 0; i < n; i++) { + pthread_join(ids[i], NULL); + } +} +*/ +import "C" + +import ( + "io" + "runtime" + "runtime/pprof" + "unsafe" +) + +func main() { + runtime.SetCgoTraceback(0, unsafe.Pointer(C.tsanTraceback), nil, nil) + pprof.StartCPUProfile(io.Discard) + C.runThreads(C.int(runtime.GOMAXPROCS(0))) + pprof.StopCPUProfile() +} diff --git a/misc/cgo/testsanitizers/tsan_test.go b/misc/cgo/testsanitizers/tsan_test.go index 00ad313b9c..f65d842363 100644 --- a/misc/cgo/testsanitizers/tsan_test.go +++ b/misc/cgo/testsanitizers/tsan_test.go @@ -46,6 +46,7 @@ func TestTSAN(t *testing.T) { {src: "tsan10.go", needsRuntime: true}, {src: "tsan11.go", needsRuntime: true}, {src: "tsan12.go", needsRuntime: true}, + {src: "tsan13.go", needsRuntime: true}, } for _, tc := range cases { tc := tc @@ -63,6 +64,9 @@ func TestTSAN(t *testing.T) { if tc.needsRuntime { config.skipIfRuntimeIncompatible(t) } + // If we don't see halt_on_error, the program + // will only exit non-zero if we call C.exit. + cmd.Env = append(cmd.Environ(), "TSAN_OPTIONS=halt_on_error=1") mustRun(t, cmd) }) } diff --git a/src/runtime/cgo/gcc_traceback.c b/src/runtime/cgo/gcc_traceback.c index 053430047b..c6643a1e53 100644 --- a/src/runtime/cgo/gcc_traceback.c +++ b/src/runtime/cgo/gcc_traceback.c @@ -39,6 +39,8 @@ x_cgo_callers(uintptr_t sig, void *info, void *context, void (*cgoTraceback)(str __msan_unpoison(&arg, sizeof arg); #endif + _cgo_tsan_acquire(); (*cgoTraceback)(&arg); + _cgo_tsan_release(); sigtramp(sig, info, context); }