tsan: more precise handling of IO synchronization

llvm-svn: 169970
This commit is contained in:
Dmitry Vyukov 2012-12-12 11:59:30 +00:00
parent ada2fbd2d4
commit 60c14ce625
5 changed files with 119 additions and 221 deletions

View File

@ -0,0 +1,33 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int fds[2];
void *Thread1(void *x) {
char buf;
read(fds[0], &buf, 1);
close(fds[0]);
return 0;
}
void *Thread2(void *x) {
close(fds[1]);
return 0;
}
int main() {
fds[0] = open("/dev/random", O_RDONLY);
fds[1] = dup2(fds[0], 100);
pthread_t t[2];
pthread_create(&t[0], NULL, Thread1, NULL);
pthread_create(&t[1], NULL, Thread2, NULL);
pthread_join(t[0], NULL);
pthread_join(t[1], NULL);
}
// CHECK-NOT: WARNING: ThreadSanitizer: data race

View File

@ -22,6 +22,7 @@
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "tsan_mman.h"
#include "tsan_fd.h"
using namespace __tsan; // NOLINT
@ -54,6 +55,7 @@ extern "C" void *pthread_self();
extern "C" void _exit(int status);
extern "C" int __cxa_atexit(void (*func)(void *arg), void *arg, void *dso);
extern "C" int *__errno_location();
extern "C" int fileno(void *stream);
const int PTHREAD_MUTEX_RECURSIVE = 1;
const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
const int kPthreadAttrSize = 56;
@ -128,10 +130,8 @@ static SignalContext *SigCtx(ThreadState *thr) {
SignalContext *ctx = (SignalContext*)thr->signal_ctx;
if (ctx == 0 && thr->is_alive) {
ScopedInRtl in_rtl;
ctx = (SignalContext*)internal_alloc(
MBlockSignal, sizeof(*ctx));
MemoryResetRange(thr, 0, (uptr)ctx, sizeof(*ctx));
internal_memset(ctx, 0, sizeof(*ctx));
ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext");
MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
thr->signal_ctx = ctx;
}
return ctx;
@ -310,216 +310,6 @@ TSAN_INTERCEPTOR(void, siglongjmp, void *env, int val) {
Die();
}
enum FdType {
FdGlobal, // Something we don't know about, global sync.
FdNone, // Does not require any sync.
FdFile,
FdSock,
FdPipe,
FdEvent, // see eventfd()
FdPoll
};
struct FdDesc {
FdType type;
u64 sync;
};
struct FdContext {
static const int kMaxFds = 10 * 1024; // Everything else is synced globally.
FdDesc desc[kMaxFds];
// Addresses used for synchronization.
u64 fdglobal;
u64 fdfile;
u64 fdsock;
u64 fdpipe;
u64 fdpoll;
u64 fdevent;
};
static FdContext fdctx;
static void FdInit() {
fdctx.desc[0].type = FdNone;
fdctx.desc[1].type = FdNone;
fdctx.desc[2].type = FdNone;
}
static void *FdAddr(int fd) {
if (fd >= FdContext::kMaxFds)
return &fdctx.fdglobal;
FdDesc *desc = &fdctx.desc[fd];
if (desc->type == FdNone)
return 0;
if (desc->type == FdGlobal)
return &fdctx.fdglobal;
if (desc->type == FdFile)
return &fdctx.fdfile;
if (desc->type == FdSock)
return &fdctx.fdsock;
if (desc->type == FdPipe)
return &fdctx.fdpipe;
if (desc->type == FdEvent)
return &fdctx.fdevent;
if (desc->type == FdPoll)
return &fdctx.fdpoll;
CHECK(0);
return 0;
}
static void FdAcquire(ThreadState *thr, uptr pc, int fd) {
void *addr = FdAddr(fd);
DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, addr);
if (addr)
Acquire(thr, pc, (uptr)addr);
if (fd < FdContext::kMaxFds)
MemoryRead8Byte(thr, pc, (uptr)&fdctx.desc[fd].sync);
}
static void FdRelease(ThreadState *thr, uptr pc, int fd) {
void *addr = FdAddr(fd);
DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, addr);
if (addr)
Release(thr, pc, (uptr)addr);
if (fd < FdContext::kMaxFds)
MemoryRead8Byte(thr, pc, (uptr)&fdctx.desc[fd].sync);
}
static void FdClose(ThreadState *thr, uptr pc, int fd) {
if (fd >= FdContext::kMaxFds)
return;
FdDesc *desc = &fdctx.desc[fd];
SyncVar *s = CTX()->synctab.GetAndRemove(thr, pc, (uptr)&desc->sync);
if (s)
DestroyAndFree(s);
// FIXME(dvyukov): change to FdNone once we handle all fd operations.
desc->type = FdGlobal;
// To catch races between fd usage and close.
MemoryWrite8Byte(thr, pc, (uptr)&desc->sync);
// We need to clear it, because if we do not intercept any call out there
// that creates fd, we will hit false postives.
MemoryResetRange(thr, pc, (uptr)&desc->sync, sizeof(desc->sync));
}
static void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
if (fd >= FdContext::kMaxFds)
return;
FdDesc *desc = &fdctx.desc[fd];
desc->type = FdFile;
// To catch races between fd usage and open.
MemoryRangeImitateWrite(thr, pc, (uptr)&desc->sync, sizeof(desc->sync));
}
static void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) {
if (oldfd >= FdContext::kMaxFds || newfd >= FdContext::kMaxFds) {
if (oldfd < FdContext::kMaxFds) {
// FIXME(dvyukov): here we lose old sync object associated with the fd,
// this can lead to false positives.
FdDesc *odesc = &fdctx.desc[oldfd];
odesc->type = FdGlobal;
}
if (newfd < FdContext::kMaxFds) {
FdClose(thr, pc, newfd);
FdDesc *ndesc = &fdctx.desc[newfd];
ndesc->type = FdGlobal;
}
return;
}
FdClose(thr, pc, newfd);
FdDesc *ndesc = &fdctx.desc[newfd];
ndesc->type = FdFile;
// To catch races between fd usage and open.
MemoryRangeImitateWrite(thr, pc, (uptr)&ndesc->sync, sizeof(ndesc->sync));
}
static void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
if (rfd >= FdContext::kMaxFds || wfd >= FdContext::kMaxFds) {
if (rfd < FdContext::kMaxFds) {
FdDesc *rdesc = &fdctx.desc[rfd];
rdesc->type = FdGlobal;
}
if (wfd < FdContext::kMaxFds) {
FdDesc *wdesc = &fdctx.desc[wfd];
wdesc->type = FdGlobal;
}
return;
}
FdDesc *rdesc = &fdctx.desc[rfd];
rdesc->type = FdPipe;
// To catch races between fd usage and open.
MemoryRangeImitateWrite(thr, pc, (uptr)&rdesc->sync, sizeof(rdesc->sync));
FdDesc *wdesc = &fdctx.desc[wfd];
wdesc->type = FdPipe;
// To catch races between fd usage and open.
MemoryRangeImitateWrite(thr, pc, (uptr)&wdesc->sync, sizeof(rdesc->sync));
DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);
}
static void FdEventCreate(ThreadState *thr, uptr pc, int fd) {
if (fd >= FdContext::kMaxFds)
return;
FdDesc *desc = &fdctx.desc[fd];
desc->type = FdEvent;
// To catch races between fd usage and open.
MemoryRangeImitateWrite(thr, pc, (uptr)&desc->sync, sizeof(desc->sync));
}
static void FdPollCreate(ThreadState *thr, uptr pc, int fd) {
if (fd >= FdContext::kMaxFds)
return;
FdDesc *desc = &fdctx.desc[fd];
desc->type = FdPoll;
// To catch races between fd usage and open.
MemoryRangeImitateWrite(thr, pc, (uptr)&desc->sync, sizeof(desc->sync));
}
static void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
if (fd >= FdContext::kMaxFds)
return;
FdDesc *desc = &fdctx.desc[fd];
// It can be UDP socket, let's assume they are not used for synchronization.
desc->type = FdNone;
// To catch races between fd usage and open.
MemoryRangeImitateWrite(thr, pc, (uptr)&desc->sync, sizeof(desc->sync));
}
static void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
if (fd < FdContext::kMaxFds) {
FdDesc *desc = &fdctx.desc[fd];
desc->type = FdNone;
MemoryRead8Byte(thr, pc, (uptr)&desc->sync);
}
if (newfd < FdContext::kMaxFds) {
FdDesc *desc = &fdctx.desc[newfd];
desc->type = FdSock;
MemoryWrite8Byte(thr, pc, (uptr)&desc->sync);
}
}
static void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {
if (fd >= FdContext::kMaxFds)
return;
FdDesc *desc = &fdctx.desc[fd];
desc->type = FdSock;
MemoryWrite8Byte(thr, pc, (uptr)&desc->sync);
}
static uptr file2addr(char *path) {
(void)path;
static u64 addr;
return (uptr)&addr;
}
static uptr dir2addr(char *path) {
(void)path;
static u64 addr;
return (uptr)&addr;
}
TSAN_INTERCEPTOR(void*, malloc, uptr size) {
void *p = 0;
{
@ -850,7 +640,7 @@ static void thread_finalize(void *v) {
SignalContext *sctx = thr->signal_ctx;
if (sctx) {
thr->signal_ctx = 0;
internal_free(sctx);
UnmapOrDie(sctx, sizeof(*sctx));
}
}
}
@ -1124,11 +914,13 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
return res;
}
/*
TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a);
int res = REAL(pthread_cond_init)(c, a);
return res;
}
*/
TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) {
SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c);
@ -1278,6 +1070,14 @@ TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
return fd;
}
TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
int fd = REAL(open64)(name, flags, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
return fd;
}
TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
int fd = REAL(creat)(name, mode);
@ -1286,6 +1086,14 @@ TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
return fd;
}
TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
int fd = REAL(creat64)(name, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
return fd;
}
TSAN_INTERCEPTOR(int, dup, int oldfd) {
SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
int newfd = REAL(dup)(oldfd);
@ -1368,10 +1176,18 @@ TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
TSAN_INTERCEPTOR(int, close, int fd) {
SCOPED_TSAN_INTERCEPTOR(close, fd);
FdClose(thr, pc, fd);
if (fd >= 0)
FdClose(thr, pc, fd);
return REAL(close)(fd);
}
TSAN_INTERCEPTOR(int, __close, int fd) {
SCOPED_TSAN_INTERCEPTOR(__close, fd);
if (fd >= 0)
FdClose(thr, pc, fd);
return REAL(__close)(fd);
}
TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
SCOPED_TSAN_INTERCEPTOR(pipe, pipefd);
int res = REAL(pipe)(pipefd);
@ -1502,7 +1318,7 @@ TSAN_INTERCEPTOR(long_t, recvmsg, int fd, void *msg, int flags) {
TSAN_INTERCEPTOR(int, unlink, char *path) {
SCOPED_TSAN_INTERCEPTOR(unlink, path);
Release(thr, pc, file2addr(path));
Release(thr, pc, File2addr(path));
int res = REAL(unlink)(path);
return res;
}
@ -1510,7 +1326,40 @@ TSAN_INTERCEPTOR(int, unlink, char *path) {
TSAN_INTERCEPTOR(void*, fopen, char *path, char *mode) {
SCOPED_TSAN_INTERCEPTOR(fopen, path, mode);
void *res = REAL(fopen)(path, mode);
Acquire(thr, pc, file2addr(path));
Acquire(thr, pc, File2addr(path));
if (res) {
int fd = fileno(res);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
}
return res;
}
TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) {
SCOPED_TSAN_INTERCEPTOR(freopen, path, mode, stream);
if (stream) {
int fd = fileno(stream);
if (fd >= 0)
FdClose(thr, pc, fd);
}
void *res = REAL(freopen)(path, mode, stream);
Acquire(thr, pc, File2addr(path));
if (res) {
int fd = fileno(res);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
}
return res;
}
TSAN_INTERCEPTOR(int, fclose, void *stream) {
SCOPED_TSAN_INTERCEPTOR(fclose, stream);
if (stream) {
int fd = fileno(stream);
if (fd >= 0)
FdClose(thr, pc, fd);
}
int res = REAL(fclose)(stream);
return res;
}
@ -1534,7 +1383,7 @@ TSAN_INTERCEPTOR(int, puts, const char *s) {
TSAN_INTERCEPTOR(int, rmdir, char *path) {
SCOPED_TSAN_INTERCEPTOR(rmdir, path);
Release(thr, pc, dir2addr(path));
Release(thr, pc, Dir2addr(path));
int res = REAL(rmdir)(path);
return res;
}
@ -1542,7 +1391,8 @@ TSAN_INTERCEPTOR(int, rmdir, char *path) {
TSAN_INTERCEPTOR(void*, opendir, char *path) {
SCOPED_TSAN_INTERCEPTOR(opendir, path);
void *res = REAL(opendir)(path);
Acquire(thr, pc, dir2addr(path));
if (res != 0)
Acquire(thr, pc, Dir2addr(path));
return res;
}
@ -1853,7 +1703,7 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pthread_rwlock_timedwrlock);
TSAN_INTERCEPT(pthread_rwlock_unlock);
TSAN_INTERCEPT(pthread_cond_init);
// TSAN_INTERCEPT(pthread_cond_init);
TSAN_INTERCEPT(pthread_cond_destroy);
TSAN_INTERCEPT(pthread_cond_signal);
TSAN_INTERCEPT(pthread_cond_broadcast);
@ -1875,7 +1725,9 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(sem_getvalue);
TSAN_INTERCEPT(open);
TSAN_INTERCEPT(open64);
TSAN_INTERCEPT(creat);
TSAN_INTERCEPT(creat64);
TSAN_INTERCEPT(dup);
TSAN_INTERCEPT(dup2);
TSAN_INTERCEPT(dup3);
@ -1907,6 +1759,8 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(unlink);
TSAN_INTERCEPT(fopen);
TSAN_INTERCEPT(freopen);
TSAN_INTERCEPT(fclose);
TSAN_INTERCEPT(fread);
TSAN_INTERCEPT(fwrite);
TSAN_INTERCEPT(puts);

View File

@ -59,6 +59,7 @@ enum MBlockType {
MBlockSuppression,
MBlockExpectRace,
MBlockSignal,
MBlockFD,
// This must be the last.
MBlockTypeCount

View File

@ -182,7 +182,9 @@ void StatOutput(u64 *stat) {
name[StatInt_sem_post] = " sem_post ";
name[StatInt_sem_getvalue] = " sem_getvalue ";
name[StatInt_open] = " open ";
name[StatInt_open64] = " open64 ";
name[StatInt_creat] = " creat ";
name[StatInt_creat64] = " creat64 ";
name[StatInt_dup] = " dup ";
name[StatInt_dup2] = " dup2 ";
name[StatInt_dup3] = " dup3 ";
@ -194,6 +196,7 @@ void StatOutput(u64 *stat) {
name[StatInt_epoll_create] = " epoll_create ";
name[StatInt_epoll_create1] = " epoll_create1 ";
name[StatInt_close] = " close ";
name[StatInt___close] = " __close ";
name[StatInt_pipe] = " pipe ";
name[StatInt_pipe2] = " pipe2 ";
name[StatInt_read] = " read ";
@ -212,6 +215,8 @@ void StatOutput(u64 *stat) {
name[StatInt_recvmsg] = " recvmsg ";
name[StatInt_unlink] = " unlink ";
name[StatInt_fopen] = " fopen ";
name[StatInt_freopen] = " freopen ";
name[StatInt_fclose] = " fclose ";
name[StatInt_fread] = " fread ";
name[StatInt_fwrite] = " fwrite ";
name[StatInt_puts] = " puts ";

View File

@ -177,7 +177,9 @@ enum StatType {
StatInt_sem_post,
StatInt_sem_getvalue,
StatInt_open,
StatInt_open64,
StatInt_creat,
StatInt_creat64,
StatInt_dup,
StatInt_dup2,
StatInt_dup3,
@ -189,6 +191,7 @@ enum StatType {
StatInt_epoll_create,
StatInt_epoll_create1,
StatInt_close,
StatInt___close,
StatInt_pipe,
StatInt_pipe2,
StatInt_read,
@ -207,6 +210,8 @@ enum StatType {
StatInt_recvmsg,
StatInt_unlink,
StatInt_fopen,
StatInt_freopen,
StatInt_fclose,
StatInt_fread,
StatInt_fwrite,
StatInt_puts,