1 //===-- combined.h ----------------------------------------------*- C++ -*-===// 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 #ifndef SCUDO_COMBINED_H_ 10 #define SCUDO_COMBINED_H_ 11 12 #include "chunk.h" 13 #include "common.h" 14 #include "flags.h" 15 #include "flags_parser.h" 16 #include "local_cache.h" 17 #include "memtag.h" 18 #include "quarantine.h" 19 #include "report.h" 20 #include "secondary.h" 21 #include "stack_depot.h" 22 #include "string_utils.h" 23 #include "tsd.h" 24 25 #include "scudo/interface.h" 26 27 #ifdef GWP_ASAN_HOOKS 28 #include "gwp_asan/guarded_pool_allocator.h" 29 #include "gwp_asan/optional/backtrace.h" 30 #include "gwp_asan/optional/segv_handler.h" 31 #endif // GWP_ASAN_HOOKS 32 33 extern "C" inline void EmptyCallback() {} 34 35 #ifdef HAVE_ANDROID_UNSAFE_FRAME_POINTER_CHASE 36 // This function is not part of the NDK so it does not appear in any public 37 // header files. We only declare/use it when targeting the platform. 38 extern "C" size_t android_unsafe_frame_pointer_chase(scudo::uptr *buf, 39 size_t num_entries); 40 #endif 41 42 namespace scudo { 43 44 enum class Option { ReleaseInterval }; 45 46 template <class Params, void (*PostInitCallback)(void) = EmptyCallback> 47 class Allocator { 48 public: 49 using PrimaryT = typename Params::Primary; 50 using CacheT = typename PrimaryT::CacheT; 51 typedef Allocator<Params, PostInitCallback> ThisT; 52 typedef typename Params::template TSDRegistryT<ThisT> TSDRegistryT; 53 54 void callPostInitCallback() { 55 static pthread_once_t OnceControl = PTHREAD_ONCE_INIT; 56 pthread_once(&OnceControl, PostInitCallback); 57 } 58 59 struct QuarantineCallback { 60 explicit QuarantineCallback(ThisT &Instance, CacheT &LocalCache) 61 : Allocator(Instance), Cache(LocalCache) {} 62 63 // Chunk recycling function, returns a quarantined chunk to the backend, 64 // first making sure it hasn't been tampered with. 65 void recycle(void *Ptr) { 66 Chunk::UnpackedHeader Header; 67 Chunk::loadHeader(Allocator.Cookie, Ptr, &Header); 68 if (UNLIKELY(Header.State != Chunk::State::Quarantined)) 69 reportInvalidChunkState(AllocatorAction::Recycling, Ptr); 70 71 Chunk::UnpackedHeader NewHeader = Header; 72 NewHeader.State = Chunk::State::Available; 73 Chunk::compareExchangeHeader(Allocator.Cookie, Ptr, &NewHeader, &Header); 74 75 void *BlockBegin = Allocator::getBlockBegin(Ptr, &NewHeader); 76 const uptr ClassId = NewHeader.ClassId; 77 if (LIKELY(ClassId)) 78 Cache.deallocate(ClassId, BlockBegin); 79 else 80 Allocator.Secondary.deallocate(BlockBegin); 81 } 82 83 // We take a shortcut when allocating a quarantine batch by working with the 84 // appropriate class ID instead of using Size. The compiler should optimize 85 // the class ID computation and work with the associated cache directly. 86 void *allocate(UNUSED uptr Size) { 87 const uptr QuarantineClassId = SizeClassMap::getClassIdBySize( 88 sizeof(QuarantineBatch) + Chunk::getHeaderSize()); 89 void *Ptr = Cache.allocate(QuarantineClassId); 90 // Quarantine batch allocation failure is fatal. 91 if (UNLIKELY(!Ptr)) 92 reportOutOfMemory(SizeClassMap::getSizeByClassId(QuarantineClassId)); 93 94 Ptr = reinterpret_cast<void *>(reinterpret_cast<uptr>(Ptr) + 95 Chunk::getHeaderSize()); 96 Chunk::UnpackedHeader Header = {}; 97 Header.ClassId = QuarantineClassId & Chunk::ClassIdMask; 98 Header.SizeOrUnusedBytes = sizeof(QuarantineBatch); 99 Header.State = Chunk::State::Allocated; 100 Chunk::storeHeader(Allocator.Cookie, Ptr, &Header); 101 102 return Ptr; 103 } 104 105 void deallocate(void *Ptr) { 106 const uptr QuarantineClassId = SizeClassMap::getClassIdBySize( 107 sizeof(QuarantineBatch) + Chunk::getHeaderSize()); 108 Chunk::UnpackedHeader Header; 109 Chunk::loadHeader(Allocator.Cookie, Ptr, &Header); 110 111 if (UNLIKELY(Header.State != Chunk::State::Allocated)) 112 reportInvalidChunkState(AllocatorAction::Deallocating, Ptr); 113 DCHECK_EQ(Header.ClassId, QuarantineClassId); 114 DCHECK_EQ(Header.Offset, 0); 115 DCHECK_EQ(Header.SizeOrUnusedBytes, sizeof(QuarantineBatch)); 116 117 Chunk::UnpackedHeader NewHeader = Header; 118 NewHeader.State = Chunk::State::Available; 119 Chunk::compareExchangeHeader(Allocator.Cookie, Ptr, &NewHeader, &Header); 120 Cache.deallocate(QuarantineClassId, 121 reinterpret_cast<void *>(reinterpret_cast<uptr>(Ptr) - 122 Chunk::getHeaderSize())); 123 } 124 125 private: 126 ThisT &Allocator; 127 CacheT &Cache; 128 }; 129 130 typedef GlobalQuarantine<QuarantineCallback, void> QuarantineT; 131 typedef typename QuarantineT::CacheT QuarantineCacheT; 132 133 void initLinkerInitialized() { 134 performSanityChecks(); 135 136 // Check if hardware CRC32 is supported in the binary and by the platform, 137 // if so, opt for the CRC32 hardware version of the checksum. 138 if (&computeHardwareCRC32 && hasHardwareCRC32()) 139 HashAlgorithm = Checksum::HardwareCRC32; 140 141 if (UNLIKELY(!getRandom(&Cookie, sizeof(Cookie)))) 142 Cookie = static_cast<u32>(getMonotonicTime() ^ 143 (reinterpret_cast<uptr>(this) >> 4)); 144 145 initFlags(); 146 reportUnrecognizedFlags(); 147 148 // Store some flags locally. 149 Options.MayReturnNull = getFlags()->may_return_null; 150 Options.FillContents = 151 getFlags()->zero_contents 152 ? ZeroFill 153 : (getFlags()->pattern_fill_contents ? PatternOrZeroFill : NoFill); 154 Options.DeallocTypeMismatch = getFlags()->dealloc_type_mismatch; 155 Options.DeleteSizeMismatch = getFlags()->delete_size_mismatch; 156 Options.TrackAllocationStacks = false; 157 Options.QuarantineMaxChunkSize = 158 static_cast<u32>(getFlags()->quarantine_max_chunk_size); 159 160 Stats.initLinkerInitialized(); 161 const s32 ReleaseToOsIntervalMs = getFlags()->release_to_os_interval_ms; 162 Primary.initLinkerInitialized(ReleaseToOsIntervalMs); 163 Secondary.initLinkerInitialized(&Stats, ReleaseToOsIntervalMs); 164 165 Quarantine.init( 166 static_cast<uptr>(getFlags()->quarantine_size_kb << 10), 167 static_cast<uptr>(getFlags()->thread_local_quarantine_size_kb << 10)); 168 } 169 170 // Initialize the embedded GWP-ASan instance. Requires the main allocator to 171 // be functional, best called from PostInitCallback. 172 void initGwpAsan() { 173 #ifdef GWP_ASAN_HOOKS 174 gwp_asan::options::Options Opt; 175 Opt.Enabled = getFlags()->GWP_ASAN_Enabled; 176 // Bear in mind - Scudo has its own alignment guarantees that are strictly 177 // enforced. Scudo exposes the same allocation function for everything from 178 // malloc() to posix_memalign, so in general this flag goes unused, as Scudo 179 // will always ask GWP-ASan for an aligned amount of bytes. 180 Opt.PerfectlyRightAlign = getFlags()->GWP_ASAN_PerfectlyRightAlign; 181 Opt.MaxSimultaneousAllocations = 182 getFlags()->GWP_ASAN_MaxSimultaneousAllocations; 183 Opt.SampleRate = getFlags()->GWP_ASAN_SampleRate; 184 Opt.InstallSignalHandlers = getFlags()->GWP_ASAN_InstallSignalHandlers; 185 // Embedded GWP-ASan is locked through the Scudo atfork handler (via 186 // Allocator::disable calling GWPASan.disable). Disable GWP-ASan's atfork 187 // handler. 188 Opt.InstallForkHandlers = false; 189 Opt.Backtrace = gwp_asan::options::getBacktraceFunction(); 190 GuardedAlloc.init(Opt); 191 192 if (Opt.InstallSignalHandlers) 193 gwp_asan::crash_handler::installSignalHandlers( 194 &GuardedAlloc, Printf, gwp_asan::options::getPrintBacktraceFunction(), 195 Opt.Backtrace); 196 #endif // GWP_ASAN_HOOKS 197 } 198 199 void reset() { memset(this, 0, sizeof(*this)); } 200 201 void unmapTestOnly() { 202 TSDRegistry.unmapTestOnly(); 203 Primary.unmapTestOnly(); 204 #ifdef GWP_ASAN_HOOKS 205 if (getFlags()->GWP_ASAN_InstallSignalHandlers) 206 gwp_asan::crash_handler::uninstallSignalHandlers(); 207 GuardedAlloc.uninitTestOnly(); 208 #endif // GWP_ASAN_HOOKS 209 } 210 211 TSDRegistryT *getTSDRegistry() { return &TSDRegistry; } 212 213 // The Cache must be provided zero-initialized. 214 void initCache(CacheT *Cache) { 215 Cache->initLinkerInitialized(&Stats, &Primary); 216 } 217 218 // Release the resources used by a TSD, which involves: 219 // - draining the local quarantine cache to the global quarantine; 220 // - releasing the cached pointers back to the Primary; 221 // - unlinking the local stats from the global ones (destroying the cache does 222 // the last two items). 223 void commitBack(TSD<ThisT> *TSD) { 224 Quarantine.drain(&TSD->QuarantineCache, 225 QuarantineCallback(*this, TSD->Cache)); 226 TSD->Cache.destroy(&Stats); 227 } 228 229 ALWAYS_INLINE void *untagPointerMaybe(void *Ptr) { 230 if (Primary.SupportsMemoryTagging) 231 return reinterpret_cast<void *>( 232 untagPointer(reinterpret_cast<uptr>(Ptr))); 233 return Ptr; 234 } 235 236 NOINLINE u32 collectStackTrace() { 237 #ifdef HAVE_ANDROID_UNSAFE_FRAME_POINTER_CHASE 238 // Discard collectStackTrace() frame and allocator function frame. 239 constexpr uptr DiscardFrames = 2; 240 uptr Stack[MaxTraceSize + DiscardFrames]; 241 uptr Size = 242 android_unsafe_frame_pointer_chase(Stack, MaxTraceSize + DiscardFrames); 243 Size = Min<uptr>(Size, MaxTraceSize + DiscardFrames); 244 return Depot.insert(Stack + Min<uptr>(DiscardFrames, Size), Stack + Size); 245 #else 246 return 0; 247 #endif 248 } 249 250 NOINLINE void *allocate(uptr Size, Chunk::Origin Origin, 251 uptr Alignment = MinAlignment, 252 bool ZeroContents = false) { 253 initThreadMaybe(); 254 255 #ifdef GWP_ASAN_HOOKS 256 if (UNLIKELY(GuardedAlloc.shouldSample())) { 257 if (void *Ptr = GuardedAlloc.allocate(roundUpTo(Size, Alignment))) 258 return Ptr; 259 } 260 #endif // GWP_ASAN_HOOKS 261 262 FillContentsMode FillContents = 263 ZeroContents ? ZeroFill : Options.FillContents; 264 265 if (UNLIKELY(Alignment > MaxAlignment)) { 266 if (Options.MayReturnNull) 267 return nullptr; 268 reportAlignmentTooBig(Alignment, MaxAlignment); 269 } 270 if (Alignment < MinAlignment) 271 Alignment = MinAlignment; 272 273 // If the requested size happens to be 0 (more common than you might think), 274 // allocate MinAlignment bytes on top of the header. Then add the extra 275 // bytes required to fulfill the alignment requirements: we allocate enough 276 // to be sure that there will be an address in the block that will satisfy 277 // the alignment. 278 const uptr NeededSize = 279 roundUpTo(Size, MinAlignment) + 280 ((Alignment > MinAlignment) ? Alignment : Chunk::getHeaderSize()); 281 282 // Takes care of extravagantly large sizes as well as integer overflows. 283 static_assert(MaxAllowedMallocSize < UINTPTR_MAX - MaxAlignment, ""); 284 if (UNLIKELY(Size >= MaxAllowedMallocSize)) { 285 if (Options.MayReturnNull) 286 return nullptr; 287 reportAllocationSizeTooBig(Size, NeededSize, MaxAllowedMallocSize); 288 } 289 DCHECK_LE(Size, NeededSize); 290 291 void *Block = nullptr; 292 uptr ClassId = 0; 293 uptr SecondaryBlockEnd; 294 if (LIKELY(PrimaryT::canAllocate(NeededSize))) { 295 ClassId = SizeClassMap::getClassIdBySize(NeededSize); 296 DCHECK_NE(ClassId, 0U); 297 bool UnlockRequired; 298 auto *TSD = TSDRegistry.getTSDAndLock(&UnlockRequired); 299 Block = TSD->Cache.allocate(ClassId); 300 // If the allocation failed, the most likely reason with a 32-bit primary 301 // is the region being full. In that event, retry in each successively 302 // larger class until it fits. If it fails to fit in the largest class, 303 // fallback to the Secondary. 304 if (UNLIKELY(!Block)) { 305 while (ClassId < SizeClassMap::LargestClassId) { 306 Block = TSD->Cache.allocate(++ClassId); 307 if (LIKELY(Block)) { 308 break; 309 } 310 } 311 if (UNLIKELY(!Block)) { 312 ClassId = 0; 313 } 314 } 315 if (UnlockRequired) 316 TSD->unlock(); 317 } 318 if (UNLIKELY(ClassId == 0)) 319 Block = Secondary.allocate(NeededSize, Alignment, &SecondaryBlockEnd, 320 FillContents); 321 322 if (UNLIKELY(!Block)) { 323 if (Options.MayReturnNull) 324 return nullptr; 325 reportOutOfMemory(NeededSize); 326 } 327 328 const uptr BlockUptr = reinterpret_cast<uptr>(Block); 329 const uptr UnalignedUserPtr = BlockUptr + Chunk::getHeaderSize(); 330 const uptr UserPtr = roundUpTo(UnalignedUserPtr, Alignment); 331 332 void *Ptr = reinterpret_cast<void *>(UserPtr); 333 void *TaggedPtr = Ptr; 334 if (ClassId) { 335 // We only need to zero or tag the contents for Primary backed 336 // allocations. We only set tags for primary allocations in order to avoid 337 // faulting potentially large numbers of pages for large secondary 338 // allocations. We assume that guard pages are enough to protect these 339 // allocations. 340 // 341 // FIXME: When the kernel provides a way to set the background tag of a 342 // mapping, we should be able to tag secondary allocations as well. 343 // 344 // When memory tagging is enabled, zeroing the contents is done as part of 345 // setting the tag. 346 if (UNLIKELY(useMemoryTagging())) { 347 uptr PrevUserPtr; 348 Chunk::UnpackedHeader Header; 349 const uptr BlockEnd = BlockUptr + PrimaryT::getSizeByClassId(ClassId); 350 // If possible, try to reuse the UAF tag that was set by deallocate(). 351 // For simplicity, only reuse tags if we have the same start address as 352 // the previous allocation. This handles the majority of cases since 353 // most allocations will not be more aligned than the minimum alignment. 354 // 355 // We need to handle situations involving reclaimed chunks, and retag 356 // the reclaimed portions if necessary. In the case where the chunk is 357 // fully reclaimed, the chunk's header will be zero, which will trigger 358 // the code path for new mappings and invalid chunks that prepares the 359 // chunk from scratch. There are three possibilities for partial 360 // reclaiming: 361 // 362 // (1) Header was reclaimed, data was partially reclaimed. 363 // (2) Header was not reclaimed, all data was reclaimed (e.g. because 364 // data started on a page boundary). 365 // (3) Header was not reclaimed, data was partially reclaimed. 366 // 367 // Case (1) will be handled in the same way as for full reclaiming, 368 // since the header will be zero. 369 // 370 // We can detect case (2) by loading the tag from the start 371 // of the chunk. If it is zero, it means that either all data was 372 // reclaimed (since we never use zero as the chunk tag), or that the 373 // previous allocation was of size zero. Either way, we need to prepare 374 // a new chunk from scratch. 375 // 376 // We can detect case (3) by moving to the next page (if covered by the 377 // chunk) and loading the tag of its first granule. If it is zero, it 378 // means that all following pages may need to be retagged. On the other 379 // hand, if it is nonzero, we can assume that all following pages are 380 // still tagged, according to the logic that if any of the pages 381 // following the next page were reclaimed, the next page would have been 382 // reclaimed as well. 383 uptr TaggedUserPtr; 384 if (getChunkFromBlock(BlockUptr, &PrevUserPtr, &Header) && 385 PrevUserPtr == UserPtr && 386 (TaggedUserPtr = loadTag(UserPtr)) != UserPtr) { 387 uptr PrevEnd = TaggedUserPtr + Header.SizeOrUnusedBytes; 388 const uptr NextPage = roundUpTo(TaggedUserPtr, getPageSizeCached()); 389 if (NextPage < PrevEnd && loadTag(NextPage) != NextPage) 390 PrevEnd = NextPage; 391 TaggedPtr = reinterpret_cast<void *>(TaggedUserPtr); 392 resizeTaggedChunk(PrevEnd, TaggedUserPtr + Size, BlockEnd); 393 if (Size) { 394 // Clear any stack metadata that may have previously been stored in 395 // the chunk data. 396 memset(TaggedPtr, 0, archMemoryTagGranuleSize()); 397 } 398 } else { 399 TaggedPtr = prepareTaggedChunk(Ptr, Size, BlockEnd); 400 } 401 storeAllocationStackMaybe(Ptr); 402 } else if (UNLIKELY(FillContents != NoFill)) { 403 // This condition is not necessarily unlikely, but since memset is 404 // costly, we might as well mark it as such. 405 memset(Block, FillContents == ZeroFill ? 0 : PatternFillByte, 406 PrimaryT::getSizeByClassId(ClassId)); 407 } 408 } 409 410 Chunk::UnpackedHeader Header = {}; 411 if (UNLIKELY(UnalignedUserPtr != UserPtr)) { 412 const uptr Offset = UserPtr - UnalignedUserPtr; 413 DCHECK_GE(Offset, 2 * sizeof(u32)); 414 // The BlockMarker has no security purpose, but is specifically meant for 415 // the chunk iteration function that can be used in debugging situations. 416 // It is the only situation where we have to locate the start of a chunk 417 // based on its block address. 418 reinterpret_cast<u32 *>(Block)[0] = BlockMarker; 419 reinterpret_cast<u32 *>(Block)[1] = static_cast<u32>(Offset); 420 Header.Offset = (Offset >> MinAlignmentLog) & Chunk::OffsetMask; 421 } 422 Header.ClassId = ClassId & Chunk::ClassIdMask; 423 Header.State = Chunk::State::Allocated; 424 Header.Origin = Origin & Chunk::OriginMask; 425 Header.SizeOrUnusedBytes = 426 (ClassId ? Size : SecondaryBlockEnd - (UserPtr + Size)) & 427 Chunk::SizeOrUnusedBytesMask; 428 Chunk::storeHeader(Cookie, Ptr, &Header); 429 430 if (&__scudo_allocate_hook) 431 __scudo_allocate_hook(TaggedPtr, Size); 432 433 return TaggedPtr; 434 } 435 436 NOINLINE void deallocate(void *Ptr, Chunk::Origin Origin, uptr DeleteSize = 0, 437 UNUSED uptr Alignment = MinAlignment) { 438 // For a deallocation, we only ensure minimal initialization, meaning thread 439 // local data will be left uninitialized for now (when using ELF TLS). The 440 // fallback cache will be used instead. This is a workaround for a situation 441 // where the only heap operation performed in a thread would be a free past 442 // the TLS destructors, ending up in initialized thread specific data never 443 // being destroyed properly. Any other heap operation will do a full init. 444 initThreadMaybe(/*MinimalInit=*/true); 445 446 #ifdef GWP_ASAN_HOOKS 447 if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) { 448 GuardedAlloc.deallocate(Ptr); 449 return; 450 } 451 #endif // GWP_ASAN_HOOKS 452 453 if (&__scudo_deallocate_hook) 454 __scudo_deallocate_hook(Ptr); 455 456 if (UNLIKELY(!Ptr)) 457 return; 458 if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(Ptr), MinAlignment))) 459 reportMisalignedPointer(AllocatorAction::Deallocating, Ptr); 460 461 Ptr = untagPointerMaybe(Ptr); 462 463 Chunk::UnpackedHeader Header; 464 Chunk::loadHeader(Cookie, Ptr, &Header); 465 466 if (UNLIKELY(Header.State != Chunk::State::Allocated)) 467 reportInvalidChunkState(AllocatorAction::Deallocating, Ptr); 468 if (Options.DeallocTypeMismatch) { 469 if (Header.Origin != Origin) { 470 // With the exception of memalign'd chunks, that can be still be free'd. 471 if (UNLIKELY(Header.Origin != Chunk::Origin::Memalign || 472 Origin != Chunk::Origin::Malloc)) 473 reportDeallocTypeMismatch(AllocatorAction::Deallocating, Ptr, 474 Header.Origin, Origin); 475 } 476 } 477 478 const uptr Size = getSize(Ptr, &Header); 479 if (DeleteSize && Options.DeleteSizeMismatch) { 480 if (UNLIKELY(DeleteSize != Size)) 481 reportDeleteSizeMismatch(Ptr, DeleteSize, Size); 482 } 483 484 quarantineOrDeallocateChunk(Ptr, &Header, Size); 485 } 486 487 void *reallocate(void *OldPtr, uptr NewSize, uptr Alignment = MinAlignment) { 488 initThreadMaybe(); 489 490 if (UNLIKELY(NewSize >= MaxAllowedMallocSize)) { 491 if (Options.MayReturnNull) 492 return nullptr; 493 reportAllocationSizeTooBig(NewSize, 0, MaxAllowedMallocSize); 494 } 495 496 void *OldTaggedPtr = OldPtr; 497 OldPtr = untagPointerMaybe(OldPtr); 498 499 // The following cases are handled by the C wrappers. 500 DCHECK_NE(OldPtr, nullptr); 501 DCHECK_NE(NewSize, 0); 502 503 #ifdef GWP_ASAN_HOOKS 504 if (UNLIKELY(GuardedAlloc.pointerIsMine(OldPtr))) { 505 uptr OldSize = GuardedAlloc.getSize(OldPtr); 506 void *NewPtr = allocate(NewSize, Chunk::Origin::Malloc, Alignment); 507 if (NewPtr) 508 memcpy(NewPtr, OldPtr, (NewSize < OldSize) ? NewSize : OldSize); 509 GuardedAlloc.deallocate(OldPtr); 510 return NewPtr; 511 } 512 #endif // GWP_ASAN_HOOKS 513 514 if (UNLIKELY(!isAligned(reinterpret_cast<uptr>(OldPtr), MinAlignment))) 515 reportMisalignedPointer(AllocatorAction::Reallocating, OldPtr); 516 517 Chunk::UnpackedHeader OldHeader; 518 Chunk::loadHeader(Cookie, OldPtr, &OldHeader); 519 520 if (UNLIKELY(OldHeader.State != Chunk::State::Allocated)) 521 reportInvalidChunkState(AllocatorAction::Reallocating, OldPtr); 522 523 // Pointer has to be allocated with a malloc-type function. Some 524 // applications think that it is OK to realloc a memalign'ed pointer, which 525 // will trigger this check. It really isn't. 526 if (Options.DeallocTypeMismatch) { 527 if (UNLIKELY(OldHeader.Origin != Chunk::Origin::Malloc)) 528 reportDeallocTypeMismatch(AllocatorAction::Reallocating, OldPtr, 529 OldHeader.Origin, Chunk::Origin::Malloc); 530 } 531 532 void *BlockBegin = getBlockBegin(OldPtr, &OldHeader); 533 uptr BlockEnd; 534 uptr OldSize; 535 const uptr ClassId = OldHeader.ClassId; 536 if (LIKELY(ClassId)) { 537 BlockEnd = reinterpret_cast<uptr>(BlockBegin) + 538 SizeClassMap::getSizeByClassId(ClassId); 539 OldSize = OldHeader.SizeOrUnusedBytes; 540 } else { 541 BlockEnd = SecondaryT::getBlockEnd(BlockBegin); 542 OldSize = BlockEnd - 543 (reinterpret_cast<uptr>(OldPtr) + OldHeader.SizeOrUnusedBytes); 544 } 545 // If the new chunk still fits in the previously allocated block (with a 546 // reasonable delta), we just keep the old block, and update the chunk 547 // header to reflect the size change. 548 if (reinterpret_cast<uptr>(OldPtr) + NewSize <= BlockEnd) { 549 if (NewSize > OldSize || (OldSize - NewSize) < getPageSizeCached()) { 550 Chunk::UnpackedHeader NewHeader = OldHeader; 551 NewHeader.SizeOrUnusedBytes = 552 (ClassId ? NewSize 553 : BlockEnd - (reinterpret_cast<uptr>(OldPtr) + NewSize)) & 554 Chunk::SizeOrUnusedBytesMask; 555 Chunk::compareExchangeHeader(Cookie, OldPtr, &NewHeader, &OldHeader); 556 if (UNLIKELY(ClassId && useMemoryTagging())) { 557 resizeTaggedChunk(reinterpret_cast<uptr>(OldTaggedPtr) + OldSize, 558 reinterpret_cast<uptr>(OldTaggedPtr) + NewSize, 559 BlockEnd); 560 storeAllocationStackMaybe(OldPtr); 561 } 562 return OldTaggedPtr; 563 } 564 } 565 566 // Otherwise we allocate a new one, and deallocate the old one. Some 567 // allocators will allocate an even larger chunk (by a fixed factor) to 568 // allow for potential further in-place realloc. The gains of such a trick 569 // are currently unclear. 570 void *NewPtr = allocate(NewSize, Chunk::Origin::Malloc, Alignment); 571 if (NewPtr) { 572 const uptr OldSize = getSize(OldPtr, &OldHeader); 573 memcpy(NewPtr, OldTaggedPtr, Min(NewSize, OldSize)); 574 quarantineOrDeallocateChunk(OldPtr, &OldHeader, OldSize); 575 } 576 return NewPtr; 577 } 578 579 // TODO(kostyak): disable() is currently best-effort. There are some small 580 // windows of time when an allocation could still succeed after 581 // this function finishes. We will revisit that later. 582 void disable() { 583 initThreadMaybe(); 584 #ifdef GWP_ASAN_HOOKS 585 GuardedAlloc.disable(); 586 #endif 587 TSDRegistry.disable(); 588 Stats.disable(); 589 Quarantine.disable(); 590 Primary.disable(); 591 Secondary.disable(); 592 } 593 594 void enable() { 595 initThreadMaybe(); 596 Secondary.enable(); 597 Primary.enable(); 598 Quarantine.enable(); 599 Stats.enable(); 600 TSDRegistry.enable(); 601 #ifdef GWP_ASAN_HOOKS 602 GuardedAlloc.enable(); 603 #endif 604 } 605 606 // The function returns the amount of bytes required to store the statistics, 607 // which might be larger than the amount of bytes provided. Note that the 608 // statistics buffer is not necessarily constant between calls to this 609 // function. This can be called with a null buffer or zero size for buffer 610 // sizing purposes. 611 uptr getStats(char *Buffer, uptr Size) { 612 ScopedString Str(1024); 613 disable(); 614 const uptr Length = getStats(&Str) + 1; 615 enable(); 616 if (Length < Size) 617 Size = Length; 618 if (Buffer && Size) { 619 memcpy(Buffer, Str.data(), Size); 620 Buffer[Size - 1] = '\0'; 621 } 622 return Length; 623 } 624 625 void printStats() { 626 ScopedString Str(1024); 627 disable(); 628 getStats(&Str); 629 enable(); 630 Str.output(); 631 } 632 633 void releaseToOS() { 634 initThreadMaybe(); 635 Primary.releaseToOS(); 636 Secondary.releaseToOS(); 637 } 638 639 // Iterate over all chunks and call a callback for all busy chunks located 640 // within the provided memory range. Said callback must not use this allocator 641 // or a deadlock can ensue. This fits Android's malloc_iterate() needs. 642 void iterateOverChunks(uptr Base, uptr Size, iterate_callback Callback, 643 void *Arg) { 644 initThreadMaybe(); 645 const uptr From = Base; 646 const uptr To = Base + Size; 647 auto Lambda = [this, From, To, Callback, Arg](uptr Block) { 648 if (Block < From || Block >= To) 649 return; 650 uptr Chunk; 651 Chunk::UnpackedHeader Header; 652 if (getChunkFromBlock(Block, &Chunk, &Header) && 653 Header.State == Chunk::State::Allocated) { 654 uptr TaggedChunk = Chunk; 655 if (useMemoryTagging()) 656 TaggedChunk = loadTag(Chunk); 657 Callback(TaggedChunk, getSize(reinterpret_cast<void *>(Chunk), &Header), 658 Arg); 659 } 660 }; 661 Primary.iterateOverBlocks(Lambda); 662 Secondary.iterateOverBlocks(Lambda); 663 #ifdef GWP_ASAN_HOOKS 664 GuardedAlloc.iterate(reinterpret_cast<void *>(Base), Size, Callback, Arg); 665 #endif 666 } 667 668 bool canReturnNull() { 669 initThreadMaybe(); 670 return Options.MayReturnNull; 671 } 672 673 bool setOption(Option O, sptr Value) { 674 if (O == Option::ReleaseInterval) { 675 Primary.setReleaseToOsIntervalMs(static_cast<s32>(Value)); 676 Secondary.setReleaseToOsIntervalMs(static_cast<s32>(Value)); 677 return true; 678 } 679 return false; 680 } 681 682 // Return the usable size for a given chunk. Technically we lie, as we just 683 // report the actual size of a chunk. This is done to counteract code actively 684 // writing past the end of a chunk (like sqlite3) when the usable size allows 685 // for it, which then forces realloc to copy the usable size of a chunk as 686 // opposed to its actual size. 687 uptr getUsableSize(const void *Ptr) { 688 initThreadMaybe(); 689 if (UNLIKELY(!Ptr)) 690 return 0; 691 692 #ifdef GWP_ASAN_HOOKS 693 if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) 694 return GuardedAlloc.getSize(Ptr); 695 #endif // GWP_ASAN_HOOKS 696 697 Ptr = untagPointerMaybe(const_cast<void *>(Ptr)); 698 Chunk::UnpackedHeader Header; 699 Chunk::loadHeader(Cookie, Ptr, &Header); 700 // Getting the usable size of a chunk only makes sense if it's allocated. 701 if (UNLIKELY(Header.State != Chunk::State::Allocated)) 702 reportInvalidChunkState(AllocatorAction::Sizing, const_cast<void *>(Ptr)); 703 return getSize(Ptr, &Header); 704 } 705 706 void getStats(StatCounters S) { 707 initThreadMaybe(); 708 Stats.get(S); 709 } 710 711 // Returns true if the pointer provided was allocated by the current 712 // allocator instance, which is compliant with tcmalloc's ownership concept. 713 // A corrupted chunk will not be reported as owned, which is WAI. 714 bool isOwned(const void *Ptr) { 715 initThreadMaybe(); 716 #ifdef GWP_ASAN_HOOKS 717 if (GuardedAlloc.pointerIsMine(Ptr)) 718 return true; 719 #endif // GWP_ASAN_HOOKS 720 if (!Ptr || !isAligned(reinterpret_cast<uptr>(Ptr), MinAlignment)) 721 return false; 722 Ptr = untagPointerMaybe(const_cast<void *>(Ptr)); 723 Chunk::UnpackedHeader Header; 724 return Chunk::isValid(Cookie, Ptr, &Header) && 725 Header.State == Chunk::State::Allocated; 726 } 727 728 bool useMemoryTagging() { return Primary.useMemoryTagging(); } 729 730 void disableMemoryTagging() { Primary.disableMemoryTagging(); } 731 732 void setTrackAllocationStacks(bool Track) { 733 initThreadMaybe(); 734 Options.TrackAllocationStacks = Track; 735 } 736 737 void setFillContents(FillContentsMode FillContents) { 738 initThreadMaybe(); 739 Options.FillContents = FillContents; 740 } 741 742 const char *getStackDepotAddress() const { 743 return reinterpret_cast<const char *>(&Depot); 744 } 745 746 const char *getRegionInfoArrayAddress() const { 747 return Primary.getRegionInfoArrayAddress(); 748 } 749 750 static uptr getRegionInfoArraySize() { 751 return PrimaryT::getRegionInfoArraySize(); 752 } 753 754 static void getErrorInfo(struct scudo_error_info *ErrorInfo, 755 uintptr_t FaultAddr, const char *DepotPtr, 756 const char *RegionInfoPtr, const char *Memory, 757 const char *MemoryTags, uintptr_t MemoryAddr, 758 size_t MemorySize) { 759 *ErrorInfo = {}; 760 if (!PrimaryT::SupportsMemoryTagging || 761 MemoryAddr + MemorySize < MemoryAddr) 762 return; 763 764 uptr UntaggedFaultAddr = untagPointer(FaultAddr); 765 u8 FaultAddrTag = extractTag(FaultAddr); 766 BlockInfo Info = 767 PrimaryT::findNearestBlock(RegionInfoPtr, UntaggedFaultAddr); 768 769 auto GetGranule = [&](uptr Addr, const char **Data, uint8_t *Tag) -> bool { 770 if (Addr < MemoryAddr || 771 Addr + archMemoryTagGranuleSize() < Addr || 772 Addr + archMemoryTagGranuleSize() > MemoryAddr + MemorySize) 773 return false; 774 *Data = &Memory[Addr - MemoryAddr]; 775 *Tag = static_cast<u8>( 776 MemoryTags[(Addr - MemoryAddr) / archMemoryTagGranuleSize()]); 777 return true; 778 }; 779 780 auto ReadBlock = [&](uptr Addr, uptr *ChunkAddr, 781 Chunk::UnpackedHeader *Header, const u32 **Data, 782 u8 *Tag) { 783 const char *BlockBegin; 784 u8 BlockBeginTag; 785 if (!GetGranule(Addr, &BlockBegin, &BlockBeginTag)) 786 return false; 787 uptr ChunkOffset = getChunkOffsetFromBlock(BlockBegin); 788 *ChunkAddr = Addr + ChunkOffset; 789 790 const char *ChunkBegin; 791 if (!GetGranule(*ChunkAddr, &ChunkBegin, Tag)) 792 return false; 793 *Header = *reinterpret_cast<const Chunk::UnpackedHeader *>( 794 ChunkBegin - Chunk::getHeaderSize()); 795 *Data = reinterpret_cast<const u32 *>(ChunkBegin); 796 return true; 797 }; 798 799 auto *Depot = reinterpret_cast<const StackDepot *>(DepotPtr); 800 801 auto MaybeCollectTrace = [&](uintptr_t(&Trace)[MaxTraceSize], u32 Hash) { 802 uptr RingPos, Size; 803 if (!Depot->find(Hash, &RingPos, &Size)) 804 return; 805 for (unsigned I = 0; I != Size && I != MaxTraceSize; ++I) 806 Trace[I] = (*Depot)[RingPos + I]; 807 }; 808 809 size_t NextErrorReport = 0; 810 811 // First, check for UAF. 812 { 813 uptr ChunkAddr; 814 Chunk::UnpackedHeader Header; 815 const u32 *Data; 816 uint8_t Tag; 817 if (ReadBlock(Info.BlockBegin, &ChunkAddr, &Header, &Data, &Tag) && 818 Header.State != Chunk::State::Allocated && 819 Data[MemTagPrevTagIndex] == FaultAddrTag) { 820 auto *R = &ErrorInfo->reports[NextErrorReport++]; 821 R->error_type = USE_AFTER_FREE; 822 R->allocation_address = ChunkAddr; 823 R->allocation_size = Header.SizeOrUnusedBytes; 824 MaybeCollectTrace(R->allocation_trace, 825 Data[MemTagAllocationTraceIndex]); 826 R->allocation_tid = Data[MemTagAllocationTidIndex]; 827 MaybeCollectTrace(R->deallocation_trace, 828 Data[MemTagDeallocationTraceIndex]); 829 R->deallocation_tid = Data[MemTagDeallocationTidIndex]; 830 } 831 } 832 833 auto CheckOOB = [&](uptr BlockAddr) { 834 if (BlockAddr < Info.RegionBegin || BlockAddr >= Info.RegionEnd) 835 return false; 836 837 uptr ChunkAddr; 838 Chunk::UnpackedHeader Header; 839 const u32 *Data; 840 uint8_t Tag; 841 if (!ReadBlock(BlockAddr, &ChunkAddr, &Header, &Data, &Tag) || 842 Header.State != Chunk::State::Allocated || Tag != FaultAddrTag) 843 return false; 844 845 auto *R = &ErrorInfo->reports[NextErrorReport++]; 846 R->error_type = 847 UntaggedFaultAddr < ChunkAddr ? BUFFER_UNDERFLOW : BUFFER_OVERFLOW; 848 R->allocation_address = ChunkAddr; 849 R->allocation_size = Header.SizeOrUnusedBytes; 850 MaybeCollectTrace(R->allocation_trace, Data[MemTagAllocationTraceIndex]); 851 R->allocation_tid = Data[MemTagAllocationTidIndex]; 852 return NextErrorReport == 853 sizeof(ErrorInfo->reports) / sizeof(ErrorInfo->reports[0]); 854 }; 855 856 if (CheckOOB(Info.BlockBegin)) 857 return; 858 859 // Check for OOB in the 30 surrounding blocks. Beyond that we are likely to 860 // hit false positives. 861 for (int I = 1; I != 16; ++I) 862 if (CheckOOB(Info.BlockBegin + I * Info.BlockSize) || 863 CheckOOB(Info.BlockBegin - I * Info.BlockSize)) 864 return; 865 } 866 867 private: 868 using SecondaryT = typename Params::Secondary; 869 typedef typename PrimaryT::SizeClassMap SizeClassMap; 870 871 static const uptr MinAlignmentLog = SCUDO_MIN_ALIGNMENT_LOG; 872 static const uptr MaxAlignmentLog = 24U; // 16 MB seems reasonable. 873 static const uptr MinAlignment = 1UL << MinAlignmentLog; 874 static const uptr MaxAlignment = 1UL << MaxAlignmentLog; 875 static const uptr MaxAllowedMallocSize = 876 FIRST_32_SECOND_64(1UL << 31, 1ULL << 40); 877 878 static_assert(MinAlignment >= sizeof(Chunk::PackedHeader), 879 "Minimal alignment must at least cover a chunk header."); 880 static_assert(!PrimaryT::SupportsMemoryTagging || 881 MinAlignment >= archMemoryTagGranuleSize(), 882 ""); 883 884 static const u32 BlockMarker = 0x44554353U; 885 886 // These are indexes into an "array" of 32-bit values that store information 887 // inline with a chunk that is relevant to diagnosing memory tag faults, where 888 // 0 corresponds to the address of the user memory. This means that negative 889 // indexes may be used to store information about allocations, while positive 890 // indexes may only be used to store information about deallocations, because 891 // the user memory is in use until it has been deallocated. The smallest index 892 // that may be used is -2, which corresponds to 8 bytes before the user 893 // memory, because the chunk header size is 8 bytes and in allocators that 894 // support memory tagging the minimum alignment is at least the tag granule 895 // size (16 on aarch64), and the largest index that may be used is 3 because 896 // we are only guaranteed to have at least a granule's worth of space in the 897 // user memory. 898 static const sptr MemTagAllocationTraceIndex = -2; 899 static const sptr MemTagAllocationTidIndex = -1; 900 static const sptr MemTagDeallocationTraceIndex = 0; 901 static const sptr MemTagDeallocationTidIndex = 1; 902 static const sptr MemTagPrevTagIndex = 2; 903 904 static const uptr MaxTraceSize = 64; 905 906 GlobalStats Stats; 907 TSDRegistryT TSDRegistry; 908 PrimaryT Primary; 909 SecondaryT Secondary; 910 QuarantineT Quarantine; 911 912 u32 Cookie; 913 914 struct { 915 u8 MayReturnNull : 1; // may_return_null 916 FillContentsMode FillContents : 2; // zero_contents, pattern_fill_contents 917 u8 DeallocTypeMismatch : 1; // dealloc_type_mismatch 918 u8 DeleteSizeMismatch : 1; // delete_size_mismatch 919 u8 TrackAllocationStacks : 1; 920 u32 QuarantineMaxChunkSize; // quarantine_max_chunk_size 921 } Options; 922 923 #ifdef GWP_ASAN_HOOKS 924 gwp_asan::GuardedPoolAllocator GuardedAlloc; 925 #endif // GWP_ASAN_HOOKS 926 927 StackDepot Depot; 928 929 // The following might get optimized out by the compiler. 930 NOINLINE void performSanityChecks() { 931 // Verify that the header offset field can hold the maximum offset. In the 932 // case of the Secondary allocator, it takes care of alignment and the 933 // offset will always be small. In the case of the Primary, the worst case 934 // scenario happens in the last size class, when the backend allocation 935 // would already be aligned on the requested alignment, which would happen 936 // to be the maximum alignment that would fit in that size class. As a 937 // result, the maximum offset will be at most the maximum alignment for the 938 // last size class minus the header size, in multiples of MinAlignment. 939 Chunk::UnpackedHeader Header = {}; 940 const uptr MaxPrimaryAlignment = 1UL << getMostSignificantSetBitIndex( 941 SizeClassMap::MaxSize - MinAlignment); 942 const uptr MaxOffset = 943 (MaxPrimaryAlignment - Chunk::getHeaderSize()) >> MinAlignmentLog; 944 Header.Offset = MaxOffset & Chunk::OffsetMask; 945 if (UNLIKELY(Header.Offset != MaxOffset)) 946 reportSanityCheckError("offset"); 947 948 // Verify that we can fit the maximum size or amount of unused bytes in the 949 // header. Given that the Secondary fits the allocation to a page, the worst 950 // case scenario happens in the Primary. It will depend on the second to 951 // last and last class sizes, as well as the dynamic base for the Primary. 952 // The following is an over-approximation that works for our needs. 953 const uptr MaxSizeOrUnusedBytes = SizeClassMap::MaxSize - 1; 954 Header.SizeOrUnusedBytes = MaxSizeOrUnusedBytes; 955 if (UNLIKELY(Header.SizeOrUnusedBytes != MaxSizeOrUnusedBytes)) 956 reportSanityCheckError("size (or unused bytes)"); 957 958 const uptr LargestClassId = SizeClassMap::LargestClassId; 959 Header.ClassId = LargestClassId; 960 if (UNLIKELY(Header.ClassId != LargestClassId)) 961 reportSanityCheckError("class ID"); 962 } 963 964 static inline void *getBlockBegin(const void *Ptr, 965 Chunk::UnpackedHeader *Header) { 966 return reinterpret_cast<void *>( 967 reinterpret_cast<uptr>(Ptr) - Chunk::getHeaderSize() - 968 (static_cast<uptr>(Header->Offset) << MinAlignmentLog)); 969 } 970 971 // Return the size of a chunk as requested during its allocation. 972 inline uptr getSize(const void *Ptr, Chunk::UnpackedHeader *Header) { 973 const uptr SizeOrUnusedBytes = Header->SizeOrUnusedBytes; 974 if (LIKELY(Header->ClassId)) 975 return SizeOrUnusedBytes; 976 return SecondaryT::getBlockEnd(getBlockBegin(Ptr, Header)) - 977 reinterpret_cast<uptr>(Ptr) - SizeOrUnusedBytes; 978 } 979 980 ALWAYS_INLINE void initThreadMaybe(bool MinimalInit = false) { 981 TSDRegistry.initThreadMaybe(this, MinimalInit); 982 } 983 984 void quarantineOrDeallocateChunk(void *Ptr, Chunk::UnpackedHeader *Header, 985 uptr Size) { 986 Chunk::UnpackedHeader NewHeader = *Header; 987 if (UNLIKELY(NewHeader.ClassId && useMemoryTagging())) { 988 u8 PrevTag = extractTag(loadTag(reinterpret_cast<uptr>(Ptr))); 989 uptr TaggedBegin, TaggedEnd; 990 // Exclude the previous tag so that immediate use after free is detected 991 // 100% of the time. 992 setRandomTag(Ptr, Size, 1UL << PrevTag, &TaggedBegin, &TaggedEnd); 993 storeDeallocationStackMaybe(Ptr, PrevTag); 994 } 995 // If the quarantine is disabled, the actual size of a chunk is 0 or larger 996 // than the maximum allowed, we return a chunk directly to the backend. 997 // Logical Or can be short-circuited, which introduces unnecessary 998 // conditional jumps, so use bitwise Or and let the compiler be clever. 999 const bool BypassQuarantine = !Quarantine.getCacheSize() | !Size | 1000 (Size > Options.QuarantineMaxChunkSize); 1001 if (BypassQuarantine) { 1002 NewHeader.State = Chunk::State::Available; 1003 Chunk::compareExchangeHeader(Cookie, Ptr, &NewHeader, Header); 1004 void *BlockBegin = getBlockBegin(Ptr, &NewHeader); 1005 const uptr ClassId = NewHeader.ClassId; 1006 if (LIKELY(ClassId)) { 1007 bool UnlockRequired; 1008 auto *TSD = TSDRegistry.getTSDAndLock(&UnlockRequired); 1009 TSD->Cache.deallocate(ClassId, BlockBegin); 1010 if (UnlockRequired) 1011 TSD->unlock(); 1012 } else { 1013 Secondary.deallocate(BlockBegin); 1014 } 1015 } else { 1016 NewHeader.State = Chunk::State::Quarantined; 1017 Chunk::compareExchangeHeader(Cookie, Ptr, &NewHeader, Header); 1018 bool UnlockRequired; 1019 auto *TSD = TSDRegistry.getTSDAndLock(&UnlockRequired); 1020 Quarantine.put(&TSD->QuarantineCache, 1021 QuarantineCallback(*this, TSD->Cache), Ptr, Size); 1022 if (UnlockRequired) 1023 TSD->unlock(); 1024 } 1025 } 1026 1027 bool getChunkFromBlock(uptr Block, uptr *Chunk, 1028 Chunk::UnpackedHeader *Header) { 1029 *Chunk = 1030 Block + getChunkOffsetFromBlock(reinterpret_cast<const char *>(Block)); 1031 return Chunk::isValid(Cookie, reinterpret_cast<void *>(*Chunk), Header); 1032 } 1033 1034 static uptr getChunkOffsetFromBlock(const char *Block) { 1035 u32 Offset = 0; 1036 if (reinterpret_cast<const u32 *>(Block)[0] == BlockMarker) 1037 Offset = reinterpret_cast<const u32 *>(Block)[1]; 1038 return Offset + Chunk::getHeaderSize(); 1039 } 1040 1041 void storeAllocationStackMaybe(void *Ptr) { 1042 if (!UNLIKELY(Options.TrackAllocationStacks)) 1043 return; 1044 auto *Ptr32 = reinterpret_cast<u32 *>(Ptr); 1045 Ptr32[MemTagAllocationTraceIndex] = collectStackTrace(); 1046 Ptr32[MemTagAllocationTidIndex] = getThreadID(); 1047 } 1048 1049 void storeDeallocationStackMaybe(void *Ptr, uint8_t PrevTag) { 1050 if (!UNLIKELY(Options.TrackAllocationStacks)) 1051 return; 1052 1053 // Disable tag checks here so that we don't need to worry about zero sized 1054 // allocations. 1055 ScopedDisableMemoryTagChecks x; 1056 auto *Ptr32 = reinterpret_cast<u32 *>(Ptr); 1057 Ptr32[MemTagDeallocationTraceIndex] = collectStackTrace(); 1058 Ptr32[MemTagDeallocationTidIndex] = getThreadID(); 1059 Ptr32[MemTagPrevTagIndex] = PrevTag; 1060 } 1061 1062 uptr getStats(ScopedString *Str) { 1063 Primary.getStats(Str); 1064 Secondary.getStats(Str); 1065 Quarantine.getStats(Str); 1066 return Str->length(); 1067 } 1068 }; 1069 1070 } // namespace scudo 1071 1072 #endif // SCUDO_COMBINED_H_ 1073