Skip to content

Commit c8ef0df

Browse files
cmd/cgo: add hooks for thread sanitizer
When Go code is used with C code compiled with -fsanitize=thread, adds thread sanitizer calls so that correctly synchronized Go code does not cause spurious failure reports from the thread sanitizer. This may cause some false negatives, but for the thread sanitizer what is most important is avoiding false positives. Change-Id: If670e4a6f2874c7a2be2ff7db8728c6036340a52 Reviewed-on: https://go-review.googlesource.com/17421 Reviewed-by: Dmitry Vyukov <[email protected]>
1 parent c86dbbe commit c8ef0df

File tree

4 files changed

+228
-36
lines changed

4 files changed

+228
-36
lines changed

misc/cgo/testsanitizers/test.bash

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,75 +15,112 @@ if test -x "$(type -p clang)"; then
1515
fi
1616
export CC
1717

18+
msan=yes
19+
1820
TMPDIR=${TMPDIR:-/tmp}
1921
echo > ${TMPDIR}/testsanitizers$$.c
2022
if $CC -fsanitize=memory -c ${TMPDIR}/testsanitizers$$.c -o ${TMPDIR}/testsanitizers$$.o 2>&1 | grep "unrecognized" >& /dev/null; then
21-
echo "skipping msan test: -fsanitize=memory not supported"
22-
rm -f ${TMPDIR}/testsanitizers$$.*
23-
exit 0
23+
echo "skipping msan tests: -fsanitize=memory not supported"
24+
msan=no
2425
fi
2526
rm -f ${TMPDIR}/testsanitizers$$.*
2627

2728
# The memory sanitizer in versions of clang before 3.6 don't work with Go.
28-
if $CC --version | grep clang >& /dev/null; then
29+
if test "$msan" = "yes" && $CC --version | grep clang >& /dev/null; then
2930
ver=$($CC --version | sed -e 's/.* version \([0-9.-]*\).*/\1/')
3031
major=$(echo $ver | sed -e 's/\([0-9]*\).*/\1/')
3132
minor=$(echo $ver | sed -e 's/[0-9]*\.\([0-9]*\).*/\1/')
3233
if test "$major" -lt 3 || test "$major" -eq 3 -a "$minor" -lt 6; then
33-
echo "skipping msan test; clang version $major.$minor (older than 3.6)"
34-
exit 0
34+
echo "skipping msan tests: clang version $major.$minor (older than 3.6)"
35+
msan=no
3536
fi
3637

3738
# Clang before 3.8 does not work with Linux at or after 4.1.
3839
# golang.org/issue/12898.
39-
if test "$major" -lt 3 || test "$major" -eq 3 -a "$minor" -lt 8; then
40+
if test "$msan" = "yes" -a "$major" -lt 3 || test "$major" -eq 3 -a "$minor" -lt 8; then
4041
if test "$(uname)" = Linux; then
4142
linuxver=$(uname -r)
4243
linuxmajor=$(echo $linuxver | sed -e 's/\([0-9]*\).*/\1/')
4344
linuxminor=$(echo $linuxver | sed -e 's/[0-9]*\.\([0-9]*\).*/\1/')
4445
if test "$linuxmajor" -gt 4 || test "$linuxmajor" -eq 4 -a "$linuxminor" -ge 1; then
45-
echo "skipping msan test; clang version $major.$minor (older than 3.8) incompatible with linux version $linuxmajor.$linuxminor (4.1 or newer)"
46-
exit 0
46+
echo "skipping msan tests: clang version $major.$minor (older than 3.8) incompatible with linux version $linuxmajor.$linuxminor (4.1 or newer)"
47+
msan=no
4748
fi
4849
fi
4950
fi
5051
fi
5152

5253
status=0
5354

54-
if ! go build -msan std; then
55-
echo "FAIL: build -msan std"
56-
status=1
57-
fi
55+
if test "$msan" = "yes"; then
56+
if ! go build -msan std; then
57+
echo "FAIL: build -msan std"
58+
status=1
59+
fi
5860

59-
if ! go run -msan msan.go; then
60-
echo "FAIL: msan"
61-
status=1
62-
fi
61+
if ! go run -msan msan.go; then
62+
echo "FAIL: msan"
63+
status=1
64+
fi
6365

64-
if ! CGO_LDFLAGS="-fsanitize=memory" CGO_CPPFLAGS="-fsanitize=memory" go run -msan -a msan2.go; then
65-
echo "FAIL: msan2 with -fsanitize=memory"
66-
status=1
67-
fi
66+
if ! CGO_LDFLAGS="-fsanitize=memory" CGO_CPPFLAGS="-fsanitize=memory" go run -msan -a msan2.go; then
67+
echo "FAIL: msan2 with -fsanitize=memory"
68+
status=1
69+
fi
6870

69-
if ! go run -msan -a msan2.go; then
70-
echo "FAIL: msan2"
71-
status=1
72-
fi
71+
if ! go run -msan -a msan2.go; then
72+
echo "FAIL: msan2"
73+
status=1
74+
fi
7375

74-
if ! go run -msan msan3.go; then
75-
echo "FAIL: msan3"
76-
status=1
76+
if ! go run -msan msan3.go; then
77+
echo "FAIL: msan3"
78+
status=1
79+
fi
80+
81+
if ! go run -msan msan4.go; then
82+
echo "FAIL: msan4"
83+
status=1
84+
fi
85+
86+
if go run -msan msan_fail.go 2>/dev/null; then
87+
echo "FAIL: msan_fail"
88+
status=1
89+
fi
7790
fi
7891

79-
if ! go run -msan msan4.go; then
80-
echo "FAIL: msan4"
81-
status=1
92+
tsan=yes
93+
94+
TMPDIR=${TMPDIR:-/tmp}
95+
echo > ${TMPDIR}/testsanitizers$$.c
96+
if $CC -fsanitize=thread -c ${TMPDIR}/testsanitizers$$.c -o ${TMPDIR}/testsanitizers$$.o 2>&1 | grep "unrecognized" >& /dev/null; then
97+
echo "skipping tsan tests: -fsanitize=thread not supported"
98+
tsan=no
8299
fi
100+
rm -f ${TMPDIR}/testsanitizers$$.*
101+
102+
if test "$tsan" = "yes"; then
103+
err=${TMPDIR}/tsanerr$$.out
104+
105+
if ! go run tsan.go 2>$err; then
106+
echo "FAIL: tsan"
107+
status=1
108+
elif grep -i warning $err >/dev/null 2>&1; then
109+
cat $err
110+
echo "FAIL: tsan"
111+
status=1
112+
fi
113+
114+
if ! go run tsan2.go 2>$err; then
115+
echo "FAIL: tsan2"
116+
status=1
117+
elif grep -i warning $err >/dev/null 2>&1; then
118+
cat $err
119+
echo "FAIL: tsan2"
120+
status=1
121+
fi
83122

84-
if go run -msan msan_fail.go 2>/dev/null; then
85-
echo "FAIL: msan_fail"
86-
status=1
123+
rm -f $err
87124
fi
88125

89126
exit $status

misc/cgo/testsanitizers/tsan.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
// This program produced false race reports when run under the C/C++
8+
// ThreadSanitizer, as it did not understand the synchronization in
9+
// the Go code.
10+
11+
/*
12+
#cgo CFLAGS: -fsanitize=thread
13+
#cgo LDFLAGS: -fsanitize=thread
14+
15+
int val;
16+
17+
int getVal() {
18+
return val;
19+
}
20+
21+
void setVal(int i) {
22+
val = i;
23+
}
24+
*/
25+
import "C"
26+
27+
import (
28+
"runtime"
29+
)
30+
31+
func main() {
32+
runtime.LockOSThread()
33+
C.setVal(1)
34+
c := make(chan bool)
35+
go func() {
36+
runtime.LockOSThread()
37+
C.setVal(2)
38+
c <- true
39+
}()
40+
<-c
41+
if v := C.getVal(); v != 2 {
42+
panic(v)
43+
}
44+
}

misc/cgo/testsanitizers/tsan2.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
// This program produced false race reports when run under the C/C++
8+
// ThreadSanitizer, as it did not understand the synchronization in
9+
// the Go code.
10+
11+
/*
12+
#cgo CFLAGS: -fsanitize=thread
13+
#cgo LDFLAGS: -fsanitize=thread
14+
15+
extern void GoRun(void);
16+
17+
// Yes, you can have definitions if you use //export, as long as they are weak.
18+
19+
int val __attribute__ ((weak));
20+
21+
int run(void) __attribute__ ((weak));
22+
23+
int run() {
24+
val = 1;
25+
GoRun();
26+
return val;
27+
}
28+
29+
void setVal(int) __attribute__ ((weak));
30+
31+
void setVal(int i) {
32+
val = i;
33+
}
34+
*/
35+
import "C"
36+
37+
import "runtime"
38+
39+
//export GoRun
40+
func GoRun() {
41+
runtime.LockOSThread()
42+
c := make(chan bool)
43+
go func() {
44+
runtime.LockOSThread()
45+
C.setVal(2)
46+
c <- true
47+
}()
48+
<-c
49+
}
50+
51+
func main() {
52+
if v := C.run(); v != 2 {
53+
panic(v)
54+
}
55+
}

0 commit comments

Comments
 (0)