brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.5 KiB · ab295a6 Raw
376 lines · cpp
1//===-- tsan_fd.cpp -------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of ThreadSanitizer (TSan), a race detector.10//11//===----------------------------------------------------------------------===//12 13#include "tsan_fd.h"14 15#include <sanitizer_common/sanitizer_atomic.h>16 17#include "tsan_interceptors.h"18#include "tsan_rtl.h"19 20namespace __tsan {21 22const int kTableSizeL1 = 1024;23const int kTableSizeL2 = 1024;24const int kTableSize = kTableSizeL1 * kTableSizeL2;25 26struct FdSync {27  atomic_uint64_t rc;28};29 30struct FdDesc {31  FdSync *sync;32  // This is used to establish write -> epoll_wait synchronization33  // where epoll_wait receives notification about the write.34  atomic_uintptr_t aux_sync;  // FdSync*35  Tid creation_tid;36  StackID creation_stack;37  bool closed;38};39 40struct FdContext {41  atomic_uintptr_t tab[kTableSizeL1];42  // Addresses used for synchronization.43  FdSync globsync;44  FdSync filesync;45  FdSync socksync;46  u64 connectsync;47};48 49static FdContext fdctx;50 51static bool bogusfd(int fd) {52  // Apparently a bogus fd value.53  return fd < 0 || fd >= kTableSize;54}55 56static FdSync *allocsync(ThreadState *thr, uptr pc) {57  FdSync *s = (FdSync*)user_alloc_internal(thr, pc, sizeof(FdSync),58      kDefaultAlignment, false);59  atomic_store(&s->rc, 1, memory_order_relaxed);60  return s;61}62 63static FdSync *ref(FdSync *s) {64  if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1)65    atomic_fetch_add(&s->rc, 1, memory_order_relaxed);66  return s;67}68 69static void unref(ThreadState *thr, uptr pc, FdSync *s) {70  if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) {71    if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) {72      CHECK_NE(s, &fdctx.globsync);73      CHECK_NE(s, &fdctx.filesync);74      CHECK_NE(s, &fdctx.socksync);75      user_free(thr, pc, s, false);76    }77  }78}79 80static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {81  CHECK_GE(fd, 0);82  CHECK_LT(fd, kTableSize);83  atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2];84  uptr l1 = atomic_load(pl1, memory_order_consume);85  if (l1 == 0) {86    uptr size = kTableSizeL2 * sizeof(FdDesc);87    // We need this to reside in user memory to properly catch races on it.88    void *p = user_alloc_internal(thr, pc, size, kDefaultAlignment, false);89    internal_memset(p, 0, size);90    MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);91    if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))92      l1 = (uptr)p;93    else94      user_free(thr, pc, p, false);95  }96  FdDesc *fds = reinterpret_cast<FdDesc *>(l1);97  return &fds[fd % kTableSizeL2];98}99 100// pd must be already ref'ed.101static void init(ThreadState *thr, uptr pc, int fd, FdSync *s,102    bool write = true) {103  FdDesc *d = fddesc(thr, pc, fd);104  // As a matter of fact, we don't intercept all close calls.105  // See e.g. libc __res_iclose().106  if (d->sync) {107    unref(thr, pc, d->sync);108    d->sync = 0;109  }110  unref(thr, pc,111        reinterpret_cast<FdSync *>(112            atomic_load(&d->aux_sync, memory_order_relaxed)));113  atomic_store(&d->aux_sync, 0, memory_order_relaxed);114  if (flags()->io_sync == 0) {115    unref(thr, pc, s);116  } else if (flags()->io_sync == 1) {117    d->sync = s;118  } else if (flags()->io_sync == 2) {119    unref(thr, pc, s);120    d->sync = &fdctx.globsync;121  }122  d->creation_tid = thr->tid;123  d->creation_stack = CurrentStackId(thr, pc);124  d->closed = false;125  // This prevents false positives on fd_close_norace3.cpp test.126  // The mechanics of the false positive are not completely clear,127  // but it happens only if global reset is enabled (flush_memory_ms=1)128  // and may be related to lost writes during asynchronous MADV_DONTNEED.129  SlotLocker locker(thr);130  if (write) {131    // To catch races between fd usage and open.132    MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);133  } else {134    // See the dup-related comment in FdClose.135    MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead | kAccessSlotLocked);136  }137}138 139void FdInit() {140  atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed);141  atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed);142  atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed);143}144 145void FdOnFork(ThreadState *thr, uptr pc) {146  // On fork() we need to reset all fd's, because the child is going147  // close all them, and that will cause races between previous read/write148  // and the close.149  for (int l1 = 0; l1 < kTableSizeL1; l1++) {150    FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);151    if (tab == 0)152      break;153    for (int l2 = 0; l2 < kTableSizeL2; l2++) {154      FdDesc *d = &tab[l2];155      MemoryResetRange(thr, pc, (uptr)d, 8);156    }157  }158}159 160bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack, bool *closed) {161  for (int l1 = 0; l1 < kTableSizeL1; l1++) {162    FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);163    if (tab == 0)164      break;165    if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) {166      int l2 = (addr - (uptr)tab) / sizeof(FdDesc);167      FdDesc *d = &tab[l2];168      *fd = l1 * kTableSizeL1 + l2;169      *tid = d->creation_tid;170      *stack = d->creation_stack;171      *closed = d->closed;172      return true;173    }174  }175  return false;176}177 178void FdAcquire(ThreadState *thr, uptr pc, int fd) {179  if (bogusfd(fd))180    return;181  FdDesc *d = fddesc(thr, pc, fd);182  FdSync *s = d->sync;183  DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s);184  MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);185  if (s)186    Acquire(thr, pc, (uptr)s);187}188 189void FdRelease(ThreadState *thr, uptr pc, int fd) {190  if (bogusfd(fd))191    return;192  FdDesc *d = fddesc(thr, pc, fd);193  FdSync *s = d->sync;194  DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s);195  MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);196  if (s)197    Release(thr, pc, (uptr)s);198  if (uptr aux_sync = atomic_load(&d->aux_sync, memory_order_acquire))199    Release(thr, pc, aux_sync);200}201 202void FdAccess(ThreadState *thr, uptr pc, int fd) {203  DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd);204  if (bogusfd(fd))205    return;206  FdDesc *d = fddesc(thr, pc, fd);207  MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);208}209 210void FdClose(ThreadState *thr, uptr pc, int fd, bool write) {211  DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);212  if (bogusfd(fd))213    return;214  FdDesc *d = fddesc(thr, pc, fd);215  {216    // Need to lock the slot to make MemoryAccess and MemoryResetRange atomic217    // with respect to global reset. See the comment in MemoryRangeFreed.218    SlotLocker locker(thr);219    if (!MustIgnoreInterceptor(thr)) {220      if (write) {221        // To catch races between fd usage and close.222        MemoryAccess(thr, pc, (uptr)d, 8,223                     kAccessWrite | kAccessCheckOnly | kAccessSlotLocked);224      } else {225        // This path is used only by dup2/dup3 calls.226        // We do read instead of write because there is a number of legitimate227        // cases where write would lead to false positives:228        // 1. Some software dups a closed pipe in place of a socket before229        // closing230        //    the socket (to prevent races actually).231        // 2. Some daemons dup /dev/null in place of stdin/stdout.232        // On the other hand we have not seen cases when write here catches real233        // bugs.234        MemoryAccess(thr, pc, (uptr)d, 8,235                     kAccessRead | kAccessCheckOnly | kAccessSlotLocked);236      }237    }238    // We need to clear it, because if we do not intercept any call out there239    // that creates fd, we will hit false postives.240    MemoryResetRange(thr, pc, (uptr)d, 8);241  }242  unref(thr, pc, d->sync);243  d->sync = 0;244  unref(thr, pc,245        reinterpret_cast<FdSync *>(246            atomic_load(&d->aux_sync, memory_order_relaxed)));247  atomic_store(&d->aux_sync, 0, memory_order_relaxed);248  d->closed = true;249  d->creation_tid = thr->tid;250  d->creation_stack = CurrentStackId(thr, pc);251}252 253void FdFileCreate(ThreadState *thr, uptr pc, int fd) {254  DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd);255  if (bogusfd(fd))256    return;257  init(thr, pc, fd, &fdctx.filesync);258}259 260void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write) {261  DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);262  if (bogusfd(oldfd) || bogusfd(newfd))263    return;264  // Ignore the case when user dups not yet connected socket.265  FdDesc *od = fddesc(thr, pc, oldfd);266  MemoryAccess(thr, pc, (uptr)od, 8, kAccessRead);267  FdClose(thr, pc, newfd, write);268  init(thr, pc, newfd, ref(od->sync), write);269}270 271void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {272  DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);273  FdSync *s = allocsync(thr, pc);274  init(thr, pc, rfd, ref(s));275  init(thr, pc, wfd, ref(s));276  unref(thr, pc, s);277}278 279void FdEventCreate(ThreadState *thr, uptr pc, int fd) {280  DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);281  if (bogusfd(fd))282    return;283  init(thr, pc, fd, allocsync(thr, pc));284}285 286void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {287  DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd);288  if (bogusfd(fd))289    return;290  init(thr, pc, fd, 0);291}292 293void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) {294  DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd);295  if (bogusfd(fd))296    return;297  init(thr, pc, fd, 0);298}299 300void FdPollCreate(ThreadState *thr, uptr pc, int fd) {301  DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);302  if (bogusfd(fd))303    return;304  init(thr, pc, fd, allocsync(thr, pc));305}306 307void FdPollAdd(ThreadState *thr, uptr pc, int epfd, int fd) {308  DPrintf("#%d: FdPollAdd(%d, %d)\n", thr->tid, epfd, fd);309  if (bogusfd(epfd) || bogusfd(fd))310    return;311  FdDesc *d = fddesc(thr, pc, fd);312  // Associate fd with epoll fd only once.313  // While an fd can be associated with multiple epolls at the same time,314  // or with different epolls during different phases of lifetime,315  // synchronization semantics (and examples) of this are unclear.316  // So we don't support this for now.317  // If we change the association, it will also create lifetime management318  // problem for FdRelease which accesses the aux_sync.319  if (atomic_load(&d->aux_sync, memory_order_relaxed))320    return;321  FdDesc *epd = fddesc(thr, pc, epfd);322  FdSync *s = epd->sync;323  if (!s)324    return;325  uptr cmp = 0;326  if (atomic_compare_exchange_strong(327          &d->aux_sync, &cmp, reinterpret_cast<uptr>(s), memory_order_release))328    ref(s);329}330 331void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {332  DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd);333  if (bogusfd(fd))334    return;335  // It can be a UDP socket.336  init(thr, pc, fd, &fdctx.socksync);337}338 339void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {340  DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd);341  if (bogusfd(fd))342    return;343  // Synchronize connect->accept.344  Acquire(thr, pc, (uptr)&fdctx.connectsync);345  init(thr, pc, newfd, &fdctx.socksync);346}347 348void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) {349  DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd);350  if (bogusfd(fd))351    return;352  // Synchronize connect->accept.353  Release(thr, pc, (uptr)&fdctx.connectsync);354}355 356void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {357  DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd);358  if (bogusfd(fd))359    return;360  init(thr, pc, fd, &fdctx.socksync);361}362 363uptr File2addr(const char *path) {364  (void)path;365  static u64 addr;366  return (uptr)&addr;367}368 369uptr Dir2addr(const char *path) {370  (void)path;371  static u64 addr;372  return (uptr)&addr;373}374 375}  //  namespace __tsan376