2012-12-10 21:52:55 +08:00
|
|
|
//===-- asan_allocator2.cc ------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// 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, 2-nd version.
|
|
|
|
// This variant uses the allocator from sanitizer_common, i.e. the one shared
|
|
|
|
// with ThreadSanitizer and MemorySanitizer.
|
|
|
|
//
|
|
|
|
// Status: under development, not enabled by default yet.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "asan_allocator.h"
|
|
|
|
#if ASAN_ALLOCATOR_VERSION == 2
|
|
|
|
|
2012-12-14 20:15:09 +08:00
|
|
|
#include "asan_mapping.h"
|
2012-12-17 17:06:25 +08:00
|
|
|
#include "asan_report.h"
|
2012-12-11 22:41:31 +08:00
|
|
|
#include "asan_thread.h"
|
|
|
|
#include "asan_thread_registry.h"
|
2012-12-11 17:02:36 +08:00
|
|
|
#include "sanitizer/asan_interface.h"
|
2012-12-10 21:52:55 +08:00
|
|
|
#include "sanitizer_common/sanitizer_allocator.h"
|
2012-12-11 17:02:36 +08:00
|
|
|
#include "sanitizer_common/sanitizer_internal_defs.h"
|
2012-12-17 17:06:25 +08:00
|
|
|
#include "sanitizer_common/sanitizer_list.h"
|
2012-12-10 21:52:55 +08:00
|
|
|
|
|
|
|
namespace __asan {
|
|
|
|
|
2012-12-14 20:15:09 +08:00
|
|
|
struct AsanMapUnmapCallback {
|
|
|
|
void OnMap(uptr p, uptr size) const {
|
|
|
|
PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
|
2012-12-19 22:56:38 +08:00
|
|
|
// Statistics.
|
|
|
|
AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
|
|
|
|
thread_stats.mmaps++;
|
|
|
|
thread_stats.mmaped += size;
|
|
|
|
// thread_stats.mmaped_by_size[size_class] += n_chunks;
|
2012-12-14 20:15:09 +08:00
|
|
|
}
|
|
|
|
void OnUnmap(uptr p, uptr size) const {
|
|
|
|
PoisonShadow(p, size, 0);
|
2012-12-19 22:56:38 +08:00
|
|
|
// Statistics.
|
|
|
|
AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
|
|
|
|
thread_stats.munmaps++;
|
|
|
|
thread_stats.munmaped += size;
|
2012-12-14 20:15:09 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-12-10 21:52:55 +08:00
|
|
|
#if SANITIZER_WORDSIZE == 64
|
|
|
|
const uptr kAllocatorSpace = 0x600000000000ULL;
|
|
|
|
const uptr kAllocatorSize = 0x10000000000ULL; // 1T.
|
|
|
|
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/,
|
2012-12-14 20:15:09 +08:00
|
|
|
DefaultSizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
|
2012-12-10 21:52:55 +08:00
|
|
|
#elif SANITIZER_WORDSIZE == 32
|
|
|
|
static const u64 kAddressSpaceSize = 1ULL << 32;
|
2012-12-14 20:15:09 +08:00
|
|
|
typedef SizeClassAllocator32<0, kAddressSpaceSize, 16,
|
|
|
|
CompactSizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
|
2012-12-10 21:52:55 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
|
2012-12-14 20:15:09 +08:00
|
|
|
typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
|
2012-12-10 21:52:55 +08:00
|
|
|
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
|
|
|
|
SecondaryAllocator> Allocator;
|
|
|
|
|
2012-12-17 21:43:47 +08:00
|
|
|
// We can not use THREADLOCAL because it is not supported on some of the
|
|
|
|
// platforms we care about (OSX 10.6, Android).
|
|
|
|
// static THREADLOCAL AllocatorCache cache;
|
|
|
|
AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
|
|
|
|
CHECK(ms);
|
|
|
|
CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator2_cache));
|
|
|
|
return reinterpret_cast<AllocatorCache *>(ms->allocator2_cache);
|
|
|
|
}
|
|
|
|
|
2012-12-11 22:41:31 +08:00
|
|
|
static Allocator allocator;
|
2012-12-10 21:52:55 +08:00
|
|
|
|
2012-12-11 22:41:31 +08:00
|
|
|
static const uptr kMaxAllowedMallocSize =
|
2012-12-17 17:06:25 +08:00
|
|
|
FIRST_32_SECOND_64(3UL << 30, 8UL << 30);
|
|
|
|
|
|
|
|
static const uptr kMaxThreadLocalQuarantine =
|
2012-12-17 22:57:25 +08:00
|
|
|
FIRST_32_SECOND_64(1 << 18, 1 << 20);
|
2012-12-17 17:06:25 +08:00
|
|
|
|
|
|
|
static const uptr kReturnOnZeroMalloc = 0x0123; // Zero page is protected.
|
2012-12-11 22:41:31 +08:00
|
|
|
|
|
|
|
static int inited = 0;
|
|
|
|
|
|
|
|
static void Init() {
|
|
|
|
if (inited) return;
|
|
|
|
__asan_init();
|
|
|
|
inited = true; // this must happen before any threads are created.
|
|
|
|
allocator.Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
enum {
|
|
|
|
CHUNK_AVAILABLE = 1,
|
|
|
|
CHUNK_ALLOCATED = 2,
|
|
|
|
CHUNK_QUARANTINE = 3
|
|
|
|
};
|
|
|
|
|
|
|
|
// The memory chunk allocated from the underlying allocator looks like this:
|
|
|
|
// L L L L L L H H U U U U U U R R
|
|
|
|
// L -- left redzone words (0 or more bytes)
|
|
|
|
// H -- ChunkHeader (16 bytes on 64-bit arch, 8 bytes on 32-bit arch).
|
|
|
|
// ChunkHeader is also a part of the left redzone.
|
|
|
|
// U -- user memory.
|
|
|
|
// R -- right redzone (0 or more bytes)
|
|
|
|
// ChunkBase consists of ChunkHeader and other bytes that overlap with user
|
|
|
|
// memory.
|
|
|
|
|
|
|
|
#if SANITIZER_WORDSIZE == 64
|
|
|
|
struct ChunkBase {
|
|
|
|
// 1-st 8 bytes.
|
|
|
|
uptr chunk_state : 8; // Must be first.
|
|
|
|
uptr alloc_tid : 24;
|
|
|
|
uptr free_tid : 24;
|
|
|
|
uptr from_memalign : 1;
|
|
|
|
// 2-nd 8 bytes
|
|
|
|
uptr user_requested_size;
|
2012-12-17 14:31:53 +08:00
|
|
|
// Header2 (intersects with user memory).
|
2012-12-11 22:41:31 +08:00
|
|
|
// 3-rd 8 bytes. These overlap with the user memory.
|
|
|
|
AsanChunk *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const uptr kChunkHeaderSize = 16;
|
2012-12-17 14:31:53 +08:00
|
|
|
static const uptr kChunkHeader2Size = 8;
|
2012-12-11 22:41:31 +08:00
|
|
|
|
|
|
|
#elif SANITIZER_WORDSIZE == 32
|
|
|
|
struct ChunkBase {
|
|
|
|
// 1-st 8 bytes.
|
|
|
|
uptr chunk_state : 8; // Must be first.
|
2012-12-17 22:57:25 +08:00
|
|
|
uptr alloc_tid : 24;
|
2012-12-11 22:41:31 +08:00
|
|
|
uptr from_memalign : 1;
|
2012-12-17 22:57:25 +08:00
|
|
|
uptr free_tid : 24;
|
|
|
|
// 2-nd 8 bytes
|
2012-12-11 22:41:31 +08:00
|
|
|
uptr user_requested_size;
|
|
|
|
AsanChunk *next;
|
2012-12-17 22:57:25 +08:00
|
|
|
// Header2 empty.
|
2012-12-11 22:41:31 +08:00
|
|
|
};
|
|
|
|
|
2012-12-17 22:57:25 +08:00
|
|
|
static const uptr kChunkHeaderSize = 16;
|
|
|
|
static const uptr kChunkHeader2Size = 0;
|
2012-12-11 22:41:31 +08:00
|
|
|
#endif
|
2012-12-17 14:31:53 +08:00
|
|
|
COMPILER_CHECK(sizeof(ChunkBase) == kChunkHeaderSize + kChunkHeader2Size);
|
2012-12-11 22:41:31 +08:00
|
|
|
|
2012-12-14 21:16:19 +08:00
|
|
|
static uptr ComputeRZSize(uptr user_requested_size) {
|
|
|
|
// FIXME: implement adaptive redzones.
|
|
|
|
return flags()->redzone;
|
|
|
|
}
|
|
|
|
|
2012-12-11 22:41:31 +08:00
|
|
|
struct AsanChunk: ChunkBase {
|
|
|
|
uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
|
|
|
|
uptr UsedSize() { return user_requested_size; }
|
2012-12-14 21:16:19 +08:00
|
|
|
// We store the alloc/free stack traces in the chunk itself.
|
2012-12-17 14:31:53 +08:00
|
|
|
u32 *AllocStackBeg() {
|
|
|
|
return (u32*)(Beg() - ComputeRZSize(UsedSize()));
|
2012-12-14 21:16:19 +08:00
|
|
|
}
|
|
|
|
uptr AllocStackSize() {
|
2012-12-17 14:31:53 +08:00
|
|
|
return (ComputeRZSize(UsedSize()) - kChunkHeaderSize) / sizeof(u32);
|
|
|
|
}
|
|
|
|
u32 *FreeStackBeg() {
|
|
|
|
return (u32*)(Beg() + kChunkHeader2Size);
|
|
|
|
}
|
|
|
|
uptr FreeStackSize() {
|
|
|
|
uptr available = Max(RoundUpTo(UsedSize(), SHADOW_GRANULARITY),
|
|
|
|
ComputeRZSize(UsedSize()));
|
|
|
|
return (available - kChunkHeader2Size) / sizeof(u32);
|
2012-12-14 21:16:19 +08:00
|
|
|
}
|
2012-12-11 22:41:31 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
uptr AsanChunkView::Beg() { return chunk_->Beg(); }
|
2012-12-11 17:02:36 +08:00
|
|
|
uptr AsanChunkView::End() { return Beg() + UsedSize(); }
|
2012-12-11 22:41:31 +08:00
|
|
|
uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
|
|
|
|
uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
|
|
|
|
uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
|
|
|
|
|
|
|
|
void AsanChunkView::GetAllocStack(StackTrace *stack) {
|
2012-12-14 21:16:19 +08:00
|
|
|
StackTrace::UncompressStack(stack, chunk_->AllocStackBeg(),
|
|
|
|
chunk_->AllocStackSize());
|
2012-12-11 22:41:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void AsanChunkView::GetFreeStack(StackTrace *stack) {
|
2012-12-17 14:31:53 +08:00
|
|
|
StackTrace::UncompressStack(stack, chunk_->FreeStackBeg(),
|
|
|
|
chunk_->FreeStackSize());
|
2012-12-11 22:41:31 +08:00
|
|
|
}
|
|
|
|
|
2012-12-17 17:06:25 +08:00
|
|
|
class Quarantine: public AsanChunkFifoList {
|
|
|
|
public:
|
2012-12-17 21:43:47 +08:00
|
|
|
void SwallowThreadLocalQuarantine(AsanThreadLocalMallocStorage *ms) {
|
|
|
|
AsanChunkFifoList *q = &ms->quarantine_;
|
2012-12-17 17:06:25 +08:00
|
|
|
if (!q->size()) return;
|
|
|
|
SpinMutexLock l(&mutex_);
|
|
|
|
PushList(q);
|
2012-12-17 21:43:47 +08:00
|
|
|
PopAndDeallocateLoop(ms);
|
2012-12-17 17:06:25 +08:00
|
|
|
}
|
2012-12-20 16:53:41 +08:00
|
|
|
|
2012-12-17 17:06:25 +08:00
|
|
|
void BypassThreadLocalQuarantine(AsanChunk *m) {
|
|
|
|
SpinMutexLock l(&mutex_);
|
|
|
|
Push(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2012-12-17 21:43:47 +08:00
|
|
|
void PopAndDeallocateLoop(AsanThreadLocalMallocStorage *ms) {
|
2012-12-17 17:06:25 +08:00
|
|
|
while (size() > (uptr)flags()->quarantine_size) {
|
2012-12-17 21:43:47 +08:00
|
|
|
PopAndDeallocate(ms);
|
2012-12-17 17:06:25 +08:00
|
|
|
}
|
|
|
|
}
|
2012-12-17 21:43:47 +08:00
|
|
|
void PopAndDeallocate(AsanThreadLocalMallocStorage *ms) {
|
2012-12-17 17:06:25 +08:00
|
|
|
CHECK_GT(size(), 0);
|
|
|
|
AsanChunk *m = Pop();
|
|
|
|
CHECK(m);
|
|
|
|
CHECK(m->chunk_state == CHUNK_QUARANTINE);
|
|
|
|
m->chunk_state = CHUNK_AVAILABLE;
|
|
|
|
CHECK_NE(m->alloc_tid, kInvalidTid);
|
|
|
|
CHECK_NE(m->free_tid, kInvalidTid);
|
|
|
|
PoisonShadow(m->Beg(),
|
|
|
|
RoundUpTo(m->user_requested_size, SHADOW_GRANULARITY),
|
|
|
|
kAsanHeapLeftRedzoneMagic);
|
|
|
|
uptr alloc_beg = m->Beg() - ComputeRZSize(m->user_requested_size);
|
|
|
|
void *p = reinterpret_cast<void *>(alloc_beg);
|
|
|
|
if (m->from_memalign)
|
|
|
|
p = allocator.GetBlockBegin(p);
|
2012-12-20 16:53:41 +08:00
|
|
|
|
|
|
|
// Statistics.
|
|
|
|
AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
|
|
|
|
thread_stats.real_frees++;
|
|
|
|
thread_stats.really_freed += m->UsedSize();
|
|
|
|
|
2012-12-17 21:43:47 +08:00
|
|
|
allocator.Deallocate(GetAllocatorCache(ms), p);
|
2012-12-17 17:06:25 +08:00
|
|
|
}
|
|
|
|
SpinMutex mutex_;
|
|
|
|
};
|
|
|
|
|
|
|
|
static Quarantine quarantine;
|
|
|
|
|
|
|
|
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->UsedSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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->UsedSize();
|
|
|
|
pop_front();
|
|
|
|
return res;
|
|
|
|
}
|
2012-12-11 22:41:31 +08:00
|
|
|
|
|
|
|
static void *Allocate(uptr size, uptr alignment, StackTrace *stack) {
|
|
|
|
Init();
|
|
|
|
CHECK(stack);
|
|
|
|
if (alignment < 8) alignment = 8;
|
|
|
|
if (size == 0)
|
|
|
|
return reinterpret_cast<void *>(kReturnOnZeroMalloc);
|
|
|
|
CHECK(IsPowerOfTwo(alignment));
|
|
|
|
uptr rz_size = ComputeRZSize(size);
|
|
|
|
uptr rounded_size = RoundUpTo(size, rz_size);
|
|
|
|
uptr needed_size = rounded_size + rz_size;
|
|
|
|
if (alignment > rz_size)
|
|
|
|
needed_size += alignment;
|
|
|
|
CHECK(IsAligned(needed_size, rz_size));
|
|
|
|
if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
|
|
|
|
Report("WARNING: AddressSanitizer failed to allocate %p bytes\n",
|
|
|
|
(void*)size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AsanThread *t = asanThreadRegistry().GetCurrent();
|
2012-12-17 21:43:47 +08:00
|
|
|
// Printf("t = %p\n", t);
|
|
|
|
CHECK(t); // FIXME
|
|
|
|
void *allocated = allocator.Allocate(
|
|
|
|
GetAllocatorCache(&t->malloc_storage()), needed_size, 8, false);
|
2012-12-11 22:41:31 +08:00
|
|
|
uptr alloc_beg = reinterpret_cast<uptr>(allocated);
|
|
|
|
uptr alloc_end = alloc_beg + needed_size;
|
|
|
|
uptr beg_plus_redzone = alloc_beg + rz_size;
|
|
|
|
uptr user_beg = beg_plus_redzone;
|
|
|
|
if (!IsAligned(user_beg, alignment))
|
|
|
|
user_beg = RoundUpTo(user_beg, alignment);
|
|
|
|
uptr user_end = user_beg + size;
|
|
|
|
CHECK_LE(user_end, alloc_end);
|
|
|
|
uptr chunk_beg = user_beg - kChunkHeaderSize;
|
|
|
|
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
|
|
|
|
m->chunk_state = CHUNK_ALLOCATED;
|
|
|
|
u32 alloc_tid = t ? t->tid() : 0;
|
|
|
|
m->alloc_tid = alloc_tid;
|
|
|
|
CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield?
|
2012-12-14 21:16:19 +08:00
|
|
|
m->free_tid = kInvalidTid;
|
2012-12-11 22:41:31 +08:00
|
|
|
m->from_memalign = user_beg != beg_plus_redzone;
|
|
|
|
m->user_requested_size = size;
|
2012-12-14 21:16:19 +08:00
|
|
|
StackTrace::CompressStack(stack, m->AllocStackBeg(), m->AllocStackSize());
|
2012-12-11 22:41:31 +08:00
|
|
|
|
2012-12-14 20:15:09 +08:00
|
|
|
uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY);
|
|
|
|
// Unpoison the bulk of the memory region.
|
|
|
|
if (size_rounded_down_to_granularity)
|
|
|
|
PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
|
|
|
|
// Deal with the end of the region if size is not aligned to granularity.
|
2012-12-20 19:54:21 +08:00
|
|
|
if (size != size_rounded_down_to_granularity && flags()->poison_heap) {
|
2012-12-14 20:15:09 +08:00
|
|
|
u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity);
|
|
|
|
*shadow = size & (SHADOW_GRANULARITY - 1);
|
|
|
|
}
|
|
|
|
|
2012-12-20 16:53:41 +08:00
|
|
|
AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
|
|
|
|
thread_stats.mallocs++;
|
|
|
|
thread_stats.malloced += size;
|
|
|
|
|
2012-12-11 22:41:31 +08:00
|
|
|
void *res = reinterpret_cast<void *>(user_beg);
|
|
|
|
ASAN_MALLOC_HOOK(res, size);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Deallocate(void *ptr, StackTrace *stack) {
|
|
|
|
uptr p = reinterpret_cast<uptr>(ptr);
|
|
|
|
if (p == 0 || p == kReturnOnZeroMalloc) return;
|
|
|
|
uptr chunk_beg = p - kChunkHeaderSize;
|
|
|
|
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
|
2012-12-17 17:06:25 +08:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2012-12-17 14:31:53 +08:00
|
|
|
CHECK_GE(m->alloc_tid, 0);
|
2012-12-17 22:57:25 +08:00
|
|
|
if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area.
|
|
|
|
CHECK_EQ(m->free_tid, kInvalidTid);
|
2012-12-17 14:31:53 +08:00
|
|
|
AsanThread *t = asanThreadRegistry().GetCurrent();
|
|
|
|
m->free_tid = t ? t->tid() : 0;
|
|
|
|
StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize());
|
2012-12-17 17:06:25 +08:00
|
|
|
CHECK(m->chunk_state == CHUNK_QUARANTINE);
|
2012-12-14 20:15:09 +08:00
|
|
|
// Poison the region.
|
2012-12-17 17:06:25 +08:00
|
|
|
PoisonShadow(m->Beg(),
|
|
|
|
RoundUpTo(m->user_requested_size, SHADOW_GRANULARITY),
|
2012-12-14 20:15:09 +08:00
|
|
|
kAsanHeapFreeMagic);
|
2012-12-17 17:06:25 +08:00
|
|
|
|
2012-12-20 16:53:41 +08:00
|
|
|
AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
|
|
|
|
thread_stats.frees++;
|
|
|
|
thread_stats.freed += m->UsedSize();
|
|
|
|
|
2012-12-17 17:06:25 +08:00
|
|
|
// Push into quarantine.
|
|
|
|
if (t) {
|
|
|
|
AsanChunkFifoList &q = t->malloc_storage().quarantine_;
|
|
|
|
q.Push(m);
|
|
|
|
|
|
|
|
if (q.size() > kMaxThreadLocalQuarantine)
|
2012-12-17 21:43:47 +08:00
|
|
|
quarantine.SwallowThreadLocalQuarantine(&t->malloc_storage());
|
2012-12-17 17:06:25 +08:00
|
|
|
} else {
|
|
|
|
quarantine.BypassThreadLocalQuarantine(m);
|
|
|
|
}
|
|
|
|
|
2012-12-11 22:41:31 +08:00
|
|
|
ASAN_FREE_HOOK(ptr);
|
|
|
|
}
|
2012-12-11 17:02:36 +08:00
|
|
|
|
2012-12-17 17:06:25 +08:00
|
|
|
static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
|
|
|
|
CHECK(old_ptr && new_size);
|
|
|
|
uptr p = reinterpret_cast<uptr>(old_ptr);
|
|
|
|
uptr chunk_beg = p - kChunkHeaderSize;
|
|
|
|
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
|
|
|
|
|
|
|
|
CHECK(m->chunk_state == CHUNK_ALLOCATED);
|
|
|
|
uptr old_size = m->UsedSize();
|
|
|
|
uptr memcpy_size = Min(new_size, old_size);
|
|
|
|
void *new_ptr = Allocate(new_size, 8, stack);
|
|
|
|
if (new_ptr) {
|
|
|
|
CHECK(REAL(memcpy) != 0);
|
|
|
|
REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
|
|
|
|
Deallocate(old_ptr, stack);
|
|
|
|
}
|
|
|
|
return new_ptr;
|
|
|
|
}
|
|
|
|
|
2012-12-17 22:57:25 +08:00
|
|
|
static AsanChunk *GetAsanChunkByAddr(uptr p) {
|
|
|
|
uptr alloc_beg = reinterpret_cast<uptr>(
|
|
|
|
allocator.GetBlockBegin(reinterpret_cast<void *>(p)));
|
|
|
|
if (!alloc_beg) return 0;
|
|
|
|
// FIXME: this does not take into account memalign.
|
|
|
|
uptr chunk_beg = alloc_beg + ComputeRZSize(0) - kChunkHeaderSize;
|
|
|
|
return reinterpret_cast<AsanChunk *>(chunk_beg);
|
|
|
|
}
|
2012-12-17 17:06:25 +08:00
|
|
|
|
2012-12-17 22:57:25 +08:00
|
|
|
static uptr AllocationSize(uptr p) {
|
|
|
|
AsanChunk *m = GetAsanChunkByAddr(p);
|
|
|
|
if (!m) return 0;
|
|
|
|
if (m->chunk_state != CHUNK_ALLOCATED) return 0;
|
|
|
|
if (m->Beg() != p) return 0;
|
|
|
|
return m->UsedSize();
|
|
|
|
}
|
2012-12-17 17:06:25 +08:00
|
|
|
|
2012-12-19 16:32:50 +08:00
|
|
|
// 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.
|
|
|
|
uptr 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
AsanChunkView FindHeapChunkByAddress(uptr addr) {
|
|
|
|
AsanChunk *m1 = GetAsanChunkByAddr(addr);
|
|
|
|
if (!m1) return AsanChunkView(m1);
|
|
|
|
uptr offset = 0;
|
|
|
|
if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) {
|
|
|
|
// The address is in the chunk's left redzone, so maybe it is actually
|
|
|
|
// a right buffer overflow from the other chunk to the left.
|
|
|
|
// Search a bit to the left to see if there is another chunk.
|
|
|
|
AsanChunk *m2 = 0;
|
|
|
|
for (uptr l = 1; l < GetPageSizeCached(); l++) {
|
|
|
|
m2 = GetAsanChunkByAddr(addr - l);
|
|
|
|
if (m2 == m1) continue; // Still the same chunk.
|
|
|
|
Printf("m1 %p m2 %p l %zd\n", m1, m2, l);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset))
|
|
|
|
m1 = ChooseChunk(addr, m2, m1);
|
|
|
|
}
|
|
|
|
return AsanChunkView(m1);
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void AsanThreadLocalMallocStorage::CommitBack() {
|
2012-12-17 21:43:47 +08:00
|
|
|
quarantine.SwallowThreadLocalQuarantine(this);
|
2012-12-20 16:53:41 +08:00
|
|
|
allocator.SwallowCache(GetAllocatorCache(this));
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE
|
|
|
|
void *asan_memalign(uptr alignment, uptr size, StackTrace *stack) {
|
2012-12-11 22:41:31 +08:00
|
|
|
return Allocate(size, alignment, stack);
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE
|
|
|
|
void asan_free(void *ptr, StackTrace *stack) {
|
2012-12-11 22:41:31 +08:00
|
|
|
Deallocate(ptr, stack);
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE
|
|
|
|
void *asan_malloc(uptr size, StackTrace *stack) {
|
2012-12-11 22:41:31 +08:00
|
|
|
return Allocate(size, 8, stack);
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
|
2012-12-14 21:16:19 +08:00
|
|
|
void *ptr = Allocate(nmemb * size, 8, stack);
|
|
|
|
if (ptr)
|
|
|
|
REAL(memset)(ptr, 0, nmemb * size);
|
2012-12-17 21:43:47 +08:00
|
|
|
return ptr;
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void *asan_realloc(void *p, uptr size, StackTrace *stack) {
|
2012-12-17 14:31:53 +08:00
|
|
|
if (p == 0)
|
2012-12-14 21:16:19 +08:00
|
|
|
return Allocate(size, 8, stack);
|
|
|
|
if (size == 0) {
|
|
|
|
Deallocate(p, stack);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-12-17 17:06:25 +08:00
|
|
|
return Reallocate(p, size, stack);
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void *asan_valloc(uptr size, StackTrace *stack) {
|
2012-12-14 21:16:19 +08:00
|
|
|
return Allocate(size, GetPageSizeCached(), stack);
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void *asan_pvalloc(uptr size, StackTrace *stack) {
|
2012-12-14 21:16:19 +08:00
|
|
|
uptr PageSize = GetPageSizeCached();
|
|
|
|
size = RoundUpTo(size, PageSize);
|
|
|
|
if (size == 0) {
|
|
|
|
// pvalloc(0) should allocate one page.
|
|
|
|
size = PageSize;
|
|
|
|
}
|
|
|
|
return Allocate(size, PageSize, stack);
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
|
2012-12-14 21:16:19 +08:00
|
|
|
StackTrace *stack) {
|
|
|
|
void *ptr = Allocate(size, alignment, stack);
|
|
|
|
CHECK(IsAligned((uptr)ptr, alignment));
|
|
|
|
*memptr = ptr;
|
2012-12-11 17:02:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) {
|
2012-12-17 22:57:25 +08:00
|
|
|
CHECK(stack);
|
|
|
|
if (ptr == 0) return 0;
|
|
|
|
uptr usable_size = AllocationSize(reinterpret_cast<uptr>(ptr));
|
|
|
|
if (flags()->check_malloc_usable_size && (usable_size == 0))
|
|
|
|
ReportMallocUsableSizeNotOwned((uptr)ptr, stack);
|
|
|
|
return usable_size;
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
uptr asan_mz_size(const void *ptr) {
|
|
|
|
UNIMPLEMENTED();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void asan_mz_force_lock() {
|
|
|
|
UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void asan_mz_force_unlock() {
|
|
|
|
UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
2012-12-10 21:52:55 +08:00
|
|
|
} // namespace __asan
|
2012-12-11 17:02:36 +08:00
|
|
|
|
|
|
|
// ---------------------- Interface ---------------- {{{1
|
|
|
|
using namespace __asan; // NOLINT
|
|
|
|
|
|
|
|
// ASan allocator doesn't reserve extra bytes, so normally we would
|
2012-12-20 16:53:41 +08:00
|
|
|
// just return "size". We don't want to expose our redzone sizes, etc here.
|
2012-12-11 17:02:36 +08:00
|
|
|
uptr __asan_get_estimated_allocated_size(uptr size) {
|
2012-12-20 16:53:41 +08:00
|
|
|
return size;
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool __asan_get_ownership(const void *p) {
|
2012-12-20 16:53:41 +08:00
|
|
|
return AllocationSize(reinterpret_cast<uptr>(p)) > 0;
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
uptr __asan_get_allocated_size(const void *p) {
|
2012-12-20 16:53:41 +08:00
|
|
|
if (p == 0) return 0;
|
|
|
|
uptr allocated_size = AllocationSize(reinterpret_cast<uptr>(p));
|
|
|
|
// Die if p is not malloced or if it is already freed.
|
|
|
|
if (allocated_size == 0) {
|
|
|
|
GET_STACK_TRACE_FATAL_HERE;
|
|
|
|
ReportAsanGetAllocatedSizeNotOwned(reinterpret_cast<uptr>(p), &stack);
|
|
|
|
}
|
|
|
|
return allocated_size;
|
2012-12-11 17:02:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
|
2012-12-10 21:52:55 +08:00
|
|
|
#endif // ASAN_ALLOCATOR_VERSION
|