diff options
-rw-r--r-- | lib/tsan/lit_tests/fd_dup_norace.cc | 33 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_interceptors.cc | 296 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_mman.h | 1 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_stat.cc | 5 | ||||
-rw-r--r-- | lib/tsan/rtl/tsan_stat.h | 5 |
5 files changed, 119 insertions, 221 deletions
diff --git a/lib/tsan/lit_tests/fd_dup_norace.cc b/lib/tsan/lit_tests/fd_dup_norace.cc new file mode 100644 index 000000000..bb3ed46b2 --- /dev/null +++ b/lib/tsan/lit_tests/fd_dup_norace.cc @@ -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 diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc index 3e2d15c22..c343134c7 100644 --- a/lib/tsan/rtl/tsan_interceptors.cc +++ b/lib/tsan/rtl/tsan_interceptors.cc @@ -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); diff --git a/lib/tsan/rtl/tsan_mman.h b/lib/tsan/rtl/tsan_mman.h index 06dce8c23..5cf00eac8 100644 --- a/lib/tsan/rtl/tsan_mman.h +++ b/lib/tsan/rtl/tsan_mman.h @@ -59,6 +59,7 @@ enum MBlockType { MBlockSuppression, MBlockExpectRace, MBlockSignal, + MBlockFD, // This must be the last. MBlockTypeCount diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc index 6053d28a1..7ebc690bf 100644 --- a/lib/tsan/rtl/tsan_stat.cc +++ b/lib/tsan/rtl/tsan_stat.cc @@ -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 "; diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h index b144ba735..cfc5fa9b3 100644 --- a/lib/tsan/rtl/tsan_stat.h +++ b/lib/tsan/rtl/tsan_stat.h @@ -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, |