summaryrefslogtreecommitdiff
path: root/libsanitizer/asan
diff options
context:
space:
mode:
Diffstat (limited to 'libsanitizer/asan')
-rw-r--r--libsanitizer/asan/Makefile.am3
-rw-r--r--libsanitizer/asan/Makefile.in14
-rw-r--r--libsanitizer/asan/asan_activation.cc8
-rw-r--r--libsanitizer/asan/asan_allocator.cc106
-rw-r--r--libsanitizer/asan/asan_allocator.h43
-rw-r--r--libsanitizer/asan/asan_debugging.cc125
-rw-r--r--libsanitizer/asan/asan_descriptions.cc484
-rw-r--r--libsanitizer/asan/asan_descriptions.h251
-rw-r--r--libsanitizer/asan/asan_errors.cc494
-rw-r--r--libsanitizer/asan/asan_errors.h376
-rw-r--r--libsanitizer/asan/asan_fake_stack.cc6
-rw-r--r--libsanitizer/asan/asan_fake_stack.h21
-rw-r--r--libsanitizer/asan/asan_flags.cc18
-rw-r--r--libsanitizer/asan/asan_flags.inc21
-rw-r--r--libsanitizer/asan/asan_globals.cc147
-rw-r--r--libsanitizer/asan/asan_init_version.h14
-rw-r--r--libsanitizer/asan/asan_interceptors.cc106
-rw-r--r--libsanitizer/asan/asan_interceptors.h14
-rw-r--r--libsanitizer/asan/asan_interface_internal.h28
-rw-r--r--libsanitizer/asan/asan_internal.h38
-rw-r--r--libsanitizer/asan/asan_linux.cc13
-rw-r--r--libsanitizer/asan/asan_mac.cc218
-rw-r--r--libsanitizer/asan/asan_malloc_linux.cc10
-rw-r--r--libsanitizer/asan/asan_malloc_mac.cc4
-rw-r--r--libsanitizer/asan/asan_malloc_win.cc139
-rw-r--r--libsanitizer/asan/asan_mapping.h77
-rw-r--r--libsanitizer/asan/asan_memory_profile.cc98
-rw-r--r--libsanitizer/asan/asan_new_delete.cc20
-rw-r--r--libsanitizer/asan/asan_poisoning.cc44
-rw-r--r--libsanitizer/asan/asan_poisoning.h2
-rw-r--r--libsanitizer/asan/asan_posix.cc30
-rw-r--r--libsanitizer/asan/asan_report.cc989
-rw-r--r--libsanitizer/asan/asan_report.h26
-rw-r--r--libsanitizer/asan/asan_rtl.cc89
-rw-r--r--libsanitizer/asan/asan_scariness_score.h72
-rw-r--r--libsanitizer/asan/asan_stack.h5
-rw-r--r--libsanitizer/asan/asan_suppressions.cc1
-rw-r--r--libsanitizer/asan/asan_thread.cc123
-rw-r--r--libsanitizer/asan/asan_thread.h41
-rw-r--r--libsanitizer/asan/asan_win.cc206
-rw-r--r--libsanitizer/asan/asan_win_dll_thunk.cc44
-rw-r--r--libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc23
-rw-r--r--libsanitizer/asan/libtool-version2
43 files changed, 3118 insertions, 1475 deletions
diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am
index bd3cd735a98..bfd28c0388e 100644
--- a/libsanitizer/asan/Makefile.am
+++ b/libsanitizer/asan/Makefile.am
@@ -19,6 +19,8 @@ asan_files = \
asan_activation.cc \
asan_allocator.cc \
asan_debugging.cc \
+ asan_descriptions.cc \
+ asan_errors.cc \
asan_fake_stack.cc \
asan_flags.cc \
asan_globals.cc \
@@ -28,6 +30,7 @@ asan_files = \
asan_malloc_linux.cc \
asan_malloc_mac.cc \
asan_malloc_win.cc \
+ asan_memory_profile.cc \
asan_new_delete.cc \
asan_poisoning.cc \
asan_posix.cc \
diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in
index 229c7b400d1..2d9f4a7b815 100644
--- a/libsanitizer/asan/Makefile.in
+++ b/libsanitizer/asan/Makefile.in
@@ -112,9 +112,10 @@ libasan_la_DEPENDENCIES = \
$(top_builddir)/lsan/libsanitizer_lsan.la $(am__append_2) \
$(am__append_3) $(am__DEPENDENCIES_1)
am__objects_1 = asan_activation.lo asan_allocator.lo asan_debugging.lo \
- asan_fake_stack.lo asan_flags.lo asan_globals.lo \
- asan_interceptors.lo asan_linux.lo asan_mac.lo \
- asan_malloc_linux.lo asan_malloc_mac.lo asan_malloc_win.lo \
+ asan_descriptions.lo asan_errors.lo asan_fake_stack.lo \
+ asan_flags.lo asan_globals.lo asan_interceptors.lo \
+ asan_linux.lo asan_mac.lo asan_malloc_linux.lo \
+ asan_malloc_mac.lo asan_malloc_win.lo asan_memory_profile.lo \
asan_new_delete.lo asan_poisoning.lo asan_posix.lo \
asan_report.lo asan_rtl.lo asan_stack.lo asan_stats.lo \
asan_suppressions.lo asan_thread.lo asan_win.lo \
@@ -219,6 +220,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
RPC_DEFS = @RPC_DEFS@
+SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
@@ -308,6 +310,8 @@ asan_files = \
asan_activation.cc \
asan_allocator.cc \
asan_debugging.cc \
+ asan_descriptions.cc \
+ asan_errors.cc \
asan_fake_stack.cc \
asan_flags.cc \
asan_globals.cc \
@@ -317,6 +321,7 @@ asan_files = \
asan_malloc_linux.cc \
asan_malloc_mac.cc \
asan_malloc_win.cc \
+ asan_memory_profile.cc \
asan_new_delete.cc \
asan_poisoning.cc \
asan_posix.cc \
@@ -454,6 +459,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_debugging.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_descriptions.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_errors.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_flags.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@
@@ -463,6 +470,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_win.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_memory_profile.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_new_delete.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_poisoning.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_posix.Plo@am__quote@
diff --git a/libsanitizer/asan/asan_activation.cc b/libsanitizer/asan/asan_activation.cc
index 58867956086..ecd767c5985 100644
--- a/libsanitizer/asan/asan_activation.cc
+++ b/libsanitizer/asan/asan_activation.cc
@@ -45,6 +45,7 @@ static struct AsanDeactivatedFlags {
FlagParser parser;
RegisterActivationFlags(&parser, &f, &cf);
+ cf.SetDefaults();
// Copy the current activation flags.
allocator_options.CopyTo(&f, &cf);
cf.malloc_context_size = malloc_context_size;
@@ -59,12 +60,7 @@ static struct AsanDeactivatedFlags {
parser.ParseString(env);
}
- // Override from getprop asan.options.
- char buf[100];
- GetExtraActivationFlags(buf, sizeof(buf));
- parser.ParseString(buf);
-
- SetVerbosity(cf.verbosity);
+ InitializeCommonFlags(&cf);
if (Verbosity()) ReportUnrecognizedFlags();
diff --git a/libsanitizer/asan/asan_allocator.cc b/libsanitizer/asan/asan_allocator.cc
index 187c405e118..d3ddb904d58 100644
--- a/libsanitizer/asan/asan_allocator.cc
+++ b/libsanitizer/asan/asan_allocator.cc
@@ -221,7 +221,7 @@ void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
struct Allocator {
static const uptr kMaxAllowedMallocSize =
- FIRST_32_SECOND_64(3UL << 30, 1UL << 40);
+ FIRST_32_SECOND_64(3UL << 30, 1ULL << 40);
static const uptr kMaxThreadLocalQuarantine =
FIRST_32_SECOND_64(1 << 18, 1 << 20);
@@ -264,9 +264,43 @@ struct Allocator {
SharedInitCode(options);
}
+ void RePoisonChunk(uptr chunk) {
+ // This could a user-facing chunk (with redzones), or some internal
+ // housekeeping chunk, like TransferBatch. Start by assuming the former.
+ AsanChunk *ac = GetAsanChunk((void *)chunk);
+ uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac);
+ uptr beg = ac->Beg();
+ uptr end = ac->Beg() + ac->UsedSize(true);
+ uptr chunk_end = chunk + allocated_size;
+ if (chunk < beg && beg < end && end <= chunk_end) {
+ // Looks like a valid AsanChunk. Or maybe not. Be conservative and only
+ // poison the redzones.
+ PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic);
+ uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY);
+ FastPoisonShadowPartialRightRedzone(
+ end_aligned_down, end - end_aligned_down,
+ chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic);
+ } else {
+ // This can not be an AsanChunk. Poison everything. It may be reused as
+ // AsanChunk later.
+ PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic);
+ }
+ }
+
void ReInitialize(const AllocatorOptions &options) {
allocator.SetMayReturnNull(options.may_return_null);
SharedInitCode(options);
+
+ // Poison all existing allocation's redzones.
+ if (CanPoisonMemory()) {
+ allocator.ForceLock();
+ allocator.ForEachChunk(
+ [](uptr chunk, void *alloc) {
+ ((Allocator *)alloc)->RePoisonChunk(chunk);
+ },
+ this);
+ allocator.ForceUnlock();
+ }
}
void GetOptions(AllocatorOptions *options) const {
@@ -354,7 +388,7 @@ struct Allocator {
if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
(void*)size);
- return allocator.ReturnNullOrDie();
+ return allocator.ReturnNullOrDieOnBadRequest();
}
AsanThread *t = GetCurrentThread();
@@ -371,8 +405,7 @@ struct Allocator {
allocator.Allocate(cache, needed_size, 8, false, check_rss_limit);
}
- if (!allocated)
- return allocator.ReturnNullOrDie();
+ if (!allocated) return allocator.ReturnNullOrDieOnOOM();
if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) {
// Heap poisoning is enabled, but the allocator provides an unpoisoned
@@ -455,29 +488,28 @@ struct Allocator {
return res;
}
- void AtomicallySetQuarantineFlag(AsanChunk *m, void *ptr,
+ // Set quarantine flag if chunk is allocated, issue ASan error report on
+ // available and quarantined chunks. Return true on success, false otherwise.
+ bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk *m, void *ptr,
BufferedStackTrace *stack) {
u8 old_chunk_state = CHUNK_ALLOCATED;
// Flip the chunk_state atomically to avoid race on double-free.
- if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state,
- CHUNK_QUARANTINE, memory_order_acquire))
+ if (!atomic_compare_exchange_strong((atomic_uint8_t *)m, &old_chunk_state,
+ CHUNK_QUARANTINE,
+ memory_order_acquire)) {
ReportInvalidFree(ptr, old_chunk_state, stack);
+ // It's not safe to push a chunk in quarantine on invalid free.
+ return false;
+ }
CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state);
+ return true;
}
// Expects the chunk to already be marked as quarantined by using
- // AtomicallySetQuarantineFlag.
+ // AtomicallySetQuarantineFlagIfAllocated.
void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack,
AllocType alloc_type) {
CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
-
- if (m->alloc_type != alloc_type) {
- if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) {
- ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type,
- (AllocType)alloc_type);
- }
- }
-
CHECK_GE(m->alloc_tid, 0);
if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area.
CHECK_EQ(m->free_tid, kInvalidTid);
@@ -514,13 +546,24 @@ struct Allocator {
uptr chunk_beg = p - kChunkHeaderSize;
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+
+ ASAN_FREE_HOOK(ptr);
+ // Must mark the chunk as quarantined before any changes to its metadata.
+ // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag.
+ if (!AtomicallySetQuarantineFlagIfAllocated(m, ptr, stack)) return;
+
+ if (m->alloc_type != alloc_type) {
+ if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) {
+ ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type,
+ (AllocType)alloc_type);
+ }
+ }
+
if (delete_size && flags()->new_delete_type_mismatch &&
delete_size != m->UsedSize()) {
ReportNewDeleteSizeMismatch(p, delete_size, stack);
}
- ASAN_FREE_HOOK(ptr);
- // Must mark the chunk as quarantined before any changes to its metadata.
- AtomicallySetQuarantineFlag(m, ptr, stack);
+
QuarantineChunk(m, ptr, stack, alloc_type);
}
@@ -551,7 +594,7 @@ struct Allocator {
void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
if (CallocShouldReturnNullDueToOverflow(size, nmemb))
- return allocator.ReturnNullOrDie();
+ return allocator.ReturnNullOrDieOnBadRequest();
void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
// If the memory comes from the secondary allocator no need to clear it
// as it comes directly from mmap.
@@ -642,6 +685,8 @@ struct Allocator {
fallback_mutex.Unlock();
allocator.ForceUnlock();
}
+
+ void ReleaseToOS() { allocator.ReleaseToOS(); }
};
static Allocator instance(LINKER_INITIALIZED);
@@ -653,11 +698,17 @@ static AsanAllocator &get_allocator() {
bool AsanChunkView::IsValid() {
return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE;
}
+bool AsanChunkView::IsAllocated() {
+ return chunk_ && chunk_->chunk_state == CHUNK_ALLOCATED;
+}
uptr AsanChunkView::Beg() { return chunk_->Beg(); }
uptr AsanChunkView::End() { return Beg() + UsedSize(); }
uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
+AllocType AsanChunkView::GetAllocType() {
+ return (AllocType)chunk_->alloc_type;
+}
static StackTrace GetStackTraceFromId(u32 id) {
CHECK(id);
@@ -666,16 +717,22 @@ static StackTrace GetStackTraceFromId(u32 id) {
return res;
}
+u32 AsanChunkView::GetAllocStackId() { return chunk_->alloc_context_id; }
+u32 AsanChunkView::GetFreeStackId() { return chunk_->free_context_id; }
+
StackTrace AsanChunkView::GetAllocStack() {
- return GetStackTraceFromId(chunk_->alloc_context_id);
+ return GetStackTraceFromId(GetAllocStackId());
}
StackTrace AsanChunkView::GetFreeStack() {
- return GetStackTraceFromId(chunk_->free_context_id);
+ return GetStackTraceFromId(GetFreeStackId());
}
+void ReleaseToOS() { instance.ReleaseToOS(); }
+
void InitializeAllocator(const AllocatorOptions &options) {
instance.Initialize(options);
+ SetAllocatorReleaseToOSCallback(ReleaseToOS);
}
void ReInitializeAllocator(const AllocatorOptions &options) {
@@ -689,6 +746,9 @@ void GetAllocatorOptions(AllocatorOptions *options) {
AsanChunkView FindHeapChunkByAddress(uptr addr) {
return instance.FindHeapChunkByAddress(addr);
}
+AsanChunkView FindHeapChunkByAllocBeg(uptr addr) {
+ return AsanChunkView(instance.GetAsanChunk(reinterpret_cast<void*>(addr)));
+}
void AsanThreadLocalMallocStorage::CommitBack() {
instance.CommitBack(this);
@@ -752,7 +812,7 @@ int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
return 0;
}
-uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) {
+uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) {
if (!ptr) return 0;
uptr usable_size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
if (flags()->check_malloc_usable_size && (usable_size == 0)) {
diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h
index 921131ab4e9..7eeddadd547 100644
--- a/libsanitizer/asan/asan_allocator.h
+++ b/libsanitizer/asan/asan_allocator.h
@@ -47,16 +47,20 @@ void GetAllocatorOptions(AllocatorOptions *options);
class AsanChunkView {
public:
explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {}
- bool IsValid(); // Checks if AsanChunkView points to a valid allocated
- // or quarantined chunk.
- uptr Beg(); // First byte of user memory.
- uptr End(); // Last byte of user memory.
- uptr UsedSize(); // Size requested by the user.
+ bool IsValid(); // Checks if AsanChunkView points to a valid allocated
+ // or quarantined chunk.
+ bool IsAllocated(); // Checks if the memory is currently allocated.
+ uptr Beg(); // First byte of user memory.
+ uptr End(); // Last byte of user memory.
+ uptr UsedSize(); // Size requested by the user.
uptr AllocTid();
uptr FreeTid();
bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; }
+ u32 GetAllocStackId();
+ u32 GetFreeStackId();
StackTrace GetAllocStack();
StackTrace GetFreeStack();
+ AllocType GetAllocType();
bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
if (addr >= Beg() && (addr + access_size) <= End()) {
*offset = addr - Beg();
@@ -85,6 +89,7 @@ class AsanChunkView {
};
AsanChunkView FindHeapChunkByAddress(uptr address);
+AsanChunkView FindHeapChunkByAllocBeg(uptr address);
// List of AsanChunks with total size.
class AsanChunkFifoList: public IntrusiveList<AsanChunk> {
@@ -112,18 +117,36 @@ struct AsanMapUnmapCallback {
# if defined(__powerpc64__)
const uptr kAllocatorSpace = 0xa0000000000ULL;
const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
+typedef DefaultSizeClassMap SizeClassMap;
+# elif defined(__aarch64__) && SANITIZER_ANDROID
+const uptr kAllocatorSpace = 0x3000000000ULL;
+const uptr kAllocatorSize = 0x2000000000ULL; // 128G.
+typedef VeryCompactSizeClassMap SizeClassMap;
# elif defined(__aarch64__)
-// AArch64/SANITIZIER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA
+// AArch64/SANITIZER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA
// so no need to different values for different VMA.
const uptr kAllocatorSpace = 0x10000000000ULL;
const uptr kAllocatorSize = 0x10000000000ULL; // 3T.
+typedef DefaultSizeClassMap SizeClassMap;
+# elif SANITIZER_WINDOWS
+const uptr kAllocatorSpace = ~(uptr)0;
+const uptr kAllocatorSize = 0x8000000000ULL; // 500G
+typedef DefaultSizeClassMap SizeClassMap;
# else
const uptr kAllocatorSpace = 0x600000000000ULL;
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
-# endif
typedef DefaultSizeClassMap SizeClassMap;
-typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/,
- SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
+# endif
+struct AP64 { // Allocator64 parameters. Deliberately using a short name.
+ static const uptr kSpaceBeg = kAllocatorSpace;
+ static const uptr kSpaceSize = kAllocatorSize;
+ static const uptr kMetadataSize = 0;
+ typedef __asan::SizeClassMap SizeClassMap;
+ typedef AsanMapUnmapCallback MapUnmapCallback;
+ static const uptr kFlags = 0;
+};
+
+typedef SizeClassAllocator64<AP64> PrimaryAllocator;
#else // Fallback to SizeClassAllocator32.
static const uptr kRegionSizeLog = 20;
static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
@@ -169,7 +192,7 @@ void *asan_pvalloc(uptr size, BufferedStackTrace *stack);
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
BufferedStackTrace *stack);
-uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp);
+uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp);
uptr asan_mz_size(const void *ptr);
void asan_mz_force_lock();
diff --git a/libsanitizer/asan/asan_debugging.cc b/libsanitizer/asan/asan_debugging.cc
index 6e33a9d4c51..1c8d0df8f6e 100644
--- a/libsanitizer/asan/asan_debugging.cc
+++ b/libsanitizer/asan/asan_debugging.cc
@@ -12,74 +12,39 @@
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
+#include "asan_descriptions.h"
#include "asan_flags.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_thread.h"
-namespace __asan {
-
-void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) {
- descr->name[0] = 0;
- descr->region_address = 0;
- descr->region_size = 0;
- descr->region_kind = "stack";
+namespace {
+using namespace __asan;
- AsanThread::StackFrameAccess access;
- if (!t->GetStackFrameAccessByAddr(addr, &access))
- return;
+static void FindInfoForStackVar(uptr addr, const char *frame_descr, uptr offset,
+ char *name, uptr name_size,
+ uptr &region_address, uptr &region_size) {
InternalMmapVector<StackVarDescr> vars(16);
- if (!ParseFrameDescription(access.frame_descr, &vars)) {
+ if (!ParseFrameDescription(frame_descr, &vars)) {
return;
}
for (uptr i = 0; i < vars.size(); i++) {
- if (access.offset <= vars[i].beg + vars[i].size) {
- internal_strncat(descr->name, vars[i].name_pos,
- Min(descr->name_size, vars[i].name_len));
- descr->region_address = addr - (access.offset - vars[i].beg);
- descr->region_size = vars[i].size;
+ if (offset <= vars[i].beg + vars[i].size) {
+ // We use name_len + 1 because strlcpy will guarantee a \0 at the end, so
+ // if we're limiting the copy due to name_len, we add 1 to ensure we copy
+ // the whole name and then terminate with '\0'.
+ internal_strlcpy(name, vars[i].name_pos,
+ Min(name_size, vars[i].name_len + 1));
+ region_address = addr - (offset - vars[i].beg);
+ region_size = vars[i].size;
return;
}
}
}
-void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) {
- AsanChunkView chunk = FindHeapChunkByAddress(addr);
-
- descr->name[0] = 0;
- descr->region_address = 0;
- descr->region_size = 0;
-
- if (!chunk.IsValid()) {
- descr->region_kind = "heap-invalid";
- return;
- }
-
- descr->region_address = chunk.Beg();
- descr->region_size = chunk.UsedSize();
- descr->region_kind = "heap";
-}
-
-void AsanLocateAddress(uptr addr, AddressDescription *descr) {
- if (DescribeAddressIfShadow(addr, descr, /* print */ false)) {
- return;
- }
- if (GetInfoForAddressIfGlobal(addr, descr)) {
- return;
- }
- asanThreadRegistry().Lock();
- AsanThread *thread = FindThreadByStackAddress(addr);
- asanThreadRegistry().Unlock();
- if (thread) {
- GetInfoForStackVar(addr, descr, thread);
- return;
- }
- GetInfoForHeapAddress(addr, descr);
-}
-
-static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
+uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
bool alloc_stack) {
AsanChunkView chunk = FindHeapChunkByAddress(addr);
if (!chunk.IsValid()) return 0;
@@ -106,18 +71,58 @@ static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
return 0;
}
-} // namespace __asan
-
-using namespace __asan;
+} // namespace
SANITIZER_INTERFACE_ATTRIBUTE
const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
- uptr *region_address, uptr *region_size) {
- AddressDescription descr = { name, name_size, 0, 0, nullptr };
- AsanLocateAddress(addr, &descr);
- if (region_address) *region_address = descr.region_address;
- if (region_size) *region_size = descr.region_size;
- return descr.region_kind;
+ uptr *region_address_ptr,
+ uptr *region_size_ptr) {
+ AddressDescription descr(addr);
+ uptr region_address = 0;
+ uptr region_size = 0;
+ const char *region_kind = nullptr;
+ if (name && name_size > 0) name[0] = 0;
+
+ if (auto shadow = descr.AsShadow()) {
+ // region_{address,size} are already 0
+ switch (shadow->kind) {
+ case kShadowKindLow:
+ region_kind = "low shadow";
+ break;
+ case kShadowKindGap:
+ region_kind = "shadow gap";
+ break;
+ case kShadowKindHigh:
+ region_kind = "high shadow";
+ break;
+ }
+ } else if (auto heap = descr.AsHeap()) {
+ region_kind = "heap";
+ region_address = heap->chunk_access.chunk_begin;
+ region_size = heap->chunk_access.chunk_size;
+ } else if (auto stack = descr.AsStack()) {
+ region_kind = "stack";
+ if (!stack->frame_descr) {
+ // region_{address,size} are already 0
+ } else {
+ FindInfoForStackVar(addr, stack->frame_descr, stack->offset, name,
+ name_size, region_address, region_size);
+ }
+ } else if (auto global = descr.AsGlobal()) {
+ region_kind = "global";
+ auto &g = global->globals[0];
+ internal_strlcpy(name, g.name, name_size);
+ region_address = g.beg;
+ region_size = g.size;
+ } else {
+ // region_{address,size} are already 0
+ region_kind = "heap-invalid";
+ }
+
+ CHECK(region_kind);
+ if (region_address_ptr) *region_address_ptr = region_address;
+ if (region_size_ptr) *region_size_ptr = region_size;
+ return region_kind;
}
SANITIZER_INTERFACE_ATTRIBUTE
diff --git a/libsanitizer/asan/asan_descriptions.cc b/libsanitizer/asan/asan_descriptions.cc
new file mode 100644
index 00000000000..35d1619f2d9
--- /dev/null
+++ b/libsanitizer/asan/asan_descriptions.cc
@@ -0,0 +1,484 @@
+//===-- asan_descriptions.cc ------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan functions for getting information about an address and/or printing it.
+//===----------------------------------------------------------------------===//
+
+#include "asan_descriptions.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __asan {
+
+// Return " (thread_name) " or an empty string if the name is empty.
+const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
+ uptr buff_len) {
+ const char *name = t->name;
+ if (name[0] == '\0') return "";
+ buff[0] = 0;
+ internal_strncat(buff, " (", 3);
+ internal_strncat(buff, name, buff_len - 4);
+ internal_strncat(buff, ")", 2);
+ return buff;
+}
+
+const char *ThreadNameWithParenthesis(u32 tid, char buff[], uptr buff_len) {
+ if (tid == kInvalidTid) return "";
+ asanThreadRegistry().CheckLocked();
+ AsanThreadContext *t = GetThreadContextByTidLocked(tid);
+ return ThreadNameWithParenthesis(t, buff, buff_len);
+}
+
+void DescribeThread(AsanThreadContext *context) {
+ CHECK(context);
+ asanThreadRegistry().CheckLocked();
+ // No need to announce the main thread.
+ if (context->tid == 0 || context->announced) {
+ return;
+ }
+ context->announced = true;
+ char tname[128];
+ InternalScopedString str(1024);
+ str.append("Thread T%d%s", context->tid,
+ ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
+ if (context->parent_tid == kInvalidTid) {
+ str.append(" created by unknown thread\n");
+ Printf("%s", str.data());
+ return;
+ }
+ str.append(
+ " created by T%d%s here:\n", context->parent_tid,
+ ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
+ Printf("%s", str.data());
+ StackDepotGet(context->stack_id).Print();
+ // Recursively described parent thread if needed.
+ if (flags()->print_full_thread_history) {
+ AsanThreadContext *parent_context =
+ GetThreadContextByTidLocked(context->parent_tid);
+ DescribeThread(parent_context);
+ }
+}
+
+// Shadow descriptions
+static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) {
+ CHECK(!AddrIsInMem(addr));
+ if (AddrIsInShadowGap(addr)) {
+ *shadow_kind = kShadowKindGap;
+ } else if (AddrIsInHighShadow(addr)) {
+ *shadow_kind = kShadowKindHigh;
+ } else if (AddrIsInLowShadow(addr)) {
+ *shadow_kind = kShadowKindLow;
+ } else {
+ CHECK(0 && "Address is not in memory and not in shadow?");
+ return false;
+ }
+ return true;
+}
+
+bool DescribeAddressIfShadow(uptr addr) {
+ ShadowAddressDescription descr;
+ if (!GetShadowAddressInformation(addr, &descr)) return false;
+ descr.Print();
+ return true;
+}
+
+bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) {
+ if (AddrIsInMem(addr)) return false;
+ ShadowKind shadow_kind;
+ if (!GetShadowKind(addr, &shadow_kind)) return false;
+ if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr;
+ descr->addr = addr;
+ descr->kind = shadow_kind;
+ return true;
+}
+
+// Heap descriptions
+static void GetAccessToHeapChunkInformation(ChunkAccess *descr,
+ AsanChunkView chunk, uptr addr,
+ uptr access_size) {
+ descr->bad_addr = addr;
+ if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) {
+ descr->access_type = kAccessTypeLeft;
+ } else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) {
+ descr->access_type = kAccessTypeRight;
+ if (descr->offset < 0) {
+ descr->bad_addr -= descr->offset;
+ descr->offset = 0;
+ }
+ } else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) {
+ descr->access_type = kAccessTypeInside;
+ } else {
+ descr->access_type = kAccessTypeUnknown;
+ }
+ descr->chunk_begin = chunk.Beg();
+ descr->chunk_size = chunk.UsedSize();
+ descr->alloc_type = chunk.GetAllocType();
+}
+
+static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) {
+ Decorator d;
+ InternalScopedString str(4096);
+ str.append("%s", d.Location());
+ switch (descr.access_type) {
+ case kAccessTypeLeft:
+ str.append("%p is located %zd bytes to the left of",
+ (void *)descr.bad_addr, descr.offset);
+ break;
+ case kAccessTypeRight:
+ str.append("%p is located %zd bytes to the right of",
+ (void *)descr.bad_addr, descr.offset);
+ break;
+ case kAccessTypeInside:
+ str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr,
+ descr.offset);
+ break;
+ case kAccessTypeUnknown:
+ str.append(
+ "%p is located somewhere around (this is AddressSanitizer bug!)",
+ (void *)descr.bad_addr);
+ }
+ str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size,
+ (void *)descr.chunk_begin,
+ (void *)(descr.chunk_begin + descr.chunk_size));
+ str.append("%s", d.EndLocation());
+ Printf("%s", str.data());
+}
+
+bool GetHeapAddressInformation(uptr addr, uptr access_size,
+ HeapAddressDescription *descr) {
+ AsanChunkView chunk = FindHeapChunkByAddress(addr);
+ if (!chunk.IsValid()) {
+ return false;
+ }
+ descr->addr = addr;
+ GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr,
+ access_size);
+ CHECK_NE(chunk.AllocTid(), kInvalidTid);
+ descr->alloc_tid = chunk.AllocTid();
+ descr->alloc_stack_id = chunk.GetAllocStackId();
+ descr->free_tid = chunk.FreeTid();
+ if (descr->free_tid != kInvalidTid)
+ descr->free_stack_id = chunk.GetFreeStackId();
+ return true;
+}
+
+static StackTrace GetStackTraceFromId(u32 id) {
+ CHECK(id);
+ StackTrace res = StackDepotGet(id);
+ CHECK(res.trace);
+ return res;
+}
+
+bool DescribeAddressIfHeap(uptr addr, uptr access_size) {
+ HeapAddressDescription descr;
+ if (!GetHeapAddressInformation(addr, access_size, &descr)) {
+ Printf(
+ "AddressSanitizer can not describe address in more detail "
+ "(wild memory access suspected).\n");
+ return false;
+ }
+ descr.Print();
+ return true;
+}
+
+// Stack descriptions
+bool GetStackAddressInformation(uptr addr, uptr access_size,
+ StackAddressDescription *descr) {
+ AsanThread *t = FindThreadByStackAddress(addr);
+ if (!t) return false;
+
+ descr->addr = addr;
+ descr->tid = t->tid();
+ // Try to fetch precise stack frame for this access.
+ AsanThread::StackFrameAccess access;
+ if (!t->GetStackFrameAccessByAddr(addr, &access)) {
+ descr->frame_descr = nullptr;
+ return true;
+ }
+
+ descr->offset = access.offset;
+ descr->access_size = access_size;
+ descr->frame_pc = access.frame_pc;
+ descr->frame_descr = access.frame_descr;
+
+#if SANITIZER_PPC64V1
+ // On PowerPC64 ELFv1, the address of a function actually points to a
+ // three-doubleword data structure with the first field containing
+ // the address of the function's code.
+ descr->frame_pc = *reinterpret_cast<uptr *>(descr->frame_pc);
+#endif
+ descr->frame_pc += 16;
+
+ return true;
+}
+
+static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
+ uptr access_size, uptr prev_var_end,
+ uptr next_var_beg) {
+ uptr var_end = var.beg + var.size;
+ uptr addr_end = addr + access_size;
+ const char *pos_descr = nullptr;
+ // If the variable [var.beg, var_end) is the nearest variable to the
+ // current memory access, indicate it in the log.
+ if (addr >= var.beg) {
+ if (addr_end <= var_end)
+ pos_descr = "is inside"; // May happen if this is a use-after-return.
+ else if (addr < var_end)
+ pos_descr = "partially overflows";
+ else if (addr_end <= next_var_beg &&
+ next_var_beg - addr_end >= addr - var_end)
+ pos_descr = "overflows";
+ } else {
+ if (addr_end > var.beg)
+ pos_descr = "partially underflows";
+ else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end)
+ pos_descr = "underflows";
+ }
+ InternalScopedString str(1024);
+ str.append(" [%zd, %zd)", var.beg, var_end);
+ // Render variable name.
+ str.append(" '");
+ for (uptr i = 0; i < var.name_len; ++i) {
+ str.append("%c", var.name_pos[i]);
+ }
+ str.append("'");
+ if (pos_descr) {
+ Decorator d;
+ // FIXME: we may want to also print the size of the access here,
+ // but in case of accesses generated by memset it may be confusing.
+ str.append("%s <== Memory access at offset %zd %s this variable%s\n",
+ d.Location(), addr, pos_descr, d.EndLocation());
+ } else {
+ str.append("\n");
+ }
+ Printf("%s", str.data());
+}
+
+bool DescribeAddressIfStack(uptr addr, uptr access_size) {
+ StackAddressDescription descr;
+ if (!GetStackAddressInformation(addr, access_size, &descr)) return false;
+ descr.Print();
+ return true;
+}
+
+// Global descriptions
+static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
+ const __asan_global &g) {
+ InternalScopedString str(4096);
+ Decorator d;
+ str.append("%s", d.Location());
+ if (addr < g.beg) {
+ str.append("%p is located %zd bytes to the left", (void *)addr,
+ g.beg - addr);
+ } else if (addr + access_size > g.beg + g.size) {
+ if (addr < g.beg + g.size) addr = g.beg + g.size;
+ str.append("%p is located %zd bytes to the right", (void *)addr,
+ addr - (g.beg + g.size));
+ } else {
+ // Can it happen?
+ str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
+ }
+ str.append(" of global variable '%s' defined in '",
+ MaybeDemangleGlobalName(g.name));
+ PrintGlobalLocation(&str, g);
+ str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
+ str.append("%s", d.EndLocation());
+ PrintGlobalNameIfASCII(&str, g);
+ Printf("%s", str.data());
+}
+
+bool GetGlobalAddressInformation(uptr addr, uptr access_size,
+ GlobalAddressDescription *descr) {
+ descr->addr = addr;
+ int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites,
+ ARRAY_SIZE(descr->globals));
+ descr->size = globals_num;
+ descr->access_size = access_size;
+ return globals_num != 0;
+}
+
+bool DescribeAddressIfGlobal(uptr addr, uptr access_size,
+ const char *bug_type) {
+ GlobalAddressDescription descr;
+ if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false;
+
+ descr.Print(bug_type);
+ return true;
+}
+
+void ShadowAddressDescription::Print() const {
+ Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]);
+}
+
+void GlobalAddressDescription::Print(const char *bug_type) const {
+ for (int i = 0; i < size; i++) {
+ DescribeAddressRelativeToGlobal(addr, access_size, globals[i]);
+ if (bug_type &&
+ 0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
+ reg_sites[i]) {
+ Printf(" registered at:\n");
+ StackDepotGet(reg_sites[i]).Print();
+ }
+ }
+}
+
+void StackAddressDescription::Print() const {
+ Decorator d;
+ char tname[128];
+ Printf("%s", d.Location());
+ Printf("Address %p is located in stack of thread T%d%s", addr, tid,
+ ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
+
+ if (!frame_descr) {
+ Printf("%s\n", d.EndLocation());
+ return;
+ }
+ Printf(" at offset %zu in frame%s\n", offset, d.EndLocation());
+
+ // Now we print the frame where the alloca has happened.
+ // We print this frame as a stack trace with one element.
+ // The symbolizer may print more than one frame if inlining was involved.
+ // The frame numbers may be different than those in the stack trace printed
+ // previously. That's unfortunate, but I have no better solution,
+ // especially given that the alloca may be from entirely different place
+ // (e.g. use-after-scope, or different thread's stack).
+ Printf("%s", d.EndLocation());
+ StackTrace alloca_stack(&frame_pc, 1);
+ alloca_stack.Print();
+
+ InternalMmapVector<StackVarDescr> vars(16);
+ if (!ParseFrameDescription(frame_descr, &vars)) {
+ Printf(
+ "AddressSanitizer can't parse the stack frame "
+ "descriptor: |%s|\n",
+ frame_descr);
+ // 'addr' is a stack address, so return true even if we can't parse frame
+ return;
+ }
+ uptr n_objects = vars.size();
+ // Report the number of stack objects.
+ Printf(" This frame has %zu object(s):\n", n_objects);
+
+ // Report all objects in this frame.
+ for (uptr i = 0; i < n_objects; i++) {
+ uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
+ uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
+ PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end,
+ next_var_beg);
+ }
+ Printf(
+ "HINT: this may be a false positive if your program uses "
+ "some custom stack unwind mechanism or swapcontext\n");
+ if (SANITIZER_WINDOWS)
+ Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
+ else
+ Printf(" (longjmp and C++ exceptions *are* supported)\n");
+
+ DescribeThread(GetThreadContextByTidLocked(tid));
+}
+
+void HeapAddressDescription::Print() const {
+ PrintHeapChunkAccess(addr, chunk_access);
+
+ asanThreadRegistry().CheckLocked();
+ AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);
+ StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);
+
+ char tname[128];
+ Decorator d;
+ AsanThreadContext *free_thread = nullptr;
+ if (free_tid != kInvalidTid) {
+ free_thread = GetThreadContextByTidLocked(free_tid);
+ Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
+ free_thread->tid,
+ ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
+ d.EndAllocation());
+ StackTrace free_stack = GetStackTraceFromId(free_stack_id);
+ free_stack.Print();
+ Printf("%spreviously allocated by thread T%d%s here:%s\n", d.Allocation(),
+ alloc_thread->tid,
+ ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+ d.EndAllocation());
+ } else {
+ Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
+ alloc_thread->tid,
+ ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+ d.EndAllocation());
+ }
+ alloc_stack.Print();
+ DescribeThread(GetCurrentThread());
+ if (free_thread) DescribeThread(free_thread);
+ DescribeThread(alloc_thread);
+}
+
+AddressDescription::AddressDescription(uptr addr, uptr access_size,
+ bool shouldLockThreadRegistry) {
+ if (GetShadowAddressInformation(addr, &data.shadow)) {
+ data.kind = kAddressKindShadow;
+ return;
+ }
+ if (GetHeapAddressInformation(addr, access_size, &data.heap)) {
+ data.kind = kAddressKindHeap;
+ return;
+ }
+
+ bool isStackMemory = false;
+ if (shouldLockThreadRegistry) {
+ ThreadRegistryLock l(&asanThreadRegistry());
+ isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
+ } else {
+ isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
+ }
+ if (isStackMemory) {
+ data.kind = kAddressKindStack;
+ return;
+ }
+
+ if (GetGlobalAddressInformation(addr, access_size, &data.global)) {
+ data.kind = kAddressKindGlobal;
+ return;
+ }
+ data.kind = kAddressKindWild;
+ addr = 0;
+}
+
+void PrintAddressDescription(uptr addr, uptr access_size,
+ const char *bug_type) {
+ ShadowAddressDescription shadow_descr;
+ if (GetShadowAddressInformation(addr, &shadow_descr)) {
+ shadow_descr.Print();
+ return;
+ }
+
+ GlobalAddressDescription global_descr;
+ if (GetGlobalAddressInformation(addr, access_size, &global_descr)) {
+ global_descr.Print(bug_type);
+ return;
+ }
+
+ StackAddressDescription stack_descr;
+ if (GetStackAddressInformation(addr, access_size, &stack_descr)) {
+ stack_descr.Print();
+ return;
+ }
+
+ HeapAddressDescription heap_descr;
+ if (GetHeapAddressInformation(addr, access_size, &heap_descr)) {
+ heap_descr.Print();
+ return;
+ }
+
+ // We exhausted our possibilities. Bail out.
+ Printf(
+ "AddressSanitizer can not describe address in more detail "
+ "(wild memory access suspected).\n");
+}
+} // namespace __asan
diff --git a/libsanitizer/asan/asan_descriptions.h b/libsanitizer/asan/asan_descriptions.h
new file mode 100644
index 00000000000..584b9ba6491
--- /dev/null
+++ b/libsanitizer/asan/asan_descriptions.h
@@ -0,0 +1,251 @@
+//===-- asan_descriptions.h -------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for asan_descriptions.cc.
+// TODO(filcab): Most struct definitions should move to the interface headers.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_DESCRIPTIONS_H
+#define ASAN_DESCRIPTIONS_H
+
+#include "asan_allocator.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+
+namespace __asan {
+
+void DescribeThread(AsanThreadContext *context);
+static inline void DescribeThread(AsanThread *t) {
+ if (t) DescribeThread(t->context());
+}
+const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
+ uptr buff_len);
+const char *ThreadNameWithParenthesis(u32 tid, char buff[], uptr buff_len);
+
+class Decorator : public __sanitizer::SanitizerCommonDecorator {
+ public:
+ Decorator() : SanitizerCommonDecorator() {}
+ const char *Access() { return Blue(); }
+ const char *EndAccess() { return Default(); }
+ const char *Location() { return Green(); }
+ const char *EndLocation() { return Default(); }
+ const char *Allocation() { return Magenta(); }
+ const char *EndAllocation() { return Default(); }
+
+ const char *ShadowByte(u8 byte) {
+ switch (byte) {
+ case kAsanHeapLeftRedzoneMagic:
+ case kAsanArrayCookieMagic:
+ return Red();
+ case kAsanHeapFreeMagic:
+ return Magenta();
+ case kAsanStackLeftRedzoneMagic:
+ case kAsanStackMidRedzoneMagic:
+ case kAsanStackRightRedzoneMagic:
+ return Red();
+ case kAsanStackAfterReturnMagic:
+ return Magenta();
+ case kAsanInitializationOrderMagic:
+ return Cyan();
+ case kAsanUserPoisonedMemoryMagic:
+ case kAsanContiguousContainerOOBMagic:
+ case kAsanAllocaLeftMagic:
+ case kAsanAllocaRightMagic:
+ return Blue();
+ case kAsanStackUseAfterScopeMagic:
+ return Magenta();
+ case kAsanGlobalRedzoneMagic:
+ return Red();
+ case kAsanInternalHeapMagic:
+ return Yellow();
+ case kAsanIntraObjectRedzone:
+ return Yellow();
+ default:
+ return Default();
+ }
+ }
+ const char *EndShadowByte() { return Default(); }
+ const char *MemoryByte() { return Magenta(); }
+ const char *EndMemoryByte() { return Default(); }
+};
+
+enum ShadowKind : u8 {
+ kShadowKindLow,
+ kShadowKindGap,
+ kShadowKindHigh,
+};
+static const char *const ShadowNames[] = {"low shadow", "shadow gap",
+ "high shadow"};
+
+struct ShadowAddressDescription {
+ uptr addr;
+ ShadowKind kind;
+ u8 shadow_byte;
+
+ void Print() const;
+};
+
+bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr);
+bool DescribeAddressIfShadow(uptr addr);
+
+enum AccessType {
+ kAccessTypeLeft,
+ kAccessTypeRight,
+ kAccessTypeInside,
+ kAccessTypeUnknown, // This means we have an AddressSanitizer bug!
+};
+
+struct ChunkAccess {
+ uptr bad_addr;
+ sptr offset;
+ uptr chunk_begin;
+ uptr chunk_size;
+ u32 access_type : 2;
+ u32 alloc_type : 2;
+};
+
+struct HeapAddressDescription {
+ uptr addr;
+ uptr alloc_tid;
+ uptr free_tid;
+ u32 alloc_stack_id;
+ u32 free_stack_id;
+ ChunkAccess chunk_access;
+
+ void Print() const;
+};
+
+bool GetHeapAddressInformation(uptr addr, uptr access_size,
+ HeapAddressDescription *descr);
+bool DescribeAddressIfHeap(uptr addr, uptr access_size = 1);
+
+struct StackAddressDescription {
+ uptr addr;
+ uptr tid;
+ uptr offset;
+ uptr frame_pc;
+ uptr access_size;
+ const char *frame_descr;
+
+ void Print() const;
+};
+
+bool GetStackAddressInformation(uptr addr, uptr access_size,
+ StackAddressDescription *descr);
+
+struct GlobalAddressDescription {
+ uptr addr;
+ // Assume address is close to at most four globals.
+ static const int kMaxGlobals = 4;
+ __asan_global globals[kMaxGlobals];
+ u32 reg_sites[kMaxGlobals];
+ uptr access_size;
+ u8 size;
+
+ void Print(const char *bug_type = "") const;
+};
+
+bool GetGlobalAddressInformation(uptr addr, uptr access_size,
+ GlobalAddressDescription *descr);
+bool DescribeAddressIfGlobal(uptr addr, uptr access_size, const char *bug_type);
+
+// General function to describe an address. Will try to describe the address as
+// a shadow, global (variable), stack, or heap address.
+// bug_type is optional and is used for checking if we're reporting an
+// initialization-order-fiasco
+// The proper access_size should be passed for stack, global, and heap
+// addresses. Defaults to 1.
+// Each of the *AddressDescription functions has its own Print() member, which
+// may take access_size and bug_type parameters if needed.
+void PrintAddressDescription(uptr addr, uptr access_size = 1,
+ const char *bug_type = "");
+
+enum AddressKind {
+ kAddressKindWild,
+ kAddressKindShadow,
+ kAddressKindHeap,
+ kAddressKindStack,
+ kAddressKindGlobal,
+};
+
+class AddressDescription {
+ struct AddressDescriptionData {
+ AddressKind kind;
+ union {
+ ShadowAddressDescription shadow;
+ HeapAddressDescription heap;
+ StackAddressDescription stack;
+ GlobalAddressDescription global;
+ uptr addr;
+ };
+ };
+
+ AddressDescriptionData data;
+
+ public:
+ AddressDescription() = default;
+ // shouldLockThreadRegistry allows us to skip locking if we're sure we already
+ // have done it.
+ AddressDescription(uptr addr, bool shouldLockThreadRegistry = true)
+ : AddressDescription(addr, 1, shouldLockThreadRegistry) {}
+ AddressDescription(uptr addr, uptr access_size,
+ bool shouldLockThreadRegistry = true);
+
+ uptr Address() const {
+ switch (data.kind) {
+ case kAddressKindWild:
+ return data.addr;
+ case kAddressKindShadow:
+ return data.shadow.addr;
+ case kAddressKindHeap:
+ return data.heap.addr;
+ case kAddressKindStack:
+ return data.stack.addr;
+ case kAddressKindGlobal:
+ return data.global.addr;
+ }
+ UNREACHABLE("AddressInformation kind is invalid");
+ }
+ void Print(const char *bug_descr = nullptr) const {
+ switch (data.kind) {
+ case kAddressKindWild:
+ Printf("Address %p is a wild pointer.\n", data.addr);
+ return;
+ case kAddressKindShadow:
+ return data.shadow.Print();
+ case kAddressKindHeap:
+ return data.heap.Print();
+ case kAddressKindStack:
+ return data.stack.Print();
+ case kAddressKindGlobal:
+ // initialization-order-fiasco has a special Print()
+ return data.global.Print(bug_descr);
+ }
+ UNREACHABLE("AddressInformation kind is invalid");
+ }
+
+ void StoreTo(AddressDescriptionData *dst) const { *dst = data; }
+
+ const ShadowAddressDescription *AsShadow() const {
+ return data.kind == kAddressKindShadow ? &data.shadow : nullptr;
+ }
+ const HeapAddressDescription *AsHeap() const {
+ return data.kind == kAddressKindHeap ? &data.heap : nullptr;
+ }
+ const StackAddressDescription *AsStack() const {
+ return data.kind == kAddressKindStack ? &data.stack : nullptr;
+ }
+ const GlobalAddressDescription *AsGlobal() const {
+ return data.kind == kAddressKindGlobal ? &data.global : nullptr;
+ }
+};
+
+} // namespace __asan
+
+#endif // ASAN_DESCRIPTIONS_H
diff --git a/libsanitizer/asan/asan_errors.cc b/libsanitizer/asan/asan_errors.cc
new file mode 100644
index 00000000000..73c4cca843b
--- /dev/null
+++ b/libsanitizer/asan/asan_errors.cc
@@ -0,0 +1,494 @@
+//===-- asan_errors.cc ------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan implementation for error structures.
+//===----------------------------------------------------------------------===//
+
+#include "asan_errors.h"
+#include <signal.h>
+#include "asan_descriptions.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __asan {
+
+void ErrorStackOverflow::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report(
+ "ERROR: AddressSanitizer: stack-overflow on address %p"
+ " (pc %p bp %p sp %p T%d)\n",
+ (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid);
+ Printf("%s", d.EndWarning());
+ scariness.Print();
+ BufferedStackTrace stack;
+ GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context,
+ common_flags()->fast_unwind_on_fatal);
+ stack.Print();
+ ReportErrorSummary("stack-overflow", &stack);
+}
+
+static void MaybeDumpInstructionBytes(uptr pc) {
+ if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) return;
+ InternalScopedString str(1024);
+ str.append("First 16 instruction bytes at pc: ");
+ if (IsAccessibleMemoryRange(pc, 16)) {
+ for (int i = 0; i < 16; ++i) {
+ PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/ false, " ");
+ }
+ str.append("\n");
+ } else {
+ str.append("unaccessible\n");
+ }
+ Report("%s", str.data());
+}
+
+void ErrorDeadlySignal::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ const char *description = DescribeSignalOrException(signo);
+ Report(
+ "ERROR: AddressSanitizer: %s on unknown address %p (pc %p bp %p sp %p "
+ "T%d)\n",
+ description, (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid);
+ Printf("%s", d.EndWarning());
+ if (pc < GetPageSizeCached()) Report("Hint: pc points to the zero page.\n");
+ if (is_memory_access) {
+ const char *access_type =
+ write_flag == SignalContext::WRITE
+ ? "WRITE"
+ : (write_flag == SignalContext::READ ? "READ" : "UNKNOWN");
+ Report("The signal is caused by a %s memory access.\n", access_type);
+ if (addr < GetPageSizeCached())
+ Report("Hint: address points to the zero page.\n");
+ }
+ scariness.Print();
+ BufferedStackTrace stack;
+ GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context,
+ common_flags()->fast_unwind_on_fatal);
+ stack.Print();
+ MaybeDumpInstructionBytes(pc);
+ Printf("AddressSanitizer can not provide additional info.\n");
+ ReportErrorSummary(description, &stack);
+}
+
+void ErrorDoubleFree::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ char tname[128];
+ Report(
+ "ERROR: AddressSanitizer: attempting double-free on %p in "
+ "thread T%d%s:\n",
+ addr_description.addr, tid,
+ ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
+ Printf("%s", d.EndWarning());
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(second_free_stack->trace[0],
+ second_free_stack->top_frame_bp);
+ stack.Print();
+ addr_description.Print();
+ ReportErrorSummary("double-free", &stack);
+}
+
+void ErrorNewDeleteSizeMismatch::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ char tname[128];
+ Report(
+ "ERROR: AddressSanitizer: new-delete-type-mismatch on %p in thread "
+ "T%d%s:\n",
+ addr_description.addr, tid,
+ ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
+ Printf("%s object passed to delete has wrong type:\n", d.EndWarning());
+ Printf(
+ " size of the allocated type: %zd bytes;\n"
+ " size of the deallocated type: %zd bytes.\n",
+ addr_description.chunk_access.chunk_size, delete_size);
+ CHECK_GT(free_stack->size, 0);
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
+ stack.Print();
+ addr_description.Print();
+ ReportErrorSummary("new-delete-type-mismatch", &stack);
+ Report(
+ "HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
+}
+
+void ErrorFreeNotMalloced::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ char tname[128];
+ Report(
+ "ERROR: AddressSanitizer: attempting free on address "
+ "which was not malloc()-ed: %p in thread T%d%s\n",
+ addr_description.Address(), tid,
+ ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
+ Printf("%s", d.EndWarning());
+ CHECK_GT(free_stack->size, 0);
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
+ stack.Print();
+ addr_description.Print();
+ ReportErrorSummary("bad-free", &stack);
+}
+
+void ErrorAllocTypeMismatch::Print() {
+ static const char *alloc_names[] = {"INVALID", "malloc", "operator new",
+ "operator new []"};
+ static const char *dealloc_names[] = {"INVALID", "free", "operator delete",
+ "operator delete []"};
+ CHECK_NE(alloc_type, dealloc_type);
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n",
+ alloc_names[alloc_type], dealloc_names[dealloc_type],
+ addr_description.addr);
+ Printf("%s", d.EndWarning());
+ CHECK_GT(dealloc_stack->size, 0);
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp);
+ stack.Print();
+ addr_description.Print();
+ ReportErrorSummary("alloc-dealloc-mismatch", &stack);
+ Report(
+ "HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
+}
+
+void ErrorMallocUsableSizeNotOwned::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report(
+ "ERROR: AddressSanitizer: attempting to call malloc_usable_size() for "
+ "pointer which is not owned: %p\n",
+ addr_description.Address());
+ Printf("%s", d.EndWarning());
+ stack->Print();
+ addr_description.Print();
+ ReportErrorSummary("bad-malloc_usable_size", stack);
+}
+
+void ErrorSanitizerGetAllocatedSizeNotOwned::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report(
+ "ERROR: AddressSanitizer: attempting to call "
+ "__sanitizer_get_allocated_size() for pointer which is not owned: %p\n",
+ addr_description.Address());
+ Printf("%s", d.EndWarning());
+ stack->Print();
+ addr_description.Print();
+ ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
+}
+
+void ErrorStringFunctionMemoryRangesOverlap::Print() {
+ Decorator d;
+ char bug_type[100];
+ internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
+ Printf("%s", d.Warning());
+ Report(
+ "ERROR: AddressSanitizer: %s: memory ranges [%p,%p) and [%p, %p) "
+ "overlap\n",
+ bug_type, addr1_description.Address(),
+ addr1_description.Address() + length1, addr2_description.Address(),
+ addr2_description.Address() + length2);
+ Printf("%s", d.EndWarning());
+ scariness.Print();
+ stack->Print();
+ addr1_description.Print();
+ addr2_description.Print();
+ ReportErrorSummary(bug_type, stack);
+}
+
+void ErrorStringFunctionSizeOverflow::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ const char *bug_type = "negative-size-param";
+ Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size);
+ Printf("%s", d.EndWarning());
+ scariness.Print();
+ stack->Print();
+ addr_description.Print();
+ ReportErrorSummary(bug_type, stack);
+}
+
+void ErrorBadParamsToAnnotateContiguousContainer::Print() {
+ Report(
+ "ERROR: AddressSanitizer: bad parameters to "
+ "__sanitizer_annotate_contiguous_container:\n"
+ " beg : %p\n"
+ " end : %p\n"
+ " old_mid : %p\n"
+ " new_mid : %p\n",
+ beg, end, old_mid, new_mid);
+ uptr granularity = SHADOW_GRANULARITY;
+ if (!IsAligned(beg, granularity))
+ Report("ERROR: beg is not aligned by %d\n", granularity);
+ stack->Print();
+ ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
+}
+
+void ErrorODRViolation::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: odr-violation (%p):\n", global1.beg);
+ Printf("%s", d.EndWarning());
+ InternalScopedString g1_loc(256), g2_loc(256);
+ PrintGlobalLocation(&g1_loc, global1);
+ PrintGlobalLocation(&g2_loc, global2);
+ Printf(" [1] size=%zd '%s' %s\n", global1.size,
+ MaybeDemangleGlobalName(global1.name), g1_loc.data());
+ Printf(" [2] size=%zd '%s' %s\n", global2.size,
+ MaybeDemangleGlobalName(global2.name), g2_loc.data());
+ if (stack_id1 && stack_id2) {
+ Printf("These globals were registered at these points:\n");
+ Printf(" [1]:\n");
+ StackDepotGet(stack_id1).Print();
+ Printf(" [2]:\n");
+ StackDepotGet(stack_id2).Print();
+ }
+ Report(
+ "HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=detect_odr_violation=0\n");
+ InternalScopedString error_msg(256);
+ error_msg.append("odr-violation: global '%s' at %s",
+ MaybeDemangleGlobalName(global1.name), g1_loc.data());
+ ReportErrorSummary(error_msg.data());
+}
+
+void ErrorInvalidPointerPair::Print() {
+ const char *bug_type = "invalid-pointer-pair";
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n",
+ addr1_description.Address(), addr2_description.Address());
+ Printf("%s", d.EndWarning());
+ GET_STACK_TRACE_FATAL(pc, bp);
+ stack.Print();
+ addr1_description.Print();
+ addr2_description.Print();
+ ReportErrorSummary(bug_type, &stack);
+}
+
+static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
+ return s[-1] > 127 && s[1] > 127;
+}
+
+ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr,
+ bool is_write_, uptr access_size_)
+ : ErrorBase(tid),
+ addr_description(addr, access_size_, /*shouldLockThreadRegistry=*/false),
+ pc(pc_),
+ bp(bp_),
+ sp(sp_),
+ access_size(access_size_),
+ is_write(is_write_),
+ shadow_val(0) {
+ scariness.Clear();
+ if (access_size) {
+ if (access_size <= 9) {
+ char desr[] = "?-byte";
+ desr[0] = '0' + access_size;
+ scariness.Scare(access_size + access_size / 2, desr);
+ } else if (access_size >= 10) {
+ scariness.Scare(15, "multi-byte");
+ }
+ is_write ? scariness.Scare(20, "write") : scariness.Scare(1, "read");
+
+ // Determine the error type.
+ bug_descr = "unknown-crash";
+ if (AddrIsInMem(addr)) {
+ u8 *shadow_addr = (u8 *)MemToShadow(addr);
+ // If we are accessing 16 bytes, look at the second shadow byte.
+ if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) shadow_addr++;
+ // If we are in the partial right redzone, look at the next shadow byte.
+ if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++;
+ bool far_from_bounds = false;
+ shadow_val = *shadow_addr;
+ int bug_type_score = 0;
+ // For use-after-frees reads are almost as bad as writes.
+ int read_after_free_bonus = 0;
+ switch (shadow_val) {
+ case kAsanHeapLeftRedzoneMagic:
+ case kAsanArrayCookieMagic:
+ bug_descr = "heap-buffer-overflow";
+ bug_type_score = 10;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ case kAsanHeapFreeMagic:
+ bug_descr = "heap-use-after-free";
+ bug_type_score = 20;
+ if (!is_write) read_after_free_bonus = 18;
+ break;
+ case kAsanStackLeftRedzoneMagic:
+ bug_descr = "stack-buffer-underflow";
+ bug_type_score = 25;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ case kAsanInitializationOrderMagic:
+ bug_descr = "initialization-order-fiasco";
+ bug_type_score = 1;
+ break;
+ case kAsanStackMidRedzoneMagic:
+ case kAsanStackRightRedzoneMagic:
+ bug_descr = "stack-buffer-overflow";
+ bug_type_score = 25;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ case kAsanStackAfterReturnMagic:
+ bug_descr = "stack-use-after-return";
+ bug_type_score = 30;
+ if (!is_write) read_after_free_bonus = 18;
+ break;
+ case kAsanUserPoisonedMemoryMagic:
+ bug_descr = "use-after-poison";
+ bug_type_score = 20;
+ break;
+ case kAsanContiguousContainerOOBMagic:
+ bug_descr = "container-overflow";
+ bug_type_score = 10;
+ break;
+ case kAsanStackUseAfterScopeMagic:
+ bug_descr = "stack-use-after-scope";
+ bug_type_score = 10;
+ break;
+ case kAsanGlobalRedzoneMagic:
+ bug_descr = "global-buffer-overflow";
+ bug_type_score = 10;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ case kAsanIntraObjectRedzone:
+ bug_descr = "intra-object-overflow";
+ bug_type_score = 10;
+ break;
+ case kAsanAllocaLeftMagic:
+ case kAsanAllocaRightMagic:
+ bug_descr = "dynamic-stack-buffer-overflow";
+ bug_type_score = 25;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ }
+ scariness.Scare(bug_type_score + read_after_free_bonus, bug_descr);
+ if (far_from_bounds) scariness.Scare(10, "far-from-bounds");
+ }
+ }
+}
+
+static void PrintContainerOverflowHint() {
+ Printf("HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=detect_container_overflow=0.\n"
+ "If you suspect a false positive see also: "
+ "https://github.com/google/sanitizers/wiki/"
+ "AddressSanitizerContainerOverflow.\n");
+}
+
+static void PrintShadowByte(InternalScopedString *str, const char *before,
+ u8 byte, const char *after = "\n") {
+ PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
+}
+
+static void PrintLegend(InternalScopedString *str) {
+ str->append(
+ "Shadow byte legend (one shadow byte represents %d "
+ "application bytes):\n",
+ (int)SHADOW_GRANULARITY);
+ PrintShadowByte(str, " Addressable: ", 0);
+ str->append(" Partially addressable: ");
+ for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
+ str->append("\n");
+ PrintShadowByte(str, " Heap left redzone: ",
+ kAsanHeapLeftRedzoneMagic);
+ PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic);
+ PrintShadowByte(str, " Stack left redzone: ",
+ kAsanStackLeftRedzoneMagic);
+ PrintShadowByte(str, " Stack mid redzone: ",
+ kAsanStackMidRedzoneMagic);
+ PrintShadowByte(str, " Stack right redzone: ",
+ kAsanStackRightRedzoneMagic);
+ PrintShadowByte(str, " Stack after return: ",
+ kAsanStackAfterReturnMagic);
+ PrintShadowByte(str, " Stack use after scope: ",
+ kAsanStackUseAfterScopeMagic);
+ PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic);
+ PrintShadowByte(str, " Global init order: ",
+ kAsanInitializationOrderMagic);
+ PrintShadowByte(str, " Poisoned by user: ",
+ kAsanUserPoisonedMemoryMagic);
+ PrintShadowByte(str, " Container overflow: ",
+ kAsanContiguousContainerOOBMagic);
+ PrintShadowByte(str, " Array cookie: ",
+ kAsanArrayCookieMagic);
+ PrintShadowByte(str, " Intra object redzone: ",
+ kAsanIntraObjectRedzone);
+ PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
+ PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic);
+ PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic);
+}
+
+static void PrintShadowBytes(InternalScopedString *str, const char *before,
+ u8 *bytes, u8 *guilty, uptr n) {
+ Decorator d;
+ if (before) str->append("%s%p:", before, bytes);
+ for (uptr i = 0; i < n; i++) {
+ u8 *p = bytes + i;
+ const char *before =
+ p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
+ const char *after = p == guilty ? "]" : "";
+ PrintShadowByte(str, before, *p, after);
+ }
+ str->append("\n");
+}
+
+static void PrintShadowMemoryForAddress(uptr addr) {
+ if (!AddrIsInMem(addr)) return;
+ uptr shadow_addr = MemToShadow(addr);
+ const uptr n_bytes_per_row = 16;
+ uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
+ InternalScopedString str(4096 * 8);
+ str.append("Shadow bytes around the buggy address:\n");
+ for (int i = -5; i <= 5; i++) {
+ const char *prefix = (i == 0) ? "=>" : " ";
+ PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row),
+ (u8 *)shadow_addr, n_bytes_per_row);
+ }
+ if (flags()->print_legend) PrintLegend(&str);
+ Printf("%s", str.data());
+}
+
+void ErrorGeneric::Print() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ uptr addr = addr_description.Address();
+ Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n",
+ bug_descr, (void *)addr, pc, bp, sp);
+ Printf("%s", d.EndWarning());
+
+ char tname[128];
+ Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(),
+ access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size,
+ (void *)addr, tid,
+ ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess());
+
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(pc, bp);
+ stack.Print();
+
+ // Pass bug_descr because we have a special case for
+ // initialization-order-fiasco
+ addr_description.Print(bug_descr);
+ if (shadow_val == kAsanContiguousContainerOOBMagic)
+ PrintContainerOverflowHint();
+ ReportErrorSummary(bug_descr, &stack);
+ PrintShadowMemoryForAddress(addr);
+}
+
+} // namespace __asan
diff --git a/libsanitizer/asan/asan_errors.h b/libsanitizer/asan/asan_errors.h
new file mode 100644
index 00000000000..6262dcf3506
--- /dev/null
+++ b/libsanitizer/asan/asan_errors.h
@@ -0,0 +1,376 @@
+//===-- asan_errors.h -------------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for error structures.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_ERRORS_H
+#define ASAN_ERRORS_H
+
+#include "asan_descriptions.h"
+#include "asan_scariness_score.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __asan {
+
+struct ErrorBase {
+ ErrorBase() = default;
+ explicit ErrorBase(u32 tid_) : tid(tid_) {}
+ ScarinessScoreBase scariness;
+ u32 tid;
+};
+
+struct ErrorStackOverflow : ErrorBase {
+ uptr addr, pc, bp, sp;
+ // ErrorStackOverflow never owns the context.
+ void *context;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorStackOverflow() = default;
+ ErrorStackOverflow(u32 tid, const SignalContext &sig)
+ : ErrorBase(tid),
+ addr(sig.addr),
+ pc(sig.pc),
+ bp(sig.bp),
+ sp(sig.sp),
+ context(sig.context) {
+ scariness.Clear();
+ scariness.Scare(10, "stack-overflow");
+ }
+ void Print();
+};
+
+struct ErrorDeadlySignal : ErrorBase {
+ uptr addr, pc, bp, sp;
+ // ErrorDeadlySignal never owns the context.
+ void *context;
+ int signo;
+ SignalContext::WriteFlag write_flag;
+ bool is_memory_access;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorDeadlySignal() = default;
+ ErrorDeadlySignal(u32 tid, const SignalContext &sig, int signo_)
+ : ErrorBase(tid),
+ addr(sig.addr),
+ pc(sig.pc),
+ bp(sig.bp),
+ sp(sig.sp),
+ context(sig.context),
+ signo(signo_),
+ write_flag(sig.write_flag),
+ is_memory_access(sig.is_memory_access) {
+ scariness.Clear();
+ if (is_memory_access) {
+ if (addr < GetPageSizeCached()) {
+ scariness.Scare(10, "null-deref");
+ } else if (addr == pc) {
+ scariness.Scare(60, "wild-jump");
+ } else if (write_flag == SignalContext::WRITE) {
+ scariness.Scare(30, "wild-addr-write");
+ } else if (write_flag == SignalContext::READ) {
+ scariness.Scare(20, "wild-addr-read");
+ } else {
+ scariness.Scare(25, "wild-addr");
+ }
+ } else {
+ scariness.Scare(10, "signal");
+ }
+ }
+ void Print();
+};
+
+struct ErrorDoubleFree : ErrorBase {
+ // ErrorDoubleFree doesn't own the stack trace.
+ const BufferedStackTrace *second_free_stack;
+ HeapAddressDescription addr_description;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorDoubleFree() = default;
+ ErrorDoubleFree(u32 tid, BufferedStackTrace *stack, uptr addr)
+ : ErrorBase(tid), second_free_stack(stack) {
+ CHECK_GT(second_free_stack->size, 0);
+ GetHeapAddressInformation(addr, 1, &addr_description);
+ scariness.Clear();
+ scariness.Scare(42, "double-free");
+ }
+ void Print();
+};
+
+struct ErrorNewDeleteSizeMismatch : ErrorBase {
+ // ErrorNewDeleteSizeMismatch doesn't own the stack trace.
+ const BufferedStackTrace *free_stack;
+ HeapAddressDescription addr_description;
+ uptr delete_size;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorNewDeleteSizeMismatch() = default;
+ ErrorNewDeleteSizeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
+ uptr delete_size_)
+ : ErrorBase(tid), free_stack(stack), delete_size(delete_size_) {
+ GetHeapAddressInformation(addr, 1, &addr_description);
+ scariness.Clear();
+ scariness.Scare(10, "new-delete-type-mismatch");
+ }
+ void Print();
+};
+
+struct ErrorFreeNotMalloced : ErrorBase {
+ // ErrorFreeNotMalloced doesn't own the stack trace.
+ const BufferedStackTrace *free_stack;
+ AddressDescription addr_description;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorFreeNotMalloced() = default;
+ ErrorFreeNotMalloced(u32 tid, BufferedStackTrace *stack, uptr addr)
+ : ErrorBase(tid),
+ free_stack(stack),
+ addr_description(addr, /*shouldLockThreadRegistry=*/false) {
+ scariness.Clear();
+ scariness.Scare(40, "bad-free");
+ }
+ void Print();
+};
+
+struct ErrorAllocTypeMismatch : ErrorBase {
+ // ErrorAllocTypeMismatch doesn't own the stack trace.
+ const BufferedStackTrace *dealloc_stack;
+ HeapAddressDescription addr_description;
+ AllocType alloc_type, dealloc_type;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorAllocTypeMismatch() = default;
+ ErrorAllocTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
+ AllocType alloc_type_, AllocType dealloc_type_)
+ : ErrorBase(tid),
+ dealloc_stack(stack),
+ alloc_type(alloc_type_),
+ dealloc_type(dealloc_type_) {
+ GetHeapAddressInformation(addr, 1, &addr_description);
+ scariness.Clear();
+ scariness.Scare(10, "alloc-dealloc-mismatch");
+ };
+ void Print();
+};
+
+struct ErrorMallocUsableSizeNotOwned : ErrorBase {
+ // ErrorMallocUsableSizeNotOwned doesn't own the stack trace.
+ const BufferedStackTrace *stack;
+ AddressDescription addr_description;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorMallocUsableSizeNotOwned() = default;
+ ErrorMallocUsableSizeNotOwned(u32 tid, BufferedStackTrace *stack_, uptr addr)
+ : ErrorBase(tid),
+ stack(stack_),
+ addr_description(addr, /*shouldLockThreadRegistry=*/false) {
+ scariness.Clear();
+ }
+ void Print();
+};
+
+struct ErrorSanitizerGetAllocatedSizeNotOwned : ErrorBase {
+ // ErrorSanitizerGetAllocatedSizeNotOwned doesn't own the stack trace.
+ const BufferedStackTrace *stack;
+ AddressDescription addr_description;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorSanitizerGetAllocatedSizeNotOwned() = default;
+ ErrorSanitizerGetAllocatedSizeNotOwned(u32 tid, BufferedStackTrace *stack_,
+ uptr addr)
+ : ErrorBase(tid),
+ stack(stack_),
+ addr_description(addr, /*shouldLockThreadRegistry=*/false) {
+ scariness.Clear();
+ }
+ void Print();
+};
+
+struct ErrorStringFunctionMemoryRangesOverlap : ErrorBase {
+ // ErrorStringFunctionMemoryRangesOverlap doesn't own the stack trace.
+ const BufferedStackTrace *stack;
+ uptr length1, length2;
+ AddressDescription addr1_description;
+ AddressDescription addr2_description;
+ const char *function;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorStringFunctionMemoryRangesOverlap() = default;
+ ErrorStringFunctionMemoryRangesOverlap(u32 tid, BufferedStackTrace *stack_,
+ uptr addr1, uptr length1_, uptr addr2,
+ uptr length2_, const char *function_)
+ : ErrorBase(tid),
+ stack(stack_),
+ length1(length1_),
+ length2(length2_),
+ addr1_description(addr1, length1, /*shouldLockThreadRegistry=*/false),
+ addr2_description(addr2, length2, /*shouldLockThreadRegistry=*/false),
+ function(function_) {
+ char bug_type[100];
+ internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
+ scariness.Clear();
+ scariness.Scare(10, bug_type);
+ }
+ void Print();
+};
+
+struct ErrorStringFunctionSizeOverflow : ErrorBase {
+ // ErrorStringFunctionSizeOverflow doesn't own the stack trace.
+ const BufferedStackTrace *stack;
+ AddressDescription addr_description;
+ uptr size;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorStringFunctionSizeOverflow() = default;
+ ErrorStringFunctionSizeOverflow(u32 tid, BufferedStackTrace *stack_,
+ uptr addr, uptr size_)
+ : ErrorBase(tid),
+ stack(stack_),
+ addr_description(addr, /*shouldLockThreadRegistry=*/false),
+ size(size_) {
+ scariness.Clear();
+ scariness.Scare(10, "negative-size-param");
+ }
+ void Print();
+};
+
+struct ErrorBadParamsToAnnotateContiguousContainer : ErrorBase {
+ // ErrorBadParamsToAnnotateContiguousContainer doesn't own the stack trace.
+ const BufferedStackTrace *stack;
+ uptr beg, end, old_mid, new_mid;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorBadParamsToAnnotateContiguousContainer() = default;
+ // PS4: Do we want an AddressDescription for beg?
+ ErrorBadParamsToAnnotateContiguousContainer(u32 tid,
+ BufferedStackTrace *stack_,
+ uptr beg_, uptr end_,
+ uptr old_mid_, uptr new_mid_)
+ : ErrorBase(tid),
+ stack(stack_),
+ beg(beg_),
+ end(end_),
+ old_mid(old_mid_),
+ new_mid(new_mid_) {}
+ void Print();
+};
+
+struct ErrorODRViolation : ErrorBase {
+ __asan_global global1, global2;
+ u32 stack_id1, stack_id2;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorODRViolation() = default;
+ ErrorODRViolation(u32 tid, const __asan_global *g1, u32 stack_id1_,
+ const __asan_global *g2, u32 stack_id2_)
+ : ErrorBase(tid),
+ global1(*g1),
+ global2(*g2),
+ stack_id1(stack_id1_),
+ stack_id2(stack_id2_) {}
+ void Print();
+};
+
+struct ErrorInvalidPointerPair : ErrorBase {
+ uptr pc, bp, sp;
+ AddressDescription addr1_description;
+ AddressDescription addr2_description;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorInvalidPointerPair() = default;
+ ErrorInvalidPointerPair(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr p1,
+ uptr p2)
+ : ErrorBase(tid),
+ pc(pc_),
+ bp(bp_),
+ sp(sp_),
+ addr1_description(p1, 1, /*shouldLockThreadRegistry=*/false),
+ addr2_description(p2, 1, /*shouldLockThreadRegistry=*/false) {}
+ void Print();
+};
+
+struct ErrorGeneric : ErrorBase {
+ AddressDescription addr_description;
+ uptr pc, bp, sp;
+ uptr access_size;
+ const char *bug_descr;
+ bool is_write;
+ u8 shadow_val;
+ // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+ // constructor
+ ErrorGeneric() = default;
+ ErrorGeneric(u32 tid, uptr addr, uptr pc_, uptr bp_, uptr sp_, bool is_write_,
+ uptr access_size_);
+ void Print();
+};
+
+// clang-format off
+#define ASAN_FOR_EACH_ERROR_KIND(macro) \
+ macro(StackOverflow) \
+ macro(DeadlySignal) \
+ macro(DoubleFree) \
+ macro(NewDeleteSizeMismatch) \
+ macro(FreeNotMalloced) \
+ macro(AllocTypeMismatch) \
+ macro(MallocUsableSizeNotOwned) \
+ macro(SanitizerGetAllocatedSizeNotOwned) \
+ macro(StringFunctionMemoryRangesOverlap) \
+ macro(StringFunctionSizeOverflow) \
+ macro(BadParamsToAnnotateContiguousContainer) \
+ macro(ODRViolation) \
+ macro(InvalidPointerPair) \
+ macro(Generic)
+// clang-format on
+
+#define ASAN_DEFINE_ERROR_KIND(name) kErrorKind##name,
+#define ASAN_ERROR_DESCRIPTION_MEMBER(name) Error##name name;
+#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \
+ ErrorDescription(Error##name const &e) : kind(kErrorKind##name), name(e) {}
+#define ASAN_ERROR_DESCRIPTION_PRINT(name) \
+ case kErrorKind##name: \
+ return name.Print();
+
+enum ErrorKind {
+ kErrorKindInvalid = 0,
+ ASAN_FOR_EACH_ERROR_KIND(ASAN_DEFINE_ERROR_KIND)
+};
+
+struct ErrorDescription {
+ ErrorKind kind;
+ // We're using a tagged union because it allows us to have a trivially
+ // copiable type and use the same structures as the public interface.
+ //
+ // We can add a wrapper around it to make it "more c++-like", but that would
+ // add a lot of code and the benefit wouldn't be that big.
+ union {
+ ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_MEMBER)
+ };
+
+ ErrorDescription() { internal_memset(this, 0, sizeof(*this)); }
+ ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_CONSTRUCTOR)
+
+ bool IsValid() { return kind != kErrorKindInvalid; }
+ void Print() {
+ switch (kind) {
+ ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_PRINT)
+ case kErrorKindInvalid:
+ CHECK(0);
+ }
+ CHECK(0);
+ }
+};
+
+#undef ASAN_FOR_EACH_ERROR_KIND
+#undef ASAN_DEFINE_ERROR_KIND
+#undef ASAN_ERROR_DESCRIPTION_MEMBER
+#undef ASAN_ERROR_DESCRIPTION_CONSTRUCTOR
+#undef ASAN_ERROR_DESCRIPTION_PRINT
+
+} // namespace __asan
+
+#endif // ASAN_ERRORS_H
diff --git a/libsanitizer/asan/asan_fake_stack.cc b/libsanitizer/asan/asan_fake_stack.cc
index de190d11a16..bf7566a334e 100644
--- a/libsanitizer/asan/asan_fake_stack.cc
+++ b/libsanitizer/asan/asan_fake_stack.cc
@@ -29,7 +29,7 @@ ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) {
CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3.
u64 *shadow = reinterpret_cast<u64*>(MemToShadow(ptr));
if (class_id <= 6) {
- for (uptr i = 0; i < (1U << class_id); i++) {
+ for (uptr i = 0; i < (((uptr)1) << class_id); i++) {
shadow[i] = magic;
// Make sure this does not become memset.
SanitizerBreakOptimization(nullptr);
@@ -98,7 +98,7 @@ FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id,
// if the signal arrives between checking and setting flags[pos], the
// signal handler's fake stack will start from a different hint_position
// and so will not touch this particular byte. So, it is safe to do this
- // with regular non-atimic load and store (at least I was not able to make
+ // with regular non-atomic load and store (at least I was not able to make
// this code crash).
if (flags[pos]) continue;
flags[pos] = 1;
@@ -119,7 +119,7 @@ uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) {
uptr class_id = (ptr - beg) >> stack_size_log;
uptr base = beg + (class_id << stack_size_log);
CHECK_LE(base, ptr);
- CHECK_LT(ptr, base + (1UL << stack_size_log));
+ CHECK_LT(ptr, base + (((uptr)1) << stack_size_log));
uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id);
uptr res = base + pos * BytesInSizeClass(class_id);
*frame_end = res + BytesInSizeClass(class_id);
diff --git a/libsanitizer/asan/asan_fake_stack.h b/libsanitizer/asan/asan_fake_stack.h
index 550a86e2972..6ac61ddd24e 100644
--- a/libsanitizer/asan/asan_fake_stack.h
+++ b/libsanitizer/asan/asan_fake_stack.h
@@ -50,7 +50,7 @@ struct FakeFrame {
// Allocate() flips the appropriate allocation flag atomically, thus achieving
// async-signal safety.
// This allocator does not have quarantine per se, but it tries to allocate the
-// frames in round robin fasion to maximize the delay between a deallocation
+// frames in round robin fashion to maximize the delay between a deallocation
// and the next allocation.
class FakeStack {
static const uptr kMinStackFrameSizeLog = 6; // Min frame is 64B.
@@ -67,12 +67,12 @@ class FakeStack {
// stack_size_log is at least 15 (stack_size >= 32K).
static uptr SizeRequiredForFlags(uptr stack_size_log) {
- return 1UL << (stack_size_log + 1 - kMinStackFrameSizeLog);
+ return ((uptr)1) << (stack_size_log + 1 - kMinStackFrameSizeLog);
}
// Each size class occupies stack_size bytes.
static uptr SizeRequiredForFrames(uptr stack_size_log) {
- return (1ULL << stack_size_log) * kNumberOfSizeClasses;
+ return (((uptr)1) << stack_size_log) * kNumberOfSizeClasses;
}
// Number of bytes requires for the whole object.
@@ -89,20 +89,20 @@ class FakeStack {
// and so on.
static uptr FlagsOffset(uptr stack_size_log, uptr class_id) {
uptr t = kNumberOfSizeClasses - 1 - class_id;
- const uptr all_ones = (1 << (kNumberOfSizeClasses - 1)) - 1;
+ const uptr all_ones = (((uptr)1) << (kNumberOfSizeClasses - 1)) - 1;
return ((all_ones >> t) << t) << (stack_size_log - 15);
}
static uptr NumberOfFrames(uptr stack_size_log, uptr class_id) {
- return 1UL << (stack_size_log - kMinStackFrameSizeLog - class_id);
+ return ((uptr)1) << (stack_size_log - kMinStackFrameSizeLog - class_id);
}
- // Divide n by the numbe of frames in size class.
+ // Divide n by the number of frames in size class.
static uptr ModuloNumberOfFrames(uptr stack_size_log, uptr class_id, uptr n) {
return n & (NumberOfFrames(stack_size_log, class_id) - 1);
}
- // The the pointer to the flags of the given class_id.
+ // The pointer to the flags of the given class_id.
u8 *GetFlags(uptr stack_size_log, uptr class_id) {
return reinterpret_cast<u8 *>(this) + kFlagsOffset +
FlagsOffset(stack_size_log, class_id);
@@ -112,7 +112,8 @@ class FakeStack {
u8 *GetFrame(uptr stack_size_log, uptr class_id, uptr pos) {
return reinterpret_cast<u8 *>(this) + kFlagsOffset +
SizeRequiredForFlags(stack_size_log) +
- (1 << stack_size_log) * class_id + BytesInSizeClass(class_id) * pos;
+ (((uptr)1) << stack_size_log) * class_id +
+ BytesInSizeClass(class_id) * pos;
}
// Allocate the fake frame.
@@ -135,7 +136,7 @@ class FakeStack {
// Number of bytes in a fake frame of this size class.
static uptr BytesInSizeClass(uptr class_id) {
- return 1UL << (class_id + kMinStackFrameSizeLog);
+ return ((uptr)1) << (class_id + kMinStackFrameSizeLog);
}
// The fake frame is guaranteed to have a right redzone.
@@ -157,7 +158,7 @@ class FakeStack {
static const uptr kFlagsOffset = 4096; // This is were the flags begin.
// Must match the number of uses of DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID
COMPILER_CHECK(kNumberOfSizeClasses == 11);
- static const uptr kMaxStackMallocSize = 1 << kMaxStackFrameSizeLog;
+ static const uptr kMaxStackMallocSize = ((uptr)1) << kMaxStackFrameSizeLog;
uptr hint_position_[kNumberOfSizeClasses];
uptr stack_size_log_;
diff --git a/libsanitizer/asan/asan_flags.cc b/libsanitizer/asan/asan_flags.cc
index 9a68ad4eaa8..c18174beed0 100644
--- a/libsanitizer/asan/asan_flags.cc
+++ b/libsanitizer/asan/asan_flags.cc
@@ -114,15 +114,7 @@ void InitializeFlags() {
ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
#endif
- // Let activation flags override current settings. On Android they come
- // from a system property. On other platforms this is no-op.
- if (!flags()->start_deactivated) {
- char buf[100];
- GetExtraActivationFlags(buf, sizeof(buf));
- asan_parser.ParseString(buf);
- }
-
- SetVerbosity(common_flags()->verbosity);
+ InitializeCommonFlags();
// TODO(eugenis): dump all flags at verbosity>=2?
if (Verbosity()) ReportUnrecognizedFlags();
@@ -165,6 +157,14 @@ void InitializeFlags() {
(ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8;
f->quarantine_size_mb = kDefaultQuarantineSizeMb;
}
+ if (!f->replace_str && common_flags()->intercept_strlen) {
+ Report("WARNING: strlen interceptor is enabled even though replace_str=0. "
+ "Use intercept_strlen=0 to disable it.");
+ }
+ if (!f->replace_str && common_flags()->intercept_strchr) {
+ Report("WARNING: strchr* interceptors are enabled even though "
+ "replace_str=0. Use intercept_strchr=0 to disable them.");
+ }
}
} // namespace __asan
diff --git a/libsanitizer/asan/asan_flags.inc b/libsanitizer/asan/asan_flags.inc
index 563b464bff1..34493eededf 100644
--- a/libsanitizer/asan/asan_flags.inc
+++ b/libsanitizer/asan/asan_flags.inc
@@ -41,10 +41,7 @@ ASAN_FLAG(
"If set, uses custom wrappers and replacements for libc string functions "
"to find more errors.")
ASAN_FLAG(bool, replace_intrin, true,
- "If set, uses custom wrappers for memset/memcpy/memmove intinsics.")
-ASAN_FLAG(bool, mac_ignore_invalid_free, false,
- "Ignore invalid free() calls to work around some bugs. Used on OS X "
- "only.")
+ "If set, uses custom wrappers for memset/memcpy/memmove intrinsics.")
ASAN_FLAG(bool, detect_stack_use_after_return, false,
"Enables stack-use-after-return checking at run-time.")
ASAN_FLAG(int, min_uar_stack_size_log, 16, // We can't do smaller anyway.
@@ -78,6 +75,8 @@ ASAN_FLAG(bool, print_stats, false,
"Print various statistics after printing an error message or if "
"atexit=1.")
ASAN_FLAG(bool, print_legend, true, "Print the legend for the shadow bytes.")
+ASAN_FLAG(bool, print_scariness, false,
+ "Print the scariness score. Experimental.")
ASAN_FLAG(bool, atexit, false,
"If set, prints ASan exit stats even after program terminates "
"successfully.")
@@ -97,15 +96,15 @@ ASAN_FLAG(bool, poison_array_cookie, true,
"Poison (or not) the array cookie after operator new[].")
// Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
-// https://code.google.com/p/address-sanitizer/issues/detail?id=131
-// https://code.google.com/p/address-sanitizer/issues/detail?id=309
+// https://github.com/google/sanitizers/issues/131
+// https://github.com/google/sanitizers/issues/309
// TODO(glider,timurrrr): Fix known issues and enable this back.
ASAN_FLAG(bool, alloc_dealloc_mismatch,
- (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0),
+ !SANITIZER_MAC && !SANITIZER_WINDOWS && !SANITIZER_ANDROID,
"Report errors on malloc/delete, new/free, new/delete[], etc.")
ASAN_FLAG(bool, new_delete_type_mismatch, true,
- "Report errors on mismatch betwen size of new and delete.")
+ "Report errors on mismatch between size of new and delete.")
ASAN_FLAG(
bool, strict_init_order, false,
"If true, assume that dynamic initializers can never access globals from "
@@ -124,8 +123,8 @@ ASAN_FLAG(
"The bigger the value the harder we try.")
ASAN_FLAG(
bool, detect_container_overflow, true,
- "If true, honor the container overflow annotations. "
- "See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow")
+ "If true, honor the container overflow annotations. See "
+ "https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow")
ASAN_FLAG(int, detect_odr_violation, 2,
"If >=2, detect violation of One-Definition-Rule (ODR); "
"If ==1, detect ODR-violation only if the two variables "
@@ -136,3 +135,5 @@ ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
ASAN_FLAG(bool, halt_on_error, true,
"Crash the program after printing the first error report "
"(WARNING: USE AT YOUR OWN RISK!)")
+ASAN_FLAG(bool, use_odr_indicator, false,
+ "Use special ODR indicator symbol for ODR violation detection")
diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc
index f3531cbd133..f2292926e6a 100644
--- a/libsanitizer/asan/asan_globals.cc
+++ b/libsanitizer/asan/asan_globals.cc
@@ -23,6 +23,7 @@
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
namespace __asan {
@@ -121,16 +122,51 @@ int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
return res;
}
-bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
- Global g = {};
- if (GetGlobalsForAddress(addr, &g, nullptr, 1)) {
- internal_strncpy(descr->name, g.name, descr->name_size);
- descr->region_address = g.beg;
- descr->region_size = g.size;
- descr->region_kind = "global";
- return true;
+enum GlobalSymbolState {
+ UNREGISTERED = 0,
+ REGISTERED = 1
+};
+
+// Check ODR violation for given global G via special ODR indicator. We use
+// this method in case compiler instruments global variables through their
+// local aliases.
+static void CheckODRViolationViaIndicator(const Global *g) {
+ u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
+ if (*odr_indicator == UNREGISTERED) {
+ *odr_indicator = REGISTERED;
+ return;
+ }
+ // If *odr_indicator is DEFINED, some module have already registered
+ // externally visible symbol with the same name. This is an ODR violation.
+ for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
+ if (g->odr_indicator == l->g->odr_indicator &&
+ (flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
+ !IsODRViolationSuppressed(g->name))
+ ReportODRViolation(g, FindRegistrationSite(g),
+ l->g, FindRegistrationSite(l->g));
}
- return false;
+}
+
+// Clang provides two different ways for global variables protection:
+// it can poison the global itself or its private alias. In former
+// case we may poison same symbol multiple times, that can help us to
+// cheaply detect ODR violation: if we try to poison an already poisoned
+// global, we have ODR violation error.
+// In latter case, we poison each symbol exactly once, so we use special
+// indicator symbol to perform similar check.
+// In either case, compiler provides a special odr_indicator field to Global
+// structure, that can contain two kinds of values:
+// 1) Non-zero value. In this case, odr_indicator is an address of
+// corresponding indicator variable for given global.
+// 2) Zero. This means that we don't use private aliases for global variables
+// and can freely check ODR violation with the first method.
+//
+// This routine chooses between two different methods of ODR violation
+// detection.
+static inline bool UseODRIndicator(const Global *g) {
+ // Use ODR indicator method iff use_odr_indicator flag is set and
+ // indicator symbol address is not 0.
+ return flags()->use_odr_indicator && g->odr_indicator > 0;
}
// Register a global variable.
@@ -142,24 +178,22 @@ static void RegisterGlobal(const Global *g) {
ReportGlobal(*g, "Added");
CHECK(flags()->report_globals);
CHECK(AddrIsInMem(g->beg));
- CHECK(AddrIsAlignedByGranularity(g->beg));
+ if (!AddrIsAlignedByGranularity(g->beg)) {
+ Report("The following global variable is not properly aligned.\n");
+ Report("This may happen if another global with the same name\n");
+ Report("resides in another non-instrumented module.\n");
+ Report("Or the global comes from a C file built w/o -fno-common.\n");
+ Report("In either case this is likely an ODR violation bug,\n");
+ Report("but AddressSanitizer can not provide more details.\n");
+ ReportODRViolation(g, FindRegistrationSite(g), g, FindRegistrationSite(g));
+ CHECK(AddrIsAlignedByGranularity(g->beg));
+ }
CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
- // This "ODR violation" detection is fundamentally incompatible with
- // how GCC registers globals. Disable as useless until rewritten upstream.
- if (0 && flags()->detect_odr_violation) {
+ if (flags()->detect_odr_violation) {
// Try detecting ODR (One Definition Rule) violation, i.e. the situation
// where two globals with the same name are defined in different modules.
- if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) {
- // This check may not be enough: if the first global is much larger
- // the entire redzone of the second global may be within the first global.
- for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
- if (g->beg == l->g->beg &&
- (flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
- !IsODRViolationSuppressed(g->name))
- ReportODRViolation(g, FindRegistrationSite(g),
- l->g, FindRegistrationSite(l->g));
- }
- }
+ if (UseODRIndicator(g))
+ CheckODRViolationViaIndicator(g);
}
if (CanPoisonMemory())
PoisonRedZones(*g);
@@ -190,6 +224,12 @@ static void UnregisterGlobal(const Global *g) {
// We unpoison the shadow memory for the global but we do not remove it from
// the list because that would require O(n^2) time with the current list
// implementation. It might not be worth doing anyway.
+
+ // Release ODR indicator.
+ if (UseODRIndicator(g)) {
+ u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
+ *odr_indicator = UNREGISTERED;
+ }
}
void StopInitOrderChecking() {
@@ -207,11 +247,70 @@ void StopInitOrderChecking() {
}
}
+static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; }
+
+const char *MaybeDemangleGlobalName(const char *name) {
+ // We can spoil names of globals with C linkage, so use an heuristic
+ // approach to check if the name should be demangled.
+ bool should_demangle = false;
+ if (name[0] == '_' && name[1] == 'Z')
+ should_demangle = true;
+ else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
+ should_demangle = true;
+
+ return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
+}
+
+// Check if the global is a zero-terminated ASCII string. If so, print it.
+void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) {
+ for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
+ unsigned char c = *(unsigned char *)p;
+ if (c == '\0' || !IsASCII(c)) return;
+ }
+ if (*(char *)(g.beg + g.size - 1) != '\0') return;
+ str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
+ (char *)g.beg);
+}
+
+static const char *GlobalFilename(const __asan_global &g) {
+ const char *res = g.module_name;
+ // Prefer the filename from source location, if is available.
+ if (g.location) res = g.location->filename;
+ CHECK(res);
+ return res;
+}
+
+void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) {
+ str->append("%s", GlobalFilename(g));
+ if (!g.location) return;
+ if (g.location->line_no) str->append(":%d", g.location->line_no);
+ if (g.location->column_no) str->append(":%d", g.location->column_no);
+}
+
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
+
+// Apply __asan_register_globals to all globals found in the same loaded
+// executable or shared library as `flag'. The flag tracks whether globals have
+// already been registered or not for this image.
+void __asan_register_image_globals(uptr *flag) {
+ if (*flag)
+ return;
+ AsanApplyToGlobals(__asan_register_globals, flag);
+ *flag = 1;
+}
+
+// This mirrors __asan_register_image_globals.
+void __asan_unregister_image_globals(uptr *flag) {
+ if (!*flag)
+ return;
+ AsanApplyToGlobals(__asan_unregister_globals, flag);
+ *flag = 0;
+}
+
// Register an array of globals.
void __asan_register_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
diff --git a/libsanitizer/asan/asan_init_version.h b/libsanitizer/asan/asan_init_version.h
index 2cda18849dd..51e8324fc13 100644
--- a/libsanitizer/asan/asan_init_version.h
+++ b/libsanitizer/asan/asan_init_version.h
@@ -17,16 +17,20 @@ extern "C" {
// Every time the ASan ABI changes we also change the version number in the
// __asan_init function name. Objects built with incompatible ASan ABI
// versions will not link with run-time.
+ //
// Changes between ABI versions:
// v1=>v2: added 'module_name' to __asan_global
// v2=>v3: stack frame description (created by the compiler)
- // contains the function PC as the 3-rd field (see
- // DescribeAddressIfStack).
- // v3=>v4: added '__asan_global_source_location' to __asan_global.
+ // contains the function PC as the 3rd field (see
+ // DescribeAddressIfStack)
+ // v3=>v4: added '__asan_global_source_location' to __asan_global
// v4=>v5: changed the semantics and format of __asan_stack_malloc_ and
- // __asan_stack_free_ functions.
+ // __asan_stack_free_ functions
// v5=>v6: changed the name of the version check symbol
- #define __asan_version_mismatch_check __asan_version_mismatch_check_v6
+ // v6=>v7: added 'odr_indicator' to __asan_global
+ // v7=>v8: added '__asan_(un)register_image_globals' functions for dead
+ // stripping support on Mach-O platforms
+ #define __asan_version_mismatch_check __asan_version_mismatch_check_v8
}
#endif // ASAN_INIT_VERSION_H
diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc
index 356f2c02897..743abe51481 100644
--- a/libsanitizer/asan/asan_interceptors.cc
+++ b/libsanitizer/asan/asan_interceptors.cc
@@ -19,6 +19,7 @@
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_suppressions.h"
+#include "lsan/lsan_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#if SANITIZER_POSIX
@@ -108,7 +109,7 @@ static inline bool RangesOverlap(const char *offset1, uptr length1,
} while (0)
static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
-#if ASAN_INTERCEPT_STRNLEN
+#if SANITIZER_INTERCEPT_STRNLEN
if (REAL(strnlen)) {
return REAL(strnlen)(s, maxlen);
}
@@ -141,6 +142,8 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
(void) ctx; \
#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name)
+#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
+ ASAN_INTERCEPT_FUNC_VER(name, ver)
#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
ASAN_WRITE_RANGE(ctx, ptr, size)
#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
@@ -176,7 +179,7 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
} while (false)
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
// Strict init-order checking is dlopen-hostile:
-// https://code.google.com/p/address-sanitizer/issues/detail?id=178
+// https://github.com/google/sanitizers/issues/178
#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \
if (flags()->strict_init_order) { \
StopInitOrderChecking(); \
@@ -193,6 +196,10 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
} else { \
*begin = *end = 0; \
}
+// Asan needs custom handling of these:
+#undef SANITIZER_INTERCEPT_MEMSET
+#undef SANITIZER_INTERCEPT_MEMMOVE
+#undef SANITIZER_INTERCEPT_MEMCPY
#include "sanitizer_common/sanitizer_common_interceptors.inc"
// Syscall interceptors don't have contexts, we don't support suppressions
@@ -216,6 +223,7 @@ struct ThreadStartParam {
atomic_uintptr_t is_registered;
};
+#if ASAN_INTERCEPT_PTHREAD_CREATE
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
ThreadStartParam *param = reinterpret_cast<ThreadStartParam *>(arg);
AsanThread *t = nullptr;
@@ -226,7 +234,6 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
return t->ThreadStart(GetTid(), &param->is_registered);
}
-#if ASAN_INTERCEPT_PTHREAD_CREATE
INTERCEPTOR(int, pthread_create, void *thread,
void *attr, void *(*start_routine)(void*), void *arg) {
EnsureMainThreadIDIsCorrect();
@@ -240,7 +247,17 @@ INTERCEPTOR(int, pthread_create, void *thread,
ThreadStartParam param;
atomic_store(&param.t, 0, memory_order_relaxed);
atomic_store(&param.is_registered, 0, memory_order_relaxed);
- int result = REAL(pthread_create)(thread, attr, asan_thread_start, &param);
+ int result;
+ {
+ // Ignore all allocations made by pthread_create: thread stack/TLS may be
+ // stored by pthread for future reuse even after thread destruction, and
+ // the linked list it's stored in doesn't even hold valid pointers to the
+ // objects, the latter are calculated by obscure pointer arithmetic.
+#if CAN_SANITIZE_LEAKS
+ __lsan::ScopedInterceptorDisabler disabler;
+#endif
+ result = REAL(pthread_create)(thread, attr, asan_thread_start, &param);
+ }
if (result == 0) {
u32 current_tid = GetCurrentTidOrInvalid();
AsanThread *t =
@@ -269,7 +286,8 @@ DEFINE_REAL_PTHREAD_FUNCTIONS
#if SANITIZER_ANDROID
INTERCEPTOR(void*, bsd_signal, int signum, void *handler) {
- if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) {
+ if (!IsHandledDeadlySignal(signum) ||
+ common_flags()->allow_user_segv_handler) {
return REAL(bsd_signal)(signum, handler);
}
return 0;
@@ -277,7 +295,8 @@ INTERCEPTOR(void*, bsd_signal, int signum, void *handler) {
#endif
INTERCEPTOR(void*, signal, int signum, void *handler) {
- if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) {
+ if (!IsHandledDeadlySignal(signum) ||
+ common_flags()->allow_user_segv_handler) {
return REAL(signal)(signum, handler);
}
return nullptr;
@@ -285,7 +304,8 @@ INTERCEPTOR(void*, signal, int signum, void *handler) {
INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact) {
- if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) {
+ if (!IsHandledDeadlySignal(signum) ||
+ common_flags()->allow_user_segv_handler) {
return REAL(sigaction)(signum, act, oldact);
}
return 0;
@@ -451,25 +471,6 @@ INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
ASAN_MEMSET_IMPL(ctx, block, c, size);
}
-INTERCEPTOR(char*, strchr, const char *str, int c) {
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strchr);
- if (UNLIKELY(!asan_inited)) return internal_strchr(str, c);
- // strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is
- // used.
- if (asan_init_is_running) {
- return REAL(strchr)(str, c);
- }
- ENSURE_ASAN_INITED();
- char *result = REAL(strchr)(str, c);
- if (flags()->replace_str) {
- uptr len = REAL(strlen)(str);
- uptr bytes_read = (result ? result - str : len) + 1;
- ASAN_READ_STRING_OF_LEN(ctx, str, len, bytes_read);
- }
- return result;
-}
-
#if ASAN_INTERCEPT_INDEX
# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
INTERCEPTOR(char*, index, const char *string, int c)
@@ -547,7 +548,6 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
return REAL(strcpy)(to, from); // NOLINT
}
-#if ASAN_INTERCEPT_STRDUP
INTERCEPTOR(char*, strdup, const char *s) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, strdup);
@@ -562,29 +562,28 @@ INTERCEPTOR(char*, strdup, const char *s) {
REAL(memcpy)(new_mem, s, length + 1);
return reinterpret_cast<char*>(new_mem);
}
-#endif
-INTERCEPTOR(SIZE_T, strlen, const char *s) {
+#if ASAN_INTERCEPT___STRDUP
+INTERCEPTOR(char*, __strdup, const char *s) {
void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strlen);
- if (UNLIKELY(!asan_inited)) return internal_strlen(s);
- // strlen is called from malloc_default_purgeable_zone()
- // in __asan::ReplaceSystemAlloc() on Mac.
- if (asan_init_is_running) {
- return REAL(strlen)(s);
- }
+ ASAN_INTERCEPTOR_ENTER(ctx, strdup);
+ if (UNLIKELY(!asan_inited)) return internal_strdup(s);
ENSURE_ASAN_INITED();
- SIZE_T length = REAL(strlen)(s);
+ uptr length = REAL(strlen)(s);
if (flags()->replace_str) {
ASAN_READ_RANGE(ctx, s, length + 1);
}
- return length;
+ GET_STACK_TRACE_MALLOC;
+ void *new_mem = asan_malloc(length + 1, &stack);
+ REAL(memcpy)(new_mem, s, length + 1);
+ return reinterpret_cast<char*>(new_mem);
}
+#endif // ASAN_INTERCEPT___STRDUP
INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) {
void *ctx;
ASAN_INTERCEPTOR_ENTER(ctx, wcslen);
- SIZE_T length = REAL(wcslen)(s);
+ SIZE_T length = internal_wcslen(s);
if (!asan_init_is_running) {
ENSURE_ASAN_INITED();
ASAN_READ_RANGE(ctx, s, (length + 1) * sizeof(wchar_t));
@@ -605,19 +604,6 @@ INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) {
return REAL(strncpy)(to, from, size);
}
-#if ASAN_INTERCEPT_STRNLEN
-INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) {
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strnlen);
- ENSURE_ASAN_INITED();
- uptr length = REAL(strnlen)(s, maxlen);
- if (flags()->replace_str) {
- ASAN_READ_RANGE(ctx, s, Min(length + 1, maxlen));
- }
- return length;
-}
-#endif // ASAN_INTERCEPT_STRNLEN
-
INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
char **endptr, int base) {
void *ctx;
@@ -700,12 +686,12 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT
}
#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL
+#if ASAN_INTERCEPT___CXA_ATEXIT
static void AtCxaAtexit(void *unused) {
(void)unused;
StopInitOrderChecking();
}
-#if ASAN_INTERCEPT___CXA_ATEXIT
INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
void *dso_handle) {
#if SANITIZER_MAC
@@ -732,7 +718,7 @@ INTERCEPTOR(int, fork, void) {
namespace __asan {
void InitializeAsanInterceptors() {
static bool was_called_once;
- CHECK(was_called_once == false);
+ CHECK(!was_called_once);
was_called_once = true;
InitializeCommonInterceptors();
@@ -740,22 +726,22 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(memmove);
ASAN_INTERCEPT_FUNC(memset);
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
+ // In asan, REAL(memmove) is not used, but it is used in msan.
ASAN_INTERCEPT_FUNC(memcpy);
+ } else {
+ ASSIGN_REAL(memcpy, memmove);
}
+ CHECK(REAL(memcpy));
// Intercept str* functions.
ASAN_INTERCEPT_FUNC(strcat); // NOLINT
- ASAN_INTERCEPT_FUNC(strchr);
ASAN_INTERCEPT_FUNC(strcpy); // NOLINT
- ASAN_INTERCEPT_FUNC(strlen);
ASAN_INTERCEPT_FUNC(wcslen);
ASAN_INTERCEPT_FUNC(strncat);
ASAN_INTERCEPT_FUNC(strncpy);
-#if ASAN_INTERCEPT_STRDUP
ASAN_INTERCEPT_FUNC(strdup);
-#endif
-#if ASAN_INTERCEPT_STRNLEN
- ASAN_INTERCEPT_FUNC(strnlen);
+#if ASAN_INTERCEPT___STRDUP
+ ASAN_INTERCEPT_FUNC(__strdup);
#endif
#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
ASAN_INTERCEPT_FUNC(index);
diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h
index 46c74176ed3..7053bb7faf5 100644
--- a/libsanitizer/asan/asan_interceptors.h
+++ b/libsanitizer/asan/asan_interceptors.h
@@ -21,14 +21,12 @@
#if !SANITIZER_WINDOWS
# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1
# define ASAN_INTERCEPT__LONGJMP 1
-# define ASAN_INTERCEPT_STRDUP 1
# define ASAN_INTERCEPT_INDEX 1
# define ASAN_INTERCEPT_PTHREAD_CREATE 1
# define ASAN_INTERCEPT_FORK 1
#else
# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
# define ASAN_INTERCEPT__LONGJMP 0
-# define ASAN_INTERCEPT_STRDUP 0
# define ASAN_INTERCEPT_INDEX 0
# define ASAN_INTERCEPT_PTHREAD_CREATE 0
# define ASAN_INTERCEPT_FORK 0
@@ -40,12 +38,6 @@
# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0
#endif
-#if !SANITIZER_MAC
-# define ASAN_INTERCEPT_STRNLEN 1
-#else
-# define ASAN_INTERCEPT_STRNLEN 0
-#endif
-
#if SANITIZER_LINUX && !SANITIZER_ANDROID
# define ASAN_INTERCEPT_SWAPCONTEXT 1
#else
@@ -78,6 +70,12 @@
# define ASAN_INTERCEPT___CXA_ATEXIT 0
#endif
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+# define ASAN_INTERCEPT___STRDUP 1
+#else
+# define ASAN_INTERCEPT___STRDUP 0
+#endif
+
DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size)
DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size)
DECLARE_REAL(void*, memset, void *block, int c, uptr size)
diff --git a/libsanitizer/asan/asan_interface_internal.h b/libsanitizer/asan/asan_interface_internal.h
index 079da9ca522..05605a8065f 100644
--- a/libsanitizer/asan/asan_interface_internal.h
+++ b/libsanitizer/asan/asan_interface_internal.h
@@ -21,6 +21,8 @@
#include "asan_init_version.h"
using __sanitizer::uptr;
+using __sanitizer::u64;
+using __sanitizer::u32;
extern "C" {
// This function should be called at the very beginning of the process,
@@ -52,8 +54,17 @@ extern "C" {
uptr has_dynamic_init; // Non-zero if the global has dynamic initializer.
__asan_global_source_location *location; // Source location of a global,
// or NULL if it is unknown.
+ uptr odr_indicator; // The address of the ODR indicator symbol.
};
+ // These functions can be called on some platforms to find globals in the same
+ // loaded image as `flag' and apply __asan_(un)register_globals to them,
+ // filtering out redundant calls.
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_register_image_globals(uptr *flag);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_unregister_image_globals(uptr *flag);
+
// These two functions should be called by the instrumented code.
// 'globals' is an array of structures describing 'n' globals.
SANITIZER_INTERFACE_ATTRIBUTE
@@ -68,6 +79,20 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_after_dynamic_init();
+ // Sets bytes of the given range of the shadow memory into specific value.
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_set_shadow_00(uptr addr, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_set_shadow_f1(uptr addr, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_set_shadow_f2(uptr addr, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_set_shadow_f3(uptr addr, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_set_shadow_f5(uptr addr, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_set_shadow_f8(uptr addr, uptr size);
+
// These two functions are used by instrumented code in the
// use-after-scope mode. They mark memory for local variables as
// unaddressable when they leave scope and addressable before the
@@ -145,6 +170,9 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
/* OPTIONAL */ const char* __asan_default_options();
+ SANITIZER_INTERFACE_ATTRIBUTE
+ extern uptr __asan_shadow_memory_dynamic_address;
+
// Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return
SANITIZER_INTERFACE_ATTRIBUTE
extern int __asan_option_detect_stack_use_after_return;
diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h
index e31f2648b1a..15a28ff777b 100644
--- a/libsanitizer/asan/asan_internal.h
+++ b/libsanitizer/asan/asan_internal.h
@@ -34,9 +34,9 @@
// If set, values like allocator chunk size, as well as defaults for some flags
// will be changed towards less memory overhead.
#ifndef ASAN_LOW_MEMORY
-#if SANITIZER_WORDSIZE == 32
+# if SANITIZER_IOS || (SANITIZER_WORDSIZE == 32)
# define ASAN_LOW_MEMORY 1
-#else
+# else
# define ASAN_LOW_MEMORY 0
# endif
#endif
@@ -60,6 +60,12 @@ using __sanitizer::StackTrace;
void AsanInitFromRtl();
+// asan_win.cc
+void InitializePlatformExceptionHandlers();
+
+// asan_win.cc / asan_posix.cc
+const char *DescribeSignalOrException(int signo);
+
// asan_rtl.cc
void NORETURN ShowStatsAndAbort();
@@ -71,10 +77,15 @@ void *AsanDoesNotSupportStaticLinkage();
void AsanCheckDynamicRTPrereqs();
void AsanCheckIncompatibleRT();
+// Support function for __asan_(un)register_image_globals. Searches for the
+// loaded image containing `needle' and then enumerates all global metadata
+// structures declared in that image, applying `op' (e.g.,
+// __asan_(un)register_globals) to them.
+typedef void (*globals_op_fptr)(__asan_global *, uptr);
+void AsanApplyToGlobals(globals_op_fptr op, const void *needle);
+
void AsanOnDeadlySignal(int, void *siginfo, void *context);
-void DisableReexec();
-void MaybeReexec();
void ReadContextStack(void *context, uptr *stack, uptr *ssize);
void StopInitOrderChecking();
@@ -95,16 +106,24 @@ void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
bool PlatformHasDifferentMemcpyAndMemmove();
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \
(PlatformHasDifferentMemcpyAndMemmove())
+#elif SANITIZER_WINDOWS64
+# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE false
#else
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
#endif // SANITIZER_MAC
// Add convenient macro for interface functions that may be represented as
// weak hooks.
-#define ASAN_MALLOC_HOOK(ptr, size) \
- if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size)
-#define ASAN_FREE_HOOK(ptr) \
- if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr)
+#define ASAN_MALLOC_HOOK(ptr, size) \
+ do { \
+ if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size); \
+ RunMallocHooks(ptr, size); \
+ } while (false)
+#define ASAN_FREE_HOOK(ptr) \
+ do { \
+ if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr); \
+ RunFreeHooks(ptr); \
+ } while (false)
#define ASAN_ON_ERROR() \
if (&__asan_on_error) __asan_on_error()
@@ -112,15 +131,12 @@ extern int asan_inited;
// Used to avoid infinite recursion in __asan_init().
extern bool asan_init_is_running;
extern void (*death_callback)(void);
-
// These magic values are written to shadow for better error reporting.
const int kAsanHeapLeftRedzoneMagic = 0xfa;
-const int kAsanHeapRightRedzoneMagic = 0xfb;
const int kAsanHeapFreeMagic = 0xfd;
const int kAsanStackLeftRedzoneMagic = 0xf1;
const int kAsanStackMidRedzoneMagic = 0xf2;
const int kAsanStackRightRedzoneMagic = 0xf3;
-const int kAsanStackPartialRedzoneMagic = 0xf4;
const int kAsanStackAfterReturnMagic = 0xf5;
const int kAsanInitializationOrderMagic = 0xf6;
const int kAsanUserPoisonedMemoryMagic = 0xf7;
diff --git a/libsanitizer/asan/asan_linux.cc b/libsanitizer/asan/asan_linux.cc
index 4e47d5a0496..9f058df71a5 100644
--- a/libsanitizer/asan/asan_linux.cc
+++ b/libsanitizer/asan/asan_linux.cc
@@ -67,20 +67,17 @@ asan_rt_version_t __asan_rt_version;
namespace __asan {
void InitializePlatformInterceptors() {}
-
-void DisableReexec() {
- // No need to re-exec on Linux.
-}
-
-void MaybeReexec() {
- // No need to re-exec on Linux.
-}
+void InitializePlatformExceptionHandlers() {}
void *AsanDoesNotSupportStaticLinkage() {
// This will fail to link with -static.
return &_DYNAMIC; // defined in link.h
}
+void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
+ UNIMPLEMENTED();
+}
+
#if SANITIZER_ANDROID
// FIXME: should we do anything for Android?
void AsanCheckDynamicRTPrereqs() {}
diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc
index ab3c6560825..4bf79be1728 100644
--- a/libsanitizer/asan/asan_mac.cc
+++ b/libsanitizer/asan/asan_mac.cc
@@ -22,18 +22,11 @@
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mac.h"
-#if !SANITIZER_IOS
-#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
-#else
-extern "C" {
- extern char ***_NSGetArgv(void);
-}
-#endif
-
-#include <dlfcn.h> // for dladdr()
+#include <dlfcn.h>
#include <fcntl.h>
#include <libkern/OSAtomic.h>
#include <mach-o/dyld.h>
+#include <mach-o/getsect.h>
#include <mach-o/loader.h>
#include <pthread.h>
#include <stdlib.h> // for free()
@@ -43,193 +36,26 @@ extern "C" {
#include <sys/ucontext.h>
#include <unistd.h>
+// from <crt_externs.h>, but we don't have that file on iOS
+extern "C" {
+ extern char ***_NSGetArgv(void);
+ extern char ***_NSGetEnviron(void);
+}
+
namespace __asan {
void InitializePlatformInterceptors() {}
+void InitializePlatformExceptionHandlers() {}
bool PlatformHasDifferentMemcpyAndMemmove() {
// On OS X 10.7 memcpy() and memmove() are both resolved
// into memmove$VARIANT$sse42.
- // See also http://code.google.com/p/address-sanitizer/issues/detail?id=34.
+ // See also https://github.com/google/sanitizers/issues/34.
// TODO(glider): need to check dynamically that memcpy() and memmove() are
// actually the same function.
return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
}
-extern "C"
-void __asan_init();
-
-static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
-LowLevelAllocator allocator_for_env;
-
-// Change the value of the env var |name|, leaking the original value.
-// If |name_value| is NULL, the variable is deleted from the environment,
-// otherwise the corresponding "NAME=value" string is replaced with
-// |name_value|.
-void LeakyResetEnv(const char *name, const char *name_value) {
- char **env = GetEnviron();
- uptr name_len = internal_strlen(name);
- while (*env != 0) {
- uptr len = internal_strlen(*env);
- if (len > name_len) {
- const char *p = *env;
- if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
- // Match.
- if (name_value) {
- // Replace the old value with the new one.
- *env = const_cast<char*>(name_value);
- } else {
- // Shift the subsequent pointers back.
- char **del = env;
- do {
- del[0] = del[1];
- } while (*del++);
- }
- }
- }
- env++;
- }
-}
-
-static bool reexec_disabled = false;
-
-void DisableReexec() {
- reexec_disabled = true;
-}
-
-extern "C" SANITIZER_WEAK_ATTRIBUTE double dyldVersionNumber;
-static const double kMinDyldVersionWithAutoInterposition = 360.0;
-
-bool DyldNeedsEnvVariable() {
- // Although sanitizer support was added to LLVM on OS X 10.7+, GCC users
- // still may want use them on older systems. On older Darwin platforms, dyld
- // doesn't export dyldVersionNumber symbol and we simply return true.
- if (!&dyldVersionNumber) return true;
- // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
- // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via
- // GetMacosVersion() doesn't work for the simulator. Let's instead check
- // `dyldVersionNumber`, which is exported by dyld, against a known version
- // number from the first OS release where this appeared.
- return dyldVersionNumber < kMinDyldVersionWithAutoInterposition;
-}
-
-void MaybeReexec() {
- if (reexec_disabled) return;
-
- // Make sure the dynamic ASan runtime library is preloaded so that the
- // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
- // ourselves.
- Dl_info info;
- CHECK(dladdr((void*)((uptr)__asan_init), &info));
- char *dyld_insert_libraries =
- const_cast<char*>(GetEnv(kDyldInsertLibraries));
- uptr old_env_len = dyld_insert_libraries ?
- internal_strlen(dyld_insert_libraries) : 0;
- uptr fname_len = internal_strlen(info.dli_fname);
- const char *dylib_name = StripModuleName(info.dli_fname);
- uptr dylib_name_len = internal_strlen(dylib_name);
-
- bool lib_is_in_env =
- dyld_insert_libraries && REAL(strstr)(dyld_insert_libraries, dylib_name);
- if (DyldNeedsEnvVariable() && !lib_is_in_env) {
- // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
- // library.
- char program_name[1024];
- uint32_t buf_size = sizeof(program_name);
- _NSGetExecutablePath(program_name, &buf_size);
- char *new_env = const_cast<char*>(info.dli_fname);
- if (dyld_insert_libraries) {
- // Append the runtime dylib name to the existing value of
- // DYLD_INSERT_LIBRARIES.
- new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
- internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
- new_env[old_env_len] = ':';
- // Copy fname_len and add a trailing zero.
- internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
- fname_len + 1);
- // Ok to use setenv() since the wrappers don't depend on the value of
- // asan_inited.
- setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
- } else {
- // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
- setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
- }
- VReport(1, "exec()-ing the program with\n");
- VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env);
- VReport(1, "to enable ASan wrappers.\n");
- execv(program_name, *_NSGetArgv());
-
- // We get here only if execv() failed.
- Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, "
- "which is required for ASan to work. ASan tried to set the "
- "environment variable and re-execute itself, but execv() failed, "
- "possibly because of sandbox restrictions. Make sure to launch the "
- "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env);
- CHECK("execv failed" && 0);
- }
-
- if (!lib_is_in_env)
- return;
-
- // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
- // the dylib from the environment variable, because interceptors are installed
- // and we don't want our children to inherit the variable.
-
- uptr env_name_len = internal_strlen(kDyldInsertLibraries);
- // Allocate memory to hold the previous env var name, its value, the '='
- // sign and the '\0' char.
- char *new_env = (char*)allocator_for_env.Allocate(
- old_env_len + 2 + env_name_len);
- CHECK(new_env);
- internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
- internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
- new_env[env_name_len] = '=';
- char *new_env_pos = new_env + env_name_len + 1;
-
- // Iterate over colon-separated pieces of |dyld_insert_libraries|.
- char *piece_start = dyld_insert_libraries;
- char *piece_end = NULL;
- char *old_env_end = dyld_insert_libraries + old_env_len;
- do {
- if (piece_start[0] == ':') piece_start++;
- piece_end = REAL(strchr)(piece_start, ':');
- if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
- if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
- uptr piece_len = piece_end - piece_start;
-
- char *filename_start =
- (char *)internal_memrchr(piece_start, '/', piece_len);
- uptr filename_len = piece_len;
- if (filename_start) {
- filename_start += 1;
- filename_len = piece_len - (filename_start - piece_start);
- } else {
- filename_start = piece_start;
- }
-
- // If the current piece isn't the runtime library name,
- // append it to new_env.
- if ((dylib_name_len != filename_len) ||
- (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
- if (new_env_pos != new_env + env_name_len + 1) {
- new_env_pos[0] = ':';
- new_env_pos++;
- }
- internal_strncpy(new_env_pos, piece_start, piece_len);
- new_env_pos += piece_len;
- }
- // Move on to the next piece.
- piece_start = piece_end;
- } while (piece_start < old_env_end);
-
- // Can't use setenv() here, because it requires the allocator to be
- // initialized.
- // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
- // a separate function called after InitializeAllocator().
- if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
- LeakyResetEnv(kDyldInsertLibraries, new_env);
-}
-
// No-op. Mac does not support static linkage anyway.
void *AsanDoesNotSupportStaticLinkage() {
return 0;
@@ -241,6 +67,30 @@ void AsanCheckDynamicRTPrereqs() {}
// No-op. Mac does not support static linkage anyway.
void AsanCheckIncompatibleRT() {}
+void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
+ // Find the Mach-O header for the image containing the needle
+ Dl_info info;
+ int err = dladdr(needle, &info);
+ if (err == 0) return;
+
+#if __LP64__
+ const struct mach_header_64 *mh = (struct mach_header_64 *)info.dli_fbase;
+#else
+ const struct mach_header *mh = (struct mach_header *)info.dli_fbase;
+#endif
+
+ // Look up the __asan_globals section in that image and register its globals
+ unsigned long size = 0;
+ __asan_global *globals = (__asan_global *)getsectiondata(
+ mh,
+ "__DATA", "__asan_globals",
+ &size);
+
+ if (!globals) return;
+ if (size % sizeof(__asan_global) != 0) return;
+ op(globals, size / sizeof(__asan_global));
+}
+
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
UNIMPLEMENTED();
}
diff --git a/libsanitizer/asan/asan_malloc_linux.cc b/libsanitizer/asan/asan_malloc_linux.cc
index bfe72af69e6..cc50a388495 100644
--- a/libsanitizer/asan/asan_malloc_linux.cc
+++ b/libsanitizer/asan/asan_malloc_linux.cc
@@ -76,7 +76,13 @@ INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
- void *new_ptr = asan_malloc(size, &stack);
+ void *new_ptr;
+ if (UNLIKELY(!asan_inited)) {
+ new_ptr = AllocateFromLocalPool(size);
+ } else {
+ copy_size = size;
+ new_ptr = asan_malloc(copy_size, &stack);
+ }
internal_memcpy(new_ptr, ptr, copy_size);
return new_ptr;
}
@@ -96,7 +102,7 @@ INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
GET_STACK_TRACE_MALLOC;
void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
- DTLS_on_libc_memalign(res, size * boundary);
+ DTLS_on_libc_memalign(res, size);
return res;
}
diff --git a/libsanitizer/asan/asan_malloc_mac.cc b/libsanitizer/asan/asan_malloc_mac.cc
index 33ccbf09470..1ca665d84c5 100644
--- a/libsanitizer/asan/asan_malloc_mac.cc
+++ b/libsanitizer/asan/asan_malloc_mac.cc
@@ -52,10 +52,6 @@ using namespace __asan;
#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
GET_STACK_TRACE_FREE; \
ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
-#define COMMON_MALLOC_IGNORE_INVALID_FREE flags()->mac_ignore_invalid_free
-#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \
- GET_STACK_TRACE_FREE; \
- WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
#define COMMON_MALLOC_NAMESPACE __asan
#include "sanitizer_common/sanitizer_malloc_mac.inc"
diff --git a/libsanitizer/asan/asan_malloc_win.cc b/libsanitizer/asan/asan_malloc_win.cc
index f2ab188bf5b..f38cd054bb0 100644
--- a/libsanitizer/asan/asan_malloc_win.cc
+++ b/libsanitizer/asan/asan_malloc_win.cc
@@ -12,6 +12,8 @@
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
#include "asan_allocator.h"
#include "asan_interceptors.h"
@@ -47,6 +49,11 @@ void _free_dbg(void *ptr, int) {
}
ALLOCATION_FUNCTION_ATTRIBUTE
+void _free_base(void *ptr) {
+ free(ptr);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
void cfree(void *ptr) {
CHECK(!"cfree() should not be used on Windows");
}
@@ -58,6 +65,11 @@ void *malloc(size_t size) {
}
ALLOCATION_FUNCTION_ATTRIBUTE
+void *_malloc_base(size_t size) {
+ return malloc(size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
void *_malloc_dbg(size_t size, int, const char *, int) {
return malloc(size);
}
@@ -69,6 +81,11 @@ void *calloc(size_t nmemb, size_t size) {
}
ALLOCATION_FUNCTION_ATTRIBUTE
+void *_calloc_base(size_t nmemb, size_t size) {
+ return calloc(nmemb, size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
return calloc(nmemb, size);
}
@@ -91,6 +108,11 @@ void *_realloc_dbg(void *ptr, size_t size, int) {
}
ALLOCATION_FUNCTION_ATTRIBUTE
+void *_realloc_base(void *ptr, size_t size) {
+ return realloc(ptr, size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
void *_recalloc(void *p, size_t n, size_t elem_size) {
if (!p)
return calloc(n, elem_size);
@@ -101,7 +123,12 @@ void *_recalloc(void *p, size_t n, size_t elem_size) {
}
ALLOCATION_FUNCTION_ATTRIBUTE
-size_t _msize(void *ptr) {
+void *_recalloc_base(void *p, size_t n, size_t elem_size) {
+ return _recalloc(p, n, elem_size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+size_t _msize(const void *ptr) {
GET_CURRENT_PC_BP_SP;
(void)sp;
return asan_malloc_usable_size(ptr, pc, bp);
@@ -137,38 +164,90 @@ int _CrtSetReportMode(int, int) {
}
} // extern "C"
+INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags,
+ SIZE_T dwBytes) {
+ GET_STACK_TRACE_MALLOC;
+ void *p = asan_malloc(dwBytes, &stack);
+ // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
+ // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
+ // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
+ if (dwFlags == HEAP_ZERO_MEMORY)
+ internal_memset(p, 0, asan_mz_size(p));
+ else
+ CHECK(dwFlags == 0 && "unsupported heap flags");
+ return p;
+}
+
+INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
+ CHECK(dwFlags == 0 && "unsupported heap flags");
+ GET_STACK_TRACE_FREE;
+ asan_free(lpMem, &stack, FROM_MALLOC);
+ return true;
+}
+
+INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
+ LPVOID lpMem, SIZE_T dwBytes) {
+ GET_STACK_TRACE_MALLOC;
+ // Realloc should never reallocate in place.
+ if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY)
+ return nullptr;
+ CHECK(dwFlags == 0 && "unsupported heap flags");
+ return asan_realloc(lpMem, dwBytes, &stack);
+}
+
+INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags,
+ LPCVOID lpMem) {
+ CHECK(dwFlags == 0 && "unsupported heap flags");
+ GET_CURRENT_PC_BP_SP;
+ (void)sp;
+ return asan_malloc_usable_size(lpMem, pc, bp);
+}
+
namespace __asan {
+
+static void TryToOverrideFunction(const char *fname, uptr new_func) {
+ // Failure here is not fatal. The CRT may not be present, and different CRT
+ // versions use different symbols.
+ if (!__interception::OverrideFunction(fname, new_func))
+ VPrintf(2, "Failed to override function %s\n", fname);
+}
+
void ReplaceSystemMalloc() {
#if defined(ASAN_DYNAMIC)
- // We don't check the result because CRT might not be used in the process.
- __interception::OverrideFunction("free", (uptr)free);
- __interception::OverrideFunction("malloc", (uptr)malloc);
- __interception::OverrideFunction("_malloc_crt", (uptr)malloc);
- __interception::OverrideFunction("calloc", (uptr)calloc);
- __interception::OverrideFunction("_calloc_crt", (uptr)calloc);
- __interception::OverrideFunction("realloc", (uptr)realloc);
- __interception::OverrideFunction("_realloc_crt", (uptr)realloc);
- __interception::OverrideFunction("_recalloc", (uptr)_recalloc);
- __interception::OverrideFunction("_recalloc_crt", (uptr)_recalloc);
- __interception::OverrideFunction("_msize", (uptr)_msize);
- __interception::OverrideFunction("_expand", (uptr)_expand);
-
- // Override different versions of 'operator new' and 'operator delete'.
- // No need to override the nothrow versions as they just wrap the throw
- // versions.
- // FIXME: Unfortunately, MSVC miscompiles the statements that take the
- // addresses of the array versions of these operators,
- // see https://connect.microsoft.com/VisualStudio/feedbackdetail/view/946992
- // We might want to try to work around this by [inline] assembly or compiling
- // parts of the RTL with Clang.
- void *(*op_new)(size_t sz) = operator new;
- void (*op_delete)(void *p) = operator delete;
- void *(*op_array_new)(size_t sz) = operator new[];
- void (*op_array_delete)(void *p) = operator delete[];
- __interception::OverrideFunction("??2@YAPAXI@Z", (uptr)op_new);
- __interception::OverrideFunction("??3@YAXPAX@Z", (uptr)op_delete);
- __interception::OverrideFunction("??_U@YAPAXI@Z", (uptr)op_array_new);
- __interception::OverrideFunction("??_V@YAXPAX@Z", (uptr)op_array_delete);
+ TryToOverrideFunction("free", (uptr)free);
+ TryToOverrideFunction("_free_base", (uptr)free);
+ TryToOverrideFunction("malloc", (uptr)malloc);
+ TryToOverrideFunction("_malloc_base", (uptr)malloc);
+ TryToOverrideFunction("_malloc_crt", (uptr)malloc);
+ TryToOverrideFunction("calloc", (uptr)calloc);
+ TryToOverrideFunction("_calloc_base", (uptr)calloc);
+ TryToOverrideFunction("_calloc_crt", (uptr)calloc);
+ TryToOverrideFunction("realloc", (uptr)realloc);
+ TryToOverrideFunction("_realloc_base", (uptr)realloc);
+ TryToOverrideFunction("_realloc_crt", (uptr)realloc);
+ TryToOverrideFunction("_recalloc", (uptr)_recalloc);
+ TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
+ TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
+ TryToOverrideFunction("_msize", (uptr)_msize);
+ TryToOverrideFunction("_expand", (uptr)_expand);
+ TryToOverrideFunction("_expand_base", (uptr)_expand);
+
+ // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which
+ // enable cross-module inlining. This means our _malloc_base hook won't catch
+ // all CRT allocations. This code here patches the import table of
+ // ucrtbase.dll so that all attempts to use the lower-level win32 heap
+ // allocation API will be directed to ASan's heap. We don't currently
+ // intercept all calls to HeapAlloc. If we did, we would have to check on
+ // HeapFree whether the pointer came from ASan of from the system.
+#define INTERCEPT_UCRT_FUNCTION(func) \
+ if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \
+ "api-ms-win-core-heap-l1-1-0.dll", func)) \
+ VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func);
+ INTERCEPT_UCRT_FUNCTION(HeapAlloc);
+ INTERCEPT_UCRT_FUNCTION(HeapFree);
+ INTERCEPT_UCRT_FUNCTION(HeapReAlloc);
+ INTERCEPT_UCRT_FUNCTION(HeapSize);
+#undef INTERCEPT_UCRT_FUNCTION
#endif
}
} // namespace __asan
diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h
index b584cfa2f3c..b9fa5f79481 100644
--- a/libsanitizer/asan/asan_mapping.h
+++ b/libsanitizer/asan/asan_mapping.h
@@ -15,7 +15,7 @@
#include "asan_internal.h"
// The full explanation of the memory mapping could be found here:
-// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
+// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm
//
// Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000:
// || `[0x10007fff8000, 0x7fffffffffff]` || HighMem ||
@@ -85,6 +85,20 @@
// || `[0x08000000000, 0x08fffffffff]` || lowshadow ||
// || `[0x00000000000, 0x07fffffffff]` || lowmem ||
//
+// Default Linux/S390 mapping:
+// || `[0x30000000, 0x7fffffff]` || HighMem ||
+// || `[0x26000000, 0x2fffffff]` || HighShadow ||
+// || `[0x24000000, 0x25ffffff]` || ShadowGap ||
+// || `[0x20000000, 0x23ffffff]` || LowShadow ||
+// || `[0x00000000, 0x1fffffff]` || LowMem ||
+//
+// Default Linux/SystemZ mapping:
+// || `[0x14000000000000, 0x1fffffffffffff]` || HighMem ||
+// || `[0x12800000000000, 0x13ffffffffffff]` || HighShadow ||
+// || `[0x12000000000000, 0x127fffffffffff]` || ShadowGap ||
+// || `[0x10000000000000, 0x11ffffffffffff]` || LowShadow ||
+// || `[0x00000000000000, 0x0fffffffffffff]` || LowMem ||
+//
// Shadow mapping on FreeBSD/x86-64 with SHADOW_OFFSET == 0x400000000000:
// || `[0x500000000000, 0x7fffffffffff]` || HighMem ||
// || `[0x4a0000000000, 0x4fffffffffff]` || HighShadow ||
@@ -109,17 +123,19 @@
// || `[0x00000000, 0x2fffffff]` || LowMem ||
static const u64 kDefaultShadowScale = 3;
+static const u64 kDefaultShadowSentinel = ~(uptr)0;
static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G.
static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000
-static const u64 kIosShadowOffset64 = 0x130000000;
+static const u64 kIosShadowOffset64 = 0x120200000;
static const u64 kIosSimShadowOffset32 = 1ULL << 30;
static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64;
static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37;
static const u64 kPPC64_ShadowOffset64 = 1ULL << 41;
+static const u64 kSystemZ_ShadowOffset64 = 1ULL << 52;
static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000
static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000
static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
@@ -136,28 +152,36 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
# define SHADOW_OFFSET kFreeBSD_ShadowOffset32
# elif SANITIZER_WINDOWS
# define SHADOW_OFFSET kWindowsShadowOffset32
-# elif SANITIZER_IOSSIM
-# define SHADOW_OFFSET kIosSimShadowOffset32
# elif SANITIZER_IOS
-# define SHADOW_OFFSET kIosShadowOffset32
+# if SANITIZER_IOSSIM
+# define SHADOW_OFFSET kIosSimShadowOffset32
+# else
+# define SHADOW_OFFSET kIosShadowOffset32
+# endif
# else
# define SHADOW_OFFSET kDefaultShadowOffset32
# endif
#else
-# if defined(__aarch64__)
+# if SANITIZER_IOS
+# if SANITIZER_IOSSIM
+# define SHADOW_OFFSET kIosSimShadowOffset64
+# else
+# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
+# endif
+# elif defined(__aarch64__)
# define SHADOW_OFFSET kAArch64_ShadowOffset64
# elif defined(__powerpc64__)
# define SHADOW_OFFSET kPPC64_ShadowOffset64
+# elif defined(__s390x__)
+# define SHADOW_OFFSET kSystemZ_ShadowOffset64
# elif SANITIZER_FREEBSD
# define SHADOW_OFFSET kFreeBSD_ShadowOffset64
# elif SANITIZER_MAC
# define SHADOW_OFFSET kDefaultShadowOffset64
# elif defined(__mips64)
# define SHADOW_OFFSET kMIPS64_ShadowOffset64
-# elif SANITIZER_IOSSIM
-# define SHADOW_OFFSET kIosSimShadowOffset64
-# elif SANITIZER_IOS
-# define SHADOW_OFFSET kIosShadowOffset64
+# elif SANITIZER_WINDOWS64
+# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
# else
# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
# endif
@@ -243,9 +267,25 @@ static inline bool AddrIsInMidMem(uptr a) {
return kMidMemBeg && a >= kMidMemBeg && a <= kMidMemEnd;
}
+static inline bool AddrIsInShadowGap(uptr a) {
+ PROFILE_ASAN_MAPPING();
+ if (kMidMemBeg) {
+ if (a <= kShadowGapEnd)
+ return SHADOW_OFFSET == 0 || a >= kShadowGapBeg;
+ return (a >= kShadowGap2Beg && a <= kShadowGap2End) ||
+ (a >= kShadowGap3Beg && a <= kShadowGap3End);
+ }
+ // 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;
+}
+
static inline bool AddrIsInMem(uptr a) {
PROFILE_ASAN_MAPPING();
- return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a);
+ return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a) ||
+ (flags()->protect_shadow_gap == 0 && AddrIsInShadowGap(a));
}
static inline uptr MemToShadow(uptr p) {
@@ -269,21 +309,6 @@ static inline bool AddrIsInShadow(uptr a) {
return AddrIsInLowShadow(a) || AddrIsInMidShadow(a) || AddrIsInHighShadow(a);
}
-static inline bool AddrIsInShadowGap(uptr a) {
- PROFILE_ASAN_MAPPING();
- if (kMidMemBeg) {
- if (a <= kShadowGapEnd)
- return SHADOW_OFFSET == 0 || a >= kShadowGapBeg;
- return (a >= kShadowGap2Beg && a <= kShadowGap2End) ||
- (a >= kShadowGap3Beg && a <= kShadowGap3End);
- }
- // 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;
-}
-
static inline bool AddrIsAlignedByGranularity(uptr a) {
PROFILE_ASAN_MAPPING();
return (a & (SHADOW_GRANULARITY - 1)) == 0;
diff --git a/libsanitizer/asan/asan_memory_profile.cc b/libsanitizer/asan/asan_memory_profile.cc
new file mode 100644
index 00000000000..5a2578596ad
--- /dev/null
+++ b/libsanitizer/asan/asan_memory_profile.cc
@@ -0,0 +1,98 @@
+//===-- asan_memory_profile.cc.cc -----------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file implements __sanitizer_print_memory_profile.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_stoptheworld.h"
+#include "lsan/lsan_common.h"
+#include "asan/asan_allocator.h"
+
+#if CAN_SANITIZE_LEAKS
+
+namespace __asan {
+
+struct AllocationSite {
+ u32 id;
+ uptr total_size;
+ uptr count;
+};
+
+class HeapProfile {
+ public:
+ HeapProfile() : allocations_(1024) {}
+ void Insert(u32 id, uptr size) {
+ total_allocated_ += size;
+ total_count_++;
+ // Linear lookup will be good enough for most cases (although not all).
+ for (uptr i = 0; i < allocations_.size(); i++) {
+ if (allocations_[i].id == id) {
+ allocations_[i].total_size += size;
+ allocations_[i].count++;
+ return;
+ }
+ }
+ allocations_.push_back({id, size, 1});
+ }
+
+ void Print(uptr top_percent) {
+ InternalSort(&allocations_, allocations_.size(),
+ [](const AllocationSite &a, const AllocationSite &b) {
+ return a.total_size > b.total_size;
+ });
+ CHECK(total_allocated_);
+ uptr total_shown = 0;
+ Printf("Live Heap Allocations: %zd bytes from %zd allocations; "
+ "showing top %zd%%\n", total_allocated_, total_count_, top_percent);
+ for (uptr i = 0; i < allocations_.size(); i++) {
+ auto &a = allocations_[i];
+ Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size,
+ a.total_size * 100 / total_allocated_, a.count);
+ StackDepotGet(a.id).Print();
+ total_shown += a.total_size;
+ if (total_shown * 100 / total_allocated_ > top_percent)
+ break;
+ }
+ }
+
+ private:
+ uptr total_allocated_ = 0;
+ uptr total_count_ = 0;
+ InternalMmapVector<AllocationSite> allocations_;
+};
+
+static void ChunkCallback(uptr chunk, void *arg) {
+ HeapProfile *hp = reinterpret_cast<HeapProfile*>(arg);
+ AsanChunkView cv = FindHeapChunkByAllocBeg(chunk);
+ if (!cv.IsAllocated()) return;
+ u32 id = cv.GetAllocStackId();
+ if (!id) return;
+ hp->Insert(id, cv.UsedSize());
+}
+
+static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list,
+ void *argument) {
+ HeapProfile hp;
+ __lsan::ForEachChunk(ChunkCallback, &hp);
+ hp.Print(reinterpret_cast<uptr>(argument));
+}
+
+} // namespace __asan
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_memory_profile(uptr top_percent) {
+ __sanitizer::StopTheWorld(__asan::MemoryProfileCB, (void*)top_percent);
+}
+} // extern "C"
+
+#endif // CAN_SANITIZE_LEAKS
diff --git a/libsanitizer/asan/asan_new_delete.cc b/libsanitizer/asan/asan_new_delete.cc
index bf7a34ac87b..2c4642cb27e 100644
--- a/libsanitizer/asan/asan_new_delete.cc
+++ b/libsanitizer/asan/asan_new_delete.cc
@@ -18,9 +18,25 @@
#include <stddef.h>
-// C++ operators can't have visibility attributes on Windows.
+// C++ operators can't have dllexport attributes on Windows. We export them
+// anyway by passing extra -export flags to the linker, which is exactly that
+// dllexport would normally do. We need to export them in order to make the
+// VS2015 dynamic CRT (MD) work.
#if SANITIZER_WINDOWS
# define CXX_OPERATOR_ATTRIBUTE
+# ifdef _WIN64
+# pragma comment(linker, "/export:??2@YAPEAX_K@Z") // operator new
+# pragma comment(linker, "/export:??3@YAXPEAX@Z") // operator delete
+# pragma comment(linker, "/export:??3@YAXPEAX_K@Z") // sized operator delete
+# pragma comment(linker, "/export:??_U@YAPEAX_K@Z") // operator new[]
+# pragma comment(linker, "/export:??_V@YAXPEAX@Z") // operator delete[]
+# else
+# pragma comment(linker, "/export:??2@YAPAXI@Z") // operator new
+# pragma comment(linker, "/export:??3@YAXPAX@Z") // operator delete
+# pragma comment(linker, "/export:??3@YAXPAXI@Z") // sized operator delete
+# pragma comment(linker, "/export:??_U@YAPAXI@Z") // operator new[]
+# pragma comment(linker, "/export:??_V@YAXPAX@Z") // operator delete[]
+# endif
#else
# define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE
#endif
@@ -37,7 +53,7 @@ using namespace __asan; // NOLINT
#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
// This code has issues on OSX.
-// See https://code.google.com/p/address-sanitizer/issues/detail?id=131.
+// See https://github.com/google/sanitizers/issues/131.
// Fake std::nothrow_t and std::align_val_t to avoid including <new>.
namespace std {
diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc
index 39f74879b38..8fe2bd42bdb 100644
--- a/libsanitizer/asan/asan_poisoning.cc
+++ b/libsanitizer/asan/asan_poisoning.cc
@@ -67,7 +67,7 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {
uptr page_size = GetPageSizeCached();
uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size);
uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size);
- FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
+ ReleaseMemoryToOS(shadow_beg, shadow_end - shadow_beg);
}
void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
@@ -100,7 +100,7 @@ using namespace __asan; // NOLINT
// that user program (un)poisons the memory it owns. It poisons memory
// conservatively, and unpoisons progressively to make sure asan shadow
// mapping invariant is preserved (see detailed mapping description here:
-// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm).
+// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm).
//
// * if user asks to poison region [left, right), the program poisons
// at least [left, AlignDown(right)).
@@ -115,9 +115,9 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) {
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
if (beg.chunk == end.chunk) {
- CHECK(beg.offset < end.offset);
+ CHECK_LT(beg.offset, end.offset);
s8 value = beg.value;
- CHECK(value == end.value);
+ CHECK_EQ(value, end.value);
// We can only poison memory if the byte in end.offset is unaddressable.
// No need to re-poison memory if it is poisoned already.
if (value > 0 && value <= end.offset) {
@@ -129,7 +129,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) {
}
return;
}
- CHECK(beg.chunk < end.chunk);
+ CHECK_LT(beg.chunk, end.chunk);
if (beg.offset > 0) {
// Mark bytes from beg.offset as unaddressable.
if (beg.value == 0) {
@@ -155,9 +155,9 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
if (beg.chunk == end.chunk) {
- CHECK(beg.offset < end.offset);
+ CHECK_LT(beg.offset, end.offset);
s8 value = beg.value;
- CHECK(value == end.value);
+ CHECK_EQ(value, end.value);
// We unpoison memory bytes up to enbytes up to end.offset if it is not
// unpoisoned already.
if (value != 0) {
@@ -165,7 +165,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
}
return;
}
- CHECK(beg.chunk < end.chunk);
+ CHECK_LT(beg.chunk, end.chunk);
if (beg.offset > 0) {
*beg.chunk = 0;
beg.chunk++;
@@ -312,6 +312,30 @@ static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
}
}
+void __asan_set_shadow_00(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0, size);
+}
+
+void __asan_set_shadow_f1(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf1, size);
+}
+
+void __asan_set_shadow_f2(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf2, size);
+}
+
+void __asan_set_shadow_f3(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf3, size);
+}
+
+void __asan_set_shadow_f5(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf5, size);
+}
+
+void __asan_set_shadow_f8(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf8, size);
+}
+
void __asan_poison_stack_memory(uptr addr, uptr size) {
VReport(1, "poisoning: %p %zx\n", (void *)addr, size);
PoisonAlignedStackMemory(addr, size, true);
@@ -341,7 +365,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p,
&stack);
}
CHECK_LE(end - beg,
- FIRST_32_SECOND_64(1UL << 30, 1UL << 34)); // Sanity check.
+ FIRST_32_SECOND_64(1UL << 30, 1ULL << 34)); // Sanity check.
uptr a = RoundDownTo(Min(old_mid, new_mid), granularity);
uptr c = RoundUpTo(Max(old_mid, new_mid), granularity);
@@ -352,7 +376,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p,
// Make a quick sanity check that we are indeed in this state.
//
// FIXME: Two of these three checks are disabled until we fix
- // https://code.google.com/p/address-sanitizer/issues/detail?id=258.
+ // https://github.com/google/sanitizers/issues/258.
// if (d1 != d2)
// CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1);
if (a + granularity <= d1)
diff --git a/libsanitizer/asan/asan_poisoning.h b/libsanitizer/asan/asan_poisoning.h
index 30e39e9077c..4ddcbc314d4 100644
--- a/libsanitizer/asan/asan_poisoning.h
+++ b/libsanitizer/asan/asan_poisoning.h
@@ -84,7 +84,7 @@ ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone(
}
}
-// Calls __sanitizer::FlushUnneededShadowMemory() on
+// Calls __sanitizer::ReleaseMemoryToOS() on
// [MemToShadow(p), MemToShadow(p+size)] with proper rounding.
void FlushUnneededASanShadowMemory(uptr p, uptr size);
diff --git a/libsanitizer/asan/asan_posix.cc b/libsanitizer/asan/asan_posix.cc
index 5b532e950bb..532afb37f1a 100644
--- a/libsanitizer/asan/asan_posix.cc
+++ b/libsanitizer/asan/asan_posix.cc
@@ -31,17 +31,39 @@
namespace __asan {
+const char *DescribeSignalOrException(int signo) {
+ switch (signo) {
+ case SIGFPE:
+ return "FPE";
+ case SIGILL:
+ return "ILL";
+ case SIGABRT:
+ return "ABRT";
+ default:
+ return "SEGV";
+ }
+}
+
void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
ScopedDeadlySignal signal_scope(GetCurrentThread());
int code = (int)((siginfo_t*)siginfo)->si_code;
- // Write the first message using the bullet-proof write.
- if (18 != internal_write(2, "ASAN:DEADLYSIGNAL\n", 18)) Die();
+ // Write the first message using fd=2, just in case.
+ // It may actually fail to write in case stderr is closed.
+ internal_write(2, "ASAN:DEADLYSIGNAL\n", 18);
SignalContext sig = SignalContext::Create(siginfo, context);
// Access at a reasonable offset above SP, or slightly below it (to account
// for x86_64 or PowerPC redzone, ARM push of multiple registers, etc) is
// probably a stack overflow.
+#ifdef __s390__
+ // On s390, the fault address in siginfo points to start of the page, not
+ // to the precise word that was accessed. Mask off the low bits of sp to
+ // take it into account.
+ bool IsStackAccess = sig.addr >= (sig.sp & ~0xFFF) &&
+ sig.addr < sig.sp + 0xFFFF;
+#else
bool IsStackAccess = sig.addr + 512 > sig.sp && sig.addr < sig.sp + 0xFFFF;
+#endif
#if __powerpc__
// Large stack frames can be allocated with e.g.
@@ -73,10 +95,8 @@ void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
// unaligned memory access.
if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
ReportStackOverflow(sig);
- else if (signo == SIGFPE)
- ReportDeadlySignal("FPE", sig);
else
- ReportDeadlySignal("SEGV", sig);
+ ReportDeadlySignal(signo, sig);
}
// ---------------------- TSD ---------------- {{{1
diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc
index 7f7eaf515e7..84d67646b40 100644
--- a/libsanitizer/asan/asan_report.cc
+++ b/libsanitizer/asan/asan_report.cc
@@ -10,10 +10,13 @@
// This file contains error reporting code.
//===----------------------------------------------------------------------===//
+#include "asan_errors.h"
#include "asan_flags.h"
+#include "asan_descriptions.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_report.h"
+#include "asan_scariness_score.h"
#include "asan_stack.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_common.h"
@@ -28,88 +31,31 @@ namespace __asan {
static void (*error_report_callback)(const char*);
static char *error_message_buffer = nullptr;
static uptr error_message_buffer_pos = 0;
-static uptr error_message_buffer_size = 0;
-
-struct ReportData {
- uptr pc;
- uptr sp;
- uptr bp;
- uptr addr;
- bool is_write;
- uptr access_size;
- const char *description;
-};
-
-static bool report_happened = false;
-static ReportData report_data = {};
+static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED);
+static const unsigned kAsanBuggyPcPoolSize = 25;
+static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
void AppendToErrorMessageBuffer(const char *buffer) {
- if (error_message_buffer) {
- uptr length = internal_strlen(buffer);
- CHECK_GE(error_message_buffer_size, error_message_buffer_pos);
- uptr remaining = error_message_buffer_size - error_message_buffer_pos;
- internal_strncpy(error_message_buffer + error_message_buffer_pos,
- buffer, remaining);
- error_message_buffer[error_message_buffer_size - 1] = '\0';
- // FIXME: reallocate the buffer instead of truncating the message.
- error_message_buffer_pos += Min(remaining, length);
+ BlockingMutexLock l(&error_message_buf_mutex);
+ if (!error_message_buffer) {
+ error_message_buffer =
+ (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__);
+ error_message_buffer_pos = 0;
}
+ uptr length = internal_strlen(buffer);
+ RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos);
+ uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos;
+ internal_strncpy(error_message_buffer + error_message_buffer_pos,
+ buffer, remaining);
+ error_message_buffer[kErrorMessageBufferSize - 1] = '\0';
+ // FIXME: reallocate the buffer instead of truncating the message.
+ error_message_buffer_pos += Min(remaining, length);
}
-// ---------------------- Decorator ------------------------------ {{{1
-class Decorator: public __sanitizer::SanitizerCommonDecorator {
- public:
- Decorator() : SanitizerCommonDecorator() { }
- const char *Access() { return Blue(); }
- const char *EndAccess() { return Default(); }
- const char *Location() { return Green(); }
- const char *EndLocation() { return Default(); }
- const char *Allocation() { return Magenta(); }
- const char *EndAllocation() { return Default(); }
-
- const char *ShadowByte(u8 byte) {
- switch (byte) {
- case kAsanHeapLeftRedzoneMagic:
- case kAsanHeapRightRedzoneMagic:
- case kAsanArrayCookieMagic:
- return Red();
- case kAsanHeapFreeMagic:
- return Magenta();
- case kAsanStackLeftRedzoneMagic:
- case kAsanStackMidRedzoneMagic:
- case kAsanStackRightRedzoneMagic:
- case kAsanStackPartialRedzoneMagic:
- return Red();
- case kAsanStackAfterReturnMagic:
- return Magenta();
- case kAsanInitializationOrderMagic:
- return Cyan();
- case kAsanUserPoisonedMemoryMagic:
- case kAsanContiguousContainerOOBMagic:
- case kAsanAllocaLeftMagic:
- case kAsanAllocaRightMagic:
- return Blue();
- case kAsanStackUseAfterScopeMagic:
- return Magenta();
- case kAsanGlobalRedzoneMagic:
- return Red();
- case kAsanInternalHeapMagic:
- return Yellow();
- case kAsanIntraObjectRedzone:
- return Yellow();
- default:
- return Default();
- }
- }
- const char *EndShadowByte() { return Default(); }
- const char *MemoryByte() { return Magenta(); }
- const char *EndMemoryByte() { return Default(); }
-};
-
// ---------------------- Helper functions ----------------------- {{{1
-static void PrintMemoryByte(InternalScopedString *str, const char *before,
- u8 byte, bool in_shadow, const char *after = "\n") {
+void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte,
+ bool in_shadow, const char *after) {
Decorator d;
str->append("%s%s%x%x%s%s", before,
in_shadow ? d.ShadowByte(byte) : d.MemoryByte(),
@@ -117,99 +63,6 @@ static void PrintMemoryByte(InternalScopedString *str, const char *before,
in_shadow ? d.EndShadowByte() : d.EndMemoryByte(), after);
}
-static void PrintShadowByte(InternalScopedString *str, const char *before,
- u8 byte, const char *after = "\n") {
- PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
-}
-
-static void PrintShadowBytes(InternalScopedString *str, const char *before,
- u8 *bytes, u8 *guilty, uptr n) {
- Decorator d;
- if (before) str->append("%s%p:", before, bytes);
- for (uptr i = 0; i < n; i++) {
- u8 *p = bytes + i;
- const char *before =
- p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
- const char *after = p == guilty ? "]" : "";
- PrintShadowByte(str, before, *p, after);
- }
- str->append("\n");
-}
-
-static void PrintLegend(InternalScopedString *str) {
- str->append(
- "Shadow byte legend (one shadow byte represents %d "
- "application bytes):\n",
- (int)SHADOW_GRANULARITY);
- PrintShadowByte(str, " Addressable: ", 0);
- str->append(" Partially addressable: ");
- for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
- str->append("\n");
- PrintShadowByte(str, " Heap left redzone: ",
- kAsanHeapLeftRedzoneMagic);
- PrintShadowByte(str, " Heap right redzone: ",
- kAsanHeapRightRedzoneMagic);
- PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic);
- PrintShadowByte(str, " Stack left redzone: ",
- kAsanStackLeftRedzoneMagic);
- PrintShadowByte(str, " Stack mid redzone: ",
- kAsanStackMidRedzoneMagic);
- PrintShadowByte(str, " Stack right redzone: ",
- kAsanStackRightRedzoneMagic);
- PrintShadowByte(str, " Stack partial redzone: ",
- kAsanStackPartialRedzoneMagic);
- PrintShadowByte(str, " Stack after return: ",
- kAsanStackAfterReturnMagic);
- PrintShadowByte(str, " Stack use after scope: ",
- kAsanStackUseAfterScopeMagic);
- PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic);
- PrintShadowByte(str, " Global init order: ",
- kAsanInitializationOrderMagic);
- PrintShadowByte(str, " Poisoned by user: ",
- kAsanUserPoisonedMemoryMagic);
- PrintShadowByte(str, " Container overflow: ",
- kAsanContiguousContainerOOBMagic);
- PrintShadowByte(str, " Array cookie: ",
- kAsanArrayCookieMagic);
- PrintShadowByte(str, " Intra object redzone: ",
- kAsanIntraObjectRedzone);
- PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
- PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic);
- PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic);
-}
-
-void MaybeDumpInstructionBytes(uptr pc) {
- if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))
- return;
- InternalScopedString str(1024);
- str.append("First 16 instruction bytes at pc: ");
- if (IsAccessibleMemoryRange(pc, 16)) {
- for (int i = 0; i < 16; ++i) {
- PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/false, " ");
- }
- str.append("\n");
- } else {
- str.append("unaccessible\n");
- }
- Report("%s", str.data());
-}
-
-static void PrintShadowMemoryForAddress(uptr addr) {
- if (!AddrIsInMem(addr)) return;
- uptr shadow_addr = MemToShadow(addr);
- const uptr n_bytes_per_row = 16;
- uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
- InternalScopedString str(4096 * 8);
- str.append("Shadow bytes around the buggy address:\n");
- for (int i = -5; i <= 5; i++) {
- const char *prefix = (i == 0) ? "=>" : " ";
- PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row),
- (u8 *)shadow_addr, n_bytes_per_row);
- }
- if (flags()->print_legend) PrintLegend(&str);
- Printf("%s", str.data());
-}
-
static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
const char *zone_name) {
if (zone_ptr) {
@@ -225,191 +78,8 @@ static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
}
}
-static void DescribeThread(AsanThread *t) {
- if (t)
- DescribeThread(t->context());
-}
-
// ---------------------- Address Descriptions ------------------- {{{1
-static bool IsASCII(unsigned char c) {
- return /*0x00 <= c &&*/ c <= 0x7F;
-}
-
-static const char *MaybeDemangleGlobalName(const char *name) {
- // We can spoil names of globals with C linkage, so use an heuristic
- // approach to check if the name should be demangled.
- bool should_demangle = false;
- if (name[0] == '_' && name[1] == 'Z')
- should_demangle = true;
- else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
- should_demangle = true;
-
- return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
-}
-
-// Check if the global is a zero-terminated ASCII string. If so, print it.
-static void PrintGlobalNameIfASCII(InternalScopedString *str,
- const __asan_global &g) {
- for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
- unsigned char c = *(unsigned char*)p;
- if (c == '\0' || !IsASCII(c)) return;
- }
- if (*(char*)(g.beg + g.size - 1) != '\0') return;
- str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
- (char *)g.beg);
-}
-
-static const char *GlobalFilename(const __asan_global &g) {
- const char *res = g.module_name;
- // Prefer the filename from source location, if is available.
- if (g.location)
- res = g.location->filename;
- CHECK(res);
- return res;
-}
-
-static void PrintGlobalLocation(InternalScopedString *str,
- const __asan_global &g) {
- str->append("%s", GlobalFilename(g));
- if (!g.location)
- return;
- if (g.location->line_no)
- str->append(":%d", g.location->line_no);
- if (g.location->column_no)
- str->append(":%d", g.location->column_no);
-}
-
-static void DescribeAddressRelativeToGlobal(uptr addr, uptr size,
- const __asan_global &g) {
- InternalScopedString str(4096);
- Decorator d;
- str.append("%s", d.Location());
- if (addr < g.beg) {
- str.append("%p is located %zd bytes to the left", (void *)addr,
- g.beg - addr);
- } else if (addr + size > g.beg + g.size) {
- if (addr < g.beg + g.size)
- addr = g.beg + g.size;
- str.append("%p is located %zd bytes to the right", (void *)addr,
- addr - (g.beg + g.size));
- } else {
- // Can it happen?
- str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
- }
- str.append(" of global variable '%s' defined in '",
- MaybeDemangleGlobalName(g.name));
- PrintGlobalLocation(&str, g);
- str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
- str.append("%s", d.EndLocation());
- PrintGlobalNameIfASCII(&str, g);
- Printf("%s", str.data());
-}
-
-static bool DescribeAddressIfGlobal(uptr addr, uptr size,
- const char *bug_type) {
- // Assume address is close to at most four globals.
- const int kMaxGlobalsInReport = 4;
- __asan_global globals[kMaxGlobalsInReport];
- u32 reg_sites[kMaxGlobalsInReport];
- int globals_num =
- GetGlobalsForAddress(addr, globals, reg_sites, ARRAY_SIZE(globals));
- if (globals_num == 0)
- return false;
- for (int i = 0; i < globals_num; i++) {
- DescribeAddressRelativeToGlobal(addr, size, globals[i]);
- if (0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
- reg_sites[i]) {
- Printf(" registered at:\n");
- StackDepotGet(reg_sites[i]).Print();
- }
- }
- return true;
-}
-
-bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr, bool print) {
- if (AddrIsInMem(addr))
- return false;
- const char *area_type = nullptr;
- if (AddrIsInShadowGap(addr)) area_type = "shadow gap";
- else if (AddrIsInHighShadow(addr)) area_type = "high shadow";
- else if (AddrIsInLowShadow(addr)) area_type = "low shadow";
- if (area_type != nullptr) {
- if (print) {
- Printf("Address %p is located in the %s area.\n", addr, area_type);
- } else {
- CHECK(descr);
- descr->region_kind = area_type;
- }
- return true;
- }
- CHECK(0 && "Address is not in memory and not in shadow?");
- return false;
-}
-
-// Return " (thread_name) " or an empty string if the name is empty.
-const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
- uptr buff_len) {
- const char *name = t->name;
- if (name[0] == '\0') return "";
- buff[0] = 0;
- internal_strncat(buff, " (", 3);
- internal_strncat(buff, name, buff_len - 4);
- internal_strncat(buff, ")", 2);
- return buff;
-}
-
-const char *ThreadNameWithParenthesis(u32 tid, char buff[],
- uptr buff_len) {
- if (tid == kInvalidTid) return "";
- asanThreadRegistry().CheckLocked();
- AsanThreadContext *t = GetThreadContextByTidLocked(tid);
- return ThreadNameWithParenthesis(t, buff, buff_len);
-}
-
-static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
- uptr access_size, uptr prev_var_end,
- uptr next_var_beg) {
- uptr var_end = var.beg + var.size;
- uptr addr_end = addr + access_size;
- const char *pos_descr = nullptr;
- // If the variable [var.beg, var_end) is the nearest variable to the
- // current memory access, indicate it in the log.
- if (addr >= var.beg) {
- if (addr_end <= var_end)
- pos_descr = "is inside"; // May happen if this is a use-after-return.
- else if (addr < var_end)
- pos_descr = "partially overflows";
- else if (addr_end <= next_var_beg &&
- next_var_beg - addr_end >= addr - var_end)
- pos_descr = "overflows";
- } else {
- if (addr_end > var.beg)
- pos_descr = "partially underflows";
- else if (addr >= prev_var_end &&
- addr - prev_var_end >= var.beg - addr_end)
- pos_descr = "underflows";
- }
- InternalScopedString str(1024);
- str.append(" [%zd, %zd)", var.beg, var_end);
- // Render variable name.
- str.append(" '");
- for (uptr i = 0; i < var.name_len; ++i) {
- str.append("%c", var.name_pos[i]);
- }
- str.append("'");
- if (pos_descr) {
- Decorator d;
- // FIXME: we may want to also print the size of the access here,
- // but in case of accesses generated by memset it may be confusing.
- str.append("%s <== Memory access at offset %zd %s this variable%s\n",
- d.Location(), addr, pos_descr, d.EndLocation());
- } else {
- str.append("\n");
- }
- Printf("%s", str.data());
-}
-
bool ParseFrameDescription(const char *frame_descr,
InternalMmapVector<StackVarDescr> *vars) {
CHECK(frame_descr);
@@ -437,195 +107,17 @@ bool ParseFrameDescription(const char *frame_descr,
return true;
}
-bool DescribeAddressIfStack(uptr addr, uptr access_size) {
- AsanThread *t = FindThreadByStackAddress(addr);
- if (!t) return false;
-
- Decorator d;
- char tname[128];
- Printf("%s", d.Location());
- Printf("Address %p is located in stack of thread T%d%s", addr, t->tid(),
- ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)));
-
- // Try to fetch precise stack frame for this access.
- AsanThread::StackFrameAccess access;
- if (!t->GetStackFrameAccessByAddr(addr, &access)) {
- Printf("%s\n", d.EndLocation());
- return true;
- }
- Printf(" at offset %zu in frame%s\n", access.offset, d.EndLocation());
-
- // Now we print the frame where the alloca has happened.
- // We print this frame as a stack trace with one element.
- // The symbolizer may print more than one frame if inlining was involved.
- // The frame numbers may be different than those in the stack trace printed
- // previously. That's unfortunate, but I have no better solution,
- // especially given that the alloca may be from entirely different place
- // (e.g. use-after-scope, or different thread's stack).
-#if defined(__powerpc64__) && defined(__BIG_ENDIAN__)
- // On PowerPC64 ELFv1, the address of a function actually points to a
- // three-doubleword data structure with the first field containing
- // the address of the function's code.
- access.frame_pc = *reinterpret_cast<uptr *>(access.frame_pc);
-#endif
- access.frame_pc += 16;
- Printf("%s", d.EndLocation());
- StackTrace alloca_stack(&access.frame_pc, 1);
- alloca_stack.Print();
-
- InternalMmapVector<StackVarDescr> vars(16);
- if (!ParseFrameDescription(access.frame_descr, &vars)) {
- Printf("AddressSanitizer can't parse the stack frame "
- "descriptor: |%s|\n", access.frame_descr);
- // 'addr' is a stack address, so return true even if we can't parse frame
- return true;
- }
- uptr n_objects = vars.size();
- // Report the number of stack objects.
- Printf(" This frame has %zu object(s):\n", n_objects);
-
- // Report all objects in this frame.
- for (uptr i = 0; i < n_objects; i++) {
- uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
- uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
- PrintAccessAndVarIntersection(vars[i], access.offset, access_size,
- prev_var_end, next_var_beg);
- }
- Printf("HINT: this may be a false positive if your program uses "
- "some custom stack unwind mechanism or swapcontext\n");
- if (SANITIZER_WINDOWS)
- Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
- else
- Printf(" (longjmp and C++ exceptions *are* supported)\n");
-
- DescribeThread(t);
- return true;
-}
-
-static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
- uptr access_size) {
- sptr offset;
- Decorator d;
- InternalScopedString str(4096);
- str.append("%s", d.Location());
- if (chunk.AddrIsAtLeft(addr, access_size, &offset)) {
- str.append("%p is located %zd bytes to the left of", (void *)addr, offset);
- } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) {
- if (offset < 0) {
- addr -= offset;
- offset = 0;
- }
- str.append("%p is located %zd bytes to the right of", (void *)addr, offset);
- } else if (chunk.AddrIsInside(addr, access_size, &offset)) {
- str.append("%p is located %zd bytes inside of", (void*)addr, offset);
- } else {
- str.append("%p is located somewhere around (this is AddressSanitizer bug!)",
- (void *)addr);
- }
- str.append(" %zu-byte region [%p,%p)\n", chunk.UsedSize(),
- (void *)(chunk.Beg()), (void *)(chunk.End()));
- str.append("%s", d.EndLocation());
- Printf("%s", str.data());
-}
-
-void DescribeHeapAddress(uptr addr, uptr access_size) {
- AsanChunkView chunk = FindHeapChunkByAddress(addr);
- if (!chunk.IsValid()) {
- Printf("AddressSanitizer can not describe address in more detail "
- "(wild memory access suspected).\n");
- return;
- }
- DescribeAccessToHeapChunk(chunk, addr, access_size);
- CHECK(chunk.AllocTid() != kInvalidTid);
- asanThreadRegistry().CheckLocked();
- AsanThreadContext *alloc_thread =
- GetThreadContextByTidLocked(chunk.AllocTid());
- StackTrace alloc_stack = chunk.GetAllocStack();
- char tname[128];
- Decorator d;
- AsanThreadContext *free_thread = nullptr;
- if (chunk.FreeTid() != kInvalidTid) {
- free_thread = GetThreadContextByTidLocked(chunk.FreeTid());
- Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
- free_thread->tid,
- ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
- d.EndAllocation());
- StackTrace free_stack = chunk.GetFreeStack();
- free_stack.Print();
- Printf("%spreviously allocated by thread T%d%s here:%s\n",
- d.Allocation(), alloc_thread->tid,
- ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
- d.EndAllocation());
- } else {
- Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
- alloc_thread->tid,
- ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
- d.EndAllocation());
- }
- alloc_stack.Print();
- DescribeThread(GetCurrentThread());
- if (free_thread)
- DescribeThread(free_thread);
- DescribeThread(alloc_thread);
-}
-
-static void DescribeAddress(uptr addr, uptr access_size, const char *bug_type) {
- // Check if this is shadow or shadow gap.
- if (DescribeAddressIfShadow(addr))
- return;
- CHECK(AddrIsInMem(addr));
- if (DescribeAddressIfGlobal(addr, access_size, bug_type))
- return;
- if (DescribeAddressIfStack(addr, access_size))
- return;
- // Assume it is a heap address.
- DescribeHeapAddress(addr, access_size);
-}
-
-// ------------------- Thread description -------------------- {{{1
-
-void DescribeThread(AsanThreadContext *context) {
- CHECK(context);
- asanThreadRegistry().CheckLocked();
- // No need to announce the main thread.
- if (context->tid == 0 || context->announced) {
- return;
- }
- context->announced = true;
- char tname[128];
- InternalScopedString str(1024);
- str.append("Thread T%d%s", context->tid,
- ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
- if (context->parent_tid == kInvalidTid) {
- str.append(" created by unknown thread\n");
- Printf("%s", str.data());
- return;
- }
- str.append(
- " created by T%d%s here:\n", context->parent_tid,
- ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
- Printf("%s", str.data());
- StackDepotGet(context->stack_id).Print();
- // Recursively described parent thread if needed.
- if (flags()->print_full_thread_history) {
- AsanThreadContext *parent_context =
- GetThreadContextByTidLocked(context->parent_tid);
- DescribeThread(parent_context);
- }
-}
-
// -------------------- Different kinds of reports ----------------- {{{1
// Use ScopedInErrorReport to run common actions just before and
// immediately after printing error report.
class ScopedInErrorReport {
public:
- explicit ScopedInErrorReport(ReportData *report = nullptr,
- bool fatal = false) {
+ explicit ScopedInErrorReport(bool fatal = false) {
halt_on_error_ = fatal || flags()->halt_on_error;
if (lock_.TryLock()) {
- StartReporting(report);
+ StartReporting();
return;
}
@@ -667,10 +159,13 @@ class ScopedInErrorReport {
lock_.Lock();
}
- StartReporting(report);
+ StartReporting();
}
~ScopedInErrorReport() {
+ ASAN_ON_ERROR();
+ if (current_error_.IsValid()) current_error_.Print();
+
// Make sure the current thread is announced.
DescribeThread(GetCurrentThread());
// We may want to grab this lock again when printing stats.
@@ -678,9 +173,30 @@ class ScopedInErrorReport {
// Print memory stats.
if (flags()->print_stats)
__asan_print_accumulated_stats();
+
+ if (common_flags()->print_cmdline)
+ PrintCmdline();
+
+ // Copy the message buffer so that we could start logging without holding a
+ // lock that gets aquired during printing.
+ InternalScopedBuffer<char> buffer_copy(kErrorMessageBufferSize);
+ {
+ BlockingMutexLock l(&error_message_buf_mutex);
+ internal_memcpy(buffer_copy.data(),
+ error_message_buffer, kErrorMessageBufferSize);
+ }
+
+ LogFullErrorReport(buffer_copy.data());
+
if (error_report_callback) {
- error_report_callback(error_message_buffer);
+ error_report_callback(buffer_copy.data());
}
+
+ // In halt_on_error = false mode, reset the current error object (before
+ // unlocking).
+ if (!halt_on_error_)
+ internal_memset(&current_error_, 0, sizeof(current_error_));
+
CommonSanitizerReportMutex.Unlock();
reporting_thread_tid_ = kInvalidTid;
lock_.Unlock();
@@ -690,11 +206,18 @@ class ScopedInErrorReport {
}
}
+ void ReportError(const ErrorDescription &description) {
+ // Can only report one error per ScopedInErrorReport.
+ CHECK_EQ(current_error_.kind, kErrorKindInvalid);
+ current_error_ = description;
+ }
+
+ static ErrorDescription &CurrentError() {
+ return current_error_;
+ }
+
private:
- void StartReporting(ReportData *report) {
- if (report) report_data = *report;
- report_happened = true;
- ASAN_ON_ERROR();
+ void StartReporting() {
// Make sure the registry and sanitizer report mutexes are locked while
// we're printing an error report.
// We can lock them only here to avoid self-deadlock in case of
@@ -708,154 +231,69 @@ class ScopedInErrorReport {
static StaticSpinMutex lock_;
static u32 reporting_thread_tid_;
+ // Error currently being reported. This enables the destructor to interact
+ // with the debugger and point it to an error description.
+ static ErrorDescription current_error_;
bool halt_on_error_;
};
StaticSpinMutex ScopedInErrorReport::lock_;
-u32 ScopedInErrorReport::reporting_thread_tid_;
+u32 ScopedInErrorReport::reporting_thread_tid_ = kInvalidTid;
+ErrorDescription ScopedInErrorReport::current_error_;
void ReportStackOverflow(const SignalContext &sig) {
- ScopedInErrorReport in_report;
- Decorator d;
- Printf("%s", d.Warning());
- Report(
- "ERROR: AddressSanitizer: stack-overflow on address %p"
- " (pc %p bp %p sp %p T%d)\n",
- (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp,
- GetCurrentTidOrInvalid());
- Printf("%s", d.EndWarning());
- GET_STACK_TRACE_SIGNAL(sig);
- stack.Print();
- ReportErrorSummary("stack-overflow", &stack);
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorStackOverflow error(GetCurrentTidOrInvalid(), sig);
+ in_report.ReportError(error);
}
-void ReportDeadlySignal(const char *description, const SignalContext &sig) {
- ScopedInErrorReport in_report(/*report*/nullptr, /*fatal*/true);
- Decorator d;
- Printf("%s", d.Warning());
- Report(
- "ERROR: AddressSanitizer: %s on unknown address %p"
- " (pc %p bp %p sp %p T%d)\n",
- description, (void *)sig.addr, (void *)sig.pc, (void *)sig.bp,
- (void *)sig.sp, GetCurrentTidOrInvalid());
- if (sig.pc < GetPageSizeCached()) {
- Report("Hint: pc points to the zero page.\n");
- }
- Printf("%s", d.EndWarning());
- GET_STACK_TRACE_SIGNAL(sig);
- stack.Print();
- MaybeDumpInstructionBytes(sig.pc);
- Printf("AddressSanitizer can not provide additional info.\n");
- ReportErrorSummary(description, &stack);
+void ReportDeadlySignal(int signo, const SignalContext &sig) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig, signo);
+ in_report.ReportError(error);
}
void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
- Decorator d;
- Printf("%s", d.Warning());
- char tname[128];
- u32 curr_tid = GetCurrentTidOrInvalid();
- Report("ERROR: AddressSanitizer: attempting double-free on %p in "
- "thread T%d%s:\n",
- addr, curr_tid,
- ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
- Printf("%s", d.EndWarning());
- CHECK_GT(free_stack->size, 0);
- GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
- stack.Print();
- DescribeHeapAddress(addr, 1);
- ReportErrorSummary("double-free", &stack);
+ ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr);
+ in_report.ReportError(error);
}
void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
- Decorator d;
- Printf("%s", d.Warning());
- char tname[128];
- u32 curr_tid = GetCurrentTidOrInvalid();
- Report("ERROR: AddressSanitizer: new-delete-type-mismatch on %p in "
- "thread T%d%s:\n",
- addr, curr_tid,
- ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
- Printf("%s object passed to delete has wrong type:\n", d.EndWarning());
- Printf(" size of the allocated type: %zd bytes;\n"
- " size of the deallocated type: %zd bytes.\n",
- asan_mz_size(reinterpret_cast<void*>(addr)), delete_size);
- CHECK_GT(free_stack->size, 0);
- GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
- stack.Print();
- DescribeHeapAddress(addr, 1);
- ReportErrorSummary("new-delete-type-mismatch", &stack);
- Report("HINT: if you don't care about these errors you may set "
- "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
+ ErrorNewDeleteSizeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr,
+ delete_size);
+ in_report.ReportError(error);
}
void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
- Decorator d;
- Printf("%s", d.Warning());
- char tname[128];
- u32 curr_tid = GetCurrentTidOrInvalid();
- Report("ERROR: AddressSanitizer: attempting free on address "
- "which was not malloc()-ed: %p in thread T%d%s\n", addr,
- curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
- Printf("%s", d.EndWarning());
- CHECK_GT(free_stack->size, 0);
- GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
- stack.Print();
- DescribeHeapAddress(addr, 1);
- ReportErrorSummary("bad-free", &stack);
+ ErrorFreeNotMalloced error(GetCurrentTidOrInvalid(), free_stack, addr);
+ in_report.ReportError(error);
}
void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
AllocType alloc_type,
AllocType dealloc_type) {
- static const char *alloc_names[] =
- {"INVALID", "malloc", "operator new", "operator new []"};
- static const char *dealloc_names[] =
- {"INVALID", "free", "operator delete", "operator delete []"};
- CHECK_NE(alloc_type, dealloc_type);
ScopedInErrorReport in_report;
- Decorator d;
- Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n",
- alloc_names[alloc_type], dealloc_names[dealloc_type], addr);
- Printf("%s", d.EndWarning());
- CHECK_GT(free_stack->size, 0);
- GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
- stack.Print();
- DescribeHeapAddress(addr, 1);
- ReportErrorSummary("alloc-dealloc-mismatch", &stack);
- Report("HINT: if you don't care about these errors you may set "
- "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
+ ErrorAllocTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr,
+ alloc_type, dealloc_type);
+ in_report.ReportError(error);
}
void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
- Decorator d;
- Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: attempting to call "
- "malloc_usable_size() for pointer which is "
- "not owned: %p\n", addr);
- Printf("%s", d.EndWarning());
- stack->Print();
- DescribeHeapAddress(addr, 1);
- ReportErrorSummary("bad-malloc_usable_size", stack);
+ ErrorMallocUsableSizeNotOwned error(GetCurrentTidOrInvalid(), stack, addr);
+ in_report.ReportError(error);
}
void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
- Decorator d;
- Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: attempting to call "
- "__sanitizer_get_allocated_size() for pointer which is "
- "not owned: %p\n", addr);
- Printf("%s", d.EndWarning());
- stack->Print();
- DescribeHeapAddress(addr, 1);
- ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
+ ErrorSanitizerGetAllocatedSizeNotOwned error(GetCurrentTidOrInvalid(), stack,
+ addr);
+ in_report.ReportError(error);
}
void ReportStringFunctionMemoryRangesOverlap(const char *function,
@@ -863,94 +301,43 @@ void ReportStringFunctionMemoryRangesOverlap(const char *function,
const char *offset2, uptr length2,
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
- Decorator d;
- char bug_type[100];
- internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
- Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: %s: "
- "memory ranges [%p,%p) and [%p, %p) overlap\n", \
- bug_type, offset1, offset1 + length1, offset2, offset2 + length2);
- Printf("%s", d.EndWarning());
- stack->Print();
- DescribeAddress((uptr)offset1, length1, bug_type);
- DescribeAddress((uptr)offset2, length2, bug_type);
- ReportErrorSummary(bug_type, stack);
+ ErrorStringFunctionMemoryRangesOverlap error(
+ GetCurrentTidOrInvalid(), stack, (uptr)offset1, length1, (uptr)offset2,
+ length2, function);
+ in_report.ReportError(error);
}
void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
- Decorator d;
- const char *bug_type = "negative-size-param";
- Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size);
- Printf("%s", d.EndWarning());
- stack->Print();
- DescribeAddress(offset, size, bug_type);
- ReportErrorSummary(bug_type, stack);
+ ErrorStringFunctionSizeOverflow error(GetCurrentTidOrInvalid(), stack, offset,
+ size);
+ in_report.ReportError(error);
}
void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
uptr old_mid, uptr new_mid,
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
- Report("ERROR: AddressSanitizer: bad parameters to "
- "__sanitizer_annotate_contiguous_container:\n"
- " beg : %p\n"
- " end : %p\n"
- " old_mid : %p\n"
- " new_mid : %p\n",
- beg, end, old_mid, new_mid);
- uptr granularity = SHADOW_GRANULARITY;
- if (!IsAligned(beg, granularity))
- Report("ERROR: beg is not aligned by %d\n", granularity);
- stack->Print();
- ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
+ ErrorBadParamsToAnnotateContiguousContainer error(
+ GetCurrentTidOrInvalid(), stack, beg, end, old_mid, new_mid);
+ in_report.ReportError(error);
}
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2) {
ScopedInErrorReport in_report;
- Decorator d;
- Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg);
- Printf("%s", d.EndWarning());
- InternalScopedString g1_loc(256), g2_loc(256);
- PrintGlobalLocation(&g1_loc, *g1);
- PrintGlobalLocation(&g2_loc, *g2);
- Printf(" [1] size=%zd '%s' %s\n", g1->size,
- MaybeDemangleGlobalName(g1->name), g1_loc.data());
- Printf(" [2] size=%zd '%s' %s\n", g2->size,
- MaybeDemangleGlobalName(g2->name), g2_loc.data());
- if (stack_id1 && stack_id2) {
- Printf("These globals were registered at these points:\n");
- Printf(" [1]:\n");
- StackDepotGet(stack_id1).Print();
- Printf(" [2]:\n");
- StackDepotGet(stack_id2).Print();
- }
- Report("HINT: if you don't care about these errors you may set "
- "ASAN_OPTIONS=detect_odr_violation=0\n");
- InternalScopedString error_msg(256);
- error_msg.append("odr-violation: global '%s' at %s",
- MaybeDemangleGlobalName(g1->name), g1_loc.data());
- ReportErrorSummary(error_msg.data());
+ ErrorODRViolation error(GetCurrentTidOrInvalid(), g1, stack_id1, g2,
+ stack_id2);
+ in_report.ReportError(error);
}
// ----------------------- CheckForInvalidPointerPair ----------- {{{1
-static NOINLINE void
-ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) {
+static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp,
+ uptr a1, uptr a2) {
ScopedInErrorReport in_report;
- const char *bug_type = "invalid-pointer-pair";
- Decorator d;
- Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", a1, a2);
- Printf("%s", d.EndWarning());
- GET_STACK_TRACE_FATAL(pc, bp);
- stack.Print();
- DescribeAddress(a1, 1, bug_type);
- DescribeAddress(a2, 1, bug_type);
- ReportErrorSummary(bug_type, &stack);
+ ErrorInvalidPointerPair error(GetCurrentTidOrInvalid(), pc, bp, sp, a1, a2);
+ in_report.ReportError(error);
}
static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
@@ -959,26 +346,15 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
uptr a2 = reinterpret_cast<uptr>(p2);
AsanChunkView chunk1 = FindHeapChunkByAddress(a1);
AsanChunkView chunk2 = FindHeapChunkByAddress(a2);
- bool valid1 = chunk1.IsValid();
- bool valid2 = chunk2.IsValid();
- if ((valid1 != valid2) || (valid1 && valid2 && !chunk1.Eq(chunk2))) {
- GET_CALLER_PC_BP_SP; \
+ bool valid1 = chunk1.IsAllocated();
+ bool valid2 = chunk2.IsAllocated();
+ if (!valid1 || !valid2 || !chunk1.Eq(chunk2)) {
+ GET_CALLER_PC_BP_SP;
return ReportInvalidPointerPair(pc, bp, sp, a1, a2);
}
}
// ----------------------- Mac-specific reports ----------------- {{{1
-void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
- BufferedStackTrace *stack) {
- // Just print a warning here.
- Printf("free_common(%p) -- attempting to free unallocated memory.\n"
- "AddressSanitizer is ignoring this error on Mac OS now.\n",
- addr);
- PrintZoneForPointer(addr, zone_ptr, zone_name);
- stack->Print();
- DescribeHeapAddress(addr, 1);
-}
-
void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
@@ -987,22 +363,26 @@ void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
addr);
PrintZoneForPointer(addr, zone_ptr, zone_name);
stack->Print();
- DescribeHeapAddress(addr, 1);
+ DescribeAddressIfHeap(addr);
}
-void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report;
- Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
- "This is an unrecoverable problem, exiting now.\n",
- addr);
- PrintZoneForPointer(addr, zone_ptr, zone_name);
- stack->Print();
- DescribeHeapAddress(addr, 1);
+// -------------- SuppressErrorReport -------------- {{{1
+// Avoid error reports duplicating for ASan recover mode.
+static bool SuppressErrorReport(uptr pc) {
+ if (!common_flags()->suppress_equal_pcs) return false;
+ for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) {
+ uptr cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]);
+ if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp,
+ pc, memory_order_relaxed))
+ return false;
+ if (cmp == pc) return true;
+ }
+ Die();
}
void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
uptr access_size, u32 exp, bool fatal) {
+ if (!fatal && SuppressErrorReport(pc)) return;
ENABLE_FRAME_POINTER;
// Optimization experiments.
@@ -1014,87 +394,10 @@ void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
// The reaction to a non-zero value of exp is to be defined.
(void)exp;
- // Determine the error type.
- const char *bug_descr = "unknown-crash";
- if (AddrIsInMem(addr)) {
- u8 *shadow_addr = (u8*)MemToShadow(addr);
- // If we are accessing 16 bytes, look at the second shadow byte.
- if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY)
- shadow_addr++;
- // If we are in the partial right redzone, look at the next shadow byte.
- if (*shadow_addr > 0 && *shadow_addr < 128)
- shadow_addr++;
- switch (*shadow_addr) {
- case kAsanHeapLeftRedzoneMagic:
- case kAsanHeapRightRedzoneMagic:
- case kAsanArrayCookieMagic:
- bug_descr = "heap-buffer-overflow";
- break;
- case kAsanHeapFreeMagic:
- bug_descr = "heap-use-after-free";
- break;
- case kAsanStackLeftRedzoneMagic:
- bug_descr = "stack-buffer-underflow";
- break;
- case kAsanInitializationOrderMagic:
- bug_descr = "initialization-order-fiasco";
- break;
- case kAsanStackMidRedzoneMagic:
- case kAsanStackRightRedzoneMagic:
- case kAsanStackPartialRedzoneMagic:
- bug_descr = "stack-buffer-overflow";
- break;
- case kAsanStackAfterReturnMagic:
- bug_descr = "stack-use-after-return";
- break;
- case kAsanUserPoisonedMemoryMagic:
- bug_descr = "use-after-poison";
- break;
- case kAsanContiguousContainerOOBMagic:
- bug_descr = "container-overflow";
- break;
- case kAsanStackUseAfterScopeMagic:
- bug_descr = "stack-use-after-scope";
- break;
- case kAsanGlobalRedzoneMagic:
- bug_descr = "global-buffer-overflow";
- break;
- case kAsanIntraObjectRedzone:
- bug_descr = "intra-object-overflow";
- break;
- case kAsanAllocaLeftMagic:
- case kAsanAllocaRightMagic:
- bug_descr = "dynamic-stack-buffer-overflow";
- break;
- }
- }
-
- ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
- bug_descr };
- ScopedInErrorReport in_report(&report, fatal);
-
- Decorator d;
- Printf("%s", d.Warning());
- Report("ERROR: AddressSanitizer: %s on address "
- "%p at pc %p bp %p sp %p\n",
- bug_descr, (void*)addr, pc, bp, sp);
- Printf("%s", d.EndWarning());
-
- u32 curr_tid = GetCurrentTidOrInvalid();
- char tname[128];
- Printf("%s%s of size %zu at %p thread T%d%s%s\n",
- d.Access(),
- access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
- access_size, (void*)addr, curr_tid,
- ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)),
- d.EndAccess());
-
- GET_STACK_TRACE_FATAL(pc, bp);
- stack.Print();
-
- DescribeAddress(addr, access_size, bug_descr);
- ReportErrorSummary(bug_descr, &stack);
- PrintShadowMemoryForAddress(addr);
+ ScopedInErrorReport in_report(fatal);
+ ErrorGeneric error(GetCurrentTidOrInvalid(), pc, bp, sp, addr, is_write,
+ access_size);
+ in_report.ReportError(error);
}
} // namespace __asan
@@ -1110,52 +413,62 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
}
void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
+ BlockingMutexLock l(&error_message_buf_mutex);
error_report_callback = callback;
- if (callback) {
- error_message_buffer_size = 1 << 16;
- error_message_buffer =
- (char*)MmapOrDie(error_message_buffer_size, __func__);
- error_message_buffer_pos = 0;
- }
}
void __asan_describe_address(uptr addr) {
// Thread registry must be locked while we're describing an address.
asanThreadRegistry().Lock();
- DescribeAddress(addr, 1, "");
+ PrintAddressDescription(addr, 1, "");
asanThreadRegistry().Unlock();
}
int __asan_report_present() {
- return report_happened ? 1 : 0;
+ return ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric;
}
uptr __asan_get_report_pc() {
- return report_data.pc;
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.pc;
+ return 0;
}
uptr __asan_get_report_bp() {
- return report_data.bp;
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.bp;
+ return 0;
}
uptr __asan_get_report_sp() {
- return report_data.sp;
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.sp;
+ return 0;
}
uptr __asan_get_report_address() {
- return report_data.addr;
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError()
+ .Generic.addr_description.Address();
+ return 0;
}
int __asan_get_report_access_type() {
- return report_data.is_write ? 1 : 0;
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.is_write;
+ return 0;
}
uptr __asan_get_report_access_size() {
- return report_data.access_size;
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.access_size;
+ return 0;
}
const char *__asan_get_report_description() {
- return report_data.description;
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.bug_descr;
+ return nullptr;
}
extern "C" {
diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h
index f2a18155232..111b8400153 100644
--- a/libsanitizer/asan/asan_report.h
+++ b/libsanitizer/asan/asan_report.h
@@ -23,34 +23,28 @@ struct StackVarDescr {
uptr name_len;
};
-struct AddressDescription {
- char *name;
- uptr name_size;
- uptr region_address;
- uptr region_size;
- const char *region_kind;
-};
-
// Returns the number of globals close to the provided address and copies
// them to "globals" array.
int GetGlobalsForAddress(uptr addr, __asan_global *globals, u32 *reg_sites,
int max_globals);
-bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
+
+const char *MaybeDemangleGlobalName(const char *name);
+void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g);
+void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g);
+
+void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte,
+ bool in_shadow, const char *after = "\n");
+
// The following functions prints address description depending
// on the memory type (shadow/heap/stack/global).
-void DescribeHeapAddress(uptr addr, uptr access_size);
-bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr,
- bool print = true);
bool ParseFrameDescription(const char *frame_descr,
InternalMmapVector<StackVarDescr> *vars);
-bool DescribeAddressIfStack(uptr addr, uptr access_size);
-void DescribeThread(AsanThreadContext *context);
// Different kinds of error reports.
void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
uptr access_size, u32 exp, bool fatal);
void ReportStackOverflow(const SignalContext &sig);
-void ReportDeadlySignal(const char *description, const SignalContext &sig);
+void ReportDeadlySignal(int signo, const SignalContext &sig);
void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
BufferedStackTrace *free_stack);
void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
@@ -75,8 +69,6 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2);
// Mac-specific errors and warnings.
-void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
- BufferedStackTrace *stack);
void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
const char *zone_name,
BufferedStackTrace *stack);
diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc
index 50b02453a2b..38009d2905f 100644
--- a/libsanitizer/asan/asan_rtl.cc
+++ b/libsanitizer/asan/asan_rtl.cc
@@ -30,6 +30,7 @@
#include "ubsan/ubsan_init.h"
#include "ubsan/ubsan_platform.h"
+uptr __asan_shadow_memory_dynamic_address; // Global interface symbol.
int __asan_option_detect_stack_use_after_return; // Global interface symbol.
uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan.
@@ -84,8 +85,8 @@ void ShowStatsAndAbort() {
// Reserve memory range [beg, end].
// We need to use inclusive range because end+1 may not be representable.
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
- CHECK_EQ((beg % GetPageSizeCached()), 0);
- CHECK_EQ(((end + 1) % GetPageSizeCached()), 0);
+ CHECK_EQ((beg % GetMmapGranularity()), 0);
+ CHECK_EQ(((end + 1) % GetMmapGranularity()), 0);
uptr size = end - beg + 1;
DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb.
void *res = MmapFixedNoReserve(beg, size, name);
@@ -261,6 +262,7 @@ static NOINLINE void force_interface_symbols() {
volatile int fake_condition = 0; // prevent dead condition elimination.
// __asan_report_* functions are noreturn, so we need a switch to prevent
// the compiler from removing any of them.
+ // clang-format off
switch (fake_condition) {
case 1: __asan_report_load1(0); break;
case 2: __asan_report_load2(0); break;
@@ -300,7 +302,14 @@ static NOINLINE void force_interface_symbols() {
case 37: __asan_unpoison_stack_memory(0, 0); break;
case 38: __asan_region_is_poisoned(0, 0); break;
case 39: __asan_describe_address(0); break;
+ case 40: __asan_set_shadow_00(0, 0); break;
+ case 41: __asan_set_shadow_f1(0, 0); break;
+ case 42: __asan_set_shadow_f2(0, 0); break;
+ case 43: __asan_set_shadow_f3(0, 0); break;
+ case 44: __asan_set_shadow_f5(0, 0); break;
+ case 45: __asan_set_shadow_f8(0, 0); break;
}
+ // clang-format on
}
static void asan_atexit() {
@@ -318,26 +327,39 @@ static void InitializeHighMemEnd() {
kHighMemEnd = GetMaxVirtualAddress();
// Increase kHighMemEnd to make sure it's properly
// aligned together with kHighMemBeg:
- kHighMemEnd |= SHADOW_GRANULARITY * GetPageSizeCached() - 1;
+ kHighMemEnd |= SHADOW_GRANULARITY * GetMmapGranularity() - 1;
#endif // !ASAN_FIXED_MAPPING
- CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0);
+ CHECK_EQ((kHighMemBeg % GetMmapGranularity()), 0);
}
static void ProtectGap(uptr addr, uptr size) {
- if (!flags()->protect_shadow_gap)
+ if (!flags()->protect_shadow_gap) {
+ // The shadow gap is unprotected, so there is a chance that someone
+ // is actually using this memory. Which means it needs a shadow...
+ uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached());
+ uptr GapShadowEnd =
+ RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1;
+ if (Verbosity())
+ Printf("protect_shadow_gap=0:"
+ " not protecting shadow gap, allocating gap's shadow\n"
+ "|| `[%p, %p]` || ShadowGap's shadow ||\n", GapShadowBeg,
+ GapShadowEnd);
+ ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd,
+ "unprotected gap shadow");
return;
- void *res = MmapNoAccess(addr, size, "shadow gap");
+ }
+ void *res = MmapFixedNoAccess(addr, size, "shadow gap");
if (addr == (uptr)res)
return;
// A few pages at the start of the address space can not be protected.
// But we really want to protect as much as possible, to prevent this memory
// being returned as a result of a non-FIXED mmap().
if (addr == kZeroBaseShadowStart) {
- uptr step = GetPageSizeCached();
+ uptr step = GetMmapGranularity();
while (size > step && addr < kZeroBaseMaxShadowStart) {
addr += step;
size -= step;
- void *res = MmapNoAccess(addr, size, "shadow gap");
+ void *res = MmapFixedNoAccess(addr, size, "shadow gap");
if (addr == (uptr)res)
return;
}
@@ -413,10 +435,13 @@ static void AsanInitInternal() {
AsanCheckIncompatibleRT();
AsanCheckDynamicRTPrereqs();
+ AvoidCVE_2016_2143();
SetCanPoisonMemory(flags()->poison_heap);
SetMallocContextSize(common_flags()->malloc_context_size);
+ InitializePlatformExceptionHandlers();
+
InitializeHighMemEnd();
// Make sure we are not statically linked.
@@ -429,7 +454,6 @@ static void AsanInitInternal() {
__sanitizer_set_report_path(common_flags()->log_path);
- // Enable UAR detection, if required.
__asan_option_detect_stack_use_after_return =
flags()->detect_stack_use_after_return;
@@ -448,7 +472,30 @@ static void AsanInitInternal() {
ReplaceSystemMalloc();
+ // Set the shadow memory address to uninitialized.
+ __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel;
+
uptr shadow_start = kLowShadowBeg;
+ // Detect if a dynamic shadow address must used and find a available location
+ // when necessary. When dynamic address is used, the macro |kLowShadowBeg|
+ // expands to |__asan_shadow_memory_dynamic_address| which is
+ // |kDefaultShadowSentinel|.
+ if (shadow_start == kDefaultShadowSentinel) {
+ __asan_shadow_memory_dynamic_address = 0;
+ CHECK_EQ(0, kLowShadowBeg);
+
+ uptr granularity = GetMmapGranularity();
+ uptr alignment = 8 * granularity;
+ uptr left_padding = granularity;
+ uptr space_size = kHighShadowEnd + left_padding;
+
+ shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity);
+ CHECK_NE((uptr)0, shadow_start);
+ CHECK(IsAligned(shadow_start, alignment));
+ }
+ // Update the shadow memory address (potentially) used by instrumentation.
+ __asan_shadow_memory_dynamic_address = shadow_start;
+
if (kLowShadowBeg)
shadow_start -= GetMmapGranularity();
bool full_shadow_is_available =
@@ -537,12 +584,12 @@ static void AsanInitInternal() {
force_interface_symbols(); // no-op.
SanitizerInitializeUnwinder();
-#if CAN_SANITIZE_LEAKS
- __lsan::InitCommonLsan();
- if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
- Atexit(__lsan::DoLeakCheck);
+ if (CAN_SANITIZE_LEAKS) {
+ __lsan::InitCommonLsan();
+ if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
+ Atexit(__lsan::DoLeakCheck);
+ }
}
-#endif // CAN_SANITIZE_LEAKS
#if CAN_SANITIZE_UB
__ubsan::InitAsPlugin();
@@ -550,6 +597,15 @@ static void AsanInitInternal() {
InitializeSuppressions();
+ if (CAN_SANITIZE_LEAKS) {
+ // LateInitialize() calls dlsym, which can allocate an error string buffer
+ // in the TLS. Let's ignore the allocation to avoid reporting a leak.
+ __lsan::ScopedInterceptorDisabler disabler;
+ Symbolizer::LateInitialize();
+ } else {
+ Symbolizer::LateInitialize();
+ }
+
VReport(1, "AddressSanitizer Init done\n");
}
@@ -579,6 +635,9 @@ static AsanInitializer asan_initializer;
using namespace __asan; // NOLINT
void NOINLINE __asan_handle_no_return() {
+ if (asan_init_is_running)
+ return;
+
int local_stack;
AsanThread *curr_thread = GetCurrentThread();
uptr PageSize = GetPageSizeCached();
@@ -603,7 +662,7 @@ void NOINLINE __asan_handle_no_return() {
"stack top: %p; bottom %p; size: %p (%zd)\n"
"False positive error reports may follow\n"
"For details see "
- "http://code.google.com/p/address-sanitizer/issues/detail?id=189\n",
+ "https://github.com/google/sanitizers/issues/189\n",
top, bottom, top - bottom, top - bottom);
return;
}
diff --git a/libsanitizer/asan/asan_scariness_score.h b/libsanitizer/asan/asan_scariness_score.h
new file mode 100644
index 00000000000..d72fce69d64
--- /dev/null
+++ b/libsanitizer/asan/asan_scariness_score.h
@@ -0,0 +1,72 @@
+//===-- asan_scariness_score.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.
+//
+// Compute the level of scariness of the error message.
+// Don't expect any deep science here, just a set of heuristics that suggest
+// that e.g. 1-byte-read-global-buffer-overflow is less scary than
+// 8-byte-write-stack-use-after-return.
+//
+// Every error report has one or more features, such as memory access size,
+// type (read or write), type of accessed memory (e.g. free-d heap, or a global
+// redzone), etc. Every such feature has an int score and a string description.
+// The overall score is the sum of all feature scores and the description
+// is a concatenation of feature descriptions.
+// Examples:
+// 17 (4-byte-read-heap-buffer-overflow)
+// 65 (multi-byte-write-stack-use-after-return)
+// 10 (null-deref)
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ASAN_SCARINESS_SCORE_H
+#define ASAN_SCARINESS_SCORE_H
+
+#include "asan_flags.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+
+namespace __asan {
+struct ScarinessScoreBase {
+ void Clear() {
+ descr[0] = 0;
+ score = 0;
+ }
+ void Scare(int add_to_score, const char *reason) {
+ if (descr[0])
+ internal_strlcat(descr, "-", sizeof(descr));
+ internal_strlcat(descr, reason, sizeof(descr));
+ score += add_to_score;
+ };
+ int GetScore() const { return score; }
+ const char *GetDescription() const { return descr; }
+ void Print() {
+ if (score && flags()->print_scariness)
+ Printf("SCARINESS: %d (%s)\n", score, descr);
+ }
+ static void PrintSimple(int score, const char *descr) {
+ ScarinessScoreBase SSB;
+ SSB.Clear();
+ SSB.Scare(score, descr);
+ SSB.Print();
+ }
+
+ private:
+ int score;
+ char descr[1024];
+};
+
+struct ScarinessScore : ScarinessScoreBase {
+ ScarinessScore() {
+ Clear();
+ }
+};
+
+} // namespace __asan
+
+#endif // ASAN_SCARINESS_SCORE_H
diff --git a/libsanitizer/asan/asan_stack.h b/libsanitizer/asan/asan_stack.h
index 30dc5921303..2b8738156ea 100644
--- a/libsanitizer/asan/asan_stack.h
+++ b/libsanitizer/asan/asan_stack.h
@@ -46,7 +46,10 @@ void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth,
uptr stack_top = t->stack_top();
uptr stack_bottom = t->stack_bottom();
ScopedUnwinding unwind_scope(t);
- stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, fast);
+ if (!SANITIZER_MIPS || IsValidFrame(bp, stack_top, stack_bottom)) {
+ stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom,
+ fast);
+ }
} else if (!t && !fast) {
/* If GetCurrentThread() has failed, try to do slow unwind anyways. */
stack->Unwind(max_depth, pc, bp, context, 0, 0, false);
diff --git a/libsanitizer/asan/asan_suppressions.cc b/libsanitizer/asan/asan_suppressions.cc
index c94fff0500a..1dc9d474240 100644
--- a/libsanitizer/asan/asan_suppressions.cc
+++ b/libsanitizer/asan/asan_suppressions.cc
@@ -87,6 +87,7 @@ bool IsStackTraceSuppressed(const StackTrace *stack) {
if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) {
SymbolizedStack *frames = symbolizer->SymbolizePC(addr);
+ CHECK(frames);
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
const char *function_name = cur->info.function;
if (!function_name) {
diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc
index 92f968bdee4..818e1261400 100644
--- a/libsanitizer/asan/asan_thread.cc
+++ b/libsanitizer/asan/asan_thread.cc
@@ -118,6 +118,77 @@ void AsanThread::Destroy() {
DTLS_Destroy();
}
+void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom,
+ uptr size) {
+ if (atomic_load(&stack_switching_, memory_order_relaxed)) {
+ Report("ERROR: starting fiber switch while in fiber switch\n");
+ Die();
+ }
+
+ next_stack_bottom_ = bottom;
+ next_stack_top_ = bottom + size;
+ atomic_store(&stack_switching_, 1, memory_order_release);
+
+ FakeStack *current_fake_stack = fake_stack_;
+ if (fake_stack_save)
+ *fake_stack_save = fake_stack_;
+ fake_stack_ = nullptr;
+ SetTLSFakeStack(nullptr);
+ // if fake_stack_save is null, the fiber will die, delete the fakestack
+ if (!fake_stack_save && current_fake_stack)
+ current_fake_stack->Destroy(this->tid());
+}
+
+void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save,
+ uptr *bottom_old,
+ uptr *size_old) {
+ if (!atomic_load(&stack_switching_, memory_order_relaxed)) {
+ Report("ERROR: finishing a fiber switch that has not started\n");
+ Die();
+ }
+
+ if (fake_stack_save) {
+ SetTLSFakeStack(fake_stack_save);
+ fake_stack_ = fake_stack_save;
+ }
+
+ if (bottom_old)
+ *bottom_old = stack_bottom_;
+ if (size_old)
+ *size_old = stack_top_ - stack_bottom_;
+ stack_bottom_ = next_stack_bottom_;
+ stack_top_ = next_stack_top_;
+ atomic_store(&stack_switching_, 0, memory_order_release);
+ next_stack_top_ = 0;
+ next_stack_bottom_ = 0;
+}
+
+inline AsanThread::StackBounds AsanThread::GetStackBounds() const {
+ if (!atomic_load(&stack_switching_, memory_order_acquire))
+ return StackBounds{stack_bottom_, stack_top_}; // NOLINT
+ char local;
+ const uptr cur_stack = (uptr)&local;
+ // Note: need to check next stack first, because FinishSwitchFiber
+ // may be in process of overwriting stack_top_/bottom_. But in such case
+ // we are already on the next stack.
+ if (cur_stack >= next_stack_bottom_ && cur_stack < next_stack_top_)
+ return StackBounds{next_stack_bottom_, next_stack_top_}; // NOLINT
+ return StackBounds{stack_bottom_, stack_top_}; // NOLINT
+}
+
+uptr AsanThread::stack_top() {
+ return GetStackBounds().top;
+}
+
+uptr AsanThread::stack_bottom() {
+ return GetStackBounds().bottom;
+}
+
+uptr AsanThread::stack_size() {
+ const auto bounds = GetStackBounds();
+ return bounds.top - bounds.bottom;
+}
+
// We want to create the FakeStack lazyly on the first use, but not eralier
// than the stack size is known and the procedure has to be async-signal safe.
FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
@@ -148,6 +219,8 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
}
void AsanThread::Init() {
+ next_stack_top_ = next_stack_bottom_ = 0;
+ atomic_store(&stack_switching_, false, memory_order_release);
fake_stack_ = nullptr; // Will be initialized lazily if needed.
CHECK_EQ(this->stack_size(), 0U);
SetThreadStackAndTls();
@@ -193,10 +266,12 @@ thread_return_t AsanThread::ThreadStart(
void AsanThread::SetThreadStackAndTls() {
uptr tls_size = 0;
- GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size_, &tls_begin_,
- &tls_size);
- stack_top_ = stack_bottom_ + stack_size_;
+ uptr stack_size = 0;
+ GetThreadStackAndTls(tid() == 0, const_cast<uptr *>(&stack_bottom_),
+ const_cast<uptr *>(&stack_size), &tls_begin_, &tls_size);
+ stack_top_ = stack_bottom_ + stack_size;
tls_end_ = tls_begin_ + tls_size;
+ dtls_ = DTLS_Get();
int local;
CHECK(AddrIsInStack((uptr)&local));
@@ -247,6 +322,11 @@ bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
return true;
}
+bool AsanThread::AddrIsInStack(uptr addr) {
+ const auto bounds = GetStackBounds();
+ return addr >= bounds.bottom && addr < bounds.top;
+}
+
static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
void *addr) {
AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
@@ -269,7 +349,7 @@ AsanThread *GetCurrentThread() {
// limits, so only do this magic on Android, and only if the found thread
// is the main thread.
AsanThreadContext *tctx = GetThreadContextByTidLocked(0);
- if (ThreadStackContainsAddress(tctx, &context)) {
+ if (tctx && ThreadStackContainsAddress(tctx, &context)) {
SetCurrentThread(tctx->thread);
return tctx->thread;
}
@@ -320,8 +400,8 @@ __asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) {
// --- Implementation of LSan-specific functions --- {{{1
namespace __lsan {
bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
- uptr *tls_begin, uptr *tls_end,
- uptr *cache_begin, uptr *cache_end) {
+ uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
+ uptr *cache_end, DTLS **dtls) {
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
if (!t) return false;
*stack_begin = t->stack_bottom();
@@ -331,6 +411,7 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
// ASan doesn't keep allocator caches in TLS, so these are unused.
*cache_begin = 0;
*cache_end = 0;
+ *dtls = t->dtls();
return true;
}
@@ -353,3 +434,33 @@ void EnsureMainThreadIDIsCorrect() {
__asan::EnsureMainThreadIDIsCorrect();
}
} // namespace __lsan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom,
+ uptr size) {
+ AsanThread *t = GetCurrentThread();
+ if (!t) {
+ VReport(1, "__asan_start_switch_fiber called from unknown thread\n");
+ return;
+ }
+ t->StartSwitchFiber((FakeStack**)fakestacksave, (uptr)bottom, size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_finish_switch_fiber(void* fakestack,
+ const void **bottom_old,
+ uptr *size_old) {
+ AsanThread *t = GetCurrentThread();
+ if (!t) {
+ VReport(1, "__asan_finish_switch_fiber called from unknown thread\n");
+ return;
+ }
+ t->FinishSwitchFiber((FakeStack*)fakestack,
+ (uptr*)bottom_old,
+ (uptr*)size_old);
+}
+}
diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h
index 27a3367d8e7..c51a58ad0bb 100644
--- a/libsanitizer/asan/asan_thread.h
+++ b/libsanitizer/asan/asan_thread.h
@@ -21,6 +21,10 @@
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_thread_registry.h"
+namespace __sanitizer {
+struct DTLS;
+} // namespace __sanitizer
+
namespace __asan {
const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits.
@@ -60,11 +64,12 @@ class AsanThread {
thread_return_t ThreadStart(uptr os_id,
atomic_uintptr_t *signal_thread_is_registered);
- uptr stack_top() { return stack_top_; }
- uptr stack_bottom() { return stack_bottom_; }
- uptr stack_size() { return stack_size_; }
+ uptr stack_top();
+ uptr stack_bottom();
+ uptr stack_size();
uptr tls_begin() { return tls_begin_; }
uptr tls_end() { return tls_end_; }
+ DTLS *dtls() { return dtls_; }
u32 tid() { return context_->tid; }
AsanThreadContext *context() { return context_; }
void set_context(AsanThreadContext *context) { context_ = context; }
@@ -76,9 +81,7 @@ class AsanThread {
};
bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access);
- bool AddrIsInStack(uptr addr) {
- return addr >= stack_bottom_ && addr < stack_top_;
- }
+ bool AddrIsInStack(uptr addr);
void DeleteFakeStack(int tid) {
if (!fake_stack_) return;
@@ -88,13 +91,20 @@ class AsanThread {
t->Destroy(tid);
}
+ void StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, uptr size);
+ void FinishSwitchFiber(FakeStack *fake_stack_save, uptr *bottom_old,
+ uptr *size_old);
+
bool has_fake_stack() {
- return (reinterpret_cast<uptr>(fake_stack_) > 1);
+ return !atomic_load(&stack_switching_, memory_order_relaxed) &&
+ (reinterpret_cast<uptr>(fake_stack_) > 1);
}
FakeStack *fake_stack() {
if (!__asan_option_detect_stack_use_after_return)
return nullptr;
+ if (atomic_load(&stack_switching_, memory_order_relaxed))
+ return nullptr;
if (!has_fake_stack())
return AsyncSignalSafeLazyInitFakeStack();
return fake_stack_;
@@ -120,16 +130,27 @@ class AsanThread {
void ClearShadowForThreadStackAndTLS();
FakeStack *AsyncSignalSafeLazyInitFakeStack();
+ struct StackBounds {
+ uptr bottom;
+ uptr top;
+ };
+ StackBounds GetStackBounds() const;
+
AsanThreadContext *context_;
thread_callback_t start_routine_;
void *arg_;
+
uptr stack_top_;
uptr stack_bottom_;
- // stack_size_ == stack_top_ - stack_bottom_;
- // It needs to be set in a async-signal-safe manner.
- uptr stack_size_;
+ // these variables are used when the thread is about to switch stack
+ uptr next_stack_top_;
+ uptr next_stack_bottom_;
+ // true if switching is in progress
+ atomic_uint8_t stack_switching_;
+
uptr tls_begin_;
uptr tls_end_;
+ DTLS *dtls_;
FakeStack *fake_stack_;
AsanThreadLocalMallocStorage malloc_storage_;
diff --git a/libsanitizer/asan/asan_win.cc b/libsanitizer/asan/asan_win.cc
index 6c12523498a..efd82bfc3b0 100644
--- a/libsanitizer/asan/asan_win.cc
+++ b/libsanitizer/asan/asan_win.cc
@@ -22,6 +22,7 @@
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_thread.h"
+#include "asan_mapping.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.h"
@@ -34,7 +35,13 @@ int __asan_should_detect_stack_use_after_return() {
return __asan_option_detect_stack_use_after_return;
}
-// -------------------- A workaround for the abscence of weak symbols ----- {{{
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_shadow_memory_dynamic_address() {
+ __asan_init();
+ return __asan_shadow_memory_dynamic_address;
+}
+
+// -------------------- A workaround for the absence of weak symbols ----- {{{
// We don't have a direct equivalent of weak symbols when using MSVC, but we can
// use the /alternatename directive to tell the linker to default a specific
// symbol to a specific value, which works nicely for allocator hooks and
@@ -44,21 +51,49 @@ void __sanitizer_default_free_hook(void *ptr) { }
const char* __asan_default_default_options() { return ""; }
const char* __asan_default_default_suppressions() { return ""; }
void __asan_default_on_error() {}
+// 64-bit msvc will not prepend an underscore for symbols.
+#ifdef _WIN64
+#pragma comment(linker, "/alternatename:__sanitizer_malloc_hook=__sanitizer_default_malloc_hook") // NOLINT
+#pragma comment(linker, "/alternatename:__sanitizer_free_hook=__sanitizer_default_free_hook") // NOLINT
+#pragma comment(linker, "/alternatename:__asan_default_options=__asan_default_default_options") // NOLINT
+#pragma comment(linker, "/alternatename:__asan_default_suppressions=__asan_default_default_suppressions") // NOLINT
+#pragma comment(linker, "/alternatename:__asan_on_error=__asan_default_on_error") // NOLINT
+#else
#pragma comment(linker, "/alternatename:___sanitizer_malloc_hook=___sanitizer_default_malloc_hook") // NOLINT
#pragma comment(linker, "/alternatename:___sanitizer_free_hook=___sanitizer_default_free_hook") // NOLINT
#pragma comment(linker, "/alternatename:___asan_default_options=___asan_default_default_options") // NOLINT
#pragma comment(linker, "/alternatename:___asan_default_suppressions=___asan_default_default_suppressions") // NOLINT
#pragma comment(linker, "/alternatename:___asan_on_error=___asan_default_on_error") // NOLINT
+#endif
// }}}
} // extern "C"
-// ---------------------- Windows-specific inteceptors ---------------- {{{
+// ---------------------- Windows-specific interceptors ---------------- {{{
+INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) {
+ CHECK(REAL(RtlRaiseException));
+ // This is a noreturn function, unless it's one of the exceptions raised to
+ // communicate with the debugger, such as the one from OutputDebugString.
+ if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
+ __asan_handle_no_return();
+ REAL(RtlRaiseException)(ExceptionRecord);
+}
+
INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
CHECK(REAL(RaiseException));
__asan_handle_no_return();
REAL(RaiseException)(a, b, c, d);
}
+#ifdef _WIN64
+
+INTERCEPTOR_WINAPI(int, __C_specific_handler, void *a, void *b, void *c, void *d) { // NOLINT
+ CHECK(REAL(__C_specific_handler));
+ __asan_handle_no_return();
+ return REAL(__C_specific_handler)(a, b, c, d);
+}
+
+#else
+
INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
CHECK(REAL(_except_handler3));
__asan_handle_no_return();
@@ -74,6 +109,7 @@ INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
__asan_handle_no_return();
return REAL(_except_handler4)(a, b, c, d);
}
+#endif
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
AsanThread *t = (AsanThread*)arg;
@@ -99,52 +135,33 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
asan_thread_start, t, thr_flags, tid);
}
-namespace {
-BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED);
-
-void EnsureWorkerThreadRegistered() {
- // FIXME: GetCurrentThread relies on TSD, which might not play well with
- // system thread pools. We might want to use something like reference
- // counting to zero out GetCurrentThread() underlying storage when the last
- // work item finishes? Or can we disable reclaiming of threads in the pool?
- BlockingMutexLock l(&mu_for_thread_tracking);
- if (__asan::GetCurrentThread())
- return;
-
- AsanThread *t = AsanThread::Create(
- /* start_routine */ nullptr, /* arg */ nullptr,
- /* parent_tid */ -1, /* stack */ nullptr, /* detached */ true);
- t->Init();
- asanThreadRegistry().StartThread(t->tid(), 0, 0);
- SetCurrentThread(t);
-}
-} // namespace
-
-INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) {
- // NtWaitForWorkViaWorkerFactory is called from system worker pool threads to
- // query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc.
- // System worker pool threads are created at arbitraty point in time and
- // without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory
- // instead and don't register a specific parent_tid/stack.
- EnsureWorkerThreadRegistered();
- return REAL(NtWaitForWorkViaWorkerFactory)(a, b);
-}
-
// }}}
namespace __asan {
void InitializePlatformInterceptors() {
ASAN_INTERCEPT_FUNC(CreateThread);
- ASAN_INTERCEPT_FUNC(RaiseException);
+
+#ifdef _WIN64
+ ASAN_INTERCEPT_FUNC(__C_specific_handler);
+#else
ASAN_INTERCEPT_FUNC(_except_handler3);
ASAN_INTERCEPT_FUNC(_except_handler4);
+#endif
+
+ // Try to intercept kernel32!RaiseException, and if that fails, intercept
+ // ntdll!RtlRaiseException instead.
+ if (!::__interception::OverrideFunction("RaiseException",
+ (uptr)WRAP(RaiseException),
+ (uptr *)&REAL(RaiseException))) {
+ CHECK(::__interception::OverrideFunction("RtlRaiseException",
+ (uptr)WRAP(RtlRaiseException),
+ (uptr *)&REAL(RtlRaiseException)));
+ }
+}
- // NtWaitForWorkViaWorkerFactory is always linked dynamically.
- CHECK(::__interception::OverrideFunction(
- "NtWaitForWorkViaWorkerFactory",
- (uptr)WRAP(NtWaitForWorkViaWorkerFactory),
- (uptr *)&REAL(NtWaitForWorkViaWorkerFactory)));
+void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
+ UNIMPLEMENTED();
}
// ---------------------- TSD ---------------- {{{
@@ -173,14 +190,6 @@ void PlatformTSDDtor(void *tsd) {
// }}}
// ---------------------- Various stuff ---------------- {{{
-void DisableReexec() {
- // No need to re-exec on Windows.
-}
-
-void MaybeReexec() {
- // No need to re-exec on Windows.
-}
-
void *AsanDoesNotSupportStaticLinkage() {
#if defined(_DEBUG)
#error Please build the runtime with a non-debug CRT: /MD or /MT
@@ -200,20 +209,95 @@ void AsanOnDeadlySignal(int, void *siginfo, void *context) {
UNIMPLEMENTED();
}
+#if SANITIZER_WINDOWS64
+// Exception handler for dealing with shadow memory.
+static LONG CALLBACK
+ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
+ uptr page_size = GetPageSizeCached();
+ // Only handle access violations.
+ if (exception_pointers->ExceptionRecord->ExceptionCode !=
+ EXCEPTION_ACCESS_VIOLATION) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ // Only handle access violations that land within the shadow memory.
+ uptr addr =
+ (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]);
+
+ // Check valid shadow range.
+ if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH;
+
+ // This is an access violation while trying to read from the shadow. Commit
+ // the relevant page and let execution continue.
+
+ // Determine the address of the page that is being accessed.
+ uptr page = RoundDownTo(addr, page_size);
+
+ // Query the existing page.
+ MEMORY_BASIC_INFORMATION mem_info = {};
+ if (::VirtualQuery((LPVOID)page, &mem_info, sizeof(mem_info)) == 0)
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ // Commit the page.
+ uptr result =
+ (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE);
+ if (result != page) return EXCEPTION_CONTINUE_SEARCH;
+
+ // The page mapping succeeded, so continue execution as usual.
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+#endif
+
+void InitializePlatformExceptionHandlers() {
+#if SANITIZER_WINDOWS64
+ // On Win64, we map memory on demand with access violation handler.
+ // Install our exception handler.
+ CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler));
+#endif
+}
+
static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
+// Check based on flags if we should report this exception.
+static bool ShouldReportDeadlyException(unsigned code) {
+ switch (code) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_IN_PAGE_ERROR:
+ return common_flags()->handle_segv;
+ case EXCEPTION_BREAKPOINT:
+ case EXCEPTION_ILLEGAL_INSTRUCTION: {
+ return common_flags()->handle_sigill;
+ }
+ }
+ return false;
+}
+
+// Return the textual name for this exception.
+const char *DescribeSignalOrException(int signo) {
+ unsigned code = signo;
+ // Get the string description of the exception if this is a known deadly
+ // exception.
+ switch (code) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ return "access-violation";
+ case EXCEPTION_IN_PAGE_ERROR:
+ return "in-page-error";
+ case EXCEPTION_BREAKPOINT:
+ return "breakpoint";
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ return "illegal-instruction";
+ }
+ return nullptr;
+}
+
static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
CONTEXT *context = info->ContextRecord;
- if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
- exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
- const char *description =
- (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
- ? "access-violation"
- : "in-page-error";
+ if (ShouldReportDeadlyException(exception_record->ExceptionCode)) {
SignalContext sig = SignalContext::Create(exception_record, context);
- ReportDeadlySignal(description, sig);
+ ReportDeadlySignal(exception_record->ExceptionCode, sig);
}
// FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
@@ -248,10 +332,16 @@ int __asan_set_seh_filter() {
}
#if !ASAN_DYNAMIC
-// Put a pointer to __asan_set_seh_filter at the end of the global list
-// of C initializers, after the default EH is set by the CRT.
-#pragma section(".CRT$XIZ", long, read) // NOLINT
-__declspec(allocate(".CRT$XIZ"))
+// The CRT runs initializers in this order:
+// - C initializers, from XIA to XIZ
+// - C++ initializers, from XCA to XCZ
+// Prior to 2015, the CRT set the unhandled exception filter at priority XIY,
+// near the end of C initialization. Starting in 2015, it was moved to the
+// beginning of C++ initialization. We set our priority to XCAB to run
+// immediately after the CRT runs. This way, our exception filter is called
+// first and we can delegate to their filter if appropriate.
+#pragma section(".CRT$XCAB", long, read) // NOLINT
+__declspec(allocate(".CRT$XCAB"))
int (*__intercept_seh)() = __asan_set_seh_filter;
#endif
// }}}
diff --git a/libsanitizer/asan/asan_win_dll_thunk.cc b/libsanitizer/asan/asan_win_dll_thunk.cc
index 691aaf36fd0..f7c9a37bf79 100644
--- a/libsanitizer/asan/asan_win_dll_thunk.cc
+++ b/libsanitizer/asan/asan_win_dll_thunk.cc
@@ -10,16 +10,16 @@
// This file defines a family of thunks that should be statically linked into
// the DLLs that have ASan instrumentation in order to delegate the calls to the
// shared runtime that lives in the main binary.
-// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the
-// details.
+// See https://github.com/google/sanitizers/issues/209 for the details.
//===----------------------------------------------------------------------===//
-// Only compile this code when buidling asan_dll_thunk.lib
+// Only compile this code when building asan_dll_thunk.lib
// Using #ifdef rather than relying on Makefiles etc.
// simplifies the build procedure.
#ifdef ASAN_DLL_THUNK
#include "asan_init_version.h"
#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_platform_interceptors.h"
// ---------- Function interception helper functions and macros ----------- {{{1
extern "C" {
@@ -28,6 +28,8 @@ void *__stdcall GetProcAddress(void *module, const char *proc_name);
void abort();
}
+using namespace __sanitizer;
+
static uptr getRealProcAddressOrDie(const char *name) {
uptr ret =
__interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name);
@@ -196,9 +198,11 @@ static void InterceptHooks();
// Don't use the INTERFACE_FUNCTION machinery for this function as we actually
// want to call it in the __asan_init interceptor.
WRAP_W_V(__asan_should_detect_stack_use_after_return)
+WRAP_W_V(__asan_get_shadow_memory_dynamic_address)
extern "C" {
int __asan_option_detect_stack_use_after_return;
+ uptr __asan_shadow_memory_dynamic_address;
// Manually wrap __asan_init as we need to initialize
// __asan_option_detect_stack_use_after_return afterwards.
@@ -212,7 +216,8 @@ extern "C" {
fn();
__asan_option_detect_stack_use_after_return =
(__asan_should_detect_stack_use_after_return() != 0);
-
+ __asan_shadow_memory_dynamic_address =
+ (uptr)__asan_get_shadow_memory_dynamic_address();
InterceptHooks();
}
}
@@ -255,6 +260,13 @@ INTERFACE_FUNCTION(__asan_memcpy);
INTERFACE_FUNCTION(__asan_memset);
INTERFACE_FUNCTION(__asan_memmove);
+INTERFACE_FUNCTION(__asan_set_shadow_00);
+INTERFACE_FUNCTION(__asan_set_shadow_f1);
+INTERFACE_FUNCTION(__asan_set_shadow_f2);
+INTERFACE_FUNCTION(__asan_set_shadow_f3);
+INTERFACE_FUNCTION(__asan_set_shadow_f5);
+INTERFACE_FUNCTION(__asan_set_shadow_f8);
+
INTERFACE_FUNCTION(__asan_alloca_poison);
INTERFACE_FUNCTION(__asan_allocas_unpoison);
@@ -309,8 +321,6 @@ INTERFACE_FUNCTION(__sanitizer_cov_init)
INTERFACE_FUNCTION(__sanitizer_cov_module_init)
INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block)
INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter)
-INTERFACE_FUNCTION(__sanitizer_cov_trace_cmp)
-INTERFACE_FUNCTION(__sanitizer_cov_trace_switch)
INTERFACE_FUNCTION(__sanitizer_cov_with_check)
INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
INTERFACE_FUNCTION(__sanitizer_get_coverage_guards)
@@ -324,6 +334,8 @@ INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)
INTERFACE_FUNCTION(__sanitizer_print_stack_trace)
+INTERFACE_FUNCTION(__sanitizer_symbolize_pc)
+INTERFACE_FUNCTION(__sanitizer_symbolize_global)
INTERFACE_FUNCTION(__sanitizer_ptr_cmp)
INTERFACE_FUNCTION(__sanitizer_ptr_sub)
INTERFACE_FUNCTION(__sanitizer_report_error_summary)
@@ -333,6 +345,7 @@ INTERFACE_FUNCTION(__sanitizer_update_counter_bitset_and_clear_counters)
INTERFACE_FUNCTION(__sanitizer_sandbox_on_notify)
INTERFACE_FUNCTION(__sanitizer_set_death_callback)
INTERFACE_FUNCTION(__sanitizer_set_report_path)
+INTERFACE_FUNCTION(__sanitizer_set_report_fd)
INTERFACE_FUNCTION(__sanitizer_unaligned_load16)
INTERFACE_FUNCTION(__sanitizer_unaligned_load32)
INTERFACE_FUNCTION(__sanitizer_unaligned_load64)
@@ -340,23 +353,31 @@ INTERFACE_FUNCTION(__sanitizer_unaligned_store16)
INTERFACE_FUNCTION(__sanitizer_unaligned_store32)
INTERFACE_FUNCTION(__sanitizer_unaligned_store64)
INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container)
+INTERFACE_FUNCTION(__sanitizer_install_malloc_and_free_hooks)
+INTERFACE_FUNCTION(__sanitizer_start_switch_fiber)
+INTERFACE_FUNCTION(__sanitizer_finish_switch_fiber)
// TODO(timurrrr): Add more interface functions on the as-needed basis.
// ----------------- Memory allocation functions ---------------------
WRAP_V_W(free)
+WRAP_V_W(_free_base)
WRAP_V_WW(_free_dbg)
WRAP_W_W(malloc)
+WRAP_W_W(_malloc_base)
WRAP_W_WWWW(_malloc_dbg)
WRAP_W_WW(calloc)
+WRAP_W_WW(_calloc_base)
WRAP_W_WWWWW(_calloc_dbg)
WRAP_W_WWW(_calloc_impl)
WRAP_W_WW(realloc)
+WRAP_W_WW(_realloc_base)
WRAP_W_WWW(_realloc_dbg)
WRAP_W_WWW(_recalloc)
+WRAP_W_WWW(_recalloc_base)
WRAP_W_W(_msize)
WRAP_W_W(_expand)
@@ -369,6 +390,10 @@ WRAP_W_W(_expand_dbg)
INTERCEPT_LIBRARY_FUNCTION(atoi);
INTERCEPT_LIBRARY_FUNCTION(atol);
+
+#ifdef _WIN64
+INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler);
+#else
INTERCEPT_LIBRARY_FUNCTION(_except_handler3);
// _except_handler4 checks -GS cookie which is different for each module, so we
@@ -377,10 +402,13 @@ INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
__asan_handle_no_return();
return REAL(_except_handler4)(a, b, c, d);
}
+#endif
INTERCEPT_LIBRARY_FUNCTION(frexp);
INTERCEPT_LIBRARY_FUNCTION(longjmp);
+#if SANITIZER_INTERCEPT_MEMCHR
INTERCEPT_LIBRARY_FUNCTION(memchr);
+#endif
INTERCEPT_LIBRARY_FUNCTION(memcmp);
INTERCEPT_LIBRARY_FUNCTION(memcpy);
INTERCEPT_LIBRARY_FUNCTION(memmove);
@@ -390,12 +418,14 @@ INTERCEPT_LIBRARY_FUNCTION(strchr);
INTERCEPT_LIBRARY_FUNCTION(strcmp);
INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT
INTERCEPT_LIBRARY_FUNCTION(strcspn);
+INTERCEPT_LIBRARY_FUNCTION(strdup);
INTERCEPT_LIBRARY_FUNCTION(strlen);
INTERCEPT_LIBRARY_FUNCTION(strncat);
INTERCEPT_LIBRARY_FUNCTION(strncmp);
INTERCEPT_LIBRARY_FUNCTION(strncpy);
INTERCEPT_LIBRARY_FUNCTION(strnlen);
INTERCEPT_LIBRARY_FUNCTION(strpbrk);
+INTERCEPT_LIBRARY_FUNCTION(strrchr);
INTERCEPT_LIBRARY_FUNCTION(strspn);
INTERCEPT_LIBRARY_FUNCTION(strstr);
INTERCEPT_LIBRARY_FUNCTION(strtol);
@@ -405,7 +435,9 @@ INTERCEPT_LIBRARY_FUNCTION(wcslen);
// is defined.
void InterceptHooks() {
INTERCEPT_HOOKS();
+#ifndef _WIN64
INTERCEPT_FUNCTION(_except_handler4);
+#endif
}
// We want to call __asan_init before C/C++ initializers/constructors are
diff --git a/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc b/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc
index f7358539a1c..8989159cc52 100644
--- a/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc
+++ b/libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc
@@ -1,4 +1,4 @@
-//===-- asan_win_uar_thunk.cc ---------------------------------------------===//
+//===-- asan_win_dynamic_runtime_thunk.cc ---------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
@@ -14,11 +14,11 @@
// This includes:
// - forwarding the detect_stack_use_after_return runtime option
// - working around deficiencies of the MD runtime
-// - installing a custom SEH handlerx
+// - installing a custom SEH handler
//
//===----------------------------------------------------------------------===//
-// Only compile this code when buidling asan_dynamic_runtime_thunk.lib
+// Only compile this code when building asan_dynamic_runtime_thunk.lib
// Using #ifdef rather than relying on Makefiles etc.
// simplifies the build procedure.
#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
@@ -27,7 +27,7 @@
// First, declare CRT sections we'll be using in this file
#pragma section(".CRT$XID", long, read) // NOLINT
-#pragma section(".CRT$XIZ", long, read) // NOLINT
+#pragma section(".CRT$XCAB", long, read) // NOLINT
#pragma section(".CRT$XTW", long, read) // NOLINT
#pragma section(".CRT$XTY", long, read) // NOLINT
@@ -40,12 +40,16 @@
// attribute adds __imp_ prefix to the symbol name of a variable.
// Since in general we don't know if a given TU is going to be used
// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
-// just to work around this issue, let's clone the a variable that is
-// constant after initialization anyways.
+// just to work around this issue, let's clone the variable that is constant
+// after initialization anyways.
extern "C" {
__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
int __asan_option_detect_stack_use_after_return =
__asan_should_detect_stack_use_after_return();
+
+__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address();
+void* __asan_shadow_memory_dynamic_address =
+ __asan_get_shadow_memory_dynamic_address();
}
////////////////////////////////////////////////////////////////////////////////
@@ -57,6 +61,7 @@ int __asan_option_detect_stack_use_after_return =
// using atexit() that calls a small subset of C terminators
// where LLVM global_dtors is placed. Fingers crossed, no other C terminators
// are there.
+extern "C" int __cdecl atexit(void (__cdecl *f)(void));
extern "C" void __cdecl _initterm(void *a, void *b);
namespace {
@@ -70,6 +75,7 @@ void UnregisterGlobals() {
int ScheduleUnregisterGlobals() {
return atexit(UnregisterGlobals);
}
+} // namespace
// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after
// atexit() is initialized (.CRT$XIC). As this is executed before C++
@@ -78,8 +84,6 @@ int ScheduleUnregisterGlobals() {
__declspec(allocate(".CRT$XID"))
int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
-} // namespace
-
////////////////////////////////////////////////////////////////////////////////
// ASan SEH handling.
// We need to set the ASan-specific SEH handler at the end of CRT initialization
@@ -90,7 +94,8 @@ static int SetSEHFilter() { return __asan_set_seh_filter(); }
// Unfortunately, putting a pointer to __asan_set_seh_filter into
// __asan_intercept_seh gets optimized out, so we have to use an extra function.
-__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter;
+__declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() =
+ SetSEHFilter;
}
#endif // ASAN_DYNAMIC_RUNTIME_THUNK
diff --git a/libsanitizer/asan/libtool-version b/libsanitizer/asan/libtool-version
index 7e838a5740b..0f14ee34185 100644
--- a/libsanitizer/asan/libtool-version
+++ b/libsanitizer/asan/libtool-version
@@ -3,4 +3,4 @@
# a separate file so that version updates don't involve re-running
# automake.
# CURRENT:REVISION:AGE
-3:0:0
+4:0:0