diff options
Diffstat (limited to 'libgo/misc/cgo/testsanitizers')
-rw-r--r-- | libgo/misc/cgo/testsanitizers/msan.go | 35 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/msan2.go | 35 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/msan3.go | 33 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/msan4.go | 50 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/msan5.go | 57 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/msan_fail.go | 36 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/msan_shared.go | 12 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/test.bash | 204 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/tsan.go | 44 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/tsan2.go | 55 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/tsan3.go | 40 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/tsan4.go | 34 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/tsan5.go | 51 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/tsan6.go | 49 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/tsan7.go | 40 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/tsan8.go | 60 | ||||
-rw-r--r-- | libgo/misc/cgo/testsanitizers/tsan9.go | 67 |
17 files changed, 902 insertions, 0 deletions
diff --git a/libgo/misc/cgo/testsanitizers/msan.go b/libgo/misc/cgo/testsanitizers/msan.go new file mode 100644 index 00000000000..7915fa84f60 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/msan.go @@ -0,0 +1,35 @@ +// Copyright 2015 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 <stdint.h> + +void f(int32_t *p, int n) { + int i; + + for (i = 0; i < n; i++) { + p[i] = (int32_t)i; + } +} +*/ +import "C" + +import ( + "fmt" + "os" + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + for i, v := range a { + if i != int(v) { + fmt.Println("bad %d: %v\n", i, a) + os.Exit(1) + } + } +} diff --git a/libgo/misc/cgo/testsanitizers/msan2.go b/libgo/misc/cgo/testsanitizers/msan2.go new file mode 100644 index 00000000000..6690cb034fc --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/msan2.go @@ -0,0 +1,35 @@ +// Copyright 2015 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 <string.h> +#include <stdint.h> +#include <stdlib.h> + +void f(int32_t *p, int n) { + int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n); + memcpy(p, q, n * sizeof(*p)); + free(q); +} + +void g(int32_t *p, int n) { + if (p[4] != 1) { + abort(); + } +} +*/ +import "C" + +import ( + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + a[4] = 1 + C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) +} diff --git a/libgo/misc/cgo/testsanitizers/msan3.go b/libgo/misc/cgo/testsanitizers/msan3.go new file mode 100644 index 00000000000..61a9c29e1a9 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/msan3.go @@ -0,0 +1,33 @@ +// Copyright 2015 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 + +/* +extern int *GoFn(int *); + +// Yes, you can have definitions if you use //export, as long as they are weak. +int f(void) __attribute__ ((weak)); + +int f() { + int i; + int *p = GoFn(&i); + if (*p != 12345) + return 0; + return 1; +} +*/ +import "C" + +//export GoFn +func GoFn(p *C.int) *C.int { + *p = C.int(12345) + return p +} + +func main() { + if r := C.f(); r != 1 { + panic(r) + } +} diff --git a/libgo/misc/cgo/testsanitizers/msan4.go b/libgo/misc/cgo/testsanitizers/msan4.go new file mode 100644 index 00000000000..6c91ff5f091 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/msan4.go @@ -0,0 +1,50 @@ +// Copyright 2015 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 + +// The memory profiler can call copy from a slice on the system stack, +// which msan used to think meant a reference to uninitialized memory. + +/* +#include <time.h> +#include <unistd.h> + +extern void Nop(char*); + +// Use weak as a hack to permit defining a function even though we use export. +void poison() __attribute__ ((weak)); + +// Poison the stack. +void poison() { + char a[1024]; + Nop(&a[0]); +} + +*/ +import "C" + +import ( + "runtime" +) + +func main() { + runtime.MemProfileRate = 1 + start(100) +} + +func start(i int) { + if i == 0 { + return + } + C.poison() + // Tie up a thread. + // We won't actually wait for this sleep to complete. + go func() { C.sleep(1) }() + start(i - 1) +} + +//export Nop +func Nop(*C.char) { +} diff --git a/libgo/misc/cgo/testsanitizers/msan5.go b/libgo/misc/cgo/testsanitizers/msan5.go new file mode 100644 index 00000000000..f1479eb8a00 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/msan5.go @@ -0,0 +1,57 @@ +// 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 + +// Using reflect to set a value was not seen by msan. + +/* +#include <stdlib.h> + +extern void Go1(int*); +extern void Go2(char*); + +// Use weak as a hack to permit defining a function even though we use export. +void C1() __attribute__ ((weak)); +void C2() __attribute__ ((weak)); + +void C1() { + int i; + Go1(&i); + if (i != 42) { + abort(); + } +} + +void C2() { + char a[2]; + a[1] = 42; + Go2(a); + if (a[0] != 42) { + abort(); + } +} +*/ +import "C" + +import ( + "reflect" + "unsafe" +) + +//export Go1 +func Go1(p *C.int) { + reflect.ValueOf(p).Elem().Set(reflect.ValueOf(C.int(42))) +} + +//export Go2 +func Go2(p *C.char) { + a := (*[2]byte)(unsafe.Pointer(p)) + reflect.Copy(reflect.ValueOf(a[:1]), reflect.ValueOf(a[1:])) +} + +func main() { + C.C1() + C.C2() +} diff --git a/libgo/misc/cgo/testsanitizers/msan_fail.go b/libgo/misc/cgo/testsanitizers/msan_fail.go new file mode 100644 index 00000000000..4c8dab34f6e --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/msan_fail.go @@ -0,0 +1,36 @@ +// Copyright 2015 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 <string.h> +#include <stdint.h> +#include <stdlib.h> + +void f(int32_t *p, int n) { + int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n); + memcpy(p, q, n * sizeof(*p)); + free(q); +} + +void g(int32_t *p, int n) { + if (p[4] != 1) { + // We shouldn't get here; msan should stop us first. + exit(0); + } +} +*/ +import "C" + +import ( + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + a[3] = 1 + C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) +} diff --git a/libgo/misc/cgo/testsanitizers/msan_shared.go b/libgo/misc/cgo/testsanitizers/msan_shared.go new file mode 100644 index 00000000000..966947cac35 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/msan_shared.go @@ -0,0 +1,12 @@ +// Copyright 2017 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. + +// This program segfaulted during libpreinit when built with -msan: +// http://golang.org/issue/18707 + +package main + +import "C" + +func main() {} diff --git a/libgo/misc/cgo/testsanitizers/test.bash b/libgo/misc/cgo/testsanitizers/test.bash new file mode 100644 index 00000000000..4da85020d89 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/test.bash @@ -0,0 +1,204 @@ +#!/usr/bin/env bash +# Copyright 2015 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. + +# This directory is intended to test the use of Go with sanitizers +# like msan, asan, etc. See https://github.com/google/sanitizers . + +set -e + +# The sanitizers were originally developed with clang, so prefer it. +CC=cc +if test -x "$(type -p clang)"; then + CC=clang +fi +export CC + +if [ "$(sysctl -n vm.overcommit_memory)" = 2 ]; then + echo "skipping msan/tsan tests: vm.overcommit_memory=2" >&2 + exit 0 +fi + +msan=yes + +TMPDIR=${TMPDIR:-/tmp} +echo 'int main() { return 0; }' > ${TMPDIR}/testsanitizers$$.c +if $CC -fsanitize=memory -o ${TMPDIR}/testsanitizers$$ ${TMPDIR}/testsanitizers$$.c 2>&1 | grep "unrecognized" >& /dev/null; then + echo "skipping msan tests: $CC -fsanitize=memory not supported" + msan=no +elif ! test -x ${TMPDIR}/testsanitizers$$; then + echo "skipping msan tests: $CC -fsanitize-memory did not generate an executable" + msan=no +elif ! ${TMPDIR}/testsanitizers$$ >/dev/null 2>&1; then + echo "skipping msan tests: $CC -fsanitize-memory generates broken executable" + msan=no +fi +rm -f ${TMPDIR}/testsanitizers$$.* + +tsan=yes + +# The memory and thread sanitizers in versions of clang before 3.6 +# don't work with Go. +if test "$msan" = "yes" && $CC --version | grep clang >& /dev/null; then + ver=$($CC --version | sed -e 's/.* version \([0-9.-]*\).*/\1/') + major=$(echo $ver | sed -e 's/\([0-9]*\).*/\1/') + minor=$(echo $ver | sed -e 's/[0-9]*\.\([0-9]*\).*/\1/') + if test "$major" -lt 3 || test "$major" -eq 3 -a "$minor" -lt 6; then + echo "skipping msan/tsan tests: clang version $major.$minor (older than 3.6)" + msan=no + tsan=no + fi + + # Clang before 3.8 does not work with Linux at or after 4.1. + # golang.org/issue/12898. + if test "$msan" = "yes" -a "$major" -lt 3 || test "$major" -eq 3 -a "$minor" -lt 8; then + if test "$(uname)" = Linux; then + linuxver=$(uname -r) + linuxmajor=$(echo $linuxver | sed -e 's/\([0-9]*\).*/\1/') + linuxminor=$(echo $linuxver | sed -e 's/[0-9]*\.\([0-9]*\).*/\1/') + if test "$linuxmajor" -gt 4 || test "$linuxmajor" -eq 4 -a "$linuxminor" -ge 1; then + echo "skipping msan/tsan tests: clang version $major.$minor (older than 3.8) incompatible with linux version $linuxmajor.$linuxminor (4.1 or newer)" + msan=no + tsan=no + fi + fi + fi +fi + +status=0 + +testmsanshared() { + goos=$(go env GOOS) + suffix="-installsuffix testsanitizers" + libext="so" + if [ "$goos" == "darwin" ]; then + libext="dylib" + fi + go build -msan -buildmode=c-shared $suffix -o ${TMPDIR}/libmsanshared.$libext msan_shared.go + + echo 'int main() { return 0; }' > ${TMPDIR}/testmsanshared.c + $CC $(go env GOGCCFLAGS) -fsanitize=memory -o ${TMPDIR}/testmsanshared ${TMPDIR}/testmsanshared.c ${TMPDIR}/libmsanshared.$libext + + if ! LD_LIBRARY_PATH=. ${TMPDIR}/testmsanshared; then + echo "FAIL: msan_shared" + status=1 + fi + rm -f ${TMPDIR}/{testmsanshared,testmsanshared.c,libmsanshared.$libext} +} + +if test "$msan" = "yes"; then + if ! go build -msan std; then + echo "FAIL: build -msan std" + status=1 + fi + + if ! go run -msan msan.go; then + echo "FAIL: msan" + status=1 + fi + + if ! CGO_LDFLAGS="-fsanitize=memory" CGO_CPPFLAGS="-fsanitize=memory" go run -msan -a msan2.go; then + echo "FAIL: msan2 with -fsanitize=memory" + status=1 + fi + + if ! go run -msan -a msan2.go; then + echo "FAIL: msan2" + status=1 + fi + + if ! go run -msan msan3.go; then + echo "FAIL: msan3" + status=1 + fi + + if ! go run -msan msan4.go; then + echo "FAIL: msan4" + status=1 + fi + + if ! go run -msan msan5.go; then + echo "FAIL: msan5" + status=1 + fi + + if go run -msan msan_fail.go 2>/dev/null; then + echo "FAIL: msan_fail" + status=1 + fi + + testmsanshared +fi + +if test "$tsan" = "yes"; then + echo 'int main() { return 0; }' > ${TMPDIR}/testsanitizers$$.c + ok=yes + if ! $CC -fsanitize=thread ${TMPDIR}/testsanitizers$$.c -o ${TMPDIR}/testsanitizers$$ &> ${TMPDIR}/testsanitizers$$.err; then + ok=no + fi + if grep "unrecognized" ${TMPDIR}/testsanitizers$$.err >& /dev/null; then + echo "skipping tsan tests: -fsanitize=thread not supported" + tsan=no + elif test "$ok" != "yes"; then + cat ${TMPDIR}/testsanitizers$$.err + echo "skipping tsan tests: -fsanitizer=thread build failed" + tsan=no + fi + rm -f ${TMPDIR}/testsanitizers$$* +fi + +# Run a TSAN test. +# $1 test name +# $2 environment variables +# $3 go run args +testtsan() { + err=${TMPDIR}/tsanerr$$.out + if ! env $2 go run $3 $1 2>$err; then + cat $err + echo "FAIL: $1" + status=1 + elif grep -i warning $err >/dev/null 2>&1; then + cat $err + echo "FAIL: $1" + status=1 + fi + rm -f $err +} + +if test "$tsan" = "yes"; then + testtsan tsan.go + testtsan tsan2.go + testtsan tsan3.go + testtsan tsan4.go + testtsan tsan8.go + testtsan tsan9.go + + # These tests are only reliable using clang or GCC version 7 or later. + # Otherwise runtime/cgo/libcgo.h can't tell whether TSAN is in use. + ok=false + if ${CC} --version | grep clang >/dev/null 2>&1; then + ok=true + else + ver=$($CC -dumpversion) + major=$(echo $ver | sed -e 's/\([0-9]*\).*/\1/') + if test "$major" -lt 7; then + echo "skipping remaining TSAN tests: GCC version $major (older than 7)" + else + ok=true + fi + fi + + if test "$ok" = "true"; then + # This test requires rebuilding os/user with -fsanitize=thread. + testtsan tsan5.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan" + + # This test requires rebuilding runtime/cgo with -fsanitize=thread. + testtsan tsan6.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan" + + # This test requires rebuilding runtime/cgo with -fsanitize=thread. + testtsan tsan7.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan" + fi +fi + +exit $status diff --git a/libgo/misc/cgo/testsanitizers/tsan.go b/libgo/misc/cgo/testsanitizers/tsan.go new file mode 100644 index 00000000000..6c377a701fb --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan.go @@ -0,0 +1,44 @@ +// Copyright 2015 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 produced false race reports when run under the C/C++ +// ThreadSanitizer, as it did not understand the synchronization in +// the Go code. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +int val; + +int getVal() { + return val; +} + +void setVal(int i) { + val = i; +} +*/ +import "C" + +import ( + "runtime" +) + +func main() { + runtime.LockOSThread() + C.setVal(1) + c := make(chan bool) + go func() { + runtime.LockOSThread() + C.setVal(2) + c <- true + }() + <-c + if v := C.getVal(); v != 2 { + panic(v) + } +} diff --git a/libgo/misc/cgo/testsanitizers/tsan2.go b/libgo/misc/cgo/testsanitizers/tsan2.go new file mode 100644 index 00000000000..5018a1987ca --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan2.go @@ -0,0 +1,55 @@ +// Copyright 2015 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 produced false race reports when run under the C/C++ +// ThreadSanitizer, as it did not understand the synchronization in +// the Go code. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +extern void GoRun(void); + +// Yes, you can have definitions if you use //export, as long as they are weak. + +int val __attribute__ ((weak)); + +int run(void) __attribute__ ((weak)); + +int run() { + val = 1; + GoRun(); + return val; +} + +void setVal(int) __attribute__ ((weak)); + +void setVal(int i) { + val = i; +} +*/ +import "C" + +import "runtime" + +//export GoRun +func GoRun() { + runtime.LockOSThread() + c := make(chan bool) + go func() { + runtime.LockOSThread() + C.setVal(2) + c <- true + }() + <-c +} + +func main() { + if v := C.run(); v != 2 { + panic(v) + } +} diff --git a/libgo/misc/cgo/testsanitizers/tsan3.go b/libgo/misc/cgo/testsanitizers/tsan3.go new file mode 100644 index 00000000000..87f6c80f1b1 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan3.go @@ -0,0 +1,40 @@ +// 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 + +// The stubs for the C functions read and write the same slot on the +// g0 stack when copying arguments in and out. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +int Func1() { + return 0; +} + +void Func2(int x) { + (void)x; +} +*/ +import "C" + +func main() { + const N = 10000 + done := make(chan bool, N) + for i := 0; i < N; i++ { + go func() { + C.Func1() + done <- true + }() + go func() { + C.Func2(0) + done <- true + }() + } + for i := 0; i < 2*N; i++ { + <-done + } +} diff --git a/libgo/misc/cgo/testsanitizers/tsan4.go b/libgo/misc/cgo/testsanitizers/tsan4.go new file mode 100644 index 00000000000..f0c76d84116 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan4.go @@ -0,0 +1,34 @@ +// 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 trigger TSAN false +// positive reports. + +// #cgo CFLAGS: -fsanitize=thread +// #cgo LDFLAGS: -fsanitize=thread +// #include <stdlib.h> +import "C" + +import ( + "runtime" + "sync" +) + +func main() { + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 100; i++ { + p := C.malloc(C.size_t(i * 10)) + runtime.Gosched() + C.free(p) + } + }() + } + wg.Wait() +} diff --git a/libgo/misc/cgo/testsanitizers/tsan5.go b/libgo/misc/cgo/testsanitizers/tsan5.go new file mode 100644 index 00000000000..1214a7743b6 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan5.go @@ -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() +} diff --git a/libgo/misc/cgo/testsanitizers/tsan6.go b/libgo/misc/cgo/testsanitizers/tsan6.go new file mode 100644 index 00000000000..c96f08d2f37 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan6.go @@ -0,0 +1,49 @@ +// 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 writes to Go allocated memory, with Go synchronization, +// do not look like a race. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +void f(char *p) { + *p = 1; +} +*/ +import "C" + +import ( + "runtime" + "sync" +) + +func main() { + var wg sync.WaitGroup + var mu sync.Mutex + c := make(chan []C.char, 100) + for i := 0; i < 10; i++ { + wg.Add(2) + go func() { + defer wg.Done() + for i := 0; i < 100; i++ { + c <- make([]C.char, 4096) + runtime.Gosched() + } + }() + go func() { + defer wg.Done() + for i := 0; i < 100; i++ { + p := &(<-c)[0] + mu.Lock() + C.f(p) + mu.Unlock() + } + }() + } + wg.Wait() +} diff --git a/libgo/misc/cgo/testsanitizers/tsan7.go b/libgo/misc/cgo/testsanitizers/tsan7.go new file mode 100644 index 00000000000..2fb9e45ee2d --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan7.go @@ -0,0 +1,40 @@ +// 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 + +// Setting an environment variable in a cgo program changes the C +// environment. Test that this does not confuse the race detector. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread +*/ +import "C" + +import ( + "fmt" + "os" + "sync" + "time" +) + +func main() { + var wg sync.WaitGroup + var mu sync.Mutex + f := func() { + defer wg.Done() + for i := 0; i < 100; i++ { + time.Sleep(time.Microsecond) + mu.Lock() + s := fmt.Sprint(i) + os.Setenv("TSAN_TEST"+s, s) + mu.Unlock() + } + } + wg.Add(2) + go f() + go f() + wg.Wait() +} diff --git a/libgo/misc/cgo/testsanitizers/tsan8.go b/libgo/misc/cgo/testsanitizers/tsan8.go new file mode 100644 index 00000000000..88d82a60789 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan8.go @@ -0,0 +1,60 @@ +// 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 + +// This program failed when run under the C/C++ ThreadSanitizer. The TSAN +// sigaction function interceptor returned SIG_DFL instead of the Go runtime's +// handler in registerSegvForwarder. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct sigaction prev_sa; + +void forwardSignal(int signo, siginfo_t *info, void *context) { + // One of sa_sigaction and/or sa_handler + if ((prev_sa.sa_flags&SA_SIGINFO) != 0) { + prev_sa.sa_sigaction(signo, info, context); + return; + } + if (prev_sa.sa_handler != SIG_IGN && prev_sa.sa_handler != SIG_DFL) { + prev_sa.sa_handler(signo); + return; + } + + fprintf(stderr, "No Go handler to forward to!\n"); + abort(); +} + +void registerSegvFowarder() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + sa.sa_sigaction = forwardSignal; + + if (sigaction(SIGSEGV, &sa, &prev_sa) != 0) { + perror("failed to register SEGV forwarder"); + exit(EXIT_FAILURE); + } +} +*/ +import "C" + +func main() { + C.registerSegvFowarder() + + defer func() { + recover() + }() + var nilp *int + *nilp = 42 +} diff --git a/libgo/misc/cgo/testsanitizers/tsan9.go b/libgo/misc/cgo/testsanitizers/tsan9.go new file mode 100644 index 00000000000..f166d8b495a --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan9.go @@ -0,0 +1,67 @@ +// 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 + +// This program failed when run under the C/C++ ThreadSanitizer. The +// TSAN library was not keeping track of whether signals should be +// delivered on the alternate signal stack, and the Go signal handler +// was not preserving callee-saved registers from C callers. + +/* +#cgo CFLAGS: -g -fsanitize=thread +#cgo LDFLAGS: -g -fsanitize=thread + +#include <stdlib.h> +#include <sys/time.h> + +void spin() { + size_t n; + struct timeval tvstart, tvnow; + int diff; + void *prev = NULL, *cur; + + 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 a signal in 50 milliseconds. + if (diff > 50 * 1000) { + break; + } + } + + free(prev); +} +*/ +import "C" + +import ( + "io/ioutil" + "runtime/pprof" + "time" +) + +func goSpin() { + start := time.Now() + for n := 0; n < 1<<20; n++ { + _ = make([]byte, n) + if time.Since(start) > 50*time.Millisecond { + break + } + } +} + +func main() { + pprof.StartCPUProfile(ioutil.Discard) + go C.spin() + goSpin() + pprof.StopCPUProfile() +} |