diff options
author | Dmitriy Vyukov <dvyukov@google.com> | 2011-07-29 13:47:24 -0400 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2011-07-29 13:47:24 -0400 |
commit | 91f0f18100564478f77c6fc8e16ea56f9528951c (patch) | |
tree | 896657acbedd11f0ca7203c2b582b73a22252668 | |
parent | eb2b3f5dc74181b66848bef194bfd0ec1401345c (diff) | |
download | go-91f0f18100564478f77c6fc8e16ea56f9528951c.tar.gz go-91f0f18100564478f77c6fc8e16ea56f9528951c.zip |
runtime: fix data race in findfunc()
The data race can lead to reads of partially
initialized concurrently mutated symbol data.
The change also adds a simple sanity test
for Caller() and FuncForPC().
R=rsc
CC=golang-dev
https://golang.org/cl/4817058
-rw-r--r-- | src/pkg/runtime/386/asm.s | 6 | ||||
-rw-r--r-- | src/pkg/runtime/amd64/asm.s | 6 | ||||
-rw-r--r-- | src/pkg/runtime/arm/atomic.c | 13 | ||||
-rw-r--r-- | src/pkg/runtime/runtime.h | 1 | ||||
-rw-r--r-- | src/pkg/runtime/symtab.c | 7 | ||||
-rw-r--r-- | src/pkg/runtime/symtab_test.go | 47 |
6 files changed, 78 insertions, 2 deletions
diff --git a/src/pkg/runtime/386/asm.s b/src/pkg/runtime/386/asm.s index 2505e4df6a..a14518839a 100644 --- a/src/pkg/runtime/386/asm.s +++ b/src/pkg/runtime/386/asm.s @@ -354,6 +354,12 @@ TEXT runtime·atomicstorep(SB), 7, $0 XCHGL AX, 0(BX) RET +TEXT runtime·atomicstore(SB), 7, $0 + MOVL 4(SP), BX + MOVL 8(SP), AX + XCHGL AX, 0(BX) + RET + // void jmpdefer(fn, sp); // called from deferreturn. // 1. pop the caller diff --git a/src/pkg/runtime/amd64/asm.s b/src/pkg/runtime/amd64/asm.s index 4723018a7a..3e3818c101 100644 --- a/src/pkg/runtime/amd64/asm.s +++ b/src/pkg/runtime/amd64/asm.s @@ -398,6 +398,12 @@ TEXT runtime·atomicstorep(SB), 7, $0 XCHGQ AX, 0(BX) RET +TEXT runtime·atomicstore(SB), 7, $0 + MOVQ 8(SP), BX + MOVL 16(SP), AX + XCHGL AX, 0(BX) + RET + // void jmpdefer(fn, sp); // called from deferreturn. // 1. pop the caller diff --git a/src/pkg/runtime/arm/atomic.c b/src/pkg/runtime/arm/atomic.c index 3199afe622..52e4059ae2 100644 --- a/src/pkg/runtime/arm/atomic.c +++ b/src/pkg/runtime/arm/atomic.c @@ -68,3 +68,16 @@ runtime·atomicstorep(void* volatile* addr, void* v) return; } } + +#pragma textflag 7 +void +runtime·atomicstore(uint32 volatile* addr, uint32 v) +{ + uint32 old; + + for(;;) { + old = *addr; + if(runtime·cas(addr, old, v)) + return; + } +}
\ No newline at end of file diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index eee346844b..44511da830 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -433,6 +433,7 @@ bool runtime·casp(void**, void*, void*); uint32 runtime·xadd(uint32 volatile*, int32); uint32 runtime·xchg(uint32 volatile*, uint32); uint32 runtime·atomicload(uint32 volatile*); +void runtime·atomicstore(uint32 volatile*, uint32); void* runtime·atomicloadp(void* volatile*); void runtime·atomicstorep(void* volatile*, void*); void runtime·jmpdefer(byte*, void*); diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c index 63e6d87849..d2ebf9b400 100644 --- a/src/pkg/runtime/symtab.c +++ b/src/pkg/runtime/symtab.c @@ -78,6 +78,7 @@ static int32 nfunc; static byte **fname; static int32 nfname; +static uint32 funcinit; static Lock funclock; static void @@ -427,10 +428,12 @@ runtime·findfunc(uintptr addr) // (Before enabling the signal handler, // SetCPUProfileRate calls findfunc to trigger // the initialization outside the handler.) - if(runtime·atomicloadp(&func) == nil) { + if(runtime·atomicload(&funcinit) == 0) { runtime·lock(&funclock); - if(func == nil) + if(funcinit == 0) { buildfuncs(); + runtime·atomicstore(&funcinit, 1); + } runtime·unlock(&funclock); } diff --git a/src/pkg/runtime/symtab_test.go b/src/pkg/runtime/symtab_test.go new file mode 100644 index 0000000000..bd9fe18c47 --- /dev/null +++ b/src/pkg/runtime/symtab_test.go @@ -0,0 +1,47 @@ +// Copyright 2009 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 runtime_test + +import ( + "runtime" + "strings" + "testing" +) + +func TestCaller(t *testing.T) { + procs := runtime.GOMAXPROCS(-1) + c := make(chan bool, procs) + for p := 0; p < procs; p++ { + go func() { + for i := 0; i < 1000; i++ { + testCallerFoo(t) + } + c <- true + }() + defer func() { + <-c + }() + } +} + +func testCallerFoo(t *testing.T) { + testCallerBar(t) +} + +func testCallerBar(t *testing.T) { + for i := 0; i < 2; i++ { + pc, file, line, ok := runtime.Caller(i) + f := runtime.FuncForPC(pc) + if !ok || + !strings.HasSuffix(file, "symtab_test.go") || + (i == 0 && !strings.HasSuffix(f.Name(), "testCallerBar")) || + (i == 1 && !strings.HasSuffix(f.Name(), "testCallerFoo")) || + line < 5 || line > 1000 || + f.Entry() >= pc { + t.Errorf("incorrect symbol info %d: %t %d %d %s %s %d", + i, ok, f.Entry(), pc, f.Name(), file, line) + } + } +} |