summaryrefslogtreecommitdiff
path: root/libsanitizer
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2013-01-23 11:41:33 +0000
committerKostya Serebryany <kcc@gcc.gnu.org>2013-01-23 11:41:33 +0000
commit2660d12d0a69539959dc1f77648f9f29f5e0edf8 (patch)
tree80989bd161e60d01560788cb7427eb644b227884 /libsanitizer
parentb39968989d6ae4289c01202c45268b5651d1c222 (diff)
downloadgcc-2660d12d0a69539959dc1f77648f9f29f5e0edf8.tar.gz
libsanitizer merge from upstream r173241
From-SVN: r195404
Diffstat (limited to 'libsanitizer')
-rw-r--r--libsanitizer/ChangeLog6
-rw-r--r--libsanitizer/MERGE2
-rw-r--r--libsanitizer/asan/asan_allocator.cc18
-rw-r--r--libsanitizer/asan/asan_allocator.h20
-rw-r--r--libsanitizer/asan/asan_allocator2.cc130
-rw-r--r--libsanitizer/asan/asan_globals.cc32
-rw-r--r--libsanitizer/asan/asan_intercepted_functions.h70
-rw-r--r--libsanitizer/asan/asan_interceptors.cc65
-rw-r--r--libsanitizer/asan/asan_internal.h2
-rw-r--r--libsanitizer/asan/asan_linux.cc35
-rw-r--r--libsanitizer/asan/asan_lock.h39
-rw-r--r--libsanitizer/asan/asan_mac.cc116
-rw-r--r--libsanitizer/asan/asan_malloc_mac.cc280
-rw-r--r--libsanitizer/asan/asan_mapping.h4
-rw-r--r--libsanitizer/asan/asan_new_delete.cc5
-rw-r--r--libsanitizer/asan/asan_poisoning.cc2
-rw-r--r--libsanitizer/asan/asan_rtl.cc8
-rw-r--r--libsanitizer/asan/asan_stats.cc5
-rw-r--r--libsanitizer/asan/asan_thread.cc2
-rw-r--r--libsanitizer/asan/asan_thread_registry.cc16
-rw-r--r--libsanitizer/asan/asan_thread_registry.h4
-rw-r--r--libsanitizer/asan/asan_win.cc46
-rw-r--r--libsanitizer/asan/dynamic/asan_interceptors_dynamic.cc14
-rw-r--r--libsanitizer/include/sanitizer/asan_interface.h5
-rw-r--r--libsanitizer/interception/interception.h2
-rwxr-xr-xlibsanitizer/merge.sh2
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_allocator.h358
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_atomic_clang.h2
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h2
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common.cc38
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common_interceptors.h77
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc222
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc144
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_internal_defs.h7
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_lfstack.h71
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_linux.cc68
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_list.h4
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_mac.cc20
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_mutex.h17
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h21
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_quarantine.h170
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer.cc190
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer.h11
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_win.cc38
-rw-r--r--libsanitizer/tsan/tsan_fd.cc6
-rw-r--r--libsanitizer/tsan/tsan_fd.h1
-rw-r--r--libsanitizer/tsan/tsan_interceptors.cc89
-rw-r--r--libsanitizer/tsan/tsan_mman.cc1
-rw-r--r--libsanitizer/tsan/tsan_report.cc9
-rw-r--r--libsanitizer/tsan/tsan_report.h1
-rw-r--r--libsanitizer/tsan/tsan_rtl_mutex.cc1
-rw-r--r--libsanitizer/tsan/tsan_rtl_report.cc53
-rw-r--r--libsanitizer/tsan/tsan_stat.cc7
-rw-r--r--libsanitizer/tsan/tsan_stat.h7
-rw-r--r--libsanitizer/tsan/tsan_symbolize.cc50
-rw-r--r--libsanitizer/tsan/tsan_symbolize.h3
56 files changed, 1559 insertions, 1059 deletions
diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog
index cb6cac2b211..e52cbea5439 100644
--- a/libsanitizer/ChangeLog
+++ b/libsanitizer/ChangeLog
@@ -1,3 +1,9 @@
+2013-01-23 Kostya Serebryany <kcc@google.com>
+
+ PR sanitizer/55989
+ * All source files: Merge from upstream r173241.
+ * merge.sh: Support merging .inc files.
+
2013-01-16 Jakub Jelinek <jakub@redhat.com>
* sanitizer_common/Makefile.am (AM_CXXFLAGS): Remove
diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE
index ff637c242ba..c8e733f1594 100644
--- a/libsanitizer/MERGE
+++ b/libsanitizer/MERGE
@@ -1,4 +1,4 @@
-171973
+173241
The first line of this file holds the svn revision number of the
last merge done from the master library sources.
diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc
index b170fe723d2..f01d2db9dbc 100644
--- a/libsanitizer/asan/asan_allocator.cc
+++ b/libsanitizer/asan/asan_allocator.cc
@@ -27,7 +27,6 @@
#if ASAN_ALLOCATOR_VERSION == 1
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_mapping.h"
#include "asan_stats.h"
#include "asan_report.h"
@@ -35,6 +34,7 @@
#include "asan_thread_registry.h"
#include "sanitizer/asan_interface.h"
#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
@@ -227,7 +227,7 @@ class MallocInfo {
AsanChunk *m = 0;
AsanChunk **fl = &free_lists_[size_class];
{
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
for (uptr i = 0; i < n_chunks; i++) {
if (!(*fl)) {
*fl = GetNewChunks(size_class);
@@ -245,7 +245,7 @@ class MallocInfo {
void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x,
bool eat_free_lists) {
CHECK(flags()->quarantine_size > 0);
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
AsanChunkFifoList *q = &x->quarantine_;
if (q->size() > 0) {
quarantine_.PushList(q);
@@ -269,18 +269,18 @@ class MallocInfo {
}
void BypassThreadLocalQuarantine(AsanChunk *chunk) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
quarantine_.Push(chunk);
}
AsanChunk *FindChunkByAddr(uptr addr) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
return FindChunkByAddrUnlocked(addr);
}
uptr AllocationSize(uptr ptr) {
if (!ptr) return 0;
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
// Make sure this is our chunk and |ptr| actually points to the beginning
// of the allocated memory.
@@ -303,7 +303,7 @@ class MallocInfo {
}
void PrintStatus() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
uptr malloced = 0;
Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ",
@@ -321,7 +321,7 @@ class MallocInfo {
}
PageGroup *FindPageGroup(uptr addr) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
return FindPageGroupUnlocked(addr);
}
@@ -479,7 +479,7 @@ class MallocInfo {
AsanChunk *free_lists_[kNumberOfSizeClasses];
AsanChunkFifoList quarantine_;
- AsanLock mu_;
+ BlockingMutex mu_;
PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize];
atomic_uint32_t n_page_groups_;
diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h
index 4ade352a3e5..cc16ce85497 100644
--- a/libsanitizer/asan/asan_allocator.h
+++ b/libsanitizer/asan/asan_allocator.h
@@ -20,8 +20,14 @@
// 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 macrozz.
-#define ASAN_ALLOCATOR_VERSION 1
+// at build time by redefining this macro.
+#ifndef ASAN_ALLOCATOR_VERSION
+# if ASAN_LINUX && !ASAN_ANDROID
+# define ASAN_ALLOCATOR_VERSION 2
+# else
+# define ASAN_ALLOCATOR_VERSION 1
+# endif
+#endif // ASAN_ALLOCATOR_VERSION
namespace __asan {
@@ -96,17 +102,21 @@ class AsanChunkFifoList: public IntrusiveList<AsanChunk> {
struct AsanThreadLocalMallocStorage {
explicit AsanThreadLocalMallocStorage(LinkerInitialized x)
- : quarantine_(x) { }
+#if ASAN_ALLOCATOR_VERSION == 1
+ : quarantine_(x)
+#endif
+ { }
AsanThreadLocalMallocStorage() {
CHECK(REAL(memset));
REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage));
}
- AsanChunkFifoList quarantine_;
#if ASAN_ALLOCATOR_VERSION == 1
+ AsanChunkFifoList quarantine_;
AsanChunk *free_lists_[kNumberOfSizeClasses];
#else
- uptr allocator2_cache[1024]; // Opaque.
+ uptr quarantine_cache[16];
+ uptr allocator2_cache[96 * (512 * 8 + 16)]; // Opaque.
#endif
void CommitBack();
};
diff --git a/libsanitizer/asan/asan_allocator2.cc b/libsanitizer/asan/asan_allocator2.cc
index d12ccb7f23b..4aa5141f2a7 100644
--- a/libsanitizer/asan/asan_allocator2.cc
+++ b/libsanitizer/asan/asan_allocator2.cc
@@ -25,6 +25,7 @@
#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"
namespace __asan {
@@ -90,15 +91,6 @@ static const uptr kMaxThreadLocalQuarantine =
static const uptr kReturnOnZeroMalloc = 2048; // Zero page is protected.
-static int inited = 0;
-
-static void Init() {
- if (inited) return;
- __asan_init();
- inited = true; // this must happen before any threads are created.
- allocator.Init();
-}
-
// Every chunk of memory allocated by this allocator can be in one of 3 states:
// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
@@ -244,31 +236,26 @@ void AsanChunkView::GetFreeStack(StackTrace *stack) {
chunk_->FreeStackSize());
}
-class Quarantine: public AsanChunkFifoList {
- public:
- void SwallowThreadLocalQuarantine(AsanThreadLocalMallocStorage *ms) {
- AsanChunkFifoList *q = &ms->quarantine_;
- if (!q->size()) return;
- SpinMutexLock l(&mutex_);
- PushList(q);
- PopAndDeallocateLoop(ms);
- }
+struct QuarantineCallback;
+typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine;
+typedef AsanQuarantine::Cache QuarantineCache;
+static AsanQuarantine quarantine(LINKER_INITIALIZED);
+static QuarantineCache fallback_quarantine_cache(LINKER_INITIALIZED);
+static AllocatorCache fallback_allocator_cache;
+static SpinMutex fallback_mutex;
- void BypassThreadLocalQuarantine(AsanChunk *m) {
- SpinMutexLock l(&mutex_);
- Push(m);
- }
+QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
+ CHECK(ms);
+ CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache));
+ return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache);
+}
- private:
- void PopAndDeallocateLoop(AsanThreadLocalMallocStorage *ms) {
- while (size() > (uptr)flags()->quarantine_size) {
- PopAndDeallocate(ms);
- }
+struct QuarantineCallback {
+ explicit QuarantineCallback(AllocatorCache *cache)
+ : cache_(cache) {
}
- void PopAndDeallocate(AsanThreadLocalMallocStorage *ms) {
- CHECK_GT(size(), 0);
- AsanChunk *m = Pop();
- CHECK(m);
+
+ void Recycle(AsanChunk *m) {
CHECK(m->chunk_state == CHUNK_QUARANTINE);
m->chunk_state = CHUNK_AVAILABLE;
CHECK_NE(m->alloc_tid, kInvalidTid);
@@ -288,34 +275,27 @@ class Quarantine: public AsanChunkFifoList {
thread_stats.real_frees++;
thread_stats.really_freed += m->UsedSize();
- allocator.Deallocate(GetAllocatorCache(ms), p);
+ allocator.Deallocate(cache_, p);
}
- SpinMutex mutex_;
-};
-static Quarantine quarantine;
+ void *Allocate(uptr size) {
+ return allocator.Allocate(cache_, size, 1, false);
+ }
-void AsanChunkFifoList::PushList(AsanChunkFifoList *q) {
- CHECK(q->size() > 0);
- size_ += q->size();
- append_back(q);
- q->clear();
-}
+ void Deallocate(void *p) {
+ allocator.Deallocate(cache_, p);
+ }
-void AsanChunkFifoList::Push(AsanChunk *n) {
- push_back(n);
- size_ += n->UsedSize();
-}
+ AllocatorCache *cache_;
+};
-// Interesting performance observation: this function takes up to 15% of overal
-// allocator time. That's because *first_ has been evicted from cache long time
-// ago. Not sure if we can or want to do anything with this.
-AsanChunk *AsanChunkFifoList::Pop() {
- CHECK(first_);
- AsanChunk *res = front();
- size_ -= res->UsedSize();
- pop_front();
- return res;
+static void Init() {
+ static int inited = 0;
+ if (inited) return;
+ __asan_init();
+ inited = true; // this must happen before any threads are created.
+ allocator.Init();
+ quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine);
}
static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
@@ -355,9 +335,18 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
}
AsanThread *t = asanThreadRegistry().GetCurrent();
- AllocatorCache *cache = t ? GetAllocatorCache(&t->malloc_storage()) : 0;
- void *allocated = allocator.Allocate(cache, needed_size, 8, false);
+ void *allocated;
+ if (t) {
+ AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
+ allocated = allocator.Allocate(cache, needed_size, 8, false);
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *cache = &fallback_allocator_cache;
+ 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;
@@ -432,7 +421,7 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
// 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);
+ memory_order_relaxed);
if (old_chunk_state == CHUNK_QUARANTINE)
ReportDoubleFree((uptr)ptr, stack);
@@ -466,13 +455,15 @@ static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
// Push into quarantine.
if (t) {
- AsanChunkFifoList &q = t->malloc_storage().quarantine_;
- q.Push(m);
-
- if (q.size() > kMaxThreadLocalQuarantine)
- quarantine.SwallowThreadLocalQuarantine(&t->malloc_storage());
+ AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
+ AllocatorCache *ac = GetAllocatorCache(ms);
+ quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac),
+ m, m->UsedSize());
} else {
- quarantine.BypassThreadLocalQuarantine(m);
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *ac = &fallback_allocator_cache;
+ quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac),
+ m, m->UsedSize());
}
ASAN_FREE_HOOK(ptr);
@@ -584,7 +575,8 @@ AsanChunkView FindHeapChunkByAddress(uptr addr) {
}
void AsanThreadLocalMallocStorage::CommitBack() {
- quarantine.SwallowThreadLocalQuarantine(this);
+ AllocatorCache *ac = GetAllocatorCache(this);
+ quarantine.Drain(GetQuarantineCache(this), QuarantineCallback(ac));
allocator.SwallowCache(GetAllocatorCache(this));
}
@@ -681,16 +673,18 @@ uptr __asan_get_estimated_allocated_size(uptr size) {
}
bool __asan_get_ownership(const void *p) {
- return AllocationSize(reinterpret_cast<uptr>(p)) > 0;
+ uptr ptr = reinterpret_cast<uptr>(p);
+ return (ptr == kReturnOnZeroMalloc) || (AllocationSize(ptr) > 0);
}
uptr __asan_get_allocated_size(const void *p) {
if (p == 0) return 0;
- uptr allocated_size = AllocationSize(reinterpret_cast<uptr>(p));
+ uptr ptr = reinterpret_cast<uptr>(p);
+ uptr allocated_size = AllocationSize(ptr);
// Die if p is not malloced or if it is already freed.
- if (allocated_size == 0) {
+ if (allocated_size == 0 && ptr != kReturnOnZeroMalloc) {
GET_STACK_TRACE_FATAL_HERE;
- ReportAsanGetAllocatedSizeNotOwned(reinterpret_cast<uptr>(p), &stack);
+ ReportAsanGetAllocatedSizeNotOwned(ptr, &stack);
}
return allocated_size;
}
diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc
index b195a9091b3..88aeefa8fe5 100644
--- a/libsanitizer/asan/asan_globals.cc
+++ b/libsanitizer/asan/asan_globals.cc
@@ -11,13 +11,13 @@
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
@@ -28,7 +28,7 @@ struct ListOfGlobals {
ListOfGlobals *next;
};
-static AsanLock mu_for_globals(LINKER_INITIALIZED);
+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;
@@ -53,14 +53,9 @@ void PoisonRedZones(const Global &g) {
}
}
-static uptr GetAlignedSize(uptr size) {
- return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone)
- * kGlobalAndStackRedzone;
-}
-
bool DescribeAddressIfGlobal(uptr addr) {
if (!flags()->report_globals) return false;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
bool res = false;
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
const Global &g = *l->g;
@@ -140,23 +135,10 @@ static void UnpoisonGlobal(const Global *g) {
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
-// Register one global with a default redzone.
-void __asan_register_global(uptr addr, uptr size,
- const char *name) {
- if (!flags()->report_globals) return;
- ScopedLock lock(&mu_for_globals);
- Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global));
- g->beg = addr;
- g->size = size;
- g->size_with_redzone = GetAlignedSize(size) + kGlobalAndStackRedzone;
- g->name = name;
- RegisterGlobal(g);
-}
-
// Register an array of globals.
void __asan_register_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
for (uptr i = 0; i < n; i++) {
RegisterGlobal(&globals[i]);
}
@@ -166,7 +148,7 @@ void __asan_register_globals(__asan_global *globals, uptr n) {
// We must do this when a shared objects gets dlclosed.
void __asan_unregister_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
for (uptr i = 0; i < n; i++) {
UnregisterGlobal(&globals[i]);
}
@@ -179,7 +161,7 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) {
void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) {
if (!flags()->check_initialization_order) return;
CHECK(list_of_dynamic_init_globals);
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
bool from_current_tu = false;
// The list looks like:
// a => ... => b => last_addr => ... => first_addr => c => ...
@@ -200,7 +182,7 @@ void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) {
// TU are poisoned. It simply unpoisons all dynamically initialized globals.
void __asan_after_dynamic_init() {
if (!flags()->check_initialization_order) return;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next)
UnpoisonGlobal(l->g);
}
diff --git a/libsanitizer/asan/asan_intercepted_functions.h b/libsanitizer/asan/asan_intercepted_functions.h
index 8341bc65745..2d678ab7000 100644
--- a/libsanitizer/asan/asan_intercepted_functions.h
+++ b/libsanitizer/asan/asan_intercepted_functions.h
@@ -16,6 +16,8 @@
#include "interception/interception.h"
#include "sanitizer_common/sanitizer_platform_interceptors.h"
+#include <stdarg.h>
+
using __sanitizer::uptr;
// Use macro to describe if specific function should be
@@ -40,10 +42,8 @@ using __sanitizer::uptr;
#if defined(__linux__)
# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1
-# define ASAN_INTERCEPT_PRCTL 1
#else
# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0
-# define ASAN_INTERCEPT_PRCTL 0
#endif
#if !defined(__APPLE__)
@@ -105,7 +105,7 @@ 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
+# endif
// string.h / strings.h
DECLARE_FUNCTION_AND_WRAPPER(int, memcmp,
@@ -139,9 +139,9 @@ DECLARE_FUNCTION_AND_WRAPPER(char*, strdup, const char *s);
# if ASAN_INTERCEPT_STRNLEN
DECLARE_FUNCTION_AND_WRAPPER(uptr, strnlen, const char *s, uptr maxlen);
# endif
-#if ASAN_INTERCEPT_INDEX
+# if ASAN_INTERCEPT_INDEX
DECLARE_FUNCTION_AND_WRAPPER(char*, index, const char *string, int c);
-#endif
+# endif
// stdlib.h
DECLARE_FUNCTION_AND_WRAPPER(int, atoi, const char *nptr);
@@ -165,6 +165,13 @@ 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);
+# endif
+
# if ASAN_INTERCEPT_MLOCKX
// mlock/munlock
DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, SIZE_T len);
@@ -186,7 +193,18 @@ DECLARE_FUNCTION_AND_WRAPPER(int, pthread_create,
void *(*start_routine)(void*), void *arg);
# endif
-#if defined(__APPLE__)
+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, ...);
+
+# if defined(__APPLE__)
typedef void* pthread_workqueue_t;
typedef void* pthread_workitem_handle_t;
@@ -196,8 +214,6 @@ typedef void* dispatch_source_t;
typedef u64 dispatch_time_t;
typedef void (*dispatch_function_t)(void *block);
typedef void* (*worker_t)(void *block);
-typedef void* CFStringRef;
-typedef void* CFAllocatorRef;
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async_f,
dispatch_queue_t dq,
@@ -215,11 +231,7 @@ DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f,
dispatch_group_t group, dispatch_queue_t dq,
void *ctxt, dispatch_function_t func);
-DECLARE_FUNCTION_AND_WRAPPER(void, __CFInitialize, void);
-DECLARE_FUNCTION_AND_WRAPPER(CFStringRef, CFStringCreateCopy,
- CFAllocatorRef alloc, CFStringRef str);
-DECLARE_FUNCTION_AND_WRAPPER(void, free, void* ptr);
-#if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT)
+# if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT)
DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async,
dispatch_group_t dg,
dispatch_queue_t dq, void (^work)(void));
@@ -231,9 +243,35 @@ 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 // MAC_INTERPOSE_FUNCTIONS
-#endif // __APPLE__
+# endif // MAC_INTERPOSE_FUNCTIONS
+
+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);
+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);
+
+
+
+# endif // __APPLE__
} // extern "C"
-#endif
+#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 26daee1727c..98329f38e61 100644
--- a/libsanitizer/asan/asan_interceptors.cc
+++ b/libsanitizer/asan/asan_interceptors.cc
@@ -73,15 +73,30 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
return internal_strnlen(s, maxlen);
}
+void SetThreadName(const char *name) {
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (t)
+ t->summary()->set_name(name);
+}
+
} // namespace __asan
// ---------------------- Wrappers ---------------- {{{1
using namespace __asan; // NOLINT
-#define COMMON_INTERCEPTOR_WRITE_RANGE(ptr, size) ASAN_WRITE_RANGE(ptr, size)
-#define COMMON_INTERCEPTOR_READ_RANGE(ptr, size) ASAN_READ_RANGE(ptr, size)
-#define COMMON_INTERCEPTOR_ENTER(func, ...) ENSURE_ASAN_INITED()
-#include "sanitizer_common/sanitizer_common_interceptors.h"
+#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(); \
+ } 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)
+#include "sanitizer_common/sanitizer_common_interceptors.inc"
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
AsanThread *t = (AsanThread*)arg;
@@ -122,6 +137,18 @@ DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act,
#endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
#if ASAN_INTERCEPT_SWAPCONTEXT
+static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) {
+ // Align to page size.
+ uptr PageSize = GetPageSizeCached();
+ uptr bottom = stack & ~(PageSize - 1);
+ ssize += stack - bottom;
+ ssize = RoundUpTo(ssize, PageSize);
+ static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb
+ if (ssize && ssize <= kMaxSaneContextStackSize) {
+ PoisonShadow(bottom, ssize, 0);
+ }
+}
+
INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp,
struct ucontext_t *ucp) {
static bool reported_warning = false;
@@ -132,16 +159,18 @@ INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp,
}
// Clear shadow memory for new context (it may share stack
// with current context).
- ClearShadowMemoryForContext(ucp);
+ uptr stack, ssize;
+ ReadContextStack(ucp, &stack, &ssize);
+ ClearShadowMemoryForContextStack(stack, ssize);
int res = REAL(swapcontext)(oucp, ucp);
// swapcontext technically does not return, but program may swap context to
// "oucp" later, that would look as if swapcontext() returned 0.
// We need to clear shadow for ucp once again, as it may be in arbitrary
// state.
- ClearShadowMemoryForContext(ucp);
+ ClearShadowMemoryForContextStack(stack, ssize);
return res;
}
-#endif
+#endif // ASAN_INTERCEPT_SWAPCONTEXT
INTERCEPTOR(void, longjmp, void *env, int val) {
__asan_handle_no_return();
@@ -162,25 +191,6 @@ INTERCEPTOR(void, siglongjmp, void *env, int val) {
}
#endif
-#if ASAN_INTERCEPT_PRCTL
-#define PR_SET_NAME 15
-INTERCEPTOR(int, prctl, int option,
- unsigned long arg2, unsigned long arg3, // NOLINT
- unsigned long arg4, unsigned long arg5) { // NOLINT
- int res = REAL(prctl(option, arg2, arg3, arg4, arg5));
- if (option == PR_SET_NAME) {
- AsanThread *t = asanThreadRegistry().GetCurrent();
- if (t) {
- char buff[17];
- internal_strncpy(buff, (char*)arg2, 16);
- buff[16] = 0;
- t->summary()->set_name(buff);
- }
- }
- return res;
-}
-#endif
-
#if ASAN_INTERCEPT___CXA_THROW
INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
CHECK(REAL(__cxa_throw));
@@ -727,9 +737,6 @@ void InitializeAsanInterceptors() {
#if ASAN_INTERCEPT_SIGLONGJMP
ASAN_INTERCEPT_FUNC(siglongjmp);
#endif
-#if ASAN_INTERCEPT_PRCTL
- ASAN_INTERCEPT_FUNC(prctl);
-#endif
// Intercept exception handling functions.
#if ASAN_INTERCEPT___CXA_THROW
diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h
index 1717fce66fb..a9c6c0f9022 100644
--- a/libsanitizer/asan/asan_internal.h
+++ b/libsanitizer/asan/asan_internal.h
@@ -114,7 +114,7 @@ bool AsanInterceptsSignal(int signum);
void SetAlternateSignalStack();
void UnsetAlternateSignalStack();
void InstallSignalHandlers();
-void ClearShadowMemoryForContext(void *context);
+void ReadContextStack(void *context, uptr *stack, uptr *ssize);
void AsanPlatformThreadInit();
// Wrapper for TLS/TSD.
diff --git a/libsanitizer/asan/asan_linux.cc b/libsanitizer/asan/asan_linux.cc
index 0e6c6280f0b..a030fcd3972 100644
--- a/libsanitizer/asan/asan_linux.cc
+++ b/libsanitizer/asan/asan_linux.cc
@@ -13,7 +13,6 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_thread.h"
#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_libc.h"
@@ -100,26 +99,6 @@ void AsanPlatformThreadInit() {
// Nothing here for now.
}
-AsanLock::AsanLock(LinkerInitialized) {
- // We assume that pthread_mutex_t initialized to all zeroes is a valid
- // unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers
- // a gcc warning:
- // extended initializer lists only available with -std=c++0x or -std=gnu++0x
-}
-
-void AsanLock::Lock() {
- CHECK(sizeof(pthread_mutex_t) <= sizeof(opaque_storage_));
- pthread_mutex_lock((pthread_mutex_t*)&opaque_storage_);
- CHECK(!owner_);
- owner_ = (uptr)pthread_self();
-}
-
-void AsanLock::Unlock() {
- CHECK(owner_ == (uptr)pthread_self());
- owner_ = 0;
- pthread_mutex_unlock((pthread_mutex_t*)&opaque_storage_);
-}
-
void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
#if defined(__arm__) || \
defined(__powerpc__) || defined(__powerpc64__) || \
@@ -139,19 +118,13 @@ void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
}
#if !ASAN_ANDROID
-void ClearShadowMemoryForContext(void *context) {
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
ucontext_t *ucp = (ucontext_t*)context;
- uptr sp = (uptr)ucp->uc_stack.ss_sp;
- uptr size = ucp->uc_stack.ss_size;
- // Align to page size.
- uptr PageSize = GetPageSizeCached();
- uptr bottom = sp & ~(PageSize - 1);
- size += sp - bottom;
- size = RoundUpTo(size, PageSize);
- PoisonShadow(bottom, size, 0);
+ *stack = (uptr)ucp->uc_stack.ss_sp;
+ *ssize = ucp->uc_stack.ss_size;
}
#else
-void ClearShadowMemoryForContext(void *context) {
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
UNIMPLEMENTED();
}
#endif
diff --git a/libsanitizer/asan/asan_lock.h b/libsanitizer/asan/asan_lock.h
index 2392e3c0e7b..8b137891791 100644
--- a/libsanitizer/asan/asan_lock.h
+++ b/libsanitizer/asan/asan_lock.h
@@ -1,40 +1 @@
-//===-- asan_lock.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.
-//
-// A wrapper for a simple lock.
-//===----------------------------------------------------------------------===//
-#ifndef ASAN_LOCK_H
-#define ASAN_LOCK_H
-#include "sanitizer_common/sanitizer_mutex.h"
-#include "asan_internal.h"
-
-// The locks in ASan are global objects and they are never destroyed to avoid
-// at-exit races (that is, a lock is being used by other threads while the main
-// thread is doing atexit destructors).
-// We define the class using opaque storage to avoid including system headers.
-
-namespace __asan {
-
-class AsanLock {
- public:
- explicit AsanLock(LinkerInitialized);
- void Lock();
- void Unlock();
- bool IsLocked() { return owner_ != 0; }
- private:
- uptr opaque_storage_[10];
- uptr owner_; // for debugging and for malloc_introspection_t interface
-};
-
-typedef GenericScopedLock<AsanLock> ScopedLock;
-
-} // namespace __asan
-
-#endif // ASAN_LOCK_H
diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc
index 094c69ff6a2..a49d5c5ab8f 100644
--- a/libsanitizer/asan/asan_mac.cc
+++ b/libsanitizer/asan/asan_mac.cc
@@ -34,7 +34,6 @@
#include <stdlib.h> // for free()
#include <unistd.h>
#include <libkern/OSAtomic.h>
-#include <CoreFoundation/CFString.h>
namespace __asan {
@@ -129,33 +128,6 @@ bool AsanInterceptsSignal(int signum) {
}
void AsanPlatformThreadInit() {
- // For the first program thread, we can't replace the allocator before
- // __CFInitialize() has been called. If it hasn't, we'll call
- // MaybeReplaceCFAllocator() later on this thread.
- // For other threads __CFInitialize() has been called before their creation.
- // See also asan_malloc_mac.cc.
- if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) {
- MaybeReplaceCFAllocator();
- }
-}
-
-AsanLock::AsanLock(LinkerInitialized) {
- // We assume that OS_SPINLOCK_INIT is zero
-}
-
-void AsanLock::Lock() {
- CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
- CHECK(OS_SPINLOCK_INIT == 0);
- CHECK(owner_ != (uptr)pthread_self());
- OSSpinLockLock((OSSpinLock*)&opaque_storage_);
- CHECK(!owner_);
- owner_ = (uptr)pthread_self();
-}
-
-void AsanLock::Unlock() {
- CHECK(owner_ == (uptr)pthread_self());
- owner_ = 0;
- OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
}
void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
@@ -170,7 +142,7 @@ void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
}
}
-void ClearShadowMemoryForContext(void *context) {
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
UNIMPLEMENTED();
}
@@ -254,9 +226,6 @@ mach_error_t __interception_deallocate_island(void *ptr) {
// The implementation details are at
// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
-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;
@@ -287,9 +256,6 @@ 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);
-int pthread_workqueue_additem_np(pthread_workqueue_t workq,
- void *(*workitem_func)(void *), void * workitem_arg,
- pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
} // extern "C"
static ALWAYS_INLINE
@@ -444,66 +410,6 @@ INTERCEPTOR(void, dispatch_source_set_event_handler,
}
#endif
-// The following stuff has been extremely helpful while looking for the
-// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity
-// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to
-// find the points of worker thread creation (each of such threads may be used
-// to run several tasks, that's why this is not enough to support the whole
-// libdispatch API.
-extern "C"
-void *wrap_workitem_func(void *arg) {
- if (flags()->verbosity >= 2) {
- Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self());
- }
- asan_block_context_t *ctxt = (asan_block_context_t*)arg;
- worker_t fn = (worker_t)(ctxt->func);
- void *result = fn(ctxt->block);
- GET_STACK_TRACE_THREAD;
- asan_free(arg, &stack, FROM_MALLOC);
- return result;
-}
-
-INTERCEPTOR(int, pthread_workqueue_additem_np, pthread_workqueue_t workq,
- void *(*workitem_func)(void *), void * workitem_arg,
- pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) {
- GET_STACK_TRACE_THREAD;
- asan_block_context_t *asan_ctxt =
- (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack);
- asan_ctxt->block = workitem_arg;
- asan_ctxt->func = (dispatch_function_t)workitem_func;
- asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
- if (flags()->verbosity >= 2) {
- Report("pthread_workqueue_additem_np: %p\n", asan_ctxt);
- PRINT_CURRENT_STACK();
- }
- return REAL(pthread_workqueue_additem_np)(workq, wrap_workitem_func,
- asan_ctxt, itemhandlep,
- gencountp);
-}
-
-// See http://opensource.apple.com/source/CF/CF-635.15/CFString.c
-int __CFStrIsConstant(CFStringRef str) {
- CFRuntimeBase *base = (CFRuntimeBase*)str;
-#if __LP64__
- return base->_rc == 0;
-#else
- return (base->_cfinfo[CF_RC_BITS]) == 0;
-#endif
-}
-
-INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc,
- CFStringRef str) {
- if (__CFStrIsConstant(str)) {
- return str;
- } else {
- return REAL(CFStringCreateCopy)(alloc, str);
- }
-}
-
-DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
-
-DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize, void)
-
namespace __asan {
void InitializeMacInterceptors() {
@@ -512,26 +418,6 @@ void InitializeMacInterceptors() {
CHECK(INTERCEPT_FUNCTION(dispatch_after_f));
CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f));
CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f));
- // We don't need to intercept pthread_workqueue_additem_np() to support the
- // libdispatch API, but it helps us to debug the unsupported functions. Let's
- // intercept it only during verbose runs.
- if (flags()->verbosity >= 2) {
- CHECK(INTERCEPT_FUNCTION(pthread_workqueue_additem_np));
- }
- // Normally CFStringCreateCopy should not copy constant CF strings.
- // Replacing the default CFAllocator causes constant strings to be copied
- // rather than just returned, which leads to bugs in big applications like
- // Chromium and WebKit, see
- // http://code.google.com/p/address-sanitizer/issues/detail?id=10
- // Until this problem is fixed we need to check that the string is
- // non-constant before calling CFStringCreateCopy.
- CHECK(INTERCEPT_FUNCTION(CFStringCreateCopy));
- // Some of the library functions call free() directly, so we have to
- // intercept it.
- CHECK(INTERCEPT_FUNCTION(free));
- if (flags()->replace_cfallocator) {
- CHECK(INTERCEPT_FUNCTION(__CFInitialize));
- }
}
} // namespace __asan
diff --git a/libsanitizer/asan/asan_malloc_mac.cc b/libsanitizer/asan/asan_malloc_mac.cc
index 97aa4424d33..3ae6c594650 100644
--- a/libsanitizer/asan/asan_malloc_mac.cc
+++ b/libsanitizer/asan/asan_malloc_mac.cc
@@ -34,85 +34,108 @@ using namespace __asan; // NOLINT
// TODO(glider): do we need both zones?
static malloc_zone_t *system_malloc_zone = 0;
-static malloc_zone_t *system_purgeable_zone = 0;
static malloc_zone_t asan_zone;
-CFAllocatorRef cf_asan = 0;
-
-// _CFRuntimeCreateInstance() checks whether the supplied allocator is
-// kCFAllocatorSystemDefault and, if it is not, stores the allocator reference
-// at the beginning of the allocated memory and returns the pointer to the
-// allocated memory plus sizeof(CFAllocatorRef). See
-// http://www.opensource.apple.com/source/CF/CF-635.21/CFRuntime.c
-// Pointers returned by _CFRuntimeCreateInstance() can then be passed directly
-// to free() or CFAllocatorDeallocate(), which leads to false invalid free
-// reports.
-// The corresponding rdar bug is http://openradar.appspot.com/radar?id=1796404.
-void* ALWAYS_INLINE get_saved_cfallocator_ref(void *ptr) {
- if (flags()->replace_cfallocator) {
- // Make sure we're not hitting the previous page. This may be incorrect
- // if ASan's malloc returns an address ending with 0xFF8, which will be
- // then padded to a page boundary with a CFAllocatorRef.
- uptr arith_ptr = (uptr)ptr;
- if ((arith_ptr & 0xFFF) > sizeof(CFAllocatorRef)) {
- CFAllocatorRef *saved =
- (CFAllocatorRef*)(arith_ptr - sizeof(CFAllocatorRef));
- if ((*saved == cf_asan) && asan_mz_size(saved)) ptr = (void*)saved;
- }
+
+INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
+ vm_size_t start_size, unsigned zone_flags) {
+ if (!asan_inited) __asan_init();
+ GET_STACK_TRACE_MALLOC;
+ malloc_zone_t *new_zone =
+ (malloc_zone_t*)asan_malloc(sizeof(asan_zone), &stack);
+ internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone));
+ new_zone->zone_name = NULL; // The name will be changed anyway.
+ return new_zone;
+}
+
+INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
+ if (!asan_inited) __asan_init();
+ return &asan_zone;
+}
+
+INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
+ // FIXME: ASan should support purgeable allocations.
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=139
+ if (!asan_inited) __asan_init();
+ return &asan_zone;
+}
+
+INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
+ // FIXME: ASan should support purgeable allocations. Ignoring them is fine
+ // for now.
+ if (!asan_inited) __asan_init();
+}
+
+INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
+ // FIXME: ASan should support purgeable allocations. Ignoring them is fine
+ // for now.
+ if (!asan_inited) __asan_init();
+ // Must return 0 if the contents were not purged since the last call to
+ // malloc_make_purgeable().
+ return 0;
+}
+
+INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
+ if (!asan_inited) __asan_init();
+ // Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes.
+ size_t buflen = 6 + (name ? internal_strlen(name) : 0);
+ InternalScopedBuffer<char> new_name(buflen);
+ if (name && zone->introspect == asan_zone.introspect) {
+ internal_snprintf(new_name.data(), buflen, "asan-%s", name);
+ name = new_name.data();
}
- return ptr;
+
+ // Call the system malloc's implementation for both external and our zones,
+ // since that appropriately changes VM region protections on the zone.
+ REAL(malloc_set_zone_name)(zone, name);
+}
+
+INTERCEPTOR(void *, malloc, size_t size) {
+ if (!asan_inited) __asan_init();
+ GET_STACK_TRACE_MALLOC;
+ void *res = asan_malloc(size, &stack);
+ return res;
}
-// The free() implementation provided by OS X calls malloc_zone_from_ptr()
-// to find the owner of |ptr|. If the result is 0, an invalid free() is
-// reported. Our implementation falls back to asan_free() in this case
-// in order to print an ASan-style report.
-//
-// For the objects created by _CFRuntimeCreateInstance a CFAllocatorRef is
-// placed at the beginning of the allocated chunk and the pointer returned by
-// our allocator is off by sizeof(CFAllocatorRef). This pointer can be then
-// passed directly to free(), which will lead to errors.
-// To overcome this we're checking whether |ptr-sizeof(CFAllocatorRef)|
-// contains a pointer to our CFAllocator (assuming no other allocator is used).
-// See http://code.google.com/p/address-sanitizer/issues/detail?id=70 for more
-// info.
INTERCEPTOR(void, free, void *ptr) {
- malloc_zone_t *zone = malloc_zone_from_ptr(ptr);
- if (zone) {
-#if defined(MAC_OS_X_VERSION_10_6) && \
- MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
- if ((zone->version >= 6) && (zone->free_definite_size)) {
- zone->free_definite_size(zone, ptr, malloc_size(ptr));
- } else {
- malloc_zone_free(zone, ptr);
- }
-#else
- malloc_zone_free(zone, ptr);
-#endif
- } else {
- if (!asan_mz_size(ptr)) ptr = get_saved_cfallocator_ref(ptr);
- GET_STACK_TRACE_FREE;
- asan_free(ptr, &stack, FROM_MALLOC);
- }
+ if (!asan_inited) __asan_init();
+ if (!ptr) return;
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
}
-// We can't always replace the default CFAllocator with cf_asan right in
-// ReplaceSystemMalloc(), because it is sometimes called before
-// __CFInitialize(), when the default allocator is invalid and replacing it may
-// crash the program. Instead we wait for the allocator to initialize and jump
-// in just after __CFInitialize(). Nobody is going to allocate memory using
-// CFAllocators before that, so we won't miss anything.
-//
-// See http://code.google.com/p/address-sanitizer/issues/detail?id=87
-// and http://opensource.apple.com/source/CF/CF-550.43/CFRuntime.c
-INTERCEPTOR(void, __CFInitialize, void) {
- // If the runtime is built as dynamic library, __CFInitialize wrapper may be
- // called before __asan_init.
-#if !MAC_INTERPOSE_FUNCTIONS
- CHECK(flags()->replace_cfallocator);
- CHECK(asan_inited);
-#endif
- REAL(__CFInitialize)();
- if (!cf_asan && asan_inited) MaybeReplaceCFAllocator();
+INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
+ if (!asan_inited) __asan_init();
+ GET_STACK_TRACE_MALLOC;
+ return asan_realloc(ptr, size, &stack);
+}
+
+INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
+ if (!asan_inited) __asan_init();
+ GET_STACK_TRACE_MALLOC;
+ return asan_calloc(nmemb, size, &stack);
+}
+
+INTERCEPTOR(void *, valloc, size_t size) {
+ if (!asan_inited) __asan_init();
+ GET_STACK_TRACE_MALLOC;
+ return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
+}
+
+INTERCEPTOR(size_t, malloc_good_size, size_t size) {
+ if (!asan_inited) __asan_init();
+ return asan_zone.introspect->good_size(&asan_zone, size);
+}
+
+INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
+ if (!asan_inited) __asan_init();
+ CHECK(memptr);
+ GET_STACK_TRACE_MALLOC;
+ void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC);
+ if (result) {
+ *memptr = result;
+ return 0;
+ }
+ return -1;
}
namespace {
@@ -132,15 +155,6 @@ void *mz_malloc(malloc_zone_t *zone, size_t size) {
return asan_malloc(size, &stack);
}
-void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) {
- if (!asan_inited) {
- CHECK(system_malloc_zone);
- return malloc_zone_malloc(system_malloc_zone, size);
- }
- GET_STACK_TRACE_MALLOC;
- return asan_malloc(size, &stack);
-}
-
void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
if (!asan_inited) {
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
@@ -172,31 +186,14 @@ void *mz_valloc(malloc_zone_t *zone, size_t size) {
void ALWAYS_INLINE free_common(void *context, void *ptr) {
if (!ptr) return;
- if (asan_mz_size(ptr)) {
- GET_STACK_TRACE_FREE;
+ GET_STACK_TRACE_FREE;
+ // FIXME: need to retire this flag.
+ if (!flags()->mac_ignore_invalid_free) {
asan_free(ptr, &stack, FROM_MALLOC);
} else {
- // If the pointer does not belong to any of the zones, use one of the
- // fallback methods to free memory.
- malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr);
- if (zone_ptr == system_purgeable_zone) {
- // allocations from malloc_default_purgeable_zone() done before
- // __asan_init() may be occasionally freed via free_common().
- // see http://code.google.com/p/address-sanitizer/issues/detail?id=99.
- malloc_zone_free(zone_ptr, ptr);
- } else {
- // If the memory chunk pointer was moved to store additional
- // CFAllocatorRef, fix it back.
- ptr = get_saved_cfallocator_ref(ptr);
- GET_STACK_TRACE_FREE;
- if (!flags()->mac_ignore_invalid_free) {
- asan_free(ptr, &stack, FROM_MALLOC);
- } else {
- GET_ZONE_FOR_PTR(ptr);
- WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
- return;
- }
- }
+ GET_ZONE_FOR_PTR(ptr);
+ WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
+ return;
}
}
@@ -205,10 +202,6 @@ void mz_free(malloc_zone_t *zone, void *ptr) {
free_common(zone, ptr);
}
-void cf_free(void *ptr, void *info) {
- free_common(info, ptr);
-}
-
void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
if (!ptr) {
GET_STACK_TRACE_MALLOC;
@@ -228,29 +221,11 @@ void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
}
}
-void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) {
- if (!ptr) {
- GET_STACK_TRACE_MALLOC;
- return asan_malloc(size, &stack);
- } else {
- if (asan_mz_size(ptr)) {
- GET_STACK_TRACE_MALLOC;
- return asan_realloc(ptr, size, &stack);
- } else {
- // We can't recover from reallocating an unknown address, because
- // this would require reading at most |size| bytes from
- // potentially unaccessible memory.
- GET_STACK_TRACE_FREE;
- GET_ZONE_FOR_PTR(ptr);
- ReportMacCfReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
- }
- }
-}
-
void mz_destroy(malloc_zone_t* zone) {
// A no-op -- we will not be destroyed!
- Printf("mz_destroy() called -- ignoring\n");
+ Report("mz_destroy() called -- ignoring\n");
}
+
// from AvailabilityMacros.h
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
@@ -322,23 +297,7 @@ boolean_t mi_zone_locked(malloc_zone_t *zone) {
} // unnamed namespace
-extern int __CFRuntimeClassTableSize;
-
namespace __asan {
-void MaybeReplaceCFAllocator() {
- static CFAllocatorContext asan_context = {
- /*version*/ 0, /*info*/ &asan_zone,
- /*retain*/ 0, /*release*/ 0,
- /*copyDescription*/0,
- /*allocate*/ &cf_malloc,
- /*reallocate*/ &cf_realloc,
- /*deallocate*/ &cf_free,
- /*preferredSize*/ 0 };
- if (!cf_asan)
- cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context);
- if (flags()->replace_cfallocator && CFAllocatorGetDefault() != cf_asan)
- CFAllocatorSetDefault(cf_asan);
-}
void ReplaceSystemMalloc() {
static malloc_introspection_t asan_introspection;
@@ -378,41 +337,10 @@ void ReplaceSystemMalloc() {
asan_zone.free_definite_size = 0;
asan_zone.memalign = &mz_memalign;
asan_introspection.zone_locked = &mi_zone_locked;
-
- // Request the default purgable zone to force its creation. The
- // current default zone is registered with the purgable zone for
- // doing tiny and small allocs. Sadly, it assumes that the default
- // zone is the szone implementation from OS X and will crash if it
- // isn't. By creating the zone now, this will be true and changing
- // the default zone won't cause a problem. (OS X 10.6 and higher.)
- system_purgeable_zone = malloc_default_purgeable_zone();
#endif
- // Register the ASan zone. At this point, it will not be the
- // default zone.
+ // Register the ASan zone.
malloc_zone_register(&asan_zone);
-
- // Unregister and reregister the default zone. Unregistering swaps
- // the specified zone with the last one registered which for the
- // default zone makes the more recently registered zone the default
- // zone. The default zone is then re-registered to ensure that
- // allocations made from it earlier will be handled correctly.
- // Things are not guaranteed to work that way, but it's how they work now.
- system_malloc_zone = malloc_default_zone();
- malloc_zone_unregister(system_malloc_zone);
- malloc_zone_register(system_malloc_zone);
- // Make sure the default allocator was replaced.
- CHECK(malloc_default_zone() == &asan_zone);
-
- // If __CFInitialize() hasn't been called yet, cf_asan will be created and
- // installed as the default allocator after __CFInitialize() finishes (see
- // the interceptor for __CFInitialize() above). Otherwise install cf_asan
- // right now. On both Snow Leopard and Lion __CFInitialize() calls
- // __CFAllocatorInitialize(), which initializes the _base._cfisa field of
- // the default allocators we check here.
- if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) {
- MaybeReplaceCFAllocator();
- }
}
} // namespace __asan
diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h
index 54e21b79678..be645c4afcd 100644
--- a/libsanitizer/asan/asan_mapping.h
+++ b/libsanitizer/asan/asan_mapping.h
@@ -109,6 +109,10 @@ static inline bool AddrIsInShadow(uptr a) {
}
static inline bool AddrIsInShadowGap(uptr a) {
+ // In zero-based shadow mode we treat addresses near zero as addresses
+ // in shadow gap as well.
+ if (SHADOW_OFFSET == 0)
+ return a <= kShadowGapEnd;
return a >= kShadowGapBeg && a <= kShadowGapEnd;
}
diff --git a/libsanitizer/asan/asan_new_delete.cc b/libsanitizer/asan/asan_new_delete.cc
index 6597b93366f..e41135d0e51 100644
--- a/libsanitizer/asan/asan_new_delete.cc
+++ b/libsanitizer/asan/asan_new_delete.cc
@@ -25,8 +25,9 @@ void ReplaceOperatorsNewAndDelete() { }
using namespace __asan; // NOLINT
-// On Android new() goes through malloc interceptors.
-#if !ASAN_ANDROID
+// On Mac and Android new() goes through malloc interceptors.
+// See also https://code.google.com/p/address-sanitizer/issues/detail?id=131.
+#if !ASAN_ANDROID && !ASAN_MAC
// Fake std::nothrow_t to avoid including <new>.
namespace std {
diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc
index a00bafffae0..11edca57ae0 100644
--- a/libsanitizer/asan/asan_poisoning.cc
+++ b/libsanitizer/asan/asan_poisoning.cc
@@ -23,7 +23,7 @@ void PoisonShadow(uptr addr, uptr size, u8 value) {
CHECK(AddrIsAlignedByGranularity(addr));
CHECK(AddrIsAlignedByGranularity(addr + size));
uptr shadow_beg = MemToShadow(addr);
- uptr shadow_end = MemToShadow(addr + size);
+ uptr shadow_end = MemToShadow(addr + size - SHADOW_GRANULARITY) + 1;
CHECK(REAL(memset) != 0);
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
}
diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc
index 6480bf4cc35..04d2d7af63f 100644
--- a/libsanitizer/asan/asan_rtl.cc
+++ b/libsanitizer/asan/asan_rtl.cc
@@ -12,7 +12,6 @@
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_stack.h"
@@ -140,10 +139,12 @@ void InitializeFlags(Flags *f, const char *env) {
f->allow_reexec = true;
f->print_full_thread_history = true;
f->log_path = 0;
- f->fast_unwind_on_fatal = true;
+ f->fast_unwind_on_fatal = false;
f->fast_unwind_on_malloc = true;
f->poison_heap = true;
- f->alloc_dealloc_mismatch = true;
+ // Turn off alloc/dealloc mismatch checker on Mac for now.
+ // TODO(glider): Fix known issues and enable this back.
+ f->alloc_dealloc_mismatch = (ASAN_MAC == 0);
f->use_stack_depot = true; // Only affects allocator2.
// Override from user-specified string.
@@ -228,7 +229,6 @@ static NOINLINE void force_interface_symbols() {
case 8: __asan_report_store4(0); break;
case 9: __asan_report_store8(0); break;
case 10: __asan_report_store16(0); break;
- case 11: __asan_register_global(0, 0, 0); break;
case 12: __asan_register_globals(0, 0); break;
case 13: __asan_unregister_globals(0, 0); break;
case 14: __asan_set_death_callback(0); break;
diff --git a/libsanitizer/asan/asan_stats.cc b/libsanitizer/asan/asan_stats.cc
index 94dd741d325..1187bd92f97 100644
--- a/libsanitizer/asan/asan_stats.cc
+++ b/libsanitizer/asan/asan_stats.cc
@@ -11,7 +11,6 @@
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_stats.h"
#include "asan_thread_registry.h"
#include "sanitizer/asan_interface.h"
@@ -53,13 +52,13 @@ void AsanStats::Print() {
malloc_large, malloc_small_slow);
}
-static AsanLock print_lock(LINKER_INITIALIZED);
+static BlockingMutex print_lock(LINKER_INITIALIZED);
static void PrintAccumulatedStats() {
AsanStats stats;
asanThreadRegistry().GetAccumulatedStats(&stats);
// Use lock to keep reports from mixing up.
- ScopedLock lock(&print_lock);
+ BlockingMutexLock lock(&print_lock);
stats.Print();
StackDepotStats *stack_depot_stats = StackDepotGetStats();
Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc
index cc2e777a977..02f49dd59ef 100644
--- a/libsanitizer/asan/asan_thread.cc
+++ b/libsanitizer/asan/asan_thread.cc
@@ -72,7 +72,7 @@ void AsanThread::Destroy() {
void AsanThread::Init() {
SetThreadStackTopAndBottom();
CHECK(AddrIsInMem(stack_bottom_));
- CHECK(AddrIsInMem(stack_top_));
+ CHECK(AddrIsInMem(stack_top_ - 1));
ClearShadowForThreadStack();
if (flags()->verbosity >= 1) {
int local = 0;
diff --git a/libsanitizer/asan/asan_thread_registry.cc b/libsanitizer/asan/asan_thread_registry.cc
index 8db6a57cdff..8fda9b6ea0a 100644
--- a/libsanitizer/asan/asan_thread_registry.cc
+++ b/libsanitizer/asan/asan_thread_registry.cc
@@ -42,7 +42,7 @@ void AsanThreadRegistry::Init() {
}
void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
u32 tid = n_threads_;
n_threads_++;
CHECK(n_threads_ < kMaxNumberOfThreads);
@@ -54,7 +54,7 @@ void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
}
void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
FlushToAccumulatedStatsUnlocked(&thread->stats());
AsanThreadSummary *summary = thread->summary();
CHECK(summary);
@@ -103,13 +103,13 @@ AsanStats &AsanThreadRegistry::GetCurrentThreadStats() {
}
void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_));
}
uptr AsanThreadRegistry::GetCurrentAllocatedBytes() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
uptr malloced = accumulated_stats_.malloced;
uptr freed = accumulated_stats_.freed;
@@ -119,13 +119,13 @@ uptr AsanThreadRegistry::GetCurrentAllocatedBytes() {
}
uptr AsanThreadRegistry::GetHeapSize() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
return accumulated_stats_.mmaped - accumulated_stats_.munmaped;
}
uptr AsanThreadRegistry::GetFreeBytes() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
uptr total_free = accumulated_stats_.mmaped
- accumulated_stats_.munmaped
@@ -141,7 +141,7 @@ uptr AsanThreadRegistry::GetFreeBytes() {
// Return several stats counters with a single call to
// UpdateAccumulatedStatsUnlocked().
void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats *malloc_stats) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
malloc_stats->blocks_in_use = accumulated_stats_.mallocs;
malloc_stats->size_in_use = accumulated_stats_.malloced;
@@ -156,7 +156,7 @@ AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) {
}
AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
for (u32 tid = 0; tid < n_threads_; tid++) {
AsanThread *t = thread_summaries_[tid]->thread();
if (!t || !(t->fake_stack().StackSize())) continue;
diff --git a/libsanitizer/asan/asan_thread_registry.h b/libsanitizer/asan/asan_thread_registry.h
index bb6b2678faa..8c3d0c886e0 100644
--- a/libsanitizer/asan/asan_thread_registry.h
+++ b/libsanitizer/asan/asan_thread_registry.h
@@ -13,10 +13,10 @@
#ifndef ASAN_THREAD_REGISTRY_H
#define ASAN_THREAD_REGISTRY_H
-#include "asan_lock.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
@@ -71,7 +71,7 @@ class AsanThreadRegistry {
// per-thread AsanStats.
uptr max_malloced_memory_;
u32 n_threads_;
- AsanLock mu_;
+ BlockingMutex mu_;
bool inited_;
};
diff --git a/libsanitizer/asan/asan_win.cc b/libsanitizer/asan/asan_win.cc
index 02a2e08868e..6acfeebc8bf 100644
--- a/libsanitizer/asan/asan_win.cc
+++ b/libsanitizer/asan/asan_win.cc
@@ -15,18 +15,16 @@
#include <dbghelp.h>
#include <stdlib.h>
-#include <new> // FIXME: temporarily needed for placement new in AsanLock.
-
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1
-static AsanLock dbghelp_lock(LINKER_INITIALIZED);
+static BlockingMutex dbghelp_lock(LINKER_INITIALIZED);
static bool dbghelp_initialized = false;
#pragma comment(lib, "dbghelp.lib")
@@ -54,42 +52,6 @@ void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
stack->trace[i] = (uptr)tmp[i + offset];
}
-// ---------------------- AsanLock ---------------- {{{1
-enum LockState {
- LOCK_UNINITIALIZED = 0,
- LOCK_READY = -1,
-};
-
-AsanLock::AsanLock(LinkerInitialized li) {
- // FIXME: see comments in AsanLock::Lock() for the details.
- CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
-
- CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
- InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
- owner_ = LOCK_READY;
-}
-
-void AsanLock::Lock() {
- if (owner_ == LOCK_UNINITIALIZED) {
- // FIXME: hm, global AsanLock objects are not initialized?!?
- // This might be a side effect of the clang+cl+link Frankenbuild...
- new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1));
-
- // FIXME: If it turns out the linker doesn't invoke our
- // constructors, we should probably manually Lock/Unlock all the global
- // locks while we're starting in one thread to avoid double-init races.
- }
- EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
- CHECK(owner_ == LOCK_READY);
- owner_ = GetThreadSelf();
-}
-
-void AsanLock::Unlock() {
- CHECK(owner_ == GetThreadSelf());
- owner_ = LOCK_READY;
- LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
-}
-
// ---------------------- TSD ---------------- {{{1
static bool tsd_key_inited = false;
@@ -138,7 +100,7 @@ void AsanPlatformThreadInit() {
// Nothing here for now.
}
-void ClearShadowMemoryForContext(void *context) {
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
UNIMPLEMENTED();
}
@@ -150,7 +112,7 @@ using namespace __asan; // NOLINT
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
bool __asan_symbolize(const void *addr, char *out_buffer, int buffer_size) {
- ScopedLock lock(&dbghelp_lock);
+ BlockingMutexLock lock(&dbghelp_lock);
if (!dbghelp_initialized) {
SymSetOptions(SYMOPT_DEFERRED_LOADS |
SYMOPT_UNDNAME |
diff --git a/libsanitizer/asan/dynamic/asan_interceptors_dynamic.cc b/libsanitizer/asan/dynamic/asan_interceptors_dynamic.cc
index 3f6f06a5118..757c704bd55 100644
--- a/libsanitizer/asan/dynamic/asan_interceptors_dynamic.cc
+++ b/libsanitizer/asan/dynamic/asan_interceptors_dynamic.cc
@@ -99,9 +99,19 @@ const interpose_substitution substitutions[]
INTERPOSE_FUNCTION(signal),
INTERPOSE_FUNCTION(sigaction),
- INTERPOSE_FUNCTION(__CFInitialize),
- INTERPOSE_FUNCTION(CFStringCreateCopy),
+ INTERPOSE_FUNCTION(malloc_create_zone),
+ INTERPOSE_FUNCTION(malloc_default_zone),
+ INTERPOSE_FUNCTION(malloc_default_purgeable_zone),
+ INTERPOSE_FUNCTION(malloc_make_purgeable),
+ INTERPOSE_FUNCTION(malloc_make_nonpurgeable),
+ INTERPOSE_FUNCTION(malloc_set_zone_name),
+ INTERPOSE_FUNCTION(malloc),
INTERPOSE_FUNCTION(free),
+ INTERPOSE_FUNCTION(realloc),
+ INTERPOSE_FUNCTION(calloc),
+ INTERPOSE_FUNCTION(valloc),
+ INTERPOSE_FUNCTION(malloc_good_size),
+ INTERPOSE_FUNCTION(posix_memalign),
};
} // namespace __asan
diff --git a/libsanitizer/include/sanitizer/asan_interface.h b/libsanitizer/include/sanitizer/asan_interface.h
index 47f780ceaa3..9f9f12cbfa2 100644
--- a/libsanitizer/include/sanitizer/asan_interface.h
+++ b/libsanitizer/include/sanitizer/asan_interface.h
@@ -26,11 +26,6 @@ extern "C" {
// before any instrumented code is executed and before any call to malloc.
void __asan_init() SANITIZER_INTERFACE_ATTRIBUTE;
- // This function should be called by the instrumented code.
- // 'addr' is the address of a global variable called 'name' of 'size' bytes.
- void __asan_register_global(uptr addr, uptr size, const char *name)
- SANITIZER_INTERFACE_ATTRIBUTE;
-
// This structure describes an instrumented global variable.
struct __asan_global {
uptr beg; // The address of the global.
diff --git a/libsanitizer/interception/interception.h b/libsanitizer/interception/interception.h
index 66bdd8830f3..ccf5e1b1ec0 100644
--- a/libsanitizer/interception/interception.h
+++ b/libsanitizer/interception/interception.h
@@ -23,6 +23,8 @@
// the standard system types (e.g. SSIZE_T instead of ssize_t)
typedef __sanitizer::uptr SIZE_T;
typedef __sanitizer::sptr SSIZE_T;
+typedef __sanitizer::sptr PTRDIFF_T;
+typedef __sanitizer::s64 INTMAX_T;
typedef __sanitizer::u64 OFF_T;
typedef __sanitizer::u64 OFF64_T;
diff --git a/libsanitizer/merge.sh b/libsanitizer/merge.sh
index 0f75431cf68..d2e622aeb1a 100755
--- a/libsanitizer/merge.sh
+++ b/libsanitizer/merge.sh
@@ -16,7 +16,7 @@ get_current_rev() {
}
list_files() {
- (cd $1; ls *.{cc,h} 2> /dev/null)
+ (cd $1; ls *.{cc,h,inc} 2> /dev/null)
}
diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.h b/libsanitizer/sanitizer_common/sanitizer_allocator.h
index d0fc315b97e..1c9852e5003 100644
--- a/libsanitizer/sanitizer_common/sanitizer_allocator.h
+++ b/libsanitizer/sanitizer_common/sanitizer_allocator.h
@@ -17,6 +17,7 @@
#include "sanitizer_libc.h"
#include "sanitizer_list.h"
#include "sanitizer_mutex.h"
+#include "sanitizer_lfstack.h"
namespace __sanitizer {
@@ -62,7 +63,8 @@ namespace __sanitizer {
// c32 => s: 512 diff: +32 06% l 9 cached: 64 32768; id 32
-template <uptr kMaxSizeLog, uptr kMaxNumCached, uptr kMaxBytesCachedLog>
+template <uptr kMaxSizeLog, uptr kMaxNumCachedT, uptr kMaxBytesCachedLog,
+ uptr kMinBatchClassT>
class SizeClassMap {
static const uptr kMinSizeLog = 3;
static const uptr kMidSizeLog = kMinSizeLog + 4;
@@ -73,6 +75,14 @@ class SizeClassMap {
static const uptr M = (1 << S) - 1;
public:
+ static const uptr kMaxNumCached = kMaxNumCachedT;
+ struct TransferBatch {
+ TransferBatch *next;
+ uptr count;
+ void *batch[kMaxNumCached];
+ };
+
+ static const uptr kMinBatchClass = kMinBatchClassT;
static const uptr kMaxSize = 1 << kMaxSizeLog;
static const uptr kNumClasses =
kMidClass + ((kMaxSizeLog - kMidSizeLog) << S) + 1;
@@ -148,44 +158,25 @@ class SizeClassMap {
if (c > 0)
CHECK_LT(Size(c-1), s);
}
- }
-};
-
-typedef SizeClassMap<15, 256, 16> DefaultSizeClassMap;
-typedef SizeClassMap<15, 64, 14> CompactSizeClassMap;
-
-struct AllocatorListNode {
- AllocatorListNode *next;
+ // TransferBatch for kMinBatchClass must fit into the block itself.
+ const uptr batch_size = sizeof(TransferBatch)
+ - sizeof(void*) // NOLINT
+ * (kMaxNumCached - MaxCached(kMinBatchClass));
+ CHECK_LE(batch_size, Size(kMinBatchClass));
+ // TransferBatch for kMinBatchClass-1 must not fit into the block itself.
+ const uptr batch_size1 = sizeof(TransferBatch)
+ - sizeof(void*) // NOLINT
+ * (kMaxNumCached - MaxCached(kMinBatchClass - 1));
+ CHECK_GT(batch_size1, Size(kMinBatchClass - 1));
+ }
};
-typedef IntrusiveList<AllocatorListNode> AllocatorFreeList;
-
-// Move at most max_count chunks from allocate_from to allocate_to.
-// This function is better be a method of AllocatorFreeList, but we can't
-// inherit it from IntrusiveList as the ancient gcc complains about non-PODness.
-static inline uptr BulkMove(uptr max_count,
- AllocatorFreeList *allocate_from,
- AllocatorFreeList *allocate_to) {
- CHECK(!allocate_from->empty());
- CHECK(allocate_to->empty());
- uptr res = 0;
- if (allocate_from->size() <= max_count) {
- res = allocate_from->size();
- allocate_to->append_front(allocate_from);
- CHECK(allocate_from->empty());
- } else {
- for (uptr i = 0; i < max_count; i++) {
- AllocatorListNode *node = allocate_from->front();
- allocate_from->pop_front();
- allocate_to->push_front(node);
- }
- res = max_count;
- CHECK(!allocate_from->empty());
- }
- CHECK(!allocate_to->empty());
- return res;
-}
+typedef SizeClassMap<17, 256, 16, FIRST_32_SECOND_64(33, 36)>
+ DefaultSizeClassMap;
+typedef SizeClassMap<17, 64, 14, FIRST_32_SECOND_64(25, 28)>
+ CompactSizeClassMap;
+template<class SizeClassAllocator> struct SizeClassAllocatorLocalCache;
// Allocators call these callbacks on mmap/munmap.
struct NoOpMapUnmapCallback {
@@ -214,6 +205,11 @@ template <const uptr kSpaceBeg, const uptr kSpaceSize,
class MapUnmapCallback = NoOpMapUnmapCallback>
class SizeClassAllocator64 {
public:
+ typedef typename SizeClassMap::TransferBatch Batch;
+ typedef SizeClassAllocator64<kSpaceBeg, kSpaceSize, kMetadataSize,
+ SizeClassMap, MapUnmapCallback> ThisT;
+ typedef SizeClassAllocatorLocalCache<ThisT> AllocatorCache;
+
void Init() {
CHECK_EQ(kSpaceBeg,
reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize)));
@@ -235,36 +231,20 @@ class SizeClassAllocator64 {
alignment <= SizeClassMap::kMaxSize;
}
- void *Allocate(uptr size, uptr alignment) {
- if (size < alignment) size = alignment;
- CHECK(CanAllocate(size, alignment));
- return AllocateBySizeClass(ClassID(size));
- }
-
- void Deallocate(void *p) {
- CHECK(PointerIsMine(p));
- DeallocateBySizeClass(p, GetSizeClass(p));
- }
-
- // Allocate several chunks of the given class_id.
- void BulkAllocate(uptr class_id, AllocatorFreeList *free_list) {
+ Batch *NOINLINE AllocateBatch(AllocatorCache *c, uptr class_id) {
CHECK_LT(class_id, kNumClasses);
RegionInfo *region = GetRegionInfo(class_id);
- SpinMutexLock l(&region->mutex);
- if (region->free_list.empty()) {
- PopulateFreeList(class_id, region);
- }
- region->n_allocated += BulkMove(SizeClassMap::MaxCached(class_id),
- &region->free_list, free_list);
+ Batch *b = region->free_list.Pop();
+ if (b == 0)
+ b = PopulateFreeList(c, class_id, region);
+ region->n_allocated += b->count;
+ return b;
}
- // Swallow the entire free_list for the given class_id.
- void BulkDeallocate(uptr class_id, AllocatorFreeList *free_list) {
- CHECK_LT(class_id, kNumClasses);
+ void NOINLINE DeallocateBatch(uptr class_id, Batch *b) {
RegionInfo *region = GetRegionInfo(class_id);
- SpinMutexLock l(&region->mutex);
- region->n_freed += free_list->size();
- region->free_list.append_front(free_list);
+ region->free_list.Push(b);
+ region->n_freed += b->count;
}
static bool PointerIsMine(void *p) {
@@ -352,15 +332,15 @@ class SizeClassAllocator64 {
COMPILER_CHECK((kRegionSize) >= (1ULL << (SANITIZER_WORDSIZE / 2)));
// Populate the free list with at most this number of bytes at once
// or with one element if its size is greater.
- static const uptr kPopulateSize = 1 << 15;
+ static const uptr kPopulateSize = 1 << 14;
// Call mmap for user memory with at least this size.
static const uptr kUserMapSize = 1 << 15;
// Call mmap for metadata memory with at least this size.
static const uptr kMetaMapSize = 1 << 16;
struct RegionInfo {
- SpinMutex mutex;
- AllocatorFreeList free_list;
+ BlockingMutex mutex;
+ LFStack<Batch> free_list;
uptr allocated_user; // Bytes allocated for user memory.
uptr allocated_meta; // Bytes allocated for metadata.
uptr mapped_user; // Bytes mapped for user memory.
@@ -388,11 +368,16 @@ class SizeClassAllocator64 {
return offset / (u32)size;
}
- void PopulateFreeList(uptr class_id, RegionInfo *region) {
- CHECK(region->free_list.empty());
+ Batch *NOINLINE PopulateFreeList(AllocatorCache *c, uptr class_id,
+ RegionInfo *region) {
+ BlockingMutexLock l(&region->mutex);
+ Batch *b = region->free_list.Pop();
+ if (b)
+ return b;
uptr size = SizeClassMap::Size(class_id);
+ uptr count = size < kPopulateSize ? SizeClassMap::MaxCached(class_id) : 1;
uptr beg_idx = region->allocated_user;
- uptr end_idx = beg_idx + kPopulateSize;
+ uptr end_idx = beg_idx + count * size;
uptr region_beg = kSpaceBeg + kRegionSize * class_id;
if (end_idx + size > region->mapped_user) {
// Do the mmap for the user memory.
@@ -403,17 +388,9 @@ class SizeClassAllocator64 {
MapWithCallback(region_beg + region->mapped_user, map_size);
region->mapped_user += map_size;
}
- uptr idx = beg_idx;
- uptr i = 0;
- do { // do-while loop because we need to put at least one item.
- uptr p = region_beg + idx;
- region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p));
- idx += size;
- i++;
- } while (idx < end_idx);
- region->allocated_user += idx - beg_idx;
- CHECK_LE(region->allocated_user, region->mapped_user);
- region->allocated_meta += i * kMetadataSize;
+ uptr total_count = (region->mapped_user - beg_idx - size)
+ / size / count * count;
+ region->allocated_meta += total_count * kMetadataSize;
if (region->allocated_meta > region->mapped_meta) {
uptr map_size = kMetaMapSize;
while (region->allocated_meta > region->mapped_meta + map_size)
@@ -431,27 +408,22 @@ class SizeClassAllocator64 {
kRegionSize / 1024 / 1024, size);
Die();
}
- }
-
- void *AllocateBySizeClass(uptr class_id) {
- CHECK_LT(class_id, kNumClasses);
- RegionInfo *region = GetRegionInfo(class_id);
- SpinMutexLock l(&region->mutex);
- if (region->free_list.empty()) {
- PopulateFreeList(class_id, region);
+ for (;;) {
+ if (class_id < SizeClassMap::kMinBatchClass)
+ b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch)));
+ else
+ b = (Batch*)(region_beg + beg_idx);
+ b->count = count;
+ for (uptr i = 0; i < count; i++)
+ b->batch[i] = (void*)(region_beg + beg_idx + i * size);
+ region->allocated_user += count * size;
+ CHECK_LE(region->allocated_user, region->mapped_user);
+ beg_idx += count * size;
+ if (beg_idx + count * size + size > region->mapped_user)
+ break;
+ region->free_list.Push(b);
}
- CHECK(!region->free_list.empty());
- AllocatorListNode *node = region->free_list.front();
- region->free_list.pop_front();
- region->n_allocated++;
- return reinterpret_cast<void*>(node);
- }
-
- void DeallocateBySizeClass(void *p, uptr class_id) {
- RegionInfo *region = GetRegionInfo(class_id);
- SpinMutexLock l(&region->mutex);
- region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p));
- region->n_freed++;
+ return b;
}
};
@@ -480,6 +452,11 @@ template <const uptr kSpaceBeg, const u64 kSpaceSize,
class MapUnmapCallback = NoOpMapUnmapCallback>
class SizeClassAllocator32 {
public:
+ typedef typename SizeClassMap::TransferBatch Batch;
+ typedef SizeClassAllocator32<kSpaceBeg, kSpaceSize, kMetadataSize,
+ SizeClassMap, MapUnmapCallback> ThisT;
+ typedef SizeClassAllocatorLocalCache<ThisT> AllocatorCache;
+
void Init() {
state_ = reinterpret_cast<State *>(MapWithCallback(sizeof(State)));
}
@@ -500,17 +477,6 @@ class SizeClassAllocator32 {
alignment <= SizeClassMap::kMaxSize;
}
- void *Allocate(uptr size, uptr alignment) {
- if (size < alignment) size = alignment;
- CHECK(CanAllocate(size, alignment));
- return AllocateBySizeClass(ClassID(size));
- }
-
- void Deallocate(void *p) {
- CHECK(PointerIsMine(p));
- DeallocateBySizeClass(p, GetSizeClass(p));
- }
-
void *GetMetaData(void *p) {
CHECK(PointerIsMine(p));
uptr mem = reinterpret_cast<uptr>(p);
@@ -522,20 +488,23 @@ class SizeClassAllocator32 {
return reinterpret_cast<void*>(meta);
}
- // Allocate several chunks of the given class_id.
- void BulkAllocate(uptr class_id, AllocatorFreeList *free_list) {
+ Batch *NOINLINE AllocateBatch(AllocatorCache *c, uptr class_id) {
+ CHECK_LT(class_id, kNumClasses);
SizeClassInfo *sci = GetSizeClassInfo(class_id);
SpinMutexLock l(&sci->mutex);
- EnsureSizeClassHasAvailableChunks(sci, class_id);
+ if (sci->free_list.empty())
+ PopulateFreeList(c, sci, class_id);
CHECK(!sci->free_list.empty());
- BulkMove(SizeClassMap::MaxCached(class_id), &sci->free_list, free_list);
+ Batch *b = sci->free_list.front();
+ sci->free_list.pop_front();
+ return b;
}
- // Swallow the entire free_list for the given class_id.
- void BulkDeallocate(uptr class_id, AllocatorFreeList *free_list) {
+ void NOINLINE DeallocateBatch(uptr class_id, Batch *b) {
+ CHECK_LT(class_id, kNumClasses);
SizeClassInfo *sci = GetSizeClassInfo(class_id);
SpinMutexLock l(&sci->mutex);
- sci->free_list.append_front(free_list);
+ sci->free_list.push_front(b);
}
bool PointerIsMine(void *p) {
@@ -593,8 +562,8 @@ class SizeClassAllocator32 {
struct SizeClassInfo {
SpinMutex mutex;
- AllocatorFreeList free_list;
- char padding[kCacheLineSize - sizeof(uptr) - sizeof(AllocatorFreeList)];
+ IntrusiveList<Batch> free_list;
+ char padding[kCacheLineSize - sizeof(uptr) - sizeof(IntrusiveList<Batch>)];
};
COMPILER_CHECK(sizeof(SizeClassInfo) == kCacheLineSize);
@@ -624,31 +593,28 @@ class SizeClassAllocator32 {
return &state_->size_class_info_array[class_id];
}
- void EnsureSizeClassHasAvailableChunks(SizeClassInfo *sci, uptr class_id) {
- if (!sci->free_list.empty()) return;
+ void PopulateFreeList(AllocatorCache *c, SizeClassInfo *sci, uptr class_id) {
uptr size = SizeClassMap::Size(class_id);
uptr reg = AllocateRegion(class_id);
uptr n_chunks = kRegionSize / (size + kMetadataSize);
- for (uptr i = reg; i < reg + n_chunks * size; i += size)
- sci->free_list.push_back(reinterpret_cast<AllocatorListNode*>(i));
- }
-
- void *AllocateBySizeClass(uptr class_id) {
- CHECK_LT(class_id, kNumClasses);
- SizeClassInfo *sci = GetSizeClassInfo(class_id);
- SpinMutexLock l(&sci->mutex);
- EnsureSizeClassHasAvailableChunks(sci, class_id);
- CHECK(!sci->free_list.empty());
- AllocatorListNode *node = sci->free_list.front();
- sci->free_list.pop_front();
- return reinterpret_cast<void*>(node);
- }
-
- void DeallocateBySizeClass(void *p, uptr class_id) {
- CHECK_LT(class_id, kNumClasses);
- SizeClassInfo *sci = GetSizeClassInfo(class_id);
- SpinMutexLock l(&sci->mutex);
- sci->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p));
+ uptr max_count = SizeClassMap::MaxCached(class_id);
+ Batch *b = 0;
+ for (uptr i = reg; i < reg + n_chunks * size; i += size) {
+ if (b == 0) {
+ if (class_id < SizeClassMap::kMinBatchClass)
+ b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch)));
+ else
+ b = (Batch*)i;
+ b->count = 0;
+ }
+ b->batch[b->count++] = (void*)i;
+ if (b->count == max_count) {
+ sci->free_list.push_back(b);
+ b = 0;
+ }
+ }
+ if (b)
+ sci->free_list.push_back(b);
}
struct State {
@@ -658,13 +624,14 @@ class SizeClassAllocator32 {
State *state_;
};
-// Objects of this type should be used as local caches for SizeClassAllocator64.
-// Since the typical use of this class is to have one object per thread in TLS,
-// is has to be POD.
+// Objects of this type should be used as local caches for SizeClassAllocator64
+// or SizeClassAllocator32. Since the typical use of this class is to have one
+// object per thread in TLS, is has to be POD.
template<class SizeClassAllocator>
struct SizeClassAllocatorLocalCache {
typedef SizeClassAllocator Allocator;
static const uptr kNumClasses = SizeClassAllocator::kNumClasses;
+
// Don't need to call Init if the object is a global (i.e. zero-initialized).
void Init() {
internal_memset(this, 0, sizeof(*this));
@@ -673,46 +640,77 @@ struct SizeClassAllocatorLocalCache {
void *Allocate(SizeClassAllocator *allocator, uptr class_id) {
CHECK_NE(class_id, 0UL);
CHECK_LT(class_id, kNumClasses);
- AllocatorFreeList *free_list = &free_lists_[class_id];
- if (free_list->empty())
- allocator->BulkAllocate(class_id, free_list);
- CHECK(!free_list->empty());
- void *res = free_list->front();
- free_list->pop_front();
+ PerClass *c = &per_class_[class_id];
+ if (UNLIKELY(c->count == 0))
+ Refill(allocator, class_id);
+ void *res = c->batch[--c->count];
+ PREFETCH(c->batch[c->count - 1]);
return res;
}
void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) {
CHECK_NE(class_id, 0UL);
CHECK_LT(class_id, kNumClasses);
- AllocatorFreeList *free_list = &free_lists_[class_id];
- free_list->push_front(reinterpret_cast<AllocatorListNode*>(p));
- if (free_list->size() >= 2 * SizeClassMap::MaxCached(class_id))
- DrainHalf(allocator, class_id);
+ PerClass *c = &per_class_[class_id];
+ if (UNLIKELY(c->count == c->max_count))
+ Drain(allocator, class_id);
+ c->batch[c->count++] = p;
}
void Drain(SizeClassAllocator *allocator) {
- for (uptr i = 0; i < kNumClasses; i++) {
- allocator->BulkDeallocate(i, &free_lists_[i]);
- CHECK(free_lists_[i].empty());
+ for (uptr class_id = 0; class_id < kNumClasses; class_id++) {
+ PerClass *c = &per_class_[class_id];
+ while (c->count > 0)
+ Drain(allocator, class_id);
}
}
// private:
typedef typename SizeClassAllocator::SizeClassMapT SizeClassMap;
- AllocatorFreeList free_lists_[kNumClasses];
-
- void DrainHalf(SizeClassAllocator *allocator, uptr class_id) {
- AllocatorFreeList *free_list = &free_lists_[class_id];
- AllocatorFreeList half;
- half.clear();
- const uptr count = free_list->size() / 2;
- for (uptr i = 0; i < count; i++) {
- AllocatorListNode *node = free_list->front();
- free_list->pop_front();
- half.push_front(node);
+ typedef typename SizeClassMap::TransferBatch Batch;
+ struct PerClass {
+ uptr count;
+ uptr max_count;
+ void *batch[2 * SizeClassMap::kMaxNumCached];
+ };
+ PerClass per_class_[kNumClasses];
+
+ void InitCache() {
+ if (per_class_[0].max_count)
+ return;
+ for (uptr i = 0; i < kNumClasses; i++) {
+ PerClass *c = &per_class_[i];
+ c->max_count = 2 * SizeClassMap::MaxCached(i);
+ }
+ }
+
+ void NOINLINE Refill(SizeClassAllocator *allocator, uptr class_id) {
+ InitCache();
+ PerClass *c = &per_class_[class_id];
+ Batch *b = allocator->AllocateBatch(this, class_id);
+ for (uptr i = 0; i < b->count; i++)
+ c->batch[i] = b->batch[i];
+ c->count = b->count;
+ if (class_id < SizeClassMap::kMinBatchClass)
+ Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), b);
+ }
+
+ void NOINLINE Drain(SizeClassAllocator *allocator, uptr class_id) {
+ InitCache();
+ PerClass *c = &per_class_[class_id];
+ Batch *b;
+ if (class_id < SizeClassMap::kMinBatchClass)
+ b = (Batch*)Allocate(allocator, SizeClassMap::ClassID(sizeof(Batch)));
+ else
+ b = (Batch*)c->batch[0];
+ uptr cnt = Min(c->max_count / 2, c->count);
+ for (uptr i = 0; i < cnt; i++) {
+ b->batch[i] = c->batch[i];
+ c->batch[i] = c->batch[i + c->max_count / 2];
}
- allocator->BulkDeallocate(class_id, &half);
+ b->count = cnt;
+ c->count -= cnt;
+ allocator->DeallocateBatch(class_id, b);
}
};
@@ -726,6 +724,7 @@ class LargeMmapAllocator {
internal_memset(this, 0, sizeof(*this));
page_size_ = GetPageSizeCached();
}
+
void *Allocate(uptr size, uptr alignment) {
CHECK(IsPowerOfTwo(alignment));
uptr map_size = RoundUpMapSize(size);
@@ -745,6 +744,8 @@ class LargeMmapAllocator {
h->size = size;
h->map_beg = map_beg;
h->map_size = map_size;
+ uptr size_log = SANITIZER_WORDSIZE - __builtin_clzl(map_size) - 1;
+ CHECK_LT(size_log, ARRAY_SIZE(stats.by_size_log));
{
SpinMutexLock l(&mutex_);
uptr idx = n_chunks_++;
@@ -754,6 +755,7 @@ class LargeMmapAllocator {
stats.n_allocs++;
stats.currently_allocated += map_size;
stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated);
+ stats.by_size_log[size_log]++;
}
return reinterpret_cast<void*>(res);
}
@@ -825,9 +827,15 @@ class LargeMmapAllocator {
void PrintStats() {
Printf("Stats: LargeMmapAllocator: allocated %zd times, "
- "remains %zd (%zd K) max %zd M\n",
+ "remains %zd (%zd K) max %zd M; by size logs: ",
stats.n_allocs, stats.n_allocs - stats.n_frees,
stats.currently_allocated >> 10, stats.max_allocated >> 20);
+ for (uptr i = 0; i < ARRAY_SIZE(stats.by_size_log); i++) {
+ uptr c = stats.by_size_log[i];
+ if (!c) continue;
+ Printf("%zd:%zd; ", i, c);
+ }
+ Printf("\n");
}
private:
@@ -858,7 +866,7 @@ class LargeMmapAllocator {
Header *chunks_[kMaxNumChunks];
uptr n_chunks_;
struct Stats {
- uptr n_allocs, n_frees, currently_allocated, max_allocated;
+ uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64];
} stats;
SpinMutex mutex_;
};
@@ -888,14 +896,10 @@ class CombinedAllocator {
if (alignment > 8)
size = RoundUpTo(size, alignment);
void *res;
- if (primary_.CanAllocate(size, alignment)) {
- if (cache) // Allocate from cache.
- res = cache->Allocate(&primary_, primary_.ClassID(size));
- else // No thread-local cache, allocate directly from primary allocator.
- res = primary_.Allocate(size, alignment);
- } else { // Secondary allocator does not use cache.
+ if (primary_.CanAllocate(size, alignment))
+ res = cache->Allocate(&primary_, primary_.ClassID(size));
+ else
res = secondary_.Allocate(size, alignment);
- }
if (alignment > 8)
CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
if (cleared && res)
diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h b/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h
index 68e79f6a2f1..bb4611d51e6 100644
--- a/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h
+++ b/libsanitizer/sanitizer_common/sanitizer_atomic_clang.h
@@ -39,6 +39,7 @@ INLINE typename T::Type atomic_load(
| memory_order_acquire | memory_order_seq_cst));
DCHECK(!((uptr)a % sizeof(*a)));
typename T::Type v;
+ // FIXME(dvyukov): 64-bit load is not atomic on 32-bits.
if (mo == memory_order_relaxed) {
v = a->val_dont_use;
} else {
@@ -54,6 +55,7 @@ INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
DCHECK(mo & (memory_order_relaxed | memory_order_release
| memory_order_seq_cst));
DCHECK(!((uptr)a % sizeof(*a)));
+ // FIXME(dvyukov): 64-bit store is not atomic on 32-bits.
if (mo == memory_order_relaxed) {
a->val_dont_use = v;
} else {
diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h
index 55e00e2204c..919e24f3b11 100644
--- a/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h
+++ b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h
@@ -70,6 +70,7 @@ INLINE typename T::Type atomic_load(
| memory_order_acquire | memory_order_seq_cst));
DCHECK(!((uptr)a % sizeof(*a)));
typename T::Type v;
+ // FIXME(dvyukov): 64-bit load is not atomic on 32-bits.
if (mo == memory_order_relaxed) {
v = a->val_dont_use;
} else {
@@ -85,6 +86,7 @@ INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
DCHECK(mo & (memory_order_relaxed | memory_order_release
| memory_order_seq_cst));
DCHECK(!((uptr)a % sizeof(*a)));
+ // FIXME(dvyukov): 64-bit store is not atomic on 32-bits.
if (mo == memory_order_relaxed) {
a->val_dont_use = v;
} else {
diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc
index 96e8808f6d1..9b70ee0eb54 100644
--- a/libsanitizer/sanitizer_common/sanitizer_common.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_common.cc
@@ -21,10 +21,16 @@ uptr GetPageSizeCached() {
return PageSize;
}
-// By default, dump to stderr. If report_fd is kInvalidFd, try to obtain file
-// descriptor by opening file in report_path.
+static bool log_to_file = false; // Set to true by __sanitizer_set_report_path
+
+// By default, dump to stderr. If |log_to_file| is true and |report_fd_pid|
+// isn't equal to the current PID, try to obtain file descriptor by opening
+// file "report_path_prefix.<PID>".
static fd_t report_fd = kStderrFd;
-static char report_path[4096]; // Set via __sanitizer_set_report_path.
+static char report_path_prefix[4096]; // Set via __sanitizer_set_report_path.
+// PID of process that opened |report_fd|. If a fork() occurs, the PID of the
+// child thread will be different from |report_fd_pid|.
+static int report_fd_pid = 0;
static void (*DieCallback)(void);
void SetDieCallback(void (*callback)(void)) {
@@ -48,21 +54,29 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
if (CheckFailedCallback) {
CheckFailedCallback(file, line, cond, v1, v2);
}
- Report("Sanitizer CHECK failed: %s:%d %s (%zd, %zd)\n", file, line, cond,
- v1, v2);
+ Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
+ v1, v2);
Die();
}
static void MaybeOpenReportFile() {
- if (report_fd != kInvalidFd)
- return;
- fd_t fd = internal_open(report_path, true);
+ if (!log_to_file || (report_fd_pid == GetPid())) return;
+ InternalScopedBuffer<char> report_path_full(4096);
+ internal_snprintf(report_path_full.data(), report_path_full.size(),
+ "%s.%d", report_path_prefix, GetPid());
+ fd_t fd = internal_open(report_path_full.data(), true);
if (fd == kInvalidFd) {
report_fd = kStderrFd;
- Report("ERROR: Can't open file: %s\n", report_path);
+ log_to_file = false;
+ Report("ERROR: Can't open file: %s\n", report_path_full.data());
Die();
}
+ if (report_fd != kInvalidFd) {
+ // We're in the child. Close the parent's log.
+ internal_close(report_fd);
+ }
report_fd = fd;
+ report_fd_pid = GetPid();
}
bool PrintsToTty() {
@@ -182,14 +196,16 @@ extern "C" {
void __sanitizer_set_report_path(const char *path) {
if (!path) return;
uptr len = internal_strlen(path);
- if (len > sizeof(report_path) - 100) {
+ if (len > sizeof(report_path_prefix) - 100) {
Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
path[0], path[1], path[2], path[3],
path[4], path[5], path[6], path[7]);
Die();
}
- internal_snprintf(report_path, sizeof(report_path), "%s.%d", path, GetPid());
+ internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix));
+ report_path_prefix[len] = '\0';
report_fd = kInvalidFd;
+ log_to_file = true;
}
void __sanitizer_set_report_fd(int fd) {
diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.h
deleted file mode 100644
index 97c6b6f7beb..00000000000
--- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.h
+++ /dev/null
@@ -1,77 +0,0 @@
-//===-- sanitizer_common_interceptors.h -------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Common function interceptors for tools like AddressSanitizer,
-// ThreadSanitizer, MemorySanitizer, etc.
-//
-// This file should be included into the tool's interceptor file,
-// which has to define it's own macros:
-// COMMON_INTERCEPTOR_ENTER
-// COMMON_INTERCEPTOR_READ_RANGE
-// COMMON_INTERCEPTOR_WRITE_RANGE
-//
-//===----------------------------------------------------------------------===//
-#ifndef SANITIZER_COMMON_INTERCEPTORS_H
-#define SANITIZER_COMMON_INTERCEPTORS_H
-
-#include "interception/interception.h"
-#include "sanitizer_platform_interceptors.h"
-
-#if SANITIZER_INTERCEPT_READ
-INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
- COMMON_INTERCEPTOR_ENTER(read, fd, ptr, count);
- SSIZE_T res = REAL(read)(fd, ptr, count);
- if (res > 0)
- COMMON_INTERCEPTOR_WRITE_RANGE(ptr, res);
- return res;
-}
-#endif
-
-#if SANITIZER_INTERCEPT_PREAD
-INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
- COMMON_INTERCEPTOR_ENTER(pread, fd, ptr, count, offset);
- SSIZE_T res = REAL(pread)(fd, ptr, count, offset);
- if (res > 0)
- COMMON_INTERCEPTOR_WRITE_RANGE(ptr, res);
- return res;
-}
-#endif
-
-#if SANITIZER_INTERCEPT_PREAD64
-INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
- COMMON_INTERCEPTOR_ENTER(pread64, fd, ptr, count, offset);
- SSIZE_T res = REAL(pread64)(fd, ptr, count, offset);
- if (res > 0)
- COMMON_INTERCEPTOR_WRITE_RANGE(ptr, res);
- return res;
-}
-#endif
-
-#if SANITIZER_INTERCEPT_READ
-# define INIT_READ INTERCEPT_FUNCTION(read)
-#else
-# define INIT_READ
-#endif
-
-#if SANITIZER_INTERCEPT_PREAD
-# define INIT_PREAD INTERCEPT_FUNCTION(pread)
-#else
-# define INIT_PREAD
-#endif
-
-#if SANITIZER_INTERCEPT_PREAD64
-# define INIT_PREAD64 INTERCEPT_FUNCTION(pread64)
-#else
-# define INIT_PREAD64
-#endif
-
-#define SANITIZER_COMMON_INTERCEPTORS_INIT \
- INIT_READ; \
- INIT_PREAD; \
- INIT_PREAD64; \
-
-#endif // SANITIZER_COMMON_INTERCEPTORS_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc
new file mode 100644
index 00000000000..724a326ef4c
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc
@@ -0,0 +1,222 @@
+//===-- sanitizer_common_interceptors.inc -----------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Common function interceptors for tools like AddressSanitizer,
+// ThreadSanitizer, MemorySanitizer, etc.
+//
+// This file should be included into the tool's interceptor file,
+// which has to define it's own macros:
+// COMMON_INTERCEPTOR_ENTER
+// COMMON_INTERCEPTOR_READ_RANGE
+// COMMON_INTERCEPTOR_WRITE_RANGE
+// COMMON_INTERCEPTOR_FD_ACQUIRE
+// COMMON_INTERCEPTOR_FD_RELEASE
+// COMMON_INTERCEPTOR_SET_THREAD_NAME
+//===----------------------------------------------------------------------===//
+#include "interception/interception.h"
+#include "sanitizer_platform_interceptors.h"
+
+#include <stdarg.h>
+
+#if SANITIZER_INTERCEPT_READ
+INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count);
+ SSIZE_T res = REAL(read)(fd, ptr, count);
+ if (res > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
+ if (res >= 0 && fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ return res;
+}
+# define INIT_READ INTERCEPT_FUNCTION(read)
+#else
+# define INIT_READ
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD
+INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset);
+ SSIZE_T res = REAL(pread)(fd, ptr, count, offset);
+ if (res > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
+ if (res >= 0 && fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ return res;
+}
+# define INIT_PREAD INTERCEPT_FUNCTION(pread)
+#else
+# define INIT_PREAD
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD64
+INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset);
+ SSIZE_T res = REAL(pread64)(fd, ptr, count, offset);
+ if (res > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
+ if (res >= 0 && fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ return res;
+}
+# define INIT_PREAD64 INTERCEPT_FUNCTION(pread64)
+#else
+# define INIT_PREAD64
+#endif
+
+#if SANITIZER_INTERCEPT_WRITE
+INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+ SSIZE_T res = REAL(write)(fd, ptr, count);
+ if (res > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
+ return res;
+}
+# define INIT_WRITE INTERCEPT_FUNCTION(write)
+#else
+# define INIT_WRITE
+#endif
+
+#if SANITIZER_INTERCEPT_PWRITE
+INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+ SSIZE_T res = REAL(pwrite)(fd, ptr, count);
+ if (res > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
+ return res;
+}
+# define INIT_PWRITE INTERCEPT_FUNCTION(pwrite)
+#else
+# define INIT_PWRITE
+#endif
+
+#if SANITIZER_INTERCEPT_PWRITE64
+INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+ SSIZE_T res = REAL(pwrite64)(fd, ptr, count);
+ if (res > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
+ return res;
+}
+# define INIT_PWRITE64 INTERCEPT_FUNCTION(pwrite64)
+#else
+# define INIT_PWRITE64
+#endif
+
+#if SANITIZER_INTERCEPT_PRCTL
+INTERCEPTOR(int, prctl, int option,
+ unsigned long arg2, unsigned long arg3, // NOLINT
+ unsigned long arg4, unsigned long arg5) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5);
+ static const int PR_SET_NAME = 15;
+ int res = REAL(prctl(option, arg2, arg3, arg4, arg5));
+ if (option == PR_SET_NAME) {
+ char buff[16];
+ internal_strncpy(buff, (char*)arg2, 15);
+ buff[15] = 0;
+ COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff);
+ }
+ return res;
+}
+# define INIT_PRCTL INTERCEPT_FUNCTION(prctl)
+#else
+# define INIT_PRCTL
+#endif // SANITIZER_INTERCEPT_PRCTL
+
+
+#if SANITIZER_INTERCEPT_SCANF
+
+#include "sanitizer_common_interceptors_scanf.inc"
+
+INTERCEPTOR(int, vscanf, const char *format, va_list ap) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, vscanf, format, ap);
+ scanf_common(ctx, format, ap);
+ int res = REAL(vscanf)(format, ap); // NOLINT
+ return res;
+}
+
+INTERCEPTOR(int, vsscanf, const char *str, const char *format, // NOLINT
+ va_list ap) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, vsscanf, str, format, ap);
+ scanf_common(ctx, format, ap);
+ int res = REAL(vsscanf)(str, format, ap); // NOLINT
+ // FIXME: read of str
+ return res;
+}
+
+INTERCEPTOR(int, vfscanf, void *stream, const char *format, // NOLINT
+ va_list ap) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, vfscanf, stream, format, ap);
+ scanf_common(ctx, format, ap);
+ int res = REAL(vfscanf)(stream, format, ap); // NOLINT
+ return res;
+}
+
+INTERCEPTOR(int, scanf, const char *format, ...) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, scanf, format);
+ va_list ap;
+ va_start(ap, format);
+ int res = vscanf(format, ap); // NOLINT
+ va_end(ap);
+ return res;
+}
+
+INTERCEPTOR(int, fscanf, void* stream, const char *format, ...) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fscanf, stream, format);
+ va_list ap;
+ va_start(ap, format);
+ int res = vfscanf(stream, format, ap); // NOLINT
+ va_end(ap);
+ return res;
+}
+
+INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sscanf, str, format); // NOLINT
+ va_list ap;
+ va_start(ap, format);
+ int res = vsscanf(str, format, ap); // NOLINT
+ va_end(ap);
+ return res;
+}
+
+#define INIT_SCANF \
+ INTERCEPT_FUNCTION(scanf); \
+ INTERCEPT_FUNCTION(sscanf); /* NOLINT */ \
+ INTERCEPT_FUNCTION(fscanf); \
+ INTERCEPT_FUNCTION(vscanf); \
+ INTERCEPT_FUNCTION(vsscanf); \
+ INTERCEPT_FUNCTION(vfscanf)
+
+#else
+#define INIT_SCANF
+#endif
+
+#define SANITIZER_COMMON_INTERCEPTORS_INIT \
+ INIT_READ; \
+ INIT_PREAD; \
+ INIT_PREAD64; \
+ INIT_PRCTL; \
+ INIT_WRITE; \
+ INIT_SCANF;
diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc
new file mode 100644
index 00000000000..f7cab5f0dbb
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_scanf.inc
@@ -0,0 +1,144 @@
+//===-- sanitizer_common_interceptors_scanf.inc -----------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Scanf implementation for use in *Sanitizer interceptors.
+//
+//===----------------------------------------------------------------------===//
+#include <stdarg.h>
+
+#ifdef _WIN32
+#define va_copy(dst, src) ((dst) = (src))
+#endif // _WIN32
+
+struct ScanfSpec {
+ char c;
+ unsigned size;
+};
+
+// One-letter specs.
+static const ScanfSpec scanf_specs[] = {
+ {'p', sizeof(void *)},
+ {'e', sizeof(float)},
+ {'E', sizeof(float)},
+ {'a', sizeof(float)},
+ {'f', sizeof(float)},
+ {'g', sizeof(float)},
+ {'d', sizeof(int)},
+ {'i', sizeof(int)},
+ {'o', sizeof(int)},
+ {'u', sizeof(int)},
+ {'x', sizeof(int)},
+ {'X', sizeof(int)},
+ {'n', sizeof(int)},
+ {'t', sizeof(PTRDIFF_T)},
+ {'z', sizeof(SIZE_T)},
+ {'j', sizeof(INTMAX_T)},
+ {'h', sizeof(short)}
+};
+
+static const unsigned scanf_specs_cnt =
+ sizeof(scanf_specs) / sizeof(scanf_specs[0]);
+
+// %ll?, %L?, %q? specs
+static const ScanfSpec scanf_llspecs[] = {
+ {'e', sizeof(long double)},
+ {'f', sizeof(long double)},
+ {'g', sizeof(long double)},
+ {'d', sizeof(long long)},
+ {'i', sizeof(long long)},
+ {'o', sizeof(long long)},
+ {'u', sizeof(long long)},
+ {'x', sizeof(long long)}
+};
+
+static const unsigned scanf_llspecs_cnt =
+ sizeof(scanf_llspecs) / sizeof(scanf_llspecs[0]);
+
+// %l? specs
+static const ScanfSpec scanf_lspecs[] = {
+ {'e', sizeof(double)},
+ {'f', sizeof(double)},
+ {'g', sizeof(double)},
+ {'d', sizeof(long)},
+ {'i', sizeof(long)},
+ {'o', sizeof(long)},
+ {'u', sizeof(long)},
+ {'x', sizeof(long)},
+ {'X', sizeof(long)},
+};
+
+static const unsigned scanf_lspecs_cnt =
+ sizeof(scanf_lspecs) / sizeof(scanf_lspecs[0]);
+
+static unsigned match_spec(const struct ScanfSpec *spec, unsigned n, char c) {
+ for (unsigned i = 0; i < n; ++i)
+ if (spec[i].c == c)
+ return spec[i].size;
+ return 0;
+}
+
+static void scanf_common(void *ctx, const char *format, va_list ap_const) {
+ va_list aq;
+ va_copy(aq, ap_const);
+
+ const char *p = format;
+ unsigned size;
+
+ while (*p) {
+ if (*p != '%') {
+ ++p;
+ continue;
+ }
+ ++p;
+ if (*p == '*' || *p == '%' || *p == 0) {
+ ++p;
+ continue;
+ }
+ if (*p == '0' || (*p >= '1' && *p <= '9')) {
+ size = internal_atoll(p);
+ // +1 for the \0 at the end
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size + 1);
+ ++p;
+ continue;
+ }
+
+ if (*p == 'L' || *p == 'q') {
+ ++p;
+ size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
+ continue;
+ }
+
+ if (*p == 'l') {
+ ++p;
+ if (*p == 'l') {
+ ++p;
+ size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
+ continue;
+ } else {
+ size = match_spec(scanf_lspecs, scanf_lspecs_cnt, *p);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
+ continue;
+ }
+ }
+
+ if (*p == 'h' && *(p + 1) == 'h') {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), sizeof(char));
+ p += 2;
+ continue;
+ }
+
+ size = match_spec(scanf_specs, scanf_specs_cnt, *p);
+ if (size) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
+ ++p;
+ continue;
+ }
+ }
+ va_end(aq);
+}
diff --git a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h
index a6795c6720b..01f08f57801 100644
--- a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h
+++ b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h
@@ -36,6 +36,7 @@ using namespace __sanitizer; // NOLINT
# define UNLIKELY(x) (x)
# define UNUSED
# define USED
+# define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */
#else // _MSC_VER
# define ALWAYS_INLINE __attribute__((always_inline))
# define ALIAS(x) __attribute__((alias(x)))
@@ -49,6 +50,12 @@ using namespace __sanitizer; // NOLINT
# define UNLIKELY(x) __builtin_expect(!!(x), 0)
# define UNUSED __attribute__((unused))
# define USED __attribute__((used))
+# if defined(__i386__) || defined(__x86_64__)
+// __builtin_prefetch(x) generates prefetchnt0 on x86
+# define PREFETCH(x) __asm__("prefetchnta (%0)" : : "r" (x))
+# else
+# define PREFETCH(x) __builtin_prefetch(x)
+# endif
#endif // _MSC_VER
#if defined(_WIN32)
diff --git a/libsanitizer/sanitizer_common/sanitizer_lfstack.h b/libsanitizer/sanitizer_common/sanitizer_lfstack.h
new file mode 100644
index 00000000000..63fbf066943
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_lfstack.h
@@ -0,0 +1,71 @@
+//===-- sanitizer_lfstack.h -=-----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Lock-free stack.
+// Uses 32/17 bits as ABA-counter on 32/64-bit platforms.
+// The memory passed to Push() must not be ever munmap'ed.
+// The type T must contain T *next field.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_LFSTACK_H
+#define SANITIZER_LFSTACK_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_common.h"
+#include "sanitizer_atomic.h"
+
+namespace __sanitizer {
+
+template<typename T>
+struct LFStack {
+ void Clear() {
+ atomic_store(&head_, 0, memory_order_relaxed);
+ }
+
+ bool Empty() const {
+ return (atomic_load(&head_, memory_order_relaxed) & kPtrMask) == 0;
+ }
+
+ void Push(T *p) {
+ u64 cmp = atomic_load(&head_, memory_order_relaxed);
+ for (;;) {
+ u64 cnt = (cmp & kCounterMask) + kCounterInc;
+ u64 xch = (u64)(uptr)p | cnt;
+ p->next = (T*)(uptr)(cmp & kPtrMask);
+ if (atomic_compare_exchange_weak(&head_, &cmp, xch,
+ memory_order_release))
+ break;
+ }
+ }
+
+ T *Pop() {
+ u64 cmp = atomic_load(&head_, memory_order_acquire);
+ for (;;) {
+ T *cur = (T*)(uptr)(cmp & kPtrMask);
+ if (cur == 0)
+ return 0;
+ T *nxt = cur->next;
+ u64 cnt = (cmp & kCounterMask);
+ u64 xch = (u64)(uptr)nxt | cnt;
+ if (atomic_compare_exchange_weak(&head_, &cmp, xch,
+ memory_order_acquire))
+ return cur;
+ }
+ }
+
+ // private:
+ static const int kCounterBits = FIRST_32_SECOND_64(32, 17);
+ static const u64 kPtrMask = ((u64)-1) >> kCounterBits;
+ static const u64 kCounterMask = ~kPtrMask;
+ static const u64 kCounterInc = kPtrMask + 1;
+
+ atomic_uint64_t head_;
+};
+}
+
+#endif // #ifndef SANITIZER_LFSTACK_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cc b/libsanitizer/sanitizer_common/sanitizer_linux.cc
index 1d0bf02192c..dc2148f7fc4 100644
--- a/libsanitizer/sanitizer_common/sanitizer_linux.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_linux.cc
@@ -32,6 +32,7 @@
#include <unwind.h>
#include <errno.h>
#include <sys/prctl.h>
+#include <linux/futex.h>
// Are we using 32-bit or 64-bit syscalls?
// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
@@ -198,24 +199,31 @@ const char *GetEnv(const char *name) {
return 0; // Not found.
}
-void ReExec() {
- static const int kMaxArgv = 100;
- InternalScopedBuffer<char*> argv(kMaxArgv + 1);
- static char *buff;
+static void ReadNullSepFileToArray(const char *path, char ***arr,
+ int arr_size) {
+ char *buff;
uptr buff_size = 0;
- ReadFileToBuffer("/proc/self/cmdline", &buff, &buff_size, 1024 * 1024);
- argv[0] = buff;
- int argc, i;
- for (argc = 1, i = 1; ; i++) {
+ *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray");
+ ReadFileToBuffer(path, &buff, &buff_size, 1024 * 1024);
+ (*arr)[0] = buff;
+ int count, i;
+ for (count = 1, i = 1; ; i++) {
if (buff[i] == 0) {
if (buff[i+1] == 0) break;
- argv[argc] = &buff[i+1];
- CHECK_LE(argc, kMaxArgv); // FIXME: make this more flexible.
- argc++;
+ (*arr)[count] = &buff[i+1];
+ CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible.
+ count++;
}
}
- argv[argc] = 0;
- execv(argv[0], argv.data());
+ (*arr)[count] = 0;
+}
+
+void ReExec() {
+ static const int kMaxArgv = 100, kMaxEnvp = 1000;
+ char **argv, **envp;
+ ReadNullSepFileToArray("/proc/self/cmdline", &argv, kMaxArgv);
+ ReadNullSepFileToArray("/proc/self/environ", &envp, kMaxEnvp);
+ execve(argv[0], argv, envp);
}
void PrepareForSandboxing() {
@@ -366,16 +374,24 @@ bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
}
bool SanitizerSetThreadName(const char *name) {
+#ifdef PR_SET_NAME
return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); // NOLINT
+#else
+ return false;
+#endif
}
bool SanitizerGetThreadName(char *name, int max_len) {
+#ifdef PR_GET_NAME
char buff[17];
if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0)) // NOLINT
return false;
internal_strncpy(name, buff, max_len);
name[max_len] = 0;
return true;
+#else
+ return false;
+#endif
}
#ifndef SANITIZER_GO
@@ -434,6 +450,32 @@ void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
#endif // #ifndef SANITIZER_GO
+enum MutexState {
+ MtxUnlocked = 0,
+ MtxLocked = 1,
+ MtxSleeping = 2
+};
+
+BlockingMutex::BlockingMutex(LinkerInitialized) {
+ CHECK_EQ(owner_, 0);
+}
+
+void BlockingMutex::Lock() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
+ return;
+ while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked)
+ syscall(__NR_futex, m, FUTEX_WAIT, MtxSleeping, 0, 0, 0);
+}
+
+void BlockingMutex::Unlock() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed);
+ CHECK_NE(v, MtxUnlocked);
+ if (v == MtxSleeping)
+ syscall(__NR_futex, m, FUTEX_WAKE, 1, 0, 0, 0);
+}
+
} // namespace __sanitizer
#endif // __linux__
diff --git a/libsanitizer/sanitizer_common/sanitizer_list.h b/libsanitizer/sanitizer_common/sanitizer_list.h
index 3df12f550a0..9692e01b8e0 100644
--- a/libsanitizer/sanitizer_common/sanitizer_list.h
+++ b/libsanitizer/sanitizer_common/sanitizer_list.h
@@ -70,6 +70,8 @@ struct IntrusiveList {
void append_front(IntrusiveList<Item> *l) {
CHECK_NE(this, l);
+ if (l->empty())
+ return;
if (empty()) {
*this = *l;
} else if (!l->empty()) {
@@ -82,6 +84,8 @@ struct IntrusiveList {
void append_back(IntrusiveList<Item> *l) {
CHECK_NE(this, l);
+ if (l->empty())
+ return;
if (empty()) {
*this = *l;
} else {
diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_mac.cc
index 0f64b306afb..76bf8670870 100644
--- a/libsanitizer/sanitizer_common/sanitizer_mac.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_mac.cc
@@ -28,6 +28,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <libkern/OSAtomic.h>
namespace __sanitizer {
@@ -265,6 +266,25 @@ bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
return IterateForObjectNameAndOffset(addr, offset, filename, filename_size);
}
+BlockingMutex::BlockingMutex(LinkerInitialized) {
+ // We assume that OS_SPINLOCK_INIT is zero
+}
+
+void BlockingMutex::Lock() {
+ CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
+ CHECK(OS_SPINLOCK_INIT == 0);
+ CHECK(owner_ != (uptr)pthread_self());
+ OSSpinLockLock((OSSpinLock*)&opaque_storage_);
+ CHECK(!owner_);
+ owner_ = (uptr)pthread_self();
+}
+
+void BlockingMutex::Unlock() {
+ CHECK(owner_ == (uptr)pthread_self());
+ owner_ = 0;
+ OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
+}
+
} // namespace __sanitizer
#endif // __APPLE__
diff --git a/libsanitizer/sanitizer_common/sanitizer_mutex.h b/libsanitizer/sanitizer_common/sanitizer_mutex.h
index a38a49ae242..27009118e62 100644
--- a/libsanitizer/sanitizer_common/sanitizer_mutex.h
+++ b/libsanitizer/sanitizer_common/sanitizer_mutex.h
@@ -25,11 +25,15 @@ class StaticSpinMutex {
}
void Lock() {
- if (atomic_exchange(&state_, 1, memory_order_acquire) == 0)
+ if (TryLock())
return;
LockSlow();
}
+ bool TryLock() {
+ return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
+ }
+
void Unlock() {
atomic_store(&state_, 0, memory_order_release);
}
@@ -61,6 +65,16 @@ class SpinMutex : public StaticSpinMutex {
void operator=(const SpinMutex&);
};
+class BlockingMutex {
+ public:
+ explicit BlockingMutex(LinkerInitialized);
+ void Lock();
+ void Unlock();
+ private:
+ uptr opaque_storage_[10];
+ uptr owner_; // for debugging
+};
+
template<typename MutexType>
class GenericScopedLock {
public:
@@ -100,6 +114,7 @@ class GenericScopedReadLock {
};
typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
+typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h
index e32206cb6d4..0ca9444fcb8 100644
--- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h
@@ -13,15 +13,24 @@
#include "sanitizer_internal_defs.h"
#if !defined(_WIN32)
-# define SANITIZER_INTERCEPT_READ 1
-# define SANITIZER_INTERCEPT_PREAD 1
+# define SI_NOT_WINDOWS 1
#else
-# define SANITIZER_INTERCEPT_READ 0
-# define SANITIZER_INTERCEPT_PREAD 0
+# define SI_NOT_WINDOWS 0
#endif
#if defined(__linux__) && !defined(ANDROID)
-# define SANITIZER_INTERCEPT_PREAD64 1
+# define SI_LINUX_NOT_ANDROID 1
#else
-# define SANITIZER_INTERCEPT_PREAD64 0
+# define SI_LINUX_NOT_ANDROID 0
#endif
+
+# define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_WRITE SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS
+
+# define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID
+# define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID
+# define SANITIZER_INTERCEPT_PRCTL SI_LINUX_NOT_ANDROID
+
+# define SANITIZER_INTERCEPT_SCANF 0
diff --git a/libsanitizer/sanitizer_common/sanitizer_quarantine.h b/libsanitizer/sanitizer_common/sanitizer_quarantine.h
new file mode 100644
index 00000000000..042fba7c1da
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_quarantine.h
@@ -0,0 +1,170 @@
+//===-- sanitizer_quarantine.h ----------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Memory quarantine for AddressSanitizer and potentially other tools.
+// Quarantine caches some specified amount of memory in per-thread caches,
+// then evicts to global FIFO queue. When the queue reaches specified threshold,
+// oldest memory is recycled.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_QUARANTINE_H
+#define SANITIZER_QUARANTINE_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_list.h"
+
+namespace __sanitizer {
+
+template<typename Node> class QuarantineCache;
+
+struct QuarantineBatch {
+ static const uptr kSize = 1024;
+ QuarantineBatch *next;
+ uptr size;
+ uptr count;
+ void *batch[kSize];
+};
+
+// The callback interface is:
+// void Callback::Recycle(Node *ptr);
+// void *cb.Allocate(uptr size);
+// void cb.Deallocate(void *ptr);
+template<typename Callback, typename Node>
+class Quarantine {
+ public:
+ typedef QuarantineCache<Callback> Cache;
+
+ explicit Quarantine(LinkerInitialized)
+ : cache_(LINKER_INITIALIZED) {
+ }
+
+ void Init(uptr size, uptr cache_size) {
+ max_size_ = size;
+ min_size_ = size / 10 * 9; // 90% of max size.
+ max_cache_size_ = cache_size;
+ }
+
+ void Put(Cache *c, Callback cb, Node *ptr, uptr size) {
+ c->Enqueue(cb, ptr, size);
+ if (c->Size() > max_cache_size_)
+ Drain(c, cb);
+ }
+
+ void NOINLINE Drain(Cache *c, Callback cb) {
+ {
+ SpinMutexLock l(&cache_mutex_);
+ cache_.Transfer(c);
+ }
+ if (cache_.Size() > max_size_ && recycle_mutex_.TryLock())
+ Recycle(cb);
+ }
+
+ private:
+ // Read-only data.
+ char pad0_[kCacheLineSize];
+ uptr max_size_;
+ uptr min_size_;
+ uptr max_cache_size_;
+ char pad1_[kCacheLineSize];
+ SpinMutex cache_mutex_;
+ SpinMutex recycle_mutex_;
+ Cache cache_;
+ char pad2_[kCacheLineSize];
+
+ void NOINLINE Recycle(Callback cb) {
+ Cache tmp;
+ {
+ SpinMutexLock l(&cache_mutex_);
+ while (cache_.Size() > min_size_) {
+ QuarantineBatch *b = cache_.DequeueBatch();
+ tmp.EnqueueBatch(b);
+ }
+ }
+ recycle_mutex_.Unlock();
+ DoRecycle(&tmp, cb);
+ }
+
+ void NOINLINE DoRecycle(Cache *c, Callback cb) {
+ while (QuarantineBatch *b = c->DequeueBatch()) {
+ const uptr kPrefetch = 16;
+ for (uptr i = 0; i < kPrefetch; i++)
+ PREFETCH(b->batch[i]);
+ for (uptr i = 0; i < b->count; i++) {
+ PREFETCH(b->batch[i + kPrefetch]);
+ cb.Recycle((Node*)b->batch[i]);
+ }
+ cb.Deallocate(b);
+ }
+ }
+};
+
+// Per-thread cache of memory blocks.
+template<typename Callback>
+class QuarantineCache {
+ public:
+ explicit QuarantineCache(LinkerInitialized) {
+ }
+
+ QuarantineCache()
+ : size_() {
+ list_.clear();
+ }
+
+ uptr Size() const {
+ return atomic_load(&size_, memory_order_relaxed);
+ }
+
+ void Enqueue(Callback cb, void *ptr, uptr size) {
+ if (list_.empty() || list_.back()->count == QuarantineBatch::kSize)
+ AllocBatch(cb);
+ QuarantineBatch *b = list_.back();
+ b->batch[b->count++] = ptr;
+ b->size += size;
+ SizeAdd(size);
+ }
+
+ void Transfer(QuarantineCache *c) {
+ list_.append_back(&c->list_);
+ SizeAdd(c->Size());
+ atomic_store(&c->size_, 0, memory_order_relaxed);
+ }
+
+ void EnqueueBatch(QuarantineBatch *b) {
+ list_.push_back(b);
+ SizeAdd(b->size);
+ }
+
+ QuarantineBatch *DequeueBatch() {
+ if (list_.empty())
+ return 0;
+ QuarantineBatch *b = list_.front();
+ list_.pop_front();
+ SizeAdd(-b->size);
+ return b;
+ }
+
+ private:
+ IntrusiveList<QuarantineBatch> list_;
+ atomic_uintptr_t size_;
+
+ void SizeAdd(uptr add) {
+ atomic_store(&size_, Size() + add, memory_order_relaxed);
+ }
+
+ QuarantineBatch *NOINLINE AllocBatch(Callback cb) {
+ QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b));
+ b->count = 0;
+ b->size = 0;
+ list_.push_back(b);
+ return b;
+ }
+};
+}
+
+#endif // #ifndef SANITIZER_QUARANTINE_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc
index 11393e44503..f62acf35f8f 100644
--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc
@@ -66,7 +66,18 @@ static const char *ExtractInt(const char *str, const char *delims,
char *buff;
const char *ret = ExtractToken(str, delims, &buff);
if (buff != 0) {
- *result = internal_atoll(buff);
+ *result = (int)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+static const char *ExtractUptr(const char *str, const char *delims,
+ uptr *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (uptr)internal_atoll(buff);
}
InternalFree(buff);
return ret;
@@ -96,66 +107,15 @@ class ExternalSymbolizer {
CHECK_NE(output_fd_, kInvalidFd);
}
- // Returns the number of frames for a given address, or zero if
- // symbolization failed.
- uptr SymbolizeCode(uptr addr, const char *module_name, uptr module_offset,
- AddressInfo *frames, uptr max_frames) {
+ char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
CHECK(module_name);
- // FIXME: Make sure this buffer always has sufficient size to hold
- // large debug info.
- static const int kMaxBufferSize = 4096;
- InternalScopedBuffer<char> buffer(kMaxBufferSize);
- char *buffer_data = buffer.data();
- internal_snprintf(buffer_data, kMaxBufferSize, "%s 0x%zx\n",
- module_name, module_offset);
- if (!writeToSymbolizer(buffer_data, internal_strlen(buffer_data)))
+ internal_snprintf(buffer_, kBufferSize, "%s%s 0x%zx\n",
+ is_data ? "DATA " : "", module_name, module_offset);
+ if (!writeToSymbolizer(buffer_, internal_strlen(buffer_)))
return 0;
-
- if (!readFromSymbolizer(buffer_data, kMaxBufferSize))
+ if (!readFromSymbolizer(buffer_, kBufferSize))
return 0;
- const char *str = buffer_data;
- uptr frame_id;
- CHECK_GT(max_frames, 0);
- for (frame_id = 0; frame_id < max_frames; frame_id++) {
- AddressInfo *info = &frames[frame_id];
- char *function_name = 0;
- str = ExtractToken(str, "\n", &function_name);
- CHECK(function_name);
- if (function_name[0] == '\0') {
- // There are no more frames.
- break;
- }
- info->Clear();
- info->FillAddressAndModuleInfo(addr, module_name, module_offset);
- info->function = function_name;
- // Parse <file>:<line>:<column> buffer.
- char *file_line_info = 0;
- str = ExtractToken(str, "\n", &file_line_info);
- CHECK(file_line_info);
- const char *line_info = ExtractToken(file_line_info, ":", &info->file);
- line_info = ExtractInt(line_info, ":", &info->line);
- line_info = ExtractInt(line_info, "", &info->column);
- InternalFree(file_line_info);
-
- // Functions and filenames can be "??", in which case we write 0
- // to address info to mark that names are unknown.
- if (0 == internal_strcmp(info->function, "??")) {
- InternalFree(info->function);
- info->function = 0;
- }
- if (0 == internal_strcmp(info->file, "??")) {
- InternalFree(info->file);
- info->file = 0;
- }
- }
- if (frame_id == 0) {
- // Make sure we return at least one frame.
- AddressInfo *info = &frames[0];
- info->Clear();
- info->FillAddressAndModuleInfo(addr, module_name, module_offset);
- frame_id = 1;
- }
- return frame_id;
+ return buffer_;
}
bool Restart() {
@@ -189,6 +149,7 @@ class ExternalSymbolizer {
}
return true;
}
+
bool writeToSymbolizer(const char *buffer, uptr length) {
if (length == 0)
return true;
@@ -204,6 +165,9 @@ class ExternalSymbolizer {
int input_fd_;
int output_fd_;
+ static const uptr kBufferSize = 16 * 1024;
+ char buffer_[kBufferSize];
+
static const uptr kMaxTimesRestarted = 5;
uptr times_restarted_;
};
@@ -220,30 +184,8 @@ class Symbolizer {
return 0;
const char *module_name = module->full_name();
uptr module_offset = addr - module->base_address();
- uptr actual_frames = 0;
- if (external_symbolizer_ == 0) {
- ReportExternalSymbolizerError(
- "WARNING: Trying to symbolize code, but external "
- "symbolizer is not initialized!\n");
- } else {
- while (true) {
- actual_frames = external_symbolizer_->SymbolizeCode(
- addr, module_name, module_offset, frames, max_frames);
- if (actual_frames > 0) {
- // Symbolization was successful.
- break;
- }
- // Try to restart symbolizer subprocess. If we don't succeed, forget
- // about it and don't try to use it later.
- if (!external_symbolizer_->Restart()) {
- ReportExternalSymbolizerError(
- "WARNING: Failed to use and restart external symbolizer!\n");
- external_symbolizer_ = 0;
- break;
- }
- }
- }
- if (external_symbolizer_ == 0) {
+ const char *str = SendCommand(false, module_name, module_offset);
+ if (str == 0) {
// External symbolizer was not initialized or failed. Fill only data
// about module name and offset.
AddressInfo *info = &frames[0];
@@ -251,17 +193,66 @@ class Symbolizer {
info->FillAddressAndModuleInfo(addr, module_name, module_offset);
return 1;
}
- // Otherwise, the data was filled by external symbolizer.
- return actual_frames;
+ uptr frame_id = 0;
+ for (frame_id = 0; frame_id < max_frames; frame_id++) {
+ AddressInfo *info = &frames[frame_id];
+ char *function_name = 0;
+ str = ExtractToken(str, "\n", &function_name);
+ CHECK(function_name);
+ if (function_name[0] == '\0') {
+ // There are no more frames.
+ break;
+ }
+ info->Clear();
+ info->FillAddressAndModuleInfo(addr, module_name, module_offset);
+ info->function = function_name;
+ // Parse <file>:<line>:<column> buffer.
+ char *file_line_info = 0;
+ str = ExtractToken(str, "\n", &file_line_info);
+ CHECK(file_line_info);
+ const char *line_info = ExtractToken(file_line_info, ":", &info->file);
+ line_info = ExtractInt(line_info, ":", &info->line);
+ line_info = ExtractInt(line_info, "", &info->column);
+ InternalFree(file_line_info);
+
+ // Functions and filenames can be "??", in which case we write 0
+ // to address info to mark that names are unknown.
+ if (0 == internal_strcmp(info->function, "??")) {
+ InternalFree(info->function);
+ info->function = 0;
+ }
+ if (0 == internal_strcmp(info->file, "??")) {
+ InternalFree(info->file);
+ info->file = 0;
+ }
+ }
+ if (frame_id == 0) {
+ // Make sure we return at least one frame.
+ AddressInfo *info = &frames[0];
+ info->Clear();
+ info->FillAddressAndModuleInfo(addr, module_name, module_offset);
+ frame_id = 1;
+ }
+ return frame_id;
}
- bool SymbolizeData(uptr addr, AddressInfo *frame) {
+ bool SymbolizeData(uptr addr, DataInfo *info) {
LoadedModule *module = FindModuleForAddress(addr);
if (module == 0)
return false;
const char *module_name = module->full_name();
uptr module_offset = addr - module->base_address();
- frame->FillAddressAndModuleInfo(addr, module_name, module_offset);
+ internal_memset(info, 0, sizeof(*info));
+ info->address = addr;
+ info->module = internal_strdup(module_name);
+ info->module_offset = module_offset;
+ const char *str = SendCommand(true, module_name, module_offset);
+ if (str == 0)
+ return true;
+ str = ExtractToken(str, "\n", &info->name);
+ str = ExtractUptr(str, " ", &info->start);
+ str = ExtractUptr(str, "\n", &info->size);
+ info->start += module->base_address();
return true;
}
@@ -276,6 +267,29 @@ class Symbolizer {
}
private:
+ char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
+ if (external_symbolizer_ == 0) {
+ ReportExternalSymbolizerError(
+ "WARNING: Trying to symbolize code, but external "
+ "symbolizer is not initialized!\n");
+ return 0;
+ }
+ for (;;) {
+ char *reply = external_symbolizer_->SendCommand(is_data, module_name,
+ module_offset);
+ if (reply)
+ return reply;
+ // Try to restart symbolizer subprocess. If we don't succeed, forget
+ // about it and don't try to use it later.
+ if (!external_symbolizer_->Restart()) {
+ ReportExternalSymbolizerError(
+ "WARNING: Failed to use and restart external symbolizer!\n");
+ external_symbolizer_ = 0;
+ return 0;
+ }
+ }
+ }
+
LoadedModule *FindModuleForAddress(uptr address) {
if (modules_ == 0) {
modules_ = (LoadedModule*)(symbolizer_allocator.Allocate(
@@ -316,8 +330,8 @@ uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames) {
return symbolizer.SymbolizeCode(address, frames, max_frames);
}
-bool SymbolizeData(uptr address, AddressInfo *frame) {
- return symbolizer.SymbolizeData(address, frame);
+bool SymbolizeData(uptr address, DataInfo *info) {
+ return symbolizer.SymbolizeData(address, info);
}
bool InitializeExternalSymbolizer(const char *path_to_symbolizer) {
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h
index 0714b3824fb..b88fa3f655a 100644
--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h
@@ -51,12 +51,21 @@ struct AddressInfo {
}
};
+struct DataInfo {
+ uptr address;
+ char *module;
+ uptr module_offset;
+ char *name;
+ uptr start;
+ uptr size;
+};
+
// Fills at most "max_frames" elements of "frames" with descriptions
// for a given address (in all inlined functions). Returns the number
// of descriptions actually filled.
// This function should NOT be called from two threads simultaneously.
uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames);
-bool SymbolizeData(uptr address, AddressInfo *frame);
+bool SymbolizeData(uptr address, DataInfo *info);
// Attempts to demangle the provided C++ mangled name.
const char *Demangle(const char *Name);
diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_win.cc
index f7300a18b60..242b4429bd7 100644
--- a/libsanitizer/sanitizer_common/sanitizer_win.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_win.cc
@@ -18,6 +18,8 @@
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_mutex.h"
namespace __sanitizer {
@@ -224,6 +226,42 @@ int internal_sched_yield() {
return 0;
}
+// ---------------------- BlockingMutex ---------------- {{{1
+enum LockState {
+ LOCK_UNINITIALIZED = 0,
+ LOCK_READY = -1,
+};
+
+BlockingMutex::BlockingMutex(LinkerInitialized li) {
+ // FIXME: see comments in BlockingMutex::Lock() for the details.
+ CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
+
+ CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
+ InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+ owner_ = LOCK_READY;
+}
+
+void BlockingMutex::Lock() {
+ if (owner_ == LOCK_UNINITIALIZED) {
+ // FIXME: hm, global BlockingMutex objects are not initialized?!?
+ // This might be a side effect of the clang+cl+link Frankenbuild...
+ new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1));
+
+ // FIXME: If it turns out the linker doesn't invoke our
+ // constructors, we should probably manually Lock/Unlock all the global
+ // locks while we're starting in one thread to avoid double-init races.
+ }
+ EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+ CHECK(owner_ == LOCK_READY);
+ owner_ = GetThreadSelf();
+}
+
+void BlockingMutex::Unlock() {
+ CHECK(owner_ == GetThreadSelf());
+ owner_ = LOCK_READY;
+ LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+}
+
} // namespace __sanitizer
#endif // _WIN32
diff --git a/libsanitizer/tsan/tsan_fd.cc b/libsanitizer/tsan/tsan_fd.cc
index 9aca9c51b38..f640c4f893e 100644
--- a/libsanitizer/tsan/tsan_fd.cc
+++ b/libsanitizer/tsan/tsan_fd.cc
@@ -162,6 +162,12 @@ void FdRelease(ThreadState *thr, uptr pc, int fd) {
MemoryRead8Byte(thr, pc, (uptr)d);
}
+void FdAccess(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd);
+ FdDesc *d = fddesc(thr, pc, fd);
+ MemoryRead8Byte(thr, pc, (uptr)d);
+}
+
void FdClose(ThreadState *thr, uptr pc, int fd) {
DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
FdDesc *d = fddesc(thr, pc, fd);
diff --git a/libsanitizer/tsan/tsan_fd.h b/libsanitizer/tsan/tsan_fd.h
index b4189a37df5..3306873223e 100644
--- a/libsanitizer/tsan/tsan_fd.h
+++ b/libsanitizer/tsan/tsan_fd.h
@@ -39,6 +39,7 @@ namespace __tsan {
void FdInit();
void FdAcquire(ThreadState *thr, uptr pc, int fd);
void FdRelease(ThreadState *thr, uptr pc, int fd);
+void FdAccess(ThreadState *thr, uptr pc, int fd);
void FdClose(ThreadState *thr, uptr pc, int fd);
void FdFileCreate(ThreadState *thr, uptr pc, int fd);
void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd);
diff --git a/libsanitizer/tsan/tsan_interceptors.cc b/libsanitizer/tsan/tsan_interceptors.cc
index 88acebf8e81..d8f66de2327 100644
--- a/libsanitizer/tsan/tsan_interceptors.cc
+++ b/libsanitizer/tsan/tsan_interceptors.cc
@@ -1239,33 +1239,6 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
return res;
}
-TSAN_INTERCEPTOR(long_t, read, int fd, void *buf, long_t sz) {
- SCOPED_TSAN_INTERCEPTOR(read, fd, buf, sz);
- int res = REAL(read)(fd, buf, sz);
- if (res >= 0 && fd >= 0) {
- FdAcquire(thr, pc, fd);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(long_t, pread, int fd, void *buf, long_t sz, unsigned off) {
- SCOPED_TSAN_INTERCEPTOR(pread, fd, buf, sz, off);
- int res = REAL(pread)(fd, buf, sz, off);
- if (res >= 0 && fd >= 0) {
- FdAcquire(thr, pc, fd);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(long_t, pread64, int fd, void *buf, long_t sz, u64 off) {
- SCOPED_TSAN_INTERCEPTOR(pread64, fd, buf, sz, off);
- int res = REAL(pread64)(fd, buf, sz, off);
- if (res >= 0 && fd >= 0) {
- FdAcquire(thr, pc, fd);
- }
- return res;
-}
-
TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) {
SCOPED_TSAN_INTERCEPTOR(readv, fd, vec, cnt);
int res = REAL(readv)(fd, vec, cnt);
@@ -1284,30 +1257,6 @@ TSAN_INTERCEPTOR(long_t, preadv64, int fd, void *vec, int cnt, u64 off) {
return res;
}
-TSAN_INTERCEPTOR(long_t, write, int fd, void *buf, long_t sz) {
- SCOPED_TSAN_INTERCEPTOR(write, fd, buf, sz);
- if (fd >= 0)
- FdRelease(thr, pc, fd);
- int res = REAL(write)(fd, buf, sz);
- return res;
-}
-
-TSAN_INTERCEPTOR(long_t, pwrite, int fd, void *buf, long_t sz, unsigned off) {
- SCOPED_TSAN_INTERCEPTOR(pwrite, fd, buf, sz, off);
- if (fd >= 0)
- FdRelease(thr, pc, fd);
- int res = REAL(pwrite)(fd, buf, sz, off);
- return res;
-}
-
-TSAN_INTERCEPTOR(long_t, pwrite64, int fd, void *buf, long_t sz, u64 off) {
- SCOPED_TSAN_INTERCEPTOR(pwrite64, fd, buf, sz, off);
- if (fd >= 0)
- FdRelease(thr, pc, fd);
- int res = REAL(pwrite64)(fd, buf, sz, off);
- return res;
-}
-
TSAN_INTERCEPTOR(long_t, writev, int fd, void *vec, int cnt) {
SCOPED_TSAN_INTERCEPTOR(writev, fd, vec, cnt);
if (fd >= 0)
@@ -1449,6 +1398,8 @@ TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
FdRelease(thr, pc, epfd);
}
int res = REAL(epoll_ctl)(epfd, op, fd, ev);
+ if (fd >= 0)
+ FdAccess(thr, pc, fd);
return res;
}
@@ -1641,6 +1592,33 @@ TSAN_INTERCEPTOR(int, fork, int fake) {
return pid;
}
+struct TsanInterceptorContext {
+ ThreadState *thr;
+ const uptr caller_pc;
+ const uptr pc;
+};
+
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
+ MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \
+ ((TsanInterceptorContext*)ctx)->pc, \
+ (uptr)ptr, size, true)
+#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
+ MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \
+ ((TsanInterceptorContext*)ctx)->pc, \
+ (uptr)ptr, size, false)
+#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
+ SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__) \
+ TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \
+ ctx = (void*)&_ctx; \
+ (void)ctx;
+#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
+ FdAcquire(((TsanInterceptorContext*)ctx)->thr, pc, fd)
+#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
+ FdRelease(((TsanInterceptorContext*)ctx)->thr, pc, fd)
+#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
+ ThreadSetName(((TsanInterceptorContext*)ctx)->thr, name)
+#include "sanitizer_common/sanitizer_common_interceptors.inc"
+
namespace __tsan {
void ProcessPendingSignals(ThreadState *thr) {
@@ -1675,6 +1653,7 @@ void ProcessPendingSignals(ThreadState *thr) {
(uptr)sigactions[sig].sa_sigaction :
(uptr)sigactions[sig].sa_handler;
stack.Init(&pc, 1);
+ Lock l(&ctx->thread_mtx);
ScopedReport rep(ReportTypeErrnoInSignal);
if (!IsFiredSuppression(ctx, rep, stack)) {
rep.AddStack(&stack);
@@ -1703,6 +1682,8 @@ void InitializeInterceptors() {
REAL(memcpy) = internal_memcpy;
REAL(memcmp) = internal_memcmp;
+ SANITIZER_COMMON_INTERCEPTORS_INIT;
+
TSAN_INTERCEPT(longjmp);
TSAN_INTERCEPT(siglongjmp);
@@ -1806,14 +1787,8 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pipe);
TSAN_INTERCEPT(pipe2);
- TSAN_INTERCEPT(read);
- TSAN_INTERCEPT(pread);
- TSAN_INTERCEPT(pread64);
TSAN_INTERCEPT(readv);
TSAN_INTERCEPT(preadv64);
- TSAN_INTERCEPT(write);
- TSAN_INTERCEPT(pwrite);
- TSAN_INTERCEPT(pwrite64);
TSAN_INTERCEPT(writev);
TSAN_INTERCEPT(pwritev64);
TSAN_INTERCEPT(send);
diff --git a/libsanitizer/tsan/tsan_mman.cc b/libsanitizer/tsan/tsan_mman.cc
index 9a8a524f262..f4fafaf77f0 100644
--- a/libsanitizer/tsan/tsan_mman.cc
+++ b/libsanitizer/tsan/tsan_mman.cc
@@ -46,6 +46,7 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
Context *ctx = CTX();
StackTrace stack;
stack.ObtainCurrent(thr, pc);
+ Lock l(&ctx->thread_mtx);
ScopedReport rep(ReportTypeSignalUnsafe);
if (!IsFiredSuppression(ctx, rep, stack)) {
rep.AddStack(&stack);
diff --git a/libsanitizer/tsan/tsan_report.cc b/libsanitizer/tsan/tsan_report.cc
index ca352662902..f99fd2ea105 100644
--- a/libsanitizer/tsan/tsan_report.cc
+++ b/libsanitizer/tsan/tsan_report.cc
@@ -102,16 +102,17 @@ static void PrintMop(const ReportMop *mop, bool first) {
static void PrintLocation(const ReportLocation *loc) {
char thrbuf[kThreadBufSize];
if (loc->type == ReportLocationGlobal) {
- Printf(" Location is global '%s' of size %zu at %zx %s:%d (%s+%p)\n\n",
- loc->name, loc->size, loc->addr, loc->file, loc->line,
- loc->module, loc->offset);
+ Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n",
+ loc->name, loc->size, loc->addr, loc->module, loc->offset);
} else if (loc->type == ReportLocationHeap) {
char thrbuf[kThreadBufSize];
Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
loc->size, loc->addr, thread_name(thrbuf, loc->tid));
PrintStack(loc->stack);
} else if (loc->type == ReportLocationStack) {
- Printf(" Location is stack of %s\n\n", thread_name(thrbuf, loc->tid));
+ Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
+ } else if (loc->type == ReportLocationTLS) {
+ Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
} else if (loc->type == ReportLocationFD) {
Printf(" Location is file descriptor %d created by %s at:\n",
loc->fd, thread_name(thrbuf, loc->tid));
diff --git a/libsanitizer/tsan/tsan_report.h b/libsanitizer/tsan/tsan_report.h
index 23fbc684209..42f52af9e37 100644
--- a/libsanitizer/tsan/tsan_report.h
+++ b/libsanitizer/tsan/tsan_report.h
@@ -56,6 +56,7 @@ enum ReportLocationType {
ReportLocationGlobal,
ReportLocationHeap,
ReportLocationStack,
+ ReportLocationTLS,
ReportLocationFD
};
diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cc b/libsanitizer/tsan/tsan_rtl_mutex.cc
index 8dd0e6d4d9b..db97e1d9853 100644
--- a/libsanitizer/tsan/tsan_rtl_mutex.cc
+++ b/libsanitizer/tsan/tsan_rtl_mutex.cc
@@ -53,6 +53,7 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
&& s->owner_tid != SyncVar::kInvalidTid
&& !s->is_broken) {
s->is_broken = true;
+ Lock l(&ctx->thread_mtx);
ScopedReport rep(ReportTypeMutexDestroyLocked);
rep.AddMutex(s);
StackTrace trace;
diff --git a/libsanitizer/tsan/tsan_rtl_report.cc b/libsanitizer/tsan/tsan_rtl_report.cc
index b65b24fce89..c4256da412a 100644
--- a/libsanitizer/tsan/tsan_rtl_report.cc
+++ b/libsanitizer/tsan/tsan_rtl_report.cc
@@ -119,6 +119,7 @@ static ReportStack *SymbolizeStack(const StackTrace& trace) {
ScopedReport::ScopedReport(ReportType typ) {
ctx_ = CTX();
+ ctx_->thread_mtx.CheckLocked();
void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc));
rep_ = new(mem) ReportDesc;
rep_->typ = typ;
@@ -185,15 +186,37 @@ void ScopedReport::AddThread(const ThreadContext *tctx) {
#ifndef TSAN_GO
static ThreadContext *FindThread(int unique_id) {
- CTX()->thread_mtx.CheckLocked();
+ Context *ctx = CTX();
+ ctx->thread_mtx.CheckLocked();
for (unsigned i = 0; i < kMaxTid; i++) {
- ThreadContext *tctx = CTX()->threads[i];
+ ThreadContext *tctx = ctx->threads[i];
if (tctx && tctx->unique_id == unique_id) {
return tctx;
}
}
return 0;
}
+
+ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
+ Context *ctx = CTX();
+ ctx->thread_mtx.CheckLocked();
+ for (unsigned i = 0; i < kMaxTid; i++) {
+ ThreadContext *tctx = ctx->threads[i];
+ if (tctx == 0 || tctx->status != ThreadStatusRunning)
+ continue;
+ ThreadState *thr = tctx->thr;
+ CHECK(thr);
+ if (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) {
+ *is_stack = true;
+ return tctx;
+ }
+ if (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size) {
+ *is_stack = false;
+ return tctx;
+ }
+ }
+ return 0;
+}
#endif
void ScopedReport::AddMutex(const SyncVar *s) {
@@ -274,25 +297,21 @@ void ScopedReport::AddLocation(uptr addr, uptr size) {
AddThread(tctx);
return;
}
-#endif
- ReportStack *symb = SymbolizeData(addr);
- if (symb) {
+ bool is_stack = false;
+ if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
ReportLocation *loc = new(mem) ReportLocation();
rep_->locs.PushBack(loc);
- loc->type = ReportLocationGlobal;
- loc->addr = addr;
- loc->size = size;
- loc->module = symb->module ? internal_strdup(symb->module) : 0;
- loc->offset = symb->offset;
- loc->tid = 0;
- loc->name = symb->func ? internal_strdup(symb->func) : 0;
- loc->file = symb->file ? internal_strdup(symb->file) : 0;
- loc->line = symb->line;
- loc->stack = 0;
- internal_free(symb);
+ loc->type = is_stack ? ReportLocationStack : ReportLocationTLS;
+ loc->tid = tctx->tid;
+ AddThread(tctx);
+ }
+ ReportLocation *loc = SymbolizeData(addr);
+ if (loc) {
+ rep_->locs.PushBack(loc);
return;
}
+#endif
}
#ifndef TSAN_GO
@@ -386,7 +405,7 @@ static bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
uptr addr_min, uptr addr_max) {
Context *ctx = CTX();
bool equal_stack = false;
- RacyStacks hash = {};
+ RacyStacks hash;
if (flags()->suppress_equal_stacks) {
hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
diff --git a/libsanitizer/tsan/tsan_stat.cc b/libsanitizer/tsan/tsan_stat.cc
index 394c9111626..105d0bc2375 100644
--- a/libsanitizer/tsan/tsan_stat.cc
+++ b/libsanitizer/tsan/tsan_stat.cc
@@ -202,6 +202,7 @@ void StatOutput(u64 *stat) {
name[StatInt_pipe] = " pipe ";
name[StatInt_pipe2] = " pipe2 ";
name[StatInt_read] = " read ";
+ name[StatInt_prctl] = " prctl ";
name[StatInt_pread] = " pread ";
name[StatInt_pread64] = " pread64 ";
name[StatInt_readv] = " readv ";
@@ -233,6 +234,12 @@ void StatOutput(u64 *stat) {
name[StatInt_nanosleep] = " nanosleep ";
name[StatInt_gettimeofday] = " gettimeofday ";
name[StatInt_fork] = " fork ";
+ name[StatInt_vscanf] = " vscanf ";
+ name[StatInt_vsscanf] = " vsscanf ";
+ name[StatInt_vfscanf] = " vfscanf ";
+ name[StatInt_scanf] = " scanf ";
+ name[StatInt_sscanf] = " sscanf ";
+ name[StatInt_fscanf] = " fscanf ";
name[StatAnnotation] = "Dynamic annotations ";
name[StatAnnotateHappensBefore] = " HappensBefore ";
diff --git a/libsanitizer/tsan/tsan_stat.h b/libsanitizer/tsan/tsan_stat.h
index cdd57365bae..f40d3a2ac5e 100644
--- a/libsanitizer/tsan/tsan_stat.h
+++ b/libsanitizer/tsan/tsan_stat.h
@@ -197,6 +197,7 @@ enum StatType {
StatInt_pipe,
StatInt_pipe2,
StatInt_read,
+ StatInt_prctl,
StatInt_pread,
StatInt_pread64,
StatInt_readv,
@@ -232,6 +233,12 @@ enum StatType {
StatInt_nanosleep,
StatInt_gettimeofday,
StatInt_fork,
+ StatInt_vscanf,
+ StatInt_vsscanf,
+ StatInt_vfscanf,
+ StatInt_scanf,
+ StatInt_sscanf,
+ StatInt_fscanf,
// Dynamic annotations.
StatAnnotation,
diff --git a/libsanitizer/tsan/tsan_symbolize.cc b/libsanitizer/tsan/tsan_symbolize.cc
index 50a4eaa518b..015b98717f1 100644
--- a/libsanitizer/tsan/tsan_symbolize.cc
+++ b/libsanitizer/tsan/tsan_symbolize.cc
@@ -27,21 +27,24 @@ ReportStack *NewReportStackEntry(uptr addr) {
return ent;
}
+// Strip module path to make output shorter.
+static char *StripModuleName(const char *module) {
+ if (module == 0)
+ return 0;
+ const char *short_module_name = internal_strrchr(module, '/');
+ if (short_module_name)
+ short_module_name += 1;
+ else
+ short_module_name = module;
+ return internal_strdup(short_module_name);
+}
+
static ReportStack *NewReportStackEntry(const AddressInfo &info) {
ReportStack *ent = NewReportStackEntry(info.address);
- if (info.module) {
- // Strip module path to make output shorter.
- const char *short_module_name = internal_strrchr(info.module, '/');
- if (short_module_name)
- short_module_name += 1;
- else
- short_module_name = info.module;
- ent->module = internal_strdup(short_module_name);
- }
+ ent->module = StripModuleName(info.module);
ent->offset = info.module_offset;
- if (info.function) {
+ if (info.function)
ent->func = internal_strdup(info.function);
- }
if (info.file)
ent->file = internal_strdup(info.file);
ent->line = info.line;
@@ -76,14 +79,23 @@ ReportStack *SymbolizeCode(uptr addr) {
return SymbolizeCodeAddr2Line(addr);
}
-ReportStack *SymbolizeData(uptr addr) {
- if (flags()->external_symbolizer_path[0]) {
- AddressInfo frame;
- if (!__sanitizer::SymbolizeData(addr, &frame))
- return 0;
- return NewReportStackEntry(frame);
- }
- return SymbolizeDataAddr2Line(addr);
+ReportLocation *SymbolizeData(uptr addr) {
+ if (flags()->external_symbolizer_path[0] == 0)
+ return 0;
+ DataInfo info;
+ if (!__sanitizer::SymbolizeData(addr, &info))
+ return 0;
+ ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack,
+ sizeof(ReportLocation));
+ internal_memset(ent, 0, sizeof(*ent));
+ ent->type = ReportLocationGlobal;
+ ent->module = StripModuleName(info.module);
+ ent->offset = info.module_offset;
+ if (info.name)
+ ent->name = internal_strdup(info.name);
+ ent->addr = info.start;
+ ent->size = info.size;
+ return ent;
}
} // namespace __tsan
diff --git a/libsanitizer/tsan/tsan_symbolize.h b/libsanitizer/tsan/tsan_symbolize.h
index 6ac19ca5ccc..5275936a293 100644
--- a/libsanitizer/tsan/tsan_symbolize.h
+++ b/libsanitizer/tsan/tsan_symbolize.h
@@ -17,10 +17,9 @@
namespace __tsan {
ReportStack *SymbolizeCode(uptr addr);
-ReportStack *SymbolizeData(uptr addr);
+ReportLocation *SymbolizeData(uptr addr);
ReportStack *SymbolizeCodeAddr2Line(uptr addr);
-ReportStack *SymbolizeDataAddr2Line(uptr addr);
ReportStack *NewReportStackEntry(uptr addr);