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), kDefaultAlignment, 52 false); 53 atomic_store(&s->rc, 1, memory_order_relaxed); 54 return s; 55 } 56 57 static FdSync *ref(FdSync *s) { 58 if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) 59 atomic_fetch_add(&s->rc, 1, memory_order_relaxed); 60 return s; 61 } 62 63 static void unref(ThreadState *thr, uptr pc, FdSync *s) { 64 if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) { 65 if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) { 66 CHECK_NE(s, &fdctx.globsync); 67 CHECK_NE(s, &fdctx.filesync); 68 CHECK_NE(s, &fdctx.socksync); 69 user_free(thr, pc, s, false); 70 } 71 } 72 } 73 74 static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { 75 CHECK_GE(fd, 0); 76 CHECK_LT(fd, kTableSize); 77 atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2]; 78 uptr l1 = atomic_load(pl1, memory_order_consume); 79 if (l1 == 0) { 80 uptr size = kTableSizeL2 * sizeof(FdDesc); 81 // We need this to reside in user memory to properly catch races on it. 82 void *p = user_alloc(thr, pc, size, kDefaultAlignment, false); 83 internal_memset(p, 0, size); 84 MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size); 85 if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel)) 86 l1 = (uptr)p; 87 else 88 user_free(thr, pc, p, false); 89 } 90 return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT 91 } 92 93 // pd must be already ref'ed. 94 static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) { 95 FdDesc *d = fddesc(thr, pc, fd); 96 // As a matter of fact, we don't intercept all close calls. 97 // See e.g. libc __res_iclose(). 98 if (d->sync) { 99 unref(thr, pc, d->sync); 100 d->sync = 0; 101 } 102 if (flags()->io_sync == 0) { 103 unref(thr, pc, s); 104 } else if (flags()->io_sync == 1) { 105 d->sync = s; 106 } else if (flags()->io_sync == 2) { 107 unref(thr, pc, s); 108 d->sync = &fdctx.globsync; 109 } 110 d->creation_tid = thr->tid; 111 d->creation_stack = CurrentStackId(thr, pc); 112 // To catch races between fd usage and open. 113 MemoryRangeImitateWrite(thr, pc, (uptr)d, 8); 114 } 115 116 void FdInit() { 117 atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed); 118 atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed); 119 atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed); 120 } 121 122 void FdOnFork(ThreadState *thr, uptr pc) { 123 // On fork() we need to reset all fd's, because the child is going 124 // close all them, and that will cause races between previous read/write 125 // and the close. 126 for (int l1 = 0; l1 < kTableSizeL1; l1++) { 127 FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed); 128 if (tab == 0) 129 break; 130 for (int l2 = 0; l2 < kTableSizeL2; l2++) { 131 FdDesc *d = &tab[l2]; 132 MemoryResetRange(thr, pc, (uptr)d, 8); 133 } 134 } 135 } 136 137 bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) { 138 for (int l1 = 0; l1 < kTableSizeL1; l1++) { 139 FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed); 140 if (tab == 0) 141 break; 142 if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) { 143 int l2 = (addr - (uptr)tab) / sizeof(FdDesc); 144 FdDesc *d = &tab[l2]; 145 *fd = l1 * kTableSizeL1 + l2; 146 *tid = d->creation_tid; 147 *stack = d->creation_stack; 148 return true; 149 } 150 } 151 return false; 152 } 153 154 void FdAcquire(ThreadState *thr, uptr pc, int fd) { 155 if (bogusfd(fd)) 156 return; 157 FdDesc *d = fddesc(thr, pc, fd); 158 FdSync *s = d->sync; 159 DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); 160 MemoryRead(thr, pc, (uptr)d, kSizeLog8); 161 if (s) 162 Acquire(thr, pc, (uptr)s); 163 } 164 165 void FdRelease(ThreadState *thr, uptr pc, int fd) { 166 if (bogusfd(fd)) 167 return; 168 FdDesc *d = fddesc(thr, pc, fd); 169 FdSync *s = d->sync; 170 DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); 171 MemoryRead(thr, pc, (uptr)d, kSizeLog8); 172 if (s) 173 Release(thr, pc, (uptr)s); 174 } 175 176 void FdAccess(ThreadState *thr, uptr pc, int fd) { 177 DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd); 178 if (bogusfd(fd)) 179 return; 180 FdDesc *d = fddesc(thr, pc, fd); 181 MemoryRead(thr, pc, (uptr)d, kSizeLog8); 182 } 183 184 void FdClose(ThreadState *thr, uptr pc, int fd) { 185 DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); 186 if (bogusfd(fd)) 187 return; 188 FdDesc *d = fddesc(thr, pc, fd); 189 // To catch races between fd usage and close. 190 MemoryWrite(thr, pc, (uptr)d, kSizeLog8); 191 // We need to clear it, because if we do not intercept any call out there 192 // that creates fd, we will hit false postives. 193 MemoryResetRange(thr, pc, (uptr)d, 8); 194 unref(thr, pc, d->sync); 195 d->sync = 0; 196 d->creation_tid = 0; 197 d->creation_stack = 0; 198 } 199 200 void FdFileCreate(ThreadState *thr, uptr pc, int fd) { 201 DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd); 202 if (bogusfd(fd)) 203 return; 204 init(thr, pc, fd, &fdctx.filesync); 205 } 206 207 void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { 208 DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); 209 if (bogusfd(oldfd) || bogusfd(newfd)) 210 return; 211 // Ignore the case when user dups not yet connected socket. 212 FdDesc *od = fddesc(thr, pc, oldfd); 213 MemoryRead(thr, pc, (uptr)od, kSizeLog8); 214 FdClose(thr, pc, newfd); 215 init(thr, pc, newfd, ref(od->sync)); 216 } 217 218 void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) { 219 DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd); 220 FdSync *s = allocsync(thr, pc); 221 init(thr, pc, rfd, ref(s)); 222 init(thr, pc, wfd, ref(s)); 223 unref(thr, pc, s); 224 } 225 226 void FdEventCreate(ThreadState *thr, uptr pc, int fd) { 227 DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd); 228 if (bogusfd(fd)) 229 return; 230 init(thr, pc, fd, allocsync(thr, pc)); 231 } 232 233 void FdSignalCreate(ThreadState *thr, uptr pc, int fd) { 234 DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd); 235 if (bogusfd(fd)) 236 return; 237 init(thr, pc, fd, 0); 238 } 239 240 void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) { 241 DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd); 242 if (bogusfd(fd)) 243 return; 244 init(thr, pc, fd, 0); 245 } 246 247 void FdPollCreate(ThreadState *thr, uptr pc, int fd) { 248 DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd); 249 if (bogusfd(fd)) 250 return; 251 init(thr, pc, fd, allocsync(thr, pc)); 252 } 253 254 void FdSocketCreate(ThreadState *thr, uptr pc, int fd) { 255 DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd); 256 if (bogusfd(fd)) 257 return; 258 // It can be a UDP socket. 259 init(thr, pc, fd, &fdctx.socksync); 260 } 261 262 void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { 263 DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd); 264 if (bogusfd(fd)) 265 return; 266 // Synchronize connect->accept. 267 Acquire(thr, pc, (uptr)&fdctx.connectsync); 268 init(thr, pc, newfd, &fdctx.socksync); 269 } 270 271 void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) { 272 DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd); 273 if (bogusfd(fd)) 274 return; 275 // Synchronize connect->accept. 276 Release(thr, pc, (uptr)&fdctx.connectsync); 277 } 278 279 void FdSocketConnect(ThreadState *thr, uptr pc, int fd) { 280 DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd); 281 if (bogusfd(fd)) 282 return; 283 init(thr, pc, fd, &fdctx.socksync); 284 } 285 286 uptr File2addr(const char *path) { 287 (void)path; 288 static u64 addr; 289 return (uptr)&addr; 290 } 291 292 uptr Dir2addr(const char *path) { 293 (void)path; 294 static u64 addr; 295 return (uptr)&addr; 296 } 297 298 } // namespace __tsan 299