tsan_fd.cc revision 175599be65a5c08551bbe0359a7ca1aadb23ec8b
1//===-- tsan_fd.cc --------------------------------------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of ThreadSanitizer (TSan), a race detector.
11//
12//===----------------------------------------------------------------------===//
13
14#include "tsan_fd.h"
15#include "tsan_rtl.h"
16#include <sanitizer_common/sanitizer_atomic.h>
17
18namespace __tsan {
19
20const int kTableSizeL1 = 1024;
21const int kTableSizeL2 = 1024;
22const int kTableSize = kTableSizeL1 * kTableSizeL2;
23
24struct FdSync {
25  atomic_uint64_t rc;
26};
27
28struct FdDesc {
29  FdSync *sync;
30  int creation_tid;
31  u32 creation_stack;
32};
33
34struct FdContext {
35  atomic_uintptr_t tab[kTableSizeL1];
36  // Addresses used for synchronization.
37  FdSync globsync;
38  FdSync filesync;
39  FdSync socksync;
40  u64 connectsync;
41};
42
43static FdContext fdctx;
44
45static bool bogusfd(int fd) {
46  // Apparently a bogus fd value.
47  return fd < 0 || fd >= (1 << 30);
48}
49
50static FdSync *allocsync() {
51  FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync));
52  atomic_store(&s->rc, 1, memory_order_relaxed);
53  return s;
54}
55
56static FdSync *ref(FdSync *s) {
57  if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1)
58    atomic_fetch_add(&s->rc, 1, memory_order_relaxed);
59  return s;
60}
61
62static void unref(ThreadState *thr, uptr pc, FdSync *s) {
63  if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) {
64    if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) {
65      CHECK_NE(s, &fdctx.globsync);
66      CHECK_NE(s, &fdctx.filesync);
67      CHECK_NE(s, &fdctx.socksync);
68      SyncVar *v = CTX()->synctab.GetAndRemove(thr, pc, (uptr)s);
69      if (v)
70        DestroyAndFree(v);
71      internal_free(s);
72    }
73  }
74}
75
76static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {
77  CHECK_GE(fd, 0);
78  CHECK_LT(fd, kTableSize);
79  atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2];
80  uptr l1 = atomic_load(pl1, memory_order_consume);
81  if (l1 == 0) {
82    uptr size = kTableSizeL2 * sizeof(FdDesc);
83    // We need this to reside in user memory to properly catch races on it.
84    void *p = user_alloc(thr, pc, size);
85    internal_memset(p, 0, size);
86    MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);
87    if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))
88      l1 = (uptr)p;
89    else
90      user_free(thr, pc, p);
91  }
92  return &((FdDesc*)l1)[fd % kTableSizeL2];  // NOLINT
93}
94
95// pd must be already ref'ed.
96static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) {
97  FdDesc *d = fddesc(thr, pc, fd);
98  // As a matter of fact, we don't intercept all close calls.
99  // See e.g. libc __res_iclose().
100  if (d->sync) {
101    unref(thr, pc, d->sync);
102    d->sync = 0;
103  }
104  if (flags()->io_sync == 0) {
105    unref(thr, pc, s);
106  } else if (flags()->io_sync == 1) {
107    d->sync = s;
108  } else if (flags()->io_sync == 2) {
109    unref(thr, pc, s);
110    d->sync = &fdctx.globsync;
111  }
112  d->creation_tid = thr->tid;
113  d->creation_stack = CurrentStackId(thr, pc);
114  // To catch races between fd usage and open.
115  MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
116}
117
118void FdInit() {
119  atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed);
120  atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed);
121  atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed);
122}
123
124void FdOnFork(ThreadState *thr, uptr pc) {
125  // On fork() we need to reset all fd's, because the child is going
126  // close all them, and that will cause races between previous read/write
127  // and the close.
128  for (int l1 = 0; l1 < kTableSizeL1; l1++) {
129    FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
130    if (tab == 0)
131      break;
132    for (int l2 = 0; l2 < kTableSizeL2; l2++) {
133      FdDesc *d = &tab[l2];
134      MemoryResetRange(thr, pc, (uptr)d, 8);
135    }
136  }
137}
138
139bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) {
140  for (int l1 = 0; l1 < kTableSizeL1; l1++) {
141    FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
142    if (tab == 0)
143      break;
144    if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) {
145      int l2 = (addr - (uptr)tab) / sizeof(FdDesc);
146      FdDesc *d = &tab[l2];
147      *fd = l1 * kTableSizeL1 + l2;
148      *tid = d->creation_tid;
149      *stack = d->creation_stack;
150      return true;
151    }
152  }
153  return false;
154}
155
156void FdAcquire(ThreadState *thr, uptr pc, int fd) {
157  if (bogusfd(fd))
158    return;
159  FdDesc *d = fddesc(thr, pc, fd);
160  FdSync *s = d->sync;
161  DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s);
162  MemoryRead(thr, pc, (uptr)d, kSizeLog8);
163  if (s)
164    Acquire(thr, pc, (uptr)s);
165}
166
167void FdRelease(ThreadState *thr, uptr pc, int fd) {
168  if (bogusfd(fd))
169    return;
170  FdDesc *d = fddesc(thr, pc, fd);
171  FdSync *s = d->sync;
172  DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s);
173  MemoryRead(thr, pc, (uptr)d, kSizeLog8);
174  if (s)
175    Release(thr, pc, (uptr)s);
176}
177
178void FdAccess(ThreadState *thr, uptr pc, int fd) {
179  DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd);
180  if (bogusfd(fd))
181    return;
182  FdDesc *d = fddesc(thr, pc, fd);
183  MemoryRead(thr, pc, (uptr)d, kSizeLog8);
184}
185
186void FdClose(ThreadState *thr, uptr pc, int fd) {
187  DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
188  if (bogusfd(fd))
189    return;
190  FdDesc *d = fddesc(thr, pc, fd);
191  // To catch races between fd usage and close.
192  MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
193  // We need to clear it, because if we do not intercept any call out there
194  // that creates fd, we will hit false postives.
195  MemoryResetRange(thr, pc, (uptr)d, 8);
196  unref(thr, pc, d->sync);
197  d->sync = 0;
198  d->creation_tid = 0;
199  d->creation_stack = 0;
200}
201
202void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
203  DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd);
204  if (bogusfd(fd))
205    return;
206  init(thr, pc, fd, &fdctx.filesync);
207}
208
209void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) {
210  DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
211  if (bogusfd(oldfd) || bogusfd(newfd))
212    return;
213  // Ignore the case when user dups not yet connected socket.
214  FdDesc *od = fddesc(thr, pc, oldfd);
215  MemoryRead(thr, pc, (uptr)od, kSizeLog8);
216  FdClose(thr, pc, newfd);
217  init(thr, pc, newfd, ref(od->sync));
218}
219
220void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
221  DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);
222  FdSync *s = allocsync();
223  init(thr, pc, rfd, ref(s));
224  init(thr, pc, wfd, ref(s));
225  unref(thr, pc, s);
226}
227
228void FdEventCreate(ThreadState *thr, uptr pc, int fd) {
229  DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);
230  if (bogusfd(fd))
231    return;
232  init(thr, pc, fd, allocsync());
233}
234
235void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {
236  DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd);
237  if (bogusfd(fd))
238    return;
239  init(thr, pc, fd, 0);
240}
241
242void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) {
243  DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd);
244  if (bogusfd(fd))
245    return;
246  init(thr, pc, fd, 0);
247}
248
249void FdPollCreate(ThreadState *thr, uptr pc, int fd) {
250  DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);
251  if (bogusfd(fd))
252    return;
253  init(thr, pc, fd, allocsync());
254}
255
256void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
257  DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd);
258  if (bogusfd(fd))
259    return;
260  // It can be a UDP socket.
261  init(thr, pc, fd, &fdctx.socksync);
262}
263
264void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
265  DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd);
266  if (bogusfd(fd))
267    return;
268  // Synchronize connect->accept.
269  Acquire(thr, pc, (uptr)&fdctx.connectsync);
270  init(thr, pc, newfd, &fdctx.socksync);
271}
272
273void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) {
274  DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd);
275  if (bogusfd(fd))
276    return;
277  // Synchronize connect->accept.
278  Release(thr, pc, (uptr)&fdctx.connectsync);
279}
280
281void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {
282  DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd);
283  if (bogusfd(fd))
284    return;
285  init(thr, pc, fd, &fdctx.socksync);
286}
287
288uptr File2addr(char *path) {
289  (void)path;
290  static u64 addr;
291  return (uptr)&addr;
292}
293
294uptr Dir2addr(char *path) {
295  (void)path;
296  static u64 addr;
297  return (uptr)&addr;
298}
299
300}  //  namespace __tsan
301