mirror of https://github.com/golang/go.git
runtime/cgo: add TSAN acquire/release calls
Add TSAN acquire/release calls to runtime/cgo to match the ones generated by cgo. This avoids a false positive race around the malloc memory used in runtime/cgo when other goroutines are simultaneously calling malloc and free from cgo. These new calls will only be used when building with CGO_CFLAGS and CGO_LDFLAGS set to -fsanitize=thread, which becomes a requirement to avoid all false positives when using TSAN. These are needed not just for runtime/cgo, but also for any runtime package that uses cgo (such as net and os/user). Add an unused attribute to the _cgo_tsan_acquire and _cgo_tsan_release functions, in case there are no actual cgo function calls. Add a test that checks that setting CGO_CFLAGS/CGO_LDFLAGS avoids a false positive report when using os/user. Change-Id: I0905c644ff7f003b6718aac782393fa219514c48 Reviewed-on: https://go-review.googlesource.com/23492 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
This commit is contained in:
parent
0e13dbc1a9
commit
66736880ca
|
|
@ -154,6 +154,17 @@ if test "$tsan" = "yes"; then
|
||||||
status=1
|
status=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# This test requires rebuilding os/user with -fsanitize=thread.
|
||||||
|
if ! CGO_CFLAGS="-fsanitize=thread" CGO_LDFLAGS="-fsanitize=thread" go run -installsuffix=tsan tsan5.go 2>$err; then
|
||||||
|
cat $err
|
||||||
|
echo "FAIL: tsan5"
|
||||||
|
status=1
|
||||||
|
elif grep -i warning $err >/dev/null 2>&1; then
|
||||||
|
cat $err
|
||||||
|
echo "FAIL: tsan5"
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
|
||||||
rm -f $err
|
rm -f $err
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright 2016 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
|
||||||
|
|
||||||
|
// Check that calls to C.malloc/C.free do not collide with the calls
|
||||||
|
// made by the os/user package.
|
||||||
|
|
||||||
|
// #cgo CFLAGS: -fsanitize=thread
|
||||||
|
// #cgo LDFLAGS: -fsanitize=thread
|
||||||
|
// #include <stdlib.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
u, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
// Let the test pass.
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
wg.Add(2)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
user.Lookup(u.Username)
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
p := C.malloc(C.size_t(len(u.Username) + 1))
|
||||||
|
runtime.Gosched()
|
||||||
|
C.free(p)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
@ -1324,6 +1324,7 @@ const noTsanProlog = `
|
||||||
#define _cgo_tsan_release()
|
#define _cgo_tsan_release()
|
||||||
`
|
`
|
||||||
|
|
||||||
|
// This must match the TSAN code in runtime/cgo/libcgo.h.
|
||||||
const yesTsanProlog = `
|
const yesTsanProlog = `
|
||||||
#define CGO_NO_SANITIZE_THREAD __attribute__ ((no_sanitize_thread))
|
#define CGO_NO_SANITIZE_THREAD __attribute__ ((no_sanitize_thread))
|
||||||
|
|
||||||
|
|
@ -1332,10 +1333,12 @@ long long _cgo_sync __attribute__ ((common));
|
||||||
extern void __tsan_acquire(void*);
|
extern void __tsan_acquire(void*);
|
||||||
extern void __tsan_release(void*);
|
extern void __tsan_release(void*);
|
||||||
|
|
||||||
|
__attribute__ ((unused))
|
||||||
static void _cgo_tsan_acquire() {
|
static void _cgo_tsan_acquire() {
|
||||||
__tsan_acquire(&_cgo_sync);
|
__tsan_acquire(&_cgo_sync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__attribute__ ((unused))
|
||||||
static void _cgo_tsan_release() {
|
static void _cgo_tsan_release() {
|
||||||
__tsan_release(&_cgo_sync);
|
__tsan_release(&_cgo_sync);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,9 @@ threadentry(void *v)
|
||||||
ThreadStart ts;
|
ThreadStart ts;
|
||||||
|
|
||||||
ts = *(ThreadStart*)v;
|
ts = *(ThreadStart*)v;
|
||||||
|
_cgo_tsan_acquire();
|
||||||
free(v);
|
free(v);
|
||||||
|
_cgo_tsan_release();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set specific keys.
|
* Set specific keys.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ x_cgo_thread_start(ThreadStart *arg)
|
||||||
ThreadStart *ts;
|
ThreadStart *ts;
|
||||||
|
|
||||||
/* Make our own copy that can persist after we return. */
|
/* Make our own copy that can persist after we return. */
|
||||||
|
_cgo_tsan_acquire();
|
||||||
ts = malloc(sizeof *ts);
|
ts = malloc(sizeof *ts);
|
||||||
|
_cgo_tsan_release();
|
||||||
if(ts == nil) {
|
if(ts == nil) {
|
||||||
fprintf(stderr, "runtime/cgo: out of memory in thread_start\n");
|
fprintf(stderr, "runtime/cgo: out of memory in thread_start\n");
|
||||||
abort();
|
abort();
|
||||||
|
|
|
||||||
|
|
@ -94,3 +94,42 @@ struct context_arg {
|
||||||
uintptr_t Context;
|
uintptr_t Context;
|
||||||
};
|
};
|
||||||
extern void (*x_cgo_context_function)(struct context_arg*);
|
extern void (*x_cgo_context_function)(struct context_arg*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TSAN support. This is only useful when building with
|
||||||
|
* CGO_CFLAGS="-fsanitize=thread" CGO_LDFLAGS="-fsanitize=thread" go install
|
||||||
|
*/
|
||||||
|
#undef CGO_TSAN
|
||||||
|
#if defined(__has_feature)
|
||||||
|
# if __has_feature(thread_sanitizer)
|
||||||
|
# define CGO_TSAN
|
||||||
|
# endif
|
||||||
|
#elif defined(__SANITIZE_THREAD__)
|
||||||
|
# define CGO_TSAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CGO_TSAN
|
||||||
|
|
||||||
|
// These must match the definitions in yesTsanProlog in cmd/cgo/out.go.
|
||||||
|
|
||||||
|
long long _cgo_sync __attribute__ ((common));
|
||||||
|
|
||||||
|
extern void __tsan_acquire(void*);
|
||||||
|
extern void __tsan_release(void*);
|
||||||
|
|
||||||
|
__attribute__ ((unused))
|
||||||
|
static void _cgo_tsan_acquire() {
|
||||||
|
__tsan_acquire(&_cgo_sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ ((unused))
|
||||||
|
static void _cgo_tsan_release() {
|
||||||
|
__tsan_release(&_cgo_sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !defined(CGO_TSAN)
|
||||||
|
|
||||||
|
#define _cgo_tsan_acquire()
|
||||||
|
#define _cgo_tsan_release()
|
||||||
|
|
||||||
|
#endif // !defined(CGO_TSAN)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue