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 Tid creation_tid; 33 StackID creation_stack; 34 }; 35 36 struct FdContext { 37 atomic_uintptr_t tab[kTableSizeL1]; 38 // Addresses used for synchronization. 39 FdSync globsync; 40 FdSync filesync; 41 FdSync socksync; 42 u64 connectsync; 43 }; 44 45 static FdContext fdctx; 46 47 static bool bogusfd(int fd) { 48 // Apparently a bogus fd value. 49 return fd < 0 || fd >= kTableSize; 50 } 51 52 static FdSync *allocsync(ThreadState *thr, uptr pc) { 53 FdSync *s = (FdSync*)user_alloc_internal(thr, pc, sizeof(FdSync), 54 kDefaultAlignment, false); 55 atomic_store(&s->rc, 1, memory_order_relaxed); 56 return s; 57 } 58 59 static FdSync *ref(FdSync *s) { 60 if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) 61 atomic_fetch_add(&s->rc, 1, memory_order_relaxed); 62 return s; 63 } 64 65 static void unref(ThreadState *thr, uptr pc, FdSync *s) { 66 if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) { 67 if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) { 68 CHECK_NE(s, &fdctx.globsync); 69 CHECK_NE(s, &fdctx.filesync); 70 CHECK_NE(s, &fdctx.socksync); 71 user_free(thr, pc, s, false); 72 } 73 } 74 } 75 76 static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { 77 CHECK_GE(fd, 0); 78 CHECK_LT(fd, kTableSize); 79 atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2]; 80 uptr l1 = atomic_load(pl1, memory_order_consume); 81 if (l1 == 0) { 82 uptr size = kTableSizeL2 * sizeof(FdDesc); 83 // We need this to reside in user memory to properly catch races on it. 84 void *p = user_alloc_internal(thr, pc, size, kDefaultAlignment, false); 85 internal_memset(p, 0, size); 86 MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size); 87 if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel)) 88 l1 = (uptr)p; 89 else 90 user_free(thr, pc, p, false); 91 } 92 FdDesc *fds = reinterpret_cast<FdDesc *>(l1); 93 return &fds[fd % kTableSizeL2]; 94 } 95 96 // pd must be already ref'ed. 97 static void init(ThreadState *thr, uptr pc, int fd, FdSync *s, 98 bool write = true) { 99 FdDesc *d = fddesc(thr, pc, fd); 100 // As a matter of fact, we don't intercept all close calls. 101 // See e.g. libc __res_iclose(). 102 if (d->sync) { 103 unref(thr, pc, d->sync); 104 d->sync = 0; 105 } 106 if (flags()->io_sync == 0) { 107 unref(thr, pc, s); 108 } else if (flags()->io_sync == 1) { 109 d->sync = s; 110 } else if (flags()->io_sync == 2) { 111 unref(thr, pc, s); 112 d->sync = &fdctx.globsync; 113 } 114 d->creation_tid = thr->tid; 115 d->creation_stack = CurrentStackId(thr, pc); 116 // This prevents false positives on fd_close_norace3.cpp test. 117 // The mechanics of the false positive are not completely clear, 118 // but it happens only if global reset is enabled (flush_memory_ms=1) 119 // and may be related to lost writes during asynchronous MADV_DONTNEED. 120 SlotLocker locker(thr); 121 if (write) { 122 // To catch races between fd usage and open. 123 MemoryRangeImitateWrite(thr, pc, (uptr)d, 8); 124 } else { 125 // See the dup-related comment in FdClose. 126 MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead | kAccessSlotLocked); 127 } 128 } 129 130 void FdInit() { 131 atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed); 132 atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed); 133 atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed); 134 } 135 136 void FdOnFork(ThreadState *thr, uptr pc) { 137 // On fork() we need to reset all fd's, because the child is going 138 // close all them, and that will cause races between previous read/write 139 // and the close. 140 for (int l1 = 0; l1 < kTableSizeL1; l1++) { 141 FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed); 142 if (tab == 0) 143 break; 144 for (int l2 = 0; l2 < kTableSizeL2; l2++) { 145 FdDesc *d = &tab[l2]; 146 MemoryResetRange(thr, pc, (uptr)d, 8); 147 } 148 } 149 } 150 151 bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack) { 152 for (int l1 = 0; l1 < kTableSizeL1; l1++) { 153 FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed); 154 if (tab == 0) 155 break; 156 if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) { 157 int l2 = (addr - (uptr)tab) / sizeof(FdDesc); 158 FdDesc *d = &tab[l2]; 159 *fd = l1 * kTableSizeL1 + l2; 160 *tid = d->creation_tid; 161 *stack = d->creation_stack; 162 return true; 163 } 164 } 165 return false; 166 } 167 168 void FdAcquire(ThreadState *thr, uptr pc, int fd) { 169 if (bogusfd(fd)) 170 return; 171 FdDesc *d = fddesc(thr, pc, fd); 172 FdSync *s = d->sync; 173 DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); 174 MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead); 175 if (s) 176 Acquire(thr, pc, (uptr)s); 177 } 178 179 void FdRelease(ThreadState *thr, uptr pc, int fd) { 180 if (bogusfd(fd)) 181 return; 182 FdDesc *d = fddesc(thr, pc, fd); 183 FdSync *s = d->sync; 184 DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); 185 MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead); 186 if (s) 187 Release(thr, pc, (uptr)s); 188 } 189 190 void FdAccess(ThreadState *thr, uptr pc, int fd) { 191 DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd); 192 if (bogusfd(fd)) 193 return; 194 FdDesc *d = fddesc(thr, pc, fd); 195 MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead); 196 } 197 198 void FdClose(ThreadState *thr, uptr pc, int fd, bool write) { 199 DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); 200 if (bogusfd(fd)) 201 return; 202 FdDesc *d = fddesc(thr, pc, fd); 203 { 204 // Need to lock the slot to make MemoryAccess and MemoryResetRange atomic 205 // with respect to global reset. See the comment in MemoryRangeFreed. 206 SlotLocker locker(thr); 207 if (!MustIgnoreInterceptor(thr)) { 208 if (write) { 209 // To catch races between fd usage and close. 210 MemoryAccess(thr, pc, (uptr)d, 8, 211 kAccessWrite | kAccessCheckOnly | kAccessSlotLocked); 212 } else { 213 // This path is used only by dup2/dup3 calls. 214 // We do read instead of write because there is a number of legitimate 215 // cases where write would lead to false positives: 216 // 1. Some software dups a closed pipe in place of a socket before 217 // closing 218 // the socket (to prevent races actually). 219 // 2. Some daemons dup /dev/null in place of stdin/stdout. 220 // On the other hand we have not seen cases when write here catches real 221 // bugs. 222 MemoryAccess(thr, pc, (uptr)d, 8, 223 kAccessRead | kAccessCheckOnly | kAccessSlotLocked); 224 } 225 } 226 // We need to clear it, because if we do not intercept any call out there 227 // that creates fd, we will hit false postives. 228 MemoryResetRange(thr, pc, (uptr)d, 8); 229 } 230 unref(thr, pc, d->sync); 231 d->sync = 0; 232 d->creation_tid = kInvalidTid; 233 d->creation_stack = kInvalidStackID; 234 } 235 236 void FdFileCreate(ThreadState *thr, uptr pc, int fd) { 237 DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd); 238 if (bogusfd(fd)) 239 return; 240 init(thr, pc, fd, &fdctx.filesync); 241 } 242 243 void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write) { 244 DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); 245 if (bogusfd(oldfd) || bogusfd(newfd)) 246 return; 247 // Ignore the case when user dups not yet connected socket. 248 FdDesc *od = fddesc(thr, pc, oldfd); 249 MemoryAccess(thr, pc, (uptr)od, 8, kAccessRead); 250 FdClose(thr, pc, newfd, write); 251 init(thr, pc, newfd, ref(od->sync), write); 252 } 253 254 void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) { 255 DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd); 256 FdSync *s = allocsync(thr, pc); 257 init(thr, pc, rfd, ref(s)); 258 init(thr, pc, wfd, ref(s)); 259 unref(thr, pc, s); 260 } 261 262 void FdEventCreate(ThreadState *thr, uptr pc, int fd) { 263 DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd); 264 if (bogusfd(fd)) 265 return; 266 init(thr, pc, fd, allocsync(thr, pc)); 267 } 268 269 void FdSignalCreate(ThreadState *thr, uptr pc, int fd) { 270 DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd); 271 if (bogusfd(fd)) 272 return; 273 init(thr, pc, fd, 0); 274 } 275 276 void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) { 277 DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd); 278 if (bogusfd(fd)) 279 return; 280 init(thr, pc, fd, 0); 281 } 282 283 void FdPollCreate(ThreadState *thr, uptr pc, int fd) { 284 DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd); 285 if (bogusfd(fd)) 286 return; 287 init(thr, pc, fd, allocsync(thr, pc)); 288 } 289 290 void FdSocketCreate(ThreadState *thr, uptr pc, int fd) { 291 DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd); 292 if (bogusfd(fd)) 293 return; 294 // It can be a UDP socket. 295 init(thr, pc, fd, &fdctx.socksync); 296 } 297 298 void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { 299 DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd); 300 if (bogusfd(fd)) 301 return; 302 // Synchronize connect->accept. 303 Acquire(thr, pc, (uptr)&fdctx.connectsync); 304 init(thr, pc, newfd, &fdctx.socksync); 305 } 306 307 void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) { 308 DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd); 309 if (bogusfd(fd)) 310 return; 311 // Synchronize connect->accept. 312 Release(thr, pc, (uptr)&fdctx.connectsync); 313 } 314 315 void FdSocketConnect(ThreadState *thr, uptr pc, int fd) { 316 DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd); 317 if (bogusfd(fd)) 318 return; 319 init(thr, pc, fd, &fdctx.socksync); 320 } 321 322 uptr File2addr(const char *path) { 323 (void)path; 324 static u64 addr; 325 return (uptr)&addr; 326 } 327 328 uptr Dir2addr(const char *path) { 329 (void)path; 330 static u64 addr; 331 return (uptr)&addr; 332 } 333 334 } // namespace __tsan 335