1//===-- asan_allocator.cpp ------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file is a part of AddressSanitizer, an address sanity checker.
10//
11// Implementation of ASan's memory allocator, 2-nd version.
12// This variant uses the allocator from sanitizer_common, i.e. the one shared
13// with ThreadSanitizer and MemorySanitizer.
14//
15//===----------------------------------------------------------------------===//
16
17#include "asan_allocator.h"
18#include "asan_mapping.h"
19#include "asan_poisoning.h"
20#include "asan_report.h"
21#include "asan_stack.h"
22#include "asan_thread.h"
23#include "sanitizer_common/sanitizer_allocator_checks.h"
24#include "sanitizer_common/sanitizer_allocator_interface.h"
25#include "sanitizer_common/sanitizer_errno.h"
26#include "sanitizer_common/sanitizer_flags.h"
27#include "sanitizer_common/sanitizer_internal_defs.h"
28#include "sanitizer_common/sanitizer_list.h"
29#include "sanitizer_common/sanitizer_stackdepot.h"
30#include "sanitizer_common/sanitizer_quarantine.h"
31#include "lsan/lsan_common.h"
32
33namespace __asan {
34
35// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits.
36// We use adaptive redzones: for larger allocation larger redzones are used.
37static u32 RZLog2Size(u32 rz_log) {
38  CHECK_LT(rz_log, 8);
39  return 16 << rz_log;
40}
41
42static u32 RZSize2Log(u32 rz_size) {
43  CHECK_GE(rz_size, 16);
44  CHECK_LE(rz_size, 2048);
45  CHECK(IsPowerOfTwo(rz_size));
46  u32 res = Log2(rz_size) - 4;
47  CHECK_EQ(rz_size, RZLog2Size(res));
48  return res;
49}
50
51static AsanAllocator &get_allocator();
52
53// The memory chunk allocated from the underlying allocator looks like this:
54// L L L L L L H H U U U U U U R R
55//   L -- left redzone words (0 or more bytes)
56//   H -- ChunkHeader (16 bytes), which is also a part of the left redzone.
57//   U -- user memory.
58//   R -- right redzone (0 or more bytes)
59// ChunkBase consists of ChunkHeader and other bytes that overlap with user
60// memory.
61
62// If the left redzone is greater than the ChunkHeader size we store a magic
63// value in the first uptr word of the memory block and store the address of
64// ChunkBase in the next uptr.
65// M B L L L L L L L L L  H H U U U U U U
66//   |                    ^
67//   ---------------------|
68//   M -- magic value kAllocBegMagic
69//   B -- address of ChunkHeader pointing to the first 'H'
70static const uptr kAllocBegMagic = 0xCC6E96B9;
71
72struct ChunkHeader {
73  // 1-st 8 bytes.
74  u32 chunk_state       : 8;  // Must be first.
75  u32 alloc_tid         : 24;
76
77  u32 free_tid          : 24;
78  u32 from_memalign     : 1;
79  u32 alloc_type        : 2;
80  u32 rz_log            : 3;
81  u32 lsan_tag          : 2;
82  // 2-nd 8 bytes
83  // This field is used for small sizes. For large sizes it is equal to
84  // SizeClassMap::kMaxSize and the actual size is stored in the
85  // SecondaryAllocator's metadata.
86  u32 user_requested_size : 29;
87  // align < 8 -> 0
88  // else      -> log2(min(align, 512)) - 2
89  u32 user_requested_alignment_log : 3;
90  u32 alloc_context_id;
91};
92
93struct ChunkBase : ChunkHeader {
94  // Header2, intersects with user memory.
95  u32 free_context_id;
96};
97
98static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
99static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize;
100COMPILER_CHECK(kChunkHeaderSize == 16);
101COMPILER_CHECK(kChunkHeader2Size <= 16);
102
103// Every chunk of memory allocated by this allocator can be in one of 3 states:
104// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
105// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
106// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
107enum {
108  CHUNK_AVAILABLE  = 0,  // 0 is the default value even if we didn't set it.
109  CHUNK_ALLOCATED  = 2,
110  CHUNK_QUARANTINE = 3
111};
112
113struct AsanChunk: ChunkBase {
114  uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
115  uptr UsedSize(bool locked_version = false) {
116    if (user_requested_size != SizeClassMap::kMaxSize)
117      return user_requested_size;
118    return *reinterpret_cast<uptr *>(
119               get_allocator().GetMetaData(AllocBeg(locked_version)));
120  }
121  void *AllocBeg(bool locked_version = false) {
122    if (from_memalign) {
123      if (locked_version)
124        return get_allocator().GetBlockBeginFastLocked(
125            reinterpret_cast<void *>(this));
126      return get_allocator().GetBlockBegin(reinterpret_cast<void *>(this));
127    }
128    return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
129  }
130  bool AddrIsInside(uptr addr, bool locked_version = false) {
131    return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
132  }
133};
134
135struct QuarantineCallback {
136  QuarantineCallback(AllocatorCache *cache, BufferedStackTrace *stack)
137      : cache_(cache),
138        stack_(stack) {
139  }
140
141  void Recycle(AsanChunk *m) {
142    CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
143    atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed);
144    CHECK_NE(m->alloc_tid, kInvalidTid);
145    CHECK_NE(m->free_tid, kInvalidTid);
146    PoisonShadow(m->Beg(),
147                 RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
148                 kAsanHeapLeftRedzoneMagic);
149    void *p = reinterpret_cast<void *>(m->AllocBeg());
150    if (p != m) {
151      uptr *alloc_magic = reinterpret_cast<uptr *>(p);
152      CHECK_EQ(alloc_magic[0], kAllocBegMagic);
153      // Clear the magic value, as allocator internals may overwrite the
154      // contents of deallocated chunk, confusing GetAsanChunk lookup.
155      alloc_magic[0] = 0;
156      CHECK_EQ(alloc_magic[1], reinterpret_cast<uptr>(m));
157    }
158
159    // Statistics.
160    AsanStats &thread_stats = GetCurrentThreadStats();
161    thread_stats.real_frees++;
162    thread_stats.really_freed += m->UsedSize();
163
164    get_allocator().Deallocate(cache_, p);
165  }
166
167  void *Allocate(uptr size) {
168    void *res = get_allocator().Allocate(cache_, size, 1);
169    // TODO(alekseys): Consider making quarantine OOM-friendly.
170    if (UNLIKELY(!res))
171      ReportOutOfMemory(size, stack_);
172    return res;
173  }
174
175  void Deallocate(void *p) {
176    get_allocator().Deallocate(cache_, p);
177  }
178
179 private:
180  AllocatorCache* const cache_;
181  BufferedStackTrace* const stack_;
182};
183
184typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine;
185typedef AsanQuarantine::Cache QuarantineCache;
186
187void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const {
188  PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
189  // Statistics.
190  AsanStats &thread_stats = GetCurrentThreadStats();
191  thread_stats.mmaps++;
192  thread_stats.mmaped += size;
193}
194void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const {
195  PoisonShadow(p, size, 0);
196  // We are about to unmap a chunk of user memory.
197  // Mark the corresponding shadow memory as not needed.
198  FlushUnneededASanShadowMemory(p, size);
199  // Statistics.
200  AsanStats &thread_stats = GetCurrentThreadStats();
201  thread_stats.munmaps++;
202  thread_stats.munmaped += size;
203}
204
205// We can not use THREADLOCAL because it is not supported on some of the
206// platforms we care about (OSX 10.6, Android).
207// static THREADLOCAL AllocatorCache cache;
208AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
209  CHECK(ms);
210  return &ms->allocator_cache;
211}
212
213QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
214  CHECK(ms);
215  CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache));
216  return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache);
217}
218
219void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) {
220  quarantine_size_mb = f->quarantine_size_mb;
221  thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb;
222  min_redzone = f->redzone;
223  max_redzone = f->max_redzone;
224  may_return_null = cf->allocator_may_return_null;
225  alloc_dealloc_mismatch = f->alloc_dealloc_mismatch;
226  release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms;
227}
228
229void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
230  f->quarantine_size_mb = quarantine_size_mb;
231  f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb;
232  f->redzone = min_redzone;
233  f->max_redzone = max_redzone;
234  cf->allocator_may_return_null = may_return_null;
235  f->alloc_dealloc_mismatch = alloc_dealloc_mismatch;
236  cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms;
237}
238
239struct Allocator {
240  static const uptr kMaxAllowedMallocSize =
241      FIRST_32_SECOND_64(3UL << 30, 1ULL << 40);
242
243  AsanAllocator allocator;
244  AsanQuarantine quarantine;
245  StaticSpinMutex fallback_mutex;
246  AllocatorCache fallback_allocator_cache;
247  QuarantineCache fallback_quarantine_cache;
248
249  uptr max_user_defined_malloc_size;
250  atomic_uint8_t rss_limit_exceeded;
251
252  // ------------------- Options --------------------------
253  atomic_uint16_t min_redzone;
254  atomic_uint16_t max_redzone;
255  atomic_uint8_t alloc_dealloc_mismatch;
256
257  // ------------------- Initialization ------------------------
258  explicit Allocator(LinkerInitialized)
259      : quarantine(LINKER_INITIALIZED),
260        fallback_quarantine_cache(LINKER_INITIALIZED) {}
261
262  void CheckOptions(const AllocatorOptions &options) const {
263    CHECK_GE(options.min_redzone, 16);
264    CHECK_GE(options.max_redzone, options.min_redzone);
265    CHECK_LE(options.max_redzone, 2048);
266    CHECK(IsPowerOfTwo(options.min_redzone));
267    CHECK(IsPowerOfTwo(options.max_redzone));
268  }
269
270  void SharedInitCode(const AllocatorOptions &options) {
271    CheckOptions(options);
272    quarantine.Init((uptr)options.quarantine_size_mb << 20,
273                    (uptr)options.thread_local_quarantine_size_kb << 10);
274    atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch,
275                 memory_order_release);
276    atomic_store(&min_redzone, options.min_redzone, memory_order_release);
277    atomic_store(&max_redzone, options.max_redzone, memory_order_release);
278  }
279
280  void InitLinkerInitialized(const AllocatorOptions &options) {
281    SetAllocatorMayReturnNull(options.may_return_null);
282    allocator.InitLinkerInitialized(options.release_to_os_interval_ms);
283    SharedInitCode(options);
284    max_user_defined_malloc_size = common_flags()->max_allocation_size_mb
285                                       ? common_flags()->max_allocation_size_mb
286                                             << 20
287                                       : kMaxAllowedMallocSize;
288  }
289
290  bool RssLimitExceeded() {
291    return atomic_load(&rss_limit_exceeded, memory_order_relaxed);
292  }
293
294  void SetRssLimitExceeded(bool limit_exceeded) {
295    atomic_store(&rss_limit_exceeded, limit_exceeded, memory_order_relaxed);
296  }
297
298  void RePoisonChunk(uptr chunk) {
299    // This could be a user-facing chunk (with redzones), or some internal
300    // housekeeping chunk, like TransferBatch. Start by assuming the former.
301    AsanChunk *ac = GetAsanChunk((void *)chunk);
302    uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac);
303    uptr beg = ac->Beg();
304    uptr end = ac->Beg() + ac->UsedSize(true);
305    uptr chunk_end = chunk + allocated_size;
306    if (chunk < beg && beg < end && end <= chunk_end &&
307        ac->chunk_state == CHUNK_ALLOCATED) {
308      // Looks like a valid AsanChunk in use, poison redzones only.
309      PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic);
310      uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY);
311      FastPoisonShadowPartialRightRedzone(
312          end_aligned_down, end - end_aligned_down,
313          chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic);
314    } else {
315      // This is either not an AsanChunk or freed or quarantined AsanChunk.
316      // In either case, poison everything.
317      PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic);
318    }
319  }
320
321  void ReInitialize(const AllocatorOptions &options) {
322    SetAllocatorMayReturnNull(options.may_return_null);
323    allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms);
324    SharedInitCode(options);
325
326    // Poison all existing allocation's redzones.
327    if (CanPoisonMemory()) {
328      allocator.ForceLock();
329      allocator.ForEachChunk(
330          [](uptr chunk, void *alloc) {
331            ((Allocator *)alloc)->RePoisonChunk(chunk);
332          },
333          this);
334      allocator.ForceUnlock();
335    }
336  }
337
338  void GetOptions(AllocatorOptions *options) const {
339    options->quarantine_size_mb = quarantine.GetSize() >> 20;
340    options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10;
341    options->min_redzone = atomic_load(&min_redzone, memory_order_acquire);
342    options->max_redzone = atomic_load(&max_redzone, memory_order_acquire);
343    options->may_return_null = AllocatorMayReturnNull();
344    options->alloc_dealloc_mismatch =
345        atomic_load(&alloc_dealloc_mismatch, memory_order_acquire);
346    options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs();
347  }
348
349  // -------------------- Helper methods. -------------------------
350  uptr ComputeRZLog(uptr user_requested_size) {
351    u32 rz_log =
352      user_requested_size <= 64        - 16   ? 0 :
353      user_requested_size <= 128       - 32   ? 1 :
354      user_requested_size <= 512       - 64   ? 2 :
355      user_requested_size <= 4096      - 128  ? 3 :
356      user_requested_size <= (1 << 14) - 256  ? 4 :
357      user_requested_size <= (1 << 15) - 512  ? 5 :
358      user_requested_size <= (1 << 16) - 1024 ? 6 : 7;
359    u32 min_rz = atomic_load(&min_redzone, memory_order_acquire);
360    u32 max_rz = atomic_load(&max_redzone, memory_order_acquire);
361    return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz));
362  }
363
364  static uptr ComputeUserRequestedAlignmentLog(uptr user_requested_alignment) {
365    if (user_requested_alignment < 8)
366      return 0;
367    if (user_requested_alignment > 512)
368      user_requested_alignment = 512;
369    return Log2(user_requested_alignment) - 2;
370  }
371
372  static uptr ComputeUserAlignment(uptr user_requested_alignment_log) {
373    if (user_requested_alignment_log == 0)
374      return 0;
375    return 1LL << (user_requested_alignment_log + 2);
376  }
377
378  // We have an address between two chunks, and we want to report just one.
379  AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk,
380                         AsanChunk *right_chunk) {
381    // Prefer an allocated chunk over freed chunk and freed chunk
382    // over available chunk.
383    if (left_chunk->chunk_state != right_chunk->chunk_state) {
384      if (left_chunk->chunk_state == CHUNK_ALLOCATED)
385        return left_chunk;
386      if (right_chunk->chunk_state == CHUNK_ALLOCATED)
387        return right_chunk;
388      if (left_chunk->chunk_state == CHUNK_QUARANTINE)
389        return left_chunk;
390      if (right_chunk->chunk_state == CHUNK_QUARANTINE)
391        return right_chunk;
392    }
393    // Same chunk_state: choose based on offset.
394    sptr l_offset = 0, r_offset = 0;
395    CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
396    CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
397    if (l_offset < r_offset)
398      return left_chunk;
399    return right_chunk;
400  }
401
402  bool UpdateAllocationStack(uptr addr, BufferedStackTrace *stack) {
403    AsanChunk *m = GetAsanChunkByAddr(addr);
404    if (!m) return false;
405    if (m->chunk_state != CHUNK_ALLOCATED) return false;
406    if (m->Beg() != addr) return false;
407    atomic_store((atomic_uint32_t *)&m->alloc_context_id, StackDepotPut(*stack),
408                 memory_order_relaxed);
409    return true;
410  }
411
412  // -------------------- Allocation/Deallocation routines ---------------
413  void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack,
414                 AllocType alloc_type, bool can_fill) {
415    if (UNLIKELY(!asan_inited))
416      AsanInitFromRtl();
417    if (RssLimitExceeded()) {
418      if (AllocatorMayReturnNull())
419        return nullptr;
420      ReportRssLimitExceeded(stack);
421    }
422    Flags &fl = *flags();
423    CHECK(stack);
424    const uptr min_alignment = SHADOW_GRANULARITY;
425    const uptr user_requested_alignment_log =
426        ComputeUserRequestedAlignmentLog(alignment);
427    if (alignment < min_alignment)
428      alignment = min_alignment;
429    if (size == 0) {
430      // We'd be happy to avoid allocating memory for zero-size requests, but
431      // some programs/tests depend on this behavior and assume that malloc
432      // would not return NULL even for zero-size allocations. Moreover, it
433      // looks like operator new should never return NULL, and results of
434      // consecutive "new" calls must be different even if the allocated size
435      // is zero.
436      size = 1;
437    }
438    CHECK(IsPowerOfTwo(alignment));
439    uptr rz_log = ComputeRZLog(size);
440    uptr rz_size = RZLog2Size(rz_log);
441    uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment);
442    uptr needed_size = rounded_size + rz_size;
443    if (alignment > min_alignment)
444      needed_size += alignment;
445    bool using_primary_allocator = true;
446    // If we are allocating from the secondary allocator, there will be no
447    // automatic right redzone, so add the right redzone manually.
448    if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) {
449      needed_size += rz_size;
450      using_primary_allocator = false;
451    }
452    CHECK(IsAligned(needed_size, min_alignment));
453    if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize ||
454        size > max_user_defined_malloc_size) {
455      if (AllocatorMayReturnNull()) {
456        Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
457               (void*)size);
458        return nullptr;
459      }
460      uptr malloc_limit =
461          Min(kMaxAllowedMallocSize, max_user_defined_malloc_size);
462      ReportAllocationSizeTooBig(size, needed_size, malloc_limit, stack);
463    }
464
465    AsanThread *t = GetCurrentThread();
466    void *allocated;
467    if (t) {
468      AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
469      allocated = allocator.Allocate(cache, needed_size, 8);
470    } else {
471      SpinMutexLock l(&fallback_mutex);
472      AllocatorCache *cache = &fallback_allocator_cache;
473      allocated = allocator.Allocate(cache, needed_size, 8);
474    }
475    if (UNLIKELY(!allocated)) {
476      SetAllocatorOutOfMemory();
477      if (AllocatorMayReturnNull())
478        return nullptr;
479      ReportOutOfMemory(size, stack);
480    }
481
482    if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) {
483      // Heap poisoning is enabled, but the allocator provides an unpoisoned
484      // chunk. This is possible if CanPoisonMemory() was false for some
485      // time, for example, due to flags()->start_disabled.
486      // Anyway, poison the block before using it for anything else.
487      uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated);
488      PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic);
489    }
490
491    uptr alloc_beg = reinterpret_cast<uptr>(allocated);
492    uptr alloc_end = alloc_beg + needed_size;
493    uptr beg_plus_redzone = alloc_beg + rz_size;
494    uptr user_beg = beg_plus_redzone;
495    if (!IsAligned(user_beg, alignment))
496      user_beg = RoundUpTo(user_beg, alignment);
497    uptr user_end = user_beg + size;
498    CHECK_LE(user_end, alloc_end);
499    uptr chunk_beg = user_beg - kChunkHeaderSize;
500    AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
501    m->alloc_type = alloc_type;
502    m->rz_log = rz_log;
503    u32 alloc_tid = t ? t->tid() : 0;
504    m->alloc_tid = alloc_tid;
505    CHECK_EQ(alloc_tid, m->alloc_tid);  // Does alloc_tid fit into the bitfield?
506    m->free_tid = kInvalidTid;
507    m->from_memalign = user_beg != beg_plus_redzone;
508    if (alloc_beg != chunk_beg) {
509      CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg);
510      reinterpret_cast<uptr *>(alloc_beg)[0] = kAllocBegMagic;
511      reinterpret_cast<uptr *>(alloc_beg)[1] = chunk_beg;
512    }
513    if (using_primary_allocator) {
514      CHECK(size);
515      m->user_requested_size = size;
516      CHECK(allocator.FromPrimary(allocated));
517    } else {
518      CHECK(!allocator.FromPrimary(allocated));
519      m->user_requested_size = SizeClassMap::kMaxSize;
520      uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated));
521      meta[0] = size;
522      meta[1] = chunk_beg;
523    }
524    m->user_requested_alignment_log = user_requested_alignment_log;
525
526    m->alloc_context_id = StackDepotPut(*stack);
527
528    uptr size_rounded_down_to_granularity =
529        RoundDownTo(size, SHADOW_GRANULARITY);
530    // Unpoison the bulk of the memory region.
531    if (size_rounded_down_to_granularity)
532      PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
533    // Deal with the end of the region if size is not aligned to granularity.
534    if (size != size_rounded_down_to_granularity && CanPoisonMemory()) {
535      u8 *shadow =
536          (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity);
537      *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0;
538    }
539
540    AsanStats &thread_stats = GetCurrentThreadStats();
541    thread_stats.mallocs++;
542    thread_stats.malloced += size;
543    thread_stats.malloced_redzones += needed_size - size;
544    if (needed_size > SizeClassMap::kMaxSize)
545      thread_stats.malloc_large++;
546    else
547      thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++;
548
549    void *res = reinterpret_cast<void *>(user_beg);
550    if (can_fill && fl.max_malloc_fill_size) {
551      uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size);
552      REAL(memset)(res, fl.malloc_fill_byte, fill_size);
553    }
554#if CAN_SANITIZE_LEAKS
555    m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored
556                                                 : __lsan::kDirectlyLeaked;
557#endif
558    // Must be the last mutation of metadata in this function.
559    atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release);
560    ASAN_MALLOC_HOOK(res, size);
561    return res;
562  }
563
564  // Set quarantine flag if chunk is allocated, issue ASan error report on
565  // available and quarantined chunks. Return true on success, false otherwise.
566  bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk *m, void *ptr,
567                                   BufferedStackTrace *stack) {
568    u8 old_chunk_state = CHUNK_ALLOCATED;
569    // Flip the chunk_state atomically to avoid race on double-free.
570    if (!atomic_compare_exchange_strong((atomic_uint8_t *)m, &old_chunk_state,
571                                        CHUNK_QUARANTINE,
572                                        memory_order_acquire)) {
573      ReportInvalidFree(ptr, old_chunk_state, stack);
574      // It's not safe to push a chunk in quarantine on invalid free.
575      return false;
576    }
577    CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state);
578    return true;
579  }
580
581  // Expects the chunk to already be marked as quarantined by using
582  // AtomicallySetQuarantineFlagIfAllocated.
583  void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack) {
584    CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
585    CHECK_GE(m->alloc_tid, 0);
586    if (SANITIZER_WORDSIZE == 64)  // On 32-bits this resides in user area.
587      CHECK_EQ(m->free_tid, kInvalidTid);
588    AsanThread *t = GetCurrentThread();
589    m->free_tid = t ? t->tid() : 0;
590    m->free_context_id = StackDepotPut(*stack);
591
592    Flags &fl = *flags();
593    if (fl.max_free_fill_size > 0) {
594      // We have to skip the chunk header, it contains free_context_id.
595      uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size;
596      if (m->UsedSize() >= kChunkHeader2Size) {  // Skip Header2 in user area.
597        uptr size_to_fill = m->UsedSize() - kChunkHeader2Size;
598        size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size);
599        REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill);
600      }
601    }
602
603    // Poison the region.
604    PoisonShadow(m->Beg(),
605                 RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
606                 kAsanHeapFreeMagic);
607
608    AsanStats &thread_stats = GetCurrentThreadStats();
609    thread_stats.frees++;
610    thread_stats.freed += m->UsedSize();
611
612    // Push into quarantine.
613    if (t) {
614      AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
615      AllocatorCache *ac = GetAllocatorCache(ms);
616      quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac, stack), m,
617                     m->UsedSize());
618    } else {
619      SpinMutexLock l(&fallback_mutex);
620      AllocatorCache *ac = &fallback_allocator_cache;
621      quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac, stack),
622                     m, m->UsedSize());
623    }
624  }
625
626  void Deallocate(void *ptr, uptr delete_size, uptr delete_alignment,
627                  BufferedStackTrace *stack, AllocType alloc_type) {
628    uptr p = reinterpret_cast<uptr>(ptr);
629    if (p == 0) return;
630
631    uptr chunk_beg = p - kChunkHeaderSize;
632    AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
633
634    // On Windows, uninstrumented DLLs may allocate memory before ASan hooks
635    // malloc. Don't report an invalid free in this case.
636    if (SANITIZER_WINDOWS &&
637        !get_allocator().PointerIsMine(ptr)) {
638      if (!IsSystemHeapAddress(p))
639        ReportFreeNotMalloced(p, stack);
640      return;
641    }
642
643    ASAN_FREE_HOOK(ptr);
644
645    // Must mark the chunk as quarantined before any changes to its metadata.
646    // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag.
647    if (!AtomicallySetQuarantineFlagIfAllocated(m, ptr, stack)) return;
648
649    if (m->alloc_type != alloc_type) {
650      if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) {
651        ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type,
652                                (AllocType)alloc_type);
653      }
654    } else {
655      if (flags()->new_delete_type_mismatch &&
656          (alloc_type == FROM_NEW || alloc_type == FROM_NEW_BR) &&
657          ((delete_size && delete_size != m->UsedSize()) ||
658           ComputeUserRequestedAlignmentLog(delete_alignment) !=
659               m->user_requested_alignment_log)) {
660        ReportNewDeleteTypeMismatch(p, delete_size, delete_alignment, stack);
661      }
662    }
663
664    QuarantineChunk(m, ptr, stack);
665  }
666
667  void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) {
668    CHECK(old_ptr && new_size);
669    uptr p = reinterpret_cast<uptr>(old_ptr);
670    uptr chunk_beg = p - kChunkHeaderSize;
671    AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
672
673    AsanStats &thread_stats = GetCurrentThreadStats();
674    thread_stats.reallocs++;
675    thread_stats.realloced += new_size;
676
677    void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true);
678    if (new_ptr) {
679      u8 chunk_state = m->chunk_state;
680      if (chunk_state != CHUNK_ALLOCATED)
681        ReportInvalidFree(old_ptr, chunk_state, stack);
682      CHECK_NE(REAL(memcpy), nullptr);
683      uptr memcpy_size = Min(new_size, m->UsedSize());
684      // If realloc() races with free(), we may start copying freed memory.
685      // However, we will report racy double-free later anyway.
686      REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
687      Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC);
688    }
689    return new_ptr;
690  }
691
692  void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
693    if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
694      if (AllocatorMayReturnNull())
695        return nullptr;
696      ReportCallocOverflow(nmemb, size, stack);
697    }
698    void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
699    // If the memory comes from the secondary allocator no need to clear it
700    // as it comes directly from mmap.
701    if (ptr && allocator.FromPrimary(ptr))
702      REAL(memset)(ptr, 0, nmemb * size);
703    return ptr;
704  }
705
706  void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) {
707    if (chunk_state == CHUNK_QUARANTINE)
708      ReportDoubleFree((uptr)ptr, stack);
709    else
710      ReportFreeNotMalloced((uptr)ptr, stack);
711  }
712
713  void CommitBack(AsanThreadLocalMallocStorage *ms, BufferedStackTrace *stack) {
714    AllocatorCache *ac = GetAllocatorCache(ms);
715    quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac, stack));
716    allocator.SwallowCache(ac);
717  }
718
719  // -------------------------- Chunk lookup ----------------------
720
721  // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg).
722  AsanChunk *GetAsanChunk(void *alloc_beg) {
723    if (!alloc_beg) return nullptr;
724    if (!allocator.FromPrimary(alloc_beg)) {
725      uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg));
726      AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]);
727      return m;
728    }
729    uptr *alloc_magic = reinterpret_cast<uptr *>(alloc_beg);
730    if (alloc_magic[0] == kAllocBegMagic)
731      return reinterpret_cast<AsanChunk *>(alloc_magic[1]);
732    return reinterpret_cast<AsanChunk *>(alloc_beg);
733  }
734
735  AsanChunk *GetAsanChunkByAddr(uptr p) {
736    void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p));
737    return GetAsanChunk(alloc_beg);
738  }
739
740  // Allocator must be locked when this function is called.
741  AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) {
742    void *alloc_beg =
743        allocator.GetBlockBeginFastLocked(reinterpret_cast<void *>(p));
744    return GetAsanChunk(alloc_beg);
745  }
746
747  uptr AllocationSize(uptr p) {
748    AsanChunk *m = GetAsanChunkByAddr(p);
749    if (!m) return 0;
750    if (m->chunk_state != CHUNK_ALLOCATED) return 0;
751    if (m->Beg() != p) return 0;
752    return m->UsedSize();
753  }
754
755  AsanChunkView FindHeapChunkByAddress(uptr addr) {
756    AsanChunk *m1 = GetAsanChunkByAddr(addr);
757    if (!m1) return AsanChunkView(m1);
758    sptr offset = 0;
759    if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) {
760      // The address is in the chunk's left redzone, so maybe it is actually
761      // a right buffer overflow from the other chunk to the left.
762      // Search a bit to the left to see if there is another chunk.
763      AsanChunk *m2 = nullptr;
764      for (uptr l = 1; l < GetPageSizeCached(); l++) {
765        m2 = GetAsanChunkByAddr(addr - l);
766        if (m2 == m1) continue;  // Still the same chunk.
767        break;
768      }
769      if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset))
770        m1 = ChooseChunk(addr, m2, m1);
771    }
772    return AsanChunkView(m1);
773  }
774
775  void Purge(BufferedStackTrace *stack) {
776    AsanThread *t = GetCurrentThread();
777    if (t) {
778      AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
779      quarantine.DrainAndRecycle(GetQuarantineCache(ms),
780                                 QuarantineCallback(GetAllocatorCache(ms),
781                                                    stack));
782    }
783    {
784      SpinMutexLock l(&fallback_mutex);
785      quarantine.DrainAndRecycle(&fallback_quarantine_cache,
786                                 QuarantineCallback(&fallback_allocator_cache,
787                                                    stack));
788    }
789
790    allocator.ForceReleaseToOS();
791  }
792
793  void PrintStats() {
794    allocator.PrintStats();
795    quarantine.PrintStats();
796  }
797
798  void ForceLock() {
799    allocator.ForceLock();
800    fallback_mutex.Lock();
801  }
802
803  void ForceUnlock() {
804    fallback_mutex.Unlock();
805    allocator.ForceUnlock();
806  }
807};
808
809static Allocator instance(LINKER_INITIALIZED);
810
811static AsanAllocator &get_allocator() {
812  return instance.allocator;
813}
814
815bool AsanChunkView::IsValid() const {
816  return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE;
817}
818bool AsanChunkView::IsAllocated() const {
819  return chunk_ && chunk_->chunk_state == CHUNK_ALLOCATED;
820}
821bool AsanChunkView::IsQuarantined() const {
822  return chunk_ && chunk_->chunk_state == CHUNK_QUARANTINE;
823}
824uptr AsanChunkView::Beg() const { return chunk_->Beg(); }
825uptr AsanChunkView::End() const { return Beg() + UsedSize(); }
826uptr AsanChunkView::UsedSize() const { return chunk_->UsedSize(); }
827u32 AsanChunkView::UserRequestedAlignment() const {
828  return Allocator::ComputeUserAlignment(chunk_->user_requested_alignment_log);
829}
830uptr AsanChunkView::AllocTid() const { return chunk_->alloc_tid; }
831uptr AsanChunkView::FreeTid() const { return chunk_->free_tid; }
832AllocType AsanChunkView::GetAllocType() const {
833  return (AllocType)chunk_->alloc_type;
834}
835
836static StackTrace GetStackTraceFromId(u32 id) {
837  CHECK(id);
838  StackTrace res = StackDepotGet(id);
839  CHECK(res.trace);
840  return res;
841}
842
843u32 AsanChunkView::GetAllocStackId() const { return chunk_->alloc_context_id; }
844u32 AsanChunkView::GetFreeStackId() const { return chunk_->free_context_id; }
845
846StackTrace AsanChunkView::GetAllocStack() const {
847  return GetStackTraceFromId(GetAllocStackId());
848}
849
850StackTrace AsanChunkView::GetFreeStack() const {
851  return GetStackTraceFromId(GetFreeStackId());
852}
853
854void InitializeAllocator(const AllocatorOptions &options) {
855  instance.InitLinkerInitialized(options);
856}
857
858void ReInitializeAllocator(const AllocatorOptions &options) {
859  instance.ReInitialize(options);
860}
861
862void GetAllocatorOptions(AllocatorOptions *options) {
863  instance.GetOptions(options);
864}
865
866AsanChunkView FindHeapChunkByAddress(uptr addr) {
867  return instance.FindHeapChunkByAddress(addr);
868}
869AsanChunkView FindHeapChunkByAllocBeg(uptr addr) {
870  return AsanChunkView(instance.GetAsanChunk(reinterpret_cast<void*>(addr)));
871}
872
873void AsanThreadLocalMallocStorage::CommitBack() {
874  GET_STACK_TRACE_MALLOC;
875  instance.CommitBack(this, &stack);
876}
877
878void PrintInternalAllocatorStats() {
879  instance.PrintStats();
880}
881
882void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) {
883  instance.Deallocate(ptr, 0, 0, stack, alloc_type);
884}
885
886void asan_delete(void *ptr, uptr size, uptr alignment,
887                 BufferedStackTrace *stack, AllocType alloc_type) {
888  instance.Deallocate(ptr, size, alignment, stack, alloc_type);
889}
890
891void *asan_malloc(uptr size, BufferedStackTrace *stack) {
892  return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true));
893}
894
895void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
896  return SetErrnoOnNull(instance.Calloc(nmemb, size, stack));
897}
898
899void *asan_reallocarray(void *p, uptr nmemb, uptr size,
900                        BufferedStackTrace *stack) {
901  if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
902    errno = errno_ENOMEM;
903    if (AllocatorMayReturnNull())
904      return nullptr;
905    ReportReallocArrayOverflow(nmemb, size, stack);
906  }
907  return asan_realloc(p, nmemb * size, stack);
908}
909
910void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) {
911  if (!p)
912    return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true));
913  if (size == 0) {
914    if (flags()->allocator_frees_and_returns_null_on_realloc_zero) {
915      instance.Deallocate(p, 0, 0, stack, FROM_MALLOC);
916      return nullptr;
917    }
918    // Allocate a size of 1 if we shouldn't free() on Realloc to 0
919    size = 1;
920  }
921  return SetErrnoOnNull(instance.Reallocate(p, size, stack));
922}
923
924void *asan_valloc(uptr size, BufferedStackTrace *stack) {
925  return SetErrnoOnNull(
926      instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true));
927}
928
929void *asan_pvalloc(uptr size, BufferedStackTrace *stack) {
930  uptr PageSize = GetPageSizeCached();
931  if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
932    errno = errno_ENOMEM;
933    if (AllocatorMayReturnNull())
934      return nullptr;
935    ReportPvallocOverflow(size, stack);
936  }
937  // pvalloc(0) should allocate one page.
938  size = size ? RoundUpTo(size, PageSize) : PageSize;
939  return SetErrnoOnNull(
940      instance.Allocate(size, PageSize, stack, FROM_MALLOC, true));
941}
942
943void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
944                    AllocType alloc_type) {
945  if (UNLIKELY(!IsPowerOfTwo(alignment))) {
946    errno = errno_EINVAL;
947    if (AllocatorMayReturnNull())
948      return nullptr;
949    ReportInvalidAllocationAlignment(alignment, stack);
950  }
951  return SetErrnoOnNull(
952      instance.Allocate(size, alignment, stack, alloc_type, true));
953}
954
955void *asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack) {
956  if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
957    errno = errno_EINVAL;
958    if (AllocatorMayReturnNull())
959      return nullptr;
960    ReportInvalidAlignedAllocAlignment(size, alignment, stack);
961  }
962  return SetErrnoOnNull(
963      instance.Allocate(size, alignment, stack, FROM_MALLOC, true));
964}
965
966int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
967                        BufferedStackTrace *stack) {
968  if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
969    if (AllocatorMayReturnNull())
970      return errno_EINVAL;
971    ReportInvalidPosixMemalignAlignment(alignment, stack);
972  }
973  void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true);
974  if (UNLIKELY(!ptr))
975    // OOM error is already taken care of by Allocate.
976    return errno_ENOMEM;
977  CHECK(IsAligned((uptr)ptr, alignment));
978  *memptr = ptr;
979  return 0;
980}
981
982uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) {
983  if (!ptr) return 0;
984  uptr usable_size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
985  if (flags()->check_malloc_usable_size && (usable_size == 0)) {
986    GET_STACK_TRACE_FATAL(pc, bp);
987    ReportMallocUsableSizeNotOwned((uptr)ptr, &stack);
988  }
989  return usable_size;
990}
991
992uptr asan_mz_size(const void *ptr) {
993  return instance.AllocationSize(reinterpret_cast<uptr>(ptr));
994}
995
996void asan_mz_force_lock() {
997  instance.ForceLock();
998}
999
1000void asan_mz_force_unlock() {
1001  instance.ForceUnlock();
1002}
1003
1004void AsanSoftRssLimitExceededCallback(bool limit_exceeded) {
1005  instance.SetRssLimitExceeded(limit_exceeded);
1006}
1007
1008} // namespace __asan
1009
1010// --- Implementation of LSan-specific functions --- {{{1
1011namespace __lsan {
1012void LockAllocator() {
1013  __asan::get_allocator().ForceLock();
1014}
1015
1016void UnlockAllocator() {
1017  __asan::get_allocator().ForceUnlock();
1018}
1019
1020void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
1021  *begin = (uptr)&__asan::get_allocator();
1022  *end = *begin + sizeof(__asan::get_allocator());
1023}
1024
1025uptr PointsIntoChunk(void* p) {
1026  uptr addr = reinterpret_cast<uptr>(p);
1027  __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr);
1028  if (!m) return 0;
1029  uptr chunk = m->Beg();
1030  if (m->chunk_state != __asan::CHUNK_ALLOCATED)
1031    return 0;
1032  if (m->AddrIsInside(addr, /*locked_version=*/true))
1033    return chunk;
1034  if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true),
1035                                  addr))
1036    return chunk;
1037  return 0;
1038}
1039
1040uptr GetUserBegin(uptr chunk) {
1041  __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk);
1042  CHECK(m);
1043  return m->Beg();
1044}
1045
1046LsanMetadata::LsanMetadata(uptr chunk) {
1047  metadata_ = reinterpret_cast<void *>(chunk - __asan::kChunkHeaderSize);
1048}
1049
1050bool LsanMetadata::allocated() const {
1051  __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
1052  return m->chunk_state == __asan::CHUNK_ALLOCATED;
1053}
1054
1055ChunkTag LsanMetadata::tag() const {
1056  __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
1057  return static_cast<ChunkTag>(m->lsan_tag);
1058}
1059
1060void LsanMetadata::set_tag(ChunkTag value) {
1061  __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
1062  m->lsan_tag = value;
1063}
1064
1065uptr LsanMetadata::requested_size() const {
1066  __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
1067  return m->UsedSize(/*locked_version=*/true);
1068}
1069
1070u32 LsanMetadata::stack_trace_id() const {
1071  __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
1072  return m->alloc_context_id;
1073}
1074
1075void ForEachChunk(ForEachChunkCallback callback, void *arg) {
1076  __asan::get_allocator().ForEachChunk(callback, arg);
1077}
1078
1079IgnoreObjectResult IgnoreObjectLocked(const void *p) {
1080  uptr addr = reinterpret_cast<uptr>(p);
1081  __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr);
1082  if (!m) return kIgnoreObjectInvalid;
1083  if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) {
1084    if (m->lsan_tag == kIgnored)
1085      return kIgnoreObjectAlreadyIgnored;
1086    m->lsan_tag = __lsan::kIgnored;
1087    return kIgnoreObjectSuccess;
1088  } else {
1089    return kIgnoreObjectInvalid;
1090  }
1091}
1092}  // namespace __lsan
1093
1094// ---------------------- Interface ---------------- {{{1
1095using namespace __asan;
1096
1097// ASan allocator doesn't reserve extra bytes, so normally we would
1098// just return "size". We don't want to expose our redzone sizes, etc here.
1099uptr __sanitizer_get_estimated_allocated_size(uptr size) {
1100  return size;
1101}
1102
1103int __sanitizer_get_ownership(const void *p) {
1104  uptr ptr = reinterpret_cast<uptr>(p);
1105  return instance.AllocationSize(ptr) > 0;
1106}
1107
1108uptr __sanitizer_get_allocated_size(const void *p) {
1109  if (!p) return 0;
1110  uptr ptr = reinterpret_cast<uptr>(p);
1111  uptr allocated_size = instance.AllocationSize(ptr);
1112  // Die if p is not malloced or if it is already freed.
1113  if (allocated_size == 0) {
1114    GET_STACK_TRACE_FATAL_HERE;
1115    ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack);
1116  }
1117  return allocated_size;
1118}
1119
1120void __sanitizer_purge_allocator() {
1121  GET_STACK_TRACE_MALLOC;
1122  instance.Purge(&stack);
1123}
1124
1125int __asan_update_allocation_context(void* addr) {
1126  GET_STACK_TRACE_MALLOC;
1127  return instance.UpdateAllocationStack((uptr)addr, &stack);
1128}
1129
1130#if !SANITIZER_SUPPORTS_WEAK_HOOKS
1131// Provide default (no-op) implementation of malloc hooks.
1132SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook,
1133                             void *ptr, uptr size) {
1134  (void)ptr;
1135  (void)size;
1136}
1137
1138SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) {
1139  (void)ptr;
1140}
1141#endif
1142