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