diff --git a/compiler-rt/lib/tsan/rtl/tsan_fd.h b/compiler-rt/lib/tsan/rtl/tsan_fd.h --- a/compiler-rt/lib/tsan/rtl/tsan_fd.h +++ b/compiler-rt/lib/tsan/rtl/tsan_fd.h @@ -54,7 +54,7 @@ void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd); void FdSocketConnecting(ThreadState *thr, uptr pc, int fd); void FdSocketConnect(ThreadState *thr, uptr pc, int fd); -bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack); +bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack, bool *closed); void FdOnFork(ThreadState *thr, uptr pc); uptr File2addr(const char *path); diff --git a/compiler-rt/lib/tsan/rtl/tsan_fd.cpp b/compiler-rt/lib/tsan/rtl/tsan_fd.cpp --- a/compiler-rt/lib/tsan/rtl/tsan_fd.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_fd.cpp @@ -34,6 +34,7 @@ atomic_uintptr_t aux_sync; // FdSync* Tid creation_tid; StackID creation_stack; + bool closed; }; struct FdContext { @@ -120,6 +121,7 @@ } d->creation_tid = thr->tid; d->creation_stack = CurrentStackId(thr, pc); + d->closed = false; // This prevents false positives on fd_close_norace3.cpp test. // The mechanics of the false positive are not completely clear, // but it happens only if global reset is enabled (flush_memory_ms=1) @@ -155,7 +157,7 @@ } } -bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack) { +bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack, bool *closed) { for (int l1 = 0; l1 < kTableSizeL1; l1++) { FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed); if (tab == 0) @@ -166,6 +168,7 @@ *fd = l1 * kTableSizeL1 + l2; *tid = d->creation_tid; *stack = d->creation_stack; + *closed = d->closed; return true; } } @@ -242,8 +245,9 @@ reinterpret_cast( atomic_load(&d->aux_sync, memory_order_relaxed))); atomic_store(&d->aux_sync, 0, memory_order_relaxed); - d->creation_tid = kInvalidTid; - d->creation_stack = kInvalidStackID; + d->closed = true; + d->creation_tid = thr->tid; + d->creation_stack = CurrentStackId(thr, pc); } void FdFileCreate(ThreadState *thr, uptr pc, int fd) { diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.h b/compiler-rt/lib/tsan/rtl/tsan_report.h --- a/compiler-rt/lib/tsan/rtl/tsan_report.h +++ b/compiler-rt/lib/tsan/rtl/tsan_report.h @@ -76,6 +76,7 @@ uptr external_tag = 0; Tid tid = kInvalidTid; int fd = 0; + bool fd_closed = false; bool suppressable = false; ReportStack *stack = nullptr; }; diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_report.cpp --- a/compiler-rt/lib/tsan/rtl/tsan_report.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_report.cpp @@ -200,8 +200,9 @@ } else if (loc->type == ReportLocationTLS) { Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid)); } else if (loc->type == ReportLocationFD) { - Printf(" Location is file descriptor %d created by %s at:\n", - loc->fd, thread_name(thrbuf, loc->tid)); + Printf(" Location is file descriptor %d %s by %s at:\n", loc->fd, + loc->fd_closed ? "destroyed" : "created", + thread_name(thrbuf, loc->tid)); print_stack = true; } Printf("%s", d.Default()); diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp @@ -281,9 +281,11 @@ int fd = -1; Tid creat_tid = kInvalidTid; StackID creat_stack = 0; - if (FdLocation(addr, &fd, &creat_tid, &creat_stack)) { + bool closed = false; + if (FdLocation(addr, &fd, &creat_tid, &creat_stack, &closed)) { auto *loc = New(); loc->type = ReportLocationFD; + loc->fd_closed = closed; loc->fd = fd; loc->tid = creat_tid; loc->stack = SymbolizeStackId(creat_stack); diff --git a/compiler-rt/test/tsan/fd_location_closed.cpp b/compiler-rt/test/tsan/fd_location_closed.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/tsan/fd_location_closed.cpp @@ -0,0 +1,30 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t |& FileCheck %s +#include "test.h" + +#include + +void *Thread(void *x) { + int fd = (long)x; + barrier_wait(&barrier); + char buf; + read(fd, &buf, 1); + close(fd); + return NULL; +} + +int main() { + const int N = 10; + barrier_init(&barrier, N); + int fd = open("/dev/random", O_RDONLY); + pthread_t t[N]; + for (auto &thr : t) + pthread_create(&thr, NULL, Thread, (void *)(long)fd); + + for (auto &thr : t) + pthread_join(thr, NULL); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Location is file descriptor {{[0-9]+}} destroyed by thread +// CHECK: #0 close +// CHECK: #1 Thread