1 //===-- tsan_mman.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 #include "sanitizer_common/sanitizer_allocator_checks.h" 13 #include "sanitizer_common/sanitizer_allocator_interface.h" 14 #include "sanitizer_common/sanitizer_allocator_report.h" 15 #include "sanitizer_common/sanitizer_common.h" 16 #include "sanitizer_common/sanitizer_errno.h" 17 #include "sanitizer_common/sanitizer_placement_new.h" 18 #include "tsan_mman.h" 19 #include "tsan_rtl.h" 20 #include "tsan_report.h" 21 #include "tsan_flags.h" 22 23 namespace __tsan { 24 25 struct MapUnmapCallback { 26 void OnMap(uptr p, uptr size) const { } 27 void OnUnmap(uptr p, uptr size) const { 28 // We are about to unmap a chunk of user memory. 29 // Mark the corresponding shadow memory as not needed. 30 DontNeedShadowFor(p, size); 31 // Mark the corresponding meta shadow memory as not needed. 32 // Note the block does not contain any meta info at this point 33 // (this happens after free). 34 const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize; 35 const uptr kPageSize = GetPageSizeCached() * kMetaRatio; 36 // Block came from LargeMmapAllocator, so must be large. 37 // We rely on this in the calculations below. 38 CHECK_GE(size, 2 * kPageSize); 39 uptr diff = RoundUp(p, kPageSize) - p; 40 if (diff != 0) { 41 p += diff; 42 size -= diff; 43 } 44 diff = p + size - RoundDown(p + size, kPageSize); 45 if (diff != 0) 46 size -= diff; 47 uptr p_meta = (uptr)MemToMeta(p); 48 ReleaseMemoryPagesToOS(p_meta, p_meta + size / kMetaRatio); 49 } 50 }; 51 52 static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64); 53 Allocator *allocator() { 54 return reinterpret_cast<Allocator*>(&allocator_placeholder); 55 } 56 57 struct GlobalProc { 58 Mutex mtx; 59 Processor *proc; 60 // This mutex represents the internal allocator combined for 61 // the purposes of deadlock detection. The internal allocator 62 // uses multiple mutexes, moreover they are locked only occasionally 63 // and they are spin mutexes which don't support deadlock detection. 64 // So we use this fake mutex to serve as a substitute for these mutexes. 65 CheckedMutex internal_alloc_mtx; 66 67 GlobalProc() 68 : mtx(MutexTypeGlobalProc), 69 proc(ProcCreate()), 70 internal_alloc_mtx(MutexTypeInternalAlloc) {} 71 }; 72 73 static char global_proc_placeholder[sizeof(GlobalProc)] ALIGNED(64); 74 GlobalProc *global_proc() { 75 return reinterpret_cast<GlobalProc*>(&global_proc_placeholder); 76 } 77 78 static void InternalAllocAccess() { 79 global_proc()->internal_alloc_mtx.Lock(); 80 global_proc()->internal_alloc_mtx.Unlock(); 81 } 82 83 ScopedGlobalProcessor::ScopedGlobalProcessor() { 84 GlobalProc *gp = global_proc(); 85 ThreadState *thr = cur_thread(); 86 if (thr->proc()) 87 return; 88 // If we don't have a proc, use the global one. 89 // There are currently only two known case where this path is triggered: 90 // __interceptor_free 91 // __nptl_deallocate_tsd 92 // start_thread 93 // clone 94 // and: 95 // ResetRange 96 // __interceptor_munmap 97 // __deallocate_stack 98 // start_thread 99 // clone 100 // Ideally, we destroy thread state (and unwire proc) when a thread actually 101 // exits (i.e. when we join/wait it). Then we would not need the global proc 102 gp->mtx.Lock(); 103 ProcWire(gp->proc, thr); 104 } 105 106 ScopedGlobalProcessor::~ScopedGlobalProcessor() { 107 GlobalProc *gp = global_proc(); 108 ThreadState *thr = cur_thread(); 109 if (thr->proc() != gp->proc) 110 return; 111 ProcUnwire(gp->proc, thr); 112 gp->mtx.Unlock(); 113 } 114 115 void AllocatorLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { 116 global_proc()->mtx.Lock(); 117 global_proc()->internal_alloc_mtx.Lock(); 118 InternalAllocatorLock(); 119 } 120 121 void AllocatorUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS { 122 InternalAllocatorUnlock(); 123 global_proc()->internal_alloc_mtx.Unlock(); 124 global_proc()->mtx.Unlock(); 125 } 126 127 static constexpr uptr kMaxAllowedMallocSize = 1ull << 40; 128 static uptr max_user_defined_malloc_size; 129 130 void InitializeAllocator() { 131 SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); 132 allocator()->Init(common_flags()->allocator_release_to_os_interval_ms); 133 max_user_defined_malloc_size = common_flags()->max_allocation_size_mb 134 ? common_flags()->max_allocation_size_mb 135 << 20 136 : kMaxAllowedMallocSize; 137 } 138 139 void InitializeAllocatorLate() { 140 new(global_proc()) GlobalProc(); 141 } 142 143 void AllocatorProcStart(Processor *proc) { 144 allocator()->InitCache(&proc->alloc_cache); 145 internal_allocator()->InitCache(&proc->internal_alloc_cache); 146 } 147 148 void AllocatorProcFinish(Processor *proc) { 149 allocator()->DestroyCache(&proc->alloc_cache); 150 internal_allocator()->DestroyCache(&proc->internal_alloc_cache); 151 } 152 153 void AllocatorPrintStats() { 154 allocator()->PrintStats(); 155 } 156 157 static void SignalUnsafeCall(ThreadState *thr, uptr pc) { 158 if (atomic_load_relaxed(&thr->in_signal_handler) == 0 || 159 !ShouldReport(thr, ReportTypeSignalUnsafe)) 160 return; 161 VarSizeStackTrace stack; 162 ObtainCurrentStack(thr, pc, &stack); 163 if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack)) 164 return; 165 ThreadRegistryLock l(&ctx->thread_registry); 166 ScopedReport rep(ReportTypeSignalUnsafe); 167 rep.AddStack(stack, true); 168 OutputReport(thr, rep); 169 } 170 171 172 void *user_alloc_internal(ThreadState *thr, uptr pc, uptr sz, uptr align, 173 bool signal) { 174 if (sz >= kMaxAllowedMallocSize || align >= kMaxAllowedMallocSize || 175 sz > max_user_defined_malloc_size) { 176 if (AllocatorMayReturnNull()) 177 return nullptr; 178 uptr malloc_limit = 179 Min(kMaxAllowedMallocSize, max_user_defined_malloc_size); 180 GET_STACK_TRACE_FATAL(thr, pc); 181 ReportAllocationSizeTooBig(sz, malloc_limit, &stack); 182 } 183 if (UNLIKELY(IsRssLimitExceeded())) { 184 if (AllocatorMayReturnNull()) 185 return nullptr; 186 GET_STACK_TRACE_FATAL(thr, pc); 187 ReportRssLimitExceeded(&stack); 188 } 189 void *p = allocator()->Allocate(&thr->proc()->alloc_cache, sz, align); 190 if (UNLIKELY(!p)) { 191 SetAllocatorOutOfMemory(); 192 if (AllocatorMayReturnNull()) 193 return nullptr; 194 GET_STACK_TRACE_FATAL(thr, pc); 195 ReportOutOfMemory(sz, &stack); 196 } 197 if (ctx && ctx->initialized) 198 OnUserAlloc(thr, pc, (uptr)p, sz, true); 199 if (signal) 200 SignalUnsafeCall(thr, pc); 201 return p; 202 } 203 204 void user_free(ThreadState *thr, uptr pc, void *p, bool signal) { 205 ScopedGlobalProcessor sgp; 206 if (ctx && ctx->initialized) 207 OnUserFree(thr, pc, (uptr)p, true); 208 allocator()->Deallocate(&thr->proc()->alloc_cache, p); 209 if (signal) 210 SignalUnsafeCall(thr, pc); 211 } 212 213 void *user_alloc(ThreadState *thr, uptr pc, uptr sz) { 214 return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, kDefaultAlignment)); 215 } 216 217 void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) { 218 if (UNLIKELY(CheckForCallocOverflow(size, n))) { 219 if (AllocatorMayReturnNull()) 220 return SetErrnoOnNull(nullptr); 221 GET_STACK_TRACE_FATAL(thr, pc); 222 ReportCallocOverflow(n, size, &stack); 223 } 224 void *p = user_alloc_internal(thr, pc, n * size); 225 if (p) 226 internal_memset(p, 0, n * size); 227 return SetErrnoOnNull(p); 228 } 229 230 void *user_reallocarray(ThreadState *thr, uptr pc, void *p, uptr size, uptr n) { 231 if (UNLIKELY(CheckForCallocOverflow(size, n))) { 232 if (AllocatorMayReturnNull()) 233 return SetErrnoOnNull(nullptr); 234 GET_STACK_TRACE_FATAL(thr, pc); 235 ReportReallocArrayOverflow(size, n, &stack); 236 } 237 return user_realloc(thr, pc, p, size * n); 238 } 239 240 void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) { 241 DPrintf("#%d: alloc(%zu) = 0x%zx\n", thr->tid, sz, p); 242 ctx->metamap.AllocBlock(thr, pc, p, sz); 243 if (write && thr->ignore_reads_and_writes == 0 && thr->is_inited) 244 MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); 245 else 246 MemoryResetRange(thr, pc, (uptr)p, sz); 247 } 248 249 void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write) { 250 CHECK_NE(p, (void*)0); 251 uptr sz = ctx->metamap.FreeBlock(thr->proc(), p); 252 DPrintf("#%d: free(0x%zx, %zu)\n", thr->tid, p, sz); 253 if (write && thr->ignore_reads_and_writes == 0 && thr->is_inited) 254 MemoryRangeFreed(thr, pc, (uptr)p, sz); 255 } 256 257 void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { 258 // FIXME: Handle "shrinking" more efficiently, 259 // it seems that some software actually does this. 260 if (!p) 261 return SetErrnoOnNull(user_alloc_internal(thr, pc, sz)); 262 if (!sz) { 263 user_free(thr, pc, p); 264 return nullptr; 265 } 266 void *new_p = user_alloc_internal(thr, pc, sz); 267 if (new_p) { 268 uptr old_sz = user_alloc_usable_size(p); 269 internal_memcpy(new_p, p, min(old_sz, sz)); 270 user_free(thr, pc, p); 271 } 272 return SetErrnoOnNull(new_p); 273 } 274 275 void *user_memalign(ThreadState *thr, uptr pc, uptr align, uptr sz) { 276 if (UNLIKELY(!IsPowerOfTwo(align))) { 277 errno = errno_EINVAL; 278 if (AllocatorMayReturnNull()) 279 return nullptr; 280 GET_STACK_TRACE_FATAL(thr, pc); 281 ReportInvalidAllocationAlignment(align, &stack); 282 } 283 return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, align)); 284 } 285 286 int user_posix_memalign(ThreadState *thr, uptr pc, void **memptr, uptr align, 287 uptr sz) { 288 if (UNLIKELY(!CheckPosixMemalignAlignment(align))) { 289 if (AllocatorMayReturnNull()) 290 return errno_EINVAL; 291 GET_STACK_TRACE_FATAL(thr, pc); 292 ReportInvalidPosixMemalignAlignment(align, &stack); 293 } 294 void *ptr = user_alloc_internal(thr, pc, sz, align); 295 if (UNLIKELY(!ptr)) 296 // OOM error is already taken care of by user_alloc_internal. 297 return errno_ENOMEM; 298 CHECK(IsAligned((uptr)ptr, align)); 299 *memptr = ptr; 300 return 0; 301 } 302 303 void *user_aligned_alloc(ThreadState *thr, uptr pc, uptr align, uptr sz) { 304 if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(align, sz))) { 305 errno = errno_EINVAL; 306 if (AllocatorMayReturnNull()) 307 return nullptr; 308 GET_STACK_TRACE_FATAL(thr, pc); 309 ReportInvalidAlignedAllocAlignment(sz, align, &stack); 310 } 311 return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, align)); 312 } 313 314 void *user_valloc(ThreadState *thr, uptr pc, uptr sz) { 315 return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, GetPageSizeCached())); 316 } 317 318 void *user_pvalloc(ThreadState *thr, uptr pc, uptr sz) { 319 uptr PageSize = GetPageSizeCached(); 320 if (UNLIKELY(CheckForPvallocOverflow(sz, PageSize))) { 321 errno = errno_ENOMEM; 322 if (AllocatorMayReturnNull()) 323 return nullptr; 324 GET_STACK_TRACE_FATAL(thr, pc); 325 ReportPvallocOverflow(sz, &stack); 326 } 327 // pvalloc(0) should allocate one page. 328 sz = sz ? RoundUpTo(sz, PageSize) : PageSize; 329 return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, PageSize)); 330 } 331 332 uptr user_alloc_usable_size(const void *p) { 333 if (p == 0) 334 return 0; 335 MBlock *b = ctx->metamap.GetBlock((uptr)p); 336 if (!b) 337 return 0; // Not a valid pointer. 338 if (b->siz == 0) 339 return 1; // Zero-sized allocations are actually 1 byte. 340 return b->siz; 341 } 342 343 void invoke_malloc_hook(void *ptr, uptr size) { 344 ThreadState *thr = cur_thread(); 345 if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) 346 return; 347 RunMallocHooks(ptr, size); 348 } 349 350 void invoke_free_hook(void *ptr) { 351 ThreadState *thr = cur_thread(); 352 if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) 353 return; 354 RunFreeHooks(ptr); 355 } 356 357 void *Alloc(uptr sz) { 358 ThreadState *thr = cur_thread(); 359 if (thr->nomalloc) { 360 thr->nomalloc = 0; // CHECK calls internal_malloc(). 361 CHECK(0); 362 } 363 InternalAllocAccess(); 364 return InternalAlloc(sz, &thr->proc()->internal_alloc_cache); 365 } 366 367 void FreeImpl(void *p) { 368 ThreadState *thr = cur_thread(); 369 if (thr->nomalloc) { 370 thr->nomalloc = 0; // CHECK calls internal_malloc(). 371 CHECK(0); 372 } 373 InternalAllocAccess(); 374 InternalFree(p, &thr->proc()->internal_alloc_cache); 375 } 376 377 } // namespace __tsan 378 379 using namespace __tsan; 380 381 extern "C" { 382 uptr __sanitizer_get_current_allocated_bytes() { 383 uptr stats[AllocatorStatCount]; 384 allocator()->GetStats(stats); 385 return stats[AllocatorStatAllocated]; 386 } 387 388 uptr __sanitizer_get_heap_size() { 389 uptr stats[AllocatorStatCount]; 390 allocator()->GetStats(stats); 391 return stats[AllocatorStatMapped]; 392 } 393 394 uptr __sanitizer_get_free_bytes() { 395 return 1; 396 } 397 398 uptr __sanitizer_get_unmapped_bytes() { 399 return 1; 400 } 401 402 uptr __sanitizer_get_estimated_allocated_size(uptr size) { 403 return size; 404 } 405 406 int __sanitizer_get_ownership(const void *p) { 407 return allocator()->GetBlockBegin(p) != 0; 408 } 409 410 uptr __sanitizer_get_allocated_size(const void *p) { 411 return user_alloc_usable_size(p); 412 } 413 414 void __tsan_on_thread_idle() { 415 ThreadState *thr = cur_thread(); 416 thr->clock.ResetCached(&thr->proc()->clock_cache); 417 thr->last_sleep_clock.ResetCached(&thr->proc()->clock_cache); 418 allocator()->SwallowCache(&thr->proc()->alloc_cache); 419 internal_allocator()->SwallowCache(&thr->proc()->internal_alloc_cache); 420 ctx->metamap.OnProcIdle(thr->proc()); 421 } 422 } // extern "C" 423