• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
AllocBeg__scudo::ScudoChunk92   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.
Checksum__scudo::ScudoChunk99   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.
loadHeader__scudo::ScudoChunk113   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.
storeHeader__scudo::ScudoChunk127   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.
compareExchangeHeader__scudo::ScudoChunk138   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 
teardownThread(void * p)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 
initInternal()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 
initGlobal()193 static void initGlobal() {
194   pthread_key_create(&pkey, teardownThread);
195   initInternal();
196 }
197 
initThread()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 {
QuarantineCallback__scudo::QuarantineCallback206   explicit QuarantineCallback(AllocatorCache *Cache)
207     : Cache_(Cache) {}
208 
209   // Chunk recycling function, returns a quarantined chunk to the backend.
Recycle__scudo::QuarantineCallback210   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.
Allocate__scudo::QuarantineCallback222   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 
Deallocate__scudo::QuarantineCallback234   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 
setFrom(const Flags * f,const CommonFlags * cf)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 
copyTo(Flags * f,CommonFlags * cf) const255 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 
Allocator__scudo::Allocator283   explicit Allocator(LinkerInitialized)
284     : AllocatorQuarantine(LINKER_INITIALIZED),
285       FallbackQuarantineCache(LINKER_INITIALIZED) {}
286 
init__scudo::Allocator287   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.
allocate__scudo::Allocator317   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).
deallocate__scudo::Allocator377   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.
getUsableSize__scudo::Allocator431   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.
getUsableSize__scudo::Allocator458   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.
reallocate__scudo::Allocator465   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 
calloc__scudo::Allocator505   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 
drainQuarantine__scudo::Allocator518   void drainQuarantine() {
519     AllocatorQuarantine.Drain(&ThreadQuarantineCache,
520                               QuarantineCallback(&Cache));
521   }
522 };
523 
524 static Allocator Instance(LINKER_INITIALIZED);
525 
getAllocator()526 static ScudoAllocator &getAllocator() {
527   return Instance.BackendAllocator;
528 }
529 
initAllocator(const AllocatorOptions & Options)530 void initAllocator(const AllocatorOptions &Options) {
531   Instance.init(Options);
532 }
533 
drainQuarantine()534 void drainQuarantine() {
535   Instance.drainQuarantine();
536 }
537 
scudoMalloc(uptr Size,AllocType Type)538 void *scudoMalloc(uptr Size, AllocType Type) {
539   return Instance.allocate(Size, Allocator::MinAlignment, Type);
540 }
541 
scudoFree(void * Ptr,AllocType Type)542 void scudoFree(void *Ptr, AllocType Type) {
543   Instance.deallocate(Ptr, 0, Type);
544 }
545 
scudoSizedFree(void * Ptr,uptr Size,AllocType Type)546 void scudoSizedFree(void *Ptr, uptr Size, AllocType Type) {
547   Instance.deallocate(Ptr, Size, Type);
548 }
549 
scudoRealloc(void * Ptr,uptr Size)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 
scudoCalloc(uptr NMemB,uptr Size)560 void *scudoCalloc(uptr NMemB, uptr Size) {
561   return Instance.calloc(NMemB, Size);
562 }
563 
scudoValloc(uptr Size)564 void *scudoValloc(uptr Size) {
565   return Instance.allocate(Size, GetPageSizeCached(), FromMemalign);
566 }
567 
scudoMemalign(uptr Alignment,uptr Size)568 void *scudoMemalign(uptr Alignment, uptr Size) {
569   return Instance.allocate(Size, Alignment, FromMemalign);
570 }
571 
scudoPvalloc(uptr Size)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 
scudoPosixMemalign(void ** MemPtr,uptr Alignment,uptr Size)582 int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) {
583   *MemPtr = Instance.allocate(Size, Alignment, FromMemalign);
584   return 0;
585 }
586 
scudoAlignedAlloc(uptr Alignment,uptr Size)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 
scudoMallocUsableSize(void * Ptr)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 
__sanitizer_get_current_allocated_bytes()605 uptr __sanitizer_get_current_allocated_bytes() {
606   uptr stats[AllocatorStatCount];
607   getAllocator().GetStats(stats);
608   return stats[AllocatorStatAllocated];
609 }
610 
__sanitizer_get_heap_size()611 uptr __sanitizer_get_heap_size() {
612   uptr stats[AllocatorStatCount];
613   getAllocator().GetStats(stats);
614   return stats[AllocatorStatMapped];
615 }
616 
__sanitizer_get_free_bytes()617 uptr __sanitizer_get_free_bytes() {
618   return 1;
619 }
620 
__sanitizer_get_unmapped_bytes()621 uptr __sanitizer_get_unmapped_bytes() {
622   return 1;
623 }
624 
__sanitizer_get_estimated_allocated_size(uptr size)625 uptr __sanitizer_get_estimated_allocated_size(uptr size) {
626   return size;
627 }
628 
__sanitizer_get_ownership(const void * p)629 int __sanitizer_get_ownership(const void *p) {
630   return Instance.getUsableSize(p) != 0;
631 }
632 
__sanitizer_get_allocated_size(const void * p)633 uptr __sanitizer_get_allocated_size(const void *p) {
634   return Instance.getUsableSize(p);
635 }
636