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