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