Home | History | Annotate | Download | only in rtl
      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 
     18 namespace __tsan {
     19 
     20 const int kTableSizeL1 = 1024;
     21 const int kTableSizeL2 = 1024;
     22 const int kTableSize = kTableSizeL1 * kTableSizeL2;
     23 
     24 struct FdSync {
     25   atomic_uint64_t rc;
     26 };
     27 
     28 struct FdDesc {
     29   FdSync *sync;
     30   int creation_tid;
     31   u32 creation_stack;
     32 };
     33 
     34 struct 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 
     43 static FdContext fdctx;
     44 
     45 static bool bogusfd(int fd) {
     46   // Apparently a bogus fd value.
     47   return fd < 0 || fd >= kTableSize;
     48 }
     49 
     50 static FdSync *allocsync(ThreadState *thr, uptr pc) {
     51   FdSync *s = (FdSync*)user_alloc(thr, pc, sizeof(FdSync));
     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);
     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(thr, pc, size);
     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);
     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   FdDesc *d = fddesc(thr, pc, fd);
     95   // As a matter of fact, we don't intercept all close calls.
     96   // See e.g. libc __res_iclose().
     97   if (d->sync) {
     98     unref(thr, pc, d->sync);
     99     d->sync = 0;
    100   }
    101   if (flags()->io_sync == 0) {
    102     unref(thr, pc, s);
    103   } else if (flags()->io_sync == 1) {
    104     d->sync = s;
    105   } else if (flags()->io_sync == 2) {
    106     unref(thr, pc, s);
    107     d->sync = &fdctx.globsync;
    108   }
    109   d->creation_tid = thr->tid;
    110   d->creation_stack = CurrentStackId(thr, pc);
    111   // To catch races between fd usage and open.
    112   MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
    113 }
    114 
    115 void FdInit() {
    116   atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed);
    117   atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed);
    118   atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed);
    119 }
    120 
    121 void FdOnFork(ThreadState *thr, uptr pc) {
    122   // On fork() we need to reset all fd's, because the child is going
    123   // close all them, and that will cause races between previous read/write
    124   // and the close.
    125   for (int l1 = 0; l1 < kTableSizeL1; l1++) {
    126     FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
    127     if (tab == 0)
    128       break;
    129     for (int l2 = 0; l2 < kTableSizeL2; l2++) {
    130       FdDesc *d = &tab[l2];
    131       MemoryResetRange(thr, pc, (uptr)d, 8);
    132     }
    133   }
    134 }
    135 
    136 bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) {
    137   for (int l1 = 0; l1 < kTableSizeL1; l1++) {
    138     FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
    139     if (tab == 0)
    140       break;
    141     if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) {
    142       int l2 = (addr - (uptr)tab) / sizeof(FdDesc);
    143       FdDesc *d = &tab[l2];
    144       *fd = l1 * kTableSizeL1 + l2;
    145       *tid = d->creation_tid;
    146       *stack = d->creation_stack;
    147       return true;
    148     }
    149   }
    150   return false;
    151 }
    152 
    153 void FdAcquire(ThreadState *thr, uptr pc, int fd) {
    154   if (bogusfd(fd))
    155     return;
    156   FdDesc *d = fddesc(thr, pc, fd);
    157   FdSync *s = d->sync;
    158   DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s);
    159   MemoryRead(thr, pc, (uptr)d, kSizeLog8);
    160   if (s)
    161     Acquire(thr, pc, (uptr)s);
    162 }
    163 
    164 void FdRelease(ThreadState *thr, uptr pc, int fd) {
    165   if (bogusfd(fd))
    166     return;
    167   FdDesc *d = fddesc(thr, pc, fd);
    168   FdSync *s = d->sync;
    169   DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s);
    170   MemoryRead(thr, pc, (uptr)d, kSizeLog8);
    171   if (s)
    172     Release(thr, pc, (uptr)s);
    173 }
    174 
    175 void FdAccess(ThreadState *thr, uptr pc, int fd) {
    176   DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd);
    177   if (bogusfd(fd))
    178     return;
    179   FdDesc *d = fddesc(thr, pc, fd);
    180   MemoryRead(thr, pc, (uptr)d, kSizeLog8);
    181 }
    182 
    183 void FdClose(ThreadState *thr, uptr pc, int fd) {
    184   DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
    185   if (bogusfd(fd))
    186     return;
    187   FdDesc *d = fddesc(thr, pc, fd);
    188   // To catch races between fd usage and close.
    189   MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
    190   // We need to clear it, because if we do not intercept any call out there
    191   // that creates fd, we will hit false postives.
    192   MemoryResetRange(thr, pc, (uptr)d, 8);
    193   unref(thr, pc, d->sync);
    194   d->sync = 0;
    195   d->creation_tid = 0;
    196   d->creation_stack = 0;
    197 }
    198 
    199 void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
    200   DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd);
    201   if (bogusfd(fd))
    202     return;
    203   init(thr, pc, fd, &fdctx.filesync);
    204 }
    205 
    206 void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) {
    207   DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
    208   if (bogusfd(oldfd) || bogusfd(newfd))
    209     return;
    210   // Ignore the case when user dups not yet connected socket.
    211   FdDesc *od = fddesc(thr, pc, oldfd);
    212   MemoryRead(thr, pc, (uptr)od, kSizeLog8);
    213   FdClose(thr, pc, newfd);
    214   init(thr, pc, newfd, ref(od->sync));
    215 }
    216 
    217 void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
    218   DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);
    219   FdSync *s = allocsync(thr, pc);
    220   init(thr, pc, rfd, ref(s));
    221   init(thr, pc, wfd, ref(s));
    222   unref(thr, pc, s);
    223 }
    224 
    225 void FdEventCreate(ThreadState *thr, uptr pc, int fd) {
    226   DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);
    227   if (bogusfd(fd))
    228     return;
    229   init(thr, pc, fd, allocsync(thr, pc));
    230 }
    231 
    232 void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {
    233   DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd);
    234   if (bogusfd(fd))
    235     return;
    236   init(thr, pc, fd, 0);
    237 }
    238 
    239 void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) {
    240   DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd);
    241   if (bogusfd(fd))
    242     return;
    243   init(thr, pc, fd, 0);
    244 }
    245 
    246 void FdPollCreate(ThreadState *thr, uptr pc, int fd) {
    247   DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);
    248   if (bogusfd(fd))
    249     return;
    250   init(thr, pc, fd, allocsync(thr, pc));
    251 }
    252 
    253 void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
    254   DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd);
    255   if (bogusfd(fd))
    256     return;
    257   // It can be a UDP socket.
    258   init(thr, pc, fd, &fdctx.socksync);
    259 }
    260 
    261 void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
    262   DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd);
    263   if (bogusfd(fd))
    264     return;
    265   // Synchronize connect->accept.
    266   Acquire(thr, pc, (uptr)&fdctx.connectsync);
    267   init(thr, pc, newfd, &fdctx.socksync);
    268 }
    269 
    270 void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) {
    271   DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd);
    272   if (bogusfd(fd))
    273     return;
    274   // Synchronize connect->accept.
    275   Release(thr, pc, (uptr)&fdctx.connectsync);
    276 }
    277 
    278 void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {
    279   DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd);
    280   if (bogusfd(fd))
    281     return;
    282   init(thr, pc, fd, &fdctx.socksync);
    283 }
    284 
    285 uptr File2addr(const char *path) {
    286   (void)path;
    287   static u64 addr;
    288   return (uptr)&addr;
    289 }
    290 
    291 uptr Dir2addr(const char *path) {
    292   (void)path;
    293   static u64 addr;
    294   return (uptr)&addr;
    295 }
    296 
    297 }  //  namespace __tsan
    298