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_utils.h" 19 20 #include "sanitizer_common/sanitizer_allocator_interface.h" 21 #include "sanitizer_common/sanitizer_quarantine.h" 22 23 #include <limits.h> 24 #include <pthread.h> 25 #include <smmintrin.h> 26 27 #include <cstring> 28 29 namespace __scudo { 30 31 struct AP { 32 static const uptr kSpaceBeg = ~0ULL; 33 static const uptr kSpaceSize = 0x10000000000ULL; 34 static const uptr kMetadataSize = 0; 35 typedef DefaultSizeClassMap SizeClassMap; 36 typedef NoOpMapUnmapCallback MapUnmapCallback; 37 static const uptr kFlags = 38 SizeClassAllocator64FlagMasks::kRandomShuffleChunks; 39 }; 40 41 typedef SizeClassAllocator64<AP> PrimaryAllocator; 42 typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; 43 typedef ScudoLargeMmapAllocator SecondaryAllocator; 44 typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, SecondaryAllocator> 45 ScudoAllocator; 46 47 static ScudoAllocator &getAllocator(); 48 49 static thread_local Xorshift128Plus Prng; 50 // Global static cookie, initialized at start-up. 51 static u64 Cookie; 52 53 struct ScudoChunk : UnpackedHeader { 54 // We can't use the offset member of the chunk itself, as we would double 55 // fetch it without any warranty that it wouldn't have been tampered. To 56 // prevent this, we work with a local copy of the header. 57 void *getAllocBeg(UnpackedHeader *Header) { 58 return reinterpret_cast<void *>( 59 reinterpret_cast<uptr>(this) - (Header->Offset << MinAlignmentLog)); 60 } 61 62 // CRC32 checksum of the Chunk pointer and its ChunkHeader. 63 // It currently uses the Intel Nehalem SSE4.2 crc32 64-bit instruction. 64 u16 computeChecksum(UnpackedHeader *Header) const { 65 u64 HeaderHolder[2]; 66 memcpy(HeaderHolder, Header, sizeof(HeaderHolder)); 67 u64 Crc = _mm_crc32_u64(Cookie, reinterpret_cast<uptr>(this)); 68 // This is somewhat of a shortcut. The checksum is stored in the 16 least 69 // significant bits of the first 8 bytes of the header, hence zero-ing 70 // those bits out. It would be more valid to zero the checksum field of the 71 // UnpackedHeader, but would require holding an additional copy of it. 72 Crc = _mm_crc32_u64(Crc, HeaderHolder[0] & 0xffffffffffff0000ULL); 73 Crc = _mm_crc32_u64(Crc, HeaderHolder[1]); 74 return static_cast<u16>(Crc); 75 } 76 77 // Loads and unpacks the header, verifying the checksum in the process. 78 void loadHeader(UnpackedHeader *NewUnpackedHeader) const { 79 const AtomicPackedHeader *AtomicHeader = 80 reinterpret_cast<const AtomicPackedHeader *>(this); 81 PackedHeader NewPackedHeader = 82 AtomicHeader->load(std::memory_order_relaxed); 83 *NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader); 84 if ((NewUnpackedHeader->Unused_0_ != 0) || 85 (NewUnpackedHeader->Unused_1_ != 0) || 86 (NewUnpackedHeader->Checksum != computeChecksum(NewUnpackedHeader))) { 87 dieWithMessage("ERROR: corrupted chunk header at address %p\n", this); 88 } 89 } 90 91 // Packs and stores the header, computing the checksum in the process. 92 void storeHeader(UnpackedHeader *NewUnpackedHeader) { 93 NewUnpackedHeader->Checksum = computeChecksum(NewUnpackedHeader); 94 PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader); 95 AtomicPackedHeader *AtomicHeader = 96 reinterpret_cast<AtomicPackedHeader *>(this); 97 AtomicHeader->store(NewPackedHeader, std::memory_order_relaxed); 98 } 99 100 // Packs and stores the header, computing the checksum in the process. We 101 // compare the current header with the expected provided one to ensure that 102 // we are not being raced by a corruption occurring in another thread. 103 void compareExchangeHeader(UnpackedHeader *NewUnpackedHeader, 104 UnpackedHeader *OldUnpackedHeader) { 105 NewUnpackedHeader->Checksum = computeChecksum(NewUnpackedHeader); 106 PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader); 107 PackedHeader OldPackedHeader = bit_cast<PackedHeader>(*OldUnpackedHeader); 108 AtomicPackedHeader *AtomicHeader = 109 reinterpret_cast<AtomicPackedHeader *>(this); 110 if (!AtomicHeader->compare_exchange_strong(OldPackedHeader, 111 NewPackedHeader, 112 std::memory_order_relaxed, 113 std::memory_order_relaxed)) { 114 dieWithMessage("ERROR: race on chunk header at address %p\n", this); 115 } 116 } 117 }; 118 119 static bool ScudoInitIsRunning = false; 120 121 static pthread_once_t GlobalInited = PTHREAD_ONCE_INIT; 122 static pthread_key_t pkey; 123 124 static thread_local bool ThreadInited = false; 125 static thread_local bool ThreadTornDown = false; 126 static thread_local AllocatorCache Cache; 127 128 static void teardownThread(void *p) { 129 uptr v = reinterpret_cast<uptr>(p); 130 // The glibc POSIX thread-local-storage deallocation routine calls user 131 // provided destructors in a loop of PTHREAD_DESTRUCTOR_ITERATIONS. 132 // We want to be called last since other destructors might call free and the 133 // like, so we wait until PTHREAD_DESTRUCTOR_ITERATIONS before draining the 134 // quarantine and swallowing the cache. 135 if (v < PTHREAD_DESTRUCTOR_ITERATIONS) { 136 pthread_setspecific(pkey, reinterpret_cast<void *>(v + 1)); 137 return; 138 } 139 drainQuarantine(); 140 getAllocator().DestroyCache(&Cache); 141 ThreadTornDown = true; 142 } 143 144 static void initInternal() { 145 SanitizerToolName = "Scudo"; 146 CHECK(!ScudoInitIsRunning && "Scudo init calls itself!"); 147 ScudoInitIsRunning = true; 148 149 initFlags(); 150 151 AllocatorOptions Options; 152 Options.setFrom(getFlags(), common_flags()); 153 initAllocator(Options); 154 155 MaybeStartBackgroudThread(); 156 157 ScudoInitIsRunning = false; 158 } 159 160 static void initGlobal() { 161 pthread_key_create(&pkey, teardownThread); 162 initInternal(); 163 } 164 165 static void NOINLINE initThread() { 166 pthread_once(&GlobalInited, initGlobal); 167 pthread_setspecific(pkey, reinterpret_cast<void *>(1)); 168 getAllocator().InitCache(&Cache); 169 ThreadInited = true; 170 } 171 172 struct QuarantineCallback { 173 explicit QuarantineCallback(AllocatorCache *Cache) 174 : Cache_(Cache) {} 175 176 // Chunk recycling function, returns a quarantined chunk to the backend. 177 void Recycle(ScudoChunk *Chunk) { 178 UnpackedHeader Header; 179 Chunk->loadHeader(&Header); 180 if (Header.State != ChunkQuarantine) { 181 dieWithMessage("ERROR: invalid chunk state when recycling address %p\n", 182 Chunk); 183 } 184 void *Ptr = Chunk->getAllocBeg(&Header); 185 getAllocator().Deallocate(Cache_, Ptr); 186 } 187 188 /// Internal quarantine allocation and deallocation functions. 189 void *Allocate(uptr Size) { 190 // The internal quarantine memory cannot be protected by us. But the only 191 // structures allocated are QuarantineBatch, that are 8KB for x64. So we 192 // will use mmap for those, and given that Deallocate doesn't pass a size 193 // in, we enforce the size of the allocation to be sizeof(QuarantineBatch). 194 // TODO(kostyak): switching to mmap impacts greatly performances, we have 195 // to find another solution 196 // CHECK_EQ(Size, sizeof(QuarantineBatch)); 197 // return MmapOrDie(Size, "QuarantineBatch"); 198 return getAllocator().Allocate(Cache_, Size, 1, false); 199 } 200 201 void Deallocate(void *Ptr) { 202 // UnmapOrDie(Ptr, sizeof(QuarantineBatch)); 203 getAllocator().Deallocate(Cache_, Ptr); 204 } 205 206 AllocatorCache *Cache_; 207 }; 208 209 typedef Quarantine<QuarantineCallback, ScudoChunk> ScudoQuarantine; 210 typedef ScudoQuarantine::Cache QuarantineCache; 211 static thread_local QuarantineCache ThreadQuarantineCache; 212 213 void AllocatorOptions::setFrom(const Flags *f, const CommonFlags *cf) { 214 MayReturnNull = cf->allocator_may_return_null; 215 QuarantineSizeMb = f->QuarantineSizeMb; 216 ThreadLocalQuarantineSizeKb = f->ThreadLocalQuarantineSizeKb; 217 DeallocationTypeMismatch = f->DeallocationTypeMismatch; 218 DeleteSizeMismatch = f->DeleteSizeMismatch; 219 ZeroContents = f->ZeroContents; 220 } 221 222 void AllocatorOptions::copyTo(Flags *f, CommonFlags *cf) const { 223 cf->allocator_may_return_null = MayReturnNull; 224 f->QuarantineSizeMb = QuarantineSizeMb; 225 f->ThreadLocalQuarantineSizeKb = ThreadLocalQuarantineSizeKb; 226 f->DeallocationTypeMismatch = DeallocationTypeMismatch; 227 f->DeleteSizeMismatch = DeleteSizeMismatch; 228 f->ZeroContents = ZeroContents; 229 } 230 231 struct Allocator { 232 static const uptr MaxAllowedMallocSize = 233 FIRST_32_SECOND_64(2UL << 30, 1ULL << 40); 234 235 ScudoAllocator BackendAllocator; 236 ScudoQuarantine AllocatorQuarantine; 237 238 // The fallback caches are used when the thread local caches have been 239 // 'detroyed' on thread tear-down. They are protected by a Mutex as they can 240 // be accessed by different threads. 241 StaticSpinMutex FallbackMutex; 242 AllocatorCache FallbackAllocatorCache; 243 QuarantineCache FallbackQuarantineCache; 244 245 bool DeallocationTypeMismatch; 246 bool ZeroContents; 247 bool DeleteSizeMismatch; 248 249 explicit Allocator(LinkerInitialized) 250 : AllocatorQuarantine(LINKER_INITIALIZED), 251 FallbackQuarantineCache(LINKER_INITIALIZED) {} 252 253 void init(const AllocatorOptions &Options) { 254 // Currently SSE 4.2 support is required. This might change later. 255 CHECK(testCPUFeature(SSE4_2)); // for crc32 256 257 // Verify that the header offset field can hold the maximum offset. In the 258 // case of the Secondary allocator, it takes care of alignment and the 259 // offset will always be 0. In the case of the Primary, the worst case 260 // scenario happens in the last size class, when the backend allocation 261 // would already be aligned on the requested alignment, which would happen 262 // to be the maximum alignment that would fit in that size class. As a 263 // result, the maximum offset will be at most the maximum alignment for the 264 // last size class minus the header size, in multiples of MinAlignment. 265 UnpackedHeader Header = {}; 266 uptr MaxPrimaryAlignment = 1 << MostSignificantSetBitIndex( 267 PrimaryAllocator::SizeClassMap::kMaxSize - MinAlignment); 268 uptr MaximumOffset = (MaxPrimaryAlignment - ChunkHeaderSize) >> 269 MinAlignmentLog; 270 Header.Offset = MaximumOffset; 271 if (Header.Offset != MaximumOffset) { 272 dieWithMessage("ERROR: the maximum possible offset doesn't fit in the " 273 "header\n"); 274 } 275 276 DeallocationTypeMismatch = Options.DeallocationTypeMismatch; 277 DeleteSizeMismatch = Options.DeleteSizeMismatch; 278 ZeroContents = Options.ZeroContents; 279 BackendAllocator.Init(Options.MayReturnNull); 280 AllocatorQuarantine.Init( 281 static_cast<uptr>(Options.QuarantineSizeMb) << 20, 282 static_cast<uptr>(Options.ThreadLocalQuarantineSizeKb) << 10); 283 BackendAllocator.InitCache(&FallbackAllocatorCache); 284 Cookie = Prng.Next(); 285 } 286 287 // Allocates a chunk. 288 void *allocate(uptr Size, uptr Alignment, AllocType Type) { 289 if (UNLIKELY(!ThreadInited)) 290 initThread(); 291 if (!IsPowerOfTwo(Alignment)) { 292 dieWithMessage("ERROR: alignment is not a power of 2\n"); 293 } 294 if (Alignment > MaxAlignment) 295 return BackendAllocator.ReturnNullOrDieOnBadRequest(); 296 if (Alignment < MinAlignment) 297 Alignment = MinAlignment; 298 if (Size == 0) 299 Size = 1; 300 if (Size >= MaxAllowedMallocSize) 301 return BackendAllocator.ReturnNullOrDieOnBadRequest(); 302 uptr RoundedSize = RoundUpTo(Size, MinAlignment); 303 uptr NeededSize = RoundedSize + ChunkHeaderSize; 304 if (Alignment > MinAlignment) 305 NeededSize += Alignment; 306 if (NeededSize >= MaxAllowedMallocSize) 307 return BackendAllocator.ReturnNullOrDieOnBadRequest(); 308 bool FromPrimary = PrimaryAllocator::CanAllocate(NeededSize, MinAlignment); 309 310 void *Ptr; 311 if (LIKELY(!ThreadTornDown)) { 312 Ptr = BackendAllocator.Allocate(&Cache, NeededSize, 313 FromPrimary ? MinAlignment : Alignment); 314 } else { 315 SpinMutexLock l(&FallbackMutex); 316 Ptr = BackendAllocator.Allocate(&FallbackAllocatorCache, NeededSize, 317 FromPrimary ? MinAlignment : Alignment); 318 } 319 if (!Ptr) 320 return BackendAllocator.ReturnNullOrDieOnOOM(); 321 322 // If requested, we will zero out the entire contents of the returned chunk. 323 if (ZeroContents && BackendAllocator.FromPrimary(Ptr)) 324 memset(Ptr, 0, BackendAllocator.GetActuallyAllocatedSize(Ptr)); 325 326 uptr AllocBeg = reinterpret_cast<uptr>(Ptr); 327 // If the allocation was serviced by the secondary, the returned pointer 328 // accounts for ChunkHeaderSize to pass the alignment check of the combined 329 // allocator. Adjust it here. 330 if (!FromPrimary) 331 AllocBeg -= ChunkHeaderSize; 332 uptr ChunkBeg = AllocBeg + ChunkHeaderSize; 333 if (!IsAligned(ChunkBeg, Alignment)) 334 ChunkBeg = RoundUpTo(ChunkBeg, Alignment); 335 CHECK_LE(ChunkBeg + Size, AllocBeg + NeededSize); 336 ScudoChunk *Chunk = 337 reinterpret_cast<ScudoChunk *>(ChunkBeg - ChunkHeaderSize); 338 UnpackedHeader Header = {}; 339 Header.State = ChunkAllocated; 340 Header.Offset = (ChunkBeg - ChunkHeaderSize - AllocBeg) >> MinAlignmentLog; 341 Header.AllocType = Type; 342 Header.RequestedSize = Size; 343 Header.Salt = static_cast<u16>(Prng.Next()); 344 Chunk->storeHeader(&Header); 345 void *UserPtr = reinterpret_cast<void *>(ChunkBeg); 346 // TODO(kostyak): hooks sound like a terrible idea security wise but might 347 // be needed for things to work properly? 348 // if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(UserPtr, Size); 349 return UserPtr; 350 } 351 352 // Deallocates a Chunk, which means adding it to the delayed free list (or 353 // Quarantine). 354 void deallocate(void *UserPtr, uptr DeleteSize, AllocType Type) { 355 if (UNLIKELY(!ThreadInited)) 356 initThread(); 357 // TODO(kostyak): see hook comment above 358 // if (&__sanitizer_free_hook) __sanitizer_free_hook(UserPtr); 359 if (!UserPtr) 360 return; 361 uptr ChunkBeg = reinterpret_cast<uptr>(UserPtr); 362 if (!IsAligned(ChunkBeg, MinAlignment)) { 363 dieWithMessage("ERROR: attempted to deallocate a chunk not properly " 364 "aligned at address %p\n", UserPtr); 365 } 366 ScudoChunk *Chunk = 367 reinterpret_cast<ScudoChunk *>(ChunkBeg - ChunkHeaderSize); 368 UnpackedHeader OldHeader; 369 Chunk->loadHeader(&OldHeader); 370 if (OldHeader.State != ChunkAllocated) { 371 dieWithMessage("ERROR: invalid chunk state when deallocating address " 372 "%p\n", Chunk); 373 } 374 UnpackedHeader NewHeader = OldHeader; 375 NewHeader.State = ChunkQuarantine; 376 Chunk->compareExchangeHeader(&NewHeader, &OldHeader); 377 if (DeallocationTypeMismatch) { 378 // The deallocation type has to match the allocation one. 379 if (NewHeader.AllocType != Type) { 380 // With the exception of memalign'd Chunks, that can be still be free'd. 381 if (NewHeader.AllocType != FromMemalign || Type != FromMalloc) { 382 dieWithMessage("ERROR: allocation type mismatch on address %p\n", 383 Chunk); 384 } 385 } 386 } 387 uptr Size = NewHeader.RequestedSize; 388 if (DeleteSizeMismatch) { 389 if (DeleteSize && DeleteSize != Size) { 390 dieWithMessage("ERROR: invalid sized delete on chunk at address %p\n", 391 Chunk); 392 } 393 } 394 if (LIKELY(!ThreadTornDown)) { 395 AllocatorQuarantine.Put(&ThreadQuarantineCache, 396 QuarantineCallback(&Cache), Chunk, Size); 397 } else { 398 SpinMutexLock l(&FallbackMutex); 399 AllocatorQuarantine.Put(&FallbackQuarantineCache, 400 QuarantineCallback(&FallbackAllocatorCache), 401 Chunk, Size); 402 } 403 } 404 405 // Returns the actual usable size of a chunk. Since this requires loading the 406 // header, we will return it in the second parameter, as it can be required 407 // by the caller to perform additional processing. 408 uptr getUsableSize(const void *Ptr, UnpackedHeader *Header) { 409 if (UNLIKELY(!ThreadInited)) 410 initThread(); 411 if (!Ptr) 412 return 0; 413 uptr ChunkBeg = reinterpret_cast<uptr>(Ptr); 414 ScudoChunk *Chunk = 415 reinterpret_cast<ScudoChunk *>(ChunkBeg - ChunkHeaderSize); 416 Chunk->loadHeader(Header); 417 // Getting the usable size of a chunk only makes sense if it's allocated. 418 if (Header->State != ChunkAllocated) { 419 dieWithMessage("ERROR: attempted to size a non-allocated chunk at " 420 "address %p\n", Chunk); 421 } 422 uptr Size = 423 BackendAllocator.GetActuallyAllocatedSize(Chunk->getAllocBeg(Header)); 424 // UsableSize works as malloc_usable_size, which is also what (AFAIU) 425 // tcmalloc's MallocExtension::GetAllocatedSize aims at providing. This 426 // means we will return the size of the chunk from the user beginning to 427 // the end of the 'user' allocation, hence us subtracting the header size 428 // and the offset from the size. 429 if (Size == 0) 430 return Size; 431 return Size - ChunkHeaderSize - (Header->Offset << MinAlignmentLog); 432 } 433 434 // Helper function that doesn't care about the header. 435 uptr getUsableSize(const void *Ptr) { 436 UnpackedHeader Header; 437 return getUsableSize(Ptr, &Header); 438 } 439 440 // Reallocates a chunk. We can save on a new allocation if the new requested 441 // size still fits in the chunk. 442 void *reallocate(void *OldPtr, uptr NewSize) { 443 if (UNLIKELY(!ThreadInited)) 444 initThread(); 445 UnpackedHeader OldHeader; 446 uptr Size = getUsableSize(OldPtr, &OldHeader); 447 uptr ChunkBeg = reinterpret_cast<uptr>(OldPtr); 448 ScudoChunk *Chunk = 449 reinterpret_cast<ScudoChunk *>(ChunkBeg - ChunkHeaderSize); 450 if (OldHeader.AllocType != FromMalloc) { 451 dieWithMessage("ERROR: invalid chunk type when reallocating address %p\n", 452 Chunk); 453 } 454 UnpackedHeader NewHeader = OldHeader; 455 // The new size still fits in the current chunk. 456 if (NewSize <= Size) { 457 NewHeader.RequestedSize = NewSize; 458 Chunk->compareExchangeHeader(&NewHeader, &OldHeader); 459 return OldPtr; 460 } 461 // Otherwise, we have to allocate a new chunk and copy the contents of the 462 // old one. 463 void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc); 464 if (NewPtr) { 465 uptr OldSize = OldHeader.RequestedSize; 466 memcpy(NewPtr, OldPtr, Min(NewSize, OldSize)); 467 NewHeader.State = ChunkQuarantine; 468 Chunk->compareExchangeHeader(&NewHeader, &OldHeader); 469 if (LIKELY(!ThreadTornDown)) { 470 AllocatorQuarantine.Put(&ThreadQuarantineCache, 471 QuarantineCallback(&Cache), Chunk, OldSize); 472 } else { 473 SpinMutexLock l(&FallbackMutex); 474 AllocatorQuarantine.Put(&FallbackQuarantineCache, 475 QuarantineCallback(&FallbackAllocatorCache), 476 Chunk, OldSize); 477 } 478 } 479 return NewPtr; 480 } 481 482 void *calloc(uptr NMemB, uptr Size) { 483 if (UNLIKELY(!ThreadInited)) 484 initThread(); 485 uptr Total = NMemB * Size; 486 if (Size != 0 && Total / Size != NMemB) // Overflow check 487 return BackendAllocator.ReturnNullOrDieOnBadRequest(); 488 void *Ptr = allocate(Total, MinAlignment, FromMalloc); 489 // If ZeroContents, the content of the chunk has already been zero'd out. 490 if (!ZeroContents && Ptr && BackendAllocator.FromPrimary(Ptr)) 491 memset(Ptr, 0, getUsableSize(Ptr)); 492 return Ptr; 493 } 494 495 void drainQuarantine() { 496 AllocatorQuarantine.Drain(&ThreadQuarantineCache, 497 QuarantineCallback(&Cache)); 498 } 499 }; 500 501 static Allocator Instance(LINKER_INITIALIZED); 502 503 static ScudoAllocator &getAllocator() { 504 return Instance.BackendAllocator; 505 } 506 507 void initAllocator(const AllocatorOptions &Options) { 508 Instance.init(Options); 509 } 510 511 void drainQuarantine() { 512 Instance.drainQuarantine(); 513 } 514 515 void *scudoMalloc(uptr Size, AllocType Type) { 516 return Instance.allocate(Size, MinAlignment, Type); 517 } 518 519 void scudoFree(void *Ptr, AllocType Type) { 520 Instance.deallocate(Ptr, 0, Type); 521 } 522 523 void scudoSizedFree(void *Ptr, uptr Size, AllocType Type) { 524 Instance.deallocate(Ptr, Size, Type); 525 } 526 527 void *scudoRealloc(void *Ptr, uptr Size) { 528 if (!Ptr) 529 return Instance.allocate(Size, MinAlignment, FromMalloc); 530 if (Size == 0) { 531 Instance.deallocate(Ptr, 0, FromMalloc); 532 return nullptr; 533 } 534 return Instance.reallocate(Ptr, Size); 535 } 536 537 void *scudoCalloc(uptr NMemB, uptr Size) { 538 return Instance.calloc(NMemB, Size); 539 } 540 541 void *scudoValloc(uptr Size) { 542 return Instance.allocate(Size, GetPageSizeCached(), FromMemalign); 543 } 544 545 void *scudoMemalign(uptr Alignment, uptr Size) { 546 return Instance.allocate(Size, Alignment, FromMemalign); 547 } 548 549 void *scudoPvalloc(uptr Size) { 550 uptr PageSize = GetPageSizeCached(); 551 Size = RoundUpTo(Size, PageSize); 552 if (Size == 0) { 553 // pvalloc(0) should allocate one page. 554 Size = PageSize; 555 } 556 return Instance.allocate(Size, PageSize, FromMemalign); 557 } 558 559 int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) { 560 *MemPtr = Instance.allocate(Size, Alignment, FromMemalign); 561 return 0; 562 } 563 564 void *scudoAlignedAlloc(uptr Alignment, uptr Size) { 565 // size must be a multiple of the alignment. To avoid a division, we first 566 // make sure that alignment is a power of 2. 567 CHECK(IsPowerOfTwo(Alignment)); 568 CHECK_EQ((Size & (Alignment - 1)), 0); 569 return Instance.allocate(Size, Alignment, FromMalloc); 570 } 571 572 uptr scudoMallocUsableSize(void *Ptr) { 573 return Instance.getUsableSize(Ptr); 574 } 575 576 } // namespace __scudo 577 578 using namespace __scudo; 579 580 // MallocExtension helper functions 581 582 uptr __sanitizer_get_current_allocated_bytes() { 583 uptr stats[AllocatorStatCount]; 584 getAllocator().GetStats(stats); 585 return stats[AllocatorStatAllocated]; 586 } 587 588 uptr __sanitizer_get_heap_size() { 589 uptr stats[AllocatorStatCount]; 590 getAllocator().GetStats(stats); 591 return stats[AllocatorStatMapped]; 592 } 593 594 uptr __sanitizer_get_free_bytes() { 595 return 1; 596 } 597 598 uptr __sanitizer_get_unmapped_bytes() { 599 return 1; 600 } 601 602 uptr __sanitizer_get_estimated_allocated_size(uptr size) { 603 return size; 604 } 605 606 int __sanitizer_get_ownership(const void *p) { 607 return Instance.getUsableSize(p) != 0; 608 } 609 610 uptr __sanitizer_get_allocated_size(const void *p) { 611 return Instance.getUsableSize(p); 612 } 613