1 //===-- scudo_allocator.cpp -------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// Scudo Hardened Allocator implementation. 11 /// It uses the sanitizer_common allocator as a base and aims at mitigating 12 /// heap corruption vulnerabilities. It provides a checksum-guarded chunk 13 /// header, a delayed free list, and additional sanity checks. 14 /// 15 //===----------------------------------------------------------------------===// 16 17 #include "scudo_allocator.h" 18 #include "scudo_crc32.h" 19 #include "scudo_flags.h" 20 #include "scudo_tsd.h" 21 #include "scudo_utils.h" 22 23 #include "sanitizer_common/sanitizer_allocator_checks.h" 24 #include "sanitizer_common/sanitizer_allocator_interface.h" 25 #include "sanitizer_common/sanitizer_quarantine.h" 26 27 #include <errno.h> 28 #include <string.h> 29 30 namespace __scudo { 31 32 // Global static cookie, initialized at start-up. 33 static uptr Cookie; 34 35 // We default to software CRC32 if the alternatives are not supported, either 36 // at compilation or at runtime. 37 static atomic_uint8_t HashAlgorithm = { CRC32Software }; 38 39 INLINE u32 computeCRC32(u32 Crc, uptr Value, uptr *Array, uptr ArraySize) { 40 // If the hardware CRC32 feature is defined here, it was enabled everywhere, 41 // as opposed to only for scudo_crc32.cpp. This means that other hardware 42 // specific instructions were likely emitted at other places, and as a 43 // result there is no reason to not use it here. 44 #if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) 45 Crc = CRC32_INTRINSIC(Crc, Value); 46 for (uptr i = 0; i < ArraySize; i++) 47 Crc = CRC32_INTRINSIC(Crc, Array[i]); 48 return Crc; 49 #else 50 if (atomic_load_relaxed(&HashAlgorithm) == CRC32Hardware) { 51 Crc = computeHardwareCRC32(Crc, Value); 52 for (uptr i = 0; i < ArraySize; i++) 53 Crc = computeHardwareCRC32(Crc, Array[i]); 54 return Crc; 55 } 56 Crc = computeSoftwareCRC32(Crc, Value); 57 for (uptr i = 0; i < ArraySize; i++) 58 Crc = computeSoftwareCRC32(Crc, Array[i]); 59 return Crc; 60 #endif // defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) 61 } 62 63 static ScudoBackendAllocator &getBackendAllocator(); 64 65 struct ScudoChunk : UnpackedHeader { 66 // We can't use the offset member of the chunk itself, as we would double 67 // fetch it without any warranty that it wouldn't have been tampered. To 68 // prevent this, we work with a local copy of the header. 69 void *getAllocBeg(UnpackedHeader *Header) { 70 return reinterpret_cast<void *>( 71 reinterpret_cast<uptr>(this) - (Header->Offset << MinAlignmentLog)); 72 } 73 74 // Returns the usable size for a chunk, meaning the amount of bytes from the 75 // beginning of the user data to the end of the backend allocated chunk. 76 uptr getUsableSize(UnpackedHeader *Header) { 77 uptr Size = 78 getBackendAllocator().getActuallyAllocatedSize(getAllocBeg(Header), 79 Header->FromPrimary); 80 if (Size == 0) 81 return 0; 82 return Size - AlignedChunkHeaderSize - (Header->Offset << MinAlignmentLog); 83 } 84 85 // Compute the checksum of the Chunk pointer and its ChunkHeader. 86 u16 computeChecksum(UnpackedHeader *Header) const { 87 UnpackedHeader ZeroChecksumHeader = *Header; 88 ZeroChecksumHeader.Checksum = 0; 89 uptr HeaderHolder[sizeof(UnpackedHeader) / sizeof(uptr)]; 90 memcpy(&HeaderHolder, &ZeroChecksumHeader, sizeof(HeaderHolder)); 91 u32 Crc = computeCRC32(static_cast<u32>(Cookie), 92 reinterpret_cast<uptr>(this), HeaderHolder, 93 ARRAY_SIZE(HeaderHolder)); 94 return static_cast<u16>(Crc); 95 } 96 97 // Checks the validity of a chunk by verifying its checksum. It doesn't 98 // incur termination in the event of an invalid chunk. 99 bool isValid() { 100 UnpackedHeader NewUnpackedHeader; 101 const AtomicPackedHeader *AtomicHeader = 102 reinterpret_cast<const AtomicPackedHeader *>(this); 103 PackedHeader NewPackedHeader = atomic_load_relaxed(AtomicHeader); 104 NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader); 105 return (NewUnpackedHeader.Checksum == computeChecksum(&NewUnpackedHeader)); 106 } 107 108 // Nulls out a chunk header. When returning the chunk to the backend, there 109 // is no need to store a valid ChunkAvailable header, as this would be 110 // computationally expensive. Zeroing out serves the same purpose by making 111 // the header invalid. In the extremely rare event where 0 would be a valid 112 // checksum for the chunk, the state of the chunk is ChunkAvailable anyway. 113 COMPILER_CHECK(ChunkAvailable == 0); 114 void eraseHeader() { 115 PackedHeader NullPackedHeader = 0; 116 AtomicPackedHeader *AtomicHeader = 117 reinterpret_cast<AtomicPackedHeader *>(this); 118 atomic_store_relaxed(AtomicHeader, NullPackedHeader); 119 } 120 121 // Loads and unpacks the header, verifying the checksum in the process. 122 void loadHeader(UnpackedHeader *NewUnpackedHeader) const { 123 const AtomicPackedHeader *AtomicHeader = 124 reinterpret_cast<const AtomicPackedHeader *>(this); 125 PackedHeader NewPackedHeader = atomic_load_relaxed(AtomicHeader); 126 *NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader); 127 if (UNLIKELY(NewUnpackedHeader->Checksum != 128 computeChecksum(NewUnpackedHeader))) { 129 dieWithMessage("ERROR: corrupted chunk header at address %p\n", this); 130 } 131 } 132 133 // Packs and stores the header, computing the checksum in the process. 134 void storeHeader(UnpackedHeader *NewUnpackedHeader) { 135 NewUnpackedHeader->Checksum = computeChecksum(NewUnpackedHeader); 136 PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader); 137 AtomicPackedHeader *AtomicHeader = 138 reinterpret_cast<AtomicPackedHeader *>(this); 139 atomic_store_relaxed(AtomicHeader, NewPackedHeader); 140 } 141 142 // Packs and stores the header, computing the checksum in the process. We 143 // compare the current header with the expected provided one to ensure that 144 // we are not being raced by a corruption occurring in another thread. 145 void compareExchangeHeader(UnpackedHeader *NewUnpackedHeader, 146 UnpackedHeader *OldUnpackedHeader) { 147 NewUnpackedHeader->Checksum = computeChecksum(NewUnpackedHeader); 148 PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader); 149 PackedHeader OldPackedHeader = bit_cast<PackedHeader>(*OldUnpackedHeader); 150 AtomicPackedHeader *AtomicHeader = 151 reinterpret_cast<AtomicPackedHeader *>(this); 152 if (UNLIKELY(!atomic_compare_exchange_strong(AtomicHeader, 153 &OldPackedHeader, 154 NewPackedHeader, 155 memory_order_relaxed))) { 156 dieWithMessage("ERROR: race on chunk header at address %p\n", this); 157 } 158 } 159 }; 160 161 ScudoChunk *getScudoChunk(uptr UserBeg) { 162 return reinterpret_cast<ScudoChunk *>(UserBeg - AlignedChunkHeaderSize); 163 } 164 165 struct QuarantineCallback { 166 explicit QuarantineCallback(AllocatorCache *Cache) 167 : Cache_(Cache) {} 168 169 // Chunk recycling function, returns a quarantined chunk to the backend, 170 // first making sure it hasn't been tampered with. 171 void Recycle(ScudoChunk *Chunk) { 172 UnpackedHeader Header; 173 Chunk->loadHeader(&Header); 174 if (UNLIKELY(Header.State != ChunkQuarantine)) { 175 dieWithMessage("ERROR: invalid chunk state when recycling address %p\n", 176 Chunk); 177 } 178 Chunk->eraseHeader(); 179 void *Ptr = Chunk->getAllocBeg(&Header); 180 if (Header.FromPrimary) 181 getBackendAllocator().deallocatePrimary(Cache_, Ptr); 182 else 183 getBackendAllocator().deallocateSecondary(Ptr); 184 } 185 186 // Internal quarantine allocation and deallocation functions. We first check 187 // that the batches are indeed serviced by the Primary. 188 // TODO(kostyak): figure out the best way to protect the batches. 189 COMPILER_CHECK(sizeof(QuarantineBatch) < SizeClassMap::kMaxSize); 190 void *Allocate(uptr Size) { 191 return getBackendAllocator().allocatePrimary(Cache_, Size); 192 } 193 194 void Deallocate(void *Ptr) { 195 getBackendAllocator().deallocatePrimary(Cache_, Ptr); 196 } 197 198 AllocatorCache *Cache_; 199 }; 200 201 typedef Quarantine<QuarantineCallback, ScudoChunk> ScudoQuarantine; 202 typedef ScudoQuarantine::Cache ScudoQuarantineCache; 203 COMPILER_CHECK(sizeof(ScudoQuarantineCache) <= 204 sizeof(ScudoTSD::QuarantineCachePlaceHolder)); 205 206 ScudoQuarantineCache *getQuarantineCache(ScudoTSD *TSD) { 207 return reinterpret_cast<ScudoQuarantineCache *>( 208 TSD->QuarantineCachePlaceHolder); 209 } 210 211 struct ScudoAllocator { 212 static const uptr MaxAllowedMallocSize = 213 FIRST_32_SECOND_64(2UL << 30, 1ULL << 40); 214 215 typedef ReturnNullOrDieOnFailure FailureHandler; 216 217 ScudoBackendAllocator BackendAllocator; 218 ScudoQuarantine AllocatorQuarantine; 219 220 StaticSpinMutex GlobalPrngMutex; 221 ScudoPrng GlobalPrng; 222 223 u32 QuarantineChunksUpToSize; 224 225 bool DeallocationTypeMismatch; 226 bool ZeroContents; 227 bool DeleteSizeMismatch; 228 229 explicit ScudoAllocator(LinkerInitialized) 230 : AllocatorQuarantine(LINKER_INITIALIZED) {} 231 232 void init() { 233 SanitizerToolName = "Scudo"; 234 initFlags(); 235 236 // Verify that the header offset field can hold the maximum offset. In the 237 // case of the Secondary allocator, it takes care of alignment and the 238 // offset will always be 0. In the case of the Primary, the worst case 239 // scenario happens in the last size class, when the backend allocation 240 // would already be aligned on the requested alignment, which would happen 241 // to be the maximum alignment that would fit in that size class. As a 242 // result, the maximum offset will be at most the maximum alignment for the 243 // last size class minus the header size, in multiples of MinAlignment. 244 UnpackedHeader Header = {}; 245 uptr MaxPrimaryAlignment = 246 1 << MostSignificantSetBitIndex(SizeClassMap::kMaxSize - MinAlignment); 247 uptr MaxOffset = 248 (MaxPrimaryAlignment - AlignedChunkHeaderSize) >> MinAlignmentLog; 249 Header.Offset = MaxOffset; 250 if (Header.Offset != MaxOffset) { 251 dieWithMessage("ERROR: the maximum possible offset doesn't fit in the " 252 "header\n"); 253 } 254 // Verify that we can fit the maximum size or amount of unused bytes in the 255 // header. Given that the Secondary fits the allocation to a page, the worst 256 // case scenario happens in the Primary. It will depend on the second to 257 // last and last class sizes, as well as the dynamic base for the Primary. 258 // The following is an over-approximation that works for our needs. 259 uptr MaxSizeOrUnusedBytes = SizeClassMap::kMaxSize - 1; 260 Header.SizeOrUnusedBytes = MaxSizeOrUnusedBytes; 261 if (Header.SizeOrUnusedBytes != MaxSizeOrUnusedBytes) { 262 dieWithMessage("ERROR: the maximum possible unused bytes doesn't fit in " 263 "the header\n"); 264 } 265 266 // Check if hardware CRC32 is supported in the binary and by the platform, 267 // if so, opt for the CRC32 hardware version of the checksum. 268 if (computeHardwareCRC32 && testCPUFeature(CRC32CPUFeature)) 269 atomic_store_relaxed(&HashAlgorithm, CRC32Hardware); 270 271 SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); 272 BackendAllocator.init(common_flags()->allocator_release_to_os_interval_ms); 273 AllocatorQuarantine.Init( 274 static_cast<uptr>(getFlags()->QuarantineSizeKb) << 10, 275 static_cast<uptr>(getFlags()->ThreadLocalQuarantineSizeKb) << 10); 276 QuarantineChunksUpToSize = getFlags()->QuarantineChunksUpToSize; 277 DeallocationTypeMismatch = getFlags()->DeallocationTypeMismatch; 278 DeleteSizeMismatch = getFlags()->DeleteSizeMismatch; 279 ZeroContents = getFlags()->ZeroContents; 280 281 GlobalPrng.init(); 282 Cookie = GlobalPrng.getU64(); 283 } 284 285 // Helper function that checks for a valid Scudo chunk. nullptr isn't. 286 bool isValidPointer(const void *UserPtr) { 287 initThreadMaybe(); 288 if (UNLIKELY(!UserPtr)) 289 return false; 290 uptr UserBeg = reinterpret_cast<uptr>(UserPtr); 291 if (!IsAligned(UserBeg, MinAlignment)) 292 return false; 293 return getScudoChunk(UserBeg)->isValid(); 294 } 295 296 // Allocates a chunk. 297 void *allocate(uptr Size, uptr Alignment, AllocType Type, 298 bool ForceZeroContents = false) { 299 initThreadMaybe(); 300 if (UNLIKELY(Alignment > MaxAlignment)) 301 return FailureHandler::OnBadRequest(); 302 if (UNLIKELY(Alignment < MinAlignment)) 303 Alignment = MinAlignment; 304 if (UNLIKELY(Size >= MaxAllowedMallocSize)) 305 return FailureHandler::OnBadRequest(); 306 if (UNLIKELY(Size == 0)) 307 Size = 1; 308 309 uptr NeededSize = RoundUpTo(Size, MinAlignment) + AlignedChunkHeaderSize; 310 uptr AlignedSize = (Alignment > MinAlignment) ? 311 NeededSize + (Alignment - AlignedChunkHeaderSize) : NeededSize; 312 if (UNLIKELY(AlignedSize >= MaxAllowedMallocSize)) 313 return FailureHandler::OnBadRequest(); 314 315 // Primary and Secondary backed allocations have a different treatment. We 316 // deal with alignment requirements of Primary serviced allocations here, 317 // but the Secondary will take care of its own alignment needs. 318 bool FromPrimary = PrimaryAllocator::CanAllocate(AlignedSize, MinAlignment); 319 320 void *Ptr; 321 u8 Salt; 322 uptr AllocSize; 323 if (FromPrimary) { 324 AllocSize = AlignedSize; 325 ScudoTSD *TSD = getTSDAndLock(); 326 Salt = TSD->Prng.getU8(); 327 Ptr = BackendAllocator.allocatePrimary(&TSD->Cache, AllocSize); 328 TSD->unlock(); 329 } else { 330 { 331 SpinMutexLock l(&GlobalPrngMutex); 332 Salt = GlobalPrng.getU8(); 333 } 334 AllocSize = NeededSize; 335 Ptr = BackendAllocator.allocateSecondary(AllocSize, Alignment); 336 } 337 if (UNLIKELY(!Ptr)) 338 return FailureHandler::OnOOM(); 339 340 // If requested, we will zero out the entire contents of the returned chunk. 341 if ((ForceZeroContents || ZeroContents) && FromPrimary) 342 memset(Ptr, 0, BackendAllocator.getActuallyAllocatedSize( 343 Ptr, /*FromPrimary=*/true)); 344 345 UnpackedHeader Header = {}; 346 uptr AllocBeg = reinterpret_cast<uptr>(Ptr); 347 uptr UserBeg = AllocBeg + AlignedChunkHeaderSize; 348 if (UNLIKELY(!IsAligned(UserBeg, Alignment))) { 349 // Since the Secondary takes care of alignment, a non-aligned pointer 350 // means it is from the Primary. It is also the only case where the offset 351 // field of the header would be non-zero. 352 CHECK(FromPrimary); 353 UserBeg = RoundUpTo(UserBeg, Alignment); 354 uptr Offset = UserBeg - AlignedChunkHeaderSize - AllocBeg; 355 Header.Offset = Offset >> MinAlignmentLog; 356 } 357 CHECK_LE(UserBeg + Size, AllocBeg + AllocSize); 358 Header.State = ChunkAllocated; 359 Header.AllocType = Type; 360 if (FromPrimary) { 361 Header.FromPrimary = 1; 362 Header.SizeOrUnusedBytes = Size; 363 } else { 364 // The secondary fits the allocations to a page, so the amount of unused 365 // bytes is the difference between the end of the user allocation and the 366 // next page boundary. 367 uptr PageSize = GetPageSizeCached(); 368 uptr TrailingBytes = (UserBeg + Size) & (PageSize - 1); 369 if (TrailingBytes) 370 Header.SizeOrUnusedBytes = PageSize - TrailingBytes; 371 } 372 Header.Salt = Salt; 373 getScudoChunk(UserBeg)->storeHeader(&Header); 374 void *UserPtr = reinterpret_cast<void *>(UserBeg); 375 // if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(UserPtr, Size); 376 return UserPtr; 377 } 378 379 // Place a chunk in the quarantine or directly deallocate it in the event of 380 // a zero-sized quarantine, or if the size of the chunk is greater than the 381 // quarantine chunk size threshold. 382 void quarantineOrDeallocateChunk(ScudoChunk *Chunk, UnpackedHeader *Header, 383 uptr Size) { 384 const bool BypassQuarantine = (AllocatorQuarantine.GetCacheSize() == 0) || 385 (Size > QuarantineChunksUpToSize); 386 if (BypassQuarantine) { 387 Chunk->eraseHeader(); 388 void *Ptr = Chunk->getAllocBeg(Header); 389 if (Header->FromPrimary) { 390 ScudoTSD *TSD = getTSDAndLock(); 391 getBackendAllocator().deallocatePrimary(&TSD->Cache, Ptr); 392 TSD->unlock(); 393 } else { 394 getBackendAllocator().deallocateSecondary(Ptr); 395 } 396 } else { 397 // If a small memory amount was allocated with a larger alignment, we want 398 // to take that into account. Otherwise the Quarantine would be filled 399 // with tiny chunks, taking a lot of VA memory. This is an approximation 400 // of the usable size, that allows us to not call 401 // GetActuallyAllocatedSize. 402 uptr EstimatedSize = Size + (Header->Offset << MinAlignmentLog); 403 UnpackedHeader NewHeader = *Header; 404 NewHeader.State = ChunkQuarantine; 405 Chunk->compareExchangeHeader(&NewHeader, Header); 406 ScudoTSD *TSD = getTSDAndLock(); 407 AllocatorQuarantine.Put(getQuarantineCache(TSD), 408 QuarantineCallback(&TSD->Cache), 409 Chunk, EstimatedSize); 410 TSD->unlock(); 411 } 412 } 413 414 // Deallocates a Chunk, which means adding it to the delayed free list (or 415 // Quarantine). 416 void deallocate(void *UserPtr, uptr DeleteSize, AllocType Type) { 417 // For a deallocation, we only ensure minimal initialization, meaning thread 418 // local data will be left uninitialized for now (when using ELF TLS). The 419 // fallback cache will be used instead. This is a workaround for a situation 420 // where the only heap operation performed in a thread would be a free past 421 // the TLS destructors, ending up in initialized thread specific data never 422 // being destroyed properly. Any other heap operation will do a full init. 423 initThreadMaybe(/*MinimalInit=*/true); 424 // if (&__sanitizer_free_hook) __sanitizer_free_hook(UserPtr); 425 if (UNLIKELY(!UserPtr)) 426 return; 427 uptr UserBeg = reinterpret_cast<uptr>(UserPtr); 428 if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) { 429 dieWithMessage("ERROR: attempted to deallocate a chunk not properly " 430 "aligned at address %p\n", UserPtr); 431 } 432 ScudoChunk *Chunk = getScudoChunk(UserBeg); 433 UnpackedHeader Header; 434 Chunk->loadHeader(&Header); 435 if (UNLIKELY(Header.State != ChunkAllocated)) { 436 dieWithMessage("ERROR: invalid chunk state when deallocating address " 437 "%p\n", UserPtr); 438 } 439 if (DeallocationTypeMismatch) { 440 // The deallocation type has to match the allocation one. 441 if (Header.AllocType != Type) { 442 // With the exception of memalign'd Chunks, that can be still be free'd. 443 if (Header.AllocType != FromMemalign || Type != FromMalloc) { 444 dieWithMessage("ERROR: allocation type mismatch when deallocating " 445 "address %p\n", UserPtr); 446 } 447 } 448 } 449 uptr Size = Header.FromPrimary ? Header.SizeOrUnusedBytes : 450 Chunk->getUsableSize(&Header) - Header.SizeOrUnusedBytes; 451 if (DeleteSizeMismatch) { 452 if (DeleteSize && DeleteSize != Size) { 453 dieWithMessage("ERROR: invalid sized delete on chunk at address %p\n", 454 UserPtr); 455 } 456 } 457 quarantineOrDeallocateChunk(Chunk, &Header, Size); 458 } 459 460 // Reallocates a chunk. We can save on a new allocation if the new requested 461 // size still fits in the chunk. 462 void *reallocate(void *OldPtr, uptr NewSize) { 463 initThreadMaybe(); 464 uptr UserBeg = reinterpret_cast<uptr>(OldPtr); 465 if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) { 466 dieWithMessage("ERROR: attempted to reallocate a chunk not properly " 467 "aligned at address %p\n", OldPtr); 468 } 469 ScudoChunk *Chunk = getScudoChunk(UserBeg); 470 UnpackedHeader OldHeader; 471 Chunk->loadHeader(&OldHeader); 472 if (UNLIKELY(OldHeader.State != ChunkAllocated)) { 473 dieWithMessage("ERROR: invalid chunk state when reallocating address " 474 "%p\n", OldPtr); 475 } 476 if (DeallocationTypeMismatch) { 477 if (UNLIKELY(OldHeader.AllocType != FromMalloc)) { 478 dieWithMessage("ERROR: allocation type mismatch when reallocating " 479 "address %p\n", OldPtr); 480 } 481 } 482 uptr UsableSize = Chunk->getUsableSize(&OldHeader); 483 // The new size still fits in the current chunk, and the size difference 484 // is reasonable. 485 if (NewSize <= UsableSize && 486 (UsableSize - NewSize) < (SizeClassMap::kMaxSize / 2)) { 487 UnpackedHeader NewHeader = OldHeader; 488 NewHeader.SizeOrUnusedBytes = 489 OldHeader.FromPrimary ? NewSize : UsableSize - NewSize; 490 Chunk->compareExchangeHeader(&NewHeader, &OldHeader); 491 return OldPtr; 492 } 493 // Otherwise, we have to allocate a new chunk and copy the contents of the 494 // old one. 495 void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc); 496 if (NewPtr) { 497 uptr OldSize = OldHeader.FromPrimary ? OldHeader.SizeOrUnusedBytes : 498 UsableSize - OldHeader.SizeOrUnusedBytes; 499 memcpy(NewPtr, OldPtr, Min(NewSize, UsableSize)); 500 quarantineOrDeallocateChunk(Chunk, &OldHeader, OldSize); 501 } 502 return NewPtr; 503 } 504 505 // Helper function that returns the actual usable size of a chunk. 506 uptr getUsableSize(const void *Ptr) { 507 initThreadMaybe(); 508 if (UNLIKELY(!Ptr)) 509 return 0; 510 uptr UserBeg = reinterpret_cast<uptr>(Ptr); 511 ScudoChunk *Chunk = getScudoChunk(UserBeg); 512 UnpackedHeader Header; 513 Chunk->loadHeader(&Header); 514 // Getting the usable size of a chunk only makes sense if it's allocated. 515 if (UNLIKELY(Header.State != ChunkAllocated)) { 516 dieWithMessage("ERROR: invalid chunk state when sizing address %p\n", 517 Ptr); 518 } 519 return Chunk->getUsableSize(&Header); 520 } 521 522 void *calloc(uptr NMemB, uptr Size) { 523 initThreadMaybe(); 524 if (UNLIKELY(CheckForCallocOverflow(NMemB, Size))) 525 return FailureHandler::OnBadRequest(); 526 return allocate(NMemB * Size, MinAlignment, FromMalloc, true); 527 } 528 529 void commitBack(ScudoTSD *TSD) { 530 AllocatorQuarantine.Drain(getQuarantineCache(TSD), 531 QuarantineCallback(&TSD->Cache)); 532 BackendAllocator.destroyCache(&TSD->Cache); 533 } 534 535 uptr getStats(AllocatorStat StatType) { 536 initThreadMaybe(); 537 uptr stats[AllocatorStatCount]; 538 BackendAllocator.getStats(stats); 539 return stats[StatType]; 540 } 541 542 void *handleBadRequest() { 543 initThreadMaybe(); 544 return FailureHandler::OnBadRequest(); 545 } 546 }; 547 548 static ScudoAllocator Instance(LINKER_INITIALIZED); 549 550 static ScudoBackendAllocator &getBackendAllocator() { 551 return Instance.BackendAllocator; 552 } 553 554 void initScudo() { 555 Instance.init(); 556 } 557 558 void ScudoTSD::init(bool Shared) { 559 UnlockRequired = Shared; 560 getBackendAllocator().initCache(&Cache); 561 Prng.init(); 562 memset(QuarantineCachePlaceHolder, 0, sizeof(QuarantineCachePlaceHolder)); 563 } 564 565 void ScudoTSD::commitBack() { 566 Instance.commitBack(this); 567 } 568 569 void *scudoMalloc(uptr Size, AllocType Type) { 570 return SetErrnoOnNull(Instance.allocate(Size, MinAlignment, Type)); 571 } 572 573 void scudoFree(void *Ptr, AllocType Type) { 574 Instance.deallocate(Ptr, 0, Type); 575 } 576 577 void scudoSizedFree(void *Ptr, uptr Size, AllocType Type) { 578 Instance.deallocate(Ptr, Size, Type); 579 } 580 581 void *scudoRealloc(void *Ptr, uptr Size) { 582 if (!Ptr) 583 return SetErrnoOnNull(Instance.allocate(Size, MinAlignment, FromMalloc)); 584 if (Size == 0) { 585 Instance.deallocate(Ptr, 0, FromMalloc); 586 return nullptr; 587 } 588 return SetErrnoOnNull(Instance.reallocate(Ptr, Size)); 589 } 590 591 void *scudoCalloc(uptr NMemB, uptr Size) { 592 return SetErrnoOnNull(Instance.calloc(NMemB, Size)); 593 } 594 595 void *scudoValloc(uptr Size) { 596 return SetErrnoOnNull( 597 Instance.allocate(Size, GetPageSizeCached(), FromMemalign)); 598 } 599 600 void *scudoPvalloc(uptr Size) { 601 uptr PageSize = GetPageSizeCached(); 602 if (UNLIKELY(CheckForPvallocOverflow(Size, PageSize))) { 603 errno = ENOMEM; 604 return Instance.handleBadRequest(); 605 } 606 // pvalloc(0) should allocate one page. 607 Size = Size ? RoundUpTo(Size, PageSize) : PageSize; 608 return SetErrnoOnNull(Instance.allocate(Size, PageSize, FromMemalign)); 609 } 610 611 void *scudoMemalign(uptr Alignment, uptr Size) { 612 if (UNLIKELY(!IsPowerOfTwo(Alignment))) { 613 errno = EINVAL; 614 return Instance.handleBadRequest(); 615 } 616 return SetErrnoOnNull(Instance.allocate(Size, Alignment, FromMemalign)); 617 } 618 619 int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) { 620 if (UNLIKELY(!CheckPosixMemalignAlignment(Alignment))) { 621 Instance.handleBadRequest(); 622 return EINVAL; 623 } 624 void *Ptr = Instance.allocate(Size, Alignment, FromMemalign); 625 if (UNLIKELY(!Ptr)) 626 return ENOMEM; 627 *MemPtr = Ptr; 628 return 0; 629 } 630 631 void *scudoAlignedAlloc(uptr Alignment, uptr Size) { 632 if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(Alignment, Size))) { 633 errno = EINVAL; 634 return Instance.handleBadRequest(); 635 } 636 return SetErrnoOnNull(Instance.allocate(Size, Alignment, FromMalloc)); 637 } 638 639 uptr scudoMallocUsableSize(void *Ptr) { 640 return Instance.getUsableSize(Ptr); 641 } 642 643 } // namespace __scudo 644 645 using namespace __scudo; 646 647 // MallocExtension helper functions 648 649 uptr __sanitizer_get_current_allocated_bytes() { 650 return Instance.getStats(AllocatorStatAllocated); 651 } 652 653 uptr __sanitizer_get_heap_size() { 654 return Instance.getStats(AllocatorStatMapped); 655 } 656 657 uptr __sanitizer_get_free_bytes() { 658 return 1; 659 } 660 661 uptr __sanitizer_get_unmapped_bytes() { 662 return 1; 663 } 664 665 uptr __sanitizer_get_estimated_allocated_size(uptr size) { 666 return size; 667 } 668 669 int __sanitizer_get_ownership(const void *Ptr) { 670 return Instance.isValidPointer(Ptr); 671 } 672 673 uptr __sanitizer_get_allocated_size(const void *Ptr) { 674 return Instance.getUsableSize(Ptr); 675 } 676