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