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