diff options
Diffstat (limited to 'gcc-4.8/libsanitizer/asan/asan_allocator.cc')
-rw-r--r-- | gcc-4.8/libsanitizer/asan/asan_allocator.cc | 811 |
1 files changed, 811 insertions, 0 deletions
diff --git a/gcc-4.8/libsanitizer/asan/asan_allocator.cc b/gcc-4.8/libsanitizer/asan/asan_allocator.cc new file mode 100644 index 000000000..4e97ff575 --- /dev/null +++ b/gcc-4.8/libsanitizer/asan/asan_allocator.cc @@ -0,0 +1,811 @@ +//===-- asan_allocator.cc -------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Implementation of ASan's memory allocator. +// Evey piece of memory (AsanChunk) allocated by the allocator +// has a left redzone of REDZONE bytes and +// a right redzone such that the end of the chunk is aligned by REDZONE +// (i.e. the right redzone is between 0 and REDZONE-1). +// The left redzone is always poisoned. +// The right redzone is poisoned on malloc, the body is poisoned on free. +// Once freed, a chunk is moved to a quarantine (fifo list). +// After quarantine, a chunk is returned to freelists. +// +// The left redzone contains ASan's internal data and the stack trace of +// the malloc call. +// Once freed, the body of the chunk contains the stack trace of the free call. +// +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" + +#if ASAN_ALLOCATOR_VERSION == 1 +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" +#include "asan_stats.h" +#include "asan_report.h" +#include "asan_thread.h" +#include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_mutex.h" + +namespace __asan { + +#define REDZONE ((uptr)(flags()->redzone)) +static const uptr kMinAllocSize = REDZONE * 2; +static const u64 kMaxAvailableRam = 128ULL << 30; // 128G +static const uptr kMaxThreadLocalQuarantine = 1 << 20; // 1M + +static const uptr kMinMmapSize = (ASAN_LOW_MEMORY) ? 4UL << 17 : 4UL << 20; +static const uptr kMaxSizeForThreadLocalFreeList = + (ASAN_LOW_MEMORY) ? 1 << 15 : 1 << 17; + +// Size classes less than kMallocSizeClassStep are powers of two. +// All other size classes are multiples of kMallocSizeClassStep. +static const uptr kMallocSizeClassStepLog = 26; +static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog; + +static const uptr kMaxAllowedMallocSize = + (SANITIZER_WORDSIZE == 32) ? 3UL << 30 : 8UL << 30; + +static inline uptr SizeClassToSize(u8 size_class) { + CHECK(size_class < kNumberOfSizeClasses); + if (size_class <= kMallocSizeClassStepLog) { + return 1UL << size_class; + } else { + return (size_class - kMallocSizeClassStepLog) * kMallocSizeClassStep; + } +} + +static inline u8 SizeToSizeClass(uptr size) { + u8 res = 0; + if (size <= kMallocSizeClassStep) { + uptr rounded = RoundUpToPowerOfTwo(size); + res = Log2(rounded); + } else { + res = ((size + kMallocSizeClassStep - 1) / kMallocSizeClassStep) + + kMallocSizeClassStepLog; + } + CHECK(res < kNumberOfSizeClasses); + CHECK(size <= SizeClassToSize(res)); + return res; +} + +// Given REDZONE bytes, we need to mark first size bytes +// as addressable and the rest REDZONE-size bytes as unaddressable. +static void PoisonHeapPartialRightRedzone(uptr mem, uptr size) { + CHECK(size <= REDZONE); + CHECK(IsAligned(mem, REDZONE)); + CHECK(IsPowerOfTwo(SHADOW_GRANULARITY)); + CHECK(IsPowerOfTwo(REDZONE)); + CHECK(REDZONE >= SHADOW_GRANULARITY); + PoisonShadowPartialRightRedzone(mem, size, REDZONE, + kAsanHeapRightRedzoneMagic); +} + +static u8 *MmapNewPagesAndPoisonShadow(uptr size) { + CHECK(IsAligned(size, GetPageSizeCached())); + u8 *res = (u8*)MmapOrDie(size, __FUNCTION__); + PoisonShadow((uptr)res, size, kAsanHeapLeftRedzoneMagic); + if (flags()->debug) { + Printf("ASAN_MMAP: [%p, %p)\n", res, res + size); + } + return res; +} + +// Every chunk of memory allocated by this allocator can be in one of 3 states: +// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. +// CHUNK_ALLOCATED: the chunk is allocated and not yet freed. +// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. +// +// The pseudo state CHUNK_MEMALIGN is used to mark that the address is not +// the beginning of a AsanChunk (in which the actual chunk resides at +// this - this->used_size). +// +// The magic numbers for the enum values are taken randomly. +enum { + CHUNK_AVAILABLE = 0x57, + CHUNK_ALLOCATED = 0x32, + CHUNK_QUARANTINE = 0x19, + CHUNK_MEMALIGN = 0xDC +}; + +struct ChunkBase { + // First 8 bytes. + uptr chunk_state : 8; + uptr alloc_tid : 24; + uptr size_class : 8; + uptr free_tid : 24; + + // Second 8 bytes. + uptr alignment_log : 8; + uptr alloc_type : 2; + uptr used_size : FIRST_32_SECOND_64(32, 54); // Size requested by the user. + + // This field may overlap with the user area and thus should not + // be used while the chunk is in CHUNK_ALLOCATED state. + AsanChunk *next; + + // Typically the beginning of the user-accessible memory is 'this'+REDZONE + // and is also aligned by REDZONE. However, if the memory is allocated + // by memalign, the alignment might be higher and the user-accessible memory + // starts at the first properly aligned address after 'this'. + uptr Beg() { return RoundUpTo((uptr)this + 1, 1 << alignment_log); } + uptr Size() { return SizeClassToSize(size_class); } + u8 SizeClass() { return size_class; } +}; + +struct AsanChunk: public ChunkBase { + u32 *compressed_alloc_stack() { + return (u32*)((uptr)this + sizeof(ChunkBase)); + } + u32 *compressed_free_stack() { + return (u32*)((uptr)this + Max((uptr)REDZONE, (uptr)sizeof(ChunkBase))); + } + + // The left redzone after the ChunkBase is given to the alloc stack trace. + uptr compressed_alloc_stack_size() { + if (REDZONE < sizeof(ChunkBase)) return 0; + return (REDZONE - sizeof(ChunkBase)) / sizeof(u32); + } + uptr compressed_free_stack_size() { + if (REDZONE < sizeof(ChunkBase)) return 0; + return (REDZONE) / sizeof(u32); + } +}; + +uptr AsanChunkView::Beg() { return chunk_->Beg(); } +uptr AsanChunkView::End() { return Beg() + UsedSize(); } +uptr AsanChunkView::UsedSize() { return chunk_->used_size; } +uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; } +uptr AsanChunkView::FreeTid() { return chunk_->free_tid; } + +void AsanChunkView::GetAllocStack(StackTrace *stack) { + StackTrace::UncompressStack(stack, chunk_->compressed_alloc_stack(), + chunk_->compressed_alloc_stack_size()); +} + +void AsanChunkView::GetFreeStack(StackTrace *stack) { + StackTrace::UncompressStack(stack, chunk_->compressed_free_stack(), + chunk_->compressed_free_stack_size()); +} + +static AsanChunk *PtrToChunk(uptr ptr) { + AsanChunk *m = (AsanChunk*)(ptr - REDZONE); + if (m->chunk_state == CHUNK_MEMALIGN) { + m = (AsanChunk*)((uptr)m - m->used_size); + } + return m; +} + +void AsanChunkFifoList::PushList(AsanChunkFifoList *q) { + CHECK(q->size() > 0); + size_ += q->size(); + append_back(q); + q->clear(); +} + +void AsanChunkFifoList::Push(AsanChunk *n) { + push_back(n); + size_ += n->Size(); +} + +// Interesting performance observation: this function takes up to 15% of overal +// allocator time. That's because *first_ has been evicted from cache long time +// ago. Not sure if we can or want to do anything with this. +AsanChunk *AsanChunkFifoList::Pop() { + CHECK(first_); + AsanChunk *res = front(); + size_ -= res->Size(); + pop_front(); + return res; +} + +// All pages we ever allocated. +struct PageGroup { + uptr beg; + uptr end; + uptr size_of_chunk; + uptr last_chunk; + bool InRange(uptr addr) { + return addr >= beg && addr < end; + } +}; + +class MallocInfo { + public: + explicit MallocInfo(LinkerInitialized x) : mu_(x) { } + + AsanChunk *AllocateChunks(u8 size_class, uptr n_chunks) { + AsanChunk *m = 0; + AsanChunk **fl = &free_lists_[size_class]; + { + BlockingMutexLock lock(&mu_); + for (uptr i = 0; i < n_chunks; i++) { + if (!(*fl)) { + *fl = GetNewChunks(size_class); + } + AsanChunk *t = *fl; + *fl = t->next; + t->next = m; + CHECK(t->chunk_state == CHUNK_AVAILABLE); + m = t; + } + } + return m; + } + + void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x, + bool eat_free_lists) { + CHECK(flags()->quarantine_size > 0); + BlockingMutexLock lock(&mu_); + AsanChunkFifoList *q = &x->quarantine_; + if (q->size() > 0) { + quarantine_.PushList(q); + while (quarantine_.size() > (uptr)flags()->quarantine_size) { + QuarantinePop(); + } + } + if (eat_free_lists) { + for (uptr size_class = 0; size_class < kNumberOfSizeClasses; + size_class++) { + AsanChunk *m = x->free_lists_[size_class]; + while (m) { + AsanChunk *t = m->next; + m->next = free_lists_[size_class]; + free_lists_[size_class] = m; + m = t; + } + x->free_lists_[size_class] = 0; + } + } + } + + void BypassThreadLocalQuarantine(AsanChunk *chunk) { + BlockingMutexLock lock(&mu_); + quarantine_.Push(chunk); + } + + AsanChunk *FindChunkByAddr(uptr addr) { + BlockingMutexLock lock(&mu_); + return FindChunkByAddrUnlocked(addr); + } + + uptr AllocationSize(uptr ptr) { + if (!ptr) return 0; + BlockingMutexLock lock(&mu_); + + // Make sure this is our chunk and |ptr| actually points to the beginning + // of the allocated memory. + AsanChunk *m = FindChunkByAddrUnlocked(ptr); + if (!m || m->Beg() != ptr) return 0; + + if (m->chunk_state == CHUNK_ALLOCATED) { + return m->used_size; + } else { + return 0; + } + } + + void ForceLock() { + mu_.Lock(); + } + + void ForceUnlock() { + mu_.Unlock(); + } + + void PrintStatus() { + BlockingMutexLock lock(&mu_); + uptr malloced = 0; + + Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ", + quarantine_.size() >> 20, malloced >> 20); + for (uptr j = 1; j < kNumberOfSizeClasses; j++) { + AsanChunk *i = free_lists_[j]; + if (!i) continue; + uptr t = 0; + for (; i; i = i->next) { + t += i->Size(); + } + Printf("%zu:%zu ", j, t >> 20); + } + Printf("\n"); + } + + PageGroup *FindPageGroup(uptr addr) { + BlockingMutexLock lock(&mu_); + return FindPageGroupUnlocked(addr); + } + + private: + PageGroup *FindPageGroupUnlocked(uptr addr) { + int n = atomic_load(&n_page_groups_, memory_order_relaxed); + // If the page groups are not sorted yet, sort them. + if (n_sorted_page_groups_ < n) { + SortArray((uptr*)page_groups_, n); + n_sorted_page_groups_ = n; + } + // Binary search over the page groups. + int beg = 0, end = n; + while (beg < end) { + int med = (beg + end) / 2; + uptr g = (uptr)page_groups_[med]; + if (addr > g) { + // 'g' points to the end of the group, so 'addr' + // may not belong to page_groups_[med] or any previous group. + beg = med + 1; + } else { + // 'addr' may belong to page_groups_[med] or a previous group. + end = med; + } + } + if (beg >= n) + return 0; + PageGroup *g = page_groups_[beg]; + CHECK(g); + if (g->InRange(addr)) + return g; + return 0; + } + + // We have an address between two chunks, and we want to report just one. + AsanChunk *ChooseChunk(uptr addr, + AsanChunk *left_chunk, AsanChunk *right_chunk) { + // Prefer an allocated chunk or a chunk from quarantine. + if (left_chunk->chunk_state == CHUNK_AVAILABLE && + right_chunk->chunk_state != CHUNK_AVAILABLE) + return right_chunk; + if (right_chunk->chunk_state == CHUNK_AVAILABLE && + left_chunk->chunk_state != CHUNK_AVAILABLE) + return left_chunk; + // Choose based on offset. + sptr l_offset = 0, r_offset = 0; + CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); + CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); + if (l_offset < r_offset) + return left_chunk; + return right_chunk; + } + + AsanChunk *FindChunkByAddrUnlocked(uptr addr) { + PageGroup *g = FindPageGroupUnlocked(addr); + if (!g) return 0; + CHECK(g->size_of_chunk); + uptr offset_from_beg = addr - g->beg; + uptr this_chunk_addr = g->beg + + (offset_from_beg / g->size_of_chunk) * g->size_of_chunk; + CHECK(g->InRange(this_chunk_addr)); + AsanChunk *m = (AsanChunk*)this_chunk_addr; + CHECK(m->chunk_state == CHUNK_ALLOCATED || + m->chunk_state == CHUNK_AVAILABLE || + m->chunk_state == CHUNK_QUARANTINE); + sptr offset = 0; + AsanChunkView m_view(m); + if (m_view.AddrIsInside(addr, 1, &offset)) + return m; + + if (m_view.AddrIsAtRight(addr, 1, &offset)) { + if (this_chunk_addr == g->last_chunk) // rightmost chunk + return m; + uptr right_chunk_addr = this_chunk_addr + g->size_of_chunk; + CHECK(g->InRange(right_chunk_addr)); + return ChooseChunk(addr, m, (AsanChunk*)right_chunk_addr); + } else { + CHECK(m_view.AddrIsAtLeft(addr, 1, &offset)); + if (this_chunk_addr == g->beg) // leftmost chunk + return m; + uptr left_chunk_addr = this_chunk_addr - g->size_of_chunk; + CHECK(g->InRange(left_chunk_addr)); + return ChooseChunk(addr, (AsanChunk*)left_chunk_addr, m); + } + } + + void QuarantinePop() { + CHECK(quarantine_.size() > 0); + AsanChunk *m = quarantine_.Pop(); + CHECK(m); + // if (F_v >= 2) Printf("MallocInfo::pop %p\n", m); + + CHECK(m->chunk_state == CHUNK_QUARANTINE); + m->chunk_state = CHUNK_AVAILABLE; + PoisonShadow((uptr)m, m->Size(), kAsanHeapLeftRedzoneMagic); + CHECK(m->alloc_tid >= 0); + CHECK(m->free_tid >= 0); + + uptr size_class = m->SizeClass(); + m->next = free_lists_[size_class]; + free_lists_[size_class] = m; + + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.real_frees++; + thread_stats.really_freed += m->used_size; + thread_stats.really_freed_redzones += m->Size() - m->used_size; + thread_stats.really_freed_by_size[m->SizeClass()]++; + } + + // Get a list of newly allocated chunks. + AsanChunk *GetNewChunks(u8 size_class) { + uptr size = SizeClassToSize(size_class); + CHECK(IsPowerOfTwo(kMinMmapSize)); + CHECK(size < kMinMmapSize || (size % kMinMmapSize) == 0); + uptr mmap_size = Max(size, kMinMmapSize); + uptr n_chunks = mmap_size / size; + CHECK(n_chunks * size == mmap_size); + uptr PageSize = GetPageSizeCached(); + if (size < PageSize) { + // Size is small, just poison the last chunk. + n_chunks--; + } else { + // Size is large, allocate an extra page at right and poison it. + mmap_size += PageSize; + } + CHECK(n_chunks > 0); + u8 *mem = MmapNewPagesAndPoisonShadow(mmap_size); + + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.mmaps++; + thread_stats.mmaped += mmap_size; + thread_stats.mmaped_by_size[size_class] += n_chunks; + + AsanChunk *res = 0; + for (uptr i = 0; i < n_chunks; i++) { + AsanChunk *m = (AsanChunk*)(mem + i * size); + m->chunk_state = CHUNK_AVAILABLE; + m->size_class = size_class; + m->next = res; + res = m; + } + PageGroup *pg = (PageGroup*)(mem + n_chunks * size); + // This memory is already poisoned, no need to poison it again. + pg->beg = (uptr)mem; + pg->end = pg->beg + mmap_size; + pg->size_of_chunk = size; + pg->last_chunk = (uptr)(mem + size * (n_chunks - 1)); + int idx = atomic_fetch_add(&n_page_groups_, 1, memory_order_relaxed); + CHECK(idx < (int)ARRAY_SIZE(page_groups_)); + page_groups_[idx] = pg; + return res; + } + + AsanChunk *free_lists_[kNumberOfSizeClasses]; + AsanChunkFifoList quarantine_; + BlockingMutex mu_; + + PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize]; + atomic_uint32_t n_page_groups_; + int n_sorted_page_groups_; +}; + +static MallocInfo malloc_info(LINKER_INITIALIZED); + +void AsanThreadLocalMallocStorage::CommitBack() { + malloc_info.SwallowThreadLocalMallocStorage(this, true); +} + +AsanChunkView FindHeapChunkByAddress(uptr address) { + return AsanChunkView(malloc_info.FindChunkByAddr(address)); +} + +static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type) { + __asan_init(); + CHECK(stack); + if (size == 0) { + size = 1; // TODO(kcc): do something smarter + } + CHECK(IsPowerOfTwo(alignment)); + uptr rounded_size = RoundUpTo(size, REDZONE); + uptr needed_size = rounded_size + REDZONE; + if (alignment > REDZONE) { + needed_size += alignment; + } + CHECK(IsAligned(needed_size, REDZONE)); + if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { + Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", + (void*)size); + return 0; + } + + u8 size_class = SizeToSizeClass(needed_size); + uptr size_to_allocate = SizeClassToSize(size_class); + CHECK(size_to_allocate >= kMinAllocSize); + CHECK(size_to_allocate >= needed_size); + CHECK(IsAligned(size_to_allocate, REDZONE)); + + if (flags()->verbosity >= 3) { + Printf("Allocate align: %zu size: %zu class: %u real: %zu\n", + alignment, size, size_class, size_to_allocate); + } + + AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + // Statistics + thread_stats.mallocs++; + thread_stats.malloced += size; + thread_stats.malloced_redzones += size_to_allocate - size; + thread_stats.malloced_by_size[size_class]++; + + AsanChunk *m = 0; + if (!t || size_to_allocate >= kMaxSizeForThreadLocalFreeList) { + // get directly from global storage. + m = malloc_info.AllocateChunks(size_class, 1); + thread_stats.malloc_large++; + } else { + // get from the thread-local storage. + AsanChunk **fl = &t->malloc_storage().free_lists_[size_class]; + if (!*fl) { + uptr n_new_chunks = kMaxSizeForThreadLocalFreeList / size_to_allocate; + *fl = malloc_info.AllocateChunks(size_class, n_new_chunks); + thread_stats.malloc_small_slow++; + } + m = *fl; + *fl = (*fl)->next; + } + CHECK(m); + CHECK(m->chunk_state == CHUNK_AVAILABLE); + m->chunk_state = CHUNK_ALLOCATED; + m->alloc_type = alloc_type; + m->next = 0; + CHECK(m->Size() == size_to_allocate); + uptr addr = (uptr)m + REDZONE; + CHECK(addr <= (uptr)m->compressed_free_stack()); + + if (alignment > REDZONE && (addr & (alignment - 1))) { + addr = RoundUpTo(addr, alignment); + CHECK((addr & (alignment - 1)) == 0); + AsanChunk *p = (AsanChunk*)(addr - REDZONE); + p->chunk_state = CHUNK_MEMALIGN; + p->used_size = (uptr)p - (uptr)m; + m->alignment_log = Log2(alignment); + CHECK(m->Beg() == addr); + } else { + m->alignment_log = Log2(REDZONE); + } + CHECK(m == PtrToChunk(addr)); + m->used_size = size; + CHECK(m->Beg() == addr); + m->alloc_tid = t ? t->tid() : 0; + m->free_tid = kInvalidTid; + StackTrace::CompressStack(stack, m->compressed_alloc_stack(), + m->compressed_alloc_stack_size()); + PoisonShadow(addr, rounded_size, 0); + if (size < rounded_size) { + PoisonHeapPartialRightRedzone(addr + rounded_size - REDZONE, + size & (REDZONE - 1)); + } + if (size <= (uptr)(flags()->max_malloc_fill_size)) { + REAL(memset)((void*)addr, 0, rounded_size); + } + return (u8*)addr; +} + +static void Deallocate(u8 *ptr, StackTrace *stack, AllocType alloc_type) { + if (!ptr) return; + CHECK(stack); + + if (flags()->debug) { + CHECK(malloc_info.FindPageGroup((uptr)ptr)); + } + + // Printf("Deallocate %p\n", ptr); + AsanChunk *m = PtrToChunk((uptr)ptr); + + // Flip the chunk_state atomically to avoid race on double-free. + u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE, + memory_order_acq_rel); + + if (old_chunk_state == CHUNK_QUARANTINE) { + ReportDoubleFree((uptr)ptr, stack); + } else if (old_chunk_state != CHUNK_ALLOCATED) { + ReportFreeNotMalloced((uptr)ptr, stack); + } + CHECK(old_chunk_state == CHUNK_ALLOCATED); + if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch) + ReportAllocTypeMismatch((uptr)ptr, stack, + (AllocType)m->alloc_type, (AllocType)alloc_type); + // With REDZONE==16 m->next is in the user area, otherwise it should be 0. + CHECK(REDZONE <= 16 || !m->next); + CHECK(m->free_tid == kInvalidTid); + CHECK(m->alloc_tid >= 0); + AsanThread *t = asanThreadRegistry().GetCurrent(); + m->free_tid = t ? t->tid() : 0; + StackTrace::CompressStack(stack, m->compressed_free_stack(), + m->compressed_free_stack_size()); + uptr rounded_size = RoundUpTo(m->used_size, REDZONE); + PoisonShadow((uptr)ptr, rounded_size, kAsanHeapFreeMagic); + + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.frees++; + thread_stats.freed += m->used_size; + thread_stats.freed_by_size[m->SizeClass()]++; + + CHECK(m->chunk_state == CHUNK_QUARANTINE); + + if (t) { + AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); + ms->quarantine_.Push(m); + + if (ms->quarantine_.size() > kMaxThreadLocalQuarantine) { + malloc_info.SwallowThreadLocalMallocStorage(ms, false); + } + } else { + malloc_info.BypassThreadLocalQuarantine(m); + } +} + +static u8 *Reallocate(u8 *old_ptr, uptr new_size, + StackTrace *stack) { + CHECK(old_ptr && new_size); + + // Statistics. + AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + thread_stats.reallocs++; + thread_stats.realloced += new_size; + + AsanChunk *m = PtrToChunk((uptr)old_ptr); + CHECK(m->chunk_state == CHUNK_ALLOCATED); + uptr old_size = m->used_size; + uptr memcpy_size = Min(new_size, old_size); + u8 *new_ptr = Allocate(0, new_size, stack, FROM_MALLOC); + if (new_ptr) { + CHECK(REAL(memcpy) != 0); + REAL(memcpy)(new_ptr, old_ptr, memcpy_size); + Deallocate(old_ptr, stack, FROM_MALLOC); + } + return new_ptr; +} + +} // namespace __asan + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +// Provide default (no-op) implementation of malloc hooks. +extern "C" { +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +void __asan_malloc_hook(void *ptr, uptr size) { + (void)ptr; + (void)size; +} +SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +void __asan_free_hook(void *ptr) { + (void)ptr; +} +} // extern "C" +#endif + +namespace __asan { + +void InitializeAllocator() { } + +void PrintInternalAllocatorStats() { +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, + AllocType alloc_type) { + void *ptr = (void*)Allocate(alignment, size, stack, alloc_type); + ASAN_MALLOC_HOOK(ptr, size); + return ptr; +} + +SANITIZER_INTERFACE_ATTRIBUTE +void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { + ASAN_FREE_HOOK(ptr); + Deallocate((u8*)ptr, stack, alloc_type); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *asan_malloc(uptr size, StackTrace *stack) { + void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC); + ASAN_MALLOC_HOOK(ptr, size); + return ptr; +} + +void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { + if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; + void *ptr = (void*)Allocate(0, nmemb * size, stack, FROM_MALLOC); + if (ptr) + REAL(memset)(ptr, 0, nmemb * size); + ASAN_MALLOC_HOOK(ptr, size); + return ptr; +} + +void *asan_realloc(void *p, uptr size, StackTrace *stack) { + if (p == 0) { + void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC); + ASAN_MALLOC_HOOK(ptr, size); + return ptr; + } else if (size == 0) { + ASAN_FREE_HOOK(p); + Deallocate((u8*)p, stack, FROM_MALLOC); + return 0; + } + return Reallocate((u8*)p, size, stack); +} + +void *asan_valloc(uptr size, StackTrace *stack) { + void *ptr = (void*)Allocate(GetPageSizeCached(), size, stack, FROM_MALLOC); + ASAN_MALLOC_HOOK(ptr, size); + return ptr; +} + +void *asan_pvalloc(uptr size, StackTrace *stack) { + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + void *ptr = (void*)Allocate(PageSize, size, stack, FROM_MALLOC); + ASAN_MALLOC_HOOK(ptr, size); + return ptr; +} + +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, + StackTrace *stack) { + void *ptr = Allocate(alignment, size, stack, FROM_MALLOC); + CHECK(IsAligned((uptr)ptr, alignment)); + ASAN_MALLOC_HOOK(ptr, size); + *memptr = ptr; + return 0; +} + +uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) { + CHECK(stack); + if (ptr == 0) return 0; + uptr usable_size = malloc_info.AllocationSize((uptr)ptr); + if (flags()->check_malloc_usable_size && (usable_size == 0)) { + ReportMallocUsableSizeNotOwned((uptr)ptr, stack); + } + return usable_size; +} + +uptr asan_mz_size(const void *ptr) { + return malloc_info.AllocationSize((uptr)ptr); +} + +void asan_mz_force_lock() { + malloc_info.ForceLock(); +} + +void asan_mz_force_unlock() { + malloc_info.ForceUnlock(); +} + +} // namespace __asan + +// ---------------------- Interface ---------------- {{{1 +using namespace __asan; // NOLINT + +// ASan allocator doesn't reserve extra bytes, so normally we would +// just return "size". +uptr __asan_get_estimated_allocated_size(uptr size) { + if (size == 0) return 1; + return Min(size, kMaxAllowedMallocSize); +} + +bool __asan_get_ownership(const void *p) { + return malloc_info.AllocationSize((uptr)p) > 0; +} + +uptr __asan_get_allocated_size(const void *p) { + if (p == 0) return 0; + uptr allocated_size = malloc_info.AllocationSize((uptr)p); + // Die if p is not malloced or if it is already freed. + if (allocated_size == 0) { + GET_STACK_TRACE_FATAL_HERE; + ReportAsanGetAllocatedSizeNotOwned((uptr)p, &stack); + } + return allocated_size; +} +#endif // ASAN_ALLOCATOR_VERSION |