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