1 //===-- asan_allocator.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 AddressSanitizer, an address sanity checker. 10 // 11 // Implementation of ASan's memory allocator, 2-nd version. 12 // This variant uses the allocator from sanitizer_common, i.e. the one shared 13 // with ThreadSanitizer and MemorySanitizer. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "asan_allocator.h" 18 19 #include "asan_mapping.h" 20 #include "asan_poisoning.h" 21 #include "asan_report.h" 22 #include "asan_stack.h" 23 #include "asan_thread.h" 24 #include "lsan/lsan_common.h" 25 #include "sanitizer_common/sanitizer_allocator_checks.h" 26 #include "sanitizer_common/sanitizer_allocator_interface.h" 27 #include "sanitizer_common/sanitizer_errno.h" 28 #include "sanitizer_common/sanitizer_flags.h" 29 #include "sanitizer_common/sanitizer_internal_defs.h" 30 #include "sanitizer_common/sanitizer_list.h" 31 #include "sanitizer_common/sanitizer_quarantine.h" 32 #include "sanitizer_common/sanitizer_stackdepot.h" 33 34 namespace __asan { 35 36 // Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits. 37 // We use adaptive redzones: for larger allocation larger redzones are used. 38 static u32 RZLog2Size(u32 rz_log) { 39 CHECK_LT(rz_log, 8); 40 return 16 << rz_log; 41 } 42 43 static u32 RZSize2Log(u32 rz_size) { 44 CHECK_GE(rz_size, 16); 45 CHECK_LE(rz_size, 2048); 46 CHECK(IsPowerOfTwo(rz_size)); 47 u32 res = Log2(rz_size) - 4; 48 CHECK_EQ(rz_size, RZLog2Size(res)); 49 return res; 50 } 51 52 static AsanAllocator &get_allocator(); 53 54 static void AtomicContextStore(volatile atomic_uint64_t *atomic_context, 55 u32 tid, u32 stack) { 56 u64 context = tid; 57 context <<= 32; 58 context += stack; 59 atomic_store(atomic_context, context, memory_order_relaxed); 60 } 61 62 static void AtomicContextLoad(const volatile atomic_uint64_t *atomic_context, 63 u32 &tid, u32 &stack) { 64 u64 context = atomic_load(atomic_context, memory_order_relaxed); 65 stack = context; 66 context >>= 32; 67 tid = context; 68 } 69 70 // The memory chunk allocated from the underlying allocator looks like this: 71 // L L L L L L H H U U U U U U R R 72 // L -- left redzone words (0 or more bytes) 73 // H -- ChunkHeader (16 bytes), which is also a part of the left redzone. 74 // U -- user memory. 75 // R -- right redzone (0 or more bytes) 76 // ChunkBase consists of ChunkHeader and other bytes that overlap with user 77 // memory. 78 79 // If the left redzone is greater than the ChunkHeader size we store a magic 80 // value in the first uptr word of the memory block and store the address of 81 // ChunkBase in the next uptr. 82 // M B L L L L L L L L L H H U U U U U U 83 // | ^ 84 // ---------------------| 85 // M -- magic value kAllocBegMagic 86 // B -- address of ChunkHeader pointing to the first 'H' 87 static const uptr kAllocBegMagic = 0xCC6E96B9; 88 89 class ChunkHeader { 90 public: 91 atomic_uint8_t chunk_state; 92 u8 from_memalign : 1; 93 u8 alloc_type : 2; 94 u8 rz_log : 3; 95 u8 lsan_tag : 2; 96 97 // This field is used for small sizes. For large sizes it is equal to 98 // SizeClassMap::kMaxSize and the actual size is stored in the 99 // SecondaryAllocator's metadata. 100 u32 user_requested_size : 29; 101 // align < 8 -> 0 102 // else -> log2(min(align, 512)) - 2 103 u32 user_requested_alignment_log : 3; 104 105 private: 106 atomic_uint64_t alloc_context_id; 107 108 public: 109 void SetAllocContext(u32 tid, u32 stack) { 110 AtomicContextStore(&alloc_context_id, tid, stack); 111 } 112 113 void GetAllocContext(u32 &tid, u32 &stack) const { 114 AtomicContextLoad(&alloc_context_id, tid, stack); 115 } 116 }; 117 118 class ChunkBase : public ChunkHeader { 119 atomic_uint64_t free_context_id; 120 121 public: 122 void SetFreeContext(u32 tid, u32 stack) { 123 AtomicContextStore(&free_context_id, tid, stack); 124 } 125 126 void GetFreeContext(u32 &tid, u32 &stack) const { 127 AtomicContextLoad(&free_context_id, tid, stack); 128 } 129 }; 130 131 static const uptr kChunkHeaderSize = sizeof(ChunkHeader); 132 static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize; 133 COMPILER_CHECK(kChunkHeaderSize == 16); 134 COMPILER_CHECK(kChunkHeader2Size <= 16); 135 136 enum { 137 // Either just allocated by underlying allocator, but AsanChunk is not yet 138 // ready, or almost returned to undelying allocator and AsanChunk is already 139 // meaningless. 140 CHUNK_INVALID = 0, 141 // The chunk is allocated and not yet freed. 142 CHUNK_ALLOCATED = 2, 143 // The chunk was freed and put into quarantine zone. 144 CHUNK_QUARANTINE = 3, 145 }; 146 147 class AsanChunk : public ChunkBase { 148 public: 149 uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; } 150 uptr UsedSize(bool locked_version = false) { 151 if (user_requested_size != SizeClassMap::kMaxSize) 152 return user_requested_size; 153 return *reinterpret_cast<uptr *>( 154 get_allocator().GetMetaData(AllocBeg(locked_version))); 155 } 156 void *AllocBeg(bool locked_version = false) { 157 if (from_memalign) { 158 if (locked_version) 159 return get_allocator().GetBlockBeginFastLocked( 160 reinterpret_cast<void *>(this)); 161 return get_allocator().GetBlockBegin(reinterpret_cast<void *>(this)); 162 } 163 return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log)); 164 } 165 bool AddrIsInside(uptr addr, bool locked_version = false) { 166 return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); 167 } 168 }; 169 170 struct QuarantineCallback { 171 QuarantineCallback(AllocatorCache *cache, BufferedStackTrace *stack) 172 : cache_(cache), 173 stack_(stack) { 174 } 175 176 void Recycle(AsanChunk *m) { 177 u8 old_chunk_state = CHUNK_QUARANTINE; 178 if (!atomic_compare_exchange_strong(&m->chunk_state, &old_chunk_state, 179 CHUNK_INVALID, memory_order_acquire)) { 180 CHECK_EQ(old_chunk_state, CHUNK_QUARANTINE); 181 } 182 183 PoisonShadow(m->Beg(), 184 RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), 185 kAsanHeapLeftRedzoneMagic); 186 void *p = reinterpret_cast<void *>(m->AllocBeg()); 187 if (p != m) { 188 uptr *alloc_magic = reinterpret_cast<uptr *>(p); 189 CHECK_EQ(alloc_magic[0], kAllocBegMagic); 190 // Clear the magic value, as allocator internals may overwrite the 191 // contents of deallocated chunk, confusing GetAsanChunk lookup. 192 alloc_magic[0] = 0; 193 CHECK_EQ(alloc_magic[1], reinterpret_cast<uptr>(m)); 194 } 195 196 // Statistics. 197 AsanStats &thread_stats = GetCurrentThreadStats(); 198 thread_stats.real_frees++; 199 thread_stats.really_freed += m->UsedSize(); 200 201 get_allocator().Deallocate(cache_, p); 202 } 203 204 void *Allocate(uptr size) { 205 void *res = get_allocator().Allocate(cache_, size, 1); 206 // TODO(alekseys): Consider making quarantine OOM-friendly. 207 if (UNLIKELY(!res)) 208 ReportOutOfMemory(size, stack_); 209 return res; 210 } 211 212 void Deallocate(void *p) { 213 get_allocator().Deallocate(cache_, p); 214 } 215 216 private: 217 AllocatorCache* const cache_; 218 BufferedStackTrace* const stack_; 219 }; 220 221 typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine; 222 typedef AsanQuarantine::Cache QuarantineCache; 223 224 void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const { 225 PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); 226 // Statistics. 227 AsanStats &thread_stats = GetCurrentThreadStats(); 228 thread_stats.mmaps++; 229 thread_stats.mmaped += size; 230 } 231 void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const { 232 PoisonShadow(p, size, 0); 233 // We are about to unmap a chunk of user memory. 234 // Mark the corresponding shadow memory as not needed. 235 FlushUnneededASanShadowMemory(p, size); 236 // Statistics. 237 AsanStats &thread_stats = GetCurrentThreadStats(); 238 thread_stats.munmaps++; 239 thread_stats.munmaped += size; 240 } 241 242 // We can not use THREADLOCAL because it is not supported on some of the 243 // platforms we care about (OSX 10.6, Android). 244 // static THREADLOCAL AllocatorCache cache; 245 AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { 246 CHECK(ms); 247 return &ms->allocator_cache; 248 } 249 250 QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) { 251 CHECK(ms); 252 CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache)); 253 return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache); 254 } 255 256 void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) { 257 quarantine_size_mb = f->quarantine_size_mb; 258 thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb; 259 min_redzone = f->redzone; 260 max_redzone = f->max_redzone; 261 may_return_null = cf->allocator_may_return_null; 262 alloc_dealloc_mismatch = f->alloc_dealloc_mismatch; 263 release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms; 264 } 265 266 void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { 267 f->quarantine_size_mb = quarantine_size_mb; 268 f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb; 269 f->redzone = min_redzone; 270 f->max_redzone = max_redzone; 271 cf->allocator_may_return_null = may_return_null; 272 f->alloc_dealloc_mismatch = alloc_dealloc_mismatch; 273 cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms; 274 } 275 276 struct Allocator { 277 static const uptr kMaxAllowedMallocSize = 278 FIRST_32_SECOND_64(3UL << 30, 1ULL << 40); 279 280 AsanAllocator allocator; 281 AsanQuarantine quarantine; 282 StaticSpinMutex fallback_mutex; 283 AllocatorCache fallback_allocator_cache; 284 QuarantineCache fallback_quarantine_cache; 285 286 uptr max_user_defined_malloc_size; 287 atomic_uint8_t rss_limit_exceeded; 288 289 // ------------------- Options -------------------------- 290 atomic_uint16_t min_redzone; 291 atomic_uint16_t max_redzone; 292 atomic_uint8_t alloc_dealloc_mismatch; 293 294 // ------------------- Initialization ------------------------ 295 explicit Allocator(LinkerInitialized) 296 : quarantine(LINKER_INITIALIZED), 297 fallback_quarantine_cache(LINKER_INITIALIZED) {} 298 299 void CheckOptions(const AllocatorOptions &options) const { 300 CHECK_GE(options.min_redzone, 16); 301 CHECK_GE(options.max_redzone, options.min_redzone); 302 CHECK_LE(options.max_redzone, 2048); 303 CHECK(IsPowerOfTwo(options.min_redzone)); 304 CHECK(IsPowerOfTwo(options.max_redzone)); 305 } 306 307 void SharedInitCode(const AllocatorOptions &options) { 308 CheckOptions(options); 309 quarantine.Init((uptr)options.quarantine_size_mb << 20, 310 (uptr)options.thread_local_quarantine_size_kb << 10); 311 atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch, 312 memory_order_release); 313 atomic_store(&min_redzone, options.min_redzone, memory_order_release); 314 atomic_store(&max_redzone, options.max_redzone, memory_order_release); 315 } 316 317 void InitLinkerInitialized(const AllocatorOptions &options) { 318 SetAllocatorMayReturnNull(options.may_return_null); 319 allocator.InitLinkerInitialized(options.release_to_os_interval_ms); 320 SharedInitCode(options); 321 max_user_defined_malloc_size = common_flags()->max_allocation_size_mb 322 ? common_flags()->max_allocation_size_mb 323 << 20 324 : kMaxAllowedMallocSize; 325 } 326 327 bool RssLimitExceeded() { 328 return atomic_load(&rss_limit_exceeded, memory_order_relaxed); 329 } 330 331 void SetRssLimitExceeded(bool limit_exceeded) { 332 atomic_store(&rss_limit_exceeded, limit_exceeded, memory_order_relaxed); 333 } 334 335 void RePoisonChunk(uptr chunk) { 336 // This could be a user-facing chunk (with redzones), or some internal 337 // housekeeping chunk, like TransferBatch. Start by assuming the former. 338 AsanChunk *ac = GetAsanChunk((void *)chunk); 339 uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)chunk); 340 if (ac && atomic_load(&ac->chunk_state, memory_order_acquire) == 341 CHUNK_ALLOCATED) { 342 uptr beg = ac->Beg(); 343 uptr end = ac->Beg() + ac->UsedSize(true); 344 uptr chunk_end = chunk + allocated_size; 345 if (chunk < beg && beg < end && end <= chunk_end) { 346 // Looks like a valid AsanChunk in use, poison redzones only. 347 PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic); 348 uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY); 349 FastPoisonShadowPartialRightRedzone( 350 end_aligned_down, end - end_aligned_down, 351 chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic); 352 return; 353 } 354 } 355 356 // This is either not an AsanChunk or freed or quarantined AsanChunk. 357 // In either case, poison everything. 358 PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic); 359 } 360 361 void ReInitialize(const AllocatorOptions &options) { 362 SetAllocatorMayReturnNull(options.may_return_null); 363 allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms); 364 SharedInitCode(options); 365 366 // Poison all existing allocation's redzones. 367 if (CanPoisonMemory()) { 368 allocator.ForceLock(); 369 allocator.ForEachChunk( 370 [](uptr chunk, void *alloc) { 371 ((Allocator *)alloc)->RePoisonChunk(chunk); 372 }, 373 this); 374 allocator.ForceUnlock(); 375 } 376 } 377 378 void GetOptions(AllocatorOptions *options) const { 379 options->quarantine_size_mb = quarantine.GetSize() >> 20; 380 options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10; 381 options->min_redzone = atomic_load(&min_redzone, memory_order_acquire); 382 options->max_redzone = atomic_load(&max_redzone, memory_order_acquire); 383 options->may_return_null = AllocatorMayReturnNull(); 384 options->alloc_dealloc_mismatch = 385 atomic_load(&alloc_dealloc_mismatch, memory_order_acquire); 386 options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs(); 387 } 388 389 // -------------------- Helper methods. ------------------------- 390 uptr ComputeRZLog(uptr user_requested_size) { 391 u32 rz_log = user_requested_size <= 64 - 16 ? 0 392 : user_requested_size <= 128 - 32 ? 1 393 : user_requested_size <= 512 - 64 ? 2 394 : user_requested_size <= 4096 - 128 ? 3 395 : user_requested_size <= (1 << 14) - 256 ? 4 396 : user_requested_size <= (1 << 15) - 512 ? 5 397 : user_requested_size <= (1 << 16) - 1024 ? 6 398 : 7; 399 u32 hdr_log = RZSize2Log(RoundUpToPowerOfTwo(sizeof(ChunkHeader))); 400 u32 min_log = RZSize2Log(atomic_load(&min_redzone, memory_order_acquire)); 401 u32 max_log = RZSize2Log(atomic_load(&max_redzone, memory_order_acquire)); 402 return Min(Max(rz_log, Max(min_log, hdr_log)), Max(max_log, hdr_log)); 403 } 404 405 static uptr ComputeUserRequestedAlignmentLog(uptr user_requested_alignment) { 406 if (user_requested_alignment < 8) 407 return 0; 408 if (user_requested_alignment > 512) 409 user_requested_alignment = 512; 410 return Log2(user_requested_alignment) - 2; 411 } 412 413 static uptr ComputeUserAlignment(uptr user_requested_alignment_log) { 414 if (user_requested_alignment_log == 0) 415 return 0; 416 return 1LL << (user_requested_alignment_log + 2); 417 } 418 419 // We have an address between two chunks, and we want to report just one. 420 AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk, 421 AsanChunk *right_chunk) { 422 if (!left_chunk) 423 return right_chunk; 424 if (!right_chunk) 425 return left_chunk; 426 // Prefer an allocated chunk over freed chunk and freed chunk 427 // over available chunk. 428 u8 left_state = atomic_load(&left_chunk->chunk_state, memory_order_relaxed); 429 u8 right_state = 430 atomic_load(&right_chunk->chunk_state, memory_order_relaxed); 431 if (left_state != right_state) { 432 if (left_state == CHUNK_ALLOCATED) 433 return left_chunk; 434 if (right_state == CHUNK_ALLOCATED) 435 return right_chunk; 436 if (left_state == CHUNK_QUARANTINE) 437 return left_chunk; 438 if (right_state == CHUNK_QUARANTINE) 439 return right_chunk; 440 } 441 // Same chunk_state: choose based on offset. 442 sptr l_offset = 0, r_offset = 0; 443 CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); 444 CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); 445 if (l_offset < r_offset) 446 return left_chunk; 447 return right_chunk; 448 } 449 450 bool UpdateAllocationStack(uptr addr, BufferedStackTrace *stack) { 451 AsanChunk *m = GetAsanChunkByAddr(addr); 452 if (!m) return false; 453 if (atomic_load(&m->chunk_state, memory_order_acquire) != CHUNK_ALLOCATED) 454 return false; 455 if (m->Beg() != addr) return false; 456 AsanThread *t = GetCurrentThread(); 457 m->SetAllocContext(t ? t->tid() : 0, StackDepotPut(*stack)); 458 return true; 459 } 460 461 // -------------------- Allocation/Deallocation routines --------------- 462 void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack, 463 AllocType alloc_type, bool can_fill) { 464 if (UNLIKELY(!asan_inited)) 465 AsanInitFromRtl(); 466 if (RssLimitExceeded()) { 467 if (AllocatorMayReturnNull()) 468 return nullptr; 469 ReportRssLimitExceeded(stack); 470 } 471 Flags &fl = *flags(); 472 CHECK(stack); 473 const uptr min_alignment = SHADOW_GRANULARITY; 474 const uptr user_requested_alignment_log = 475 ComputeUserRequestedAlignmentLog(alignment); 476 if (alignment < min_alignment) 477 alignment = min_alignment; 478 if (size == 0) { 479 // We'd be happy to avoid allocating memory for zero-size requests, but 480 // some programs/tests depend on this behavior and assume that malloc 481 // would not return NULL even for zero-size allocations. Moreover, it 482 // looks like operator new should never return NULL, and results of 483 // consecutive "new" calls must be different even if the allocated size 484 // is zero. 485 size = 1; 486 } 487 CHECK(IsPowerOfTwo(alignment)); 488 uptr rz_log = ComputeRZLog(size); 489 uptr rz_size = RZLog2Size(rz_log); 490 uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); 491 uptr needed_size = rounded_size + rz_size; 492 if (alignment > min_alignment) 493 needed_size += alignment; 494 bool using_primary_allocator = true; 495 // If we are allocating from the secondary allocator, there will be no 496 // automatic right redzone, so add the right redzone manually. 497 if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) { 498 needed_size += rz_size; 499 using_primary_allocator = false; 500 } 501 CHECK(IsAligned(needed_size, min_alignment)); 502 if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize || 503 size > max_user_defined_malloc_size) { 504 if (AllocatorMayReturnNull()) { 505 Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n", 506 (void*)size); 507 return nullptr; 508 } 509 uptr malloc_limit = 510 Min(kMaxAllowedMallocSize, max_user_defined_malloc_size); 511 ReportAllocationSizeTooBig(size, needed_size, malloc_limit, stack); 512 } 513 514 AsanThread *t = GetCurrentThread(); 515 void *allocated; 516 if (t) { 517 AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); 518 allocated = allocator.Allocate(cache, needed_size, 8); 519 } else { 520 SpinMutexLock l(&fallback_mutex); 521 AllocatorCache *cache = &fallback_allocator_cache; 522 allocated = allocator.Allocate(cache, needed_size, 8); 523 } 524 if (UNLIKELY(!allocated)) { 525 SetAllocatorOutOfMemory(); 526 if (AllocatorMayReturnNull()) 527 return nullptr; 528 ReportOutOfMemory(size, stack); 529 } 530 531 if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) { 532 // Heap poisoning is enabled, but the allocator provides an unpoisoned 533 // chunk. This is possible if CanPoisonMemory() was false for some 534 // time, for example, due to flags()->start_disabled. 535 // Anyway, poison the block before using it for anything else. 536 uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated); 537 PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic); 538 } 539 540 uptr alloc_beg = reinterpret_cast<uptr>(allocated); 541 uptr alloc_end = alloc_beg + needed_size; 542 uptr beg_plus_redzone = alloc_beg + rz_size; 543 uptr user_beg = beg_plus_redzone; 544 if (!IsAligned(user_beg, alignment)) 545 user_beg = RoundUpTo(user_beg, alignment); 546 uptr user_end = user_beg + size; 547 CHECK_LE(user_end, alloc_end); 548 uptr chunk_beg = user_beg - kChunkHeaderSize; 549 AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); 550 m->alloc_type = alloc_type; 551 m->rz_log = rz_log; 552 m->from_memalign = user_beg != beg_plus_redzone; 553 if (alloc_beg != chunk_beg) { 554 CHECK_LE(alloc_beg + 2 * sizeof(uptr), chunk_beg); 555 reinterpret_cast<uptr *>(alloc_beg)[0] = kAllocBegMagic; 556 reinterpret_cast<uptr *>(alloc_beg)[1] = chunk_beg; 557 } 558 if (using_primary_allocator) { 559 CHECK(size); 560 m->user_requested_size = size; 561 CHECK(allocator.FromPrimary(allocated)); 562 } else { 563 CHECK(!allocator.FromPrimary(allocated)); 564 m->user_requested_size = SizeClassMap::kMaxSize; 565 uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated)); 566 meta[0] = size; 567 meta[1] = chunk_beg; 568 } 569 m->user_requested_alignment_log = user_requested_alignment_log; 570 571 m->SetAllocContext(t ? t->tid() : 0, StackDepotPut(*stack)); 572 573 uptr size_rounded_down_to_granularity = 574 RoundDownTo(size, SHADOW_GRANULARITY); 575 // Unpoison the bulk of the memory region. 576 if (size_rounded_down_to_granularity) 577 PoisonShadow(user_beg, size_rounded_down_to_granularity, 0); 578 // Deal with the end of the region if size is not aligned to granularity. 579 if (size != size_rounded_down_to_granularity && CanPoisonMemory()) { 580 u8 *shadow = 581 (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity); 582 *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; 583 } 584 585 AsanStats &thread_stats = GetCurrentThreadStats(); 586 thread_stats.mallocs++; 587 thread_stats.malloced += size; 588 thread_stats.malloced_redzones += needed_size - size; 589 if (needed_size > SizeClassMap::kMaxSize) 590 thread_stats.malloc_large++; 591 else 592 thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++; 593 594 void *res = reinterpret_cast<void *>(user_beg); 595 if (can_fill && fl.max_malloc_fill_size) { 596 uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); 597 REAL(memset)(res, fl.malloc_fill_byte, fill_size); 598 } 599 #if CAN_SANITIZE_LEAKS 600 m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored 601 : __lsan::kDirectlyLeaked; 602 #endif 603 // Must be the last mutation of metadata in this function. 604 atomic_store(&m->chunk_state, CHUNK_ALLOCATED, memory_order_release); 605 ASAN_MALLOC_HOOK(res, size); 606 return res; 607 } 608 609 // Set quarantine flag if chunk is allocated, issue ASan error report on 610 // available and quarantined chunks. Return true on success, false otherwise. 611 bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk *m, void *ptr, 612 BufferedStackTrace *stack) { 613 u8 old_chunk_state = CHUNK_ALLOCATED; 614 // Flip the chunk_state atomically to avoid race on double-free. 615 if (!atomic_compare_exchange_strong(&m->chunk_state, &old_chunk_state, 616 CHUNK_QUARANTINE, 617 memory_order_acquire)) { 618 ReportInvalidFree(ptr, old_chunk_state, stack); 619 // It's not safe to push a chunk in quarantine on invalid free. 620 return false; 621 } 622 CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); 623 // It was a user data. 624 m->SetFreeContext(kInvalidTid, 0); 625 return true; 626 } 627 628 // Expects the chunk to already be marked as quarantined by using 629 // AtomicallySetQuarantineFlagIfAllocated. 630 void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack) { 631 CHECK_EQ(atomic_load(&m->chunk_state, memory_order_relaxed), 632 CHUNK_QUARANTINE); 633 AsanThread *t = GetCurrentThread(); 634 m->SetFreeContext(t ? t->tid() : 0, StackDepotPut(*stack)); 635 636 Flags &fl = *flags(); 637 if (fl.max_free_fill_size > 0) { 638 // We have to skip the chunk header, it contains free_context_id. 639 uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size; 640 if (m->UsedSize() >= kChunkHeader2Size) { // Skip Header2 in user area. 641 uptr size_to_fill = m->UsedSize() - kChunkHeader2Size; 642 size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size); 643 REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill); 644 } 645 } 646 647 // Poison the region. 648 PoisonShadow(m->Beg(), 649 RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), 650 kAsanHeapFreeMagic); 651 652 AsanStats &thread_stats = GetCurrentThreadStats(); 653 thread_stats.frees++; 654 thread_stats.freed += m->UsedSize(); 655 656 // Push into quarantine. 657 if (t) { 658 AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); 659 AllocatorCache *ac = GetAllocatorCache(ms); 660 quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac, stack), m, 661 m->UsedSize()); 662 } else { 663 SpinMutexLock l(&fallback_mutex); 664 AllocatorCache *ac = &fallback_allocator_cache; 665 quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac, stack), 666 m, m->UsedSize()); 667 } 668 } 669 670 void Deallocate(void *ptr, uptr delete_size, uptr delete_alignment, 671 BufferedStackTrace *stack, AllocType alloc_type) { 672 uptr p = reinterpret_cast<uptr>(ptr); 673 if (p == 0) return; 674 675 uptr chunk_beg = p - kChunkHeaderSize; 676 AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); 677 678 // On Windows, uninstrumented DLLs may allocate memory before ASan hooks 679 // malloc. Don't report an invalid free in this case. 680 if (SANITIZER_WINDOWS && 681 !get_allocator().PointerIsMine(ptr)) { 682 if (!IsSystemHeapAddress(p)) 683 ReportFreeNotMalloced(p, stack); 684 return; 685 } 686 687 ASAN_FREE_HOOK(ptr); 688 689 // Must mark the chunk as quarantined before any changes to its metadata. 690 // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag. 691 if (!AtomicallySetQuarantineFlagIfAllocated(m, ptr, stack)) return; 692 693 if (m->alloc_type != alloc_type) { 694 if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) { 695 ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, 696 (AllocType)alloc_type); 697 } 698 } else { 699 if (flags()->new_delete_type_mismatch && 700 (alloc_type == FROM_NEW || alloc_type == FROM_NEW_BR) && 701 ((delete_size && delete_size != m->UsedSize()) || 702 ComputeUserRequestedAlignmentLog(delete_alignment) != 703 m->user_requested_alignment_log)) { 704 ReportNewDeleteTypeMismatch(p, delete_size, delete_alignment, stack); 705 } 706 } 707 708 QuarantineChunk(m, ptr, stack); 709 } 710 711 void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) { 712 CHECK(old_ptr && new_size); 713 uptr p = reinterpret_cast<uptr>(old_ptr); 714 uptr chunk_beg = p - kChunkHeaderSize; 715 AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); 716 717 AsanStats &thread_stats = GetCurrentThreadStats(); 718 thread_stats.reallocs++; 719 thread_stats.realloced += new_size; 720 721 void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); 722 if (new_ptr) { 723 u8 chunk_state = atomic_load(&m->chunk_state, memory_order_acquire); 724 if (chunk_state != CHUNK_ALLOCATED) 725 ReportInvalidFree(old_ptr, chunk_state, stack); 726 CHECK_NE(REAL(memcpy), nullptr); 727 uptr memcpy_size = Min(new_size, m->UsedSize()); 728 // If realloc() races with free(), we may start copying freed memory. 729 // However, we will report racy double-free later anyway. 730 REAL(memcpy)(new_ptr, old_ptr, memcpy_size); 731 Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC); 732 } 733 return new_ptr; 734 } 735 736 void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { 737 if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { 738 if (AllocatorMayReturnNull()) 739 return nullptr; 740 ReportCallocOverflow(nmemb, size, stack); 741 } 742 void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); 743 // If the memory comes from the secondary allocator no need to clear it 744 // as it comes directly from mmap. 745 if (ptr && allocator.FromPrimary(ptr)) 746 REAL(memset)(ptr, 0, nmemb * size); 747 return ptr; 748 } 749 750 void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) { 751 if (chunk_state == CHUNK_QUARANTINE) 752 ReportDoubleFree((uptr)ptr, stack); 753 else 754 ReportFreeNotMalloced((uptr)ptr, stack); 755 } 756 757 void CommitBack(AsanThreadLocalMallocStorage *ms, BufferedStackTrace *stack) { 758 AllocatorCache *ac = GetAllocatorCache(ms); 759 quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac, stack)); 760 allocator.SwallowCache(ac); 761 } 762 763 // -------------------------- Chunk lookup ---------------------- 764 765 // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). 766 // Returns nullptr if AsanChunk is not yet initialized just after 767 // get_allocator().Allocate(), or is being destroyed just before 768 // get_allocator().Deallocate(). 769 AsanChunk *GetAsanChunk(void *alloc_beg) { 770 if (!alloc_beg) 771 return nullptr; 772 AsanChunk *p = nullptr; 773 if (!allocator.FromPrimary(alloc_beg)) { 774 uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg)); 775 p = reinterpret_cast<AsanChunk *>(meta[1]); 776 } else { 777 uptr *alloc_magic = reinterpret_cast<uptr *>(alloc_beg); 778 if (alloc_magic[0] == kAllocBegMagic) 779 p = reinterpret_cast<AsanChunk *>(alloc_magic[1]); 780 else 781 p = reinterpret_cast<AsanChunk *>(alloc_beg); 782 } 783 if (!p) 784 return nullptr; 785 u8 state = atomic_load(&p->chunk_state, memory_order_relaxed); 786 // It does not guaranty that Chunk is initialized, but it's 787 // definitely not for any other value. 788 if (state == CHUNK_ALLOCATED || state == CHUNK_QUARANTINE) 789 return p; 790 return nullptr; 791 } 792 793 AsanChunk *GetAsanChunkByAddr(uptr p) { 794 void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p)); 795 return GetAsanChunk(alloc_beg); 796 } 797 798 // Allocator must be locked when this function is called. 799 AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { 800 void *alloc_beg = 801 allocator.GetBlockBeginFastLocked(reinterpret_cast<void *>(p)); 802 return GetAsanChunk(alloc_beg); 803 } 804 805 uptr AllocationSize(uptr p) { 806 AsanChunk *m = GetAsanChunkByAddr(p); 807 if (!m) return 0; 808 if (atomic_load(&m->chunk_state, memory_order_acquire) != CHUNK_ALLOCATED) 809 return 0; 810 if (m->Beg() != p) return 0; 811 return m->UsedSize(); 812 } 813 814 AsanChunkView FindHeapChunkByAddress(uptr addr) { 815 AsanChunk *m1 = GetAsanChunkByAddr(addr); 816 sptr offset = 0; 817 if (!m1 || AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) { 818 // The address is in the chunk's left redzone, so maybe it is actually 819 // a right buffer overflow from the other chunk to the left. 820 // Search a bit to the left to see if there is another chunk. 821 AsanChunk *m2 = nullptr; 822 for (uptr l = 1; l < GetPageSizeCached(); l++) { 823 m2 = GetAsanChunkByAddr(addr - l); 824 if (m2 == m1) continue; // Still the same chunk. 825 break; 826 } 827 if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset)) 828 m1 = ChooseChunk(addr, m2, m1); 829 } 830 return AsanChunkView(m1); 831 } 832 833 void Purge(BufferedStackTrace *stack) { 834 AsanThread *t = GetCurrentThread(); 835 if (t) { 836 AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); 837 quarantine.DrainAndRecycle(GetQuarantineCache(ms), 838 QuarantineCallback(GetAllocatorCache(ms), 839 stack)); 840 } 841 { 842 SpinMutexLock l(&fallback_mutex); 843 quarantine.DrainAndRecycle(&fallback_quarantine_cache, 844 QuarantineCallback(&fallback_allocator_cache, 845 stack)); 846 } 847 848 allocator.ForceReleaseToOS(); 849 } 850 851 void PrintStats() { 852 allocator.PrintStats(); 853 quarantine.PrintStats(); 854 } 855 856 void ForceLock() { 857 allocator.ForceLock(); 858 fallback_mutex.Lock(); 859 } 860 861 void ForceUnlock() { 862 fallback_mutex.Unlock(); 863 allocator.ForceUnlock(); 864 } 865 }; 866 867 static Allocator instance(LINKER_INITIALIZED); 868 869 static AsanAllocator &get_allocator() { 870 return instance.allocator; 871 } 872 873 bool AsanChunkView::IsValid() const { 874 return chunk_ && atomic_load(&chunk_->chunk_state, memory_order_relaxed) != 875 CHUNK_INVALID; 876 } 877 bool AsanChunkView::IsAllocated() const { 878 return chunk_ && atomic_load(&chunk_->chunk_state, memory_order_relaxed) == 879 CHUNK_ALLOCATED; 880 } 881 bool AsanChunkView::IsQuarantined() const { 882 return chunk_ && atomic_load(&chunk_->chunk_state, memory_order_relaxed) == 883 CHUNK_QUARANTINE; 884 } 885 uptr AsanChunkView::Beg() const { return chunk_->Beg(); } 886 uptr AsanChunkView::End() const { return Beg() + UsedSize(); } 887 uptr AsanChunkView::UsedSize() const { return chunk_->UsedSize(); } 888 u32 AsanChunkView::UserRequestedAlignment() const { 889 return Allocator::ComputeUserAlignment(chunk_->user_requested_alignment_log); 890 } 891 892 uptr AsanChunkView::AllocTid() const { 893 u32 tid = 0; 894 u32 stack = 0; 895 chunk_->GetAllocContext(tid, stack); 896 return tid; 897 } 898 899 uptr AsanChunkView::FreeTid() const { 900 if (!IsQuarantined()) 901 return kInvalidTid; 902 u32 tid = 0; 903 u32 stack = 0; 904 chunk_->GetFreeContext(tid, stack); 905 return tid; 906 } 907 908 AllocType AsanChunkView::GetAllocType() const { 909 return (AllocType)chunk_->alloc_type; 910 } 911 912 static StackTrace GetStackTraceFromId(u32 id) { 913 CHECK(id); 914 StackTrace res = StackDepotGet(id); 915 CHECK(res.trace); 916 return res; 917 } 918 919 u32 AsanChunkView::GetAllocStackId() const { 920 u32 tid = 0; 921 u32 stack = 0; 922 chunk_->GetAllocContext(tid, stack); 923 return stack; 924 } 925 926 u32 AsanChunkView::GetFreeStackId() const { 927 if (!IsQuarantined()) 928 return 0; 929 u32 tid = 0; 930 u32 stack = 0; 931 chunk_->GetFreeContext(tid, stack); 932 return stack; 933 } 934 935 StackTrace AsanChunkView::GetAllocStack() const { 936 return GetStackTraceFromId(GetAllocStackId()); 937 } 938 939 StackTrace AsanChunkView::GetFreeStack() const { 940 return GetStackTraceFromId(GetFreeStackId()); 941 } 942 943 void InitializeAllocator(const AllocatorOptions &options) { 944 instance.InitLinkerInitialized(options); 945 } 946 947 void ReInitializeAllocator(const AllocatorOptions &options) { 948 instance.ReInitialize(options); 949 } 950 951 void GetAllocatorOptions(AllocatorOptions *options) { 952 instance.GetOptions(options); 953 } 954 955 AsanChunkView FindHeapChunkByAddress(uptr addr) { 956 return instance.FindHeapChunkByAddress(addr); 957 } 958 AsanChunkView FindHeapChunkByAllocBeg(uptr addr) { 959 return AsanChunkView(instance.GetAsanChunk(reinterpret_cast<void*>(addr))); 960 } 961 962 void AsanThreadLocalMallocStorage::CommitBack() { 963 GET_STACK_TRACE_MALLOC; 964 instance.CommitBack(this, &stack); 965 } 966 967 void PrintInternalAllocatorStats() { 968 instance.PrintStats(); 969 } 970 971 void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { 972 instance.Deallocate(ptr, 0, 0, stack, alloc_type); 973 } 974 975 void asan_delete(void *ptr, uptr size, uptr alignment, 976 BufferedStackTrace *stack, AllocType alloc_type) { 977 instance.Deallocate(ptr, size, alignment, stack, alloc_type); 978 } 979 980 void *asan_malloc(uptr size, BufferedStackTrace *stack) { 981 return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true)); 982 } 983 984 void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { 985 return SetErrnoOnNull(instance.Calloc(nmemb, size, stack)); 986 } 987 988 void *asan_reallocarray(void *p, uptr nmemb, uptr size, 989 BufferedStackTrace *stack) { 990 if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { 991 errno = errno_ENOMEM; 992 if (AllocatorMayReturnNull()) 993 return nullptr; 994 ReportReallocArrayOverflow(nmemb, size, stack); 995 } 996 return asan_realloc(p, nmemb * size, stack); 997 } 998 999 void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { 1000 if (!p) 1001 return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true)); 1002 if (size == 0) { 1003 if (flags()->allocator_frees_and_returns_null_on_realloc_zero) { 1004 instance.Deallocate(p, 0, 0, stack, FROM_MALLOC); 1005 return nullptr; 1006 } 1007 // Allocate a size of 1 if we shouldn't free() on Realloc to 0 1008 size = 1; 1009 } 1010 return SetErrnoOnNull(instance.Reallocate(p, size, stack)); 1011 } 1012 1013 void *asan_valloc(uptr size, BufferedStackTrace *stack) { 1014 return SetErrnoOnNull( 1015 instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true)); 1016 } 1017 1018 void *asan_pvalloc(uptr size, BufferedStackTrace *stack) { 1019 uptr PageSize = GetPageSizeCached(); 1020 if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) { 1021 errno = errno_ENOMEM; 1022 if (AllocatorMayReturnNull()) 1023 return nullptr; 1024 ReportPvallocOverflow(size, stack); 1025 } 1026 // pvalloc(0) should allocate one page. 1027 size = size ? RoundUpTo(size, PageSize) : PageSize; 1028 return SetErrnoOnNull( 1029 instance.Allocate(size, PageSize, stack, FROM_MALLOC, true)); 1030 } 1031 1032 void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, 1033 AllocType alloc_type) { 1034 if (UNLIKELY(!IsPowerOfTwo(alignment))) { 1035 errno = errno_EINVAL; 1036 if (AllocatorMayReturnNull()) 1037 return nullptr; 1038 ReportInvalidAllocationAlignment(alignment, stack); 1039 } 1040 return SetErrnoOnNull( 1041 instance.Allocate(size, alignment, stack, alloc_type, true)); 1042 } 1043 1044 void *asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack) { 1045 if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) { 1046 errno = errno_EINVAL; 1047 if (AllocatorMayReturnNull()) 1048 return nullptr; 1049 ReportInvalidAlignedAllocAlignment(size, alignment, stack); 1050 } 1051 return SetErrnoOnNull( 1052 instance.Allocate(size, alignment, stack, FROM_MALLOC, true)); 1053 } 1054 1055 int asan_posix_memalign(void **memptr, uptr alignment, uptr size, 1056 BufferedStackTrace *stack) { 1057 if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) { 1058 if (AllocatorMayReturnNull()) 1059 return errno_EINVAL; 1060 ReportInvalidPosixMemalignAlignment(alignment, stack); 1061 } 1062 void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true); 1063 if (UNLIKELY(!ptr)) 1064 // OOM error is already taken care of by Allocate. 1065 return errno_ENOMEM; 1066 CHECK(IsAligned((uptr)ptr, alignment)); 1067 *memptr = ptr; 1068 return 0; 1069 } 1070 1071 uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) { 1072 if (!ptr) return 0; 1073 uptr usable_size = instance.AllocationSize(reinterpret_cast<uptr>(ptr)); 1074 if (flags()->check_malloc_usable_size && (usable_size == 0)) { 1075 GET_STACK_TRACE_FATAL(pc, bp); 1076 ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); 1077 } 1078 return usable_size; 1079 } 1080 1081 uptr asan_mz_size(const void *ptr) { 1082 return instance.AllocationSize(reinterpret_cast<uptr>(ptr)); 1083 } 1084 1085 void asan_mz_force_lock() { 1086 instance.ForceLock(); 1087 } 1088 1089 void asan_mz_force_unlock() { 1090 instance.ForceUnlock(); 1091 } 1092 1093 void AsanSoftRssLimitExceededCallback(bool limit_exceeded) { 1094 instance.SetRssLimitExceeded(limit_exceeded); 1095 } 1096 1097 } // namespace __asan 1098 1099 // --- Implementation of LSan-specific functions --- {{{1 1100 namespace __lsan { 1101 void LockAllocator() { 1102 __asan::get_allocator().ForceLock(); 1103 } 1104 1105 void UnlockAllocator() { 1106 __asan::get_allocator().ForceUnlock(); 1107 } 1108 1109 void GetAllocatorGlobalRange(uptr *begin, uptr *end) { 1110 *begin = (uptr)&__asan::get_allocator(); 1111 *end = *begin + sizeof(__asan::get_allocator()); 1112 } 1113 1114 uptr PointsIntoChunk(void* p) { 1115 uptr addr = reinterpret_cast<uptr>(p); 1116 __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr); 1117 if (!m || atomic_load(&m->chunk_state, memory_order_acquire) != 1118 __asan::CHUNK_ALLOCATED) 1119 return 0; 1120 uptr chunk = m->Beg(); 1121 if (m->AddrIsInside(addr, /*locked_version=*/true)) 1122 return chunk; 1123 if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true), 1124 addr)) 1125 return chunk; 1126 return 0; 1127 } 1128 1129 uptr GetUserBegin(uptr chunk) { 1130 __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk); 1131 return m ? m->Beg() : 0; 1132 } 1133 1134 LsanMetadata::LsanMetadata(uptr chunk) { 1135 metadata_ = chunk ? reinterpret_cast<void *>(chunk - __asan::kChunkHeaderSize) 1136 : nullptr; 1137 } 1138 1139 bool LsanMetadata::allocated() const { 1140 if (!metadata_) 1141 return false; 1142 __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); 1143 return atomic_load(&m->chunk_state, memory_order_relaxed) == 1144 __asan::CHUNK_ALLOCATED; 1145 } 1146 1147 ChunkTag LsanMetadata::tag() const { 1148 __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); 1149 return static_cast<ChunkTag>(m->lsan_tag); 1150 } 1151 1152 void LsanMetadata::set_tag(ChunkTag value) { 1153 __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); 1154 m->lsan_tag = value; 1155 } 1156 1157 uptr LsanMetadata::requested_size() const { 1158 __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); 1159 return m->UsedSize(/*locked_version=*/true); 1160 } 1161 1162 u32 LsanMetadata::stack_trace_id() const { 1163 __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); 1164 u32 tid = 0; 1165 u32 stack = 0; 1166 m->GetAllocContext(tid, stack); 1167 return stack; 1168 } 1169 1170 void ForEachChunk(ForEachChunkCallback callback, void *arg) { 1171 __asan::get_allocator().ForEachChunk(callback, arg); 1172 } 1173 1174 IgnoreObjectResult IgnoreObjectLocked(const void *p) { 1175 uptr addr = reinterpret_cast<uptr>(p); 1176 __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr); 1177 if (!m) return kIgnoreObjectInvalid; 1178 if ((atomic_load(&m->chunk_state, memory_order_acquire) == 1179 __asan::CHUNK_ALLOCATED) && 1180 m->AddrIsInside(addr)) { 1181 if (m->lsan_tag == kIgnored) 1182 return kIgnoreObjectAlreadyIgnored; 1183 m->lsan_tag = __lsan::kIgnored; 1184 return kIgnoreObjectSuccess; 1185 } 1186 return kIgnoreObjectInvalid; 1187 } 1188 } // namespace __lsan 1189 1190 // ---------------------- Interface ---------------- {{{1 1191 using namespace __asan; 1192 1193 // ASan allocator doesn't reserve extra bytes, so normally we would 1194 // just return "size". We don't want to expose our redzone sizes, etc here. 1195 uptr __sanitizer_get_estimated_allocated_size(uptr size) { 1196 return size; 1197 } 1198 1199 int __sanitizer_get_ownership(const void *p) { 1200 uptr ptr = reinterpret_cast<uptr>(p); 1201 return instance.AllocationSize(ptr) > 0; 1202 } 1203 1204 uptr __sanitizer_get_allocated_size(const void *p) { 1205 if (!p) return 0; 1206 uptr ptr = reinterpret_cast<uptr>(p); 1207 uptr allocated_size = instance.AllocationSize(ptr); 1208 // Die if p is not malloced or if it is already freed. 1209 if (allocated_size == 0) { 1210 GET_STACK_TRACE_FATAL_HERE; 1211 ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack); 1212 } 1213 return allocated_size; 1214 } 1215 1216 void __sanitizer_purge_allocator() { 1217 GET_STACK_TRACE_MALLOC; 1218 instance.Purge(&stack); 1219 } 1220 1221 int __asan_update_allocation_context(void* addr) { 1222 GET_STACK_TRACE_MALLOC; 1223 return instance.UpdateAllocationStack((uptr)addr, &stack); 1224 } 1225 1226 #if !SANITIZER_SUPPORTS_WEAK_HOOKS 1227 // Provide default (no-op) implementation of malloc hooks. 1228 SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook, 1229 void *ptr, uptr size) { 1230 (void)ptr; 1231 (void)size; 1232 } 1233 1234 SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) { 1235 (void)ptr; 1236 } 1237 #endif 1238