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 AllocatorOptions { 166 u32 QuarantineSizeKb; 167 u32 ThreadLocalQuarantineSizeKb; 168 u32 QuarantineChunksUpToSize; 169 bool MayReturnNull; 170 s32 ReleaseToOSIntervalMs; 171 bool DeallocationTypeMismatch; 172 bool DeleteSizeMismatch; 173 bool ZeroContents; 174 175 void setFrom(const Flags *f, const CommonFlags *cf); 176 }; 177 178 void AllocatorOptions::setFrom(const Flags *f, const CommonFlags *cf) { 179 MayReturnNull = cf->allocator_may_return_null; 180 ReleaseToOSIntervalMs = cf->allocator_release_to_os_interval_ms; 181 QuarantineSizeKb = f->QuarantineSizeKb; 182 ThreadLocalQuarantineSizeKb = f->ThreadLocalQuarantineSizeKb; 183 QuarantineChunksUpToSize = f->QuarantineChunksUpToSize; 184 DeallocationTypeMismatch = f->DeallocationTypeMismatch; 185 DeleteSizeMismatch = f->DeleteSizeMismatch; 186 ZeroContents = f->ZeroContents; 187 } 188 189 static void initScudoInternal(const AllocatorOptions &Options); 190 191 static bool ScudoInitIsRunning = false; 192 193 void initScudo() { 194 SanitizerToolName = "Scudo"; 195 CHECK(!ScudoInitIsRunning && "Scudo init calls itself!"); 196 ScudoInitIsRunning = true; 197 198 // Check if hardware CRC32 is supported in the binary and by the platform, if 199 // so, opt for the CRC32 hardware version of the checksum. 200 if (computeHardwareCRC32 && testCPUFeature(CRC32CPUFeature)) 201 atomic_store_relaxed(&HashAlgorithm, CRC32Hardware); 202 203 initFlags(); 204 205 AllocatorOptions Options; 206 Options.setFrom(getFlags(), common_flags()); 207 initScudoInternal(Options); 208 209 // TODO(kostyak): determine if MaybeStartBackgroudThread could be of some use. 210 211 ScudoInitIsRunning = false; 212 } 213 214 struct QuarantineCallback { 215 explicit QuarantineCallback(AllocatorCache *Cache) 216 : Cache_(Cache) {} 217 218 // Chunk recycling function, returns a quarantined chunk to the backend, 219 // first making sure it hasn't been tampered with. 220 void Recycle(ScudoChunk *Chunk) { 221 UnpackedHeader Header; 222 Chunk->loadHeader(&Header); 223 if (UNLIKELY(Header.State != ChunkQuarantine)) { 224 dieWithMessage("ERROR: invalid chunk state when recycling address %p\n", 225 Chunk); 226 } 227 Chunk->eraseHeader(); 228 void *Ptr = Chunk->getAllocBeg(&Header); 229 if (Header.FromPrimary) 230 getBackendAllocator().deallocatePrimary(Cache_, Ptr); 231 else 232 getBackendAllocator().deallocateSecondary(Ptr); 233 } 234 235 // Internal quarantine allocation and deallocation functions. We first check 236 // that the batches are indeed serviced by the Primary. 237 // TODO(kostyak): figure out the best way to protect the batches. 238 COMPILER_CHECK(sizeof(QuarantineBatch) < SizeClassMap::kMaxSize); 239 void *Allocate(uptr Size) { 240 return getBackendAllocator().allocatePrimary(Cache_, Size); 241 } 242 243 void Deallocate(void *Ptr) { 244 getBackendAllocator().deallocatePrimary(Cache_, Ptr); 245 } 246 247 AllocatorCache *Cache_; 248 }; 249 250 typedef Quarantine<QuarantineCallback, ScudoChunk> ScudoQuarantine; 251 typedef ScudoQuarantine::Cache ScudoQuarantineCache; 252 COMPILER_CHECK(sizeof(ScudoQuarantineCache) <= 253 sizeof(ScudoTSD::QuarantineCachePlaceHolder)); 254 255 ScudoQuarantineCache *getQuarantineCache(ScudoTSD *TSD) { 256 return reinterpret_cast<ScudoQuarantineCache *>( 257 TSD->QuarantineCachePlaceHolder); 258 } 259 260 struct ScudoAllocator { 261 static const uptr MaxAllowedMallocSize = 262 FIRST_32_SECOND_64(2UL << 30, 1ULL << 40); 263 264 typedef ReturnNullOrDieOnFailure FailureHandler; 265 266 ScudoBackendAllocator BackendAllocator; 267 ScudoQuarantine AllocatorQuarantine; 268 269 StaticSpinMutex GlobalPrngMutex; 270 ScudoPrng GlobalPrng; 271 272 u32 QuarantineChunksUpToSize; 273 274 bool DeallocationTypeMismatch; 275 bool ZeroContents; 276 bool DeleteSizeMismatch; 277 278 explicit ScudoAllocator(LinkerInitialized) 279 : AllocatorQuarantine(LINKER_INITIALIZED) {} 280 281 void init(const AllocatorOptions &Options) { 282 // Verify that the header offset field can hold the maximum offset. In the 283 // case of the Secondary allocator, it takes care of alignment and the 284 // offset will always be 0. In the case of the Primary, the worst case 285 // scenario happens in the last size class, when the backend allocation 286 // would already be aligned on the requested alignment, which would happen 287 // to be the maximum alignment that would fit in that size class. As a 288 // result, the maximum offset will be at most the maximum alignment for the 289 // last size class minus the header size, in multiples of MinAlignment. 290 UnpackedHeader Header = {}; 291 uptr MaxPrimaryAlignment = 292 1 << MostSignificantSetBitIndex(SizeClassMap::kMaxSize - MinAlignment); 293 uptr MaxOffset = 294 (MaxPrimaryAlignment - AlignedChunkHeaderSize) >> MinAlignmentLog; 295 Header.Offset = MaxOffset; 296 if (Header.Offset != MaxOffset) { 297 dieWithMessage("ERROR: the maximum possible offset doesn't fit in the " 298 "header\n"); 299 } 300 // Verify that we can fit the maximum size or amount of unused bytes in the 301 // header. Given that the Secondary fits the allocation to a page, the worst 302 // case scenario happens in the Primary. It will depend on the second to 303 // last and last class sizes, as well as the dynamic base for the Primary. 304 // The following is an over-approximation that works for our needs. 305 uptr MaxSizeOrUnusedBytes = SizeClassMap::kMaxSize - 1; 306 Header.SizeOrUnusedBytes = MaxSizeOrUnusedBytes; 307 if (Header.SizeOrUnusedBytes != MaxSizeOrUnusedBytes) { 308 dieWithMessage("ERROR: the maximum possible unused bytes doesn't fit in " 309 "the header\n"); 310 } 311 312 DeallocationTypeMismatch = Options.DeallocationTypeMismatch; 313 DeleteSizeMismatch = Options.DeleteSizeMismatch; 314 ZeroContents = Options.ZeroContents; 315 SetAllocatorMayReturnNull(Options.MayReturnNull); 316 BackendAllocator.init(Options.ReleaseToOSIntervalMs); 317 AllocatorQuarantine.Init( 318 static_cast<uptr>(Options.QuarantineSizeKb) << 10, 319 static_cast<uptr>(Options.ThreadLocalQuarantineSizeKb) << 10); 320 QuarantineChunksUpToSize = Options.QuarantineChunksUpToSize; 321 GlobalPrng.init(); 322 Cookie = GlobalPrng.getU64(); 323 } 324 325 // Helper function that checks for a valid Scudo chunk. nullptr isn't. 326 bool isValidPointer(const void *UserPtr) { 327 initThreadMaybe(); 328 if (UNLIKELY(!UserPtr)) 329 return false; 330 uptr UserBeg = reinterpret_cast<uptr>(UserPtr); 331 if (!IsAligned(UserBeg, MinAlignment)) 332 return false; 333 return getScudoChunk(UserBeg)->isValid(); 334 } 335 336 // Allocates a chunk. 337 void *allocate(uptr Size, uptr Alignment, AllocType Type, 338 bool ForceZeroContents = false) { 339 initThreadMaybe(); 340 if (UNLIKELY(Alignment > MaxAlignment)) 341 return FailureHandler::OnBadRequest(); 342 if (UNLIKELY(Alignment < MinAlignment)) 343 Alignment = MinAlignment; 344 if (UNLIKELY(Size >= MaxAllowedMallocSize)) 345 return FailureHandler::OnBadRequest(); 346 if (UNLIKELY(Size == 0)) 347 Size = 1; 348 349 uptr NeededSize = RoundUpTo(Size, MinAlignment) + AlignedChunkHeaderSize; 350 uptr AlignedSize = (Alignment > MinAlignment) ? 351 NeededSize + (Alignment - AlignedChunkHeaderSize) : NeededSize; 352 if (UNLIKELY(AlignedSize >= MaxAllowedMallocSize)) 353 return FailureHandler::OnBadRequest(); 354 355 // Primary and Secondary backed allocations have a different treatment. We 356 // deal with alignment requirements of Primary serviced allocations here, 357 // but the Secondary will take care of its own alignment needs. 358 bool FromPrimary = PrimaryAllocator::CanAllocate(AlignedSize, MinAlignment); 359 360 void *Ptr; 361 u8 Salt; 362 uptr AllocSize; 363 if (FromPrimary) { 364 AllocSize = AlignedSize; 365 ScudoTSD *TSD = getTSDAndLock(); 366 Salt = TSD->Prng.getU8(); 367 Ptr = BackendAllocator.allocatePrimary(&TSD->Cache, AllocSize); 368 TSD->unlock(); 369 } else { 370 { 371 SpinMutexLock l(&GlobalPrngMutex); 372 Salt = GlobalPrng.getU8(); 373 } 374 AllocSize = NeededSize; 375 Ptr = BackendAllocator.allocateSecondary(AllocSize, Alignment); 376 } 377 if (UNLIKELY(!Ptr)) 378 return FailureHandler::OnOOM(); 379 380 // If requested, we will zero out the entire contents of the returned chunk. 381 if ((ForceZeroContents || ZeroContents) && FromPrimary) 382 memset(Ptr, 0, BackendAllocator.getActuallyAllocatedSize( 383 Ptr, /*FromPrimary=*/true)); 384 385 UnpackedHeader Header = {}; 386 uptr AllocBeg = reinterpret_cast<uptr>(Ptr); 387 uptr UserBeg = AllocBeg + AlignedChunkHeaderSize; 388 if (UNLIKELY(!IsAligned(UserBeg, Alignment))) { 389 // Since the Secondary takes care of alignment, a non-aligned pointer 390 // means it is from the Primary. It is also the only case where the offset 391 // field of the header would be non-zero. 392 CHECK(FromPrimary); 393 UserBeg = RoundUpTo(UserBeg, Alignment); 394 uptr Offset = UserBeg - AlignedChunkHeaderSize - AllocBeg; 395 Header.Offset = Offset >> MinAlignmentLog; 396 } 397 CHECK_LE(UserBeg + Size, AllocBeg + AllocSize); 398 Header.State = ChunkAllocated; 399 Header.AllocType = Type; 400 if (FromPrimary) { 401 Header.FromPrimary = 1; 402 Header.SizeOrUnusedBytes = Size; 403 } else { 404 // The secondary fits the allocations to a page, so the amount of unused 405 // bytes is the difference between the end of the user allocation and the 406 // next page boundary. 407 uptr PageSize = GetPageSizeCached(); 408 uptr TrailingBytes = (UserBeg + Size) & (PageSize - 1); 409 if (TrailingBytes) 410 Header.SizeOrUnusedBytes = PageSize - TrailingBytes; 411 } 412 Header.Salt = Salt; 413 getScudoChunk(UserBeg)->storeHeader(&Header); 414 void *UserPtr = reinterpret_cast<void *>(UserBeg); 415 // if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(UserPtr, Size); 416 return UserPtr; 417 } 418 419 // Place a chunk in the quarantine or directly deallocate it in the event of 420 // a zero-sized quarantine, or if the size of the chunk is greater than the 421 // quarantine chunk size threshold. 422 void quarantineOrDeallocateChunk(ScudoChunk *Chunk, UnpackedHeader *Header, 423 uptr Size) { 424 const bool BypassQuarantine = (AllocatorQuarantine.GetCacheSize() == 0) || 425 (Size > QuarantineChunksUpToSize); 426 if (BypassQuarantine) { 427 Chunk->eraseHeader(); 428 void *Ptr = Chunk->getAllocBeg(Header); 429 if (Header->FromPrimary) { 430 ScudoTSD *TSD = getTSDAndLock(); 431 getBackendAllocator().deallocatePrimary(&TSD->Cache, Ptr); 432 TSD->unlock(); 433 } else { 434 getBackendAllocator().deallocateSecondary(Ptr); 435 } 436 } else { 437 // If a small memory amount was allocated with a larger alignment, we want 438 // to take that into account. Otherwise the Quarantine would be filled 439 // with tiny chunks, taking a lot of VA memory. This is an approximation 440 // of the usable size, that allows us to not call 441 // GetActuallyAllocatedSize. 442 uptr EstimatedSize = Size + (Header->Offset << MinAlignmentLog); 443 UnpackedHeader NewHeader = *Header; 444 NewHeader.State = ChunkQuarantine; 445 Chunk->compareExchangeHeader(&NewHeader, Header); 446 ScudoTSD *TSD = getTSDAndLock(); 447 AllocatorQuarantine.Put(getQuarantineCache(TSD), 448 QuarantineCallback(&TSD->Cache), 449 Chunk, EstimatedSize); 450 TSD->unlock(); 451 } 452 } 453 454 // Deallocates a Chunk, which means adding it to the delayed free list (or 455 // Quarantine). 456 void deallocate(void *UserPtr, uptr DeleteSize, AllocType Type) { 457 // For a deallocation, we only ensure minimal initialization, meaning thread 458 // local data will be left uninitialized for now (when using ELF TLS). The 459 // fallback cache will be used instead. This is a workaround for a situation 460 // where the only heap operation performed in a thread would be a free past 461 // the TLS destructors, ending up in initialized thread specific data never 462 // being destroyed properly. Any other heap operation will do a full init. 463 initThreadMaybe(/*MinimalInit=*/true); 464 // if (&__sanitizer_free_hook) __sanitizer_free_hook(UserPtr); 465 if (UNLIKELY(!UserPtr)) 466 return; 467 uptr UserBeg = reinterpret_cast<uptr>(UserPtr); 468 if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) { 469 dieWithMessage("ERROR: attempted to deallocate a chunk not properly " 470 "aligned at address %p\n", UserPtr); 471 } 472 ScudoChunk *Chunk = getScudoChunk(UserBeg); 473 UnpackedHeader Header; 474 Chunk->loadHeader(&Header); 475 if (UNLIKELY(Header.State != ChunkAllocated)) { 476 dieWithMessage("ERROR: invalid chunk state when deallocating address " 477 "%p\n", UserPtr); 478 } 479 if (DeallocationTypeMismatch) { 480 // The deallocation type has to match the allocation one. 481 if (Header.AllocType != Type) { 482 // With the exception of memalign'd Chunks, that can be still be free'd. 483 if (Header.AllocType != FromMemalign || Type != FromMalloc) { 484 dieWithMessage("ERROR: allocation type mismatch when deallocating " 485 "address %p\n", UserPtr); 486 } 487 } 488 } 489 uptr Size = Header.FromPrimary ? Header.SizeOrUnusedBytes : 490 Chunk->getUsableSize(&Header) - Header.SizeOrUnusedBytes; 491 if (DeleteSizeMismatch) { 492 if (DeleteSize && DeleteSize != Size) { 493 dieWithMessage("ERROR: invalid sized delete on chunk at address %p\n", 494 UserPtr); 495 } 496 } 497 quarantineOrDeallocateChunk(Chunk, &Header, Size); 498 } 499 500 // Reallocates a chunk. We can save on a new allocation if the new requested 501 // size still fits in the chunk. 502 void *reallocate(void *OldPtr, uptr NewSize) { 503 initThreadMaybe(); 504 uptr UserBeg = reinterpret_cast<uptr>(OldPtr); 505 if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) { 506 dieWithMessage("ERROR: attempted to reallocate a chunk not properly " 507 "aligned at address %p\n", OldPtr); 508 } 509 ScudoChunk *Chunk = getScudoChunk(UserBeg); 510 UnpackedHeader OldHeader; 511 Chunk->loadHeader(&OldHeader); 512 if (UNLIKELY(OldHeader.State != ChunkAllocated)) { 513 dieWithMessage("ERROR: invalid chunk state when reallocating address " 514 "%p\n", OldPtr); 515 } 516 if (DeallocationTypeMismatch) { 517 if (UNLIKELY(OldHeader.AllocType != FromMalloc)) { 518 dieWithMessage("ERROR: allocation type mismatch when reallocating " 519 "address %p\n", OldPtr); 520 } 521 } 522 uptr UsableSize = Chunk->getUsableSize(&OldHeader); 523 // The new size still fits in the current chunk, and the size difference 524 // is reasonable. 525 if (NewSize <= UsableSize && 526 (UsableSize - NewSize) < (SizeClassMap::kMaxSize / 2)) { 527 UnpackedHeader NewHeader = OldHeader; 528 NewHeader.SizeOrUnusedBytes = 529 OldHeader.FromPrimary ? NewSize : UsableSize - NewSize; 530 Chunk->compareExchangeHeader(&NewHeader, &OldHeader); 531 return OldPtr; 532 } 533 // Otherwise, we have to allocate a new chunk and copy the contents of the 534 // old one. 535 void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc); 536 if (NewPtr) { 537 uptr OldSize = OldHeader.FromPrimary ? OldHeader.SizeOrUnusedBytes : 538 UsableSize - OldHeader.SizeOrUnusedBytes; 539 memcpy(NewPtr, OldPtr, Min(NewSize, UsableSize)); 540 quarantineOrDeallocateChunk(Chunk, &OldHeader, OldSize); 541 } 542 return NewPtr; 543 } 544 545 // Helper function that returns the actual usable size of a chunk. 546 uptr getUsableSize(const void *Ptr) { 547 initThreadMaybe(); 548 if (UNLIKELY(!Ptr)) 549 return 0; 550 uptr UserBeg = reinterpret_cast<uptr>(Ptr); 551 ScudoChunk *Chunk = getScudoChunk(UserBeg); 552 UnpackedHeader Header; 553 Chunk->loadHeader(&Header); 554 // Getting the usable size of a chunk only makes sense if it's allocated. 555 if (UNLIKELY(Header.State != ChunkAllocated)) { 556 dieWithMessage("ERROR: invalid chunk state when sizing address %p\n", 557 Ptr); 558 } 559 return Chunk->getUsableSize(&Header); 560 } 561 562 void *calloc(uptr NMemB, uptr Size) { 563 initThreadMaybe(); 564 if (UNLIKELY(CheckForCallocOverflow(NMemB, Size))) 565 return FailureHandler::OnBadRequest(); 566 return allocate(NMemB * Size, MinAlignment, FromMalloc, true); 567 } 568 569 void commitBack(ScudoTSD *TSD) { 570 AllocatorQuarantine.Drain(getQuarantineCache(TSD), 571 QuarantineCallback(&TSD->Cache)); 572 BackendAllocator.destroyCache(&TSD->Cache); 573 } 574 575 uptr getStats(AllocatorStat StatType) { 576 initThreadMaybe(); 577 uptr stats[AllocatorStatCount]; 578 BackendAllocator.getStats(stats); 579 return stats[StatType]; 580 } 581 582 void *handleBadRequest() { 583 initThreadMaybe(); 584 return FailureHandler::OnBadRequest(); 585 } 586 }; 587 588 static ScudoAllocator Instance(LINKER_INITIALIZED); 589 590 static ScudoBackendAllocator &getBackendAllocator() { 591 return Instance.BackendAllocator; 592 } 593 594 static void initScudoInternal(const AllocatorOptions &Options) { 595 Instance.init(Options); 596 } 597 598 void ScudoTSD::init(bool Shared) { 599 UnlockRequired = Shared; 600 getBackendAllocator().initCache(&Cache); 601 Prng.init(); 602 memset(QuarantineCachePlaceHolder, 0, sizeof(QuarantineCachePlaceHolder)); 603 } 604 605 void ScudoTSD::commitBack() { 606 Instance.commitBack(this); 607 } 608 609 void *scudoMalloc(uptr Size, AllocType Type) { 610 return SetErrnoOnNull(Instance.allocate(Size, MinAlignment, Type)); 611 } 612 613 void scudoFree(void *Ptr, AllocType Type) { 614 Instance.deallocate(Ptr, 0, Type); 615 } 616 617 void scudoSizedFree(void *Ptr, uptr Size, AllocType Type) { 618 Instance.deallocate(Ptr, Size, Type); 619 } 620 621 void *scudoRealloc(void *Ptr, uptr Size) { 622 if (!Ptr) 623 return SetErrnoOnNull(Instance.allocate(Size, MinAlignment, FromMalloc)); 624 if (Size == 0) { 625 Instance.deallocate(Ptr, 0, FromMalloc); 626 return nullptr; 627 } 628 return SetErrnoOnNull(Instance.reallocate(Ptr, Size)); 629 } 630 631 void *scudoCalloc(uptr NMemB, uptr Size) { 632 return SetErrnoOnNull(Instance.calloc(NMemB, Size)); 633 } 634 635 void *scudoValloc(uptr Size) { 636 return SetErrnoOnNull( 637 Instance.allocate(Size, GetPageSizeCached(), FromMemalign)); 638 } 639 640 void *scudoPvalloc(uptr Size) { 641 uptr PageSize = GetPageSizeCached(); 642 if (UNLIKELY(CheckForPvallocOverflow(Size, PageSize))) { 643 errno = ENOMEM; 644 return Instance.handleBadRequest(); 645 } 646 // pvalloc(0) should allocate one page. 647 Size = Size ? RoundUpTo(Size, PageSize) : PageSize; 648 return SetErrnoOnNull(Instance.allocate(Size, PageSize, FromMemalign)); 649 } 650 651 void *scudoMemalign(uptr Alignment, uptr Size) { 652 if (UNLIKELY(!IsPowerOfTwo(Alignment))) { 653 errno = EINVAL; 654 return Instance.handleBadRequest(); 655 } 656 return SetErrnoOnNull(Instance.allocate(Size, Alignment, FromMemalign)); 657 } 658 659 int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) { 660 if (UNLIKELY(!CheckPosixMemalignAlignment(Alignment))) { 661 Instance.handleBadRequest(); 662 return EINVAL; 663 } 664 void *Ptr = Instance.allocate(Size, Alignment, FromMemalign); 665 if (UNLIKELY(!Ptr)) 666 return ENOMEM; 667 *MemPtr = Ptr; 668 return 0; 669 } 670 671 void *scudoAlignedAlloc(uptr Alignment, uptr Size) { 672 if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(Alignment, Size))) { 673 errno = EINVAL; 674 return Instance.handleBadRequest(); 675 } 676 return SetErrnoOnNull(Instance.allocate(Size, Alignment, FromMalloc)); 677 } 678 679 uptr scudoMallocUsableSize(void *Ptr) { 680 return Instance.getUsableSize(Ptr); 681 } 682 683 } // namespace __scudo 684 685 using namespace __scudo; 686 687 // MallocExtension helper functions 688 689 uptr __sanitizer_get_current_allocated_bytes() { 690 return Instance.getStats(AllocatorStatAllocated); 691 } 692 693 uptr __sanitizer_get_heap_size() { 694 return Instance.getStats(AllocatorStatMapped); 695 } 696 697 uptr __sanitizer_get_free_bytes() { 698 return 1; 699 } 700 701 uptr __sanitizer_get_unmapped_bytes() { 702 return 1; 703 } 704 705 uptr __sanitizer_get_estimated_allocated_size(uptr size) { 706 return size; 707 } 708 709 int __sanitizer_get_ownership(const void *Ptr) { 710 return Instance.isValidPointer(Ptr); 711 } 712 713 uptr __sanitizer_get_allocated_size(const void *Ptr) { 714 return Instance.getUsableSize(Ptr); 715 } 716