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-exception 6 // 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 20 namespace __tsan { 21 22 const int kTableSizeL1 = 1024; 23 const int kTableSizeL2 = 1024; 24 const int kTableSize = kTableSizeL1 * kTableSizeL2; 25 26 struct FdSync { 27 atomic_uint64_t rc; 28 }; 29 30 struct FdDesc { 31 FdSync *sync; 32 // This is used to establish write -> epoll_wait synchronization 33 // 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 40 struct 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 49 static FdContext fdctx; 50 51 static bool bogusfd(int fd) { 52 // Apparently a bogus fd value. 53 return fd < 0 || fd >= kTableSize; 54 } 55 56 static 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 63 static 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 69 static 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 80 static 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 else 94 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. 101 static 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 139 void 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 145 void FdOnFork(ThreadState *thr, uptr pc) { 146 // On fork() we need to reset all fd's, because the child is going 147 // close all them, and that will cause races between previous read/write 148 // 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 160 bool 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 178 void 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 189 void 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 202 void 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 210 void 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 atomic 217 // 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 legitimate 227 // cases where write would lead to false positives: 228 // 1. Some software dups a closed pipe in place of a socket before 229 // closing 230 // 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 real 233 // 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 there 239 // 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 253 void 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 260 void 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 271 void 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 279 void 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 286 void 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 293 void 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 300 void 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 307 void 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 management 318 // 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 331 void 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 339 void 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 348 void 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 356 void 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 363 uptr File2addr(const char *path) { 364 (void)path; 365 static u64 addr; 366 return (uptr)&addr; 367 } 368 369 uptr Dir2addr(const char *path) { 370 (void)path; 371 static u64 addr; 372 return (uptr)&addr; 373 } 374 375 } // namespace __tsan 376