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