diff options
author | kcc <kcc@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-11-04 21:33:31 +0000 |
---|---|---|
committer | kcc <kcc@138bc75d-0d04-0410-961f-82ee72b054a4> | 2013-11-04 21:33:31 +0000 |
commit | 1e80ce4111e28463d870335befe7d99066b5971e (patch) | |
tree | 7cfc103c9b6b4ce7ca19d39f91509a1b68819a63 /libsanitizer/asan | |
parent | 482026b63e8a488d6b7f0eab53fcbfe12c3309ae (diff) | |
download | gcc-1e80ce4111e28463d870335befe7d99066b5971e.tar.gz |
libsanitizer merge from upstream r191666
This may break gcc-asan on Mac, will follow up separately.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@204368 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libsanitizer/asan')
39 files changed, 2163 insertions, 2503 deletions
diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am index f7847db3647..8764007ca45 100644 --- a/libsanitizer/asan/Makefile.am +++ b/libsanitizer/asan/Makefile.am @@ -15,32 +15,31 @@ toolexeclib_LTLIBRARIES = libasan.la nodist_toolexeclib_HEADERS = libasan_preinit.o asan_files = \ - asan_allocator.cc \ asan_allocator2.cc \ - asan_interceptors.cc \ - asan_mac.cc \ - asan_malloc_mac.cc \ - asan_new_delete.cc \ - asan_posix.cc \ - asan_rtl.cc \ - asan_stats.cc \ - asan_thread_registry.cc \ + asan_dll_thunk.cc \ asan_fake_stack.cc \ asan_globals.cc \ + asan_interceptors.cc \ asan_linux.cc \ + asan_mac.cc \ asan_malloc_linux.cc \ + asan_malloc_mac.cc \ asan_malloc_win.cc \ + asan_new_delete.cc \ asan_poisoning.cc \ + asan_posix.cc \ asan_report.cc \ + asan_rtl.cc \ asan_stack.cc \ + asan_stats.cc \ asan_thread.cc \ asan_win.cc libasan_la_SOURCES = $(asan_files) if USING_MAC_INTERPOSE -libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la +libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la else -libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la +libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la $(top_builddir)/interception/libinterception.la endif libasan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS) diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in index 1db6fa3da71..be8b879b549 100644 --- a/libsanitizer/asan/Makefile.in +++ b/libsanitizer/asan/Makefile.in @@ -81,17 +81,18 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \ LTLIBRARIES = $(toolexeclib_LTLIBRARIES) am__DEPENDENCIES_1 = @USING_MAC_INTERPOSE_FALSE@libasan_la_DEPENDENCIES = $(top_builddir)/sanitizer_common/libsanitizer_common.la \ +@USING_MAC_INTERPOSE_FALSE@ $(top_builddir)/lsan/libsanitizer_lsan.la \ @USING_MAC_INTERPOSE_FALSE@ $(top_builddir)/interception/libinterception.la \ @USING_MAC_INTERPOSE_FALSE@ $(am__DEPENDENCIES_1) @USING_MAC_INTERPOSE_TRUE@libasan_la_DEPENDENCIES = $(top_builddir)/sanitizer_common/libsanitizer_common.la \ +@USING_MAC_INTERPOSE_TRUE@ $(top_builddir)/lsan/libsanitizer_lsan.la \ @USING_MAC_INTERPOSE_TRUE@ $(am__DEPENDENCIES_1) -am__objects_1 = asan_allocator.lo asan_allocator2.lo \ - asan_interceptors.lo asan_mac.lo asan_malloc_mac.lo \ - asan_new_delete.lo asan_posix.lo asan_rtl.lo asan_stats.lo \ - asan_thread_registry.lo asan_fake_stack.lo asan_globals.lo \ - asan_linux.lo asan_malloc_linux.lo asan_malloc_win.lo \ - asan_poisoning.lo asan_report.lo asan_stack.lo asan_thread.lo \ - asan_win.lo +am__objects_1 = asan_allocator2.lo asan_dll_thunk.lo \ + asan_fake_stack.lo asan_globals.lo asan_interceptors.lo \ + asan_linux.lo asan_mac.lo asan_malloc_linux.lo \ + asan_malloc_mac.lo asan_malloc_win.lo asan_new_delete.lo \ + asan_poisoning.lo asan_posix.lo asan_report.lo asan_rtl.lo \ + asan_stack.lo asan_stats.lo asan_thread.lo asan_win.lo am_libasan_la_OBJECTS = $(am__objects_1) libasan_la_OBJECTS = $(am_libasan_la_OBJECTS) libasan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ @@ -260,32 +261,33 @@ ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config toolexeclib_LTLIBRARIES = libasan.la nodist_toolexeclib_HEADERS = libasan_preinit.o asan_files = \ - asan_allocator.cc \ asan_allocator2.cc \ - asan_interceptors.cc \ - asan_mac.cc \ - asan_malloc_mac.cc \ - asan_new_delete.cc \ - asan_posix.cc \ - asan_rtl.cc \ - asan_stats.cc \ - asan_thread_registry.cc \ + asan_dll_thunk.cc \ asan_fake_stack.cc \ asan_globals.cc \ + asan_interceptors.cc \ asan_linux.cc \ + asan_mac.cc \ asan_malloc_linux.cc \ + asan_malloc_mac.cc \ asan_malloc_win.cc \ + asan_new_delete.cc \ asan_poisoning.cc \ + asan_posix.cc \ asan_report.cc \ + asan_rtl.cc \ asan_stack.cc \ + asan_stats.cc \ asan_thread.cc \ asan_win.cc libasan_la_SOURCES = $(asan_files) @USING_MAC_INTERPOSE_FALSE@libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la \ +@USING_MAC_INTERPOSE_FALSE@ $(top_builddir)/lsan/libsanitizer_lsan.la \ @USING_MAC_INTERPOSE_FALSE@ $(top_builddir)/interception/libinterception.la \ @USING_MAC_INTERPOSE_FALSE@ $(LIBSTDCXX_RAW_CXX_LDFLAGS) @USING_MAC_INTERPOSE_TRUE@libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la \ +@USING_MAC_INTERPOSE_TRUE@ $(top_builddir)/lsan/libsanitizer_lsan.la \ @USING_MAC_INTERPOSE_TRUE@ $(LIBSTDCXX_RAW_CXX_LDFLAGS) libasan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` -lpthread -ldl @@ -402,8 +404,8 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator2.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_dll_thunk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@ @@ -420,7 +422,6 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread_registry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@ .cc.o: diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc deleted file mode 100644 index 4e97ff57530..00000000000 --- a/libsanitizer/asan/asan_allocator.cc +++ /dev/null @@ -1,811 +0,0 @@ -//===-- 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 diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h index df2f520c41a..1f83dcd6780 100644 --- a/libsanitizer/asan/asan_allocator.h +++ b/libsanitizer/asan/asan_allocator.h @@ -7,7 +7,7 @@ // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_allocator.cc. +// ASan-private header for asan_allocator2.cc. //===----------------------------------------------------------------------===// #ifndef ASAN_ALLOCATOR_H @@ -17,18 +17,6 @@ #include "asan_interceptors.h" #include "sanitizer_common/sanitizer_list.h" -// We are in the process of transitioning from the old allocator (version 1) -// to a new one (version 2). The change is quite intrusive so both allocators -// will co-exist in the source base for a while. The actual allocator is chosen -// at build time by redefining this macro. -#ifndef ASAN_ALLOCATOR_VERSION -# if (ASAN_LINUX && !ASAN_ANDROID) || ASAN_MAC || ASAN_WINDOWS -# define ASAN_ALLOCATOR_VERSION 2 -# else -# define ASAN_ALLOCATOR_VERSION 1 -# endif -#endif // ASAN_ALLOCATOR_VERSION - namespace __asan { enum AllocType { @@ -101,109 +89,17 @@ class AsanChunkFifoList: public IntrusiveList<AsanChunk> { struct AsanThreadLocalMallocStorage { explicit AsanThreadLocalMallocStorage(LinkerInitialized x) -#if ASAN_ALLOCATOR_VERSION == 1 - : quarantine_(x) -#endif { } AsanThreadLocalMallocStorage() { CHECK(REAL(memset)); REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage)); } -#if ASAN_ALLOCATOR_VERSION == 1 - AsanChunkFifoList quarantine_; - AsanChunk *free_lists_[kNumberOfSizeClasses]; -#else uptr quarantine_cache[16]; uptr allocator2_cache[96 * (512 * 8 + 16)]; // Opaque. -#endif void CommitBack(); }; -// Fake stack frame contains local variables of one function. -// This struct should fit into a stack redzone (32 bytes). -struct FakeFrame { - uptr magic; // Modified by the instrumented code. - uptr descr; // Modified by the instrumented code. - FakeFrame *next; - u64 real_stack : 48; - u64 size_minus_one : 16; -}; - -struct FakeFrameFifo { - public: - void FifoPush(FakeFrame *node); - FakeFrame *FifoPop(); - private: - FakeFrame *first_, *last_; -}; - -class FakeFrameLifo { - public: - void LifoPush(FakeFrame *node) { - node->next = top_; - top_ = node; - } - void LifoPop() { - CHECK(top_); - top_ = top_->next; - } - FakeFrame *top() { return top_; } - private: - FakeFrame *top_; -}; - -// For each thread we create a fake stack and place stack objects on this fake -// stack instead of the real stack. The fake stack is not really a stack but -// a fast malloc-like allocator so that when a function exits the fake stack -// is not poped but remains there for quite some time until gets used again. -// So, we poison the objects on the fake stack when function returns. -// It helps us find use-after-return bugs. -// We can not rely on __asan_stack_free being called on every function exit, -// so we maintain a lifo list of all current fake frames and update it on every -// call to __asan_stack_malloc. -class FakeStack { - public: - FakeStack(); - explicit FakeStack(LinkerInitialized) {} - void Init(uptr stack_size); - void StopUsingFakeStack() { alive_ = false; } - void Cleanup(); - uptr AllocateStack(uptr size, uptr real_stack); - static void OnFree(uptr ptr, uptr size, uptr real_stack); - // Return the bottom of the maped region. - uptr AddrIsInFakeStack(uptr addr); - bool StackSize() { return stack_size_; } - - private: - static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B. - static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. - static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; - static const uptr kNumberOfSizeClasses = - kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; - - bool AddrIsInSizeClass(uptr addr, uptr size_class); - - // Each size class should be large enough to hold all frames. - uptr ClassMmapSize(uptr size_class); - - uptr ClassSize(uptr size_class) { - return 1UL << (size_class + kMinStackFrameSizeLog); - } - - void DeallocateFrame(FakeFrame *fake_frame); - - uptr ComputeSizeClass(uptr alloc_size); - void AllocateOneSizeClass(uptr size_class); - - uptr stack_size_; - bool alive_; - - uptr allocated_size_classes_[kNumberOfSizeClasses]; - FakeFrameFifo size_classes_[kNumberOfSizeClasses]; - FakeFrameLifo call_stack_; -}; - void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, AllocType alloc_type); void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type); diff --git a/libsanitizer/asan/asan_allocator2.cc b/libsanitizer/asan/asan_allocator2.cc index 1ff120e555c..34aad11ed75 100644 --- a/libsanitizer/asan/asan_allocator2.cc +++ b/libsanitizer/asan/asan_allocator2.cc @@ -11,20 +11,20 @@ // 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 #include "asan_mapping.h" +#include "asan_poisoning.h" #include "asan_report.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_list.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_quarantine.h" +#include "lsan/lsan_common.h" namespace __asan { @@ -32,7 +32,7 @@ struct AsanMapUnmapCallback { void OnMap(uptr p, uptr size) const { PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.mmaps++; thread_stats.mmaped += size; } @@ -47,7 +47,7 @@ struct AsanMapUnmapCallback { uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size); FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg); // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.munmaps++; thread_stats.munmaped += size; } @@ -56,18 +56,23 @@ struct AsanMapUnmapCallback { #if SANITIZER_WORDSIZE == 64 #if defined(__powerpc64__) const uptr kAllocatorSpace = 0xa0000000000ULL; +const uptr kAllocatorSize = 0x20000000000ULL; // 2T. #else const uptr kAllocatorSpace = 0x600000000000ULL; +const uptr kAllocatorSize = 0x40000000000ULL; // 4T. #endif -const uptr kAllocatorSize = 0x10000000000ULL; // 1T. typedef DefaultSizeClassMap SizeClassMap; typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/, SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator; #elif SANITIZER_WORDSIZE == 32 static const u64 kAddressSpaceSize = 1ULL << 32; typedef CompactSizeClassMap SizeClassMap; +static const uptr kRegionSizeLog = 20; +static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog; typedef SizeClassAllocator32<0, kAddressSpaceSize, 16, - SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator; + SizeClassMap, kRegionSizeLog, + FlatByteMap<kFlatByteMapSize>, + AsanMapUnmapCallback> PrimaryAllocator; #endif typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; @@ -139,14 +144,15 @@ static uptr ComputeRZLog(uptr user_requested_size) { // ChunkBase consists of ChunkHeader and other bytes that overlap with user // memory. -// If a memory chunk is allocated by memalign and we had to increase the -// allocation size to achieve the proper alignment, then we store this magic +// If the left redzone is greater than the ChunkHeader size we store a magic // value in the first uptr word of the memory block and store the address of // ChunkBase in the next uptr. -// M B ? ? ? L L L L L L H H U U U U U U -// M -- magic value kMemalignMagic +// M B L L L L L L L L L H H U U U U U U +// | ^ +// ---------------------| +// M -- magic value kAllocBegMagic // B -- address of ChunkHeader pointing to the first 'H' -static const uptr kMemalignMagic = 0xCC6E96B9; +static const uptr kAllocBegMagic = 0xCC6E96B9; struct ChunkHeader { // 1-st 8 bytes. @@ -157,6 +163,7 @@ struct ChunkHeader { u32 from_memalign : 1; u32 alloc_type : 2; u32 rz_log : 3; + u32 lsan_tag : 2; // 2-nd 8 bytes // This field is used for small sizes. For large sizes it is equal to // SizeClassMap::kMaxSize and the actual size is stored in the @@ -167,7 +174,6 @@ struct ChunkHeader { struct ChunkBase : ChunkHeader { // Header2, intersects with user memory. - AsanChunk *next; u32 free_context_id; }; @@ -188,7 +194,8 @@ struct AsanChunk: ChunkBase { return allocator.GetBlockBegin(reinterpret_cast<void *>(this)); return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log)); } - // We store the alloc/free stack traces in the chunk itself. + // If we don't use stack depot, we store the alloc/free stack traces + // in the chunk itself. u32 *AllocStackBeg() { return (u32*)(Beg() - RZLog2Size(rz_log)); } @@ -204,6 +211,9 @@ struct AsanChunk: ChunkBase { uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY); return (available - kChunkHeader2Size) / sizeof(u32); } + bool AddrIsInside(uptr addr) { + return (addr >= Beg()) && (addr < Beg() + UsedSize()); + } }; uptr AsanChunkView::Beg() { return chunk_->Beg(); } @@ -257,22 +267,25 @@ struct QuarantineCallback { } void Recycle(AsanChunk *m) { - CHECK(m->chunk_state == CHUNK_QUARANTINE); - m->chunk_state = CHUNK_AVAILABLE; + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); CHECK_NE(m->alloc_tid, kInvalidTid); CHECK_NE(m->free_tid, kInvalidTid); PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), kAsanHeapLeftRedzoneMagic); void *p = reinterpret_cast<void *>(m->AllocBeg()); - if (m->from_memalign) { - uptr *memalign_magic = reinterpret_cast<uptr *>(p); - CHECK_EQ(memalign_magic[0], kMemalignMagic); - CHECK_EQ(memalign_magic[1], reinterpret_cast<uptr>(m)); + if (p != m) { + uptr *alloc_magic = reinterpret_cast<uptr *>(p); + CHECK_EQ(alloc_magic[0], kAllocBegMagic); + // Clear the magic value, as allocator internals may overwrite the + // contents of deallocated chunk, confusing GetAsanChunk lookup. + alloc_magic[0] = 0; + CHECK_EQ(alloc_magic[1], reinterpret_cast<uptr>(m)); } // Statistics. - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.real_frees++; thread_stats.really_freed += m->UsedSize(); @@ -296,9 +309,10 @@ void InitializeAllocator() { } static void *Allocate(uptr size, uptr alignment, StackTrace *stack, - AllocType alloc_type) { + AllocType alloc_type, bool can_fill) { if (!asan_inited) __asan_init(); + Flags &fl = *flags(); CHECK(stack); const uptr min_alignment = SHADOW_GRANULARITY; if (alignment < min_alignment) @@ -314,9 +328,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, CHECK(IsPowerOfTwo(alignment)); uptr rz_log = ComputeRZLog(size); uptr rz_size = RZLog2Size(rz_log); - uptr rounded_size = RoundUpTo(size, alignment); - if (rounded_size < kChunkHeader2Size) - rounded_size = kChunkHeader2Size; + uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); uptr needed_size = rounded_size + rz_size; if (alignment > min_alignment) needed_size += alignment; @@ -331,10 +343,10 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", (void*)size); - return 0; + return AllocatorReturnNull(); } - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); void *allocated; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); @@ -345,8 +357,6 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, allocated = allocator.Allocate(cache, needed_size, 8, false); } uptr alloc_beg = reinterpret_cast<uptr>(allocated); - // Clear the first allocated word (an old kMemalignMagic may still be there). - reinterpret_cast<uptr *>(alloc_beg)[0] = 0; uptr alloc_end = alloc_beg + needed_size; uptr beg_plus_redzone = alloc_beg + rz_size; uptr user_beg = beg_plus_redzone; @@ -356,7 +366,6 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, CHECK_LE(user_end, alloc_end); uptr chunk_beg = user_beg - kChunkHeaderSize; AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); - m->chunk_state = CHUNK_ALLOCATED; m->alloc_type = alloc_type; m->rz_log = rz_log; u32 alloc_tid = t ? t->tid() : 0; @@ -364,11 +373,10 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? m->free_tid = kInvalidTid; m->from_memalign = user_beg != beg_plus_redzone; - if (m->from_memalign) { - CHECK_LE(beg_plus_redzone + 2 * sizeof(uptr), user_beg); - uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg); - memalign_magic[0] = kMemalignMagic; - memalign_magic[1] = chunk_beg; + if (alloc_beg != chunk_beg) { + CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); + reinterpret_cast<uptr *>(alloc_beg)[0] = kAllocBegMagic; + reinterpret_cast<uptr *>(alloc_beg)[1] = chunk_beg; } if (using_primary_allocator) { CHECK(size); @@ -382,7 +390,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, meta[1] = chunk_beg; } - if (flags()->use_stack_depot) { + if (fl.use_stack_depot) { m->alloc_context_id = StackDepotPut(stack->trace, stack->size); } else { m->alloc_context_id = 0; @@ -394,12 +402,12 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, 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. - if (size != size_rounded_down_to_granularity && flags()->poison_heap) { + if (size != size_rounded_down_to_granularity && fl.poison_heap) { u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity); *shadow = size & (SHADOW_GRANULARITY - 1); } - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.mallocs++; thread_stats.malloced += size; thread_stats.malloced_redzones += needed_size - size; @@ -409,26 +417,43 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, thread_stats.malloc_large++; void *res = reinterpret_cast<void *>(user_beg); + if (can_fill && fl.max_malloc_fill_size) { + uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); + REAL(memset)(res, fl.malloc_fill_byte, fill_size); + } +#if CAN_SANITIZE_LEAKS + m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored + : __lsan::kDirectlyLeaked; +#endif + // Must be the last mutation of metadata in this function. + atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); ASAN_MALLOC_HOOK(res, size); return res; } -static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { - uptr p = reinterpret_cast<uptr>(ptr); - if (p == 0) return; - ASAN_FREE_HOOK(ptr); - uptr chunk_beg = p - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); +static void ReportInvalidFree(void *ptr, u8 chunk_state, StackTrace *stack) { + if (chunk_state == CHUNK_QUARANTINE) + ReportDoubleFree((uptr)ptr, stack); + else + ReportFreeNotMalloced((uptr)ptr, stack); +} +static void AtomicallySetQuarantineFlag(AsanChunk *m, + void *ptr, StackTrace *stack) { + u8 old_chunk_state = CHUNK_ALLOCATED; // 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_relaxed); + if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state, + CHUNK_QUARANTINE, memory_order_acquire)) + ReportInvalidFree(ptr, old_chunk_state, stack); + CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); +} + +// Expects the chunk to already be marked as quarantined by using +// AtomicallySetQuarantineFlag. +static void QuarantineChunk(AsanChunk *m, void *ptr, + StackTrace *stack, AllocType alloc_type) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); - 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); @@ -436,7 +461,7 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { CHECK_GE(m->alloc_tid, 0); if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. CHECK_EQ(m->free_tid, kInvalidTid); - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); m->free_tid = t ? t->tid() : 0; if (flags()->use_stack_depot) { m->free_context_id = StackDepotPut(stack->trace, stack->size); @@ -444,13 +469,12 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { m->free_context_id = 0; StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize()); } - CHECK(m->chunk_state == CHUNK_QUARANTINE); // Poison the region. PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), kAsanHeapFreeMagic); - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.frees++; thread_stats.freed += m->UsedSize(); @@ -468,57 +492,67 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { } } +static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { + uptr p = reinterpret_cast<uptr>(ptr); + if (p == 0) return; + + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); + ASAN_FREE_HOOK(ptr); + // Must mark the chunk as quarantined before any changes to its metadata. + AtomicallySetQuarantineFlag(m, ptr, stack); + QuarantineChunk(m, ptr, stack, alloc_type); +} + 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); - AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats(); + AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.reallocs++; thread_stats.realloced += new_size; - 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, FROM_MALLOC); + void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); if (new_ptr) { - CHECK(REAL(memcpy) != 0); + u8 chunk_state = m->chunk_state; + if (chunk_state != CHUNK_ALLOCATED) + ReportInvalidFree(old_ptr, chunk_state, stack); + CHECK_NE(REAL(memcpy), (void*)0); + uptr memcpy_size = Min(new_size, m->UsedSize()); + // If realloc() races with free(), we may start copying freed memory. + // However, we will report racy double-free later anyway. REAL(memcpy)(new_ptr, old_ptr, memcpy_size); Deallocate(old_ptr, stack, FROM_MALLOC); } return new_ptr; } -static AsanChunk *GetAsanChunkByAddr(uptr p) { - void *ptr = reinterpret_cast<void *>(p); - uptr alloc_beg = reinterpret_cast<uptr>(allocator.GetBlockBegin(ptr)); +// Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). +static AsanChunk *GetAsanChunk(void *alloc_beg) { if (!alloc_beg) return 0; - uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg); - if (memalign_magic[0] == kMemalignMagic) { - AsanChunk *m = reinterpret_cast<AsanChunk *>(memalign_magic[1]); - CHECK(m->from_memalign); - return m; - } - if (!allocator.FromPrimary(ptr)) { - uptr *meta = reinterpret_cast<uptr *>( - allocator.GetMetaData(reinterpret_cast<void *>(alloc_beg))); + if (!allocator.FromPrimary(alloc_beg)) { + uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg)); AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]); return m; } - uptr actual_size = allocator.GetActuallyAllocatedSize(ptr); - CHECK_LE(actual_size, SizeClassMap::kMaxSize); - // We know the actually allocted size, but we don't know the redzone size. - // Just try all possible redzone sizes. - for (u32 rz_log = 0; rz_log < 8; rz_log++) { - u32 rz_size = RZLog2Size(rz_log); - uptr max_possible_size = actual_size - rz_size; - if (ComputeRZLog(max_possible_size) != rz_log) - continue; - return reinterpret_cast<AsanChunk *>( - alloc_beg + rz_size - kChunkHeaderSize); - } - return 0; + uptr *alloc_magic = reinterpret_cast<uptr *>(alloc_beg); + if (alloc_magic[0] == kAllocBegMagic) + return reinterpret_cast<AsanChunk *>(alloc_magic[1]); + return reinterpret_cast<AsanChunk *>(alloc_beg); +} + +static AsanChunk *GetAsanChunkByAddr(uptr p) { + void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p)); + return GetAsanChunk(alloc_beg); +} + +// Allocator must be locked when this function is called. +static AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { + void *alloc_beg = + allocator.GetBlockBeginFastLocked(reinterpret_cast<void *>(p)); + return GetAsanChunk(alloc_beg); } static uptr AllocationSize(uptr p) { @@ -583,33 +617,33 @@ void PrintInternalAllocatorStats() { allocator.PrintStats(); } -SANITIZER_INTERFACE_ATTRIBUTE void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, AllocType alloc_type) { - return Allocate(size, alignment, stack, alloc_type); + return Allocate(size, alignment, stack, alloc_type, true); } -SANITIZER_INTERFACE_ATTRIBUTE void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { Deallocate(ptr, stack, alloc_type); } -SANITIZER_INTERFACE_ATTRIBUTE void *asan_malloc(uptr size, StackTrace *stack) { - return Allocate(size, 8, stack, FROM_MALLOC); + return Allocate(size, 8, stack, FROM_MALLOC, true); } void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; - void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC); - if (ptr) + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return AllocatorReturnNull(); + void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); + // If the memory comes from the secondary allocator no need to clear it + // as it comes directly from mmap. + if (ptr && allocator.FromPrimary(ptr)) REAL(memset)(ptr, 0, nmemb * size); return ptr; } void *asan_realloc(void *p, uptr size, StackTrace *stack) { if (p == 0) - return Allocate(size, 8, stack, FROM_MALLOC); + return Allocate(size, 8, stack, FROM_MALLOC, true); if (size == 0) { Deallocate(p, stack, FROM_MALLOC); return 0; @@ -618,7 +652,7 @@ void *asan_realloc(void *p, uptr size, StackTrace *stack) { } void *asan_valloc(uptr size, StackTrace *stack) { - return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC); + return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true); } void *asan_pvalloc(uptr size, StackTrace *stack) { @@ -628,12 +662,12 @@ void *asan_pvalloc(uptr size, StackTrace *stack) { // pvalloc(0) should allocate one page. size = PageSize; } - return Allocate(size, PageSize, stack, FROM_MALLOC); + return Allocate(size, PageSize, stack, FROM_MALLOC, true); } int asan_posix_memalign(void **memptr, uptr alignment, uptr size, StackTrace *stack) { - void *ptr = Allocate(size, alignment, stack, FROM_MALLOC); + void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true); CHECK(IsAligned((uptr)ptr, alignment)); *memptr = ptr; return 0; @@ -664,6 +698,86 @@ void asan_mz_force_unlock() { } // namespace __asan +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +void LockAllocator() { + __asan::allocator.ForceLock(); +} + +void UnlockAllocator() { + __asan::allocator.ForceUnlock(); +} + +void GetAllocatorGlobalRange(uptr *begin, uptr *end) { + *begin = (uptr)&__asan::allocator; + *end = *begin + sizeof(__asan::allocator); +} + +uptr PointsIntoChunk(void* p) { + uptr addr = reinterpret_cast<uptr>(p); + __asan::AsanChunk *m = __asan::GetAsanChunkByAddrFastLocked(addr); + if (!m) return 0; + uptr chunk = m->Beg(); + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) + return chunk; + return 0; +} + +uptr GetUserBegin(uptr chunk) { + __asan::AsanChunk *m = + __asan::GetAsanChunkByAddrFastLocked(chunk); + CHECK(m); + return m->Beg(); +} + +LsanMetadata::LsanMetadata(uptr chunk) { + metadata_ = reinterpret_cast<void *>(chunk - __asan::kChunkHeaderSize); +} + +bool LsanMetadata::allocated() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->chunk_state == __asan::CHUNK_ALLOCATED; +} + +ChunkTag LsanMetadata::tag() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return static_cast<ChunkTag>(m->lsan_tag); +} + +void LsanMetadata::set_tag(ChunkTag value) { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + m->lsan_tag = value; +} + +uptr LsanMetadata::requested_size() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->UsedSize(); +} + +u32 LsanMetadata::stack_trace_id() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->alloc_context_id; +} + +void ForEachChunk(ForEachChunkCallback callback, void *arg) { + __asan::allocator.ForEachChunk(callback, arg); +} + +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + uptr addr = reinterpret_cast<uptr>(p); + __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr); + if (!m) return kIgnoreObjectInvalid; + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { + if (m->lsan_tag == kIgnored) + return kIgnoreObjectAlreadyIgnored; + m->lsan_tag = __lsan::kIgnored; + return kIgnoreObjectSuccess; + } else { + return kIgnoreObjectInvalid; + } +} +} // namespace __lsan + // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT @@ -693,17 +807,14 @@ uptr __asan_get_allocated_size(const void *p) { #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default (no-op) implementation of malloc hooks. extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __asan_malloc_hook(void *ptr, uptr size) { (void)ptr; (void)size; } -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __asan_free_hook(void *ptr) { (void)ptr; } } // extern "C" #endif - - -#endif // ASAN_ALLOCATOR_VERSION diff --git a/libsanitizer/asan/asan_dll_thunk.cc b/libsanitizer/asan/asan_dll_thunk.cc new file mode 100644 index 00000000000..26e19441523 --- /dev/null +++ b/libsanitizer/asan/asan_dll_thunk.cc @@ -0,0 +1,194 @@ +//===-- asan_dll_thunk.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. +// +// This file defines a family of thunks that should be statically linked into +// the DLLs that have ASan instrumentation in order to delegate the calls to the +// shared runtime that lives in the main binary. +// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the +// details. +//===----------------------------------------------------------------------===// + +// Only compile this code when buidling asan_dll_thunk.lib +// Using #ifdef rather than relying on Makefiles etc. +// simplifies the build procedure. +#ifdef ASAN_DLL_THUNK + +// ----------------- Helper functions and macros --------------------- {{{1 +extern "C" { +void *__stdcall GetModuleHandleA(const char *module_name); +void *__stdcall GetProcAddress(void *module, const char *proc_name); +void abort(); +} + +static void *getRealProcAddressOrDie(const char *name) { + void *ret = GetProcAddress(GetModuleHandleA(0), name); + if (!ret) + abort(); + return ret; +} + +#define WRAP_V_V(name) \ + extern "C" void name() { \ + typedef void (*fntype)(); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(); \ + } + +#define WRAP_V_W(name) \ + extern "C" void name(void *arg) { \ + typedef void (*fntype)(void *arg); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(arg); \ + } + +#define WRAP_V_WW(name) \ + extern "C" void name(void *arg1, void *arg2) { \ + typedef void (*fntype)(void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(arg1, arg2); \ + } + +#define WRAP_V_WWW(name) \ + extern "C" void name(void *arg1, void *arg2, void *arg3) { \ + typedef void *(*fntype)(void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + fn(arg1, arg2, arg3); \ + } + +#define WRAP_W_V(name) \ + extern "C" void *name() { \ + typedef void *(*fntype)(); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(); \ + } + +#define WRAP_W_W(name) \ + extern "C" void *name(void *arg) { \ + typedef void *(*fntype)(void *arg); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg); \ + } + +#define WRAP_W_WW(name) \ + extern "C" void *name(void *arg1, void *arg2) { \ + typedef void *(*fntype)(void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2); \ + } + +#define WRAP_W_WWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3) { \ + typedef void *(*fntype)(void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3); \ + } + +#define WRAP_W_WWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \ + typedef void *(*fntype)(void *, void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4); \ + } + +#define WRAP_W_WWWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ + void *arg5) { \ + typedef void *(*fntype)(void *, void *, void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4, arg5); \ + } + +#define WRAP_W_WWWWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ + void *arg5, void *arg6) { \ + typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \ + static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4, arg5, arg6); \ + } +// }}} + +// ----------------- ASan own interface functions -------------------- +WRAP_W_V(__asan_should_detect_stack_use_after_return) + +extern "C" { + int __asan_option_detect_stack_use_after_return; + + // Manually wrap __asan_init as we need to initialize + // __asan_option_detect_stack_use_after_return afterwards. + void __asan_init_v3() { + typedef void (*fntype)(); + static fntype fn = (fntype)getRealProcAddressOrDie("__asan_init_v3"); + fn(); + __asan_option_detect_stack_use_after_return = + (__asan_should_detect_stack_use_after_return() != 0); + } +} + +WRAP_V_W(__asan_report_store1) +WRAP_V_W(__asan_report_store2) +WRAP_V_W(__asan_report_store4) +WRAP_V_W(__asan_report_store8) +WRAP_V_W(__asan_report_store16) +WRAP_V_WW(__asan_report_store_n) + +WRAP_V_W(__asan_report_load1) +WRAP_V_W(__asan_report_load2) +WRAP_V_W(__asan_report_load4) +WRAP_V_W(__asan_report_load8) +WRAP_V_W(__asan_report_load16) +WRAP_V_WW(__asan_report_load_n) + +WRAP_V_WW(__asan_register_globals) +WRAP_V_WW(__asan_unregister_globals) + +WRAP_W_WW(__asan_stack_malloc_0) +WRAP_W_WW(__asan_stack_malloc_1) +WRAP_W_WW(__asan_stack_malloc_2) +WRAP_W_WW(__asan_stack_malloc_3) +WRAP_W_WW(__asan_stack_malloc_4) +WRAP_W_WW(__asan_stack_malloc_5) +WRAP_W_WW(__asan_stack_malloc_6) +WRAP_W_WW(__asan_stack_malloc_7) +WRAP_W_WW(__asan_stack_malloc_8) +WRAP_W_WW(__asan_stack_malloc_9) +WRAP_W_WW(__asan_stack_malloc_10) + +WRAP_V_WWW(__asan_stack_free_0) +WRAP_V_WWW(__asan_stack_free_1) +WRAP_V_WWW(__asan_stack_free_2) +WRAP_V_WWW(__asan_stack_free_4) +WRAP_V_WWW(__asan_stack_free_5) +WRAP_V_WWW(__asan_stack_free_6) +WRAP_V_WWW(__asan_stack_free_7) +WRAP_V_WWW(__asan_stack_free_8) +WRAP_V_WWW(__asan_stack_free_9) +WRAP_V_WWW(__asan_stack_free_10) + +// TODO(timurrrr): Add more interface functions on the as-needed basis. + +// ----------------- Memory allocation functions --------------------- +WRAP_V_W(free) +WRAP_V_WW(_free_dbg) + +WRAP_W_W(malloc) +WRAP_W_WWWW(_malloc_dbg) + +WRAP_W_WW(calloc) +WRAP_W_WWWWW(_calloc_dbg) +WRAP_W_WWW(_calloc_impl) + +WRAP_W_WW(realloc) +WRAP_W_WWW(_realloc_dbg) +WRAP_W_WWW(_recalloc) + +WRAP_W_W(_msize) + +// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc). + +#endif // ASAN_DLL_THUNK diff --git a/libsanitizer/asan/asan_fake_stack.cc b/libsanitizer/asan/asan_fake_stack.cc index 1fc0415750b..b9cce88f34f 100644 --- a/libsanitizer/asan/asan_fake_stack.cc +++ b/libsanitizer/asan/asan_fake_stack.cc @@ -10,170 +10,195 @@ // FakeStack is used to detect use-after-return bugs. //===----------------------------------------------------------------------===// #include "asan_allocator.h" +#include "asan_poisoning.h" #include "asan_thread.h" -#include "asan_thread_registry.h" namespace __asan { -FakeStack::FakeStack() { - CHECK(REAL(memset) != 0); - REAL(memset)(this, 0, sizeof(*this)); +static const u64 kMagic1 = kAsanStackAfterReturnMagic; +static const u64 kMagic2 = (kMagic1 << 8) | kMagic1; +static const u64 kMagic4 = (kMagic2 << 16) | kMagic2; +static const u64 kMagic8 = (kMagic4 << 32) | kMagic4; + +// For small size classes inline PoisonShadow for better performance. +ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { + CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3. + u64 *shadow = reinterpret_cast<u64*>(MemToShadow(ptr)); + if (class_id <= 6) { + for (uptr i = 0; i < (1U << class_id); i++) + shadow[i] = magic; + } else { + // The size class is too big, it's cheaper to poison only size bytes. + PoisonShadow(ptr, size, static_cast<u8>(magic)); + } } -bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) { - uptr mem = allocated_size_classes_[size_class]; - uptr size = ClassMmapSize(size_class); - bool res = mem && addr >= mem && addr < mem + size; +FakeStack *FakeStack::Create(uptr stack_size_log) { + static uptr kMinStackSizeLog = 16; + static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28); + if (stack_size_log < kMinStackSizeLog) + stack_size_log = kMinStackSizeLog; + if (stack_size_log > kMaxStackSizeLog) + stack_size_log = kMaxStackSizeLog; + FakeStack *res = reinterpret_cast<FakeStack *>( + MmapOrDie(RequiredSize(stack_size_log), "FakeStack")); + res->stack_size_log_ = stack_size_log; + if (flags()->verbosity) { + u8 *p = reinterpret_cast<u8 *>(res); + Report("T%d: FakeStack created: %p -- %p stack_size_log: %zd \n", + GetCurrentTidOrInvalid(), p, + p + FakeStack::RequiredSize(stack_size_log), stack_size_log); + } return res; } -uptr FakeStack::AddrIsInFakeStack(uptr addr) { - for (uptr i = 0; i < kNumberOfSizeClasses; i++) { - if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i]; - } - return 0; +void FakeStack::Destroy() { + PoisonAll(0); + UnmapOrDie(this, RequiredSize(stack_size_log_)); } -// We may want to compute this during compilation. -inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) { - uptr rounded_size = RoundUpToPowerOfTwo(alloc_size); - uptr log = Log2(rounded_size); - CHECK(alloc_size <= (1UL << log)); - if (!(alloc_size > (1UL << (log-1)))) { - Printf("alloc_size %zu log %zu\n", alloc_size, log); - } - CHECK(alloc_size > (1UL << (log-1))); - uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog; - CHECK(res < kNumberOfSizeClasses); - CHECK(ClassSize(res) >= rounded_size); - return res; +void FakeStack::PoisonAll(u8 magic) { + PoisonShadow(reinterpret_cast<uptr>(this), RequiredSize(stack_size_log()), + magic); } -void FakeFrameFifo::FifoPush(FakeFrame *node) { - CHECK(node); - node->next = 0; - if (first_ == 0 && last_ == 0) { - first_ = last_ = node; - } else { - CHECK(first_); - CHECK(last_); - last_->next = node; - last_ = node; +ALWAYS_INLINE USED +FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id, + uptr real_stack) { + CHECK_LT(class_id, kNumberOfSizeClasses); + if (needs_gc_) + GC(real_stack); + uptr &hint_position = hint_position_[class_id]; + const int num_iter = NumberOfFrames(stack_size_log, class_id); + u8 *flags = GetFlags(stack_size_log, class_id); + for (int i = 0; i < num_iter; i++) { + uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++); + // This part is tricky. On one hand, checking and setting flags[pos] + // should be atomic to ensure async-signal safety. But on the other hand, + // if the signal arrives between checking and setting flags[pos], the + // signal handler's fake stack will start from a different hint_position + // and so will not touch this particular byte. So, it is safe to do this + // with regular non-atimic load and store (at least I was not able to make + // this code crash). + if (flags[pos]) continue; + flags[pos] = 1; + FakeFrame *res = reinterpret_cast<FakeFrame *>( + GetFrame(stack_size_log, class_id, pos)); + res->real_stack = real_stack; + *SavedFlagPtr(reinterpret_cast<uptr>(res), class_id) = &flags[pos]; + return res; } + return 0; // We are out of fake stack. } -FakeFrame *FakeFrameFifo::FifoPop() { - CHECK(first_ && last_ && "Exhausted fake stack"); - FakeFrame *res = 0; - if (first_ == last_) { - res = first_; - first_ = last_ = 0; - } else { - res = first_; - first_ = first_->next; - } - return res; +uptr FakeStack::AddrIsInFakeStack(uptr ptr) { + uptr stack_size_log = this->stack_size_log(); + uptr beg = reinterpret_cast<uptr>(GetFrame(stack_size_log, 0, 0)); + uptr end = reinterpret_cast<uptr>(this) + RequiredSize(stack_size_log); + if (ptr < beg || ptr >= end) return 0; + uptr class_id = (ptr - beg) >> stack_size_log; + uptr base = beg + (class_id << stack_size_log); + CHECK_LE(base, ptr); + CHECK_LT(ptr, base + (1UL << stack_size_log)); + uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id); + return base + pos * BytesInSizeClass(class_id); } -void FakeStack::Init(uptr stack_size) { - stack_size_ = stack_size; - alive_ = true; +void FakeStack::HandleNoReturn() { + needs_gc_ = true; } -void FakeStack::Cleanup() { - alive_ = false; - for (uptr i = 0; i < kNumberOfSizeClasses; i++) { - uptr mem = allocated_size_classes_[i]; - if (mem) { - PoisonShadow(mem, ClassMmapSize(i), 0); - allocated_size_classes_[i] = 0; - UnmapOrDie((void*)mem, ClassMmapSize(i)); +// When throw, longjmp or some such happens we don't call OnFree() and +// as the result may leak one or more fake frames, but the good news is that +// we are notified about all such events by HandleNoReturn(). +// If we recently had such no-return event we need to collect garbage frames. +// We do it based on their 'real_stack' values -- everything that is lower +// than the current real_stack is garbage. +NOINLINE void FakeStack::GC(uptr real_stack) { + uptr collected = 0; + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast<FakeFrame *>( + GetFrame(stack_size_log(), class_id, i)); + if (ff->real_stack < real_stack) { + flags[i] = 0; + collected++; + } } } + needs_gc_ = false; } -uptr FakeStack::ClassMmapSize(uptr size_class) { - return RoundUpToPowerOfTwo(stack_size_); -} +#if SANITIZER_LINUX && !SANITIZER_ANDROID +static THREADLOCAL FakeStack *fake_stack_tls; -void FakeStack::AllocateOneSizeClass(uptr size_class) { - CHECK(ClassMmapSize(size_class) >= GetPageSizeCached()); - uptr new_mem = (uptr)MmapOrDie( - ClassMmapSize(size_class), __FUNCTION__); - // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n", - // asanThreadRegistry().GetCurrent()->tid(), - // size_class, new_mem, new_mem + ClassMmapSize(size_class), - // ClassMmapSize(size_class)); - uptr i; - for (i = 0; i < ClassMmapSize(size_class); - i += ClassSize(size_class)) { - size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i)); - } - CHECK(i == ClassMmapSize(size_class)); - allocated_size_classes_[size_class] = new_mem; +FakeStack *GetTLSFakeStack() { + return fake_stack_tls; +} +void SetTLSFakeStack(FakeStack *fs) { + fake_stack_tls = fs; +} +#else +FakeStack *GetTLSFakeStack() { return 0; } +void SetTLSFakeStack(FakeStack *fs) { } +#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + +static FakeStack *GetFakeStack() { + AsanThread *t = GetCurrentThread(); + if (!t) return 0; + return t->fake_stack(); } -uptr FakeStack::AllocateStack(uptr size, uptr real_stack) { - if (!alive_) return real_stack; - CHECK(size <= kMaxStackMallocSize && size > 1); - uptr size_class = ComputeSizeClass(size); - if (!allocated_size_classes_[size_class]) { - AllocateOneSizeClass(size_class); - } - FakeFrame *fake_frame = size_classes_[size_class].FifoPop(); - CHECK(fake_frame); - fake_frame->size_minus_one = size - 1; - fake_frame->real_stack = real_stack; - while (FakeFrame *top = call_stack_.top()) { - if (top->real_stack > real_stack) break; - call_stack_.LifoPop(); - DeallocateFrame(top); - } - call_stack_.LifoPush(fake_frame); - uptr ptr = (uptr)fake_frame; - PoisonShadow(ptr, size, 0); - return ptr; +static FakeStack *GetFakeStackFast() { + if (FakeStack *fs = GetTLSFakeStack()) + return fs; + if (!__asan_option_detect_stack_use_after_return) + return 0; + return GetFakeStack(); } -void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { - CHECK(alive_); - uptr size = fake_frame->size_minus_one + 1; - uptr size_class = ComputeSizeClass(size); - CHECK(allocated_size_classes_[size_class]); - uptr ptr = (uptr)fake_frame; - CHECK(AddrIsInSizeClass(ptr, size_class)); - CHECK(AddrIsInSizeClass(ptr + size - 1, size_class)); - size_classes_[size_class].FifoPush(fake_frame); +ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) { + FakeStack *fs = GetFakeStackFast(); + if (!fs) return real_stack; + FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack); + if (!ff) + return real_stack; // Out of fake stack, return the real one. + uptr ptr = reinterpret_cast<uptr>(ff); + SetShadow(ptr, size, class_id, 0); + return ptr; } -void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) { - FakeFrame *fake_frame = (FakeFrame*)ptr; - CHECK(fake_frame->magic = kRetiredStackFrameMagic); - CHECK(fake_frame->descr != 0); - CHECK(fake_frame->size_minus_one == size - 1); - PoisonShadow(ptr, size, kAsanStackAfterReturnMagic); +ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) { + if (ptr == real_stack) + return; + FakeStack::Deallocate(ptr, class_id); + SetShadow(ptr, size, class_id, kMagic8); } } // namespace __asan // ---------------------- Interface ---------------- {{{1 -using namespace __asan; // NOLINT - -uptr __asan_stack_malloc(uptr size, uptr real_stack) { - if (!flags()->use_fake_stack) return real_stack; - AsanThread *t = asanThreadRegistry().GetCurrent(); - if (!t) { - // TSD is gone, use the real stack. - return real_stack; +#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \ + __asan_stack_malloc_##class_id(uptr size, uptr real_stack) { \ + return __asan::OnMalloc(class_id, size, real_stack); \ + } \ + extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \ + uptr ptr, uptr size, uptr real_stack) { \ + __asan::OnFree(ptr, class_id, size, real_stack); \ } - uptr ptr = t->fake_stack().AllocateStack(size, real_stack); - // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack); - return ptr; -} -void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) { - if (!flags()->use_fake_stack) return; - if (ptr != real_stack) { - FakeStack::OnFree(ptr, size, real_stack); - } -} +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9) +DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10) diff --git a/libsanitizer/asan/asan_fake_stack.h b/libsanitizer/asan/asan_fake_stack.h new file mode 100644 index 00000000000..4287497fd5d --- /dev/null +++ b/libsanitizer/asan/asan_fake_stack.h @@ -0,0 +1,167 @@ +//===-- asan_fake_stack.h ---------------------------------------*- C++ -*-===// +// +// 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. +// +// ASan-private header for asan_fake_stack.cc, implements FakeStack. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_FAKE_STACK_H +#define ASAN_FAKE_STACK_H + +#include "sanitizer_common/sanitizer_common.h" + +namespace __asan { + +// Fake stack frame contains local variables of one function. +struct FakeFrame { + uptr magic; // Modified by the instrumented code. + uptr descr; // Modified by the instrumented code. + uptr pc; // Modified by the instrumented code. + uptr real_stack; +}; + +// For each thread we create a fake stack and place stack objects on this fake +// stack instead of the real stack. The fake stack is not really a stack but +// a fast malloc-like allocator so that when a function exits the fake stack +// is not popped but remains there for quite some time until gets used again. +// So, we poison the objects on the fake stack when function returns. +// It helps us find use-after-return bugs. +// +// The FakeStack objects is allocated by a single mmap call and has no other +// pointers. The size of the fake stack depends on the actual thread stack size +// and thus can not be a constant. +// stack_size is a power of two greater or equal to the thread's stack size; +// we store it as its logarithm (stack_size_log). +// FakeStack has kNumberOfSizeClasses (11) size classes, each size class +// is a power of two, starting from 64 bytes. Each size class occupies +// stack_size bytes and thus can allocate +// NumberOfFrames=(stack_size/BytesInSizeClass) fake frames (also a power of 2). +// For each size class we have NumberOfFrames allocation flags, +// each flag indicates whether the given frame is currently allocated. +// All flags for size classes 0 .. 10 are stored in a single contiguous region +// followed by another contiguous region which contains the actual memory for +// size classes. The addresses are computed by GetFlags and GetFrame without +// any memory accesses solely based on 'this' and stack_size_log. +// Allocate() flips the appropriate allocation flag atomically, thus achieving +// async-signal safety. +// This allocator does not have quarantine per se, but it tries to allocate the +// frames in round robin fasion to maximize the delay between a deallocation +// and the next allocation. +class FakeStack { + static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B. + static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K. + + public: + static const uptr kNumberOfSizeClasses = + kMaxStackFrameSizeLog - kMinStackFrameSizeLog + 1; + + // CTOR: create the FakeStack as a single mmap-ed object. + static FakeStack *Create(uptr stack_size_log); + + void Destroy(); + + // stack_size_log is at least 15 (stack_size >= 32K). + static uptr SizeRequiredForFlags(uptr stack_size_log) { + return 1UL << (stack_size_log + 1 - kMinStackFrameSizeLog); + } + + // Each size class occupies stack_size bytes. + static uptr SizeRequiredForFrames(uptr stack_size_log) { + return (1ULL << stack_size_log) * kNumberOfSizeClasses; + } + + // Number of bytes requires for the whole object. + static uptr RequiredSize(uptr stack_size_log) { + return kFlagsOffset + SizeRequiredForFlags(stack_size_log) + + SizeRequiredForFrames(stack_size_log); + } + + // Offset of the given flag from the first flag. + // The flags for class 0 begin at offset 000000000 + // The flags for class 1 begin at offset 100000000 + // ....................2................ 110000000 + // ....................3................ 111000000 + // and so on. + static uptr FlagsOffset(uptr stack_size_log, uptr class_id) { + uptr t = kNumberOfSizeClasses - 1 - class_id; + const uptr all_ones = (1 << (kNumberOfSizeClasses - 1)) - 1; + return ((all_ones >> t) << t) << (stack_size_log - 15); + } + + static uptr NumberOfFrames(uptr stack_size_log, uptr class_id) { + return 1UL << (stack_size_log - kMinStackFrameSizeLog - class_id); + } + + // Divide n by the numbe of frames in size class. + static uptr ModuloNumberOfFrames(uptr stack_size_log, uptr class_id, uptr n) { + return n & (NumberOfFrames(stack_size_log, class_id) - 1); + } + + // The the pointer to the flags of the given class_id. + u8 *GetFlags(uptr stack_size_log, uptr class_id) { + return reinterpret_cast<u8 *>(this) + kFlagsOffset + + FlagsOffset(stack_size_log, class_id); + } + + // Get frame by class_id and pos. + u8 *GetFrame(uptr stack_size_log, uptr class_id, uptr pos) { + return reinterpret_cast<u8 *>(this) + kFlagsOffset + + SizeRequiredForFlags(stack_size_log) + + (1 << stack_size_log) * class_id + BytesInSizeClass(class_id) * pos; + } + + // Allocate the fake frame. + FakeFrame *Allocate(uptr stack_size_log, uptr class_id, uptr real_stack); + + // Deallocate the fake frame: read the saved flag address and write 0 there. + static void Deallocate(uptr x, uptr class_id) { + **SavedFlagPtr(x, class_id) = 0; + } + + // Poison the entire FakeStack's shadow with the magic value. + void PoisonAll(u8 magic); + + // Return the beginning of the FakeFrame or 0 if the address is not ours. + uptr AddrIsInFakeStack(uptr addr); + + // Number of bytes in a fake frame of this size class. + static uptr BytesInSizeClass(uptr class_id) { + return 1UL << (class_id + kMinStackFrameSizeLog); + } + + // The fake frame is guaranteed to have a right redzone. + // We use the last word of that redzone to store the address of the flag + // that corresponds to the current frame to make faster deallocation. + static u8 **SavedFlagPtr(uptr x, uptr class_id) { + return reinterpret_cast<u8 **>(x + BytesInSizeClass(class_id) - sizeof(x)); + } + + uptr stack_size_log() const { return stack_size_log_; } + + void HandleNoReturn(); + void GC(uptr real_stack); + + private: + FakeStack() { } + static const uptr kFlagsOffset = 4096; // This is were the flags begin. + // Must match the number of uses of DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID + COMPILER_CHECK(kNumberOfSizeClasses == 11); + static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog; + + uptr hint_position_[kNumberOfSizeClasses]; + uptr stack_size_log_; + // a bit is set if something was allocated from the corresponding size class. + bool needs_gc_; +}; + +FakeStack *GetTLSFakeStack(); +void SetTLSFakeStack(FakeStack *fs); + +} // namespace __asan + +#endif // ASAN_FAKE_STACK_H diff --git a/libsanitizer/asan/asan_flags.h b/libsanitizer/asan/asan_flags.h index b880896c7a3..c115997ff29 100644 --- a/libsanitizer/asan/asan_flags.h +++ b/libsanitizer/asan/asan_flags.h @@ -30,8 +30,6 @@ struct Flags { // Lower value may reduce memory usage but increase the chance of // false negatives. int quarantine_size; - // If set, uses in-process symbolizer from common sanitizer runtime. - bool symbolize; // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). int verbosity; // Size (in bytes) of redzones around heap objects. @@ -45,8 +43,6 @@ struct Flags { int report_globals; // If set, attempts to catch initialization order issues. bool check_initialization_order; - // Max number of stack frames kept for each allocation/deallocation. - int malloc_context_size; // If set, uses custom wrappers and replacements for libc string functions // to find more errors. bool replace_str; @@ -54,11 +50,13 @@ struct Flags { bool replace_intrin; // Used on Mac only. bool mac_ignore_invalid_free; - // ASan allocator flag. See asan_allocator.cc. - bool use_fake_stack; - // ASan allocator flag. Sets the maximal size of allocation request - // that would return memory filled with zero bytes. - int max_malloc_fill_size; + // Enables stack-use-after-return checking at run-time. + bool detect_stack_use_after_return; + // The minimal fake stack size log. + int uar_stack_size_log; + // ASan allocator flag. max_malloc_fill_size is the maximal amount of bytes + // that will be filled with malloc_fill_byte on malloc. + int max_malloc_fill_size, malloc_fill_byte; // Override exit status if something was reported. int exitcode; // If set, user may manually mark memory regions as poisoned or unpoisoned. @@ -69,6 +67,8 @@ struct Flags { int sleep_before_dying; // If set, registers ASan custom segv handler. bool handle_segv; + // If set, allows user register segv handler even if ASan registers one. + bool allow_user_segv_handler; // If set, uses alternate stack for signal handling. bool use_sigaltstack; // Allow the users to work around the bug in Nvidia drivers prior to 295.*. @@ -89,18 +89,10 @@ struct Flags { // Allow the tool to re-exec the program. This may interfere badly with the // debugger. bool allow_reexec; - // Strips this prefix from file paths in error reports. - const char *strip_path_prefix; // If set, prints not only thread creation stacks for threads in error report, // but also thread creation stacks for threads that created those threads, // etc. up to main thread. bool print_full_thread_history; - // ASan will write logs to "log_path.pid" instead of stderr. - const char *log_path; - // Use fast (frame-pointer-based) unwinder on fatal errors (if available). - bool fast_unwind_on_fatal; - // Use fast (frame-pointer-based) unwinder on malloc/free (if available). - bool fast_unwind_on_malloc; // Poison (or not) the heap memory on [de]allocation. Zero value is useful // for benchmarking the allocator or instrumentator. bool poison_heap; @@ -108,9 +100,18 @@ struct Flags { bool alloc_dealloc_mismatch; // Use stack depot instead of storing stacks in the redzones. bool use_stack_depot; + // If true, assume that memcmp(p1, p2, n) always reads n bytes before + // comparing p1 and p2. + bool strict_memcmp; + // If true, assume that dynamic initializers can never access globals from + // other modules, even if the latter are already initialized. + bool strict_init_order; }; -Flags *flags(); +extern Flags asan_flags_dont_use_directly; +inline Flags *flags() { + return &asan_flags_dont_use_directly; +} void InitializeFlags(Flags *f, const char *env); } // namespace __asan diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc index 7093c445588..96985af71a9 100644 --- a/libsanitizer/asan/asan_globals.cc +++ b/libsanitizer/asan/asan_globals.cc @@ -12,11 +12,14 @@ #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_mapping.h" +#include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_thread.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_mutex.h" +#include "sanitizer_common/sanitizer_placement_new.h" namespace __asan { @@ -30,15 +33,26 @@ struct ListOfGlobals { static BlockingMutex mu_for_globals(LINKER_INITIALIZED); static LowLevelAllocator allocator_for_globals; static ListOfGlobals *list_of_all_globals; -static ListOfGlobals *list_of_dynamic_init_globals; -void PoisonRedZones(const Global &g) { +static const int kDynamicInitGlobalsInitialCapacity = 512; +struct DynInitGlobal { + Global g; + bool initialized; +}; +typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals; +// Lazy-initialized and never deleted. +static VectorOfGlobals *dynamic_init_globals; + +ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { + FastPoisonShadow(g->beg, g->size_with_redzone, value); +} + +ALWAYS_INLINE void PoisonRedZones(const Global &g) { uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); - PoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, - kAsanGlobalRedzoneMagic); + FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, + kAsanGlobalRedzoneMagic); if (g.size != aligned_size) { - // partial right redzone - PoisonShadowPartialRightRedzone( + FastPoisonShadowPartialRightRedzone( g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), g.size % SHADOW_GRANULARITY, SHADOW_GRANULARITY, @@ -46,6 +60,12 @@ void PoisonRedZones(const Global &g) { } } +static void ReportGlobal(const Global &g, const char *prefix) { + Report("%s Global: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n", + prefix, (void*)g.beg, g.size, g.size_with_redzone, g.name, + g.module_name, g.has_dynamic_init); +} + bool DescribeAddressIfGlobal(uptr addr, uptr size) { if (!flags()->report_globals) return false; BlockingMutexLock lock(&mu_for_globals); @@ -53,8 +73,7 @@ bool DescribeAddressIfGlobal(uptr addr, uptr size) { for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { const Global &g = *l->g; if (flags()->report_globals >= 2) - Report("Search Global: beg=%p size=%zu name=%s\n", - (void*)g.beg, g.size, (char*)g.name); + ReportGlobal(g, "Search"); res |= DescribeAddressRelativeToGlobal(addr, size, g); } return res; @@ -66,24 +85,26 @@ bool DescribeAddressIfGlobal(uptr addr, uptr size) { static void RegisterGlobal(const Global *g) { CHECK(asan_inited); if (flags()->report_globals >= 2) - Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n", - (void*)g->beg, g->size, g->size_with_redzone, g->name, - g->has_dynamic_init); + ReportGlobal(*g, "Added"); CHECK(flags()->report_globals); CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - PoisonRedZones(*g); + if (flags()->poison_heap) + PoisonRedZones(*g); ListOfGlobals *l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); l->g = g; l->next = list_of_all_globals; list_of_all_globals = l; if (g->has_dynamic_init) { - l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); - l->g = g; - l->next = list_of_dynamic_init_globals; - list_of_dynamic_init_globals = l; + if (dynamic_init_globals == 0) { + void *mem = allocator_for_globals.Allocate(sizeof(VectorOfGlobals)); + dynamic_init_globals = new(mem) + VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); + } + DynInitGlobal dyn_global = { *g, false }; + dynamic_init_globals->push_back(dyn_global); } } @@ -93,34 +114,26 @@ static void UnregisterGlobal(const Global *g) { CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - PoisonShadow(g->beg, g->size_with_redzone, 0); + if (flags()->poison_heap) + PoisonShadowForGlobal(g, 0); // We unpoison the shadow memory for the global but we do not remove it from // the list because that would require O(n^2) time with the current list // implementation. It might not be worth doing anyway. } -// Poison all shadow memory for a single global. -static void PoisonGlobalAndRedzones(const Global *g) { - CHECK(asan_inited); - CHECK(flags()->check_initialization_order); - CHECK(AddrIsInMem(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (flags()->report_globals >= 3) - Printf("DynInitPoison : %s\n", g->name); - PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic); -} - -static void UnpoisonGlobal(const Global *g) { - CHECK(asan_inited); - CHECK(flags()->check_initialization_order); - CHECK(AddrIsInMem(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->beg)); - CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (flags()->report_globals >= 3) - Printf("DynInitUnpoison: %s\n", g->name); - PoisonShadow(g->beg, g->size_with_redzone, 0); - PoisonRedZones(*g); +void StopInitOrderChecking() { + BlockingMutexLock lock(&mu_for_globals); + if (!flags()->check_initialization_order || !dynamic_init_globals) + return; + flags()->check_initialization_order = false; + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } } } // namespace __asan @@ -151,31 +164,47 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) { // when all dynamically initialized globals are unpoisoned. This method // poisons all global variables not defined in this TU, so that a dynamic // initializer can only touch global variables in the same TU. -void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) { - if (!flags()->check_initialization_order) return; - CHECK(list_of_dynamic_init_globals); +void __asan_before_dynamic_init(const char *module_name) { + if (!flags()->check_initialization_order || + !flags()->poison_heap) + return; + bool strict_init_order = flags()->strict_init_order; + CHECK(dynamic_init_globals); + CHECK(module_name); + CHECK(asan_inited); BlockingMutexLock lock(&mu_for_globals); - bool from_current_tu = false; - // The list looks like: - // a => ... => b => last_addr => ... => first_addr => c => ... - // The globals of the current TU reside between last_addr and first_addr. - for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) { - if (l->g->beg == last_addr) - from_current_tu = true; - if (!from_current_tu) - PoisonGlobalAndRedzones(l->g); - if (l->g->beg == first_addr) - from_current_tu = false; + if (flags()->report_globals >= 3) + Printf("DynInitPoison module: %s\n", module_name); + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (dyn_g.initialized) + continue; + if (g->module_name != module_name) + PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); + else if (!strict_init_order) + dyn_g.initialized = true; } - CHECK(!from_current_tu); } // This method runs immediately after dynamic initialization in each TU, when // all dynamically initialized globals except for those defined in the current // TU are poisoned. It simply unpoisons all dynamically initialized globals. void __asan_after_dynamic_init() { - if (!flags()->check_initialization_order) return; + if (!flags()->check_initialization_order || + !flags()->poison_heap) + return; + CHECK(asan_inited); BlockingMutexLock lock(&mu_for_globals); - for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) - UnpoisonGlobal(l->g); + // FIXME: Optionally report that we're unpoisoning globals from a module. + for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { + DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; + const Global *g = &dyn_g.g; + if (!dyn_g.initialized) { + // Unpoison the whole global. + PoisonShadowForGlobal(g, 0); + // Poison redzones back. + PoisonRedZones(*g); + } + } } diff --git a/libsanitizer/asan/asan_intercepted_functions.h b/libsanitizer/asan/asan_intercepted_functions.h index ed75c428439..19b53363a5b 100644 --- a/libsanitizer/asan/asan_intercepted_functions.h +++ b/libsanitizer/asan/asan_intercepted_functions.h @@ -12,22 +12,14 @@ #ifndef ASAN_INTERCEPTED_FUNCTIONS_H #define ASAN_INTERCEPTED_FUNCTIONS_H -#include "asan_internal.h" -#include "interception/interception.h" #include "sanitizer_common/sanitizer_platform_interceptors.h" -#include <stdarg.h> -#include <stddef.h> - -using __sanitizer::uptr; - // Use macro to describe if specific function should be // intercepted on a given platform. -#if !defined(_WIN32) +#if !SANITIZER_WINDOWS # define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1 # define ASAN_INTERCEPT__LONGJMP 1 # define ASAN_INTERCEPT_STRDUP 1 -# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 1 # define ASAN_INTERCEPT_INDEX 1 # define ASAN_INTERCEPT_PTHREAD_CREATE 1 # define ASAN_INTERCEPT_MLOCKX 1 @@ -35,290 +27,51 @@ using __sanitizer::uptr; # define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0 # define ASAN_INTERCEPT__LONGJMP 0 # define ASAN_INTERCEPT_STRDUP 0 -# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 0 # define ASAN_INTERCEPT_INDEX 0 # define ASAN_INTERCEPT_PTHREAD_CREATE 0 # define ASAN_INTERCEPT_MLOCKX 0 #endif -#if defined(__linux__) +#if SANITIZER_LINUX # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1 #else # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0 #endif -#if !defined(__APPLE__) +#if !SANITIZER_MAC # define ASAN_INTERCEPT_STRNLEN 1 #else # define ASAN_INTERCEPT_STRNLEN 0 #endif -#if defined(__linux__) && !defined(ANDROID) +#if SANITIZER_LINUX && !SANITIZER_ANDROID # define ASAN_INTERCEPT_SWAPCONTEXT 1 #else # define ASAN_INTERCEPT_SWAPCONTEXT 0 #endif -#if !defined(ANDROID) && !defined(_WIN32) +#if !SANITIZER_ANDROID && !SANITIZER_WINDOWS # define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1 #else # define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 #endif -#if !defined(_WIN32) +#if !SANITIZER_WINDOWS # define ASAN_INTERCEPT_SIGLONGJMP 1 #else # define ASAN_INTERCEPT_SIGLONGJMP 0 #endif -#if ASAN_HAS_EXCEPTIONS && !defined(_WIN32) +#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS # define ASAN_INTERCEPT___CXA_THROW 1 #else # define ASAN_INTERCEPT___CXA_THROW 0 #endif -#define INTERPOSE_FUNCTION(function) \ - { reinterpret_cast<const uptr>(WRAP(function)), \ - reinterpret_cast<const uptr>(function) } - -#define INTERPOSE_FUNCTION_2(function, wrapper) \ - { reinterpret_cast<const uptr>(wrapper), \ - reinterpret_cast<const uptr>(function) } - -struct interpose_substitution { - const uptr replacement; - const uptr original; -}; - -#define INTERPOSER(func) __attribute__((used)) \ -const interpose_substitution substitution_##func[] \ - __attribute__((section("__DATA, __interpose"))) = { \ - INTERPOSE_FUNCTION(func), \ -} - -#define INTERPOSER_2(func, wrapper) __attribute__((used)) \ -const interpose_substitution substitution_##func[] \ - __attribute__((section("__DATA, __interpose"))) = { \ - INTERPOSE_FUNCTION_2(func, wrapper), \ -} - - -#define DECLARE_FUNCTION_AND_WRAPPER(ret_type, func, ...) \ - ret_type func(__VA_ARGS__); \ - ret_type WRAP(func)(__VA_ARGS__); \ - INTERPOSER(func) - -// Use extern declarations of intercepted functions on Mac and Windows -// to avoid including system headers. -#if defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL)) -extern "C" { -// signal.h -# if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION -struct sigaction; -DECLARE_FUNCTION_AND_WRAPPER(int, sigaction, int sig, - const struct sigaction *act, - struct sigaction *oldact); -DECLARE_FUNCTION_AND_WRAPPER(void*, signal, int signum, void *handler); -# endif - -// setjmp.h -DECLARE_FUNCTION_AND_WRAPPER(void, longjmp, void *env, int value); -# if ASAN_INTERCEPT__LONGJMP -DECLARE_FUNCTION_AND_WRAPPER(void, _longjmp, void *env, int value); -# endif -# if ASAN_INTERCEPT_SIGLONGJMP -DECLARE_FUNCTION_AND_WRAPPER(void, siglongjmp, void *env, int value); -# endif -# if ASAN_INTERCEPT___CXA_THROW -DECLARE_FUNCTION_AND_WRAPPER(void, __cxa_throw, void *a, void *b, void *c); -# endif - -// string.h / strings.h -DECLARE_FUNCTION_AND_WRAPPER(int, memcmp, - const void *a1, const void *a2, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(void*, memmove, - void *to, const void *from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(void*, memcpy, - void *to, const void *from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(void*, memset, void *block, int c, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(char*, strchr, const char *str, int c); -DECLARE_FUNCTION_AND_WRAPPER(char*, strcat, /* NOLINT */ - char *to, const char* from); -DECLARE_FUNCTION_AND_WRAPPER(char*, strncat, - char *to, const char* from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(char*, strcpy, /* NOLINT */ - char *to, const char* from); -DECLARE_FUNCTION_AND_WRAPPER(char*, strncpy, - char *to, const char* from, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(int, strcmp, const char *s1, const char* s2); -DECLARE_FUNCTION_AND_WRAPPER(int, strncmp, - const char *s1, const char* s2, uptr size); -DECLARE_FUNCTION_AND_WRAPPER(uptr, strlen, const char *s); -# if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP -DECLARE_FUNCTION_AND_WRAPPER(int, strcasecmp, const char *s1, const char *s2); -DECLARE_FUNCTION_AND_WRAPPER(int, strncasecmp, - const char *s1, const char *s2, uptr n); -# endif -# if ASAN_INTERCEPT_STRDUP -DECLARE_FUNCTION_AND_WRAPPER(char*, strdup, const char *s); -# endif -# if ASAN_INTERCEPT_STRNLEN -DECLARE_FUNCTION_AND_WRAPPER(uptr, strnlen, const char *s, uptr maxlen); -# endif -# if ASAN_INTERCEPT_INDEX -char* index(const char *string, int c); -INTERPOSER_2(index, WRAP(strchr)); -# endif - -// stdlib.h -DECLARE_FUNCTION_AND_WRAPPER(int, atoi, const char *nptr); -DECLARE_FUNCTION_AND_WRAPPER(long, atol, const char *nptr); // NOLINT -DECLARE_FUNCTION_AND_WRAPPER(long, strtol, const char *nptr, char **endptr, int base); // NOLINT -# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL -DECLARE_FUNCTION_AND_WRAPPER(long long, atoll, const char *nptr); // NOLINT -DECLARE_FUNCTION_AND_WRAPPER(long long, strtoll, const char *nptr, char **endptr, int base); // NOLINT -# endif - -// unistd.h -# if SANITIZER_INTERCEPT_READ -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, read, int fd, void *buf, SIZE_T count); -# endif -# if SANITIZER_INTERCEPT_PREAD -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread, int fd, void *buf, - SIZE_T count, OFF_T offset); -# endif -# if SANITIZER_INTERCEPT_PREAD64 -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread64, int fd, void *buf, - SIZE_T count, OFF64_T offset); -# endif - -# if SANITIZER_INTERCEPT_WRITE -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, write, int fd, void *ptr, SIZE_T count); -# endif -# if SANITIZER_INTERCEPT_PWRITE -DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pwrite, - int fd, void *ptr, SIZE_T count, OFF_T offset); -# endif - -# if ASAN_INTERCEPT_MLOCKX -// mlock/munlock -DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, SIZE_T len); -DECLARE_FUNCTION_AND_WRAPPER(int, munlock, const void *addr, SIZE_T len); -DECLARE_FUNCTION_AND_WRAPPER(int, mlockall, int flags); -DECLARE_FUNCTION_AND_WRAPPER(int, munlockall, void); -# endif - -// Windows threads. -# if defined(_WIN32) -__declspec(dllimport) -void* __stdcall CreateThread(void *sec, uptr st, void* start, - void *arg, DWORD fl, DWORD *id); -# endif -// Posix threads. -# if ASAN_INTERCEPT_PTHREAD_CREATE -DECLARE_FUNCTION_AND_WRAPPER(int, pthread_create, - void *thread, void *attr, - void *(*start_routine)(void*), void *arg); -# endif - -# if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS -DECLARE_FUNCTION_AND_WRAPPER(void *, localtime, unsigned long *timep); -DECLARE_FUNCTION_AND_WRAPPER(void *, localtime_r, unsigned long *timep, - void *result); -DECLARE_FUNCTION_AND_WRAPPER(void *, gmtime, unsigned long *timep); -DECLARE_FUNCTION_AND_WRAPPER(void *, gmtime_r, unsigned long *timep, - void *result); -DECLARE_FUNCTION_AND_WRAPPER(char *, ctime, unsigned long *timep); -DECLARE_FUNCTION_AND_WRAPPER(char *, ctime_r, unsigned long *timep, - char *result); -DECLARE_FUNCTION_AND_WRAPPER(char *, asctime, void *tm); -DECLARE_FUNCTION_AND_WRAPPER(char *, asctime_r, void *tm, char *result); -# endif - -// stdio.h -# if SANITIZER_INTERCEPT_SCANF -DECLARE_FUNCTION_AND_WRAPPER(int, vscanf, const char *format, va_list ap); -DECLARE_FUNCTION_AND_WRAPPER(int, vsscanf, const char *str, const char *format, - va_list ap); -DECLARE_FUNCTION_AND_WRAPPER(int, vfscanf, void *stream, const char *format, - va_list ap); -DECLARE_FUNCTION_AND_WRAPPER(int, scanf, const char *format, ...); -DECLARE_FUNCTION_AND_WRAPPER(int, fscanf, - void* stream, const char *format, ...); -DECLARE_FUNCTION_AND_WRAPPER(int, sscanf, // NOLINT - const char *str, const char *format, ...); -# endif - -# if defined(__APPLE__) -typedef void* pthread_workqueue_t; -typedef void* pthread_workitem_handle_t; - -typedef void* dispatch_group_t; -typedef void* dispatch_queue_t; -typedef void* dispatch_source_t; -typedef u64 dispatch_time_t; -typedef void (*dispatch_function_t)(void *block); -typedef void* (*worker_t)(void *block); - -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async_f, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_sync_f, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after_f, - dispatch_time_t when, dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_barrier_async_f, - dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f, - dispatch_group_t group, dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); - -# if !defined(MISSING_BLOCKS_SUPPORT) -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async, - dispatch_group_t dg, - dispatch_queue_t dq, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async, - dispatch_queue_t dq, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after, - dispatch_queue_t dq, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_event_handler, - dispatch_source_t ds, void (^work)(void)); -DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_cancel_handler, - dispatch_source_t ds, void (^work)(void)); -# endif // MISSING_BLOCKS_SUPPORT - -typedef void malloc_zone_t; -typedef size_t vm_size_t; -DECLARE_FUNCTION_AND_WRAPPER(malloc_zone_t *, malloc_create_zone, - vm_size_t start_size, unsigned flags); -DECLARE_FUNCTION_AND_WRAPPER(malloc_zone_t *, malloc_default_zone, void); -DECLARE_FUNCTION_AND_WRAPPER( - malloc_zone_t *, malloc_default_purgeable_zone, void); -DECLARE_FUNCTION_AND_WRAPPER(void, malloc_make_purgeable, void *ptr); -DECLARE_FUNCTION_AND_WRAPPER(int, malloc_make_nonpurgeable, void *ptr); -DECLARE_FUNCTION_AND_WRAPPER(void, malloc_set_zone_name, - malloc_zone_t *zone, const char *name); -DECLARE_FUNCTION_AND_WRAPPER(void *, malloc, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(void, free, void *ptr); -DECLARE_FUNCTION_AND_WRAPPER(void *, realloc, void *ptr, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(void *, calloc, size_t nmemb, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(void *, valloc, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(size_t, malloc_good_size, size_t size); -DECLARE_FUNCTION_AND_WRAPPER(int, posix_memalign, - void **memptr, size_t alignment, size_t size); -#if 0 -DECLARE_FUNCTION_AND_WRAPPER(void, _malloc_fork_prepare, void); -DECLARE_FUNCTION_AND_WRAPPER(void, _malloc_fork_parent, void); -DECLARE_FUNCTION_AND_WRAPPER(void, _malloc_fork_child, void); +#if !SANITIZER_WINDOWS +# define ASAN_INTERCEPT___CXA_ATEXIT 1 +#else +# define ASAN_INTERCEPT___CXA_ATEXIT 0 #endif - - -# endif // __APPLE__ -} // extern "C" -#endif // defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL)) - #endif // ASAN_INTERCEPTED_FUNCTIONS_H diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc index 064fc6261b0..6fa968da0a3 100644 --- a/libsanitizer/asan/asan_interceptors.cc +++ b/libsanitizer/asan/asan_interceptors.cc @@ -15,10 +15,10 @@ #include "asan_intercepted_functions.h" #include "asan_internal.h" #include "asan_mapping.h" +#include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" -#include "asan_thread_registry.h" #include "interception/interception.h" #include "sanitizer_common/sanitizer_libc.h" @@ -42,15 +42,16 @@ static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) { #define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \ uptr __offset = (uptr)(offset); \ uptr __size = (uptr)(size); \ + uptr __bad = 0; \ if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \ - __asan_region_is_poisoned(__offset, __size)) { \ + (__bad = __asan_region_is_poisoned(__offset, __size))) { \ GET_CURRENT_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, __offset, isWrite, __size); \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \ } \ } while (0) #define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false) -#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true); +#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true) // Behavior of functions like "memcpy" or "strcpy" is undefined // if memory intervals overlap. We report error in this case. @@ -86,9 +87,9 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { } void SetThreadName(const char *name) { - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); if (t) - t->summary()->set_name(name); + asanThreadRegistry().SetThreadName(t->tid(), name); } } // namespace __asan @@ -96,40 +97,76 @@ void SetThreadName(const char *name) { // ---------------------- Wrappers ---------------- {{{1 using namespace __asan; // NOLINT +DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr) +DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) + +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ ASAN_WRITE_RANGE(ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size) -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - do { \ - ctx = 0; \ - (void)ctx; \ - ENSURE_ASAN_INITED(); \ +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + do { \ + if (asan_init_is_running) return REAL(func)(__VA_ARGS__); \ + ctx = 0; \ + (void) ctx; \ + ENSURE_ASAN_INITED(); \ + } while (false) +#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ } while (false) -#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) do { } while (false) -#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #include "sanitizer_common/sanitizer_common_interceptors.inc" +#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(p, s) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + } while (false) +#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ + do { \ + } while (false) +#include "sanitizer_common/sanitizer_common_syscalls.inc" + static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { AsanThread *t = (AsanThread*)arg; - asanThreadRegistry().SetCurrent(t); - return t->ThreadStart(); + SetCurrentThread(t); + return t->ThreadStart(GetTid()); } #if ASAN_INTERCEPT_PTHREAD_CREATE +extern "C" int pthread_attr_getdetachstate(void *attr, int *v); + INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { + EnsureMainThreadIDIsCorrect(); + // Strict init-order checking in thread-hostile. + if (flags()->strict_init_order) + StopInitOrderChecking(); GET_STACK_TRACE_THREAD; - u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); - AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); - asanThreadRegistry().RegisterThread(t); + int detached = 0; + if (attr != 0) + pthread_attr_getdetachstate(attr, &detached); + + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(start_routine, arg); + CreateThreadContextArgs args = { t, &stack }; + asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args); return REAL(pthread_create)(thread, attr, asan_thread_start, t); } #endif // ASAN_INTERCEPT_PTHREAD_CREATE #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION INTERCEPTOR(void*, signal, int signum, void *handler) { - if (!AsanInterceptsSignal(signum)) { + if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) { return REAL(signal)(signum, handler); } return 0; @@ -137,15 +174,15 @@ INTERCEPTOR(void*, signal, int signum, void *handler) { INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) { - if (!AsanInterceptsSignal(signum)) { + if (!AsanInterceptsSignal(signum) || flags()->allow_user_segv_handler) { return REAL(sigaction)(signum, act, oldact); } return 0; } -#elif ASAN_POSIX +#elif SANITIZER_POSIX // We need to have defined REAL(sigaction) on posix systems. DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, - struct sigaction *oldact); + struct sigaction *oldact) #endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION #if ASAN_INTERCEPT_SWAPCONTEXT @@ -215,13 +252,15 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { // Since asan maps 16T of RAM, mlock is completely unfriendly to asan. // All functions return 0 (success). static void MlockIsUnsupported() { - static bool printed = 0; + static bool printed = false; if (printed) return; printed = true; - Printf("INFO: AddressSanitizer ignores mlock/mlockall/munlock/munlockall\n"); + if (flags()->verbosity > 0) { + Printf("INFO: AddressSanitizer ignores " + "mlock/mlockall/munlock/munlockall\n"); + } } -extern "C" { INTERCEPTOR(int, mlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; @@ -241,36 +280,56 @@ INTERCEPTOR(int, munlockall, void) { MlockIsUnsupported(); return 0; } -} // extern "C" static inline int CharCmp(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; } -static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { - int c1_low = ToLower(c1); - int c2_low = ToLower(c2); - return c1_low - c2_low; -} - INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { if (!asan_inited) return internal_memcmp(a1, a2, size); ENSURE_ASAN_INITED(); - unsigned char c1 = 0, c2 = 0; - const unsigned char *s1 = (const unsigned char*)a1; - const unsigned char *s2 = (const unsigned char*)a2; - uptr i; - for (i = 0; i < size; i++) { - c1 = s1[i]; - c2 = s2[i]; - if (c1 != c2) break; + if (flags()->replace_intrin) { + if (flags()->strict_memcmp) { + // Check the entire regions even if the first bytes of the buffers are + // different. + ASAN_READ_RANGE(a1, size); + ASAN_READ_RANGE(a2, size); + // Fallthrough to REAL(memcmp) below. + } else { + unsigned char c1 = 0, c2 = 0; + const unsigned char *s1 = (const unsigned char*)a1; + const unsigned char *s2 = (const unsigned char*)a2; + uptr i; + for (i = 0; i < size; i++) { + c1 = s1[i]; + c2 = s2[i]; + if (c1 != c2) break; + } + ASAN_READ_RANGE(s1, Min(i + 1, size)); + ASAN_READ_RANGE(s2, Min(i + 1, size)); + return CharCmp(c1, c2); + } } - ASAN_READ_RANGE(s1, Min(i + 1, size)); - ASAN_READ_RANGE(s2, Min(i + 1, size)); - return CharCmp(c1, c2); + return REAL(memcmp(a1, a2, size)); } +#define MEMMOVE_BODY { \ + if (!asan_inited) return internal_memmove(to, from, size); \ + if (asan_init_is_running) { \ + return REAL(memmove)(to, from, size); \ + } \ + ENSURE_ASAN_INITED(); \ + if (flags()->replace_intrin) { \ + ASAN_READ_RANGE(from, size); \ + ASAN_WRITE_RANGE(to, size); \ + } \ + return internal_memmove(to, from, size); \ +} + +INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) MEMMOVE_BODY + INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { +#if !SANITIZER_MAC if (!asan_inited) return internal_memcpy(to, from, size); // memcpy is called during __asan_init() from the internals // of printf(...). @@ -287,24 +346,19 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) { ASAN_READ_RANGE(from, size); ASAN_WRITE_RANGE(to, size); } - // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8. + // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8, so + // calling REAL(memcpy) here leads to infinite recursion. // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. return internal_memcpy(to, from, size); -} - -INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) { - if (!asan_inited) return internal_memmove(to, from, size); - if (asan_init_is_running) { - return REAL(memmove)(to, from, size); - } - ENSURE_ASAN_INITED(); - if (flags()->replace_intrin) { - ASAN_READ_RANGE(from, size); - ASAN_WRITE_RANGE(to, size); - } - // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8. - // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116. - return internal_memmove(to, from, size); +#else + // At least on 10.7 and 10.8 both memcpy() and memmove() are being replaced + // with WRAP(memcpy). As a result, false positives are reported for memmove() + // calls. If we just disable error reporting with + // ASAN_OPTIONS=replace_intrin=0, memmove() is still replaced with + // internal_memcpy(), which may lead to crashes, see + // http://llvm.org/bugs/show_bug.cgi?id=16362. + MEMMOVE_BODY +#endif // !SANITIZER_MAC } INTERCEPTOR(void*, memset, void *block, int c, uptr size) { @@ -341,7 +395,12 @@ INTERCEPTOR(char*, strchr, const char *str, int c) { INTERCEPTOR(char*, index, const char *string, int c) ALIAS(WRAPPER_NAME(strchr)); # else +# if SANITIZER_MAC +DECLARE_REAL(char*, index, const char *string, int c) +OVERRIDE_FUNCTION(index, strchr); +# else DEFINE_REAL(char*, index, const char *string, int c) +# endif # endif #endif // ASAN_INTERCEPT_INDEX @@ -383,26 +442,8 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { return REAL(strncat)(to, from, size); } -INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { - if (!asan_inited) return internal_strcmp(s1, s2); - if (asan_init_is_running) { - return REAL(strcmp)(s1, s2); - } - ENSURE_ASAN_INITED(); - unsigned char c1, c2; - uptr i; - for (i = 0; ; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (c1 != c2 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, i + 1); - ASAN_READ_RANGE(s2, i + 1); - return CharCmp(c1, c2); -} - INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT -#if defined(__APPLE__) +#if SANITIZER_MAC if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT #endif // strcpy is called from malloc_default_purgeable_zone() @@ -422,21 +463,16 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT #if ASAN_INTERCEPT_STRDUP INTERCEPTOR(char*, strdup, const char *s) { -#if defined(__APPLE__) - // FIXME: because internal_strdup() uses InternalAlloc(), which currently - // just calls malloc() on Mac, we can't use internal_strdup() with the - // dynamic runtime. We can remove the call to REAL(strdup) once InternalAlloc - // starts using mmap() instead. - // See also http://code.google.com/p/address-sanitizer/issues/detail?id=123. - if (!asan_inited) return REAL(strdup)(s); -#endif if (!asan_inited) return internal_strdup(s); ENSURE_ASAN_INITED(); + uptr length = REAL(strlen)(s); if (flags()->replace_str) { - uptr length = REAL(strlen)(s); ASAN_READ_RANGE(s, length + 1); } - return REAL(strdup)(s); + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(length + 1, &stack); + REAL(memcpy)(new_mem, s, length + 1); + return reinterpret_cast<char*>(new_mem); } #endif @@ -455,54 +491,13 @@ INTERCEPTOR(uptr, strlen, const char *s) { return length; } -#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP -INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { - ENSURE_ASAN_INITED(); - unsigned char c1, c2; - uptr i; - for (i = 0; ; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, i + 1); - ASAN_READ_RANGE(s2, i + 1); - return CharCaseCmp(c1, c2); -} - -INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr n) { - ENSURE_ASAN_INITED(); - unsigned char c1 = 0, c2 = 0; - uptr i; - for (i = 0; i < n; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; - } - ASAN_READ_RANGE(s1, Min(i + 1, n)); - ASAN_READ_RANGE(s2, Min(i + 1, n)); - return CharCaseCmp(c1, c2); -} -#endif // ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP - -INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { - if (!asan_inited) return internal_strncmp(s1, s2, size); - // strncmp is called from malloc_default_purgeable_zone() - // in __asan::ReplaceSystemAlloc() on Mac. - if (asan_init_is_running) { - return REAL(strncmp)(s1, s2, size); - } - ENSURE_ASAN_INITED(); - unsigned char c1 = 0, c2 = 0; - uptr i; - for (i = 0; i < size; i++) { - c1 = (unsigned char)s1[i]; - c2 = (unsigned char)s2[i]; - if (c1 != c2 || c1 == '\0') break; +INTERCEPTOR(uptr, wcslen, const wchar_t *s) { + uptr length = REAL(wcslen)(s); + if (!asan_init_is_running) { + ENSURE_ASAN_INITED(); + ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t)); } - ASAN_READ_RANGE(s1, Min(i + 1, size)); - ASAN_READ_RANGE(s2, Min(i + 1, size)); - return CharCmp(c1, c2); + return length; } INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { @@ -532,7 +527,7 @@ static inline bool IsValidStrtolBase(int base) { } static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { - CHECK(endptr != 0); + CHECK(endptr); if (nptr == *endptr) { // No digits were found at strtol call, we need to find out the last // symbol accessed by strtoll on our own. @@ -563,7 +558,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT } INTERCEPTOR(int, atoi, const char *nptr) { -#if defined(__APPLE__) +#if SANITIZER_MAC if (!asan_inited) return REAL(atoi)(nptr); #endif ENSURE_ASAN_INITED(); @@ -582,7 +577,7 @@ INTERCEPTOR(int, atoi, const char *nptr) { } INTERCEPTOR(long, atol, const char *nptr) { // NOLINT -#if defined(__APPLE__) +#if SANITIZER_MAC if (!asan_inited) return REAL(atol)(nptr); #endif ENSURE_ASAN_INITED(); @@ -631,22 +626,47 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL +static void AtCxaAtexit(void *unused) { + (void)unused; + StopInitOrderChecking(); +} + +#if ASAN_INTERCEPT___CXA_ATEXIT +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { + ENSURE_ASAN_INITED(); + int res = REAL(__cxa_atexit)(func, arg, dso_handle); + REAL(__cxa_atexit)(AtCxaAtexit, 0, 0); + return res; +} +#endif // ASAN_INTERCEPT___CXA_ATEXIT + +#if !SANITIZER_MAC #define ASAN_INTERCEPT_FUNC(name) do { \ if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \ Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ } while (0) +#else +// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. +#define ASAN_INTERCEPT_FUNC(name) +#endif // SANITIZER_MAC -#if defined(_WIN32) +#if SANITIZER_WINDOWS INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, uptr stack_size, DWORD (__stdcall *start_routine)(void*), void* arg, - DWORD flags, void* tid) { + DWORD thr_flags, void* tid) { + // Strict init-order checking in thread-hostile. + if (flags()->strict_init_order) + StopInitOrderChecking(); GET_STACK_TRACE_THREAD; - u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); - AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack); - asanThreadRegistry().RegisterThread(t); + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(start_routine, arg); + CreateThreadContextArgs args = { t, &stack }; + bool detached = false; // FIXME: how can we determine it on Windows? + asanThreadRegistry().CreateThread(*(uptr*)t, detached, current_tid, &args); return REAL(CreateThread)(security, stack_size, - asan_thread_start, t, flags, tid); + asan_thread_start, t, thr_flags, tid); } namespace __asan { @@ -663,9 +683,6 @@ void InitializeAsanInterceptors() { static bool was_called_once; CHECK(was_called_once == false); was_called_once = true; -#if defined(__APPLE__) - return; -#else SANITIZER_COMMON_INTERCEPTORS_INIT; // Intercept mem* functions. @@ -679,16 +696,11 @@ void InitializeAsanInterceptors() { // Intercept str* functions. ASAN_INTERCEPT_FUNC(strcat); // NOLINT ASAN_INTERCEPT_FUNC(strchr); - ASAN_INTERCEPT_FUNC(strcmp); ASAN_INTERCEPT_FUNC(strcpy); // NOLINT ASAN_INTERCEPT_FUNC(strlen); + ASAN_INTERCEPT_FUNC(wcslen); ASAN_INTERCEPT_FUNC(strncat); - ASAN_INTERCEPT_FUNC(strncmp); ASAN_INTERCEPT_FUNC(strncpy); -#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP - ASAN_INTERCEPT_FUNC(strcasecmp); - ASAN_INTERCEPT_FUNC(strncasecmp); -#endif #if ASAN_INTERCEPT_STRDUP ASAN_INTERCEPT_FUNC(strdup); #endif @@ -741,15 +753,19 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(pthread_create); #endif + // Intercept atexit function. +#if ASAN_INTERCEPT___CXA_ATEXIT + ASAN_INTERCEPT_FUNC(__cxa_atexit); +#endif + // Some Windows-specific interceptors. -#if defined(_WIN32) +#if SANITIZER_WINDOWS InitializeWindowsInterceptors(); #endif if (flags()->verbosity > 0) { Report("AddressSanitizer: libc interceptors initialized\n"); } -#endif // __APPLE__ } } // namespace __asan diff --git a/libsanitizer/asan/asan_interface_internal.h b/libsanitizer/asan/asan_interface_internal.h index 2fd58b856bc..7deed9f4607 100644 --- a/libsanitizer/asan/asan_interface_internal.h +++ b/libsanitizer/asan/asan_interface_internal.h @@ -23,8 +23,13 @@ extern "C" { // Everytime the asan ABI changes we also change the version number in this // name. Objects build with incompatible asan ABI version // will not link with run-time. - void __asan_init_v1() SANITIZER_INTERFACE_ATTRIBUTE; - #define __asan_init __asan_init_v1 + // Changes between ABI versions: + // v1=>v2: added 'module_name' to __asan_global + // v2=>v3: stack frame description (created by the compiler) + // contains the function PC as the 3-rd field (see + // DescribeAddressIfStack). + SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3(); + #define __asan_init __asan_init_v3 // This structure describes an instrumented global variable. struct __asan_global { @@ -32,102 +37,92 @@ extern "C" { uptr size; // The original size of the global. uptr size_with_redzone; // The size with the redzone. const char *name; // Name as a C string. + const char *module_name; // Module name as a C string. This pointer is a + // unique identifier of a module. uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. }; // These two functions should be called by the instrumented code. // 'globals' is an array of structures describing 'n' globals. - void __asan_register_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unregister_globals(__asan_global *globals, uptr n) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_register_globals(__asan_global *globals, uptr n); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unregister_globals(__asan_global *globals, uptr n); // These two functions should be called before and after dynamic initializers - // run, respectively. They should be called with parameters describing all - // dynamically initialized globals defined in the calling TU. - void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_after_dynamic_init() - SANITIZER_INTERFACE_ATTRIBUTE; - - // These two functions are used by the instrumented code in the - // use-after-return mode. __asan_stack_malloc allocates size bytes of - // fake stack and __asan_stack_free poisons it. real_stack is a pointer to - // the real stack region. - uptr __asan_stack_malloc(uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) - SANITIZER_INTERFACE_ATTRIBUTE; + // of a single module run, respectively. + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_before_dynamic_init(const char *module_name); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_after_dynamic_init(); // These two functions are used by instrumented code in the // use-after-scope mode. They mark memory for local variables as // unaddressable when they leave scope and addressable before the // function exits. - void __asan_poison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unpoison_stack_memory(uptr addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_poison_stack_memory(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unpoison_stack_memory(uptr addr, uptr size); // Performs cleanup before a NoReturn function. Must be called before things // like _exit and execl to avoid false positives on stack. - void __asan_handle_no_return() SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE void __asan_handle_no_return(); - void __asan_poison_memory_region(void const volatile *addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_unpoison_memory_region(void const volatile *addr, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_poison_memory_region(void const volatile *addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_unpoison_memory_region(void const volatile *addr, uptr size); - bool __asan_address_is_poisoned(void const volatile *addr) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + bool __asan_address_is_poisoned(void const volatile *addr); - uptr __asan_region_is_poisoned(uptr beg, uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_region_is_poisoned(uptr beg, uptr size); - void __asan_describe_address(uptr addr) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_describe_address(uptr addr); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_report_error(uptr pc, uptr bp, uptr sp, - uptr addr, bool is_write, uptr access_size) - SANITIZER_INTERFACE_ATTRIBUTE; + uptr addr, bool is_write, uptr access_size); - int __asan_set_error_exit_code(int exit_code) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_set_death_callback(void (*callback)(void)) - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_set_error_report_callback(void (*callback)(const char*)) - SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_set_error_exit_code(int exit_code); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_death_callback(void (*callback)(void)); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_set_error_report_callback(void (*callback)(const char*)); - /* OPTIONAL */ void __asan_on_error() - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ void __asan_on_error(); + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer, - int out_size) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; - - uptr __asan_get_estimated_allocated_size(uptr size) - SANITIZER_INTERFACE_ATTRIBUTE; - bool __asan_get_ownership(const void *p) - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_allocated_size(const void *p) - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_current_allocated_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_heap_size() - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_free_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; - uptr __asan_get_unmapped_bytes() - SANITIZER_INTERFACE_ATTRIBUTE; - void __asan_print_accumulated_stats() - SANITIZER_INTERFACE_ATTRIBUTE; - - /* OPTIONAL */ const char* __asan_default_options() - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; - - /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; - /* OPTIONAL */ void __asan_free_hook(void *ptr) - SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE; + int out_size); + + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_get_estimated_allocated_size(uptr size); + + SANITIZER_INTERFACE_ATTRIBUTE bool __asan_get_ownership(const void *p); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_allocated_size(const void *p); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_current_allocated_bytes(); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_heap_size(); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_free_bytes(); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_unmapped_bytes(); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats(); + + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ const char* __asan_default_options(); + + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + /* OPTIONAL */ void __asan_free_hook(void *ptr); + + // Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return + SANITIZER_INTERFACE_ATTRIBUTE + extern int __asan_option_detect_stack_use_after_return; } // extern "C" #endif // ASAN_INTERFACE_INTERNAL_H diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h index 1ccbf108647..b5b48708090 100644 --- a/libsanitizer/asan/asan_internal.h +++ b/libsanitizer/asan/asan_internal.h @@ -19,39 +19,8 @@ #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_libc.h" -#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32) -# error "This operating system is not supported by AddressSanitizer" -#endif - #define ASAN_DEFAULT_FAILURE_EXITCODE 1 -#if defined(__linux__) -# define ASAN_LINUX 1 -#else -# define ASAN_LINUX 0 -#endif - -#if defined(__APPLE__) -# define ASAN_MAC 1 -#else -# define ASAN_MAC 0 -#endif - -#if defined(_WIN32) -# define ASAN_WINDOWS 1 -#else -# define ASAN_WINDOWS 0 -#endif - -#if defined(__ANDROID__) || defined(ANDROID) -# define ASAN_ANDROID 1 -#else -# define ASAN_ANDROID 0 -#endif - - -#define ASAN_POSIX (ASAN_LINUX || ASAN_MAC) - #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) # error "The AddressSanitizer run-time should not be" " instrumented by AddressSanitizer" @@ -61,7 +30,7 @@ // If set, asan will install its own SEGV signal handler. #ifndef ASAN_NEEDS_SEGV -# if ASAN_ANDROID == 1 +# if SANITIZER_ANDROID == 1 # define ASAN_NEEDS_SEGV 0 # else # define ASAN_NEEDS_SEGV 1 @@ -90,7 +59,7 @@ #endif #ifndef ASAN_USE_PREINIT_ARRAY -# define ASAN_USE_PREINIT_ARRAY (ASAN_LINUX && !ASAN_ANDROID) +# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID) #endif // All internal functions in asan reside inside the __asan namespace @@ -121,6 +90,7 @@ void UnsetAlternateSignalStack(); void InstallSignalHandlers(); void ReadContextStack(void *context, uptr *stack, uptr *ssize); void AsanPlatformThreadInit(); +void StopInitOrderChecking(); // Wrapper for TLS/TSD. void AsanTSDInit(void (*destructor)(void *tsd)); @@ -129,24 +99,14 @@ void AsanTSDSet(void *tsd); void AppendToErrorMessageBuffer(const char *buffer); -// asan_poisoning.cc -// Poisons the shadow memory for "size" bytes starting from "addr". -void PoisonShadow(uptr addr, uptr size, u8 value); -// Poisons the shadow memory for "redzone_size" bytes starting from -// "addr + size". -void PoisonShadowPartialRightRedzone(uptr addr, - uptr size, - uptr redzone_size, - u8 value); - // Platfrom-specific options. -#ifdef __APPLE__ +#if SANITIZER_MAC bool PlatformHasDifferentMemcpyAndMemmove(); # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ (PlatformHasDifferentMemcpyAndMemmove()) #else # define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true -#endif // __APPLE__ +#endif // SANITIZER_MAC // Add convenient macro for interface functions that may be represented as // weak hooks. diff --git a/libsanitizer/asan/asan_linux.cc b/libsanitizer/asan/asan_linux.cc index a030fcd3972..10c6175092b 100644 --- a/libsanitizer/asan/asan_linux.cc +++ b/libsanitizer/asan/asan_linux.cc @@ -9,12 +9,13 @@ // // Linux-specific details. //===----------------------------------------------------------------------===// -#ifdef __linux__ + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" @@ -29,7 +30,7 @@ #include <unistd.h> #include <unwind.h> -#if !ASAN_ANDROID +#if !SANITIZER_ANDROID // FIXME: where to get ucontext on Android? #include <sys/ucontext.h> #endif @@ -48,7 +49,7 @@ void *AsanDoesNotSupportStaticLinkage() { } void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { -#if ASAN_ANDROID +#if SANITIZER_ANDROID *pc = *sp = *bp = 0; #elif defined(__arm__) ucontext_t *ucontext = (ucontext_t*)context; @@ -86,6 +87,11 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { stk_ptr = (uptr *) *sp; *bp = stk_ptr[15]; # endif +# elif defined(__mips__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[31]; + *bp = ucontext->uc_mcontext.gregs[30]; + *sp = ucontext->uc_mcontext.gregs[29]; #else # error "Unsupported arch" #endif @@ -99,25 +105,7 @@ void AsanPlatformThreadInit() { // Nothing here for now. } -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) { -#if defined(__arm__) || \ - defined(__powerpc__) || defined(__powerpc64__) || \ - defined(__sparc__) - fast = false; -#endif - if (!fast) - return stack->SlowUnwindStack(pc, max_s); - stack->size = 0; - stack->trace[0] = pc; - if (max_s > 1) { - stack->max_size = max_s; - if (!asan_inited) return; - if (AsanThread *t = asanThreadRegistry().GetCurrent()) - stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom()); - } -} - -#if !ASAN_ANDROID +#if !SANITIZER_ANDROID void ReadContextStack(void *context, uptr *stack, uptr *ssize) { ucontext_t *ucp = (ucontext_t*)context; *stack = (uptr)ucp->uc_stack.ss_sp; @@ -131,4 +119,4 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) { } // namespace __asan -#endif // __linux__ +#endif // SANITIZER_LINUX diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc index dd2657df1e2..4b28c1422cd 100644 --- a/libsanitizer/asan/asan_mac.cc +++ b/libsanitizer/asan/asan_mac.cc @@ -10,7 +10,8 @@ // Mac-specific details. //===----------------------------------------------------------------------===// -#ifdef __APPLE__ +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC #include "asan_interceptors.h" #include "asan_internal.h" @@ -18,7 +19,7 @@ #include "asan_mapping.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_thread_registry.h" +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_libc.h" #include <crt_externs.h> // for _NSGetArgv @@ -50,15 +51,17 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { # endif // SANITIZER_WORDSIZE } -int GetMacosVersion() { +MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED; + +MacosVersion GetMacosVersionInternal() { int mib[2] = { CTL_KERN, KERN_OSRELEASE }; char version[100]; uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]); for (uptr i = 0; i < maxlen; i++) version[i] = '\0'; // Get the version length. - CHECK(sysctl(mib, 2, 0, &len, 0, 0) != -1); - CHECK(len < maxlen); - CHECK(sysctl(mib, 2, version, &len, 0, 0) != -1); + CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1); + CHECK_LT(len, maxlen); + CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1); switch (version[0]) { case '9': return MACOS_VERSION_LEOPARD; case '1': { @@ -66,6 +69,7 @@ int GetMacosVersion() { case '0': return MACOS_VERSION_SNOW_LEOPARD; case '1': return MACOS_VERSION_LION; case '2': return MACOS_VERSION_MOUNTAIN_LION; + case '3': return MACOS_VERSION_MAVERICKS; default: return MACOS_VERSION_UNKNOWN; } } @@ -73,6 +77,18 @@ int GetMacosVersion() { } } +MacosVersion GetMacosVersion() { + atomic_uint32_t *cache = + reinterpret_cast<atomic_uint32_t*>(&cached_macos_version); + MacosVersion result = + static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire)); + if (result == MACOS_VERSION_UNINITIALIZED) { + result = GetMacosVersionInternal(); + atomic_store(cache, result, memory_order_release); + } + return result; +} + bool PlatformHasDifferentMemcpyAndMemmove() { // On OS X 10.7 memcpy() and memmove() are both resolved // into memmove$VARIANT$sse42. @@ -227,18 +243,6 @@ bool AsanInterceptsSignal(int signum) { void AsanPlatformThreadInit() { } -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) { - (void)fast; - stack->size = 0; - stack->trace[0] = pc; - if ((max_s) > 1) { - stack->max_size = max_s; - if (!asan_inited) return; - if (AsanThread *t = asanThreadRegistry().GetCurrent()) - stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom()); - } -} - void ReadContextStack(void *context, uptr *stack, uptr *ssize) { UNIMPLEMENTED(); } @@ -286,32 +290,16 @@ typedef struct { u32 parent_tid; } asan_block_context_t; -// We use extern declarations of libdispatch functions here instead -// of including <dispatch/dispatch.h>. This header is not present on -// Mac OS X Leopard and eariler, and although we don't expect ASan to -// work on legacy systems, it's bad to break the build of -// LLVM compiler-rt there. -extern "C" { -void dispatch_async_f(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_sync_f(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_after_f(dispatch_time_t when, dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt, - dispatch_function_t func); -void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t dq, - void *ctxt, dispatch_function_t func); -} // extern "C" - -static ALWAYS_INLINE +ALWAYS_INLINE void asan_register_worker_thread(int parent_tid, StackTrace *stack) { - AsanThread *t = asanThreadRegistry().GetCurrent(); + AsanThread *t = GetCurrentThread(); if (!t) { - t = AsanThread::Create(parent_tid, 0, 0, stack); - asanThreadRegistry().RegisterThread(t); + t = AsanThread::Create(0, 0); + CreateThreadContextArgs args = { t, stack }; + asanThreadRegistry().CreateThread(*(uptr*)t, true, parent_tid, &args); t->Init(); - asanThreadRegistry().SetCurrent(t); + asanThreadRegistry().StartThread(t->tid(), 0, 0); + SetCurrentThread(t); } } @@ -345,7 +333,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack); asan_ctxt->block = ctxt; asan_ctxt->func = func; - asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + asan_ctxt->parent_tid = GetCurrentTidOrInvalid(); return asan_ctxt; } @@ -411,7 +399,7 @@ void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); #define GET_ASAN_BLOCK(work) \ void (^asan_block)(void); \ - int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \ + int parent_tid = GetCurrentTidOrInvalid(); \ asan_block = ^(void) { \ GET_STACK_TRACE_THREAD; \ asan_register_worker_thread(parent_tid, &stack); \ @@ -449,4 +437,4 @@ INTERCEPTOR(void, dispatch_source_set_event_handler, } #endif -#endif // __APPLE__ +#endif // SANITIZER_MAC diff --git a/libsanitizer/asan/asan_mac.h b/libsanitizer/asan/asan_mac.h index 2c162fb0c39..2d1d4b0bfb3 100644 --- a/libsanitizer/asan/asan_mac.h +++ b/libsanitizer/asan/asan_mac.h @@ -34,12 +34,14 @@ typedef struct __CFRuntimeBase { #endif } CFRuntimeBase; -enum { - MACOS_VERSION_UNKNOWN = 0, +enum MacosVersion { + MACOS_VERSION_UNINITIALIZED = 0, + MACOS_VERSION_UNKNOWN, MACOS_VERSION_LEOPARD, MACOS_VERSION_SNOW_LEOPARD, MACOS_VERSION_LION, - MACOS_VERSION_MOUNTAIN_LION + MACOS_VERSION_MOUNTAIN_LION, + MACOS_VERSION_MAVERICKS }; // Used by asan_malloc_mac.cc and asan_mac.cc @@ -47,7 +49,7 @@ extern "C" void __CFInitialize(); namespace __asan { -int GetMacosVersion(); +MacosVersion GetMacosVersion(); void MaybeReplaceCFAllocator(); } // namespace __asan diff --git a/libsanitizer/asan/asan_malloc_linux.cc b/libsanitizer/asan/asan_malloc_linux.cc index 18e6a3be865..97691fcd361 100644 --- a/libsanitizer/asan/asan_malloc_linux.cc +++ b/libsanitizer/asan/asan_malloc_linux.cc @@ -11,15 +11,16 @@ // We simply define functions like malloc, free, realloc, etc. // They will replace the corresponding libc functions automagically. //===----------------------------------------------------------------------===// -#ifdef __linux__ + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX #include "asan_allocator.h" #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_stack.h" -#include "asan_thread_registry.h" -#if ASAN_ANDROID +#if SANITIZER_ANDROID DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size) DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr) DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) @@ -144,4 +145,4 @@ INTERCEPTOR(void, malloc_stats, void) { __asan_print_accumulated_stats(); } -#endif // __linux__ +#endif // SANITIZER_LINUX diff --git a/libsanitizer/asan/asan_malloc_mac.cc b/libsanitizer/asan/asan_malloc_mac.cc index 3ae6c594650..342e806e3b6 100644 --- a/libsanitizer/asan/asan_malloc_mac.cc +++ b/libsanitizer/asan/asan_malloc_mac.cc @@ -10,12 +10,14 @@ // Mac-specific malloc interception. //===----------------------------------------------------------------------===// -#ifdef __APPLE__ +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_MAC #include <AvailabilityMacros.h> #include <CoreFoundation/CFBase.h> #include <dlfcn.h> #include <malloc/malloc.h> +#include <sys/mman.h> #include "asan_allocator.h" #include "asan_interceptors.h" @@ -24,7 +26,6 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" -#include "asan_thread_registry.h" // Similar code is used in Google Perftools, // http://code.google.com/p/google-perftools. @@ -40,10 +41,19 @@ INTERCEPTOR(malloc_zone_t *, malloc_create_zone, vm_size_t start_size, unsigned zone_flags) { if (!asan_inited) __asan_init(); GET_STACK_TRACE_MALLOC; + uptr page_size = GetPageSizeCached(); + uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size); malloc_zone_t *new_zone = - (malloc_zone_t*)asan_malloc(sizeof(asan_zone), &stack); + (malloc_zone_t*)asan_memalign(page_size, allocated_size, + &stack, FROM_MALLOC); internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone)); new_zone->zone_name = NULL; // The name will be changed anyway. + if (GetMacosVersion() >= MACOS_VERSION_LION) { + // Prevent the client app from overwriting the zone contents. + // Library functions that need to modify the zone will set PROT_WRITE on it. + // This matches the behavior of malloc_create_zone() on OSX 10.7 and higher. + mprotect(new_zone, allocated_size, PROT_READ); + } return new_zone; } @@ -282,7 +292,7 @@ void mi_force_unlock(malloc_zone_t *zone) { void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { AsanMallocStats malloc_stats; - asanThreadRegistry().FillMallocStatistics(&malloc_stats); + FillMallocStatistics(&malloc_stats); CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); } @@ -344,4 +354,4 @@ void ReplaceSystemMalloc() { } } // namespace __asan -#endif // __APPLE__ +#endif // SANITIZER_MAC diff --git a/libsanitizer/asan/asan_malloc_win.cc b/libsanitizer/asan/asan_malloc_win.cc index 437079f5d1d..cabf8cd254c 100644 --- a/libsanitizer/asan/asan_malloc_win.cc +++ b/libsanitizer/asan/asan_malloc_win.cc @@ -9,7 +9,9 @@ // // Windows-specific malloc interception. //===----------------------------------------------------------------------===// -#ifdef _WIN32 + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS #include "asan_allocator.h" #include "asan_interceptors.h" @@ -28,11 +30,13 @@ using namespace __asan; // NOLINT // revisited in the future. extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE void free(void *ptr) { GET_STACK_TRACE_FREE; return asan_free(ptr, &stack, FROM_MALLOC); } +SANITIZER_INTERFACE_ATTRIBUTE void _free_dbg(void* ptr, int) { free(ptr); } @@ -41,38 +45,46 @@ void cfree(void *ptr) { CHECK(!"cfree() should not be used on Windows?"); } +SANITIZER_INTERFACE_ATTRIBUTE void *malloc(size_t size) { GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } +SANITIZER_INTERFACE_ATTRIBUTE void* _malloc_dbg(size_t size, int , const char*, int) { return malloc(size); } +SANITIZER_INTERFACE_ATTRIBUTE void *calloc(size_t nmemb, size_t size) { GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } +SANITIZER_INTERFACE_ATTRIBUTE void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { return calloc(n, size); } +SANITIZER_INTERFACE_ATTRIBUTE void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { return calloc(nmemb, size); } +SANITIZER_INTERFACE_ATTRIBUTE void *realloc(void *ptr, size_t size) { GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } +SANITIZER_INTERFACE_ATTRIBUTE void *_realloc_dbg(void *ptr, size_t size, int) { CHECK(!"_realloc_dbg should not exist!"); return 0; } +SANITIZER_INTERFACE_ATTRIBUTE void* _recalloc(void* p, size_t n, size_t elem_size) { if (!p) return calloc(n, elem_size); @@ -82,6 +94,7 @@ void* _recalloc(void* p, size_t n, size_t elem_size) { return realloc(p, size); } +SANITIZER_INTERFACE_ATTRIBUTE size_t _msize(void *ptr) { GET_STACK_TRACE_MALLOC; return asan_malloc_usable_size(ptr, &stack); diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h index 9b4dd35f1eb..fd5c2039bca 100644 --- a/libsanitizer/asan/asan_mapping.h +++ b/libsanitizer/asan/asan_mapping.h @@ -47,6 +47,20 @@ // || `[0x24000000, 0x27ffffff]` || ShadowGap || // || `[0x20000000, 0x23ffffff]` || LowShadow || // || `[0x00000000, 0x1fffffff]` || LowMem || +// +// Default Linux/MIPS mapping: +// || `[0x2aaa8000, 0xffffffff]` || HighMem || +// || `[0x0fffd000, 0x2aaa7fff]` || HighShadow || +// || `[0x0bffd000, 0x0fffcfff]` || ShadowGap || +// || `[0x0aaa8000, 0x0bffcfff]` || LowShadow || +// || `[0x00000000, 0x0aaa7fff]` || LowMem || + +static const u64 kDefaultShadowScale = 3; +static const u64 kDefaultShadowOffset32 = 1ULL << 29; +static const u64 kDefaultShadowOffset64 = 1ULL << 44; +static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G. +static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; +static const u64 kMIPS32_ShadowOffset32 = 0x0aaa8000; #if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1 extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale; @@ -54,22 +68,23 @@ extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset; # define SHADOW_SCALE (__asan_mapping_scale) # define SHADOW_OFFSET (__asan_mapping_offset) #else -# if ASAN_ANDROID -# define SHADOW_SCALE (3) +# define SHADOW_SCALE kDefaultShadowScale +# if SANITIZER_ANDROID # define SHADOW_OFFSET (0) # else -# define SHADOW_SCALE (3) # if SANITIZER_WORDSIZE == 32 -# define SHADOW_OFFSET (1 << 29) +# if defined(__mips__) +# define SHADOW_OFFSET kMIPS32_ShadowOffset32 +# else +# define SHADOW_OFFSET kDefaultShadowOffset32 +# endif # else # if defined(__powerpc64__) -# define SHADOW_OFFSET (1ULL << 41) +# define SHADOW_OFFSET kPPC64_ShadowOffset64 +# elif SANITIZER_MAC +# define SHADOW_OFFSET kDefaultShadowOffset64 # else -# if ASAN_MAC -# define SHADOW_OFFSET (1ULL << 44) -# else -# define SHADOW_OFFSET 0x7fff8000ULL -# endif +# define SHADOW_OFFSET kDefaultShort64bitShadowOffset # endif # endif # endif @@ -131,7 +146,6 @@ static uptr kHighMemEnd = 0x7fffffffffffULL; static uptr kMidMemBeg = 0x3000000000ULL; static uptr kMidMemEnd = 0x4fffffffffULL; #else -SANITIZER_INTERFACE_ATTRIBUTE extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init. #endif diff --git a/libsanitizer/asan/asan_new_delete.cc b/libsanitizer/asan/asan_new_delete.cc index fd47eee4205..beac8cdbdd5 100644 --- a/libsanitizer/asan/asan_new_delete.cc +++ b/libsanitizer/asan/asan_new_delete.cc @@ -27,7 +27,7 @@ using namespace __asan; // NOLINT // On Android new() goes through malloc interceptors. // See also https://code.google.com/p/address-sanitizer/issues/detail?id=131. -#if !ASAN_ANDROID +#if !SANITIZER_ANDROID // Fake std::nothrow_t to avoid including <new>. namespace std { @@ -38,6 +38,14 @@ struct nothrow_t {}; GET_STACK_TRACE_MALLOC;\ return asan_memalign(0, size, &stack, type); +// On OS X it's not enough to just provide our own 'operator new' and +// 'operator delete' implementations, because they're going to be in the +// runtime dylib, and the main executable will depend on both the runtime +// dylib and libstdc++, each of those'll have its implementation of new and +// delete. +// To make sure that C++ allocation/deallocation operators are overridden on +// OS X we need to intercept them using their mangled names. +#if !SANITIZER_MAC INTERCEPTOR_ATTRIBUTE void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE @@ -49,10 +57,26 @@ INTERCEPTOR_ATTRIBUTE void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY(FROM_NEW_BR); } +#else // SANITIZER_MAC +INTERCEPTOR(void *, _Znwm, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW); +} +INTERCEPTOR(void *, _Znam, size_t size) { + OPERATOR_NEW_BODY(FROM_NEW_BR); +} +INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW); +} +INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(FROM_NEW_BR); +} +#endif + #define OPERATOR_DELETE_BODY(type) \ GET_STACK_TRACE_FREE;\ asan_free(ptr, &stack, type); +#if !SANITIZER_MAC INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); } INTERCEPTOR_ATTRIBUTE @@ -64,4 +88,19 @@ INTERCEPTOR_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } +#else // SANITIZER_MAC +INTERCEPTOR(void, _ZdlPv, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW); +} +INTERCEPTOR(void, _ZdaPv, void *ptr) { + OPERATOR_DELETE_BODY(FROM_NEW_BR); +} +INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(FROM_NEW); +} +INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(FROM_NEW_BR); +} +#endif + #endif diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc index 7e930034bef..b967acded63 100644 --- a/libsanitizer/asan/asan_poisoning.cc +++ b/libsanitizer/asan/asan_poisoning.cc @@ -10,9 +10,7 @@ // Shadow memory poisoning by ASan RTL and by user application. //===----------------------------------------------------------------------===// -#include "asan_interceptors.h" -#include "asan_internal.h" -#include "asan_mapping.h" +#include "asan_poisoning.h" #include "sanitizer_common/sanitizer_libc.h" namespace __asan { @@ -20,11 +18,11 @@ namespace __asan { void PoisonShadow(uptr addr, uptr size, u8 value) { if (!flags()->poison_heap) return; CHECK(AddrIsAlignedByGranularity(addr)); + CHECK(AddrIsInMem(addr)); CHECK(AddrIsAlignedByGranularity(addr + size)); - uptr shadow_beg = MemToShadow(addr); - uptr shadow_end = MemToShadow(addr + size - SHADOW_GRANULARITY) + 1; - CHECK(REAL(memset) != 0); - REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); + CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY)); + CHECK(REAL(memset)); + FastPoisonShadow(addr, size, value); } void PoisonShadowPartialRightRedzone(uptr addr, @@ -33,20 +31,10 @@ void PoisonShadowPartialRightRedzone(uptr addr, u8 value) { if (!flags()->poison_heap) return; CHECK(AddrIsAlignedByGranularity(addr)); - u8 *shadow = (u8*)MemToShadow(addr); - for (uptr i = 0; i < redzone_size; - i += SHADOW_GRANULARITY, shadow++) { - if (i + SHADOW_GRANULARITY <= size) { - *shadow = 0; // fully addressable - } else if (i >= size) { - *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable - } else { - *shadow = size - i; // first size-i bytes are addressable - } - } + CHECK(AddrIsInMem(addr)); + FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value); } - struct ShadowSegmentEndpoint { u8 *chunk; s8 offset; // in [0, SHADOW_GRANULARITY) @@ -179,6 +167,55 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) { return 0; } +#define CHECK_SMALL_REGION(p, size, isWrite) \ + do { \ + uptr __p = reinterpret_cast<uptr>(p); \ + uptr __size = size; \ + if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \ + __asan::AddressIsPoisoned(__p + __size - 1))) { \ + GET_CURRENT_PC_BP_SP; \ + uptr __bad = __asan_region_is_poisoned(__p, __size); \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size);\ + } \ + } while (false); \ + + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u16 __sanitizer_unaligned_load16(const uu16 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u32 __sanitizer_unaligned_load32(const uu32 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +u64 __sanitizer_unaligned_load64(const uu64 *p) { + CHECK_SMALL_REGION(p, sizeof(*p), false); + return *p; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(uu16 *p, u16 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(uu32 *p, u32 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(uu64 *p, u64 x) { + CHECK_SMALL_REGION(p, sizeof(*p), true); + *p = x; +} + // This is a simplified version of __asan_(un)poison_memory_region, which // assumes that left border of region to be poisoned is properly aligned. static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { diff --git a/libsanitizer/asan/asan_poisoning.h b/libsanitizer/asan/asan_poisoning.h new file mode 100644 index 00000000000..866c0a57c7e --- /dev/null +++ b/libsanitizer/asan/asan_poisoning.h @@ -0,0 +1,57 @@ +//===-- asan_poisoning.h ----------------------------------------*- C++ -*-===// +// +// 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. +// +// Shadow memory poisoning by ASan RTL and by user application. +//===----------------------------------------------------------------------===// + +#include "asan_interceptors.h" +#include "asan_internal.h" +#include "asan_mapping.h" + +namespace __asan { + +// Poisons the shadow memory for "size" bytes starting from "addr". +void PoisonShadow(uptr addr, uptr size, u8 value); + +// Poisons the shadow memory for "redzone_size" bytes starting from +// "addr + size". +void PoisonShadowPartialRightRedzone(uptr addr, + uptr size, + uptr redzone_size, + u8 value); + +// Fast versions of PoisonShadow and PoisonShadowPartialRightRedzone that +// assume that memory addresses are properly aligned. Use in +// performance-critical code with care. +ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, + u8 value) { + DCHECK(flags()->poison_heap); + uptr shadow_beg = MEM_TO_SHADOW(aligned_beg); + uptr shadow_end = MEM_TO_SHADOW( + aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1; + REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); +} + +ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( + uptr aligned_addr, uptr size, uptr redzone_size, u8 value) { + DCHECK(flags()->poison_heap); + u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr); + for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) { + if (i + SHADOW_GRANULARITY <= size) { + *shadow = 0; // fully addressable + } else if (i >= size) { + *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable + } else { + // first size-i bytes are addressable + *shadow = static_cast<u8>(size - i); + } + } +} + +} // namespace __asan diff --git a/libsanitizer/asan/asan_posix.cc b/libsanitizer/asan/asan_posix.cc index 177b84ae67f..a210a810036 100644 --- a/libsanitizer/asan/asan_posix.cc +++ b/libsanitizer/asan/asan_posix.cc @@ -9,14 +9,15 @@ // // Posix-specific details. //===----------------------------------------------------------------------===// -#if defined(__linux__) || defined(__APPLE__) + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_LINUX || SANITIZER_MAC #include "asan_internal.h" #include "asan_interceptors.h" #include "asan_mapping.h" #include "asan_report.h" #include "asan_stack.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" @@ -40,7 +41,7 @@ static void MaybeInstallSigaction(int signum, sigact.sa_sigaction = handler; sigact.sa_flags = SA_SIGINFO; if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; - CHECK(0 == REAL(sigaction)(signum, &sigact, 0)); + CHECK_EQ(0, REAL(sigaction)(signum, &sigact, 0)); if (flags()->verbosity >= 1) { Report("Installed the sigaction for signal %d\n", signum); } @@ -57,7 +58,7 @@ static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) { void SetAlternateSignalStack() { stack_t altstack, oldstack; - CHECK(0 == sigaltstack(0, &oldstack)); + CHECK_EQ(0, sigaltstack(0, &oldstack)); // If the alternate stack is already in place, do nothing. if ((oldstack.ss_flags & SS_DISABLE) == 0) return; // TODO(glider): the mapped stack should have the MAP_STACK flag in the @@ -67,10 +68,10 @@ void SetAlternateSignalStack() { altstack.ss_sp = base; altstack.ss_flags = 0; altstack.ss_size = kAltStackSize; - CHECK(0 == sigaltstack(&altstack, 0)); + CHECK_EQ(0, sigaltstack(&altstack, 0)); if (flags()->verbosity > 0) { Report("Alternative stack for T%d set: [%p,%p)\n", - asanThreadRegistry().GetCurrentTidOrInvalid(), + GetCurrentTidOrInvalid(), altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); } } @@ -80,7 +81,7 @@ void UnsetAlternateSignalStack() { altstack.ss_sp = 0; altstack.ss_flags = SS_DISABLE; altstack.ss_size = 0; - CHECK(0 == sigaltstack(&altstack, &oldstack)); + CHECK_EQ(0, sigaltstack(&altstack, &oldstack)); UnmapOrDie(oldstack.ss_sp, oldstack.ss_size); } @@ -100,7 +101,7 @@ static bool tsd_key_inited = false; void AsanTSDInit(void (*destructor)(void *tsd)) { CHECK(!tsd_key_inited); tsd_key_inited = true; - CHECK(0 == pthread_key_create(&tsd_key, destructor)); + CHECK_EQ(0, pthread_key_create(&tsd_key, destructor)); } void *AsanTSDGet() { @@ -115,4 +116,4 @@ void AsanTSDSet(void *tsd) { } // namespace __asan -#endif // __linux__ || __APPLE_ +#endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/libsanitizer/asan/asan_preinit.cc b/libsanitizer/asan/asan_preinit.cc index 40309fa389d..31042401536 100644 --- a/libsanitizer/asan/asan_preinit.cc +++ b/libsanitizer/asan/asan_preinit.cc @@ -16,9 +16,11 @@ // On Linux, we force __asan_init to be called before anyone else // by placing it into .preinit_array section. // FIXME: do we have anything like this on Mac? + // The symbol is called __local_asan_preinit, because it's not intended to be + // exported. __attribute__((section(".preinit_array"), used)) - void (*__asan_preinit)(void) =__asan_init; -#elif defined(_WIN32) && defined(_DLL) + void (*__local_asan_preinit)(void) = __asan_init; +#elif SANITIZER_WINDOWS && defined(_DLL) // On Windows, when using dynamic CRT (/MD), we can put a pointer // to __asan_init into the global list of C initializers. // See crt0dat.c in the CRT sources for the details. diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc index 13e94c421b5..8f11ff4eac3 100644 --- a/libsanitizer/asan/asan_report.cc +++ b/libsanitizer/asan/asan_report.cc @@ -15,8 +15,8 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_symbolizer.h" @@ -42,15 +42,6 @@ void AppendToErrorMessageBuffer(const char *buffer) { } // ---------------------- Decorator ------------------------------ {{{1 -bool PrintsToTtyCached() { - static int cached = 0; - static bool prints_to_tty; - if (!cached) { // Ok wrt threads since we are printing only from one thread. - prints_to_tty = PrintsToTty(); - cached = 1; - } - return prints_to_tty; -} class Decorator: private __sanitizer::AnsiColorDecorator { public: Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } @@ -111,7 +102,7 @@ static void PrintShadowBytes(const char *before, u8 *bytes, for (uptr i = 0; i < n; i++) { u8 *p = bytes + i; const char *before = p == guilty ? "[" : - p - 1 == guilty ? "" : " "; + (p - 1 == guilty && i != 0) ? "" : " "; const char *after = p == guilty ? "]" : ""; PrintShadowByte(before, *p, after); } @@ -123,12 +114,12 @@ static void PrintLegend() { "application bytes):\n", (int)SHADOW_GRANULARITY); PrintShadowByte(" Addressable: ", 0); Printf(" Partially addressable: "); - for (uptr i = 1; i < SHADOW_GRANULARITY; i++) + for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte("", i, " "); Printf("\n"); PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic); - PrintShadowByte(" Heap righ redzone: ", kAsanHeapRightRedzoneMagic); - PrintShadowByte(" Freed Heap region: ", kAsanHeapFreeMagic); + PrintShadowByte(" Heap right redzone: ", kAsanHeapRightRedzoneMagic); + PrintShadowByte(" Freed heap region: ", kAsanHeapFreeMagic); PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic); PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic); PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic); @@ -173,19 +164,34 @@ static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, } } +static void DescribeThread(AsanThread *t) { + if (t) + DescribeThread(t->context()); +} + // ---------------------- Address Descriptions ------------------- {{{1 static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } +static const char *MaybeDemangleGlobalName(const char *name) { + // We can spoil names of globals with C linkage, so use an heuristic + // approach to check if the name should be demangled. + return (name[0] == '_' && name[1] == 'Z' && &getSymbolizer) + ? getSymbolizer()->Demangle(name) + : name; +} + // Check if the global is a zero-terminated ASCII string. If so, print it. static void PrintGlobalNameIfASCII(const __asan_global &g) { for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { - if (!IsASCII(*(unsigned char*)p)) return; + unsigned char c = *(unsigned char*)p; + if (c == '\0' || !IsASCII(c)) return; } - if (*(char*)(g.beg + g.size - 1) != 0) return; - Printf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg); + if (*(char*)(g.beg + g.size - 1) != '\0') return; + Printf(" '%s' is ascii string '%s'\n", + MaybeDemangleGlobalName(g.name), (char*)g.beg); } bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, @@ -206,8 +212,8 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, // Can it happen? Printf("%p is located %zd bytes inside", (void*)addr, addr - g.beg); } - Printf(" of global variable '%s' (0x%zx) of size %zu\n", - g.name, g.beg, g.size); + Printf(" of global variable '%s' from '%s' (0x%zx) of size %zu\n", + MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size); Printf("%s", d.EndLocation()); PrintGlobalNameIfASCII(g); return true; @@ -234,57 +240,149 @@ bool DescribeAddressIfShadow(uptr addr) { return false; } +// Return " (thread_name) " or an empty string if the name is empty. +const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], + uptr buff_len) { + const char *name = t->name; + if (name[0] == '\0') return ""; + buff[0] = 0; + internal_strncat(buff, " (", 3); + internal_strncat(buff, name, buff_len - 4); + internal_strncat(buff, ")", 2); + return buff; +} + +const char *ThreadNameWithParenthesis(u32 tid, char buff[], + uptr buff_len) { + if (tid == kInvalidTid) return ""; + asanThreadRegistry().CheckLocked(); + AsanThreadContext *t = GetThreadContextByTidLocked(tid); + return ThreadNameWithParenthesis(t, buff, buff_len); +} + +void PrintAccessAndVarIntersection(const char *var_name, + uptr var_beg, uptr var_size, + uptr addr, uptr access_size, + uptr prev_var_end, uptr next_var_beg) { + uptr var_end = var_beg + var_size; + uptr addr_end = addr + access_size; + const char *pos_descr = 0; + // If the variable [var_beg, var_end) is the nearest variable to the + // current memory access, indicate it in the log. + if (addr >= var_beg) { + if (addr_end <= var_end) + pos_descr = "is inside"; // May happen if this is a use-after-return. + else if (addr < var_end) + pos_descr = "partially overflows"; + else if (addr_end <= next_var_beg && + next_var_beg - addr_end >= addr - var_end) + pos_descr = "overflows"; + } else { + if (addr_end > var_beg) + pos_descr = "partially underflows"; + else if (addr >= prev_var_end && + addr - prev_var_end >= var_beg - addr_end) + pos_descr = "underflows"; + } + Printf(" [%zd, %zd) '%s'", var_beg, var_beg + var_size, var_name); + if (pos_descr) { + Decorator d; + // FIXME: we may want to also print the size of the access here, + // but in case of accesses generated by memset it may be confusing. + Printf("%s <== Memory access at offset %zd %s this variable%s\n", + d.Location(), addr, pos_descr, d.EndLocation()); + } else { + Printf("\n"); + } +} + +struct StackVarDescr { + uptr beg; + uptr size; + const char *name_pos; + uptr name_len; +}; + bool DescribeAddressIfStack(uptr addr, uptr access_size) { - AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr); + AsanThread *t = FindThreadByStackAddress(addr); if (!t) return false; - const sptr kBufSize = 4095; + const uptr kBufSize = 4095; char buf[kBufSize]; uptr offset = 0; - const char *frame_descr = t->GetFrameNameByAddr(addr, &offset); + uptr frame_pc = 0; + char tname[128]; + const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc); + +#ifdef __powerpc64__ + // On PowerPC64, the address of a function actually points to a + // three-doubleword data structure with the first field containing + // the address of the function's code. + frame_pc = *reinterpret_cast<uptr *>(frame_pc); +#endif + // This string is created by the compiler and has the following form: - // "FunctioName n alloc_1 alloc_2 ... alloc_n" + // "n alloc_1 alloc_2 ... alloc_n" // where alloc_i looks like "offset size len ObjectName ". CHECK(frame_descr); - // Report the function name and the offset. - const char *name_end = internal_strchr(frame_descr, ' '); - CHECK(name_end); - buf[0] = 0; - internal_strncat(buf, frame_descr, - Min(kBufSize, - static_cast<sptr>(name_end - frame_descr))); Decorator d; Printf("%s", d.Location()); - Printf("Address %p is located at offset %zu " - "in frame <%s> of T%d's stack:\n", - (void*)addr, offset, Demangle(buf), t->tid()); + Printf("Address %p is located in stack of thread T%d%s " + "at offset %zu in frame\n", + addr, t->tid(), + ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)), + offset); + // Now we print the frame where the alloca has happened. + // We print this frame as a stack trace with one element. + // The symbolizer may print more than one frame if inlining was involved. + // The frame numbers may be different than those in the stack trace printed + // previously. That's unfortunate, but I have no better solution, + // especially given that the alloca may be from entirely different place + // (e.g. use-after-scope, or different thread's stack). + StackTrace alloca_stack; + alloca_stack.trace[0] = frame_pc + 16; + alloca_stack.size = 1; Printf("%s", d.EndLocation()); + PrintStack(&alloca_stack); // Report the number of stack objects. char *p; - uptr n_objects = internal_simple_strtoll(name_end, &p, 10); - CHECK(n_objects > 0); + uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10); + CHECK_GT(n_objects, 0); Printf(" This frame has %zu object(s):\n", n_objects); + // Report all objects in this frame. + InternalScopedBuffer<StackVarDescr> vars(n_objects); for (uptr i = 0; i < n_objects; i++) { uptr beg, size; - sptr len; - beg = internal_simple_strtoll(p, &p, 10); - size = internal_simple_strtoll(p, &p, 10); - len = internal_simple_strtoll(p, &p, 10); - if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') { + uptr len; + beg = (uptr)internal_simple_strtoll(p, &p, 10); + size = (uptr)internal_simple_strtoll(p, &p, 10); + len = (uptr)internal_simple_strtoll(p, &p, 10); + if (beg == 0 || size == 0 || *p != ' ') { Printf("AddressSanitizer can't parse the stack frame " "descriptor: |%s|\n", frame_descr); break; } p++; - buf[0] = 0; - internal_strncat(buf, p, Min(kBufSize, len)); + vars[i].beg = beg; + vars[i].size = size; + vars[i].name_pos = p; + vars[i].name_len = len; p += len; - Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); + } + for (uptr i = 0; i < n_objects; i++) { + buf[0] = 0; + internal_strncat(buf, vars[i].name_pos, + static_cast<uptr>(Min(kBufSize, vars[i].name_len))); + uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; + uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); + PrintAccessAndVarIntersection(buf, vars[i].beg, vars[i].size, + offset, access_size, + prev_var_end, next_var_beg); } Printf("HINT: this may be a false positive if your program uses " "some custom stack unwind mechanism or swapcontext\n" " (longjmp and C++ exceptions *are* supported)\n"); - DescribeThread(t->summary()); + DescribeThread(t); return true; } @@ -312,65 +410,43 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, Printf("%s", d.EndLocation()); } -// Return " (thread_name) " or an empty string if the name is empty. -const char *ThreadNameWithParenthesis(AsanThreadSummary *t, char buff[], - uptr buff_len) { - const char *name = t->name(); - if (*name == 0) return ""; - buff[0] = 0; - internal_strncat(buff, " (", 3); - internal_strncat(buff, name, buff_len - 4); - internal_strncat(buff, ")", 2); - return buff; -} - -const char *ThreadNameWithParenthesis(u32 tid, char buff[], - uptr buff_len) { - if (tid == kInvalidTid) return ""; - AsanThreadSummary *t = asanThreadRegistry().FindByTid(tid); - return ThreadNameWithParenthesis(t, buff, buff_len); -} - void DescribeHeapAddress(uptr addr, uptr access_size) { AsanChunkView chunk = FindHeapChunkByAddress(addr); if (!chunk.IsValid()) return; DescribeAccessToHeapChunk(chunk, addr, access_size); CHECK(chunk.AllocTid() != kInvalidTid); - AsanThreadSummary *alloc_thread = - asanThreadRegistry().FindByTid(chunk.AllocTid()); + asanThreadRegistry().CheckLocked(); + AsanThreadContext *alloc_thread = + GetThreadContextByTidLocked(chunk.AllocTid()); StackTrace alloc_stack; chunk.GetAllocStack(&alloc_stack); - AsanThread *t = asanThreadRegistry().GetCurrent(); - CHECK(t); char tname[128]; Decorator d; + AsanThreadContext *free_thread = 0; if (chunk.FreeTid() != kInvalidTid) { - AsanThreadSummary *free_thread = - asanThreadRegistry().FindByTid(chunk.FreeTid()); + free_thread = GetThreadContextByTidLocked(chunk.FreeTid()); Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), - free_thread->tid(), + free_thread->tid, ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), d.EndAllocation()); StackTrace free_stack; chunk.GetFreeStack(&free_stack); PrintStack(&free_stack); Printf("%spreviously allocated by thread T%d%s here:%s\n", - d.Allocation(), alloc_thread->tid(), + d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); - PrintStack(&alloc_stack); - DescribeThread(t->summary()); - DescribeThread(free_thread); - DescribeThread(alloc_thread); } else { Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), - alloc_thread->tid(), + alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); - PrintStack(&alloc_stack); - DescribeThread(t->summary()); - DescribeThread(alloc_thread); } + PrintStack(&alloc_stack); + DescribeThread(GetCurrentThread()); + if (free_thread) + DescribeThread(free_thread); + DescribeThread(alloc_thread); } void DescribeAddress(uptr addr, uptr access_size) { @@ -388,26 +464,27 @@ void DescribeAddress(uptr addr, uptr access_size) { // ------------------- Thread description -------------------- {{{1 -void DescribeThread(AsanThreadSummary *summary) { - CHECK(summary); +void DescribeThread(AsanThreadContext *context) { + CHECK(context); + asanThreadRegistry().CheckLocked(); // No need to announce the main thread. - if (summary->tid() == 0 || summary->announced()) { + if (context->tid == 0 || context->announced) { return; } - summary->set_announced(true); + context->announced = true; char tname[128]; - Printf("Thread T%d%s", summary->tid(), - ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname))); + Printf("Thread T%d%s", context->tid, + ThreadNameWithParenthesis(context->tid, tname, sizeof(tname))); Printf(" created by T%d%s here:\n", - summary->parent_tid(), - ThreadNameWithParenthesis(summary->parent_tid(), + context->parent_tid, + ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); - PrintStack(summary->stack()); + PrintStack(&context->stack); // Recursively described parent thread if needed. if (flags()->print_full_thread_history) { - AsanThreadSummary *parent_summary = - asanThreadRegistry().FindByTid(summary->parent_tid()); - DescribeThread(parent_summary); + AsanThreadContext *parent_context = + GetThreadContextByTidLocked(context->parent_tid); + DescribeThread(parent_context); } } @@ -426,7 +503,7 @@ class ScopedInErrorReport { // they are defined as no-return. Report("AddressSanitizer: while reporting a bug found another one." "Ignoring.\n"); - u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + u32 current_tid = GetCurrentTidOrInvalid(); if (current_tid != reporting_thread_tid) { // ASan found two bugs in different threads simultaneously. Sleep // long enough to make sure that the thread which started to print @@ -438,24 +515,20 @@ class ScopedInErrorReport { internal__exit(flags()->exitcode); } ASAN_ON_ERROR(); - reporting_thread_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + // Make sure the registry and sanitizer report mutexes are locked while + // we're printing an error report. + // We can lock them only here to avoid self-deadlock in case of + // recursive reports. + asanThreadRegistry().Lock(); + CommonSanitizerReportMutex.Lock(); + reporting_thread_tid = GetCurrentTidOrInvalid(); Printf("====================================================" "=============\n"); - if (reporting_thread_tid != kInvalidTid) { - // We started reporting an error message. Stop using the fake stack - // in case we call an instrumented function from a symbolizer. - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - CHECK(curr_thread); - curr_thread->fake_stack().StopUsingFakeStack(); - } } // Destructor is NORETURN, as functions that report errors are. NORETURN ~ScopedInErrorReport() { // Make sure the current thread is announced. - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); - if (curr_thread) { - DescribeThread(curr_thread->summary()); - } + DescribeThread(GetCurrentThread()); // Print memory stats. if (flags()->print_stats) __asan_print_accumulated_stats(); @@ -469,13 +542,15 @@ class ScopedInErrorReport { static void ReportSummary(const char *error_type, StackTrace *stack) { if (!stack->size) return; - if (IsSymbolizerAvailable()) { + if (&getSymbolizer && getSymbolizer()->IsAvailable()) { AddressInfo ai; // Currently, we include the first stack frame into the report summary. // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). - SymbolizeCode(stack->trace[0], &ai, 1); + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + getSymbolizer()->SymbolizeCode(pc, &ai, 1); ReportErrorSummary(error_type, - StripPathPrefix(ai.file, flags()->strip_path_prefix), + StripPathPrefix(ai.file, + common_flags()->strip_path_prefix), ai.line, ai.function); } // FIXME: do we need to print anything at all if there is no symbolizer? @@ -488,7 +563,7 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { Report("ERROR: AddressSanitizer: SEGV on unknown address %p" " (pc %p sp %p bp %p T%d)\n", (void*)addr, (void*)pc, (void*)sp, (void*)bp, - asanThreadRegistry().GetCurrentTidOrInvalid()); + GetCurrentTidOrInvalid()); Printf("%s", d.EndWarning()); Printf("AddressSanitizer can not provide additional info.\n"); GET_STACK_TRACE_FATAL(pc, bp); @@ -500,7 +575,13 @@ void ReportDoubleFree(uptr addr, StackTrace *stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); - Report("ERROR: AddressSanitizer: attempting double-free on %p:\n", addr); + char tname[128]; + u32 curr_tid = GetCurrentTidOrInvalid(); + Report("ERROR: AddressSanitizer: attempting double-free on %p in " + "thread T%d%s:\n", + addr, curr_tid, + ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); + Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); @@ -511,8 +592,11 @@ void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); + char tname[128]; + u32 curr_tid = GetCurrentTidOrInvalid(); Report("ERROR: AddressSanitizer: attempting free on address " - "which was not malloc()-ed: %p\n", addr); + "which was not malloc()-ed: %p in thread T%d%s\n", addr, + curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); @@ -678,7 +762,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, bug_descr, (void*)addr, pc, bp, sp); Printf("%s", d.EndWarning()); - u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); + u32 curr_tid = GetCurrentTidOrInvalid(); char tname[128]; Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(), @@ -712,6 +796,6 @@ void __asan_describe_address(uptr addr) { #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default implementation of __asan_on_error that does nothing // and may be overriden by user. -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE void __asan_on_error() {} #endif diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h index 13724dab9ee..afe7673304c 100644 --- a/libsanitizer/asan/asan_report.h +++ b/libsanitizer/asan/asan_report.h @@ -27,7 +27,7 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size); // Determines memory type on its own. void DescribeAddress(uptr addr, uptr access_size); -void DescribeThread(AsanThreadSummary *summary); +void DescribeThread(AsanThreadContext *context); // Different kinds of error reports. void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr); diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc index 6ddb01329ad..67327611e84 100644 --- a/libsanitizer/asan/asan_rtl.cc +++ b/libsanitizer/asan/asan_rtl.cc @@ -11,17 +11,21 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_interceptors.h" +#include "asan_interface_internal.h" #include "asan_internal.h" #include "asan_mapping.h" +#include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_symbolizer.h" +#include "lsan/lsan_common.h" + +int __asan_option_detect_stack_use_after_return; // Global interface symbol. namespace __asan { @@ -62,13 +66,9 @@ static void AsanCheckFailed(const char *file, int line, const char *cond, } // -------------------------- Flags ------------------------- {{{1 -static const int kDeafultMallocContextSize = 30; - -static Flags asan_flags; +static const int kDefaultMallocContextSize = 30; -Flags *flags() { - return &asan_flags; -} +Flags asan_flags_dont_use_directly; // use via flags(). static const char *MaybeCallAsanDefaultOptions() { return (&__asan_default_options) ? __asan_default_options() : ""; @@ -86,28 +86,32 @@ static const char *MaybeUseAsanDefaultOptionsCompileDefiniton() { } static void ParseFlagsFromString(Flags *f, const char *str) { + ParseCommonFlagsFromString(str); + CHECK((uptr)common_flags()->malloc_context_size <= kStackTraceMax); + ParseFlag(str, &f->quarantine_size, "quarantine_size"); - ParseFlag(str, &f->symbolize, "symbolize"); ParseFlag(str, &f->verbosity, "verbosity"); ParseFlag(str, &f->redzone, "redzone"); - CHECK(f->redzone >= 16); + CHECK_GE(f->redzone, 16); CHECK(IsPowerOfTwo(f->redzone)); ParseFlag(str, &f->debug, "debug"); ParseFlag(str, &f->report_globals, "report_globals"); - ParseFlag(str, &f->check_initialization_order, "initialization_order"); - ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); - CHECK((uptr)f->malloc_context_size <= kStackTraceMax); + ParseFlag(str, &f->check_initialization_order, "check_initialization_order"); ParseFlag(str, &f->replace_str, "replace_str"); ParseFlag(str, &f->replace_intrin, "replace_intrin"); ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free"); - ParseFlag(str, &f->use_fake_stack, "use_fake_stack"); + ParseFlag(str, &f->detect_stack_use_after_return, + "detect_stack_use_after_return"); + ParseFlag(str, &f->uar_stack_size_log, "uar_stack_size_log"); ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size"); + ParseFlag(str, &f->malloc_fill_byte, "malloc_fill_byte"); ParseFlag(str, &f->exitcode, "exitcode"); ParseFlag(str, &f->allow_user_poisoning, "allow_user_poisoning"); ParseFlag(str, &f->sleep_before_dying, "sleep_before_dying"); ParseFlag(str, &f->handle_segv, "handle_segv"); + ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler"); ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack"); ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size"); ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit"); @@ -116,37 +120,47 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->print_legend, "print_legend"); ParseFlag(str, &f->atexit, "atexit"); ParseFlag(str, &f->disable_core, "disable_core"); - ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); ParseFlag(str, &f->allow_reexec, "allow_reexec"); ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history"); - ParseFlag(str, &f->log_path, "log_path"); - ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal"); - ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc"); ParseFlag(str, &f->poison_heap, "poison_heap"); ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); ParseFlag(str, &f->use_stack_depot, "use_stack_depot"); + ParseFlag(str, &f->strict_memcmp, "strict_memcmp"); + ParseFlag(str, &f->strict_init_order, "strict_init_order"); } void InitializeFlags(Flags *f, const char *env) { - internal_memset(f, 0, sizeof(*f)); + CommonFlags *cf = common_flags(); + cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); + cf->symbolize = true; + cf->malloc_context_size = kDefaultMallocContextSize; + cf->fast_unwind_on_fatal = false; + cf->fast_unwind_on_malloc = true; + cf->strip_path_prefix = ""; + cf->handle_ioctl = false; + cf->log_path = 0; + cf->detect_leaks = false; + cf->leak_check_at_exit = true; + internal_memset(f, 0, sizeof(*f)); f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; - f->symbolize = false; f->verbosity = 0; - f->redzone = ASAN_ALLOCATOR_VERSION == 2 ? 16 : (ASAN_LOW_MEMORY) ? 64 : 128; + f->redzone = 16; f->debug = false; f->report_globals = 1; - f->check_initialization_order = true; - f->malloc_context_size = kDeafultMallocContextSize; + f->check_initialization_order = false; f->replace_str = true; f->replace_intrin = true; f->mac_ignore_invalid_free = false; - f->use_fake_stack = true; - f->max_malloc_fill_size = 0; + f->detect_stack_use_after_return = false; // Also needs the compiler flag. + f->uar_stack_size_log = 0; + f->max_malloc_fill_size = 0x1000; // By default, fill only the first 4K. + f->malloc_fill_byte = 0xbe; f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE; f->allow_user_poisoning = true; f->sleep_before_dying = 0; f->handle_segv = ASAN_NEEDS_SEGV; + f->allow_user_segv_handler = false; f->use_sigaltstack = false; f->check_malloc_usable_size = true; f->unmap_shadow_on_exit = false; @@ -155,15 +169,15 @@ void InitializeFlags(Flags *f, const char *env) { f->print_legend = true; f->atexit = false; f->disable_core = (SANITIZER_WORDSIZE == 64); - f->strip_path_prefix = ""; f->allow_reexec = true; f->print_full_thread_history = true; - f->log_path = 0; - f->fast_unwind_on_fatal = false; - f->fast_unwind_on_malloc = true; f->poison_heap = true; - f->alloc_dealloc_mismatch = true; - f->use_stack_depot = true; // Only affects allocator2. + // Turn off alloc/dealloc mismatch checker on Mac and Windows for now. + // TODO(glider,timurrrr): Fix known issues and enable this back. + f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0); + f->use_stack_depot = true; + f->strict_memcmp = true; + f->strict_init_order = false; // Override from compile definition. ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton()); @@ -177,6 +191,20 @@ void InitializeFlags(Flags *f, const char *env) { // Override from command line. ParseFlagsFromString(f, env); + +#if !CAN_SANITIZE_LEAKS + if (cf->detect_leaks) { + Report("%s: detect_leaks is not supported on this platform.\n", + SanitizerToolName); + cf->detect_leaks = false; + } +#endif + + if (cf->detect_leaks && !f->use_stack_depot) { + Report("%s: detect_leaks is ignored (requires use_stack_depot).\n", + SanitizerToolName); + cf->detect_leaks = false; + } } // -------------------------- Globals --------------------- {{{1 @@ -197,8 +225,8 @@ void ShowStatsAndAbort() { // ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. static void ReserveShadowMemoryRange(uptr beg, uptr end) { - CHECK((beg % GetPageSizeCached()) == 0); - CHECK(((end + 1) % GetPageSizeCached()) == 0); + CHECK_EQ((beg % GetPageSizeCached()), 0); + CHECK_EQ(((end + 1) % GetPageSizeCached()), 0); uptr size = end - beg + 1; void *res = MmapFixedNoReserve(beg, size); if (res != (void*)beg) { @@ -281,9 +309,7 @@ static NOINLINE void force_interface_symbols() { case 25: __asan_poison_memory_region(0, 0); break; case 26: __asan_unpoison_memory_region(0, 0); break; case 27: __asan_set_error_exit_code(0); break; - case 28: __asan_stack_free(0, 0, 0); break; - case 29: __asan_stack_malloc(0, 0); break; - case 30: __asan_before_dynamic_init(0, 0); break; + case 30: __asan_before_dynamic_init(0); break; case 31: __asan_after_dynamic_init(); break; case 32: __asan_poison_stack_memory(0, 0); break; case 33: __asan_unpoison_stack_memory(0, 0); break; @@ -304,22 +330,12 @@ static void asan_atexit() { static void InitializeHighMemEnd() { #if !ASAN_FIXED_MAPPING -#if SANITIZER_WORDSIZE == 64 -# if defined(__powerpc64__) - // FIXME: - // On PowerPC64 we have two different address space layouts: 44- and 46-bit. - // We somehow need to figure our which one we are using now and choose - // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. - // Note that with 'ulimit -s unlimited' the stack is moved away from the top - // of the address space, so simply checking the stack address is not enough. - kHighMemEnd = (1ULL << 44) - 1; // 0x00000fffffffffffUL -# else - kHighMemEnd = (1ULL << 47) - 1; // 0x00007fffffffffffUL; -# endif -#else // SANITIZER_WORDSIZE == 32 - kHighMemEnd = (1ULL << 32) - 1; // 0xffffffff; -#endif // SANITIZER_WORDSIZE + kHighMemEnd = GetMaxVirtualAddress(); + // Increase kHighMemEnd to make sure it's properly + // aligned together with kHighMemBeg: + kHighMemEnd |= SHADOW_GRANULARITY * GetPageSizeCached() - 1; #endif // !ASAN_FIXED_MAPPING + CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0); } static void ProtectGap(uptr a, uptr size) { @@ -361,7 +377,9 @@ static void PrintAddressSpaceLayout() { } Printf("\n"); Printf("red_zone=%zu\n", (uptr)flags()->redzone); - Printf("malloc_context_size=%zu\n", (uptr)flags()->malloc_context_size); + Printf("quarantine_size=%zuM\n", (uptr)flags()->quarantine_size >> 20); + Printf("malloc_context_size=%zu\n", + (uptr)common_flags()->malloc_context_size); Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE); Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY); @@ -380,7 +398,7 @@ using namespace __asan; // NOLINT #if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char* __asan_default_options() { return ""; } } // extern "C" #endif @@ -393,12 +411,28 @@ int NOINLINE __asan_set_error_exit_code(int exit_code) { void NOINLINE __asan_handle_no_return() { int local_stack; - AsanThread *curr_thread = asanThreadRegistry().GetCurrent(); + AsanThread *curr_thread = GetCurrentThread(); CHECK(curr_thread); uptr PageSize = GetPageSizeCached(); uptr top = curr_thread->stack_top(); uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1); + static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M + if (top - bottom > kMaxExpectedCleanupSize) { + static bool reported_warning = false; + if (reported_warning) + return; + reported_warning = true; + Report("WARNING: ASan is ignoring requested __asan_handle_no_return: " + "stack top: %p; bottom %p; size: %p (%zd)\n" + "False positive error reports may follow\n" + "For details see " + "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n", + top, bottom, top - bottom, top - bottom); + return; + } PoisonShadow(bottom, top - bottom, 0); + if (curr_thread->has_fake_stack()) + curr_thread->fake_stack()->HandleNoReturn(); } void NOINLINE __asan_set_death_callback(void (*callback)(void)) { @@ -424,7 +458,9 @@ void __asan_init() { // initialization steps look at flags(). const char *options = GetEnv("ASAN_OPTIONS"); InitializeFlags(flags(), options); - __sanitizer_set_report_path(flags()->log_path); + __sanitizer_set_report_path(common_flags()->log_path); + __asan_option_detect_stack_use_after_return = + flags()->detect_stack_use_after_return; if (flags()->verbosity && options) { Report("Parsed ASAN_OPTIONS: %s\n", options); @@ -447,12 +483,12 @@ void __asan_init() { ReplaceOperatorsNewAndDelete(); uptr shadow_start = kLowShadowBeg; - if (kLowShadowBeg) shadow_start -= GetMmapGranularity(); - uptr shadow_end = kHighShadowEnd; + if (kLowShadowBeg) + shadow_start -= GetMmapGranularity(); bool full_shadow_is_available = - MemoryRangeIsAvailable(shadow_start, shadow_end); + MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); -#if ASAN_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING +#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING if (!full_shadow_is_available) { kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; @@ -476,7 +512,7 @@ void __asan_init() { ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); } else if (kMidMemBeg && MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && - MemoryRangeIsAvailable(kMidMemEnd + 1, shadow_end)) { + MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { CHECK(kLowShadowBeg != kLowShadowEnd); // mmap the low shadow plus at least one page at the left. ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); @@ -496,12 +532,16 @@ void __asan_init() { } InstallSignalHandlers(); + + AsanTSDInit(AsanThread::TSDDtor); + // Allocator should be initialized before starting external symbolizer, as + // fork() on Mac locks the allocator. + InitializeAllocator(); + // Start symbolizer process if necessary. - if (flags()->symbolize) { - const char *external_symbolizer = GetEnv("ASAN_SYMBOLIZER_PATH"); - if (external_symbolizer) { - InitializeExternalSymbolizer(external_symbolizer); - } + if (common_flags()->symbolize && &getSymbolizer) { + getSymbolizer() + ->InitializeExternal(common_flags()->external_symbolizer_path); } // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited @@ -509,11 +549,24 @@ void __asan_init() { asan_inited = 1; asan_init_is_running = false; - asanThreadRegistry().Init(); - asanThreadRegistry().GetMain()->ThreadStart(); + InitTlsSize(); + + // Create main thread. + AsanThread *main_thread = AsanThread::Create(0, 0); + CreateThreadContextArgs create_main_args = { main_thread, 0 }; + u32 main_tid = asanThreadRegistry().CreateThread( + 0, true, 0, &create_main_args); + CHECK_EQ(0, main_tid); + SetCurrentThread(main_thread); + main_thread->ThreadStart(internal_getpid()); force_interface_symbols(); // no-op. - InitializeAllocator(); +#if CAN_SANITIZE_LEAKS + __lsan::InitCommonLsan(); + if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { + Atexit(__lsan::DoLeakCheck); + } +#endif // CAN_SANITIZE_LEAKS if (flags()->verbosity) { Report("AddressSanitizer Init done\n"); diff --git a/libsanitizer/asan/asan_stack.cc b/libsanitizer/asan/asan_stack.cc index 999cbfba757..74952518642 100644 --- a/libsanitizer/asan/asan_stack.cc +++ b/libsanitizer/asan/asan_stack.cc @@ -12,6 +12,7 @@ #include "asan_internal.h" #include "asan_flags.h" #include "asan_stack.h" +#include "sanitizer_common/sanitizer_flags.h" namespace __asan { @@ -22,8 +23,8 @@ static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer, } void PrintStack(StackTrace *stack) { - stack->PrintStack(stack->trace, stack->size, flags()->symbolize, - flags()->strip_path_prefix, MaybeCallAsanSymbolize); + stack->PrintStack(stack->trace, stack->size, common_flags()->symbolize, + common_flags()->strip_path_prefix, MaybeCallAsanSymbolize); } } // namespace __asan @@ -33,8 +34,8 @@ void PrintStack(StackTrace *stack) { // Provide default implementation of __asan_symbolize that does nothing // and may be overriden by user if he wants to use his own symbolization. // ASan on Windows has its own implementation of this. -#if !defined(_WIN32) && !SANITIZER_SUPPORTS_WEAK_HOOKS -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +#if !SANITIZER_WINDOWS && !SANITIZER_SUPPORTS_WEAK_HOOKS +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) { return false; } diff --git a/libsanitizer/asan/asan_stack.h b/libsanitizer/asan/asan_stack.h index 6a5ffc934cc..3c0ac31f6c6 100644 --- a/libsanitizer/asan/asan_stack.h +++ b/libsanitizer/asan/asan_stack.h @@ -12,12 +12,13 @@ #ifndef ASAN_STACK_H #define ASAN_STACK_H -#include "sanitizer_common/sanitizer_stacktrace.h" #include "asan_flags.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_stacktrace.h" namespace __asan { -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast); void PrintStack(StackTrace *stack); } // namespace __asan @@ -25,10 +26,24 @@ void PrintStack(StackTrace *stack); // Get the stack trace with the given pc and bp. // The pc will be in the position 0 of the resulting stack trace. // The bp may refer to the current frame or to the caller's frame. -// fast_unwind is currently unused. -#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ - StackTrace stack; \ - GetStackTrace(&stack, max_s, pc, bp, fast) +#if SANITIZER_WINDOWS +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + GetStackTrace(&stack, max_s, pc, bp, 0, 0, fast) +#else +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + { \ + AsanThread *t; \ + stack.size = 0; \ + if (asan_inited && (t = GetCurrentThread()) && !t->isUnwinding()) { \ + uptr stack_top = t->stack_top(); \ + uptr stack_bottom = t->stack_bottom(); \ + ScopedUnwinding unwind_scope(t); \ + GetStackTrace(&stack, max_s, pc, bp, stack_top, stack_bottom, fast); \ + } \ + } +#endif // SANITIZER_WINDOWS // NOTE: A Rule of thumb is to retrieve stack trace in the interceptors // as early as possible (in functions exposed to the user), as we generally @@ -40,24 +55,24 @@ void PrintStack(StackTrace *stack); #define GET_STACK_TRACE_FATAL(pc, bp) \ GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \ - flags()->fast_unwind_on_fatal) + common_flags()->fast_unwind_on_fatal) -#define GET_STACK_TRACE_FATAL_HERE \ - GET_STACK_TRACE(kStackTraceMax, flags()->fast_unwind_on_fatal) +#define GET_STACK_TRACE_FATAL_HERE \ + GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal) -#define GET_STACK_TRACE_THREAD \ +#define GET_STACK_TRACE_THREAD \ GET_STACK_TRACE(kStackTraceMax, true) -#define GET_STACK_TRACE_MALLOC \ - GET_STACK_TRACE(flags()->malloc_context_size, \ - flags()->fast_unwind_on_malloc) +#define GET_STACK_TRACE_MALLOC \ + GET_STACK_TRACE(common_flags()->malloc_context_size, \ + common_flags()->fast_unwind_on_malloc) #define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC #define PRINT_CURRENT_STACK() \ { \ GET_STACK_TRACE(kStackTraceMax, \ - flags()->fast_unwind_on_fatal); \ + common_flags()->fast_unwind_on_fatal); \ PrintStack(&stack); \ } diff --git a/libsanitizer/asan/asan_stats.cc b/libsanitizer/asan/asan_stats.cc index 935b33e20ac..71c8582e81c 100644 --- a/libsanitizer/asan/asan_stats.cc +++ b/libsanitizer/asan/asan_stats.cc @@ -12,13 +12,18 @@ #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_stats.h" -#include "asan_thread_registry.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_stackdepot.h" namespace __asan { AsanStats::AsanStats() { - CHECK(REAL(memset) != 0); + Clear(); +} + +void AsanStats::Clear() { + CHECK(REAL(memset)); REAL(memset)(this, 0, sizeof(AsanStats)); } @@ -51,11 +56,73 @@ void AsanStats::Print() { malloc_large, malloc_small_slow); } +void AsanStats::MergeFrom(const AsanStats *stats) { + uptr *dst_ptr = reinterpret_cast<uptr*>(this); + const uptr *src_ptr = reinterpret_cast<const uptr*>(stats); + uptr num_fields = sizeof(*this) / sizeof(uptr); + for (uptr i = 0; i < num_fields; i++) + dst_ptr[i] += src_ptr[i]; +} + static BlockingMutex print_lock(LINKER_INITIALIZED); +static AsanStats unknown_thread_stats(LINKER_INITIALIZED); +static AsanStats dead_threads_stats(LINKER_INITIALIZED); +static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED); +// Required for malloc_zone_statistics() on OS X. This can't be stored in +// per-thread AsanStats. +static uptr max_malloced_memory; + +static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) { + AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg); + AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base); + if (AsanThread *t = tctx->thread) + accumulated_stats->MergeFrom(&t->stats()); +} + +static void GetAccumulatedStats(AsanStats *stats) { + stats->Clear(); + { + ThreadRegistryLock l(&asanThreadRegistry()); + asanThreadRegistry() + .RunCallbackForEachThreadLocked(MergeThreadStats, stats); + } + stats->MergeFrom(&unknown_thread_stats); + { + BlockingMutexLock lock(&dead_threads_stats_lock); + stats->MergeFrom(&dead_threads_stats); + } + // This is not very accurate: we may miss allocation peaks that happen + // between two updates of accumulated_stats_. For more accurate bookkeeping + // the maximum should be updated on every malloc(), which is unacceptable. + if (max_malloced_memory < stats->malloced) { + max_malloced_memory = stats->malloced; + } +} + +void FlushToDeadThreadStats(AsanStats *stats) { + BlockingMutexLock lock(&dead_threads_stats_lock); + dead_threads_stats.MergeFrom(stats); + stats->Clear(); +} + +void FillMallocStatistics(AsanMallocStats *malloc_stats) { + AsanStats stats; + GetAccumulatedStats(&stats); + malloc_stats->blocks_in_use = stats.mallocs; + malloc_stats->size_in_use = stats.malloced; + malloc_stats->max_size_in_use = max_malloced_memory; + malloc_stats->size_allocated = stats.mmaped; +} + +AsanStats &GetCurrentThreadStats() { + AsanThread *t = GetCurrentThread(); + return (t) ? t->stats() : unknown_thread_stats; +} + static void PrintAccumulatedStats() { AsanStats stats; - asanThreadRegistry().GetAccumulatedStats(&stats); + GetAccumulatedStats(&stats); // Use lock to keep reports from mixing up. BlockingMutexLock lock(&print_lock); stats.Print(); @@ -71,15 +138,33 @@ static void PrintAccumulatedStats() { using namespace __asan; // NOLINT uptr __asan_get_current_allocated_bytes() { - return asanThreadRegistry().GetCurrentAllocatedBytes(); + AsanStats stats; + GetAccumulatedStats(&stats); + uptr malloced = stats.malloced; + uptr freed = stats.freed; + // Return sane value if malloced < freed due to racy + // way we update accumulated stats. + return (malloced > freed) ? malloced - freed : 1; } uptr __asan_get_heap_size() { - return asanThreadRegistry().GetHeapSize(); + AsanStats stats; + GetAccumulatedStats(&stats); + return stats.mmaped - stats.munmaped; } uptr __asan_get_free_bytes() { - return asanThreadRegistry().GetFreeBytes(); + AsanStats stats; + GetAccumulatedStats(&stats); + uptr total_free = stats.mmaped + - stats.munmaped + + stats.really_freed + + stats.really_freed_redzones; + uptr total_used = stats.malloced + + stats.malloced_redzones; + // Return sane value if total_free < total_used due to racy + // way we update accumulated stats. + return (total_free > total_used) ? total_free - total_used : 1; } uptr __asan_get_unmapped_bytes() { diff --git a/libsanitizer/asan/asan_stats.h b/libsanitizer/asan/asan_stats.h index fd27451aef2..2f964f8d052 100644 --- a/libsanitizer/asan/asan_stats.h +++ b/libsanitizer/asan/asan_stats.h @@ -50,10 +50,17 @@ struct AsanStats { // Default ctor for thread-local stats. AsanStats(); - // Prints formatted stats to stderr. - void Print(); + void Print(); // Prints formatted stats to stderr. + void Clear(); + void MergeFrom(const AsanStats *stats); }; +// Returns stats for GetCurrentThread(), or stats for fake "unknown thread" +// if GetCurrentThread() returns 0. +AsanStats &GetCurrentThreadStats(); +// Flushes a given stats into accumulated stats of dead threads. +void FlushToDeadThreadStats(AsanStats *stats); + // A cross-platform equivalent of malloc_statistics_t on Mac OS. struct AsanMallocStats { uptr blocks_in_use; @@ -62,6 +69,8 @@ struct AsanMallocStats { uptr size_allocated; }; +void FillMallocStatistics(AsanMallocStats *malloc_stats); + } // namespace __asan #endif // ASAN_STATS_H diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc index 02f49dd59ef..1da714c6013 100644 --- a/libsanitizer/asan/asan_thread.cc +++ b/libsanitizer/asan/asan_thread.cc @@ -11,46 +11,82 @@ //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_interceptors.h" +#include "asan_poisoning.h" #include "asan_stack.h" #include "asan_thread.h" -#include "asan_thread_registry.h" #include "asan_mapping.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_placement_new.h" +#include "lsan/lsan_common.h" namespace __asan { -AsanThread::AsanThread(LinkerInitialized x) - : fake_stack_(x), - malloc_storage_(x), - stats_(x) { } +// AsanThreadContext implementation. -AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine, - void *arg, StackTrace *stack) { +void AsanThreadContext::OnCreated(void *arg) { + CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg); + if (args->stack) { + internal_memcpy(&stack, args->stack, sizeof(stack)); + } + thread = args->thread; + thread->set_context(this); +} + +void AsanThreadContext::OnFinished() { + // Drop the link to the AsanThread object. + thread = 0; +} + +// MIPS requires aligned address +static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)]; +static ThreadRegistry *asan_thread_registry; + +static ThreadContextBase *GetAsanThreadContext(u32 tid) { + void *mem = MmapOrDie(sizeof(AsanThreadContext), "AsanThreadContext"); + return new(mem) AsanThreadContext(tid); +} + +ThreadRegistry &asanThreadRegistry() { + static bool initialized; + // Don't worry about thread_safety - this should be called when there is + // a single thread. + if (!initialized) { + // Never reuse ASan threads: we store pointer to AsanThreadContext + // in TSD and can't reliably tell when no more TSD destructors will + // be called. It would be wrong to reuse AsanThreadContext for another + // thread before all TSD destructors will be called for it. + asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry( + GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads); + initialized = true; + } + return *asan_thread_registry; +} + +AsanThreadContext *GetThreadContextByTidLocked(u32 tid) { + return static_cast<AsanThreadContext *>( + asanThreadRegistry().GetThreadLocked(tid)); +} + +// AsanThread implementation. + +AsanThread *AsanThread::Create(thread_callback_t start_routine, + void *arg) { uptr PageSize = GetPageSizeCached(); uptr size = RoundUpTo(sizeof(AsanThread), PageSize); AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__); thread->start_routine_ = start_routine; thread->arg_ = arg; - - const uptr kSummaryAllocSize = PageSize; - CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize); - AsanThreadSummary *summary = - (AsanThreadSummary*)MmapOrDie(PageSize, "AsanThreadSummary"); - summary->Init(parent_tid, stack); - summary->set_thread(thread); - thread->set_summary(summary); + thread->context_ = 0; return thread; } -void AsanThreadSummary::TSDDtor(void *tsd) { - AsanThreadSummary *summary = (AsanThreadSummary*)tsd; - if (flags()->verbosity >= 1) { - Report("T%d TSDDtor\n", summary->tid()); - } - if (summary->thread()) { - summary->thread()->Destroy(); - } +void AsanThread::TSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + if (flags()->verbosity >= 1) + Report("T%d TSDDtor\n", context->tid); + if (context->thread) + context->thread->Destroy(); } void AsanThread::Destroy() { @@ -58,41 +94,68 @@ void AsanThread::Destroy() { Report("T%d exited\n", tid()); } - asanThreadRegistry().UnregisterThread(this); - CHECK(summary()->thread() == 0); + asanThreadRegistry().FinishThread(tid()); + FlushToDeadThreadStats(&stats_); // We also clear the shadow on thread destruction because // some code may still be executing in later TSD destructors // and we don't want it to have any poisoned stack. - ClearShadowForThreadStack(); - fake_stack().Cleanup(); + ClearShadowForThreadStackAndTLS(); + DeleteFakeStack(); uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached()); UnmapOrDie(this, size); } +// We want to create the FakeStack lazyly on the first use, but not eralier +// than the stack size is known and the procedure has to be async-signal safe. +FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { + uptr stack_size = this->stack_size(); + if (stack_size == 0) // stack_size is not yet available, don't use FakeStack. + return 0; + uptr old_val = 0; + // fake_stack_ has 3 states: + // 0 -- not initialized + // 1 -- being initialized + // ptr -- initialized + // This CAS checks if the state was 0 and if so changes it to state 1, + // if that was successfull, it initilizes the pointer. + if (atomic_compare_exchange_strong( + reinterpret_cast<atomic_uintptr_t *>(&fake_stack_), &old_val, 1UL, + memory_order_relaxed)) { + uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size)); + if (flags()->uar_stack_size_log) + stack_size_log = static_cast<uptr>(flags()->uar_stack_size_log); + fake_stack_ = FakeStack::Create(stack_size_log); + SetTLSFakeStack(fake_stack_); + return fake_stack_; + } + return 0; +} + void AsanThread::Init() { - SetThreadStackTopAndBottom(); + SetThreadStackAndTls(); CHECK(AddrIsInMem(stack_bottom_)); CHECK(AddrIsInMem(stack_top_ - 1)); - ClearShadowForThreadStack(); + ClearShadowForThreadStackAndTLS(); if (flags()->verbosity >= 1) { int local = 0; Report("T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), (void*)stack_bottom_, (void*)stack_top_, stack_top_ - stack_bottom_, &local); } - fake_stack_.Init(stack_size()); + fake_stack_ = 0; // Will be initialized lazily if needed. AsanPlatformThreadInit(); } -thread_return_t AsanThread::ThreadStart() { +thread_return_t AsanThread::ThreadStart(uptr os_id) { Init(); + asanThreadRegistry().StartThread(tid(), os_id, 0); if (flags()->use_sigaltstack) SetAlternateSignalStack(); if (!start_routine_) { // start_routine_ == 0 if we're on the main thread or on one of the // OS X libdispatch worker threads. But nobody is supposed to call // ThreadStart() for the worker threads. - CHECK(tid() == 0); + CHECK_EQ(tid(), 0); return 0; } @@ -105,24 +168,33 @@ thread_return_t AsanThread::ThreadStart() { return res; } -void AsanThread::SetThreadStackTopAndBottom() { - GetThreadStackTopAndBottom(tid() == 0, &stack_top_, &stack_bottom_); +void AsanThread::SetThreadStackAndTls() { + uptr tls_size = 0; + GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_, &tls_begin_, + &tls_size); + stack_top_ = stack_bottom_ + stack_size_; + tls_end_ = tls_begin_ + tls_size; + int local; CHECK(AddrIsInStack((uptr)&local)); } -void AsanThread::ClearShadowForThreadStack() { +void AsanThread::ClearShadowForThreadStackAndTLS() { PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); + if (tls_begin_ != tls_end_) + PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0); } -const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { +const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset, + uptr *frame_pc) { uptr bottom = 0; if (AddrIsInStack(addr)) { bottom = stack_bottom(); - } else { - bottom = fake_stack().AddrIsInFakeStack(addr); + } else if (has_fake_stack()) { + bottom = fake_stack()->AddrIsInFakeStack(addr); CHECK(bottom); *offset = addr - bottom; + *frame_pc = ((uptr*)bottom)[2]; return (const char *)((uptr*)bottom)[1]; } uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr. @@ -147,7 +219,104 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1)); CHECK(ptr[0] == kCurrentStackFrameMagic); *offset = addr - (uptr)ptr; + *frame_pc = ptr[2]; return (const char*)ptr[1]; } +static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base, + void *addr) { + AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base); + AsanThread *t = tctx->thread; + if (!t) return false; + if (t->AddrIsInStack((uptr)addr)) return true; + if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr)) + return true; + return false; +} + +AsanThread *GetCurrentThread() { + AsanThreadContext *context = + reinterpret_cast<AsanThreadContext *>(AsanTSDGet()); + if (!context) { + if (SANITIZER_ANDROID) { + // On Android, libc constructor is called _after_ asan_init, and cleans up + // TSD. Try to figure out if this is still the main thread by the stack + // address. We are not entirely sure that we have correct main thread + // limits, so only do this magic on Android, and only if the found thread + // is the main thread. + AsanThreadContext *tctx = GetThreadContextByTidLocked(0); + if (ThreadStackContainsAddress(tctx, &context)) { + SetCurrentThread(tctx->thread); + return tctx->thread; + } + } + return 0; + } + return context->thread; +} + +void SetCurrentThread(AsanThread *t) { + CHECK(t->context()); + if (flags()->verbosity >= 2) { + Report("SetCurrentThread: %p for thread %p\n", + t->context(), (void*)GetThreadSelf()); + } + // Make sure we do not reset the current AsanThread. + CHECK_EQ(0, AsanTSDGet()); + AsanTSDSet(t->context()); + CHECK_EQ(t->context(), AsanTSDGet()); +} + +u32 GetCurrentTidOrInvalid() { + AsanThread *t = GetCurrentThread(); + return t ? t->tid() : kInvalidTid; +} + +AsanThread *FindThreadByStackAddress(uptr addr) { + asanThreadRegistry().CheckLocked(); + AsanThreadContext *tctx = static_cast<AsanThreadContext *>( + asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress, + (void *)addr)); + return tctx ? tctx->thread : 0; +} + +void EnsureMainThreadIDIsCorrect() { + AsanThreadContext *context = + reinterpret_cast<AsanThreadContext *>(AsanTSDGet()); + if (context && (context->tid == 0)) + context->os_id = GetTid(); +} } // namespace __asan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, + uptr *tls_begin, uptr *tls_end, + uptr *cache_begin, uptr *cache_end) { + __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( + __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); + if (!context) return false; + __asan::AsanThread *t = context->thread; + if (!t) return false; + *stack_begin = t->stack_bottom(); + *stack_end = t->stack_top(); + *tls_begin = t->tls_begin(); + *tls_end = t->tls_end(); + // ASan doesn't keep allocator caches in TLS, so these are unused. + *cache_begin = 0; + *cache_end = 0; + return true; +} + +void LockThreadRegistry() { + __asan::asanThreadRegistry().Lock(); +} + +void UnlockThreadRegistry() { + __asan::asanThreadRegistry().Unlock(); +} + +void EnsureMainThreadIDIsCorrect() { + __asan::EnsureMainThreadIDIsCorrect(); +} +} // namespace __lsan diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h index f385ec35fcd..f21971ff430 100644 --- a/libsanitizer/asan/asan_thread.h +++ b/libsanitizer/asan/asan_thread.h @@ -14,99 +14,148 @@ #include "asan_allocator.h" #include "asan_internal.h" +#include "asan_fake_stack.h" #include "asan_stack.h" #include "asan_stats.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_thread_registry.h" namespace __asan { const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits. +const u32 kMaxNumberOfThreads = (1 << 22); // 4M class AsanThread; // These objects are created for every thread and are never deleted, // so we can find them by tid even if the thread is long dead. -class AsanThreadSummary { +class AsanThreadContext : public ThreadContextBase { public: - explicit AsanThreadSummary(LinkerInitialized) { } // for T0. - void Init(u32 parent_tid, StackTrace *stack) { - parent_tid_ = parent_tid; - announced_ = false; - tid_ = kInvalidTid; - if (stack) { - internal_memcpy(&stack_, stack, sizeof(*stack)); - } - thread_ = 0; - name_[0] = 0; + explicit AsanThreadContext(int tid) + : ThreadContextBase(tid), + announced(false), + thread(0) { + internal_memset(&stack, 0, sizeof(stack)); } - u32 tid() { return tid_; } - void set_tid(u32 tid) { tid_ = tid; } - u32 parent_tid() { return parent_tid_; } - bool announced() { return announced_; } - void set_announced(bool announced) { announced_ = announced; } - StackTrace *stack() { return &stack_; } - AsanThread *thread() { return thread_; } - void set_thread(AsanThread *thread) { thread_ = thread; } - static void TSDDtor(void *tsd); - void set_name(const char *name) { - internal_strncpy(name_, name, sizeof(name_) - 1); - } - const char *name() { return name_; } + bool announced; + StackTrace stack; + AsanThread *thread; - private: - u32 tid_; - u32 parent_tid_; - bool announced_; - StackTrace stack_; - AsanThread *thread_; - char name_[128]; + void OnCreated(void *arg); + void OnFinished(); }; -// AsanThreadSummary objects are never freed, so we need many of them. -COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094); +// AsanThreadContext objects are never freed, so we need many of them. +COMPILER_CHECK(sizeof(AsanThreadContext) <= 4096); // AsanThread are stored in TSD and destroyed when the thread dies. class AsanThread { public: - explicit AsanThread(LinkerInitialized); // for T0. - static AsanThread *Create(u32 parent_tid, thread_callback_t start_routine, - void *arg, StackTrace *stack); + static AsanThread *Create(thread_callback_t start_routine, void *arg); + static void TSDDtor(void *tsd); void Destroy(); void Init(); // Should be called from the thread itself. - thread_return_t ThreadStart(); + thread_return_t ThreadStart(uptr os_id); uptr stack_top() { return stack_top_; } uptr stack_bottom() { return stack_bottom_; } - uptr stack_size() { return stack_top_ - stack_bottom_; } - u32 tid() { return summary_->tid(); } - AsanThreadSummary *summary() { return summary_; } - void set_summary(AsanThreadSummary *summary) { summary_ = summary; } + uptr stack_size() { return stack_size_; } + uptr tls_begin() { return tls_begin_; } + uptr tls_end() { return tls_end_; } + u32 tid() { return context_->tid; } + AsanThreadContext *context() { return context_; } + void set_context(AsanThreadContext *context) { context_ = context; } - const char *GetFrameNameByAddr(uptr addr, uptr *offset); + const char *GetFrameNameByAddr(uptr addr, uptr *offset, uptr *frame_pc); bool AddrIsInStack(uptr addr) { return addr >= stack_bottom_ && addr < stack_top_; } - FakeStack &fake_stack() { return fake_stack_; } + void DeleteFakeStack() { + if (!fake_stack_) return; + FakeStack *t = fake_stack_; + fake_stack_ = 0; + SetTLSFakeStack(0); + t->Destroy(); + } + + bool has_fake_stack() { + return (reinterpret_cast<uptr>(fake_stack_) > 1); + } + + FakeStack *fake_stack() { + if (!__asan_option_detect_stack_use_after_return) + return 0; + if (!has_fake_stack()) + return AsyncSignalSafeLazyInitFakeStack(); + return fake_stack_; + } + + // True is this thread is currently unwinding stack (i.e. collecting a stack + // trace). Used to prevent deadlocks on platforms where libc unwinder calls + // malloc internally. See PR17116 for more details. + bool isUnwinding() const { return unwinding; } + void setUnwinding(bool b) { unwinding = b; } + AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } AsanStats &stats() { return stats_; } private: - void SetThreadStackTopAndBottom(); - void ClearShadowForThreadStack(); - AsanThreadSummary *summary_; + AsanThread() : unwinding(false) {} + void SetThreadStackAndTls(); + void ClearShadowForThreadStackAndTLS(); + FakeStack *AsyncSignalSafeLazyInitFakeStack(); + + AsanThreadContext *context_; thread_callback_t start_routine_; void *arg_; uptr stack_top_; uptr stack_bottom_; + // stack_size_ == stack_top_ - stack_bottom_; + // It needs to be set in a async-signal-safe manner. + uptr stack_size_; + uptr tls_begin_; + uptr tls_end_; - FakeStack fake_stack_; + FakeStack *fake_stack_; AsanThreadLocalMallocStorage malloc_storage_; AsanStats stats_; + bool unwinding; }; +// ScopedUnwinding is a scope for stacktracing member of a context +class ScopedUnwinding { + public: + explicit ScopedUnwinding(AsanThread *t) : thread(t) { + t->setUnwinding(true); + } + ~ScopedUnwinding() { thread->setUnwinding(false); } + + private: + AsanThread *thread; +}; + +struct CreateThreadContextArgs { + AsanThread *thread; + StackTrace *stack; +}; + +// Returns a single instance of registry. +ThreadRegistry &asanThreadRegistry(); + +// Must be called under ThreadRegistryLock. +AsanThreadContext *GetThreadContextByTidLocked(u32 tid); + +// Get the current thread. May return 0. +AsanThread *GetCurrentThread(); +void SetCurrentThread(AsanThread *t); +u32 GetCurrentTidOrInvalid(); +AsanThread *FindThreadByStackAddress(uptr addr); + +// Used to handle fork(). +void EnsureMainThreadIDIsCorrect(); } // namespace __asan #endif // ASAN_THREAD_H diff --git a/libsanitizer/asan/asan_thread_registry.cc b/libsanitizer/asan/asan_thread_registry.cc deleted file mode 100644 index 8fda9b6ea0a..00000000000 --- a/libsanitizer/asan/asan_thread_registry.cc +++ /dev/null @@ -1,196 +0,0 @@ -//===-- asan_thread_registry.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. -// -// AsanThreadRegistry-related code. AsanThreadRegistry is a container -// for summaries of all created threads. -//===----------------------------------------------------------------------===// - -#include "asan_stack.h" -#include "asan_thread.h" -#include "asan_thread_registry.h" -#include "sanitizer_common/sanitizer_common.h" - -namespace __asan { - -static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED); - -AsanThreadRegistry &asanThreadRegistry() { - return asan_thread_registry; -} - -AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x) - : main_thread_(x), - main_thread_summary_(x), - accumulated_stats_(x), - max_malloced_memory_(x), - mu_(x) { } - -void AsanThreadRegistry::Init() { - AsanTSDInit(AsanThreadSummary::TSDDtor); - main_thread_.set_summary(&main_thread_summary_); - main_thread_summary_.set_thread(&main_thread_); - RegisterThread(&main_thread_); - SetCurrent(&main_thread_); - // At this point only one thread exists. - inited_ = true; -} - -void AsanThreadRegistry::RegisterThread(AsanThread *thread) { - BlockingMutexLock lock(&mu_); - u32 tid = n_threads_; - n_threads_++; - CHECK(n_threads_ < kMaxNumberOfThreads); - - AsanThreadSummary *summary = thread->summary(); - CHECK(summary != 0); - summary->set_tid(tid); - thread_summaries_[tid] = summary; -} - -void AsanThreadRegistry::UnregisterThread(AsanThread *thread) { - BlockingMutexLock lock(&mu_); - FlushToAccumulatedStatsUnlocked(&thread->stats()); - AsanThreadSummary *summary = thread->summary(); - CHECK(summary); - summary->set_thread(0); -} - -AsanThread *AsanThreadRegistry::GetMain() { - return &main_thread_; -} - -AsanThread *AsanThreadRegistry::GetCurrent() { - AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet(); - if (!summary) { -#if ASAN_ANDROID - // On Android, libc constructor is called _after_ asan_init, and cleans up - // TSD. Try to figure out if this is still the main thread by the stack - // address. We are not entirely sure that we have correct main thread - // limits, so only do this magic on Android, and only if the found thread is - // the main thread. - AsanThread* thread = FindThreadByStackAddress((uptr)&summary); - if (thread && thread->tid() == 0) { - SetCurrent(thread); - return thread; - } -#endif - return 0; - } - return summary->thread(); -} - -void AsanThreadRegistry::SetCurrent(AsanThread *t) { - CHECK(t->summary()); - if (flags()->verbosity >= 2) { - Report("SetCurrent: %p for thread %p\n", - t->summary(), (void*)GetThreadSelf()); - } - // Make sure we do not reset the current AsanThread. - CHECK(AsanTSDGet() == 0); - AsanTSDSet(t->summary()); - CHECK(AsanTSDGet() == t->summary()); -} - -AsanStats &AsanThreadRegistry::GetCurrentThreadStats() { - AsanThread *t = GetCurrent(); - return (t) ? t->stats() : main_thread_.stats(); -} - -void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_)); -} - -uptr AsanThreadRegistry::GetCurrentAllocatedBytes() { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - uptr malloced = accumulated_stats_.malloced; - uptr freed = accumulated_stats_.freed; - // Return sane value if malloced < freed due to racy - // way we update accumulated stats. - return (malloced > freed) ? malloced - freed : 1; -} - -uptr AsanThreadRegistry::GetHeapSize() { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - return accumulated_stats_.mmaped - accumulated_stats_.munmaped; -} - -uptr AsanThreadRegistry::GetFreeBytes() { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - uptr total_free = accumulated_stats_.mmaped - - accumulated_stats_.munmaped - + accumulated_stats_.really_freed - + accumulated_stats_.really_freed_redzones; - uptr total_used = accumulated_stats_.malloced - + accumulated_stats_.malloced_redzones; - // Return sane value if total_free < total_used due to racy - // way we update accumulated stats. - return (total_free > total_used) ? total_free - total_used : 1; -} - -// Return several stats counters with a single call to -// UpdateAccumulatedStatsUnlocked(). -void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats *malloc_stats) { - BlockingMutexLock lock(&mu_); - UpdateAccumulatedStatsUnlocked(); - malloc_stats->blocks_in_use = accumulated_stats_.mallocs; - malloc_stats->size_in_use = accumulated_stats_.malloced; - malloc_stats->max_size_in_use = max_malloced_memory_; - malloc_stats->size_allocated = accumulated_stats_.mmaped; -} - -AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) { - CHECK(tid < n_threads_); - CHECK(thread_summaries_[tid]); - return thread_summaries_[tid]; -} - -AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) { - BlockingMutexLock lock(&mu_); - for (u32 tid = 0; tid < n_threads_; tid++) { - AsanThread *t = thread_summaries_[tid]->thread(); - if (!t || !(t->fake_stack().StackSize())) continue; - if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) { - return t; - } - } - return 0; -} - -void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() { - for (u32 tid = 0; tid < n_threads_; tid++) { - AsanThread *t = thread_summaries_[tid]->thread(); - if (t != 0) { - FlushToAccumulatedStatsUnlocked(&t->stats()); - } - } - // This is not very accurate: we may miss allocation peaks that happen - // between two updates of accumulated_stats_. For more accurate bookkeeping - // the maximum should be updated on every malloc(), which is unacceptable. - if (max_malloced_memory_ < accumulated_stats_.malloced) { - max_malloced_memory_ = accumulated_stats_.malloced; - } -} - -void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) { - // AsanStats consists of variables of type uptr only. - uptr *dst = (uptr*)&accumulated_stats_; - uptr *src = (uptr*)stats; - uptr num_fields = sizeof(AsanStats) / sizeof(uptr); - for (uptr i = 0; i < num_fields; i++) { - dst[i] += src[i]; - src[i] = 0; - } -} - -} // namespace __asan diff --git a/libsanitizer/asan/asan_thread_registry.h b/libsanitizer/asan/asan_thread_registry.h deleted file mode 100644 index 8c3d0c886e0..00000000000 --- a/libsanitizer/asan/asan_thread_registry.h +++ /dev/null @@ -1,83 +0,0 @@ -//===-- asan_thread_registry.h ----------------------------------*- C++ -*-===// -// -// 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. -// -// ASan-private header for asan_thread_registry.cc -//===----------------------------------------------------------------------===// - -#ifndef ASAN_THREAD_REGISTRY_H -#define ASAN_THREAD_REGISTRY_H - -#include "asan_stack.h" -#include "asan_stats.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_mutex.h" - -namespace __asan { - -// Stores summaries of all created threads, returns current thread, -// thread by tid, thread by stack address. There is a single instance -// of AsanThreadRegistry for the whole program. -// AsanThreadRegistry is thread-safe. -class AsanThreadRegistry { - public: - explicit AsanThreadRegistry(LinkerInitialized); - void Init(); - void RegisterThread(AsanThread *thread); - void UnregisterThread(AsanThread *thread); - - AsanThread *GetMain(); - // Get the current thread. May return 0. - AsanThread *GetCurrent(); - void SetCurrent(AsanThread *t); - - u32 GetCurrentTidOrInvalid() { - if (!inited_) return 0; - AsanThread *t = GetCurrent(); - return t ? t->tid() : kInvalidTid; - } - - // Returns stats for GetCurrent(), or stats for - // T0 if GetCurrent() returns 0. - AsanStats &GetCurrentThreadStats(); - // Flushes all thread-local stats to accumulated stats, and makes - // a copy of accumulated stats. - void GetAccumulatedStats(AsanStats *stats); - uptr GetCurrentAllocatedBytes(); - uptr GetHeapSize(); - uptr GetFreeBytes(); - void FillMallocStatistics(AsanMallocStats *malloc_stats); - - AsanThreadSummary *FindByTid(u32 tid); - AsanThread *FindThreadByStackAddress(uptr addr); - - private: - void UpdateAccumulatedStatsUnlocked(); - // Adds values of all counters in "stats" to accumulated stats, - // and fills "stats" with zeroes. - void FlushToAccumulatedStatsUnlocked(AsanStats *stats); - - static const u32 kMaxNumberOfThreads = (1 << 22); // 4M - AsanThreadSummary *thread_summaries_[kMaxNumberOfThreads]; - AsanThread main_thread_; - AsanThreadSummary main_thread_summary_; - AsanStats accumulated_stats_; - // Required for malloc_zone_statistics() on OS X. This can't be stored in - // per-thread AsanStats. - uptr max_malloced_memory_; - u32 n_threads_; - BlockingMutex mu_; - bool inited_; -}; - -// Returns a single instance of registry. -AsanThreadRegistry &asanThreadRegistry(); - -} // namespace __asan - -#endif // ASAN_THREAD_REGISTRY_H diff --git a/libsanitizer/asan/asan_win.cc b/libsanitizer/asan/asan_win.cc index 6acfeebc8bf..ed785b69281 100644 --- a/libsanitizer/asan/asan_win.cc +++ b/libsanitizer/asan/asan_win.cc @@ -9,7 +9,9 @@ // // Windows-specific details. //===----------------------------------------------------------------------===// -#ifdef _WIN32 + +#include "sanitizer_common/sanitizer_platform.h" +#if SANITIZER_WINDOWS #include <windows.h> #include <dbghelp.h> @@ -21,6 +23,14 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" +extern "C" { + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_should_detect_stack_use_after_return() { + __asan_init(); + return __asan_option_detect_stack_use_after_return; + } +} + namespace __asan { // ---------------------- Stacktraces, symbols, etc. ---------------- {{{1 @@ -28,30 +38,6 @@ static BlockingMutex dbghelp_lock(LINKER_INITIALIZED); static bool dbghelp_initialized = false; #pragma comment(lib, "dbghelp.lib") -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) { - (void)fast; - stack->max_size = max_s; - void *tmp[kStackTraceMax]; - - // FIXME: CaptureStackBackTrace might be too slow for us. - // FIXME: Compare with StackWalk64. - // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc - uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0); - uptr offset = 0; - // Skip the RTL frames by searching for the PC in the stacktrace. - // FIXME: this doesn't work well for the malloc/free stacks yet. - for (uptr i = 0; i < cs_ret; i++) { - if (pc != (uptr)tmp[i]) - continue; - offset = i; - break; - } - - stack->size = cs_ret - offset; - for (uptr i = 0; i < stack->size; i++) - stack->trace[i] = (uptr)tmp[i + offset]; -} - // ---------------------- TSD ---------------- {{{1 static bool tsd_key_inited = false; diff --git a/libsanitizer/asan/libtool-version b/libsanitizer/asan/libtool-version index 204fdd2d8e5..9a16cf57844 100644 --- a/libsanitizer/asan/libtool-version +++ b/libsanitizer/asan/libtool-version @@ -1,6 +1,6 @@ -# This file is used to maintain libtool version info for libmudflap. See +# This file is used to maintain libtool version info for libasan. See # the libtool manual to understand the meaning of the fields. This is # a separate file so that version updates don't involve re-running # automake. # CURRENT:REVISION:AGE -0:0:0 +1:0:0 |