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