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