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