mirror of git://gcc.gnu.org/git/gcc.git
[libsanitizer merge from upstream r218156]
From-SVN: r215527
This commit is contained in:
parent
e8ee40544a
commit
866e32ad33
|
@ -1,3 +1,12 @@
|
||||||
|
2014-09-23 Kostya Serebryany <kcc@google.com>
|
||||||
|
|
||||||
|
Update to match the changed asan API.
|
||||||
|
* asan.c (asan_global_struct): Update the __asan_global definition
|
||||||
|
to match the new API.
|
||||||
|
(asan_add_global): Ditto.
|
||||||
|
* sanitizer.def (BUILT_IN_ASAN_INIT): Rename __asan_init_v3
|
||||||
|
to __asan_init_v4.
|
||||||
|
|
||||||
2014-09-23 Michael Meissner <meissner@linux.vnet.ibm.com>
|
2014-09-23 Michael Meissner <meissner@linux.vnet.ibm.com>
|
||||||
|
|
||||||
* config/rs6000/rs6000.md (f32_vsx): New mode attributes to
|
* config/rs6000/rs6000.md (f32_vsx): New mode attributes to
|
||||||
|
|
14
gcc/asan.c
14
gcc/asan.c
|
@ -230,6 +230,9 @@ along with GCC; see the file COPYING3. If not see
|
||||||
|
|
||||||
// 1 if it has dynamic initialization, 0 otherwise.
|
// 1 if it has dynamic initialization, 0 otherwise.
|
||||||
uptr __has_dynamic_init;
|
uptr __has_dynamic_init;
|
||||||
|
|
||||||
|
// A pointer to struct that contains source location, could be NULL.
|
||||||
|
__asan_global_source_location *__location;
|
||||||
}
|
}
|
||||||
|
|
||||||
A destructor function that calls the runtime asan library function
|
A destructor function that calls the runtime asan library function
|
||||||
|
@ -2136,19 +2139,20 @@ asan_dynamic_init_call (bool after_p)
|
||||||
const void *__name;
|
const void *__name;
|
||||||
const void *__module_name;
|
const void *__module_name;
|
||||||
uptr __has_dynamic_init;
|
uptr __has_dynamic_init;
|
||||||
|
__asan_global_source_location *__location;
|
||||||
} type. */
|
} type. */
|
||||||
|
|
||||||
static tree
|
static tree
|
||||||
asan_global_struct (void)
|
asan_global_struct (void)
|
||||||
{
|
{
|
||||||
static const char *field_names[6]
|
static const char *field_names[7]
|
||||||
= { "__beg", "__size", "__size_with_redzone",
|
= { "__beg", "__size", "__size_with_redzone",
|
||||||
"__name", "__module_name", "__has_dynamic_init" };
|
"__name", "__module_name", "__has_dynamic_init", "__location"};
|
||||||
tree fields[6], ret;
|
tree fields[7], ret;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
ret = make_node (RECORD_TYPE);
|
ret = make_node (RECORD_TYPE);
|
||||||
for (i = 0; i < 6; i++)
|
for (i = 0; i < 7; i++)
|
||||||
{
|
{
|
||||||
fields[i]
|
fields[i]
|
||||||
= build_decl (UNKNOWN_LOCATION, FIELD_DECL,
|
= build_decl (UNKNOWN_LOCATION, FIELD_DECL,
|
||||||
|
@ -2220,6 +2224,8 @@ asan_add_global (tree decl, tree type, vec<constructor_elt, va_gc> *v)
|
||||||
int has_dynamic_init = vnode ? vnode->dynamically_initialized : 0;
|
int has_dynamic_init = vnode ? vnode->dynamically_initialized : 0;
|
||||||
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
|
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
|
||||||
build_int_cst (uptr, has_dynamic_init));
|
build_int_cst (uptr, has_dynamic_init));
|
||||||
|
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
|
||||||
|
build_int_cst (uptr, 0));
|
||||||
init = build_constructor (type, vinner);
|
init = build_constructor (type, vinner);
|
||||||
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init);
|
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ along with GCC; see the file COPYING3. If not see
|
||||||
for other FEs by asan.c. */
|
for other FEs by asan.c. */
|
||||||
|
|
||||||
/* Address Sanitizer */
|
/* Address Sanitizer */
|
||||||
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init_v3",
|
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init_v4",
|
||||||
BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
|
BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
|
||||||
/* Do not reorder the BUILT_IN_ASAN_{REPORT,CHECK}* builtins, e.g. cfgcleanup.c
|
/* Do not reorder the BUILT_IN_ASAN_{REPORT,CHECK}* builtins, e.g. cfgcleanup.c
|
||||||
relies on this order. */
|
relies on this order. */
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
2014-09-19 Kostya Serebryany <kcc@google.com>
|
||||||
|
|
||||||
|
* All source files: Merge from upstream r218156.
|
||||||
|
* asan/Makefile.am (asan_files): Added new files.
|
||||||
|
* asan/Makefile.in: Regenerate.
|
||||||
|
* ubsan/Makefile.am (ubsan_files): Added new files.
|
||||||
|
* ubsan/Makefile.in: Regenerate.
|
||||||
|
* tsan/Makefile.am (tsan_files): Added new files.
|
||||||
|
* tsan/Makefile.in: Regenerate.
|
||||||
|
* sanitizer_common/Makefile.am (sanitizer_common_files): Added new
|
||||||
|
files.
|
||||||
|
* sanitizer_common/Makefile.in: Regenerate.
|
||||||
|
* asan/libtool-version: Bump the libasan SONAME.
|
||||||
|
|
||||||
2014-09-10 Jakub Jelinek <jakub@redhat.com>
|
2014-09-10 Jakub Jelinek <jakub@redhat.com>
|
||||||
|
|
||||||
* ubsan/ubsan_handlers.cc, ubsan/ubsan_handlers.h: Cherry pick
|
* ubsan/ubsan_handlers.cc, ubsan/ubsan_handlers.h: Cherry pick
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
209283
|
218156
|
||||||
|
|
||||||
The first line of this file holds the svn revision number of the
|
The first line of this file holds the svn revision number of the
|
||||||
last merge done from the master library sources.
|
last merge done from the master library sources.
|
||||||
|
|
|
@ -17,7 +17,7 @@ nodist_toolexeclib_HEADERS = libasan_preinit.o
|
||||||
asan_files = \
|
asan_files = \
|
||||||
asan_activation.cc \
|
asan_activation.cc \
|
||||||
asan_allocator2.cc \
|
asan_allocator2.cc \
|
||||||
asan_dll_thunk.cc \
|
asan_debugging.cc \
|
||||||
asan_fake_stack.cc \
|
asan_fake_stack.cc \
|
||||||
asan_globals.cc \
|
asan_globals.cc \
|
||||||
asan_interceptors.cc \
|
asan_interceptors.cc \
|
||||||
|
@ -34,7 +34,9 @@ asan_files = \
|
||||||
asan_stack.cc \
|
asan_stack.cc \
|
||||||
asan_stats.cc \
|
asan_stats.cc \
|
||||||
asan_thread.cc \
|
asan_thread.cc \
|
||||||
asan_win.cc
|
asan_win.cc \
|
||||||
|
asan_win_dll_thunk.cc \
|
||||||
|
asan_win_dynamic_runtime_thunk.cc
|
||||||
|
|
||||||
libasan_la_SOURCES = $(asan_files)
|
libasan_la_SOURCES = $(asan_files)
|
||||||
libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la
|
libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la
|
||||||
|
|
|
@ -89,12 +89,13 @@ libasan_la_DEPENDENCIES = \
|
||||||
$(top_builddir)/lsan/libsanitizer_lsan.la $(am__append_2) \
|
$(top_builddir)/lsan/libsanitizer_lsan.la $(am__append_2) \
|
||||||
$(am__append_3) $(am__DEPENDENCIES_1)
|
$(am__append_3) $(am__DEPENDENCIES_1)
|
||||||
am__objects_1 = asan_activation.lo asan_allocator2.lo \
|
am__objects_1 = asan_activation.lo asan_allocator2.lo \
|
||||||
asan_dll_thunk.lo asan_fake_stack.lo asan_globals.lo \
|
asan_debugging.lo asan_fake_stack.lo asan_globals.lo \
|
||||||
asan_interceptors.lo asan_linux.lo asan_mac.lo \
|
asan_interceptors.lo asan_linux.lo asan_mac.lo \
|
||||||
asan_malloc_linux.lo asan_malloc_mac.lo asan_malloc_win.lo \
|
asan_malloc_linux.lo asan_malloc_mac.lo asan_malloc_win.lo \
|
||||||
asan_new_delete.lo asan_poisoning.lo asan_posix.lo \
|
asan_new_delete.lo asan_poisoning.lo asan_posix.lo \
|
||||||
asan_report.lo asan_rtl.lo asan_stack.lo asan_stats.lo \
|
asan_report.lo asan_rtl.lo asan_stack.lo asan_stats.lo \
|
||||||
asan_thread.lo asan_win.lo
|
asan_thread.lo asan_win.lo asan_win_dll_thunk.lo \
|
||||||
|
asan_win_dynamic_runtime_thunk.lo
|
||||||
am_libasan_la_OBJECTS = $(am__objects_1)
|
am_libasan_la_OBJECTS = $(am__objects_1)
|
||||||
libasan_la_OBJECTS = $(am_libasan_la_OBJECTS)
|
libasan_la_OBJECTS = $(am_libasan_la_OBJECTS)
|
||||||
libasan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
|
libasan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
|
||||||
|
@ -275,7 +276,7 @@ nodist_toolexeclib_HEADERS = libasan_preinit.o
|
||||||
asan_files = \
|
asan_files = \
|
||||||
asan_activation.cc \
|
asan_activation.cc \
|
||||||
asan_allocator2.cc \
|
asan_allocator2.cc \
|
||||||
asan_dll_thunk.cc \
|
asan_debugging.cc \
|
||||||
asan_fake_stack.cc \
|
asan_fake_stack.cc \
|
||||||
asan_globals.cc \
|
asan_globals.cc \
|
||||||
asan_interceptors.cc \
|
asan_interceptors.cc \
|
||||||
|
@ -292,7 +293,9 @@ asan_files = \
|
||||||
asan_stack.cc \
|
asan_stack.cc \
|
||||||
asan_stats.cc \
|
asan_stats.cc \
|
||||||
asan_thread.cc \
|
asan_thread.cc \
|
||||||
asan_win.cc
|
asan_win.cc \
|
||||||
|
asan_win_dll_thunk.cc \
|
||||||
|
asan_win_dynamic_runtime_thunk.cc
|
||||||
|
|
||||||
libasan_la_SOURCES = $(asan_files)
|
libasan_la_SOURCES = $(asan_files)
|
||||||
libasan_la_LIBADD = \
|
libasan_la_LIBADD = \
|
||||||
|
@ -416,7 +419,7 @@ distclean-compile:
|
||||||
|
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator2.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator2.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_dll_thunk.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_debugging.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@
|
||||||
|
@ -434,6 +437,8 @@ distclean-compile:
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dll_thunk.Plo@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@
|
||||||
|
|
||||||
.cc.o:
|
.cc.o:
|
||||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||||
|
|
|
@ -140,6 +140,8 @@ struct AsanThreadLocalMallocStorage {
|
||||||
void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
|
void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
|
||||||
AllocType alloc_type);
|
AllocType alloc_type);
|
||||||
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
|
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
|
||||||
|
void asan_sized_free(void *ptr, uptr size, StackTrace *stack,
|
||||||
|
AllocType alloc_type);
|
||||||
|
|
||||||
void *asan_malloc(uptr size, StackTrace *stack);
|
void *asan_malloc(uptr size, StackTrace *stack);
|
||||||
void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack);
|
void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "asan_report.h"
|
#include "asan_report.h"
|
||||||
#include "asan_stack.h"
|
#include "asan_stack.h"
|
||||||
#include "asan_thread.h"
|
#include "asan_thread.h"
|
||||||
|
#include "sanitizer_common/sanitizer_allocator_interface.h"
|
||||||
#include "sanitizer_common/sanitizer_flags.h"
|
#include "sanitizer_common/sanitizer_flags.h"
|
||||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||||
#include "sanitizer_common/sanitizer_list.h"
|
#include "sanitizer_common/sanitizer_list.h"
|
||||||
|
@ -165,23 +166,6 @@ struct AsanChunk: ChunkBase {
|
||||||
}
|
}
|
||||||
return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
|
return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
|
||||||
}
|
}
|
||||||
// If we don't use stack depot, we store the alloc/free stack traces
|
|
||||||
// in the chunk itself.
|
|
||||||
u32 *AllocStackBeg() {
|
|
||||||
return (u32*)(Beg() - RZLog2Size(rz_log));
|
|
||||||
}
|
|
||||||
uptr AllocStackSize() {
|
|
||||||
CHECK_LE(RZLog2Size(rz_log), kChunkHeaderSize);
|
|
||||||
return (RZLog2Size(rz_log) - kChunkHeaderSize) / sizeof(u32);
|
|
||||||
}
|
|
||||||
u32 *FreeStackBeg() {
|
|
||||||
return (u32*)(Beg() + kChunkHeader2Size);
|
|
||||||
}
|
|
||||||
uptr FreeStackSize() {
|
|
||||||
if (user_requested_size < kChunkHeader2Size) return 0;
|
|
||||||
uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY);
|
|
||||||
return (available - kChunkHeader2Size) / sizeof(u32);
|
|
||||||
}
|
|
||||||
bool AddrIsInside(uptr addr, bool locked_version = false) {
|
bool AddrIsInside(uptr addr, bool locked_version = false) {
|
||||||
return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
|
return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
|
||||||
}
|
}
|
||||||
|
@ -461,12 +445,17 @@ static void QuarantineChunk(AsanChunk *m, void *ptr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
|
static void Deallocate(void *ptr, uptr delete_size, StackTrace *stack,
|
||||||
|
AllocType alloc_type) {
|
||||||
uptr p = reinterpret_cast<uptr>(ptr);
|
uptr p = reinterpret_cast<uptr>(ptr);
|
||||||
if (p == 0) return;
|
if (p == 0) return;
|
||||||
|
|
||||||
uptr chunk_beg = p - kChunkHeaderSize;
|
uptr chunk_beg = p - kChunkHeaderSize;
|
||||||
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
|
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
|
||||||
|
if (delete_size && flags()->new_delete_type_mismatch &&
|
||||||
|
delete_size != m->UsedSize()) {
|
||||||
|
ReportNewDeleteSizeMismatch(p, delete_size, stack);
|
||||||
|
}
|
||||||
ASAN_FREE_HOOK(ptr);
|
ASAN_FREE_HOOK(ptr);
|
||||||
// Must mark the chunk as quarantined before any changes to its metadata.
|
// Must mark the chunk as quarantined before any changes to its metadata.
|
||||||
AtomicallySetQuarantineFlag(m, ptr, stack);
|
AtomicallySetQuarantineFlag(m, ptr, stack);
|
||||||
|
@ -493,7 +482,7 @@ static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
|
||||||
// If realloc() races with free(), we may start copying freed memory.
|
// If realloc() races with free(), we may start copying freed memory.
|
||||||
// However, we will report racy double-free later anyway.
|
// However, we will report racy double-free later anyway.
|
||||||
REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
|
REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
|
||||||
Deallocate(old_ptr, stack, FROM_MALLOC);
|
Deallocate(old_ptr, 0, stack, FROM_MALLOC);
|
||||||
}
|
}
|
||||||
return new_ptr;
|
return new_ptr;
|
||||||
}
|
}
|
||||||
|
@ -592,7 +581,12 @@ void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
|
||||||
}
|
}
|
||||||
|
|
||||||
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
|
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
|
||||||
Deallocate(ptr, stack, alloc_type);
|
Deallocate(ptr, 0, stack, alloc_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void asan_sized_free(void *ptr, uptr size, StackTrace *stack,
|
||||||
|
AllocType alloc_type) {
|
||||||
|
Deallocate(ptr, size, stack, alloc_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *asan_malloc(uptr size, StackTrace *stack) {
|
void *asan_malloc(uptr size, StackTrace *stack) {
|
||||||
|
@ -614,7 +608,7 @@ void *asan_realloc(void *p, uptr size, StackTrace *stack) {
|
||||||
if (p == 0)
|
if (p == 0)
|
||||||
return Allocate(size, 8, stack, FROM_MALLOC, true);
|
return Allocate(size, 8, stack, FROM_MALLOC, true);
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
Deallocate(p, stack, FROM_MALLOC);
|
Deallocate(p, 0, stack, FROM_MALLOC);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return Reallocate(p, size, stack);
|
return Reallocate(p, size, stack);
|
||||||
|
@ -758,23 +752,23 @@ using namespace __asan; // NOLINT
|
||||||
|
|
||||||
// ASan allocator doesn't reserve extra bytes, so normally we would
|
// ASan allocator doesn't reserve extra bytes, so normally we would
|
||||||
// just return "size". We don't want to expose our redzone sizes, etc here.
|
// just return "size". We don't want to expose our redzone sizes, etc here.
|
||||||
uptr __asan_get_estimated_allocated_size(uptr size) {
|
uptr __sanitizer_get_estimated_allocated_size(uptr size) {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __asan_get_ownership(const void *p) {
|
int __sanitizer_get_ownership(const void *p) {
|
||||||
uptr ptr = reinterpret_cast<uptr>(p);
|
uptr ptr = reinterpret_cast<uptr>(p);
|
||||||
return (AllocationSize(ptr) > 0);
|
return (AllocationSize(ptr) > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uptr __asan_get_allocated_size(const void *p) {
|
uptr __sanitizer_get_allocated_size(const void *p) {
|
||||||
if (p == 0) return 0;
|
if (p == 0) return 0;
|
||||||
uptr ptr = reinterpret_cast<uptr>(p);
|
uptr ptr = reinterpret_cast<uptr>(p);
|
||||||
uptr allocated_size = AllocationSize(ptr);
|
uptr allocated_size = AllocationSize(ptr);
|
||||||
// Die if p is not malloced or if it is already freed.
|
// Die if p is not malloced or if it is already freed.
|
||||||
if (allocated_size == 0) {
|
if (allocated_size == 0) {
|
||||||
GET_STACK_TRACE_FATAL_HERE;
|
GET_STACK_TRACE_FATAL_HERE;
|
||||||
ReportAsanGetAllocatedSizeNotOwned(ptr, &stack);
|
ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack);
|
||||||
}
|
}
|
||||||
return allocated_size;
|
return allocated_size;
|
||||||
}
|
}
|
||||||
|
@ -783,12 +777,12 @@ uptr __asan_get_allocated_size(const void *p) {
|
||||||
// Provide default (no-op) implementation of malloc hooks.
|
// Provide default (no-op) implementation of malloc hooks.
|
||||||
extern "C" {
|
extern "C" {
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||||
void __asan_malloc_hook(void *ptr, uptr size) {
|
void __sanitizer_malloc_hook(void *ptr, uptr size) {
|
||||||
(void)ptr;
|
(void)ptr;
|
||||||
(void)size;
|
(void)size;
|
||||||
}
|
}
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||||
void __asan_free_hook(void *ptr) {
|
void __sanitizer_free_hook(void *ptr) {
|
||||||
(void)ptr;
|
(void)ptr;
|
||||||
}
|
}
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
|
@ -1,599 +0,0 @@
|
||||||
// This file was generated by gen_asm_instrumentation.sh. Please, do not edit
|
|
||||||
.section .text
|
|
||||||
#if defined(__x86_64__) || defined(__i386__)
|
|
||||||
.globl __asan_report_store1
|
|
||||||
.globl __asan_report_load1
|
|
||||||
.globl __asan_report_store2
|
|
||||||
.globl __asan_report_load2
|
|
||||||
.globl __asan_report_store4
|
|
||||||
.globl __asan_report_load4
|
|
||||||
.globl __asan_report_store8
|
|
||||||
.globl __asan_report_load8
|
|
||||||
.globl __asan_report_store16
|
|
||||||
.globl __asan_report_load16
|
|
||||||
#endif // defined(__x86_64__) || defined(__i386__)
|
|
||||||
#if defined(__i386__)
|
|
||||||
// Sanitize 1-byte store. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store1
|
|
||||||
.type __sanitizer_sanitize_store1, @function
|
|
||||||
__sanitizer_sanitize_store1:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushl %edx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
movb 0x20000000(%ecx), %cl
|
|
||||||
testb %cl, %cl
|
|
||||||
je .sanitize_store1_done
|
|
||||||
movl %eax, %edx
|
|
||||||
andl $0x7, %edx
|
|
||||||
movsbl %cl, %ecx
|
|
||||||
cmpl %ecx, %edx
|
|
||||||
jl .sanitize_store1_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store1@PLT
|
|
||||||
.sanitize_store1_done:
|
|
||||||
popfl
|
|
||||||
popl %edx
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
// Sanitize 1-byte load. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load1
|
|
||||||
.type __sanitizer_sanitize_load1, @function
|
|
||||||
__sanitizer_sanitize_load1:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushl %edx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
movb 0x20000000(%ecx), %cl
|
|
||||||
testb %cl, %cl
|
|
||||||
je .sanitize_load1_done
|
|
||||||
movl %eax, %edx
|
|
||||||
andl $0x7, %edx
|
|
||||||
movsbl %cl, %ecx
|
|
||||||
cmpl %ecx, %edx
|
|
||||||
jl .sanitize_load1_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load1@PLT
|
|
||||||
.sanitize_load1_done:
|
|
||||||
popfl
|
|
||||||
popl %edx
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
// Sanitize 2-byte store. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store2
|
|
||||||
.type __sanitizer_sanitize_store2, @function
|
|
||||||
__sanitizer_sanitize_store2:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushl %edx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
movb 0x20000000(%ecx), %cl
|
|
||||||
testb %cl, %cl
|
|
||||||
je .sanitize_store2_done
|
|
||||||
movl %eax, %edx
|
|
||||||
andl $0x7, %edx
|
|
||||||
incl %edx
|
|
||||||
movsbl %cl, %ecx
|
|
||||||
cmpl %ecx, %edx
|
|
||||||
jl .sanitize_store2_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store2@PLT
|
|
||||||
.sanitize_store2_done:
|
|
||||||
popfl
|
|
||||||
popl %edx
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
// Sanitize 2-byte load. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load2
|
|
||||||
.type __sanitizer_sanitize_load2, @function
|
|
||||||
__sanitizer_sanitize_load2:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushl %edx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
movb 0x20000000(%ecx), %cl
|
|
||||||
testb %cl, %cl
|
|
||||||
je .sanitize_load2_done
|
|
||||||
movl %eax, %edx
|
|
||||||
andl $0x7, %edx
|
|
||||||
incl %edx
|
|
||||||
movsbl %cl, %ecx
|
|
||||||
cmpl %ecx, %edx
|
|
||||||
jl .sanitize_load2_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load2@PLT
|
|
||||||
.sanitize_load2_done:
|
|
||||||
popfl
|
|
||||||
popl %edx
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
// Sanitize 4-byte store. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store4
|
|
||||||
.type __sanitizer_sanitize_store4, @function
|
|
||||||
__sanitizer_sanitize_store4:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushl %edx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
movb 0x20000000(%ecx), %cl
|
|
||||||
testb %cl, %cl
|
|
||||||
je .sanitize_store4_done
|
|
||||||
movl %eax, %edx
|
|
||||||
andl $0x7, %edx
|
|
||||||
addl $0x3, %edx
|
|
||||||
movsbl %cl, %ecx
|
|
||||||
cmpl %ecx, %edx
|
|
||||||
jl .sanitize_store4_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store4@PLT
|
|
||||||
.sanitize_store4_done:
|
|
||||||
popfl
|
|
||||||
popl %edx
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
// Sanitize 4-byte load. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load4
|
|
||||||
.type __sanitizer_sanitize_load4, @function
|
|
||||||
__sanitizer_sanitize_load4:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushl %edx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
movb 0x20000000(%ecx), %cl
|
|
||||||
testb %cl, %cl
|
|
||||||
je .sanitize_load4_done
|
|
||||||
movl %eax, %edx
|
|
||||||
andl $0x7, %edx
|
|
||||||
addl $0x3, %edx
|
|
||||||
movsbl %cl, %ecx
|
|
||||||
cmpl %ecx, %edx
|
|
||||||
jl .sanitize_load4_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load4@PLT
|
|
||||||
.sanitize_load4_done:
|
|
||||||
popfl
|
|
||||||
popl %edx
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
// Sanitize 8-byte store. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store8
|
|
||||||
.type __sanitizer_sanitize_store8, @function
|
|
||||||
__sanitizer_sanitize_store8:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
cmpb $0x0, 0x20000000(%ecx)
|
|
||||||
je .sanitize_store8_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store8@PLT
|
|
||||||
.sanitize_store8_done:
|
|
||||||
popfl
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
// Sanitize 8-byte load. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load8
|
|
||||||
.type __sanitizer_sanitize_load8, @function
|
|
||||||
__sanitizer_sanitize_load8:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
cmpb $0x0, 0x20000000(%ecx)
|
|
||||||
je .sanitize_load8_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load8@PLT
|
|
||||||
.sanitize_load8_done:
|
|
||||||
popfl
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
// Sanitize 16-byte store. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store16
|
|
||||||
.type __sanitizer_sanitize_store16, @function
|
|
||||||
__sanitizer_sanitize_store16:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
cmpw $0x0, 0x20000000(%ecx)
|
|
||||||
je .sanitize_store16_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store16@PLT
|
|
||||||
.sanitize_store16_done:
|
|
||||||
popfl
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
// Sanitize 16-byte load. Takes one 4-byte address as an argument on
|
|
||||||
// stack, nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load16
|
|
||||||
.type __sanitizer_sanitize_load16, @function
|
|
||||||
__sanitizer_sanitize_load16:
|
|
||||||
pushl %ebp
|
|
||||||
movl %esp, %ebp
|
|
||||||
pushl %eax
|
|
||||||
pushl %ecx
|
|
||||||
pushfl
|
|
||||||
movl 8(%ebp), %eax
|
|
||||||
movl %eax, %ecx
|
|
||||||
shrl $0x3, %ecx
|
|
||||||
cmpw $0x0, 0x20000000(%ecx)
|
|
||||||
je .sanitize_load16_done
|
|
||||||
pushl %eax
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load16@PLT
|
|
||||||
.sanitize_load16_done:
|
|
||||||
popfl
|
|
||||||
popl %ecx
|
|
||||||
popl %eax
|
|
||||||
leave
|
|
||||||
ret
|
|
||||||
#endif // defined(__i386__)
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
// Sanitize 1-byte store. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store1
|
|
||||||
.type __sanitizer_sanitize_store1, @function
|
|
||||||
__sanitizer_sanitize_store1:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushq %rcx
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
movb 0x7fff8000(%rax), %al
|
|
||||||
test %al, %al
|
|
||||||
je .sanitize_store1_done
|
|
||||||
movl %edi, %ecx
|
|
||||||
andl $0x7, %ecx
|
|
||||||
movsbl %al, %eax
|
|
||||||
cmpl %eax, %ecx
|
|
||||||
jl .sanitize_store1_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store1@PLT
|
|
||||||
.sanitize_store1_done:
|
|
||||||
popfq
|
|
||||||
popq %rcx
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
// Sanitize 1-byte load. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load1
|
|
||||||
.type __sanitizer_sanitize_load1, @function
|
|
||||||
__sanitizer_sanitize_load1:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushq %rcx
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
movb 0x7fff8000(%rax), %al
|
|
||||||
test %al, %al
|
|
||||||
je .sanitize_load1_done
|
|
||||||
movl %edi, %ecx
|
|
||||||
andl $0x7, %ecx
|
|
||||||
movsbl %al, %eax
|
|
||||||
cmpl %eax, %ecx
|
|
||||||
jl .sanitize_load1_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load1@PLT
|
|
||||||
.sanitize_load1_done:
|
|
||||||
popfq
|
|
||||||
popq %rcx
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
// Sanitize 2-byte store. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store2
|
|
||||||
.type __sanitizer_sanitize_store2, @function
|
|
||||||
__sanitizer_sanitize_store2:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushq %rcx
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
movb 0x7fff8000(%rax), %al
|
|
||||||
test %al, %al
|
|
||||||
je .sanitize_store2_done
|
|
||||||
movl %edi, %ecx
|
|
||||||
andl $0x7, %ecx
|
|
||||||
incl %ecx
|
|
||||||
movsbl %al, %eax
|
|
||||||
cmpl %eax, %ecx
|
|
||||||
jl .sanitize_store2_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store2@PLT
|
|
||||||
.sanitize_store2_done:
|
|
||||||
popfq
|
|
||||||
popq %rcx
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
// Sanitize 2-byte load. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load2
|
|
||||||
.type __sanitizer_sanitize_load2, @function
|
|
||||||
__sanitizer_sanitize_load2:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushq %rcx
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
movb 0x7fff8000(%rax), %al
|
|
||||||
test %al, %al
|
|
||||||
je .sanitize_load2_done
|
|
||||||
movl %edi, %ecx
|
|
||||||
andl $0x7, %ecx
|
|
||||||
incl %ecx
|
|
||||||
movsbl %al, %eax
|
|
||||||
cmpl %eax, %ecx
|
|
||||||
jl .sanitize_load2_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load2@PLT
|
|
||||||
.sanitize_load2_done:
|
|
||||||
popfq
|
|
||||||
popq %rcx
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
// Sanitize 4-byte store. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store4
|
|
||||||
.type __sanitizer_sanitize_store4, @function
|
|
||||||
__sanitizer_sanitize_store4:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushq %rcx
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
movb 0x7fff8000(%rax), %al
|
|
||||||
test %al, %al
|
|
||||||
je .sanitize_store4_done
|
|
||||||
movl %edi, %ecx
|
|
||||||
andl $0x7, %ecx
|
|
||||||
addl $0x3, %ecx
|
|
||||||
movsbl %al, %eax
|
|
||||||
cmpl %eax, %ecx
|
|
||||||
jl .sanitize_store4_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store4@PLT
|
|
||||||
.sanitize_store4_done:
|
|
||||||
popfq
|
|
||||||
popq %rcx
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
// Sanitize 4-byte load. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load4
|
|
||||||
.type __sanitizer_sanitize_load4, @function
|
|
||||||
__sanitizer_sanitize_load4:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushq %rcx
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
movb 0x7fff8000(%rax), %al
|
|
||||||
test %al, %al
|
|
||||||
je .sanitize_load4_done
|
|
||||||
movl %edi, %ecx
|
|
||||||
andl $0x7, %ecx
|
|
||||||
addl $0x3, %ecx
|
|
||||||
movsbl %al, %eax
|
|
||||||
cmpl %eax, %ecx
|
|
||||||
jl .sanitize_load4_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load4@PLT
|
|
||||||
.sanitize_load4_done:
|
|
||||||
popfq
|
|
||||||
popq %rcx
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
// Sanitize 8-byte store. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store8
|
|
||||||
.type __sanitizer_sanitize_store8, @function
|
|
||||||
__sanitizer_sanitize_store8:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
cmpb $0x0, 0x7fff8000(%rax)
|
|
||||||
je .sanitize_store8_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store8@PLT
|
|
||||||
.sanitize_store8_done:
|
|
||||||
popfq
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
// Sanitize 8-byte load. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load8
|
|
||||||
.type __sanitizer_sanitize_load8, @function
|
|
||||||
__sanitizer_sanitize_load8:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
cmpb $0x0, 0x7fff8000(%rax)
|
|
||||||
je .sanitize_load8_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load8@PLT
|
|
||||||
.sanitize_load8_done:
|
|
||||||
popfq
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
// Sanitize 16-byte store. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_store16
|
|
||||||
.type __sanitizer_sanitize_store16, @function
|
|
||||||
__sanitizer_sanitize_store16:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
cmpw $0x0, 0x7fff8000(%rax)
|
|
||||||
je .sanitize_store16_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_store16@PLT
|
|
||||||
.sanitize_store16_done:
|
|
||||||
popfq
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
// Sanitize 16-byte load. Takes one 8-byte address as an argument in %rdi,
|
|
||||||
// nothing is returned.
|
|
||||||
.globl __sanitizer_sanitize_load16
|
|
||||||
.type __sanitizer_sanitize_load16, @function
|
|
||||||
__sanitizer_sanitize_load16:
|
|
||||||
leaq -128(%rsp), %rsp
|
|
||||||
pushq %rax
|
|
||||||
pushfq
|
|
||||||
movq %rdi, %rax
|
|
||||||
shrq $0x3, %rax
|
|
||||||
cmpw $0x0, 0x7fff8000(%rax)
|
|
||||||
je .sanitize_load16_done
|
|
||||||
subq $8, %rsp
|
|
||||||
andq $-16, %rsp
|
|
||||||
cld
|
|
||||||
emms
|
|
||||||
call __asan_report_load16@PLT
|
|
||||||
.sanitize_load16_done:
|
|
||||||
popfq
|
|
||||||
popq %rax
|
|
||||||
leaq 128(%rsp), %rsp
|
|
||||||
ret
|
|
||||||
#endif // defined(__x86_64__)
|
|
||||||
/* We do not need executable stack. */
|
|
||||||
#if defined(__arm__)
|
|
||||||
.section .note.GNU-stack,"",%progbits
|
|
||||||
#else
|
|
||||||
.section .note.GNU-stack,"",@progbits
|
|
||||||
#endif // defined(__arm__)
|
|
||||||
#endif // __linux__
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
//===-- asan_debugging.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 contains various functions that are generally useful to call when
|
||||||
|
// using a debugger (LLDB, GDB).
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "asan_allocator.h"
|
||||||
|
#include "asan_flags.h"
|
||||||
|
#include "asan_internal.h"
|
||||||
|
#include "asan_mapping.h"
|
||||||
|
#include "asan_thread.h"
|
||||||
|
|
||||||
|
namespace __asan {
|
||||||
|
|
||||||
|
uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id,
|
||||||
|
bool alloc_stack) {
|
||||||
|
AsanChunkView chunk = FindHeapChunkByAddress(addr);
|
||||||
|
if (!chunk.IsValid()) return 0;
|
||||||
|
|
||||||
|
StackTrace stack;
|
||||||
|
if (alloc_stack) {
|
||||||
|
if (chunk.AllocTid() == kInvalidTid) return 0;
|
||||||
|
chunk.GetAllocStack(&stack);
|
||||||
|
if (thread_id) *thread_id = chunk.AllocTid();
|
||||||
|
} else {
|
||||||
|
if (chunk.FreeTid() == kInvalidTid) return 0;
|
||||||
|
chunk.GetFreeStack(&stack);
|
||||||
|
if (thread_id) *thread_id = chunk.FreeTid();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trace && size) {
|
||||||
|
if (size > kStackTraceMax)
|
||||||
|
size = kStackTraceMax;
|
||||||
|
if (size > stack.size)
|
||||||
|
size = stack.size;
|
||||||
|
for (uptr i = 0; i < size; i++)
|
||||||
|
trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]);
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __asan
|
||||||
|
|
||||||
|
using namespace __asan;
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
|
||||||
|
return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true);
|
||||||
|
}
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
|
||||||
|
return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset) {
|
||||||
|
if (shadow_scale)
|
||||||
|
*shadow_scale = SHADOW_SCALE;
|
||||||
|
if (shadow_offset)
|
||||||
|
*shadow_offset = SHADOW_OFFSET;
|
||||||
|
}
|
|
@ -50,12 +50,13 @@ struct Flags {
|
||||||
bool print_stats;
|
bool print_stats;
|
||||||
bool print_legend;
|
bool print_legend;
|
||||||
bool atexit;
|
bool atexit;
|
||||||
bool disable_core;
|
|
||||||
bool allow_reexec;
|
bool allow_reexec;
|
||||||
bool print_full_thread_history;
|
bool print_full_thread_history;
|
||||||
bool poison_heap;
|
bool poison_heap;
|
||||||
bool poison_partial;
|
bool poison_partial;
|
||||||
|
bool poison_array_cookie;
|
||||||
bool alloc_dealloc_mismatch;
|
bool alloc_dealloc_mismatch;
|
||||||
|
bool new_delete_type_mismatch;
|
||||||
bool strict_memcmp;
|
bool strict_memcmp;
|
||||||
bool strict_init_order;
|
bool strict_init_order;
|
||||||
bool start_deactivated;
|
bool start_deactivated;
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "sanitizer_common/sanitizer_common.h"
|
#include "sanitizer_common/sanitizer_common.h"
|
||||||
#include "sanitizer_common/sanitizer_mutex.h"
|
#include "sanitizer_common/sanitizer_mutex.h"
|
||||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||||
|
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||||
|
|
||||||
namespace __asan {
|
namespace __asan {
|
||||||
|
|
||||||
|
@ -43,6 +44,14 @@ typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals;
|
||||||
// Lazy-initialized and never deleted.
|
// Lazy-initialized and never deleted.
|
||||||
static VectorOfGlobals *dynamic_init_globals;
|
static VectorOfGlobals *dynamic_init_globals;
|
||||||
|
|
||||||
|
// We want to remember where a certain range of globals was registered.
|
||||||
|
struct GlobalRegistrationSite {
|
||||||
|
u32 stack_id;
|
||||||
|
Global *g_first, *g_last;
|
||||||
|
};
|
||||||
|
typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector;
|
||||||
|
static GlobalRegistrationSiteVector *global_registration_site_vector;
|
||||||
|
|
||||||
ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
|
ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
|
||||||
FastPoisonShadow(g->beg, g->size_with_redzone, value);
|
FastPoisonShadow(g->beg, g->size_with_redzone, value);
|
||||||
}
|
}
|
||||||
|
@ -61,9 +70,14 @@ ALWAYS_INLINE void PoisonRedZones(const Global &g) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReportGlobal(const Global &g, const char *prefix) {
|
static void ReportGlobal(const Global &g, const char *prefix) {
|
||||||
Report("%s Global: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
|
Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
|
||||||
prefix, (void*)g.beg, g.size, g.size_with_redzone, g.name,
|
prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
|
||||||
g.module_name, g.has_dynamic_init);
|
g.module_name, g.has_dynamic_init);
|
||||||
|
if (g.location) {
|
||||||
|
Report(" location (%p): name=%s[%p], %d %d\n", g.location,
|
||||||
|
g.location->filename, g.location->filename, g.location->line_no,
|
||||||
|
g.location->column_no);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DescribeAddressIfGlobal(uptr addr, uptr size) {
|
bool DescribeAddressIfGlobal(uptr addr, uptr size) {
|
||||||
|
@ -79,6 +93,16 @@ bool DescribeAddressIfGlobal(uptr addr, uptr size) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 FindRegistrationSite(const Global *g) {
|
||||||
|
CHECK(global_registration_site_vector);
|
||||||
|
for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
|
||||||
|
GlobalRegistrationSite &grs = (*global_registration_site_vector)[i];
|
||||||
|
if (g >= grs.g_first && g <= grs.g_last)
|
||||||
|
return grs.stack_id;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Register a global variable.
|
// Register a global variable.
|
||||||
// This function may be called more than once for every global
|
// This function may be called more than once for every global
|
||||||
// so we store the globals in a map.
|
// so we store the globals in a map.
|
||||||
|
@ -99,7 +123,8 @@ static void RegisterGlobal(const Global *g) {
|
||||||
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
|
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
|
||||||
if (g->beg == l->g->beg &&
|
if (g->beg == l->g->beg &&
|
||||||
(flags()->detect_odr_violation >= 2 || g->size != l->g->size))
|
(flags()->detect_odr_violation >= 2 || g->size != l->g->size))
|
||||||
ReportODRViolation(g, l->g);
|
ReportODRViolation(g, FindRegistrationSite(g),
|
||||||
|
l->g, FindRegistrationSite(l->g));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,7 +180,18 @@ using namespace __asan; // NOLINT
|
||||||
// Register an array of globals.
|
// Register an array of globals.
|
||||||
void __asan_register_globals(__asan_global *globals, uptr n) {
|
void __asan_register_globals(__asan_global *globals, uptr n) {
|
||||||
if (!flags()->report_globals) return;
|
if (!flags()->report_globals) return;
|
||||||
|
GET_STACK_TRACE_FATAL_HERE;
|
||||||
|
u32 stack_id = StackDepotPut(stack.trace, stack.size);
|
||||||
BlockingMutexLock lock(&mu_for_globals);
|
BlockingMutexLock lock(&mu_for_globals);
|
||||||
|
if (!global_registration_site_vector)
|
||||||
|
global_registration_site_vector =
|
||||||
|
new(allocator_for_globals) GlobalRegistrationSiteVector(128);
|
||||||
|
GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]};
|
||||||
|
global_registration_site_vector->push_back(site);
|
||||||
|
if (flags()->report_globals >= 2) {
|
||||||
|
PRINT_CURRENT_STACK();
|
||||||
|
Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]);
|
||||||
|
}
|
||||||
for (uptr i = 0; i < n; i++) {
|
for (uptr i = 0; i < n; i++) {
|
||||||
RegisterGlobal(&globals[i]);
|
RegisterGlobal(&globals[i]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
//===-- asan_init_version.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.
|
||||||
|
//
|
||||||
|
// This header defines a versioned __asan_init function to be called at the
|
||||||
|
// startup of the instrumented program.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#ifndef ASAN_INIT_VERSION_H
|
||||||
|
#define ASAN_INIT_VERSION_H
|
||||||
|
|
||||||
|
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.
|
||||||
|
#define __asan_init __asan_init_v4
|
||||||
|
#define __asan_init_name "__asan_init_v4"
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ASAN_INIT_VERSION_H
|
|
@ -144,6 +144,9 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
|
||||||
} while (false)
|
} while (false)
|
||||||
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
|
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
|
||||||
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
|
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
|
||||||
|
#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res) CovUpdateMapping()
|
||||||
|
#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CovUpdateMapping()
|
||||||
|
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited)
|
||||||
#include "sanitizer_common/sanitizer_common_interceptors.inc"
|
#include "sanitizer_common/sanitizer_common_interceptors.inc"
|
||||||
|
|
||||||
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s)
|
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s)
|
||||||
|
@ -291,37 +294,29 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// intercept mlock and friends.
|
#if SANITIZER_WINDOWS
|
||||||
// Since asan maps 16T of RAM, mlock is completely unfriendly to asan.
|
INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
|
||||||
// All functions return 0 (success).
|
CHECK(REAL(RaiseException));
|
||||||
static void MlockIsUnsupported() {
|
__asan_handle_no_return();
|
||||||
static bool printed = false;
|
REAL(RaiseException)(a, b, c, d);
|
||||||
if (printed) return;
|
|
||||||
printed = true;
|
|
||||||
VPrintf(1,
|
|
||||||
"INFO: AddressSanitizer ignores "
|
|
||||||
"mlock/mlockall/munlock/munlockall\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(int, mlock, const void *addr, uptr len) {
|
INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
|
||||||
MlockIsUnsupported();
|
CHECK(REAL(_except_handler3));
|
||||||
return 0;
|
__asan_handle_no_return();
|
||||||
|
return REAL(_except_handler3)(a, b, c, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(int, munlock, const void *addr, uptr len) {
|
#if ASAN_DYNAMIC
|
||||||
MlockIsUnsupported();
|
// This handler is named differently in -MT and -MD CRTs.
|
||||||
return 0;
|
#define _except_handler4 _except_handler4_common
|
||||||
}
|
#endif
|
||||||
|
INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
|
||||||
INTERCEPTOR(int, mlockall, int flags) {
|
CHECK(REAL(_except_handler4));
|
||||||
MlockIsUnsupported();
|
__asan_handle_no_return();
|
||||||
return 0;
|
return REAL(_except_handler4)(a, b, c, d);
|
||||||
}
|
|
||||||
|
|
||||||
INTERCEPTOR(int, munlockall, void) {
|
|
||||||
MlockIsUnsupported();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline int CharCmp(unsigned char c1, unsigned char c2) {
|
static inline int CharCmp(unsigned char c1, unsigned char c2) {
|
||||||
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
|
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
|
||||||
|
@ -523,7 +518,7 @@ INTERCEPTOR(char*, strdup, const char *s) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
INTERCEPTOR(uptr, strlen, const char *s) {
|
INTERCEPTOR(SIZE_T, strlen, const char *s) {
|
||||||
if (UNLIKELY(!asan_inited)) return internal_strlen(s);
|
if (UNLIKELY(!asan_inited)) return internal_strlen(s);
|
||||||
// strlen is called from malloc_default_purgeable_zone()
|
// strlen is called from malloc_default_purgeable_zone()
|
||||||
// in __asan::ReplaceSystemAlloc() on Mac.
|
// in __asan::ReplaceSystemAlloc() on Mac.
|
||||||
|
@ -531,15 +526,15 @@ INTERCEPTOR(uptr, strlen, const char *s) {
|
||||||
return REAL(strlen)(s);
|
return REAL(strlen)(s);
|
||||||
}
|
}
|
||||||
ENSURE_ASAN_INITED();
|
ENSURE_ASAN_INITED();
|
||||||
uptr length = REAL(strlen)(s);
|
SIZE_T length = REAL(strlen)(s);
|
||||||
if (flags()->replace_str) {
|
if (flags()->replace_str) {
|
||||||
ASAN_READ_RANGE(s, length + 1);
|
ASAN_READ_RANGE(s, length + 1);
|
||||||
}
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(uptr, wcslen, const wchar_t *s) {
|
INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) {
|
||||||
uptr length = REAL(wcslen)(s);
|
SIZE_T length = REAL(wcslen)(s);
|
||||||
if (!asan_init_is_running) {
|
if (!asan_init_is_running) {
|
||||||
ENSURE_ASAN_INITED();
|
ENSURE_ASAN_INITED();
|
||||||
ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t));
|
ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t));
|
||||||
|
@ -691,6 +686,16 @@ INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
|
||||||
}
|
}
|
||||||
#endif // ASAN_INTERCEPT___CXA_ATEXIT
|
#endif // ASAN_INTERCEPT___CXA_ATEXIT
|
||||||
|
|
||||||
|
#if ASAN_INTERCEPT_FORK
|
||||||
|
INTERCEPTOR(int, fork, void) {
|
||||||
|
ENSURE_ASAN_INITED();
|
||||||
|
if (common_flags()->coverage) CovBeforeFork();
|
||||||
|
int pid = REAL(fork)();
|
||||||
|
if (common_flags()->coverage) CovAfterFork(pid);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
#endif // ASAN_INTERCEPT_FORK
|
||||||
|
|
||||||
#if SANITIZER_WINDOWS
|
#if SANITIZER_WINDOWS
|
||||||
INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
||||||
void* security, uptr stack_size,
|
void* security, uptr stack_size,
|
||||||
|
@ -712,6 +717,9 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
||||||
namespace __asan {
|
namespace __asan {
|
||||||
void InitializeWindowsInterceptors() {
|
void InitializeWindowsInterceptors() {
|
||||||
ASAN_INTERCEPT_FUNC(CreateThread);
|
ASAN_INTERCEPT_FUNC(CreateThread);
|
||||||
|
ASAN_INTERCEPT_FUNC(RaiseException);
|
||||||
|
ASAN_INTERCEPT_FUNC(_except_handler3);
|
||||||
|
ASAN_INTERCEPT_FUNC(_except_handler4);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace __asan
|
} // namespace __asan
|
||||||
|
@ -759,14 +767,6 @@ void InitializeAsanInterceptors() {
|
||||||
ASAN_INTERCEPT_FUNC(strtoll);
|
ASAN_INTERCEPT_FUNC(strtoll);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ASAN_INTERCEPT_MLOCKX
|
|
||||||
// Intercept mlock/munlock.
|
|
||||||
ASAN_INTERCEPT_FUNC(mlock);
|
|
||||||
ASAN_INTERCEPT_FUNC(munlock);
|
|
||||||
ASAN_INTERCEPT_FUNC(mlockall);
|
|
||||||
ASAN_INTERCEPT_FUNC(munlockall);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Intecept signal- and jump-related functions.
|
// Intecept signal- and jump-related functions.
|
||||||
ASAN_INTERCEPT_FUNC(longjmp);
|
ASAN_INTERCEPT_FUNC(longjmp);
|
||||||
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
|
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
|
||||||
|
@ -789,7 +789,7 @@ void InitializeAsanInterceptors() {
|
||||||
|
|
||||||
// Intercept exception handling functions.
|
// Intercept exception handling functions.
|
||||||
#if ASAN_INTERCEPT___CXA_THROW
|
#if ASAN_INTERCEPT___CXA_THROW
|
||||||
INTERCEPT_FUNCTION(__cxa_throw);
|
ASAN_INTERCEPT_FUNC(__cxa_throw);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Intercept threading-related functions
|
// Intercept threading-related functions
|
||||||
|
@ -802,6 +802,10 @@ void InitializeAsanInterceptors() {
|
||||||
ASAN_INTERCEPT_FUNC(__cxa_atexit);
|
ASAN_INTERCEPT_FUNC(__cxa_atexit);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if ASAN_INTERCEPT_FORK
|
||||||
|
ASAN_INTERCEPT_FUNC(fork);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Some Windows-specific interceptors.
|
// Some Windows-specific interceptors.
|
||||||
#if SANITIZER_WINDOWS
|
#if SANITIZER_WINDOWS
|
||||||
InitializeWindowsInterceptors();
|
InitializeWindowsInterceptors();
|
||||||
|
|
|
@ -24,14 +24,14 @@
|
||||||
# define ASAN_INTERCEPT_STRDUP 1
|
# define ASAN_INTERCEPT_STRDUP 1
|
||||||
# define ASAN_INTERCEPT_INDEX 1
|
# define ASAN_INTERCEPT_INDEX 1
|
||||||
# define ASAN_INTERCEPT_PTHREAD_CREATE 1
|
# define ASAN_INTERCEPT_PTHREAD_CREATE 1
|
||||||
# define ASAN_INTERCEPT_MLOCKX 1
|
# define ASAN_INTERCEPT_FORK 1
|
||||||
#else
|
#else
|
||||||
# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
|
# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
|
||||||
# define ASAN_INTERCEPT__LONGJMP 0
|
# define ASAN_INTERCEPT__LONGJMP 0
|
||||||
# define ASAN_INTERCEPT_STRDUP 0
|
# define ASAN_INTERCEPT_STRDUP 0
|
||||||
# define ASAN_INTERCEPT_INDEX 0
|
# define ASAN_INTERCEPT_INDEX 0
|
||||||
# define ASAN_INTERCEPT_PTHREAD_CREATE 0
|
# define ASAN_INTERCEPT_PTHREAD_CREATE 0
|
||||||
# define ASAN_INTERCEPT_MLOCKX 0
|
# define ASAN_INTERCEPT_FORK 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SANITIZER_FREEBSD || SANITIZER_LINUX
|
#if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||||
|
@ -64,7 +64,9 @@
|
||||||
# define ASAN_INTERCEPT_SIGLONGJMP 0
|
# define ASAN_INTERCEPT_SIGLONGJMP 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS
|
// Android bug: https://code.google.com/p/android/issues/detail?id=61799
|
||||||
|
#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS && \
|
||||||
|
!(SANITIZER_ANDROID && defined(__i386))
|
||||||
# define ASAN_INTERCEPT___CXA_THROW 1
|
# define ASAN_INTERCEPT___CXA_THROW 1
|
||||||
#else
|
#else
|
||||||
# define ASAN_INTERCEPT___CXA_THROW 0
|
# define ASAN_INTERCEPT___CXA_THROW 0
|
||||||
|
@ -80,7 +82,7 @@ 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*, memcpy, void *to, const void *from, uptr size)
|
||||||
DECLARE_REAL(void*, memset, void *block, int c, uptr size)
|
DECLARE_REAL(void*, memset, void *block, int c, uptr size)
|
||||||
DECLARE_REAL(char*, strchr, const char *str, int c)
|
DECLARE_REAL(char*, strchr, const char *str, int c)
|
||||||
DECLARE_REAL(uptr, strlen, const char *s)
|
DECLARE_REAL(SIZE_T, strlen, const char *s)
|
||||||
DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size)
|
DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size)
|
||||||
DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen)
|
DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen)
|
||||||
DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
|
DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
|
||||||
|
|
|
@ -15,21 +15,24 @@
|
||||||
|
|
||||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||||
|
|
||||||
|
#include "asan_init_version.h"
|
||||||
|
|
||||||
using __sanitizer::uptr;
|
using __sanitizer::uptr;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// This function should be called at the very beginning of the process,
|
// This function should be called at the very beginning of the process,
|
||||||
// before any instrumented code is executed and before any call to malloc.
|
// before any instrumented code is executed and before any call to malloc.
|
||||||
// Every time the asan ABI changes we also change the version number in this
|
// Please note that __asan_init is a macro that is replaced with
|
||||||
// name. Objects build with incompatible asan ABI version
|
// __asan_init_vXXX at compile-time.
|
||||||
// will not link with run-time.
|
SANITIZER_INTERFACE_ATTRIBUTE void __asan_init();
|
||||||
// Changes between ABI versions:
|
|
||||||
// v1=>v2: added 'module_name' to __asan_global
|
// This structure is used to describe the source location of a place where
|
||||||
// v2=>v3: stack frame description (created by the compiler)
|
// global was defined.
|
||||||
// contains the function PC as the 3-rd field (see
|
struct __asan_global_source_location {
|
||||||
// DescribeAddressIfStack).
|
const char *filename;
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3();
|
int line_no;
|
||||||
#define __asan_init __asan_init_v3
|
int column_no;
|
||||||
|
};
|
||||||
|
|
||||||
// This structure describes an instrumented global variable.
|
// This structure describes an instrumented global variable.
|
||||||
struct __asan_global {
|
struct __asan_global {
|
||||||
|
@ -40,6 +43,8 @@ extern "C" {
|
||||||
const char *module_name; // Module name as a C string. This pointer is a
|
const char *module_name; // Module name as a C string. This pointer is a
|
||||||
// unique identifier of a module.
|
// unique identifier of a module.
|
||||||
uptr has_dynamic_init; // Non-zero if the global has dynamic initializer.
|
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.
|
||||||
};
|
};
|
||||||
|
|
||||||
// These two functions should be called by the instrumented code.
|
// These two functions should be called by the instrumented code.
|
||||||
|
@ -83,6 +88,17 @@ extern "C" {
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
void __asan_describe_address(uptr addr);
|
void __asan_describe_address(uptr addr);
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size,
|
||||||
|
u32 *thread_id);
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size,
|
||||||
|
u32 *thread_id);
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset);
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
void __asan_report_error(uptr pc, uptr bp, uptr sp,
|
void __asan_report_error(uptr pc, uptr bp, uptr sp,
|
||||||
uptr addr, int is_write, uptr access_size);
|
uptr addr, int is_write, uptr access_size);
|
||||||
|
@ -97,25 +113,11 @@ extern "C" {
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||||
/* OPTIONAL */ void __asan_on_error();
|
/* OPTIONAL */ void __asan_on_error();
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
|
||||||
uptr __asan_get_estimated_allocated_size(uptr size);
|
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE int __asan_get_ownership(const void *p);
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_allocated_size(const void *p);
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_current_allocated_bytes();
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_heap_size();
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_free_bytes();
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_unmapped_bytes();
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats();
|
SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats();
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||||
/* OPTIONAL */ const char* __asan_default_options();
|
/* OPTIONAL */ const char* __asan_default_options();
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
|
||||||
/* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size);
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
|
||||||
/* OPTIONAL */ void __asan_free_hook(void *ptr);
|
|
||||||
|
|
||||||
// Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return
|
// Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
extern int __asan_option_detect_stack_use_after_return;
|
extern int __asan_option_detect_stack_use_after_return;
|
||||||
|
@ -142,6 +144,11 @@ extern "C" {
|
||||||
void* __asan_memset(void *s, int c, uptr n);
|
void* __asan_memset(void *s, int c, uptr n);
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
void* __asan_memmove(void* dest, const void* src, uptr n);
|
void* __asan_memmove(void* dest, const void* src, uptr n);
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
void __asan_poison_cxx_array_cookie(uptr p);
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __asan_load_cxx_array_cookie(uptr *p);
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
#endif // ASAN_INTERFACE_INTERNAL_H
|
#endif // ASAN_INTERFACE_INTERNAL_H
|
||||||
|
|
|
@ -43,10 +43,6 @@
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ASAN_USE_PREINIT_ARRAY
|
|
||||||
# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ASAN_DYNAMIC
|
#ifndef ASAN_DYNAMIC
|
||||||
# ifdef PIC
|
# ifdef PIC
|
||||||
# define ASAN_DYNAMIC 1
|
# define ASAN_DYNAMIC 1
|
||||||
|
@ -96,6 +92,8 @@ void AppendToErrorMessageBuffer(const char *buffer);
|
||||||
|
|
||||||
void ParseExtraActivationFlags();
|
void ParseExtraActivationFlags();
|
||||||
|
|
||||||
|
void *AsanDlSymNext(const char *sym);
|
||||||
|
|
||||||
// Platform-specific options.
|
// Platform-specific options.
|
||||||
#if SANITIZER_MAC
|
#if SANITIZER_MAC
|
||||||
bool PlatformHasDifferentMemcpyAndMemmove();
|
bool PlatformHasDifferentMemcpyAndMemmove();
|
||||||
|
@ -108,9 +106,9 @@ bool PlatformHasDifferentMemcpyAndMemmove();
|
||||||
// Add convenient macro for interface functions that may be represented as
|
// Add convenient macro for interface functions that may be represented as
|
||||||
// weak hooks.
|
// weak hooks.
|
||||||
#define ASAN_MALLOC_HOOK(ptr, size) \
|
#define ASAN_MALLOC_HOOK(ptr, size) \
|
||||||
if (&__asan_malloc_hook) __asan_malloc_hook(ptr, size)
|
if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size)
|
||||||
#define ASAN_FREE_HOOK(ptr) \
|
#define ASAN_FREE_HOOK(ptr) \
|
||||||
if (&__asan_free_hook) __asan_free_hook(ptr)
|
if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr)
|
||||||
#define ASAN_ON_ERROR() \
|
#define ASAN_ON_ERROR() \
|
||||||
if (&__asan_on_error) __asan_on_error()
|
if (&__asan_on_error) __asan_on_error()
|
||||||
|
|
||||||
|
@ -134,6 +132,7 @@ const int kAsanContiguousContainerOOBMagic = 0xfc;
|
||||||
const int kAsanStackUseAfterScopeMagic = 0xf8;
|
const int kAsanStackUseAfterScopeMagic = 0xf8;
|
||||||
const int kAsanGlobalRedzoneMagic = 0xf9;
|
const int kAsanGlobalRedzoneMagic = 0xf9;
|
||||||
const int kAsanInternalHeapMagic = 0xfe;
|
const int kAsanInternalHeapMagic = 0xfe;
|
||||||
|
const int kAsanArrayCookieMagic = 0xac;
|
||||||
|
|
||||||
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
|
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
|
||||||
static const uptr kRetiredStackFrameMagic = 0x45E0360E;
|
static const uptr kRetiredStackFrameMagic = 0x45E0360E;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "asan_internal.h"
|
#include "asan_internal.h"
|
||||||
#include "asan_thread.h"
|
#include "asan_thread.h"
|
||||||
#include "sanitizer_common/sanitizer_flags.h"
|
#include "sanitizer_common/sanitizer_flags.h"
|
||||||
|
#include "sanitizer_common/sanitizer_freebsd.h"
|
||||||
#include "sanitizer_common/sanitizer_libc.h"
|
#include "sanitizer_common/sanitizer_libc.h"
|
||||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -40,19 +42,14 @@
|
||||||
extern "C" void* _DYNAMIC;
|
extern "C" void* _DYNAMIC;
|
||||||
#else
|
#else
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <link.h>
|
#include <link.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// x86_64 FreeBSD 9.2 and older define 64-bit register names in both 64-bit
|
// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
|
||||||
// and 32-bit modes.
|
// 32-bit mode.
|
||||||
#if SANITIZER_FREEBSD
|
#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \
|
||||||
#include <sys/param.h>
|
__FreeBSD_version <= 902001 // v9.2
|
||||||
# if __FreeBSD_version <= 902001 // v9.2
|
#define ucontext_t xucontext_t
|
||||||
# define mc_eip mc_rip
|
|
||||||
# define mc_ebp mc_rbp
|
|
||||||
# define mc_esp mc_rsp
|
|
||||||
# endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -241,6 +238,10 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void *AsanDlSymNext(const char *sym) {
|
||||||
|
return dlsym(RTLD_NEXT, sym);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace __asan
|
} // namespace __asan
|
||||||
|
|
||||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||||
|
|
|
@ -372,32 +372,44 @@ void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
|
||||||
work(); \
|
work(); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forces the compiler to generate a frame pointer in the function.
|
||||||
|
#define ENABLE_FRAME_POINTER \
|
||||||
|
do { \
|
||||||
|
volatile uptr enable_fp; \
|
||||||
|
enable_fp = GET_CURRENT_FRAME(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
INTERCEPTOR(void, dispatch_async,
|
INTERCEPTOR(void, dispatch_async,
|
||||||
dispatch_queue_t dq, void(^work)(void)) {
|
dispatch_queue_t dq, void(^work)(void)) {
|
||||||
|
ENABLE_FRAME_POINTER;
|
||||||
GET_ASAN_BLOCK(work);
|
GET_ASAN_BLOCK(work);
|
||||||
REAL(dispatch_async)(dq, asan_block);
|
REAL(dispatch_async)(dq, asan_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void, dispatch_group_async,
|
INTERCEPTOR(void, dispatch_group_async,
|
||||||
dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
|
dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
|
||||||
|
ENABLE_FRAME_POINTER;
|
||||||
GET_ASAN_BLOCK(work);
|
GET_ASAN_BLOCK(work);
|
||||||
REAL(dispatch_group_async)(dg, dq, asan_block);
|
REAL(dispatch_group_async)(dg, dq, asan_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void, dispatch_after,
|
INTERCEPTOR(void, dispatch_after,
|
||||||
dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
|
dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
|
||||||
|
ENABLE_FRAME_POINTER;
|
||||||
GET_ASAN_BLOCK(work);
|
GET_ASAN_BLOCK(work);
|
||||||
REAL(dispatch_after)(when, queue, asan_block);
|
REAL(dispatch_after)(when, queue, asan_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void, dispatch_source_set_cancel_handler,
|
INTERCEPTOR(void, dispatch_source_set_cancel_handler,
|
||||||
dispatch_source_t ds, void(^work)(void)) {
|
dispatch_source_t ds, void(^work)(void)) {
|
||||||
|
ENABLE_FRAME_POINTER;
|
||||||
GET_ASAN_BLOCK(work);
|
GET_ASAN_BLOCK(work);
|
||||||
REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
|
REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void, dispatch_source_set_event_handler,
|
INTERCEPTOR(void, dispatch_source_set_event_handler,
|
||||||
dispatch_source_t ds, void(^work)(void)) {
|
dispatch_source_t ds, void(^work)(void)) {
|
||||||
|
ENABLE_FRAME_POINTER;
|
||||||
GET_ASAN_BLOCK(work);
|
GET_ASAN_BLOCK(work);
|
||||||
REAL(dispatch_source_set_event_handler)(ds, asan_block);
|
REAL(dispatch_source_set_event_handler)(ds, asan_block);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,41 +21,6 @@
|
||||||
#include "asan_internal.h"
|
#include "asan_internal.h"
|
||||||
#include "asan_stack.h"
|
#include "asan_stack.h"
|
||||||
|
|
||||||
#if SANITIZER_ANDROID
|
|
||||||
DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size)
|
|
||||||
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
|
|
||||||
DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size)
|
|
||||||
DECLARE_REAL_AND_INTERCEPTOR(void*, realloc, void *ptr, uptr size)
|
|
||||||
DECLARE_REAL_AND_INTERCEPTOR(void*, memalign, uptr boundary, uptr size)
|
|
||||||
|
|
||||||
struct MallocDebug {
|
|
||||||
void* (*malloc)(uptr bytes);
|
|
||||||
void (*free)(void* mem);
|
|
||||||
void* (*calloc)(uptr n_elements, uptr elem_size);
|
|
||||||
void* (*realloc)(void* oldMem, uptr bytes);
|
|
||||||
void* (*memalign)(uptr alignment, uptr bytes);
|
|
||||||
};
|
|
||||||
|
|
||||||
const MallocDebug asan_malloc_dispatch ALIGNED(32) = {
|
|
||||||
WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign)
|
|
||||||
};
|
|
||||||
|
|
||||||
extern "C" const MallocDebug* __libc_malloc_dispatch;
|
|
||||||
|
|
||||||
namespace __asan {
|
|
||||||
void ReplaceSystemMalloc() {
|
|
||||||
__libc_malloc_dispatch = &asan_malloc_dispatch;
|
|
||||||
}
|
|
||||||
} // namespace __asan
|
|
||||||
|
|
||||||
#else // ANDROID
|
|
||||||
|
|
||||||
namespace __asan {
|
|
||||||
void ReplaceSystemMalloc() {
|
|
||||||
}
|
|
||||||
} // namespace __asan
|
|
||||||
#endif // ANDROID
|
|
||||||
|
|
||||||
// ---------------------- Replacement functions ---------------- {{{1
|
// ---------------------- Replacement functions ---------------- {{{1
|
||||||
using namespace __asan; // NOLINT
|
using namespace __asan; // NOLINT
|
||||||
|
|
||||||
|
@ -100,6 +65,11 @@ INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
|
||||||
return asan_memalign(boundary, size, &stack, FROM_MALLOC);
|
return asan_memalign(boundary, size, &stack, FROM_MALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
|
||||||
|
GET_STACK_TRACE_MALLOC;
|
||||||
|
return asan_memalign(boundary, size, &stack, FROM_MALLOC);
|
||||||
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
|
INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
|
void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
|
||||||
|
@ -151,4 +121,64 @@ INTERCEPTOR(void, malloc_stats, void) {
|
||||||
__asan_print_accumulated_stats();
|
__asan_print_accumulated_stats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SANITIZER_ANDROID
|
||||||
|
// Format of __libc_malloc_dispatch has changed in Android L.
|
||||||
|
// While we are moving towards a solution that does not depend on bionic
|
||||||
|
// internals, here is something to support both K* and L releases.
|
||||||
|
struct MallocDebugK {
|
||||||
|
void *(*malloc)(uptr bytes);
|
||||||
|
void (*free)(void *mem);
|
||||||
|
void *(*calloc)(uptr n_elements, uptr elem_size);
|
||||||
|
void *(*realloc)(void *oldMem, uptr bytes);
|
||||||
|
void *(*memalign)(uptr alignment, uptr bytes);
|
||||||
|
uptr (*malloc_usable_size)(void *mem);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MallocDebugL {
|
||||||
|
void *(*calloc)(uptr n_elements, uptr elem_size);
|
||||||
|
void (*free)(void *mem);
|
||||||
|
fake_mallinfo (*mallinfo)(void);
|
||||||
|
void *(*malloc)(uptr bytes);
|
||||||
|
uptr (*malloc_usable_size)(void *mem);
|
||||||
|
void *(*memalign)(uptr alignment, uptr bytes);
|
||||||
|
int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
|
||||||
|
void* (*pvalloc)(uptr size);
|
||||||
|
void *(*realloc)(void *oldMem, uptr bytes);
|
||||||
|
void* (*valloc)(uptr size);
|
||||||
|
};
|
||||||
|
|
||||||
|
ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
|
||||||
|
WRAP(malloc), WRAP(free), WRAP(calloc),
|
||||||
|
WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
|
||||||
|
|
||||||
|
ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
|
||||||
|
WRAP(calloc), WRAP(free), WRAP(mallinfo),
|
||||||
|
WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign),
|
||||||
|
WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc),
|
||||||
|
WRAP(valloc)};
|
||||||
|
|
||||||
|
namespace __asan {
|
||||||
|
void ReplaceSystemMalloc() {
|
||||||
|
void **__libc_malloc_dispatch_p =
|
||||||
|
(void **)AsanDlSymNext("__libc_malloc_dispatch");
|
||||||
|
if (__libc_malloc_dispatch_p) {
|
||||||
|
// Decide on K vs L dispatch format by the presence of
|
||||||
|
// __libc_malloc_default_dispatch export in libc.
|
||||||
|
void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
|
||||||
|
if (default_dispatch_p)
|
||||||
|
*__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
|
||||||
|
else
|
||||||
|
*__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace __asan
|
||||||
|
|
||||||
|
#else // SANITIZER_ANDROID
|
||||||
|
|
||||||
|
namespace __asan {
|
||||||
|
void ReplaceSystemMalloc() {
|
||||||
|
}
|
||||||
|
} // namespace __asan
|
||||||
|
#endif // SANITIZER_ANDROID
|
||||||
|
|
||||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||||
|
|
|
@ -21,71 +21,77 @@
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
// ---------------------- Replacement functions ---------------- {{{1
|
|
||||||
using namespace __asan; // NOLINT
|
using namespace __asan; // NOLINT
|
||||||
|
|
||||||
// FIXME: Simply defining functions with the same signature in *.obj
|
// MT: Simply defining functions with the same signature in *.obj
|
||||||
// files overrides the standard functions in *.lib
|
// files overrides the standard functions in the CRT.
|
||||||
// This works well for simple helloworld-like tests but might need to be
|
// MD: Memory allocation functions are defined in the CRT .dll,
|
||||||
// revisited in the future.
|
// so we have to intercept them before they are called for the first time.
|
||||||
|
|
||||||
|
#if ASAN_DYNAMIC
|
||||||
|
# define ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
|
#else
|
||||||
|
# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
#endif
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void free(void *ptr) {
|
void free(void *ptr) {
|
||||||
GET_STACK_TRACE_FREE;
|
GET_STACK_TRACE_FREE;
|
||||||
return asan_free(ptr, &stack, FROM_MALLOC);
|
return asan_free(ptr, &stack, FROM_MALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void _free_dbg(void* ptr, int) {
|
void _free_dbg(void *ptr, int) {
|
||||||
free(ptr);
|
free(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void cfree(void *ptr) {
|
void cfree(void *ptr) {
|
||||||
CHECK(!"cfree() should not be used on Windows?");
|
CHECK(!"cfree() should not be used on Windows");
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void *malloc(size_t size) {
|
void *malloc(size_t size) {
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return asan_malloc(size, &stack);
|
return asan_malloc(size, &stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void* _malloc_dbg(size_t size, int , const char*, int) {
|
void *_malloc_dbg(size_t size, int, const char *, int) {
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void *calloc(size_t nmemb, size_t size) {
|
void *calloc(size_t nmemb, size_t size) {
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return asan_calloc(nmemb, size, &stack);
|
return asan_calloc(nmemb, size, &stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void* _calloc_dbg(size_t n, size_t size, int, const char*, int) {
|
void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
|
||||||
return calloc(n, size);
|
return calloc(nmemb, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
|
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
|
||||||
return calloc(nmemb, size);
|
return calloc(nmemb, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void *realloc(void *ptr, size_t size) {
|
void *realloc(void *ptr, size_t size) {
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return asan_realloc(ptr, size, &stack);
|
return asan_realloc(ptr, size, &stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void *_realloc_dbg(void *ptr, size_t size, int) {
|
void *_realloc_dbg(void *ptr, size_t size, int) {
|
||||||
CHECK(!"_realloc_dbg should not exist!");
|
CHECK(!"_realloc_dbg should not exist!");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void* _recalloc(void* p, size_t n, size_t elem_size) {
|
void *_recalloc(void *p, size_t n, size_t elem_size) {
|
||||||
if (!p)
|
if (!p)
|
||||||
return calloc(n, elem_size);
|
return calloc(n, elem_size);
|
||||||
const size_t size = n * elem_size;
|
const size_t size = n * elem_size;
|
||||||
|
@ -94,23 +100,23 @@ void* _recalloc(void* p, size_t n, size_t elem_size) {
|
||||||
return realloc(p, size);
|
return realloc(p, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
size_t _msize(void *ptr) {
|
size_t _msize(void *ptr) {
|
||||||
GET_CURRENT_PC_BP_SP;
|
GET_CURRENT_PC_BP_SP;
|
||||||
(void)sp;
|
(void)sp;
|
||||||
return asan_malloc_usable_size(ptr, pc, bp);
|
return asan_malloc_usable_size(ptr, pc, bp);
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void *_expand(void *memblock, size_t size) {
|
void *_expand(void *memblock, size_t size) {
|
||||||
// _expand is used in realloc-like functions to resize the buffer if possible.
|
// _expand is used in realloc-like functions to resize the buffer if possible.
|
||||||
// We don't want memory to stand still while resizing buffers, so return 0.
|
// We don't want memory to stand still while resizing buffers, so return 0.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE
|
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||||
void *_expand_dbg(void *memblock, size_t size) {
|
void *_expand_dbg(void *memblock, size_t size) {
|
||||||
return 0;
|
return _expand(memblock, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(timurrrr): Might want to add support for _aligned_* allocation
|
// TODO(timurrrr): Might want to add support for _aligned_* allocation
|
||||||
|
@ -131,37 +137,38 @@ int _CrtSetReportMode(int, int) {
|
||||||
}
|
}
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
using __interception::GetRealFunctionAddress;
|
|
||||||
|
|
||||||
// We don't want to include "windows.h" in this file to avoid extra attributes
|
|
||||||
// set on malloc/free etc (e.g. dllimport), so declare a few things manually:
|
|
||||||
extern "C" int __stdcall VirtualProtect(void* addr, size_t size,
|
|
||||||
DWORD prot, DWORD *old_prot);
|
|
||||||
const int PAGE_EXECUTE_READWRITE = 0x40;
|
|
||||||
|
|
||||||
namespace __asan {
|
namespace __asan {
|
||||||
void ReplaceSystemMalloc() {
|
void ReplaceSystemMalloc() {
|
||||||
#if defined(_DLL)
|
#if defined(ASAN_DYNAMIC)
|
||||||
# ifdef _WIN64
|
// We don't check the result because CRT might not be used in the process.
|
||||||
# error ReplaceSystemMalloc was not tested on x64
|
__interception::OverrideFunction("free", (uptr)free);
|
||||||
# endif
|
__interception::OverrideFunction("malloc", (uptr)malloc);
|
||||||
char *crt_malloc;
|
__interception::OverrideFunction("_malloc_crt", (uptr)malloc);
|
||||||
if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) {
|
__interception::OverrideFunction("calloc", (uptr)calloc);
|
||||||
// Replace malloc in the CRT dll with a jump to our malloc.
|
__interception::OverrideFunction("_calloc_crt", (uptr)calloc);
|
||||||
DWORD old_prot, unused;
|
__interception::OverrideFunction("realloc", (uptr)realloc);
|
||||||
CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot));
|
__interception::OverrideFunction("_realloc_crt", (uptr)realloc);
|
||||||
REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case.
|
__interception::OverrideFunction("_recalloc", (uptr)_recalloc);
|
||||||
|
__interception::OverrideFunction("_recalloc_crt", (uptr)_recalloc);
|
||||||
|
__interception::OverrideFunction("_msize", (uptr)_msize);
|
||||||
|
__interception::OverrideFunction("_expand", (uptr)_expand);
|
||||||
|
|
||||||
ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5;
|
// Override different versions of 'operator new' and 'operator delete'.
|
||||||
crt_malloc[0] = 0xE9; // jmp, should be followed by an offset.
|
// No need to override the nothrow versions as they just wrap the throw
|
||||||
REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset));
|
// versions.
|
||||||
|
// FIXME: Unfortunately, MSVC miscompiles the statements that take the
|
||||||
CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused));
|
// addresses of the array versions of these operators,
|
||||||
|
// see https://connect.microsoft.com/VisualStudio/feedbackdetail/view/946992
|
||||||
// FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64.
|
// 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;
|
||||||
// FIXME: investigate whether anything else is needed.
|
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);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
} // namespace __asan
|
} // namespace __asan
|
||||||
|
|
|
@ -18,6 +18,13 @@
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// C++ operators can't have visibility attributes on Windows.
|
||||||
|
#if SANITIZER_WINDOWS
|
||||||
|
# define CXX_OPERATOR_ATTRIBUTE
|
||||||
|
#else
|
||||||
|
# define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace __asan; // NOLINT
|
using namespace __asan; // NOLINT
|
||||||
|
|
||||||
// This code has issues on OSX.
|
// This code has issues on OSX.
|
||||||
|
@ -49,14 +56,14 @@ struct nothrow_t {};
|
||||||
#endif // __FreeBSD_version
|
#endif // __FreeBSD_version
|
||||||
#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
|
#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
|
||||||
|
|
||||||
INTERCEPTOR_ATTRIBUTE
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
|
void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
|
||||||
INTERCEPTOR_ATTRIBUTE
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); }
|
void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); }
|
||||||
INTERCEPTOR_ATTRIBUTE
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
void *operator new(size_t size, std::nothrow_t const&)
|
void *operator new(size_t size, std::nothrow_t const&)
|
||||||
{ OPERATOR_NEW_BODY(FROM_NEW); }
|
{ OPERATOR_NEW_BODY(FROM_NEW); }
|
||||||
INTERCEPTOR_ATTRIBUTE
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
void *operator new[](size_t size, std::nothrow_t const&)
|
void *operator new[](size_t size, std::nothrow_t const&)
|
||||||
{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
|
{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
|
||||||
|
|
||||||
|
@ -80,22 +87,32 @@ INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
|
||||||
asan_free(ptr, &stack, type);
|
asan_free(ptr, &stack, type);
|
||||||
|
|
||||||
#if !SANITIZER_MAC
|
#if !SANITIZER_MAC
|
||||||
INTERCEPTOR_ATTRIBUTE
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
void operator delete(void *ptr) throw() {
|
void operator delete(void *ptr) throw() {
|
||||||
OPERATOR_DELETE_BODY(FROM_NEW);
|
OPERATOR_DELETE_BODY(FROM_NEW);
|
||||||
}
|
}
|
||||||
INTERCEPTOR_ATTRIBUTE
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
void operator delete[](void *ptr) throw() {
|
void operator delete[](void *ptr) throw() {
|
||||||
OPERATOR_DELETE_BODY(FROM_NEW_BR);
|
OPERATOR_DELETE_BODY(FROM_NEW_BR);
|
||||||
}
|
}
|
||||||
INTERCEPTOR_ATTRIBUTE
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
void operator delete(void *ptr, std::nothrow_t const&) {
|
void operator delete(void *ptr, std::nothrow_t const&) {
|
||||||
OPERATOR_DELETE_BODY(FROM_NEW);
|
OPERATOR_DELETE_BODY(FROM_NEW);
|
||||||
}
|
}
|
||||||
INTERCEPTOR_ATTRIBUTE
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
void operator delete[](void *ptr, std::nothrow_t const&) {
|
void operator delete[](void *ptr, std::nothrow_t const&) {
|
||||||
OPERATOR_DELETE_BODY(FROM_NEW_BR);
|
OPERATOR_DELETE_BODY(FROM_NEW_BR);
|
||||||
}
|
}
|
||||||
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
|
void operator delete(void *ptr, size_t size) throw() {
|
||||||
|
GET_STACK_TRACE_FREE;
|
||||||
|
asan_sized_free(ptr, size, &stack, FROM_NEW);
|
||||||
|
}
|
||||||
|
CXX_OPERATOR_ATTRIBUTE
|
||||||
|
void operator delete[](void *ptr, size_t size) throw() {
|
||||||
|
GET_STACK_TRACE_FREE;
|
||||||
|
asan_sized_free(ptr, size, &stack, FROM_NEW_BR);
|
||||||
|
}
|
||||||
|
|
||||||
#else // SANITIZER_MAC
|
#else // SANITIZER_MAC
|
||||||
INTERCEPTOR(void, _ZdlPv, void *ptr) {
|
INTERCEPTOR(void, _ZdlPv, void *ptr) {
|
||||||
|
|
|
@ -225,6 +225,35 @@ void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
|
||||||
*p = x;
|
*p = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
void __asan_poison_cxx_array_cookie(uptr p) {
|
||||||
|
if (SANITIZER_WORDSIZE != 64) return;
|
||||||
|
if (!flags()->poison_array_cookie) return;
|
||||||
|
uptr s = MEM_TO_SHADOW(p);
|
||||||
|
*reinterpret_cast<u8*>(s) = kAsanArrayCookieMagic;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __asan_load_cxx_array_cookie(uptr *p) {
|
||||||
|
if (SANITIZER_WORDSIZE != 64) return *p;
|
||||||
|
if (!flags()->poison_array_cookie) return *p;
|
||||||
|
uptr s = MEM_TO_SHADOW(reinterpret_cast<uptr>(p));
|
||||||
|
u8 sval = *reinterpret_cast<u8*>(s);
|
||||||
|
if (sval == kAsanArrayCookieMagic) return *p;
|
||||||
|
// If sval is not kAsanArrayCookieMagic it can only be freed memory,
|
||||||
|
// which means that we are going to get double-free. So, return 0 to avoid
|
||||||
|
// infinite loop of destructors. We don't want to report a double-free here
|
||||||
|
// though, so print a warning just in case.
|
||||||
|
// CHECK_EQ(sval, kAsanHeapFreeMagic);
|
||||||
|
if (sval == kAsanHeapFreeMagic) {
|
||||||
|
Report("AddressSanitizer: loaded array cookie from free-d memory; "
|
||||||
|
"expect a double-free report\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// FIXME: apparently it can be something else; need to find a reproducer.
|
||||||
|
return *p;
|
||||||
|
}
|
||||||
|
|
||||||
// This is a simplified version of __asan_(un)poison_memory_region, which
|
// This is a simplified version of __asan_(un)poison_memory_region, which
|
||||||
// assumes that left border of region to be poisoned is properly aligned.
|
// assumes that left border of region to be poisoned is properly aligned.
|
||||||
static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
|
static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
|
||||||
|
|
|
@ -33,7 +33,6 @@ void PoisonShadowPartialRightRedzone(uptr addr,
|
||||||
ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
|
ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
|
||||||
u8 value) {
|
u8 value) {
|
||||||
DCHECK(flags()->poison_heap);
|
DCHECK(flags()->poison_heap);
|
||||||
uptr PageSize = GetPageSizeCached();
|
|
||||||
uptr shadow_beg = MEM_TO_SHADOW(aligned_beg);
|
uptr shadow_beg = MEM_TO_SHADOW(aligned_beg);
|
||||||
uptr shadow_end = MEM_TO_SHADOW(
|
uptr shadow_end = MEM_TO_SHADOW(
|
||||||
aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1;
|
aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1;
|
||||||
|
@ -46,8 +45,9 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
|
||||||
shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
|
shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
|
||||||
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
|
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
|
||||||
} else {
|
} else {
|
||||||
uptr page_beg = RoundUpTo(shadow_beg, PageSize);
|
uptr page_size = GetPageSizeCached();
|
||||||
uptr page_end = RoundDownTo(shadow_end, PageSize);
|
uptr page_beg = RoundUpTo(shadow_beg, page_size);
|
||||||
|
uptr page_end = RoundDownTo(shadow_end, page_size);
|
||||||
|
|
||||||
if (page_beg >= page_end) {
|
if (page_beg >= page_end) {
|
||||||
REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
|
REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
|
||||||
|
|
|
@ -48,7 +48,7 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
|
||||||
(code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
|
(code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
|
||||||
ReportStackOverflow(pc, sp, bp, context, addr);
|
ReportStackOverflow(pc, sp, bp, context, addr);
|
||||||
else
|
else
|
||||||
ReportSIGSEGV(pc, sp, bp, context, addr);
|
ReportSIGSEGV("SEGV", pc, sp, bp, context, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------- TSD ---------------- {{{1
|
// ---------------------- TSD ---------------- {{{1
|
||||||
|
|
|
@ -8,22 +8,12 @@
|
||||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||||
//
|
//
|
||||||
// Call __asan_init at the very early stage of process startup.
|
// Call __asan_init at the very early stage of process startup.
|
||||||
// On Linux we use .preinit_array section (unless PIC macro is defined).
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
#include "asan_internal.h"
|
#include "asan_internal.h"
|
||||||
|
|
||||||
#if ASAN_USE_PREINIT_ARRAY && !defined(PIC)
|
#if SANITIZER_CAN_USE_PREINIT_ARRAY
|
||||||
// On Linux, we force __asan_init to be called before anyone else
|
|
||||||
// by placing it into .preinit_array section.
|
|
||||||
// FIXME: do we have anything like this on Mac?
|
|
||||||
// The symbol is called __local_asan_preinit, because it's not intended to be
|
// The symbol is called __local_asan_preinit, because it's not intended to be
|
||||||
// exported.
|
// exported.
|
||||||
__attribute__((section(".preinit_array"), used))
|
__attribute__((section(".preinit_array"), used))
|
||||||
void (*__local_asan_preinit)(void) = __asan_init;
|
void (*__local_asan_preinit)(void) = __asan_init;
|
||||||
#elif SANITIZER_WINDOWS && defined(_DLL)
|
|
||||||
// On Windows, when using dynamic CRT (/MD), we can put a pointer
|
|
||||||
// to __asan_init into the global list of C initializers.
|
|
||||||
// See crt0dat.c in the CRT sources for the details.
|
|
||||||
#pragma section(".CRT$XIB", long, read) // NOLINT
|
|
||||||
__declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -57,6 +57,7 @@ class Decorator: public __sanitizer::SanitizerCommonDecorator {
|
||||||
switch (byte) {
|
switch (byte) {
|
||||||
case kAsanHeapLeftRedzoneMagic:
|
case kAsanHeapLeftRedzoneMagic:
|
||||||
case kAsanHeapRightRedzoneMagic:
|
case kAsanHeapRightRedzoneMagic:
|
||||||
|
case kAsanArrayCookieMagic:
|
||||||
return Red();
|
return Red();
|
||||||
case kAsanHeapFreeMagic:
|
case kAsanHeapFreeMagic:
|
||||||
return Magenta();
|
return Magenta();
|
||||||
|
@ -141,6 +142,8 @@ static void PrintLegend(InternalScopedString *str) {
|
||||||
kAsanUserPoisonedMemoryMagic);
|
kAsanUserPoisonedMemoryMagic);
|
||||||
PrintShadowByte(str, " Container overflow: ",
|
PrintShadowByte(str, " Container overflow: ",
|
||||||
kAsanContiguousContainerOOBMagic);
|
kAsanContiguousContainerOOBMagic);
|
||||||
|
PrintShadowByte(str, " Array cookie: ",
|
||||||
|
kAsanArrayCookieMagic);
|
||||||
PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
|
PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +198,7 @@ static const char *MaybeDemangleGlobalName(const char *name) {
|
||||||
else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
|
else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
|
||||||
should_demangle = true;
|
should_demangle = true;
|
||||||
|
|
||||||
return should_demangle ? Symbolizer::Get()->Demangle(name) : name;
|
return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the global is a zero-terminated ASCII string. If so, print it.
|
// Check if the global is a zero-terminated ASCII string. If so, print it.
|
||||||
|
@ -210,6 +213,26 @@ static void PrintGlobalNameIfASCII(InternalScopedString *str,
|
||||||
(char *)g.beg);
|
(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);
|
||||||
|
}
|
||||||
|
|
||||||
bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
||||||
const __asan_global &g) {
|
const __asan_global &g) {
|
||||||
static const uptr kMinimalDistanceFromAnotherGlobal = 64;
|
static const uptr kMinimalDistanceFromAnotherGlobal = 64;
|
||||||
|
@ -230,8 +253,10 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
||||||
// Can it happen?
|
// Can it happen?
|
||||||
str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
|
str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
|
||||||
}
|
}
|
||||||
str.append(" of global variable '%s' from '%s' (0x%zx) of size %zu\n",
|
str.append(" of global variable '%s' defined in '",
|
||||||
MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size);
|
MaybeDemangleGlobalName(g.name));
|
||||||
|
PrintGlobalLocation(&str, g);
|
||||||
|
str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
|
||||||
str.append("%s", d.EndLocation());
|
str.append("%s", d.EndLocation());
|
||||||
PrintGlobalNameIfASCII(&str, g);
|
PrintGlobalNameIfASCII(&str, g);
|
||||||
Printf("%s", str.data());
|
Printf("%s", str.data());
|
||||||
|
@ -317,12 +342,27 @@ void PrintAccessAndVarIntersection(const char *var_name,
|
||||||
Printf("%s", str.data());
|
Printf("%s", str.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StackVarDescr {
|
bool ParseFrameDescription(const char *frame_descr,
|
||||||
uptr beg;
|
InternalMmapVector<StackVarDescr> *vars) {
|
||||||
uptr size;
|
char *p;
|
||||||
const char *name_pos;
|
uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
|
||||||
uptr name_len;
|
CHECK_GT(n_objects, 0);
|
||||||
};
|
|
||||||
|
for (uptr i = 0; i < n_objects; i++) {
|
||||||
|
uptr beg = (uptr)internal_simple_strtoll(p, &p, 10);
|
||||||
|
uptr size = (uptr)internal_simple_strtoll(p, &p, 10);
|
||||||
|
uptr len = (uptr)internal_simple_strtoll(p, &p, 10);
|
||||||
|
if (beg == 0 || size == 0 || *p != ' ') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
StackVarDescr var = {beg, size, p, len};
|
||||||
|
vars->push_back(var);
|
||||||
|
p += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
||||||
AsanThread *t = FindThreadByStackAddress(addr);
|
AsanThread *t = FindThreadByStackAddress(addr);
|
||||||
|
@ -364,32 +404,19 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
||||||
alloca_stack.size = 1;
|
alloca_stack.size = 1;
|
||||||
Printf("%s", d.EndLocation());
|
Printf("%s", d.EndLocation());
|
||||||
alloca_stack.Print();
|
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 true;
|
||||||
|
}
|
||||||
|
uptr n_objects = vars.size();
|
||||||
// Report the number of stack objects.
|
// Report the number of stack objects.
|
||||||
char *p;
|
|
||||||
uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
|
|
||||||
CHECK_GT(n_objects, 0);
|
|
||||||
Printf(" This frame has %zu object(s):\n", n_objects);
|
Printf(" This frame has %zu object(s):\n", n_objects);
|
||||||
|
|
||||||
// Report all objects in this frame.
|
// Report all objects in this frame.
|
||||||
InternalScopedBuffer<StackVarDescr> vars(n_objects);
|
|
||||||
for (uptr i = 0; i < n_objects; i++) {
|
|
||||||
uptr beg, size;
|
|
||||||
uptr len;
|
|
||||||
beg = (uptr)internal_simple_strtoll(p, &p, 10);
|
|
||||||
size = (uptr)internal_simple_strtoll(p, &p, 10);
|
|
||||||
len = (uptr)internal_simple_strtoll(p, &p, 10);
|
|
||||||
if (beg == 0 || size == 0 || *p != ' ') {
|
|
||||||
Printf("AddressSanitizer can't parse the stack frame "
|
|
||||||
"descriptor: |%s|\n", frame_descr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
p++;
|
|
||||||
vars[i].beg = beg;
|
|
||||||
vars[i].size = size;
|
|
||||||
vars[i].name_pos = p;
|
|
||||||
vars[i].name_len = len;
|
|
||||||
p += len;
|
|
||||||
}
|
|
||||||
for (uptr i = 0; i < n_objects; i++) {
|
for (uptr i = 0; i < n_objects; i++) {
|
||||||
buf[0] = 0;
|
buf[0] = 0;
|
||||||
internal_strncat(buf, vars[i].name_pos,
|
internal_strncat(buf, vars[i].name_pos,
|
||||||
|
@ -401,8 +428,12 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
||||||
prev_var_end, next_var_beg);
|
prev_var_end, next_var_beg);
|
||||||
}
|
}
|
||||||
Printf("HINT: this may be a false positive if your program uses "
|
Printf("HINT: this may be a false positive if your program uses "
|
||||||
"some custom stack unwind mechanism or swapcontext\n"
|
"some custom stack unwind mechanism or swapcontext\n");
|
||||||
" (longjmp and C++ exceptions *are* supported)\n");
|
if (SANITIZER_WINDOWS)
|
||||||
|
Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
|
||||||
|
else
|
||||||
|
Printf(" (longjmp and C++ exceptions *are* supported)\n");
|
||||||
|
|
||||||
DescribeThread(t);
|
DescribeThread(t);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -531,7 +562,7 @@ class ScopedInErrorReport {
|
||||||
// Do not print more than one report, otherwise they will mix up.
|
// Do not print more than one report, otherwise they will mix up.
|
||||||
// Error reporting functions shouldn't return at this situation, as
|
// Error reporting functions shouldn't return at this situation, as
|
||||||
// they are defined as no-return.
|
// they are defined as no-return.
|
||||||
Report("AddressSanitizer: while reporting a bug found another one."
|
Report("AddressSanitizer: while reporting a bug found another one. "
|
||||||
"Ignoring.\n");
|
"Ignoring.\n");
|
||||||
u32 current_tid = GetCurrentTidOrInvalid();
|
u32 current_tid = GetCurrentTidOrInvalid();
|
||||||
if (current_tid != reporting_thread_tid) {
|
if (current_tid != reporting_thread_tid) {
|
||||||
|
@ -578,8 +609,8 @@ void ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr) {
|
||||||
Printf("%s", d.Warning());
|
Printf("%s", d.Warning());
|
||||||
Report(
|
Report(
|
||||||
"ERROR: AddressSanitizer: stack-overflow on address %p"
|
"ERROR: AddressSanitizer: stack-overflow on address %p"
|
||||||
" (pc %p sp %p bp %p T%d)\n",
|
" (pc %p bp %p sp %p T%d)\n",
|
||||||
(void *)addr, (void *)pc, (void *)sp, (void *)bp,
|
(void *)addr, (void *)pc, (void *)bp, (void *)sp,
|
||||||
GetCurrentTidOrInvalid());
|
GetCurrentTidOrInvalid());
|
||||||
Printf("%s", d.EndWarning());
|
Printf("%s", d.EndWarning());
|
||||||
GET_STACK_TRACE_SIGNAL(pc, bp, context);
|
GET_STACK_TRACE_SIGNAL(pc, bp, context);
|
||||||
|
@ -587,15 +618,19 @@ void ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr) {
|
||||||
ReportErrorSummary("stack-overflow", &stack);
|
ReportErrorSummary("stack-overflow", &stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, void *context, uptr addr) {
|
void ReportSIGSEGV(const char *description, uptr pc, uptr sp, uptr bp,
|
||||||
|
void *context, uptr addr) {
|
||||||
ScopedInErrorReport in_report;
|
ScopedInErrorReport in_report;
|
||||||
Decorator d;
|
Decorator d;
|
||||||
Printf("%s", d.Warning());
|
Printf("%s", d.Warning());
|
||||||
Report(
|
Report(
|
||||||
"ERROR: AddressSanitizer: SEGV on unknown address %p"
|
"ERROR: AddressSanitizer: %s on unknown address %p"
|
||||||
" (pc %p sp %p bp %p T%d)\n",
|
" (pc %p bp %p sp %p T%d)\n",
|
||||||
(void *)addr, (void *)pc, (void *)sp, (void *)bp,
|
description, (void *)addr, (void *)pc, (void *)bp, (void *)sp,
|
||||||
GetCurrentTidOrInvalid());
|
GetCurrentTidOrInvalid());
|
||||||
|
if (pc < GetPageSizeCached()) {
|
||||||
|
Report("Hint: pc points to the zero page.\n");
|
||||||
|
}
|
||||||
Printf("%s", d.EndWarning());
|
Printf("%s", d.EndWarning());
|
||||||
GET_STACK_TRACE_SIGNAL(pc, bp, context);
|
GET_STACK_TRACE_SIGNAL(pc, bp, context);
|
||||||
stack.Print();
|
stack.Print();
|
||||||
|
@ -621,6 +656,30 @@ void ReportDoubleFree(uptr addr, StackTrace *free_stack) {
|
||||||
ReportErrorSummary("double-free", &stack);
|
ReportErrorSummary("double-free", &stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
|
||||||
|
StackTrace *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 warnings you may set "
|
||||||
|
"ASAN_OPTIONS=new_delete_type_mismatch=0\n");
|
||||||
|
}
|
||||||
|
|
||||||
void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) {
|
void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) {
|
||||||
ScopedInErrorReport in_report;
|
ScopedInErrorReport in_report;
|
||||||
Decorator d;
|
Decorator d;
|
||||||
|
@ -674,17 +733,17 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
|
||||||
ReportErrorSummary("bad-malloc_usable_size", stack);
|
ReportErrorSummary("bad-malloc_usable_size", stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
|
void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
|
||||||
ScopedInErrorReport in_report;
|
ScopedInErrorReport in_report;
|
||||||
Decorator d;
|
Decorator d;
|
||||||
Printf("%s", d.Warning());
|
Printf("%s", d.Warning());
|
||||||
Report("ERROR: AddressSanitizer: attempting to call "
|
Report("ERROR: AddressSanitizer: attempting to call "
|
||||||
"__asan_get_allocated_size() for pointer which is "
|
"__sanitizer_get_allocated_size() for pointer which is "
|
||||||
"not owned: %p\n", addr);
|
"not owned: %p\n", addr);
|
||||||
Printf("%s", d.EndWarning());
|
Printf("%s", d.EndWarning());
|
||||||
stack->Print();
|
stack->Print();
|
||||||
DescribeHeapAddress(addr, 1);
|
DescribeHeapAddress(addr, 1);
|
||||||
ReportErrorSummary("bad-__asan_get_allocated_size", stack);
|
ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReportStringFunctionMemoryRangesOverlap(
|
void ReportStringFunctionMemoryRangesOverlap(
|
||||||
|
@ -733,17 +792,36 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
|
||||||
ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
|
ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReportODRViolation(const __asan_global *g1, const __asan_global *g2) {
|
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
|
||||||
|
const __asan_global *g2, u32 stack_id2) {
|
||||||
ScopedInErrorReport in_report;
|
ScopedInErrorReport in_report;
|
||||||
Decorator d;
|
Decorator d;
|
||||||
Printf("%s", d.Warning());
|
Printf("%s", d.Warning());
|
||||||
Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg);
|
Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg);
|
||||||
Printf("%s", d.EndWarning());
|
Printf("%s", d.EndWarning());
|
||||||
Printf(" [1] size=%zd %s %s\n", g1->size, g1->name, g1->module_name);
|
InternalScopedString g1_loc(256), g2_loc(256);
|
||||||
Printf(" [2] size=%zd %s %s\n", g2->size, g2->name, g2->module_name);
|
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");
|
||||||
|
uptr stack_size;
|
||||||
|
const uptr *stack_trace = StackDepotGet(stack_id1, &stack_size);
|
||||||
|
StackTrace::PrintStack(stack_trace, stack_size);
|
||||||
|
Printf(" [2]:\n");
|
||||||
|
stack_trace = StackDepotGet(stack_id2, &stack_size);
|
||||||
|
StackTrace::PrintStack(stack_trace, stack_size);
|
||||||
|
}
|
||||||
Report("HINT: if you don't care about these warnings you may set "
|
Report("HINT: if you don't care about these warnings you may set "
|
||||||
"ASAN_OPTIONS=detect_odr_violation=0\n");
|
"ASAN_OPTIONS=detect_odr_violation=0\n");
|
||||||
ReportErrorSummary("odr-violation", g1->module_name, 0, g1->name);
|
InternalScopedString error_msg(256);
|
||||||
|
error_msg.append("odr-violation: global '%s' at %s",
|
||||||
|
MaybeDemangleGlobalName(g1->name), g1_loc.data());
|
||||||
|
ReportErrorSummary(error_msg.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------- CheckForInvalidPointerPair ----------- {{{1
|
// ----------------------- CheckForInvalidPointerPair ----------- {{{1
|
||||||
|
@ -831,6 +909,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
||||||
switch (*shadow_addr) {
|
switch (*shadow_addr) {
|
||||||
case kAsanHeapLeftRedzoneMagic:
|
case kAsanHeapLeftRedzoneMagic:
|
||||||
case kAsanHeapRightRedzoneMagic:
|
case kAsanHeapRightRedzoneMagic:
|
||||||
|
case kAsanArrayCookieMagic:
|
||||||
bug_descr = "heap-buffer-overflow";
|
bug_descr = "heap-buffer-overflow";
|
||||||
break;
|
break;
|
||||||
case kAsanHeapFreeMagic:
|
case kAsanHeapFreeMagic:
|
||||||
|
@ -867,7 +946,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
||||||
Decorator d;
|
Decorator d;
|
||||||
Printf("%s", d.Warning());
|
Printf("%s", d.Warning());
|
||||||
Report("ERROR: AddressSanitizer: %s on address "
|
Report("ERROR: AddressSanitizer: %s on address "
|
||||||
"%p at pc 0x%zx bp 0x%zx sp 0x%zx\n",
|
"%p at pc %p bp %p sp %p\n",
|
||||||
bug_descr, (void*)addr, pc, bp, sp);
|
bug_descr, (void*)addr, pc, bp, sp);
|
||||||
Printf("%s", d.EndWarning());
|
Printf("%s", d.EndWarning());
|
||||||
|
|
||||||
|
@ -899,7 +978,10 @@ void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void __asan_describe_address(uptr addr) {
|
void __asan_describe_address(uptr addr) {
|
||||||
|
// Thread registry must be locked while we're describing an address.
|
||||||
|
asanThreadRegistry().Lock();
|
||||||
DescribeAddress(addr, 1);
|
DescribeAddress(addr, 1);
|
||||||
|
asanThreadRegistry().Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
@ -16,6 +16,13 @@
|
||||||
|
|
||||||
namespace __asan {
|
namespace __asan {
|
||||||
|
|
||||||
|
struct StackVarDescr {
|
||||||
|
uptr beg;
|
||||||
|
uptr size;
|
||||||
|
const char *name_pos;
|
||||||
|
uptr name_len;
|
||||||
|
};
|
||||||
|
|
||||||
// The following functions prints address description depending
|
// The following functions prints address description depending
|
||||||
// on the memory type (shadow/heap/stack/global).
|
// on the memory type (shadow/heap/stack/global).
|
||||||
void DescribeHeapAddress(uptr addr, uptr access_size);
|
void DescribeHeapAddress(uptr addr, uptr access_size);
|
||||||
|
@ -23,6 +30,8 @@ bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
|
||||||
bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
|
bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
|
||||||
const __asan_global &g);
|
const __asan_global &g);
|
||||||
bool DescribeAddressIfShadow(uptr addr);
|
bool DescribeAddressIfShadow(uptr addr);
|
||||||
|
bool ParseFrameDescription(const char *frame_descr,
|
||||||
|
InternalMmapVector<StackVarDescr> *vars);
|
||||||
bool DescribeAddressIfStack(uptr addr, uptr access_size);
|
bool DescribeAddressIfStack(uptr addr, uptr access_size);
|
||||||
// Determines memory type on its own.
|
// Determines memory type on its own.
|
||||||
void DescribeAddress(uptr addr, uptr access_size);
|
void DescribeAddress(uptr addr, uptr access_size);
|
||||||
|
@ -32,8 +41,10 @@ void DescribeThread(AsanThreadContext *context);
|
||||||
// Different kinds of error reports.
|
// Different kinds of error reports.
|
||||||
void NORETURN
|
void NORETURN
|
||||||
ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr);
|
ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr);
|
||||||
void NORETURN
|
void NORETURN ReportSIGSEGV(const char *description, uptr pc, uptr sp, uptr bp,
|
||||||
ReportSIGSEGV(uptr pc, uptr sp, uptr bp, void *context, uptr addr);
|
void *context, uptr addr);
|
||||||
|
void NORETURN ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
|
||||||
|
StackTrace *free_stack);
|
||||||
void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack);
|
void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack);
|
||||||
void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack);
|
void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack);
|
||||||
void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
|
void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
|
||||||
|
@ -41,8 +52,8 @@ void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
|
||||||
AllocType dealloc_type);
|
AllocType dealloc_type);
|
||||||
void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
|
void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
|
||||||
StackTrace *stack);
|
StackTrace *stack);
|
||||||
void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr,
|
void NORETURN
|
||||||
StackTrace *stack);
|
ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack);
|
||||||
void NORETURN ReportStringFunctionMemoryRangesOverlap(
|
void NORETURN ReportStringFunctionMemoryRangesOverlap(
|
||||||
const char *function, const char *offset1, uptr length1,
|
const char *function, const char *offset1, uptr length1,
|
||||||
const char *offset2, uptr length2, StackTrace *stack);
|
const char *offset2, uptr length2, StackTrace *stack);
|
||||||
|
@ -53,7 +64,8 @@ ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, uptr old_mid,
|
||||||
uptr new_mid, StackTrace *stack);
|
uptr new_mid, StackTrace *stack);
|
||||||
|
|
||||||
void NORETURN
|
void NORETURN
|
||||||
ReportODRViolation(const __asan_global *g1, const __asan_global *g2);
|
ReportODRViolation(const __asan_global *g1, u32 stack_id1,
|
||||||
|
const __asan_global *g2, u32 stack_id2);
|
||||||
|
|
||||||
// Mac-specific errors and warnings.
|
// Mac-specific errors and warnings.
|
||||||
void WarnMacFreeUnallocated(
|
void WarnMacFreeUnallocated(
|
||||||
|
|
|
@ -171,11 +171,6 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
|
||||||
"If set, prints ASan exit stats even after program terminates "
|
"If set, prints ASan exit stats even after program terminates "
|
||||||
"successfully.");
|
"successfully.");
|
||||||
|
|
||||||
ParseFlag(str, &f->disable_core, "disable_core",
|
|
||||||
"Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
|
|
||||||
"dumping a 16T+ core file. "
|
|
||||||
"Ignored on OSes that don't dump core by default.");
|
|
||||||
|
|
||||||
ParseFlag(str, &f->allow_reexec, "allow_reexec",
|
ParseFlag(str, &f->allow_reexec, "allow_reexec",
|
||||||
"Allow the tool to re-exec the program. This may interfere badly with "
|
"Allow the tool to re-exec the program. This may interfere badly with "
|
||||||
"the debugger.");
|
"the debugger.");
|
||||||
|
@ -189,6 +184,9 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
|
||||||
"Poison (or not) the heap memory on [de]allocation. Zero value is useful "
|
"Poison (or not) the heap memory on [de]allocation. Zero value is useful "
|
||||||
"for benchmarking the allocator or instrumentator.");
|
"for benchmarking the allocator or instrumentator.");
|
||||||
|
|
||||||
|
ParseFlag(str, &f->poison_array_cookie, "poison_array_cookie",
|
||||||
|
"Poison (or not) the array cookie after operator new[].");
|
||||||
|
|
||||||
ParseFlag(str, &f->poison_partial, "poison_partial",
|
ParseFlag(str, &f->poison_partial, "poison_partial",
|
||||||
"If true, poison partially addressable 8-byte aligned words "
|
"If true, poison partially addressable 8-byte aligned words "
|
||||||
"(default=true). This flag affects heap and global buffers, but not "
|
"(default=true). This flag affects heap and global buffers, but not "
|
||||||
|
@ -196,6 +194,10 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
|
||||||
|
|
||||||
ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch",
|
ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch",
|
||||||
"Report errors on malloc/delete, new/free, new/delete[], etc.");
|
"Report errors on malloc/delete, new/free, new/delete[], etc.");
|
||||||
|
|
||||||
|
ParseFlag(str, &f->new_delete_type_mismatch, "new_delete_type_mismatch",
|
||||||
|
"Report errors on mismatch betwen size of new and delete.");
|
||||||
|
|
||||||
ParseFlag(str, &f->strict_memcmp, "strict_memcmp",
|
ParseFlag(str, &f->strict_memcmp, "strict_memcmp",
|
||||||
"If true, assume that memcmp(p1, p2, n) always reads n bytes before "
|
"If true, assume that memcmp(p1, p2, n) always reads n bytes before "
|
||||||
"comparing p1 and p2.");
|
"comparing p1 and p2.");
|
||||||
|
@ -262,21 +264,23 @@ void InitializeFlags(Flags *f, const char *env) {
|
||||||
f->print_stats = false;
|
f->print_stats = false;
|
||||||
f->print_legend = true;
|
f->print_legend = true;
|
||||||
f->atexit = false;
|
f->atexit = false;
|
||||||
f->disable_core = (SANITIZER_WORDSIZE == 64);
|
|
||||||
f->allow_reexec = true;
|
f->allow_reexec = true;
|
||||||
f->print_full_thread_history = true;
|
f->print_full_thread_history = true;
|
||||||
f->poison_heap = true;
|
f->poison_heap = true;
|
||||||
|
f->poison_array_cookie = true;
|
||||||
f->poison_partial = true;
|
f->poison_partial = true;
|
||||||
// Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
|
// 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=131
|
||||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=309
|
// https://code.google.com/p/address-sanitizer/issues/detail?id=309
|
||||||
// TODO(glider,timurrrr): Fix known issues and enable this back.
|
// TODO(glider,timurrrr): Fix known issues and enable this back.
|
||||||
f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0);
|
f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0);
|
||||||
|
f->new_delete_type_mismatch = true;
|
||||||
f->strict_memcmp = true;
|
f->strict_memcmp = true;
|
||||||
f->strict_init_order = false;
|
f->strict_init_order = false;
|
||||||
f->start_deactivated = false;
|
f->start_deactivated = false;
|
||||||
f->detect_invalid_pointer_pairs = 0;
|
f->detect_invalid_pointer_pairs = 0;
|
||||||
f->detect_container_overflow = true;
|
f->detect_container_overflow = true;
|
||||||
|
f->detect_odr_violation = 2;
|
||||||
|
|
||||||
// Override from compile definition.
|
// Override from compile definition.
|
||||||
ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefinition());
|
ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefinition());
|
||||||
|
@ -456,13 +460,6 @@ static NOINLINE void force_interface_symbols() {
|
||||||
case 15: __asan_set_error_report_callback(0); break;
|
case 15: __asan_set_error_report_callback(0); break;
|
||||||
case 16: __asan_handle_no_return(); break;
|
case 16: __asan_handle_no_return(); break;
|
||||||
case 17: __asan_address_is_poisoned(0); break;
|
case 17: __asan_address_is_poisoned(0); break;
|
||||||
case 18: __asan_get_allocated_size(0); break;
|
|
||||||
case 19: __asan_get_current_allocated_bytes(); break;
|
|
||||||
case 20: __asan_get_estimated_allocated_size(0); break;
|
|
||||||
case 21: __asan_get_free_bytes(); break;
|
|
||||||
case 22: __asan_get_heap_size(); break;
|
|
||||||
case 23: __asan_get_ownership(0); break;
|
|
||||||
case 24: __asan_get_unmapped_bytes(); break;
|
|
||||||
case 25: __asan_poison_memory_region(0, 0); break;
|
case 25: __asan_poison_memory_region(0, 0); break;
|
||||||
case 26: __asan_unpoison_memory_region(0, 0); break;
|
case 26: __asan_unpoison_memory_region(0, 0); break;
|
||||||
case 27: __asan_set_error_exit_code(0); break;
|
case 27: __asan_set_error_exit_code(0); break;
|
||||||
|
@ -593,6 +590,11 @@ static void AsanInitInternal() {
|
||||||
|
|
||||||
InitializeAsanInterceptors();
|
InitializeAsanInterceptors();
|
||||||
|
|
||||||
|
// Enable system log ("adb logcat") on Android.
|
||||||
|
// Doing this before interceptors are initialized crashes in:
|
||||||
|
// AsanInitInternal -> android_log_write -> __interceptor_strcmp
|
||||||
|
AndroidLogInit();
|
||||||
|
|
||||||
ReplaceSystemMalloc();
|
ReplaceSystemMalloc();
|
||||||
|
|
||||||
uptr shadow_start = kLowShadowBeg;
|
uptr shadow_start = kLowShadowBeg;
|
||||||
|
@ -601,7 +603,8 @@ static void AsanInitInternal() {
|
||||||
bool full_shadow_is_available =
|
bool full_shadow_is_available =
|
||||||
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
|
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
|
||||||
|
|
||||||
#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING
|
#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
|
||||||
|
!ASAN_FIXED_MAPPING
|
||||||
if (!full_shadow_is_available) {
|
if (!full_shadow_is_available) {
|
||||||
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
|
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
|
||||||
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
|
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
|
||||||
|
@ -611,9 +614,7 @@ static void AsanInitInternal() {
|
||||||
if (common_flags()->verbosity)
|
if (common_flags()->verbosity)
|
||||||
PrintAddressSpaceLayout();
|
PrintAddressSpaceLayout();
|
||||||
|
|
||||||
if (flags()->disable_core) {
|
DisableCoreDumperIfNecessary();
|
||||||
DisableCoreDumper();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_shadow_is_available) {
|
if (full_shadow_is_available) {
|
||||||
// mmap the low shadow plus at least one page at the left.
|
// mmap the low shadow plus at least one page at the left.
|
||||||
|
@ -648,12 +649,8 @@ static void AsanInitInternal() {
|
||||||
AsanTSDInit(PlatformTSDDtor);
|
AsanTSDInit(PlatformTSDDtor);
|
||||||
InstallDeadlySignalHandlers(AsanOnSIGSEGV);
|
InstallDeadlySignalHandlers(AsanOnSIGSEGV);
|
||||||
|
|
||||||
// Allocator should be initialized before starting external symbolizer, as
|
|
||||||
// fork() on Mac locks the allocator.
|
|
||||||
InitializeAllocator();
|
InitializeAllocator();
|
||||||
|
|
||||||
Symbolizer::Init(common_flags()->external_symbolizer_path);
|
|
||||||
|
|
||||||
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
|
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
|
||||||
// should be set to 1 prior to initializing the threads.
|
// should be set to 1 prior to initializing the threads.
|
||||||
asan_inited = 1;
|
asan_inited = 1;
|
||||||
|
@ -682,7 +679,7 @@ static void AsanInitInternal() {
|
||||||
SanitizerInitializeUnwinder();
|
SanitizerInitializeUnwinder();
|
||||||
|
|
||||||
#if CAN_SANITIZE_LEAKS
|
#if CAN_SANITIZE_LEAKS
|
||||||
__lsan::InitCommonLsan();
|
__lsan::InitCommonLsan(false);
|
||||||
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
|
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
|
||||||
Atexit(__lsan::DoLeakCheck);
|
Atexit(__lsan::DoLeakCheck);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "asan_internal.h"
|
#include "asan_internal.h"
|
||||||
#include "asan_stats.h"
|
#include "asan_stats.h"
|
||||||
#include "asan_thread.h"
|
#include "asan_thread.h"
|
||||||
|
#include "sanitizer_common/sanitizer_allocator_interface.h"
|
||||||
#include "sanitizer_common/sanitizer_mutex.h"
|
#include "sanitizer_common/sanitizer_mutex.h"
|
||||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||||
|
|
||||||
|
@ -127,8 +128,8 @@ static void PrintAccumulatedStats() {
|
||||||
BlockingMutexLock lock(&print_lock);
|
BlockingMutexLock lock(&print_lock);
|
||||||
stats.Print();
|
stats.Print();
|
||||||
StackDepotStats *stack_depot_stats = StackDepotGetStats();
|
StackDepotStats *stack_depot_stats = StackDepotGetStats();
|
||||||
Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
|
Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
|
||||||
stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20);
|
stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
|
||||||
PrintInternalAllocatorStats();
|
PrintInternalAllocatorStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +138,7 @@ static void PrintAccumulatedStats() {
|
||||||
// ---------------------- Interface ---------------- {{{1
|
// ---------------------- Interface ---------------- {{{1
|
||||||
using namespace __asan; // NOLINT
|
using namespace __asan; // NOLINT
|
||||||
|
|
||||||
uptr __asan_get_current_allocated_bytes() {
|
uptr __sanitizer_get_current_allocated_bytes() {
|
||||||
AsanStats stats;
|
AsanStats stats;
|
||||||
GetAccumulatedStats(&stats);
|
GetAccumulatedStats(&stats);
|
||||||
uptr malloced = stats.malloced;
|
uptr malloced = stats.malloced;
|
||||||
|
@ -147,13 +148,13 @@ uptr __asan_get_current_allocated_bytes() {
|
||||||
return (malloced > freed) ? malloced - freed : 1;
|
return (malloced > freed) ? malloced - freed : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uptr __asan_get_heap_size() {
|
uptr __sanitizer_get_heap_size() {
|
||||||
AsanStats stats;
|
AsanStats stats;
|
||||||
GetAccumulatedStats(&stats);
|
GetAccumulatedStats(&stats);
|
||||||
return stats.mmaped - stats.munmaped;
|
return stats.mmaped - stats.munmaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
uptr __asan_get_free_bytes() {
|
uptr __sanitizer_get_free_bytes() {
|
||||||
AsanStats stats;
|
AsanStats stats;
|
||||||
GetAccumulatedStats(&stats);
|
GetAccumulatedStats(&stats);
|
||||||
uptr total_free = stats.mmaped
|
uptr total_free = stats.mmaped
|
||||||
|
@ -167,7 +168,7 @@ uptr __asan_get_free_bytes() {
|
||||||
return (total_free > total_used) ? total_free - total_used : 1;
|
return (total_free > total_used) ? total_free - total_used : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uptr __asan_get_unmapped_bytes() {
|
uptr __sanitizer_get_unmapped_bytes() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,10 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsanThread::Init() {
|
void AsanThread::Init() {
|
||||||
|
fake_stack_ = 0; // Will be initialized lazily if needed.
|
||||||
|
CHECK_EQ(this->stack_size(), 0U);
|
||||||
SetThreadStackAndTls();
|
SetThreadStackAndTls();
|
||||||
|
CHECK_GT(this->stack_size(), 0U);
|
||||||
CHECK(AddrIsInMem(stack_bottom_));
|
CHECK(AddrIsInMem(stack_bottom_));
|
||||||
CHECK(AddrIsInMem(stack_top_ - 1));
|
CHECK(AddrIsInMem(stack_top_ - 1));
|
||||||
ClearShadowForThreadStackAndTLS();
|
ClearShadowForThreadStackAndTLS();
|
||||||
|
@ -147,7 +150,6 @@ void AsanThread::Init() {
|
||||||
VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
|
VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
|
||||||
(void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
|
(void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
|
||||||
&local);
|
&local);
|
||||||
fake_stack_ = 0; // Will be initialized lazily if needed.
|
|
||||||
AsanPlatformThreadInit();
|
AsanPlatformThreadInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "asan_interceptors.h"
|
#include "asan_interceptors.h"
|
||||||
#include "asan_internal.h"
|
#include "asan_internal.h"
|
||||||
|
#include "asan_report.h"
|
||||||
#include "asan_thread.h"
|
#include "asan_thread.h"
|
||||||
#include "sanitizer_common/sanitizer_libc.h"
|
#include "sanitizer_common/sanitizer_libc.h"
|
||||||
#include "sanitizer_common/sanitizer_mutex.h"
|
#include "sanitizer_common/sanitizer_mutex.h"
|
||||||
|
@ -68,7 +69,7 @@ void *AsanDoesNotSupportStaticLinkage() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsanCheckDynamicRTPrereqs() { UNIMPLEMENTED(); }
|
void AsanCheckDynamicRTPrereqs() {}
|
||||||
|
|
||||||
void AsanCheckIncompatibleRT() {}
|
void AsanCheckIncompatibleRT() {}
|
||||||
|
|
||||||
|
@ -84,6 +85,67 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
|
||||||
|
|
||||||
|
static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
|
||||||
|
EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
|
||||||
|
CONTEXT *context = info->ContextRecord;
|
||||||
|
uptr pc = (uptr)exception_record->ExceptionAddress;
|
||||||
|
#ifdef _WIN64
|
||||||
|
uptr bp = (uptr)context->Rbp, sp = (uptr)context->Rsp;
|
||||||
|
#else
|
||||||
|
uptr bp = (uptr)context->Ebp, sp = (uptr)context->Esp;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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";
|
||||||
|
uptr access_addr = exception_record->ExceptionInformation[1];
|
||||||
|
ReportSIGSEGV(description, pc, sp, bp, context, access_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
|
||||||
|
|
||||||
|
return default_seh_handler(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to install our own exception handler (EH) to print helpful reports
|
||||||
|
// on access violations and whatnot. Unfortunately, the CRT initializers assume
|
||||||
|
// they are run before any user code and drop any previously-installed EHs on
|
||||||
|
// the floor, so we can't install our handler inside __asan_init.
|
||||||
|
// (See crt0dat.c in the CRT sources for the details)
|
||||||
|
//
|
||||||
|
// Things get even more complicated with the dynamic runtime, as it finishes its
|
||||||
|
// initialization before the .exe module CRT begins to initialize.
|
||||||
|
//
|
||||||
|
// For the static runtime (-MT), it's enough to put a callback to
|
||||||
|
// __asan_set_seh_filter in the last section for C initializers.
|
||||||
|
//
|
||||||
|
// For the dynamic runtime (-MD), we want link the same
|
||||||
|
// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
|
||||||
|
// will be called for each instrumented module. This ensures that at least one
|
||||||
|
// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
|
||||||
|
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
int __asan_set_seh_filter() {
|
||||||
|
// We should only store the previous handler if it's not our own handler in
|
||||||
|
// order to avoid loops in the EH chain.
|
||||||
|
auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
|
||||||
|
if (prev_seh_handler != &SEHHandler)
|
||||||
|
default_seh_handler = prev_seh_handler;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
|
static __declspec(allocate(".CRT$XIZ"))
|
||||||
|
int (*__intercept_seh)() = __asan_set_seh_filter;
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace __asan
|
} // namespace __asan
|
||||||
|
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//===-- asan_dll_thunk.cc -------------------------------------------------===//
|
//===-- asan_win_dll_thunk.cc ---------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This file is distributed under the University of Illinois Open Source
|
// This file is distributed under the University of Illinois Open Source
|
||||||
// License. See LICENSE.TXT for details.
|
// License. See LICENSE.TXT for details.
|
||||||
|
@ -18,9 +18,10 @@
|
||||||
// Using #ifdef rather than relying on Makefiles etc.
|
// Using #ifdef rather than relying on Makefiles etc.
|
||||||
// simplifies the build procedure.
|
// simplifies the build procedure.
|
||||||
#ifdef ASAN_DLL_THUNK
|
#ifdef ASAN_DLL_THUNK
|
||||||
|
#include "asan_init_version.h"
|
||||||
#include "sanitizer_common/sanitizer_interception.h"
|
#include "sanitizer_common/sanitizer_interception.h"
|
||||||
|
|
||||||
// ----------------- Helper functions and macros --------------------- {{{1
|
// ---------- Function interception helper functions and macros ----------- {{{1
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void *__stdcall GetModuleHandleA(const char *module_name);
|
void *__stdcall GetModuleHandleA(const char *module_name);
|
||||||
void *__stdcall GetProcAddress(void *module, const char *proc_name);
|
void *__stdcall GetProcAddress(void *module, const char *proc_name);
|
||||||
|
@ -34,68 +35,143 @@ static void *getRealProcAddressOrDie(const char *name) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to intercept some functions (e.g. ASan interface, memory allocator --
|
||||||
|
// let's call them "hooks") exported by the DLL thunk and forward the hooks to
|
||||||
|
// the runtime in the main module.
|
||||||
|
// However, we don't want to keep two lists of these hooks.
|
||||||
|
// To avoid that, the list of hooks should be defined using the
|
||||||
|
// INTERCEPT_WHEN_POSSIBLE macro. Then, all these hooks can be intercepted
|
||||||
|
// at once by calling INTERCEPT_HOOKS().
|
||||||
|
|
||||||
|
// Use macro+template magic to automatically generate the list of hooks.
|
||||||
|
// Each hook at line LINE defines a template class with a static
|
||||||
|
// FunctionInterceptor<LINE>::Execute() method intercepting the hook.
|
||||||
|
// The default implementation of FunctionInterceptor<LINE> is to call
|
||||||
|
// the Execute() method corresponding to the previous line.
|
||||||
|
template<int LINE>
|
||||||
|
struct FunctionInterceptor {
|
||||||
|
static void Execute() { FunctionInterceptor<LINE-1>::Execute(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// There shouldn't be any hooks with negative definition line number.
|
||||||
|
template<>
|
||||||
|
struct FunctionInterceptor<0> {
|
||||||
|
static void Execute() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define INTERCEPT_WHEN_POSSIBLE(main_function, dll_function) \
|
||||||
|
template<> struct FunctionInterceptor<__LINE__> { \
|
||||||
|
static void Execute() { \
|
||||||
|
void *wrapper = getRealProcAddressOrDie(main_function); \
|
||||||
|
if (!__interception::OverrideFunction((uptr)dll_function, \
|
||||||
|
(uptr)wrapper, 0)) \
|
||||||
|
abort(); \
|
||||||
|
FunctionInterceptor<__LINE__-1>::Execute(); \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
// Special case of hooks -- ASan own interface functions. Those are only called
|
||||||
|
// after __asan_init, thus an empty implementation is sufficient.
|
||||||
|
#define INTERFACE_FUNCTION(name) \
|
||||||
|
extern "C" __declspec(noinline) void name() { \
|
||||||
|
volatile int prevent_icf = (__LINE__ << 8); (void)prevent_icf; \
|
||||||
|
__debugbreak(); \
|
||||||
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name)
|
||||||
|
|
||||||
|
// INTERCEPT_HOOKS must be used after the last INTERCEPT_WHEN_POSSIBLE.
|
||||||
|
#define INTERCEPT_HOOKS FunctionInterceptor<__LINE__>::Execute
|
||||||
|
|
||||||
|
// We can't define our own version of strlen etc. because that would lead to
|
||||||
|
// link-time or even type mismatch errors. Instead, we can declare a function
|
||||||
|
// just to be able to get its address. Me may miss the first few calls to the
|
||||||
|
// functions since it can be called before __asan_init, but that would lead to
|
||||||
|
// false negatives in the startup code before user's global initializers, which
|
||||||
|
// isn't a big deal.
|
||||||
|
#define INTERCEPT_LIBRARY_FUNCTION(name) \
|
||||||
|
extern "C" void name(); \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(WRAPPER_NAME(name), name)
|
||||||
|
|
||||||
|
// Disable compiler warnings that show up if we declare our own version
|
||||||
|
// of a compiler intrinsic (e.g. strlen).
|
||||||
|
#pragma warning(disable: 4391)
|
||||||
|
#pragma warning(disable: 4392)
|
||||||
|
|
||||||
|
static void InterceptHooks();
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
// ---------- Function wrapping helpers ----------------------------------- {{{1
|
||||||
#define WRAP_V_V(name) \
|
#define WRAP_V_V(name) \
|
||||||
extern "C" void name() { \
|
extern "C" void name() { \
|
||||||
typedef void (*fntype)(); \
|
typedef void (*fntype)(); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
fn(); \
|
fn(); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_V_W(name) \
|
#define WRAP_V_W(name) \
|
||||||
extern "C" void name(void *arg) { \
|
extern "C" void name(void *arg) { \
|
||||||
typedef void (*fntype)(void *arg); \
|
typedef void (*fntype)(void *arg); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
fn(arg); \
|
fn(arg); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_V_WW(name) \
|
#define WRAP_V_WW(name) \
|
||||||
extern "C" void name(void *arg1, void *arg2) { \
|
extern "C" void name(void *arg1, void *arg2) { \
|
||||||
typedef void (*fntype)(void *, void *); \
|
typedef void (*fntype)(void *, void *); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
fn(arg1, arg2); \
|
fn(arg1, arg2); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_V_WWW(name) \
|
#define WRAP_V_WWW(name) \
|
||||||
extern "C" void name(void *arg1, void *arg2, void *arg3) { \
|
extern "C" void name(void *arg1, void *arg2, void *arg3) { \
|
||||||
typedef void *(*fntype)(void *, void *, void *); \
|
typedef void *(*fntype)(void *, void *, void *); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
fn(arg1, arg2, arg3); \
|
fn(arg1, arg2, arg3); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_W_V(name) \
|
#define WRAP_W_V(name) \
|
||||||
extern "C" void *name() { \
|
extern "C" void *name() { \
|
||||||
typedef void *(*fntype)(); \
|
typedef void *(*fntype)(); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
return fn(); \
|
return fn(); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_W_W(name) \
|
#define WRAP_W_W(name) \
|
||||||
extern "C" void *name(void *arg) { \
|
extern "C" void *name(void *arg) { \
|
||||||
typedef void *(*fntype)(void *arg); \
|
typedef void *(*fntype)(void *arg); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
return fn(arg); \
|
return fn(arg); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_W_WW(name) \
|
#define WRAP_W_WW(name) \
|
||||||
extern "C" void *name(void *arg1, void *arg2) { \
|
extern "C" void *name(void *arg1, void *arg2) { \
|
||||||
typedef void *(*fntype)(void *, void *); \
|
typedef void *(*fntype)(void *, void *); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
return fn(arg1, arg2); \
|
return fn(arg1, arg2); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_W_WWW(name) \
|
#define WRAP_W_WWW(name) \
|
||||||
extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
|
extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
|
||||||
typedef void *(*fntype)(void *, void *, void *); \
|
typedef void *(*fntype)(void *, void *, void *); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
return fn(arg1, arg2, arg3); \
|
return fn(arg1, arg2, arg3); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_W_WWWW(name) \
|
#define WRAP_W_WWWW(name) \
|
||||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
|
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
|
||||||
typedef void *(*fntype)(void *, void *, void *, void *); \
|
typedef void *(*fntype)(void *, void *, void *, void *); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
return fn(arg1, arg2, arg3, arg4); \
|
return fn(arg1, arg2, arg3, arg4); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_W_WWWWW(name) \
|
#define WRAP_W_WWWWW(name) \
|
||||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
|
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
|
||||||
|
@ -103,7 +179,8 @@ static void *getRealProcAddressOrDie(const char *name) {
|
||||||
typedef void *(*fntype)(void *, void *, void *, void *, void *); \
|
typedef void *(*fntype)(void *, void *, void *, void *, void *); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
return fn(arg1, arg2, arg3, arg4, arg5); \
|
return fn(arg1, arg2, arg3, arg4, arg5); \
|
||||||
}
|
} \
|
||||||
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
#define WRAP_W_WWWWWW(name) \
|
#define WRAP_W_WWWWWW(name) \
|
||||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
|
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
|
||||||
|
@ -111,48 +188,8 @@ static void *getRealProcAddressOrDie(const char *name) {
|
||||||
typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \
|
typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \
|
||||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||||
return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
|
return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
|
||||||
}
|
} \
|
||||||
// }}}
|
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||||
|
|
||||||
// --------- Interface interception helper functions and macros ----------- {{{1
|
|
||||||
// We need to intercept the ASan interface exported by the DLL thunk and forward
|
|
||||||
// all the functions to the runtime in the main module.
|
|
||||||
// However, we don't want to keep two lists of interface functions.
|
|
||||||
// To avoid that, the list of interface functions should be defined using the
|
|
||||||
// INTERFACE_FUNCTION macro. Then, all the interface can be intercepted at once
|
|
||||||
// by calling INTERCEPT_ASAN_INTERFACE().
|
|
||||||
|
|
||||||
// Use macro+template magic to automatically generate the list of interface
|
|
||||||
// functions. Each interface function at line LINE defines a template class
|
|
||||||
// with a static InterfaceInteceptor<LINE>::Execute() method intercepting the
|
|
||||||
// function. The default implementation of InterfaceInteceptor<LINE> is to call
|
|
||||||
// the Execute() method corresponding to the previous line.
|
|
||||||
template<int LINE>
|
|
||||||
struct InterfaceInteceptor {
|
|
||||||
static void Execute() { InterfaceInteceptor<LINE-1>::Execute(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// There shouldn't be any interface function with negative line number.
|
|
||||||
template<>
|
|
||||||
struct InterfaceInteceptor<0> {
|
|
||||||
static void Execute() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define INTERFACE_FUNCTION(name) \
|
|
||||||
extern "C" void name() { __debugbreak(); } \
|
|
||||||
template<> struct InterfaceInteceptor<__LINE__> { \
|
|
||||||
static void Execute() { \
|
|
||||||
void *wrapper = getRealProcAddressOrDie(#name); \
|
|
||||||
if (!__interception::OverrideFunction((uptr)name, (uptr)wrapper, 0)) \
|
|
||||||
abort(); \
|
|
||||||
InterfaceInteceptor<__LINE__-1>::Execute(); \
|
|
||||||
} \
|
|
||||||
};
|
|
||||||
|
|
||||||
// INTERCEPT_ASAN_INTERFACE must be used after the last INTERFACE_FUNCTION.
|
|
||||||
#define INTERCEPT_ASAN_INTERFACE InterfaceInteceptor<__LINE__>::Execute
|
|
||||||
|
|
||||||
static void InterceptASanInterface();
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// ----------------- ASan own interface functions --------------------
|
// ----------------- ASan own interface functions --------------------
|
||||||
|
@ -165,17 +202,18 @@ extern "C" {
|
||||||
|
|
||||||
// Manually wrap __asan_init as we need to initialize
|
// Manually wrap __asan_init as we need to initialize
|
||||||
// __asan_option_detect_stack_use_after_return afterwards.
|
// __asan_option_detect_stack_use_after_return afterwards.
|
||||||
void __asan_init_v3() {
|
void __asan_init() {
|
||||||
typedef void (*fntype)();
|
typedef void (*fntype)();
|
||||||
static fntype fn = 0;
|
static fntype fn = 0;
|
||||||
|
// __asan_init is expected to be called by only one thread.
|
||||||
if (fn) return;
|
if (fn) return;
|
||||||
|
|
||||||
fn = (fntype)getRealProcAddressOrDie("__asan_init_v3");
|
fn = (fntype)getRealProcAddressOrDie(__asan_init_name);
|
||||||
fn();
|
fn();
|
||||||
__asan_option_detect_stack_use_after_return =
|
__asan_option_detect_stack_use_after_return =
|
||||||
(__asan_should_detect_stack_use_after_return() != 0);
|
(__asan_should_detect_stack_use_after_return() != 0);
|
||||||
|
|
||||||
InterceptASanInterface();
|
InterceptHooks();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +233,20 @@ INTERFACE_FUNCTION(__asan_report_load8)
|
||||||
INTERFACE_FUNCTION(__asan_report_load16)
|
INTERFACE_FUNCTION(__asan_report_load16)
|
||||||
INTERFACE_FUNCTION(__asan_report_load_n)
|
INTERFACE_FUNCTION(__asan_report_load_n)
|
||||||
|
|
||||||
|
INTERFACE_FUNCTION(__asan_store1)
|
||||||
|
INTERFACE_FUNCTION(__asan_store2)
|
||||||
|
INTERFACE_FUNCTION(__asan_store4)
|
||||||
|
INTERFACE_FUNCTION(__asan_store8)
|
||||||
|
INTERFACE_FUNCTION(__asan_store16)
|
||||||
|
INTERFACE_FUNCTION(__asan_storeN)
|
||||||
|
|
||||||
|
INTERFACE_FUNCTION(__asan_load1)
|
||||||
|
INTERFACE_FUNCTION(__asan_load2)
|
||||||
|
INTERFACE_FUNCTION(__asan_load4)
|
||||||
|
INTERFACE_FUNCTION(__asan_load8)
|
||||||
|
INTERFACE_FUNCTION(__asan_load16)
|
||||||
|
INTERFACE_FUNCTION(__asan_loadN)
|
||||||
|
|
||||||
INTERFACE_FUNCTION(__asan_memcpy);
|
INTERFACE_FUNCTION(__asan_memcpy);
|
||||||
INTERFACE_FUNCTION(__asan_memset);
|
INTERFACE_FUNCTION(__asan_memset);
|
||||||
INTERFACE_FUNCTION(__asan_memmove);
|
INTERFACE_FUNCTION(__asan_memmove);
|
||||||
|
@ -211,6 +263,9 @@ INTERFACE_FUNCTION(__asan_unpoison_stack_memory)
|
||||||
INTERFACE_FUNCTION(__asan_poison_memory_region)
|
INTERFACE_FUNCTION(__asan_poison_memory_region)
|
||||||
INTERFACE_FUNCTION(__asan_unpoison_memory_region)
|
INTERFACE_FUNCTION(__asan_unpoison_memory_region)
|
||||||
|
|
||||||
|
INTERFACE_FUNCTION(__asan_address_is_poisoned)
|
||||||
|
INTERFACE_FUNCTION(__asan_region_is_poisoned)
|
||||||
|
|
||||||
INTERFACE_FUNCTION(__asan_get_current_fake_stack)
|
INTERFACE_FUNCTION(__asan_get_current_fake_stack)
|
||||||
INTERFACE_FUNCTION(__asan_addr_is_in_fake_stack)
|
INTERFACE_FUNCTION(__asan_addr_is_in_fake_stack)
|
||||||
|
|
||||||
|
@ -237,6 +292,8 @@ INTERFACE_FUNCTION(__asan_stack_free_8)
|
||||||
INTERFACE_FUNCTION(__asan_stack_free_9)
|
INTERFACE_FUNCTION(__asan_stack_free_9)
|
||||||
INTERFACE_FUNCTION(__asan_stack_free_10)
|
INTERFACE_FUNCTION(__asan_stack_free_10)
|
||||||
|
|
||||||
|
INTERFACE_FUNCTION(__sanitizer_cov_module_init)
|
||||||
|
|
||||||
// TODO(timurrrr): Add more interface functions on the as-needed basis.
|
// TODO(timurrrr): Add more interface functions on the as-needed basis.
|
||||||
|
|
||||||
// ----------------- Memory allocation functions ---------------------
|
// ----------------- Memory allocation functions ---------------------
|
||||||
|
@ -263,8 +320,55 @@ WRAP_W_W(_expand_dbg)
|
||||||
|
|
||||||
// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc).
|
// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc).
|
||||||
|
|
||||||
void InterceptASanInterface() {
|
INTERCEPT_LIBRARY_FUNCTION(atoi);
|
||||||
INTERCEPT_ASAN_INTERFACE();
|
INTERCEPT_LIBRARY_FUNCTION(atol);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(_except_handler3);
|
||||||
|
|
||||||
|
// _except_handler4 checks -GS cookie which is different for each module, so we
|
||||||
|
// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4).
|
||||||
|
INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
|
||||||
|
__asan_handle_no_return();
|
||||||
|
return REAL(_except_handler4)(a, b, c, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(frexp);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(longjmp);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(memchr);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(memcmp);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(memcpy);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(memmove);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(memset);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strchr);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strcmp);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strlen);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strncat);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strncmp);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strncpy);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strnlen);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(strtol);
|
||||||
|
INTERCEPT_LIBRARY_FUNCTION(wcslen);
|
||||||
|
|
||||||
|
// Must be after all the interceptor declarations due to the way INTERCEPT_HOOKS
|
||||||
|
// is defined.
|
||||||
|
void InterceptHooks() {
|
||||||
|
INTERCEPT_HOOKS();
|
||||||
|
INTERCEPT_FUNCTION(_except_handler4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to call __asan_init before C/C++ initializers/constructors are
|
||||||
|
// executed, otherwise functions like memset might be invoked.
|
||||||
|
// For some strange reason, merely linking in asan_preinit.cc doesn't work
|
||||||
|
// as the callback is never called... Is link.exe doing something too smart?
|
||||||
|
|
||||||
|
// In DLLs, the callbacks are expected to return 0,
|
||||||
|
// otherwise CRT initialization fails.
|
||||||
|
static int call_asan_init() {
|
||||||
|
__asan_init();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#pragma section(".CRT$XIB", long, read) // NOLINT
|
||||||
|
__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = call_asan_init;
|
||||||
|
|
||||||
#endif // ASAN_DLL_THUNK
|
#endif // ASAN_DLL_THUNK
|
|
@ -0,0 +1,50 @@
|
||||||
|
//===-- asan_win_uar_thunk.cc ---------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||||
|
//
|
||||||
|
// This file defines things that need to be present in the application modules
|
||||||
|
// to interact with the ASan DLL runtime correctly and can't be implemented
|
||||||
|
// using the default "import library" generated when linking the DLL RTL.
|
||||||
|
//
|
||||||
|
// This includes:
|
||||||
|
// - forwarding the detect_stack_use_after_return runtime option
|
||||||
|
// - installing a custom SEH handler
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
// Only compile this code when buidling asan_dynamic_runtime_thunk.lib
|
||||||
|
// Using #ifdef rather than relying on Makefiles etc.
|
||||||
|
// simplifies the build procedure.
|
||||||
|
#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
|
||||||
|
extern "C" {
|
||||||
|
__declspec(dllimport) int __asan_set_seh_filter();
|
||||||
|
__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
|
||||||
|
|
||||||
|
// Define a copy of __asan_option_detect_stack_use_after_return that should be
|
||||||
|
// used when linking an MD runtime with a set of object files on Windows.
|
||||||
|
//
|
||||||
|
// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return',
|
||||||
|
// so normally we would just dllimport it. Unfortunately, the dllimport
|
||||||
|
// 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.
|
||||||
|
int __asan_option_detect_stack_use_after_return =
|
||||||
|
__asan_should_detect_stack_use_after_return();
|
||||||
|
|
||||||
|
// Set the ASan-specific SEH handler at the end of CRT initialization of each
|
||||||
|
// module (see asan_win.cc for the details).
|
||||||
|
//
|
||||||
|
// Unfortunately, putting a pointer to __asan_set_seh_filter into
|
||||||
|
// __asan_intercept_seh gets optimized out, so we have to use an extra function.
|
||||||
|
static int SetSEHFilter() { return __asan_set_seh_filter(); }
|
||||||
|
#pragma section(".CRT$XIZ", long, read) // NOLINT
|
||||||
|
__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter;
|
||||||
|
}
|
||||||
|
#endif // ASAN_DYNAMIC_RUNTIME_THUNK
|
|
@ -3,4 +3,4 @@
|
||||||
# a separate file so that version updates don't involve re-running
|
# a separate file so that version updates don't involve re-running
|
||||||
# automake.
|
# automake.
|
||||||
# CURRENT:REVISION:AGE
|
# CURRENT:REVISION:AGE
|
||||||
1:0:0
|
2:0:0
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
//===-- allocator_interface.h ---------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Public interface header for allocator used in sanitizers (ASan/TSan/MSan).
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#ifndef SANITIZER_ALLOCATOR_INTERFACE_H
|
||||||
|
#define SANITIZER_ALLOCATOR_INTERFACE_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
/* Returns the estimated number of bytes that will be reserved by allocator
|
||||||
|
for request of "size" bytes. If allocator can't allocate that much
|
||||||
|
memory, returns the maximal possible allocation size, otherwise returns
|
||||||
|
"size". */
|
||||||
|
size_t __sanitizer_get_estimated_allocated_size(size_t size);
|
||||||
|
|
||||||
|
/* Returns true if p was returned by the allocator and
|
||||||
|
is not yet freed. */
|
||||||
|
int __sanitizer_get_ownership(const volatile void *p);
|
||||||
|
|
||||||
|
/* Returns the number of bytes reserved for the pointer p.
|
||||||
|
Requires (get_ownership(p) == true) or (p == 0). */
|
||||||
|
size_t __sanitizer_get_allocated_size(const volatile void *p);
|
||||||
|
|
||||||
|
/* Number of bytes, allocated and not yet freed by the application. */
|
||||||
|
size_t __sanitizer_get_current_allocated_bytes();
|
||||||
|
|
||||||
|
/* Number of bytes, mmaped by the allocator to fulfill allocation requests.
|
||||||
|
Generally, for request of X bytes, allocator can reserve and add to free
|
||||||
|
lists a large number of chunks of size X to use them for future requests.
|
||||||
|
All these chunks count toward the heap size. Currently, allocator never
|
||||||
|
releases memory to OS (instead, it just puts freed chunks to free
|
||||||
|
lists). */
|
||||||
|
size_t __sanitizer_get_heap_size();
|
||||||
|
|
||||||
|
/* Number of bytes, mmaped by the allocator, which can be used to fulfill
|
||||||
|
allocation requests. When a user program frees memory chunk, it can first
|
||||||
|
fall into quarantine and will count toward __sanitizer_get_free_bytes()
|
||||||
|
later. */
|
||||||
|
size_t __sanitizer_get_free_bytes();
|
||||||
|
|
||||||
|
/* Number of bytes in unmapped pages, that are released to OS. Currently,
|
||||||
|
always returns 0. */
|
||||||
|
size_t __sanitizer_get_unmapped_bytes();
|
||||||
|
|
||||||
|
/* Malloc hooks that may be optionally provided by user.
|
||||||
|
__sanitizer_malloc_hook(ptr, size) is called immediately after
|
||||||
|
allocation of "size" bytes, which returned "ptr".
|
||||||
|
__sanitizer_free_hook(ptr) is called immediately before
|
||||||
|
deallocation of "ptr". */
|
||||||
|
void __sanitizer_malloc_hook(const volatile void *ptr, size_t size);
|
||||||
|
void __sanitizer_free_hook(const volatile void *ptr);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -60,6 +60,22 @@ extern "C" {
|
||||||
// Print the description of addr (useful when debugging in gdb).
|
// Print the description of addr (useful when debugging in gdb).
|
||||||
void __asan_describe_address(void *addr);
|
void __asan_describe_address(void *addr);
|
||||||
|
|
||||||
|
// Useful for calling from the debugger to get the allocation stack trace
|
||||||
|
// and thread ID for a heap address. Stores up to 'size' frames into 'trace',
|
||||||
|
// returns the number of stored frames or 0 on error.
|
||||||
|
size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size,
|
||||||
|
int *thread_id);
|
||||||
|
|
||||||
|
// Useful for calling from the debugger to get the free stack trace
|
||||||
|
// and thread ID for a heap address. Stores up to 'size' frames into 'trace',
|
||||||
|
// returns the number of stored frames or 0 on error.
|
||||||
|
size_t __asan_get_free_stack(void *addr, void **trace, size_t size,
|
||||||
|
int *thread_id);
|
||||||
|
|
||||||
|
// Useful for calling from the debugger to get the current shadow memory
|
||||||
|
// mapping.
|
||||||
|
void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset);
|
||||||
|
|
||||||
// This is an internal function that is called to report an error.
|
// This is an internal function that is called to report an error.
|
||||||
// However it is still a part of the interface because users may want to
|
// However it is still a part of the interface because users may want to
|
||||||
// set a breakpoint on this function in a debugger.
|
// set a breakpoint on this function in a debugger.
|
||||||
|
@ -81,32 +97,6 @@ extern "C" {
|
||||||
// the program crashes before ASan report is printed.
|
// the program crashes before ASan report is printed.
|
||||||
void __asan_on_error();
|
void __asan_on_error();
|
||||||
|
|
||||||
// Returns the estimated number of bytes that will be reserved by allocator
|
|
||||||
// for request of "size" bytes. If ASan allocator can't allocate that much
|
|
||||||
// memory, returns the maximal possible allocation size, otherwise returns
|
|
||||||
// "size".
|
|
||||||
size_t __asan_get_estimated_allocated_size(size_t size);
|
|
||||||
// Returns 1 if p was returned by the ASan allocator and is not yet freed.
|
|
||||||
// Otherwise returns 0.
|
|
||||||
int __asan_get_ownership(const void *p);
|
|
||||||
// Returns the number of bytes reserved for the pointer p.
|
|
||||||
// Requires (get_ownership(p) == true) or (p == 0).
|
|
||||||
size_t __asan_get_allocated_size(const void *p);
|
|
||||||
// Number of bytes, allocated and not yet freed by the application.
|
|
||||||
size_t __asan_get_current_allocated_bytes();
|
|
||||||
// Number of bytes, mmaped by asan allocator to fulfill allocation requests.
|
|
||||||
// Generally, for request of X bytes, allocator can reserve and add to free
|
|
||||||
// lists a large number of chunks of size X to use them for future requests.
|
|
||||||
// All these chunks count toward the heap size. Currently, allocator never
|
|
||||||
// releases memory to OS (instead, it just puts freed chunks to free lists).
|
|
||||||
size_t __asan_get_heap_size();
|
|
||||||
// Number of bytes, mmaped by asan allocator, which can be used to fulfill
|
|
||||||
// allocation requests. When a user program frees memory chunk, it can first
|
|
||||||
// fall into quarantine and will count toward __asan_get_free_bytes() later.
|
|
||||||
size_t __asan_get_free_bytes();
|
|
||||||
// Number of bytes in unmapped pages, that are released to OS. Currently,
|
|
||||||
// always returns 0.
|
|
||||||
size_t __asan_get_unmapped_bytes();
|
|
||||||
// Prints accumulated stats to stderr. Used for debugging.
|
// Prints accumulated stats to stderr. Used for debugging.
|
||||||
void __asan_print_accumulated_stats();
|
void __asan_print_accumulated_stats();
|
||||||
|
|
||||||
|
@ -114,14 +104,6 @@ extern "C" {
|
||||||
// a string containing ASan runtime options. See asan_flags.h for details.
|
// a string containing ASan runtime options. See asan_flags.h for details.
|
||||||
const char* __asan_default_options();
|
const char* __asan_default_options();
|
||||||
|
|
||||||
// Malloc hooks that may be optionally provided by user.
|
|
||||||
// __asan_malloc_hook(ptr, size) is called immediately after
|
|
||||||
// allocation of "size" bytes, which returned "ptr".
|
|
||||||
// __asan_free_hook(ptr) is called immediately before
|
|
||||||
// deallocation of "ptr".
|
|
||||||
void __asan_malloc_hook(void *ptr, size_t size);
|
|
||||||
void __asan_free_hook(void *ptr);
|
|
||||||
|
|
||||||
// The following 2 functions facilitate garbage collection in presence of
|
// The following 2 functions facilitate garbage collection in presence of
|
||||||
// asan's fake stack.
|
// asan's fake stack.
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,10 @@ extern "C" {
|
||||||
void __sanitizer_cov_init();
|
void __sanitizer_cov_init();
|
||||||
// Record and dump coverage info.
|
// Record and dump coverage info.
|
||||||
void __sanitizer_cov_dump();
|
void __sanitizer_cov_dump();
|
||||||
|
// Open <name>.sancov.packed in the coverage directory and return the file
|
||||||
|
// descriptor. Returns -1 on failure, or if coverage dumping is disabled.
|
||||||
|
// This is intended for use by sandboxing code.
|
||||||
|
intptr_t __sanitizer_maybe_open_cov_file(const char *name);
|
||||||
|
|
||||||
// Annotate the current state of a contiguous container, such as
|
// Annotate the current state of a contiguous container, such as
|
||||||
// std::vector, std::string or similar.
|
// std::vector, std::string or similar.
|
||||||
|
|
|
@ -17,10 +17,6 @@
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
/* Returns a string describing a stack origin.
|
|
||||||
Return NULL if the origin is invalid, or is not a stack origin. */
|
|
||||||
const char *__msan_get_origin_descr_if_stack(uint32_t id);
|
|
||||||
|
|
||||||
/* Set raw origin for the memory range. */
|
/* Set raw origin for the memory range. */
|
||||||
void __msan_set_origin(const volatile void *a, size_t size, uint32_t origin);
|
void __msan_set_origin(const volatile void *a, size_t size, uint32_t origin);
|
||||||
|
|
||||||
|
@ -91,55 +87,10 @@ extern "C" {
|
||||||
a string containing Msan runtime options. See msan_flags.h for details. */
|
a string containing Msan runtime options. See msan_flags.h for details. */
|
||||||
const char* __msan_default_options();
|
const char* __msan_default_options();
|
||||||
|
|
||||||
// Sets the callback to be called right before death on error.
|
/* Sets the callback to be called right before death on error.
|
||||||
// Passing 0 will unset the callback.
|
Passing 0 will unset the callback. */
|
||||||
void __msan_set_death_callback(void (*callback)(void));
|
void __msan_set_death_callback(void (*callback)(void));
|
||||||
|
|
||||||
/***********************************/
|
|
||||||
/* Allocator statistics interface. */
|
|
||||||
|
|
||||||
/* Returns the estimated number of bytes that will be reserved by allocator
|
|
||||||
for request of "size" bytes. If Msan allocator can't allocate that much
|
|
||||||
memory, returns the maximal possible allocation size, otherwise returns
|
|
||||||
"size". */
|
|
||||||
size_t __msan_get_estimated_allocated_size(size_t size);
|
|
||||||
|
|
||||||
/* Returns true if p was returned by the Msan allocator and
|
|
||||||
is not yet freed. */
|
|
||||||
int __msan_get_ownership(const volatile void *p);
|
|
||||||
|
|
||||||
/* Returns the number of bytes reserved for the pointer p.
|
|
||||||
Requires (get_ownership(p) == true) or (p == 0). */
|
|
||||||
size_t __msan_get_allocated_size(const volatile void *p);
|
|
||||||
|
|
||||||
/* Number of bytes, allocated and not yet freed by the application. */
|
|
||||||
size_t __msan_get_current_allocated_bytes();
|
|
||||||
|
|
||||||
/* Number of bytes, mmaped by msan allocator to fulfill allocation requests.
|
|
||||||
Generally, for request of X bytes, allocator can reserve and add to free
|
|
||||||
lists a large number of chunks of size X to use them for future requests.
|
|
||||||
All these chunks count toward the heap size. Currently, allocator never
|
|
||||||
releases memory to OS (instead, it just puts freed chunks to free
|
|
||||||
lists). */
|
|
||||||
size_t __msan_get_heap_size();
|
|
||||||
|
|
||||||
/* Number of bytes, mmaped by msan allocator, which can be used to fulfill
|
|
||||||
allocation requests. When a user program frees memory chunk, it can first
|
|
||||||
fall into quarantine and will count toward __msan_get_free_bytes()
|
|
||||||
later. */
|
|
||||||
size_t __msan_get_free_bytes();
|
|
||||||
|
|
||||||
/* Number of bytes in unmapped pages, that are released to OS. Currently,
|
|
||||||
always returns 0. */
|
|
||||||
size_t __msan_get_unmapped_bytes();
|
|
||||||
|
|
||||||
/* Malloc hooks that may be optionally provided by user.
|
|
||||||
__msan_malloc_hook(ptr, size) is called immediately after
|
|
||||||
allocation of "size" bytes, which returned "ptr".
|
|
||||||
__msan_free_hook(ptr) is called immediately before
|
|
||||||
deallocation of "ptr". */
|
|
||||||
void __msan_malloc_hook(const volatile void *ptr, size_t size);
|
|
||||||
void __msan_free_hook(const volatile void *ptr);
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -120,19 +120,23 @@ const interpose_substitution substitution_##func_name[] \
|
||||||
# define DECLARE_WRAPPER(ret_type, func, ...)
|
# define DECLARE_WRAPPER(ret_type, func, ...)
|
||||||
|
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
# if defined(_DLL) // DLL CRT
|
# define WRAP(x) __asan_wrap_##x
|
||||||
# define WRAP(x) x
|
# define WRAPPER_NAME(x) "__asan_wrap_"#x
|
||||||
# define WRAPPER_NAME(x) #x
|
# define INTERCEPTOR_ATTRIBUTE __declspec(dllexport)
|
||||||
# define INTERCEPTOR_ATTRIBUTE
|
|
||||||
# else // Static CRT
|
|
||||||
# define WRAP(x) wrap_##x
|
|
||||||
# define WRAPPER_NAME(x) "wrap_"#x
|
|
||||||
# define INTERCEPTOR_ATTRIBUTE
|
|
||||||
# endif
|
|
||||||
# define DECLARE_WRAPPER(ret_type, func, ...) \
|
# define DECLARE_WRAPPER(ret_type, func, ...) \
|
||||||
extern "C" ret_type func(__VA_ARGS__);
|
extern "C" ret_type func(__VA_ARGS__);
|
||||||
# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
|
# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
|
||||||
extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__);
|
extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__);
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
# define WRAP(x) __interceptor_ ## x
|
||||||
|
# define WRAPPER_NAME(x) "__interceptor_" #x
|
||||||
|
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
|
||||||
|
// FreeBSD's dynamic linker (incompliantly) gives non-weak symbols higher
|
||||||
|
// priority than weak ones so weak aliases won't work for indirect calls
|
||||||
|
// in position-independent (-fPIC / -fPIE) mode.
|
||||||
|
# define DECLARE_WRAPPER(ret_type, func, ...) \
|
||||||
|
extern "C" ret_type func(__VA_ARGS__) \
|
||||||
|
__attribute__((alias("__interceptor_" #func), visibility("default")));
|
||||||
#else
|
#else
|
||||||
# define WRAP(x) __interceptor_ ## x
|
# define WRAP(x) __interceptor_ ## x
|
||||||
# define WRAPPER_NAME(x) "__interceptor_" #x
|
# define WRAPPER_NAME(x) "__interceptor_" #x
|
||||||
|
|
|
@ -17,20 +17,6 @@
|
||||||
|
|
||||||
namespace __interception {
|
namespace __interception {
|
||||||
|
|
||||||
bool GetRealFunctionAddress(const char *func_name, uptr *func_addr) {
|
|
||||||
const char *DLLS[] = {
|
|
||||||
"msvcr80.dll",
|
|
||||||
"msvcr90.dll",
|
|
||||||
"kernel32.dll",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
*func_addr = 0;
|
|
||||||
for (size_t i = 0; *func_addr == 0 && DLLS[i]; ++i) {
|
|
||||||
*func_addr = (uptr)GetProcAddress(GetModuleHandleA(DLLS[i]), func_name);
|
|
||||||
}
|
|
||||||
return (*func_addr != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: internal_str* and internal_mem* functions should be moved from the
|
// FIXME: internal_str* and internal_mem* functions should be moved from the
|
||||||
// ASan sources into interception/.
|
// ASan sources into interception/.
|
||||||
|
|
||||||
|
@ -108,9 +94,11 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) {
|
||||||
case 0x458B: // 8B 45 XX = mov eax, dword ptr [ebp+XXh]
|
case 0x458B: // 8B 45 XX = mov eax, dword ptr [ebp+XXh]
|
||||||
case 0x5D8B: // 8B 5D XX = mov ebx, dword ptr [ebp+XXh]
|
case 0x5D8B: // 8B 5D XX = mov ebx, dword ptr [ebp+XXh]
|
||||||
case 0xEC83: // 83 EC XX = sub esp, XX
|
case 0xEC83: // 83 EC XX = sub esp, XX
|
||||||
|
case 0x75FF: // FF 75 XX = push dword ptr [ebp+XXh]
|
||||||
cursor += 3;
|
cursor += 3;
|
||||||
continue;
|
continue;
|
||||||
case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX
|
case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX
|
||||||
|
case 0x25FF: // FF 25 XX YY ZZ WW = jmp dword ptr ds:[WWZZYYXX]
|
||||||
cursor += 6;
|
cursor += 6;
|
||||||
continue;
|
continue;
|
||||||
case 0x3D83: // 83 3D XX YY ZZ WW TT = cmp TT, WWZZYYXX
|
case 0x3D83: // 83 3D XX YY ZZ WW TT = cmp TT, WWZZYYXX
|
||||||
|
@ -119,6 +107,7 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) {
|
||||||
}
|
}
|
||||||
switch (0x00FFFFFF & *(unsigned int*)(code + cursor)) {
|
switch (0x00FFFFFF & *(unsigned int*)(code + cursor)) {
|
||||||
case 0x24448A: // 8A 44 24 XX = mov eal, dword ptr [esp+XXh]
|
case 0x24448A: // 8A 44 24 XX = mov eal, dword ptr [esp+XXh]
|
||||||
|
case 0x24448B: // 8B 44 24 XX = mov eax, dword ptr [esp+XXh]
|
||||||
case 0x244C8B: // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh]
|
case 0x244C8B: // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh]
|
||||||
case 0x24548B: // 8B 54 24 XX = mov edx, dword ptr [esp+XXh]
|
case 0x24548B: // 8B 54 24 XX = mov edx, dword ptr [esp+XXh]
|
||||||
case 0x24748B: // 8B 74 24 XX = mov esi, dword ptr [esp+XXh]
|
case 0x24748B: // 8B 74 24 XX = mov esi, dword ptr [esp+XXh]
|
||||||
|
@ -131,8 +120,9 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) {
|
||||||
// FIXME: Unknown instruction failures might happen when we add a new
|
// FIXME: Unknown instruction failures might happen when we add a new
|
||||||
// interceptor or a new compiler version. In either case, they should result
|
// interceptor or a new compiler version. In either case, they should result
|
||||||
// in visible and readable error messages. However, merely calling abort()
|
// in visible and readable error messages. However, merely calling abort()
|
||||||
// or __debugbreak() leads to an infinite recursion in CheckFailed.
|
// leads to an infinite recursion in CheckFailed.
|
||||||
// Do we have a good way to abort with an error message here?
|
// Do we have a good way to abort with an error message here?
|
||||||
|
__debugbreak();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +179,33 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const void **InterestingDLLsAvailable() {
|
||||||
|
const char *InterestingDLLs[] = { "kernel32.dll", "msvcr120.dll", NULL };
|
||||||
|
static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
|
||||||
|
if (!result[0]) {
|
||||||
|
for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
|
||||||
|
if (HMODULE h = GetModuleHandleA(InterestingDLLs[i]))
|
||||||
|
result[j++] = (void *)h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (const void **)&result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool GetFunctionAddressInDLLs(const char *func_name, uptr *func_addr) {
|
||||||
|
*func_addr = 0;
|
||||||
|
const void **DLLs = InterestingDLLsAvailable();
|
||||||
|
for (size_t i = 0; *func_addr == 0 && DLLs[i]; ++i)
|
||||||
|
*func_addr = (uptr)GetProcAddress((HMODULE)DLLs[i], func_name);
|
||||||
|
return (*func_addr != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func) {
|
||||||
|
uptr orig_func;
|
||||||
|
if (!GetFunctionAddressInDLLs(name, &orig_func))
|
||||||
|
return false;
|
||||||
|
return OverrideFunction(orig_func, new_func, orig_old_func);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace __interception
|
} // namespace __interception
|
||||||
|
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
|
@ -20,27 +20,29 @@
|
||||||
#define INTERCEPTION_WIN_H
|
#define INTERCEPTION_WIN_H
|
||||||
|
|
||||||
namespace __interception {
|
namespace __interception {
|
||||||
// returns true if a function with the given name was found.
|
// All the functions in the OverrideFunction() family return true on success,
|
||||||
bool GetRealFunctionAddress(const char *func_name, uptr *func_addr);
|
// false on failure (including "couldn't find the function").
|
||||||
|
|
||||||
// returns true if the old function existed, false on failure.
|
// Overrides a function by its address.
|
||||||
bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func);
|
bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func = 0);
|
||||||
|
|
||||||
|
// Overrides a function in a system DLL or DLL CRT by its exported name.
|
||||||
|
bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func = 0);
|
||||||
} // namespace __interception
|
} // namespace __interception
|
||||||
|
|
||||||
#if defined(_DLL)
|
#if defined(INTERCEPTION_DYNAMIC_CRT)
|
||||||
# define INTERCEPT_FUNCTION_WIN(func) \
|
#define INTERCEPT_FUNCTION_WIN(func) \
|
||||||
::__interception::GetRealFunctionAddress( \
|
::__interception::OverrideFunction(#func, \
|
||||||
#func, (::__interception::uptr*)&REAL(func))
|
(::__interception::uptr)WRAP(func), \
|
||||||
|
(::__interception::uptr *)&REAL(func))
|
||||||
#else
|
#else
|
||||||
# define INTERCEPT_FUNCTION_WIN(func) \
|
#define INTERCEPT_FUNCTION_WIN(func) \
|
||||||
::__interception::OverrideFunction( \
|
::__interception::OverrideFunction((::__interception::uptr)func, \
|
||||||
(::__interception::uptr)func, \
|
(::__interception::uptr)WRAP(func), \
|
||||||
(::__interception::uptr)WRAP(func), \
|
(::__interception::uptr *)&REAL(func))
|
||||||
(::__interception::uptr*)&REAL(func))
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define INTERCEPT_FUNCTION_VER_WIN(func, symver) \
|
#define INTERCEPT_FUNCTION_VER_WIN(func, symver) INTERCEPT_FUNCTION_WIN(func)
|
||||||
INTERCEPT_FUNCTION_WIN(func)
|
|
||||||
|
|
||||||
#endif // INTERCEPTION_WIN_H
|
#endif // INTERCEPTION_WIN_H
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
|
@ -23,16 +23,6 @@ bool lsan_init_is_running;
|
||||||
|
|
||||||
namespace __lsan {
|
namespace __lsan {
|
||||||
|
|
||||||
static void InitializeCommonFlags() {
|
|
||||||
CommonFlags *cf = common_flags();
|
|
||||||
SetCommonFlagsDefaults(cf);
|
|
||||||
cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
|
|
||||||
cf->malloc_context_size = 30;
|
|
||||||
cf->detect_leaks = true;
|
|
||||||
|
|
||||||
ParseCommonFlagsFromString(cf, GetEnv("LSAN_OPTIONS"));
|
|
||||||
}
|
|
||||||
|
|
||||||
///// Interface to the common LSan module. /////
|
///// Interface to the common LSan module. /////
|
||||||
bool WordIsPoisoned(uptr addr) {
|
bool WordIsPoisoned(uptr addr) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -48,7 +38,7 @@ extern "C" void __lsan_init() {
|
||||||
return;
|
return;
|
||||||
lsan_init_is_running = true;
|
lsan_init_is_running = true;
|
||||||
SanitizerToolName = "LeakSanitizer";
|
SanitizerToolName = "LeakSanitizer";
|
||||||
InitializeCommonFlags();
|
InitCommonLsan(true);
|
||||||
InitializeAllocator();
|
InitializeAllocator();
|
||||||
InitTlsSize();
|
InitTlsSize();
|
||||||
InitializeInterceptors();
|
InitializeInterceptors();
|
||||||
|
@ -58,11 +48,14 @@ extern "C" void __lsan_init() {
|
||||||
ThreadStart(tid, GetTid());
|
ThreadStart(tid, GetTid());
|
||||||
SetCurrentThread(tid);
|
SetCurrentThread(tid);
|
||||||
|
|
||||||
Symbolizer::Init(common_flags()->external_symbolizer_path);
|
|
||||||
|
|
||||||
InitCommonLsan();
|
|
||||||
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
|
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
|
||||||
Atexit(DoLeakCheck);
|
Atexit(DoLeakCheck);
|
||||||
lsan_inited = true;
|
lsan_inited = true;
|
||||||
lsan_init_is_running = false;
|
lsan_init_is_running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
void __sanitizer_print_stack_trace() {
|
||||||
|
GET_STACK_TRACE_FATAL;
|
||||||
|
stack.Print();
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,26 @@
|
||||||
#include "sanitizer_common/sanitizer_flags.h"
|
#include "sanitizer_common/sanitizer_flags.h"
|
||||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||||
|
|
||||||
|
#define GET_STACK_TRACE(max_size, fast) \
|
||||||
|
StackTrace stack; \
|
||||||
|
{ \
|
||||||
|
uptr stack_top = 0, stack_bottom = 0; \
|
||||||
|
ThreadContext *t; \
|
||||||
|
if (fast && (t = CurrentThreadContext())) { \
|
||||||
|
stack_top = t->stack_end(); \
|
||||||
|
stack_bottom = t->stack_begin(); \
|
||||||
|
} \
|
||||||
|
stack.Unwind(max_size, StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
|
||||||
|
/* context */ 0, stack_top, stack_bottom, fast); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GET_STACK_TRACE_FATAL \
|
||||||
|
GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
|
||||||
|
|
||||||
|
#define GET_STACK_TRACE_MALLOC \
|
||||||
|
GET_STACK_TRACE(__sanitizer::common_flags()->malloc_context_size, \
|
||||||
|
common_flags()->fast_unwind_on_malloc)
|
||||||
|
|
||||||
namespace __lsan {
|
namespace __lsan {
|
||||||
|
|
||||||
void InitializeInterceptors();
|
void InitializeInterceptors();
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "lsan_allocator.h"
|
#include "lsan_allocator.h"
|
||||||
|
|
||||||
#include "sanitizer_common/sanitizer_allocator.h"
|
#include "sanitizer_common/sanitizer_allocator.h"
|
||||||
|
#include "sanitizer_common/sanitizer_allocator_interface.h"
|
||||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||||
|
@ -51,7 +52,7 @@ void AllocatorThreadFinish() {
|
||||||
allocator.SwallowCache(&cache);
|
allocator.SwallowCache(&cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ChunkMetadata *Metadata(void *p) {
|
static ChunkMetadata *Metadata(const void *p) {
|
||||||
return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
|
return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,10 +86,12 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
|
||||||
if (cleared && allocator.FromPrimary(p))
|
if (cleared && allocator.FromPrimary(p))
|
||||||
memset(p, 0, size);
|
memset(p, 0, size);
|
||||||
RegisterAllocation(stack, p, size);
|
RegisterAllocation(stack, p, size);
|
||||||
|
if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Deallocate(void *p) {
|
void Deallocate(void *p) {
|
||||||
|
if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
|
||||||
RegisterDeallocation(p);
|
RegisterDeallocation(p);
|
||||||
allocator.Deallocate(&cache, p);
|
allocator.Deallocate(&cache, p);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +114,7 @@ void GetAllocatorCacheRange(uptr *begin, uptr *end) {
|
||||||
*end = *begin + sizeof(cache);
|
*end = *begin + sizeof(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
uptr GetMallocUsableSize(void *p) {
|
uptr GetMallocUsableSize(const void *p) {
|
||||||
ChunkMetadata *m = Metadata(p);
|
ChunkMetadata *m = Metadata(p);
|
||||||
if (!m) return 0;
|
if (!m) return 0;
|
||||||
return m->requested_size;
|
return m->requested_size;
|
||||||
|
@ -198,3 +201,38 @@ IgnoreObjectResult IgnoreObjectLocked(const void *p) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace __lsan
|
} // namespace __lsan
|
||||||
|
|
||||||
|
using namespace __lsan;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __sanitizer_get_current_allocated_bytes() {
|
||||||
|
uptr stats[AllocatorStatCount];
|
||||||
|
allocator.GetStats(stats);
|
||||||
|
return stats[AllocatorStatAllocated];
|
||||||
|
}
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __sanitizer_get_heap_size() {
|
||||||
|
uptr stats[AllocatorStatCount];
|
||||||
|
allocator.GetStats(stats);
|
||||||
|
return stats[AllocatorStatMapped];
|
||||||
|
}
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __sanitizer_get_free_bytes() { return 0; }
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __sanitizer_get_unmapped_bytes() { return 0; }
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
int __sanitizer_get_ownership(const void *p) { return Metadata(p) != 0; }
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __sanitizer_get_allocated_size(const void *p) {
|
||||||
|
return GetMallocUsableSize(p);
|
||||||
|
}
|
||||||
|
} // extern "C"
|
||||||
|
|
|
@ -23,7 +23,7 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
|
||||||
void Deallocate(void *p);
|
void Deallocate(void *p);
|
||||||
void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
|
void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
|
||||||
uptr alignment);
|
uptr alignment);
|
||||||
uptr GetMallocUsableSize(void *p);
|
uptr GetMallocUsableSize(const void *p);
|
||||||
|
|
||||||
template<typename Callable>
|
template<typename Callable>
|
||||||
void ForEachChunk(const Callable &callback);
|
void ForEachChunk(const Callable &callback);
|
||||||
|
|
|
@ -34,15 +34,13 @@ bool DisabledInThisThread() { return disable_counter > 0; }
|
||||||
|
|
||||||
Flags lsan_flags;
|
Flags lsan_flags;
|
||||||
|
|
||||||
static void InitializeFlags() {
|
static void InitializeFlags(bool standalone) {
|
||||||
Flags *f = flags();
|
Flags *f = flags();
|
||||||
// Default values.
|
// Default values.
|
||||||
f->report_objects = false;
|
f->report_objects = false;
|
||||||
f->resolution = 0;
|
f->resolution = 0;
|
||||||
f->max_leaks = 0;
|
f->max_leaks = 0;
|
||||||
f->exitcode = 23;
|
f->exitcode = 23;
|
||||||
f->print_suppressions = true;
|
|
||||||
f->suppressions="";
|
|
||||||
f->use_registers = true;
|
f->use_registers = true;
|
||||||
f->use_globals = true;
|
f->use_globals = true;
|
||||||
f->use_stacks = true;
|
f->use_stacks = true;
|
||||||
|
@ -70,9 +68,18 @@ static void InitializeFlags() {
|
||||||
ParseFlag(options, &f->log_pointers, "log_pointers", "");
|
ParseFlag(options, &f->log_pointers, "log_pointers", "");
|
||||||
ParseFlag(options, &f->log_threads, "log_threads", "");
|
ParseFlag(options, &f->log_threads, "log_threads", "");
|
||||||
ParseFlag(options, &f->exitcode, "exitcode", "");
|
ParseFlag(options, &f->exitcode, "exitcode", "");
|
||||||
ParseFlag(options, &f->print_suppressions, "print_suppressions", "");
|
|
||||||
ParseFlag(options, &f->suppressions, "suppressions", "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set defaults for common flags (only in standalone mode) and parse
|
||||||
|
// them from LSAN_OPTIONS.
|
||||||
|
CommonFlags *cf = common_flags();
|
||||||
|
if (standalone) {
|
||||||
|
SetCommonFlagsDefaults(cf);
|
||||||
|
cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
|
||||||
|
cf->malloc_context_size = 30;
|
||||||
|
cf->detect_leaks = true;
|
||||||
|
}
|
||||||
|
ParseCommonFlagsFromString(cf, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LOG_POINTERS(...) \
|
#define LOG_POINTERS(...) \
|
||||||
|
@ -85,24 +92,14 @@ static void InitializeFlags() {
|
||||||
if (flags()->log_threads) Report(__VA_ARGS__); \
|
if (flags()->log_threads) Report(__VA_ARGS__); \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
SuppressionContext *suppression_ctx;
|
static bool suppressions_inited = false;
|
||||||
|
|
||||||
void InitializeSuppressions() {
|
void InitializeSuppressions() {
|
||||||
CHECK(!suppression_ctx);
|
CHECK(!suppressions_inited);
|
||||||
ALIGNED(64) static char placeholder[sizeof(SuppressionContext)];
|
SuppressionContext::InitIfNecessary();
|
||||||
suppression_ctx = new(placeholder) SuppressionContext;
|
|
||||||
char *suppressions_from_file;
|
|
||||||
uptr buffer_size;
|
|
||||||
if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file,
|
|
||||||
&buffer_size, 1 << 26 /* max_len */))
|
|
||||||
suppression_ctx->Parse(suppressions_from_file);
|
|
||||||
if (flags()->suppressions[0] && !buffer_size) {
|
|
||||||
Printf("LeakSanitizer: failed to read suppressions file '%s'\n",
|
|
||||||
flags()->suppressions);
|
|
||||||
Die();
|
|
||||||
}
|
|
||||||
if (&__lsan_default_suppressions)
|
if (&__lsan_default_suppressions)
|
||||||
suppression_ctx->Parse(__lsan_default_suppressions());
|
SuppressionContext::Get()->Parse(__lsan_default_suppressions());
|
||||||
|
suppressions_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RootRegion {
|
struct RootRegion {
|
||||||
|
@ -118,8 +115,8 @@ void InitializeRootRegions() {
|
||||||
root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
|
root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitCommonLsan() {
|
void InitCommonLsan(bool standalone) {
|
||||||
InitializeFlags();
|
InitializeFlags(standalone);
|
||||||
InitializeRootRegions();
|
InitializeRootRegions();
|
||||||
if (common_flags()->detect_leaks) {
|
if (common_flags()->detect_leaks) {
|
||||||
// Initialization which can fail or print warnings should only be done if
|
// Initialization which can fail or print warnings should only be done if
|
||||||
|
@ -129,9 +126,9 @@ void InitCommonLsan() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Decorator: private __sanitizer::AnsiColorDecorator {
|
class Decorator: public __sanitizer::SanitizerCommonDecorator {
|
||||||
public:
|
public:
|
||||||
Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
|
Decorator() : SanitizerCommonDecorator() { }
|
||||||
const char *Error() { return Red(); }
|
const char *Error() { return Red(); }
|
||||||
const char *Leak() { return Blue(); }
|
const char *Leak() { return Blue(); }
|
||||||
const char *End() { return Default(); }
|
const char *End() { return Default(); }
|
||||||
|
@ -387,7 +384,7 @@ static void CollectLeaksCb(uptr chunk, void *arg) {
|
||||||
|
|
||||||
static void PrintMatchedSuppressions() {
|
static void PrintMatchedSuppressions() {
|
||||||
InternalMmapVector<Suppression *> matched(1);
|
InternalMmapVector<Suppression *> matched(1);
|
||||||
suppression_ctx->GetMatched(&matched);
|
SuppressionContext::Get()->GetMatched(&matched);
|
||||||
if (!matched.size())
|
if (!matched.size())
|
||||||
return;
|
return;
|
||||||
const char *line = "-----------------------------------------------------";
|
const char *line = "-----------------------------------------------------";
|
||||||
|
@ -448,7 +445,7 @@ void DoLeakCheck() {
|
||||||
Printf("%s", d.End());
|
Printf("%s", d.End());
|
||||||
param.leak_report.ReportTopLeaks(flags()->max_leaks);
|
param.leak_report.ReportTopLeaks(flags()->max_leaks);
|
||||||
}
|
}
|
||||||
if (flags()->print_suppressions)
|
if (common_flags()->print_suppressions)
|
||||||
PrintMatchedSuppressions();
|
PrintMatchedSuppressions();
|
||||||
if (unsuppressed_count > 0) {
|
if (unsuppressed_count > 0) {
|
||||||
param.leak_report.PrintSummary();
|
param.leak_report.PrintSummary();
|
||||||
|
@ -463,20 +460,22 @@ static Suppression *GetSuppressionForAddr(uptr addr) {
|
||||||
// Suppress by module name.
|
// Suppress by module name.
|
||||||
const char *module_name;
|
const char *module_name;
|
||||||
uptr module_offset;
|
uptr module_offset;
|
||||||
if (Symbolizer::Get()->GetModuleNameAndOffsetForPC(addr, &module_name,
|
if (Symbolizer::GetOrInit()
|
||||||
&module_offset) &&
|
->GetModuleNameAndOffsetForPC(addr, &module_name, &module_offset) &&
|
||||||
suppression_ctx->Match(module_name, SuppressionLeak, &s))
|
SuppressionContext::Get()->Match(module_name, SuppressionLeak, &s))
|
||||||
return s;
|
return s;
|
||||||
|
|
||||||
// Suppress by file or function name.
|
// Suppress by file or function name.
|
||||||
static const uptr kMaxAddrFrames = 16;
|
static const uptr kMaxAddrFrames = 16;
|
||||||
InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
|
InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
|
||||||
for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo();
|
for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo();
|
||||||
uptr addr_frames_num = Symbolizer::Get()->SymbolizePC(
|
uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
|
||||||
addr, addr_frames.data(), kMaxAddrFrames);
|
addr, addr_frames.data(), kMaxAddrFrames);
|
||||||
for (uptr i = 0; i < addr_frames_num; i++) {
|
for (uptr i = 0; i < addr_frames_num; i++) {
|
||||||
if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) ||
|
if (SuppressionContext::Get()->Match(addr_frames[i].function,
|
||||||
suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s))
|
SuppressionLeak, &s) ||
|
||||||
|
SuppressionContext::Get()->Match(addr_frames[i].file, SuppressionLeak,
|
||||||
|
&s))
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -49,10 +49,6 @@ struct Flags {
|
||||||
int max_leaks;
|
int max_leaks;
|
||||||
// If nonzero kill the process with this exit code upon finding leaks.
|
// If nonzero kill the process with this exit code upon finding leaks.
|
||||||
int exitcode;
|
int exitcode;
|
||||||
// Print matched suppressions after leak checking.
|
|
||||||
bool print_suppressions;
|
|
||||||
// Suppressions file name.
|
|
||||||
const char* suppressions;
|
|
||||||
|
|
||||||
// Flags controlling the root set of reachable memory.
|
// Flags controlling the root set of reachable memory.
|
||||||
// Global variables (.data and .bss).
|
// Global variables (.data and .bss).
|
||||||
|
@ -133,7 +129,7 @@ enum IgnoreObjectResult {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Functions called from the parent tool.
|
// Functions called from the parent tool.
|
||||||
void InitCommonLsan();
|
void InitCommonLsan(bool standalone);
|
||||||
void DoLeakCheck();
|
void DoLeakCheck();
|
||||||
bool DisabledInThisThread();
|
bool DisabledInThisThread();
|
||||||
|
|
||||||
|
|
|
@ -32,21 +32,6 @@ int pthread_key_create(unsigned *key, void (*destructor)(void* v));
|
||||||
int pthread_setspecific(unsigned key, const void *v);
|
int pthread_setspecific(unsigned key, const void *v);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GET_STACK_TRACE \
|
|
||||||
StackTrace stack; \
|
|
||||||
{ \
|
|
||||||
uptr stack_top = 0, stack_bottom = 0; \
|
|
||||||
ThreadContext *t; \
|
|
||||||
bool fast = common_flags()->fast_unwind_on_malloc; \
|
|
||||||
if (fast && (t = CurrentThreadContext())) { \
|
|
||||||
stack_top = t->stack_end(); \
|
|
||||||
stack_bottom = t->stack_begin(); \
|
|
||||||
} \
|
|
||||||
stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \
|
|
||||||
StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), 0, \
|
|
||||||
stack_top, stack_bottom, fast); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ENSURE_LSAN_INITED do { \
|
#define ENSURE_LSAN_INITED do { \
|
||||||
CHECK(!lsan_init_is_running); \
|
CHECK(!lsan_init_is_running); \
|
||||||
if (!lsan_inited) \
|
if (!lsan_inited) \
|
||||||
|
@ -63,7 +48,7 @@ namespace std {
|
||||||
|
|
||||||
INTERCEPTOR(void*, malloc, uptr size) {
|
INTERCEPTOR(void*, malloc, uptr size) {
|
||||||
ENSURE_LSAN_INITED;
|
ENSURE_LSAN_INITED;
|
||||||
GET_STACK_TRACE;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return Allocate(stack, size, 1, kAlwaysClearMemory);
|
return Allocate(stack, size, 1, kAlwaysClearMemory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,26 +71,32 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
|
||||||
}
|
}
|
||||||
if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0;
|
if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0;
|
||||||
ENSURE_LSAN_INITED;
|
ENSURE_LSAN_INITED;
|
||||||
GET_STACK_TRACE;
|
GET_STACK_TRACE_MALLOC;
|
||||||
size *= nmemb;
|
size *= nmemb;
|
||||||
return Allocate(stack, size, 1, true);
|
return Allocate(stack, size, 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void*, realloc, void *q, uptr size) {
|
INTERCEPTOR(void*, realloc, void *q, uptr size) {
|
||||||
ENSURE_LSAN_INITED;
|
ENSURE_LSAN_INITED;
|
||||||
GET_STACK_TRACE;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return Reallocate(stack, q, size, 1);
|
return Reallocate(stack, q, size, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
|
INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
|
||||||
ENSURE_LSAN_INITED;
|
ENSURE_LSAN_INITED;
|
||||||
GET_STACK_TRACE;
|
GET_STACK_TRACE_MALLOC;
|
||||||
|
return Allocate(stack, size, alignment, kAlwaysClearMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) {
|
||||||
|
ENSURE_LSAN_INITED;
|
||||||
|
GET_STACK_TRACE_MALLOC;
|
||||||
return Allocate(stack, size, alignment, kAlwaysClearMemory);
|
return Allocate(stack, size, alignment, kAlwaysClearMemory);
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
|
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
|
||||||
ENSURE_LSAN_INITED;
|
ENSURE_LSAN_INITED;
|
||||||
GET_STACK_TRACE;
|
GET_STACK_TRACE_MALLOC;
|
||||||
*memptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
|
*memptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
|
||||||
// FIXME: Return ENOMEM if user requested more than max alloc size.
|
// FIXME: Return ENOMEM if user requested more than max alloc size.
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -113,7 +104,7 @@ INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
|
||||||
|
|
||||||
INTERCEPTOR(void*, valloc, uptr size) {
|
INTERCEPTOR(void*, valloc, uptr size) {
|
||||||
ENSURE_LSAN_INITED;
|
ENSURE_LSAN_INITED;
|
||||||
GET_STACK_TRACE;
|
GET_STACK_TRACE_MALLOC;
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
size = GetPageSizeCached();
|
size = GetPageSizeCached();
|
||||||
return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory);
|
return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory);
|
||||||
|
@ -140,7 +131,7 @@ INTERCEPTOR(int, mallopt, int cmd, int value) {
|
||||||
|
|
||||||
INTERCEPTOR(void*, pvalloc, uptr size) {
|
INTERCEPTOR(void*, pvalloc, uptr size) {
|
||||||
ENSURE_LSAN_INITED;
|
ENSURE_LSAN_INITED;
|
||||||
GET_STACK_TRACE;
|
GET_STACK_TRACE_MALLOC;
|
||||||
uptr PageSize = GetPageSizeCached();
|
uptr PageSize = GetPageSizeCached();
|
||||||
size = RoundUpTo(size, PageSize);
|
size = RoundUpTo(size, PageSize);
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
|
@ -154,7 +145,7 @@ INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free));
|
||||||
|
|
||||||
#define OPERATOR_NEW_BODY \
|
#define OPERATOR_NEW_BODY \
|
||||||
ENSURE_LSAN_INITED; \
|
ENSURE_LSAN_INITED; \
|
||||||
GET_STACK_TRACE; \
|
GET_STACK_TRACE_MALLOC; \
|
||||||
return Allocate(stack, size, 1, kAlwaysClearMemory);
|
return Allocate(stack, size, 1, kAlwaysClearMemory);
|
||||||
|
|
||||||
INTERCEPTOR_ATTRIBUTE
|
INTERCEPTOR_ATTRIBUTE
|
||||||
|
|
|
@ -12,11 +12,7 @@
|
||||||
|
|
||||||
#include "lsan.h"
|
#include "lsan.h"
|
||||||
|
|
||||||
#ifndef LSAN_USE_PREINIT_ARRAY
|
#if SANITIZER_CAN_USE_PREINIT_ARRAY
|
||||||
#define LSAN_USE_PREINIT_ARRAY 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if LSAN_USE_PREINIT_ARRAY && !defined(PIC)
|
|
||||||
// We force __lsan_init to be called before anyone else by placing it into
|
// We force __lsan_init to be called before anyone else by placing it into
|
||||||
// .preinit_array section.
|
// .preinit_array section.
|
||||||
__attribute__((section(".preinit_array"), used))
|
__attribute__((section(".preinit_array"), used))
|
||||||
|
|
|
@ -21,7 +21,8 @@ sanitizer_common_files = \
|
||||||
sanitizer_allocator.cc \
|
sanitizer_allocator.cc \
|
||||||
sanitizer_common.cc \
|
sanitizer_common.cc \
|
||||||
sanitizer_common_libcdep.cc \
|
sanitizer_common_libcdep.cc \
|
||||||
sanitizer_coverage.cc \
|
sanitizer_coverage_libcdep.cc \
|
||||||
|
sanitizer_coverage_mapping_libcdep.cc \
|
||||||
sanitizer_deadlock_detector1.cc \
|
sanitizer_deadlock_detector1.cc \
|
||||||
sanitizer_deadlock_detector2.cc \
|
sanitizer_deadlock_detector2.cc \
|
||||||
sanitizer_flags.cc \
|
sanitizer_flags.cc \
|
||||||
|
@ -30,11 +31,14 @@ sanitizer_common_files = \
|
||||||
sanitizer_linux.cc \
|
sanitizer_linux.cc \
|
||||||
sanitizer_linux_libcdep.cc \
|
sanitizer_linux_libcdep.cc \
|
||||||
sanitizer_mac.cc \
|
sanitizer_mac.cc \
|
||||||
|
sanitizer_persistent_allocator.cc \
|
||||||
sanitizer_platform_limits_linux.cc \
|
sanitizer_platform_limits_linux.cc \
|
||||||
sanitizer_platform_limits_posix.cc \
|
sanitizer_platform_limits_posix.cc \
|
||||||
sanitizer_posix.cc \
|
sanitizer_posix.cc \
|
||||||
sanitizer_posix_libcdep.cc \
|
sanitizer_posix_libcdep.cc \
|
||||||
sanitizer_printf.cc \
|
sanitizer_printf.cc \
|
||||||
|
sanitizer_procmaps_common.cc \
|
||||||
|
sanitizer_procmaps_freebsd.cc \
|
||||||
sanitizer_procmaps_linux.cc \
|
sanitizer_procmaps_linux.cc \
|
||||||
sanitizer_procmaps_mac.cc \
|
sanitizer_procmaps_mac.cc \
|
||||||
sanitizer_stackdepot.cc \
|
sanitizer_stackdepot.cc \
|
||||||
|
@ -49,8 +53,10 @@ sanitizer_common_files = \
|
||||||
sanitizer_symbolizer_win.cc \
|
sanitizer_symbolizer_win.cc \
|
||||||
sanitizer_thread_registry.cc \
|
sanitizer_thread_registry.cc \
|
||||||
sanitizer_tls_get_addr.cc \
|
sanitizer_tls_get_addr.cc \
|
||||||
|
sanitizer_unwind_posix_libcdep.cc \
|
||||||
sanitizer_win.cc
|
sanitizer_win.cc
|
||||||
|
|
||||||
|
|
||||||
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
|
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
|
||||||
|
|
||||||
# Work around what appears to be a GNU make bug handling MAKEFLAGS
|
# Work around what appears to be a GNU make bug handling MAKEFLAGS
|
||||||
|
|
|
@ -64,14 +64,17 @@ CONFIG_CLEAN_VPATH_FILES =
|
||||||
LTLIBRARIES = $(noinst_LTLIBRARIES)
|
LTLIBRARIES = $(noinst_LTLIBRARIES)
|
||||||
libsanitizer_common_la_LIBADD =
|
libsanitizer_common_la_LIBADD =
|
||||||
am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
|
am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
|
||||||
sanitizer_common_libcdep.lo sanitizer_coverage.lo \
|
sanitizer_common_libcdep.lo sanitizer_coverage_libcdep.lo \
|
||||||
|
sanitizer_coverage_mapping_libcdep.lo \
|
||||||
sanitizer_deadlock_detector1.lo \
|
sanitizer_deadlock_detector1.lo \
|
||||||
sanitizer_deadlock_detector2.lo sanitizer_flags.lo \
|
sanitizer_deadlock_detector2.lo sanitizer_flags.lo \
|
||||||
sanitizer_libc.lo sanitizer_libignore.lo sanitizer_linux.lo \
|
sanitizer_libc.lo sanitizer_libignore.lo sanitizer_linux.lo \
|
||||||
sanitizer_linux_libcdep.lo sanitizer_mac.lo \
|
sanitizer_linux_libcdep.lo sanitizer_mac.lo \
|
||||||
|
sanitizer_persistent_allocator.lo \
|
||||||
sanitizer_platform_limits_linux.lo \
|
sanitizer_platform_limits_linux.lo \
|
||||||
sanitizer_platform_limits_posix.lo sanitizer_posix.lo \
|
sanitizer_platform_limits_posix.lo sanitizer_posix.lo \
|
||||||
sanitizer_posix_libcdep.lo sanitizer_printf.lo \
|
sanitizer_posix_libcdep.lo sanitizer_printf.lo \
|
||||||
|
sanitizer_procmaps_common.lo sanitizer_procmaps_freebsd.lo \
|
||||||
sanitizer_procmaps_linux.lo sanitizer_procmaps_mac.lo \
|
sanitizer_procmaps_linux.lo sanitizer_procmaps_mac.lo \
|
||||||
sanitizer_stackdepot.lo sanitizer_stacktrace.lo \
|
sanitizer_stackdepot.lo sanitizer_stacktrace.lo \
|
||||||
sanitizer_stacktrace_libcdep.lo \
|
sanitizer_stacktrace_libcdep.lo \
|
||||||
|
@ -81,7 +84,8 @@ am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
|
||||||
sanitizer_symbolizer_libcdep.lo \
|
sanitizer_symbolizer_libcdep.lo \
|
||||||
sanitizer_symbolizer_posix_libcdep.lo \
|
sanitizer_symbolizer_posix_libcdep.lo \
|
||||||
sanitizer_symbolizer_win.lo sanitizer_thread_registry.lo \
|
sanitizer_symbolizer_win.lo sanitizer_thread_registry.lo \
|
||||||
sanitizer_tls_get_addr.lo sanitizer_win.lo
|
sanitizer_tls_get_addr.lo sanitizer_unwind_posix_libcdep.lo \
|
||||||
|
sanitizer_win.lo
|
||||||
am_libsanitizer_common_la_OBJECTS = $(am__objects_1)
|
am_libsanitizer_common_la_OBJECTS = $(am__objects_1)
|
||||||
libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS)
|
libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS)
|
||||||
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
|
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
|
||||||
|
@ -256,7 +260,8 @@ sanitizer_common_files = \
|
||||||
sanitizer_allocator.cc \
|
sanitizer_allocator.cc \
|
||||||
sanitizer_common.cc \
|
sanitizer_common.cc \
|
||||||
sanitizer_common_libcdep.cc \
|
sanitizer_common_libcdep.cc \
|
||||||
sanitizer_coverage.cc \
|
sanitizer_coverage_libcdep.cc \
|
||||||
|
sanitizer_coverage_mapping_libcdep.cc \
|
||||||
sanitizer_deadlock_detector1.cc \
|
sanitizer_deadlock_detector1.cc \
|
||||||
sanitizer_deadlock_detector2.cc \
|
sanitizer_deadlock_detector2.cc \
|
||||||
sanitizer_flags.cc \
|
sanitizer_flags.cc \
|
||||||
|
@ -265,11 +270,14 @@ sanitizer_common_files = \
|
||||||
sanitizer_linux.cc \
|
sanitizer_linux.cc \
|
||||||
sanitizer_linux_libcdep.cc \
|
sanitizer_linux_libcdep.cc \
|
||||||
sanitizer_mac.cc \
|
sanitizer_mac.cc \
|
||||||
|
sanitizer_persistent_allocator.cc \
|
||||||
sanitizer_platform_limits_linux.cc \
|
sanitizer_platform_limits_linux.cc \
|
||||||
sanitizer_platform_limits_posix.cc \
|
sanitizer_platform_limits_posix.cc \
|
||||||
sanitizer_posix.cc \
|
sanitizer_posix.cc \
|
||||||
sanitizer_posix_libcdep.cc \
|
sanitizer_posix_libcdep.cc \
|
||||||
sanitizer_printf.cc \
|
sanitizer_printf.cc \
|
||||||
|
sanitizer_procmaps_common.cc \
|
||||||
|
sanitizer_procmaps_freebsd.cc \
|
||||||
sanitizer_procmaps_linux.cc \
|
sanitizer_procmaps_linux.cc \
|
||||||
sanitizer_procmaps_mac.cc \
|
sanitizer_procmaps_mac.cc \
|
||||||
sanitizer_stackdepot.cc \
|
sanitizer_stackdepot.cc \
|
||||||
|
@ -284,6 +292,7 @@ sanitizer_common_files = \
|
||||||
sanitizer_symbolizer_win.cc \
|
sanitizer_symbolizer_win.cc \
|
||||||
sanitizer_thread_registry.cc \
|
sanitizer_thread_registry.cc \
|
||||||
sanitizer_tls_get_addr.cc \
|
sanitizer_tls_get_addr.cc \
|
||||||
|
sanitizer_unwind_posix_libcdep.cc \
|
||||||
sanitizer_win.cc
|
sanitizer_win.cc
|
||||||
|
|
||||||
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
|
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
|
||||||
|
@ -382,7 +391,8 @@ distclean-compile:
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common_libcdep.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common_libcdep.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage_libcdep.Plo@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage_mapping_libcdep.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector1.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector1.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector2.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector2.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@
|
||||||
|
@ -391,11 +401,14 @@ distclean-compile:
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_libcdep.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_libcdep.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_persistent_allocator.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_linux.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_linux.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_posix.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_posix.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix_libcdep.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix_libcdep.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_printf.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_printf.Plo@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_common.Plo@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_freebsd.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_linux.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_linux.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_mac.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_mac.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@
|
||||||
|
@ -410,6 +423,7 @@ distclean-compile:
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_tls_get_addr.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_tls_get_addr.Plo@am__quote@
|
||||||
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_posix_libcdep.Plo@am__quote@
|
||||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@
|
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@
|
||||||
|
|
||||||
.cc.o:
|
.cc.o:
|
||||||
|
|
|
@ -271,9 +271,9 @@ class AllocatorGlobalStats : public AllocatorStats {
|
||||||
if (stats == this)
|
if (stats == this)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// All stats must be positive.
|
// All stats must be non-negative.
|
||||||
for (int i = 0; i < AllocatorStatCount; i++)
|
for (int i = 0; i < AllocatorStatCount; i++)
|
||||||
s[i] = ((sptr)s[i]) > 0 ? s[i] : 1;
|
s[i] = ((sptr)s[i]) >= 0 ? s[i] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
//===-- sanitizer_allocator_interface.h ------------------------- C++ -----===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Re-declaration of functions from public sanitizer allocator interface.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef SANITIZER_ALLOCATOR_INTERFACE_H
|
||||||
|
#define SANITIZER_ALLOCATOR_INTERFACE_H
|
||||||
|
|
||||||
|
#include "sanitizer_internal_defs.h"
|
||||||
|
|
||||||
|
using __sanitizer::uptr;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
uptr __sanitizer_get_estimated_allocated_size(uptr size);
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_get_ownership(const void *p);
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE uptr
|
||||||
|
__sanitizer_get_allocated_size(const void *p);
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_current_allocated_bytes();
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_heap_size();
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_free_bytes();
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_unmapped_bytes();
|
||||||
|
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||||
|
/* OPTIONAL */ void __sanitizer_malloc_hook(void *ptr, uptr size);
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||||
|
/* OPTIONAL */ void __sanitizer_free_hook(void *ptr);
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#endif // SANITIZER_ALLOCATOR_INTERFACE_H
|
|
@ -23,38 +23,25 @@ typedef CompactSizeClassMap InternalSizeClassMap;
|
||||||
|
|
||||||
static const uptr kInternalAllocatorSpace = 0;
|
static const uptr kInternalAllocatorSpace = 0;
|
||||||
static const u64 kInternalAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
|
static const u64 kInternalAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
|
||||||
#if SANITIZER_WORDSIZE == 32
|
|
||||||
static const uptr kInternalAllocatorRegionSizeLog = 20;
|
static const uptr kInternalAllocatorRegionSizeLog = 20;
|
||||||
|
#if SANITIZER_WORDSIZE == 32
|
||||||
static const uptr kInternalAllocatorNumRegions =
|
static const uptr kInternalAllocatorNumRegions =
|
||||||
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
|
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
|
||||||
typedef FlatByteMap<kInternalAllocatorNumRegions> ByteMap;
|
typedef FlatByteMap<kInternalAllocatorNumRegions> ByteMap;
|
||||||
#else
|
#else
|
||||||
static const uptr kInternalAllocatorRegionSizeLog = 24;
|
|
||||||
static const uptr kInternalAllocatorNumRegions =
|
static const uptr kInternalAllocatorNumRegions =
|
||||||
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
|
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
|
||||||
typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap;
|
typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap;
|
||||||
#endif
|
#endif
|
||||||
typedef SizeClassAllocator32<
|
typedef SizeClassAllocator32<
|
||||||
kInternalAllocatorSpace, kInternalAllocatorSize, 16, InternalSizeClassMap,
|
kInternalAllocatorSpace, kInternalAllocatorSize, 0, InternalSizeClassMap,
|
||||||
kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator;
|
kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator;
|
||||||
|
|
||||||
typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator>
|
typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator>
|
||||||
InternalAllocatorCache;
|
InternalAllocatorCache;
|
||||||
|
|
||||||
// We don't want our internal allocator to do any map/unmap operations from
|
|
||||||
// LargeMmapAllocator.
|
|
||||||
struct CrashOnMapUnmap {
|
|
||||||
void OnMap(uptr p, uptr size) const {
|
|
||||||
RAW_CHECK_MSG(0, "Unexpected mmap in InternalAllocator!\n");
|
|
||||||
}
|
|
||||||
void OnUnmap(uptr p, uptr size) const {
|
|
||||||
RAW_CHECK_MSG(0, "Unexpected munmap in InternalAllocator!\n");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache,
|
typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache,
|
||||||
LargeMmapAllocator<CrashOnMapUnmap> >
|
LargeMmapAllocator<> > InternalAllocator;
|
||||||
InternalAllocator;
|
|
||||||
|
|
||||||
void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0);
|
void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0);
|
||||||
void InternalFree(void *p, InternalAllocatorCache *cache = 0);
|
void InternalFree(void *p, InternalAllocatorCache *cache = 0);
|
||||||
|
|
|
@ -31,33 +31,20 @@ long long _InterlockedCompareExchange64( // NOLINT
|
||||||
long long volatile *Destination, // NOLINT
|
long long volatile *Destination, // NOLINT
|
||||||
long long Exchange, long long Comparand); // NOLINT
|
long long Exchange, long long Comparand); // NOLINT
|
||||||
#pragma intrinsic(_InterlockedCompareExchange64)
|
#pragma intrinsic(_InterlockedCompareExchange64)
|
||||||
|
|
||||||
#ifdef _WIN64
|
|
||||||
extern "C" long long _InterlockedExchangeAdd64( // NOLINT
|
|
||||||
long long volatile * Addend, long long Value); // NOLINT
|
|
||||||
#pragma intrinsic(_InterlockedExchangeAdd64)
|
|
||||||
extern "C" void *_InterlockedCompareExchangePointer(
|
extern "C" void *_InterlockedCompareExchangePointer(
|
||||||
void *volatile *Destination,
|
void *volatile *Destination,
|
||||||
void *Exchange, void *Comparand);
|
void *Exchange, void *Comparand);
|
||||||
#pragma intrinsic(_InterlockedCompareExchangePointer)
|
#pragma intrinsic(_InterlockedCompareExchangePointer)
|
||||||
#else
|
|
||||||
// There's no _InterlockedCompareExchangePointer intrinsic on x86,
|
|
||||||
// so call _InterlockedCompareExchange instead.
|
|
||||||
extern "C"
|
extern "C"
|
||||||
long __cdecl _InterlockedCompareExchange( // NOLINT
|
long __cdecl _InterlockedCompareExchange( // NOLINT
|
||||||
long volatile *Destination, // NOLINT
|
long volatile *Destination, // NOLINT
|
||||||
long Exchange, long Comparand); // NOLINT
|
long Exchange, long Comparand); // NOLINT
|
||||||
#pragma intrinsic(_InterlockedCompareExchange)
|
#pragma intrinsic(_InterlockedCompareExchange)
|
||||||
|
|
||||||
inline static void *_InterlockedCompareExchangePointer(
|
#ifdef _WIN64
|
||||||
void *volatile *Destination,
|
extern "C" long long _InterlockedExchangeAdd64( // NOLINT
|
||||||
void *Exchange, void *Comparand) {
|
long long volatile * Addend, long long Value); // NOLINT
|
||||||
return reinterpret_cast<void*>(
|
#pragma intrinsic(_InterlockedExchangeAdd64)
|
||||||
_InterlockedCompareExchange(
|
|
||||||
reinterpret_cast<long volatile*>(Destination), // NOLINT
|
|
||||||
reinterpret_cast<long>(Exchange), // NOLINT
|
|
||||||
reinterpret_cast<long>(Comparand))); // NOLINT
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace __sanitizer {
|
namespace __sanitizer {
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
#include "sanitizer_common.h"
|
#include "sanitizer_common.h"
|
||||||
#include "sanitizer_flags.h"
|
#include "sanitizer_flags.h"
|
||||||
#include "sanitizer_libc.h"
|
#include "sanitizer_libc.h"
|
||||||
#include "sanitizer_stacktrace.h"
|
|
||||||
#include "sanitizer_symbolizer.h"
|
|
||||||
|
|
||||||
namespace __sanitizer {
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
@ -195,31 +193,17 @@ void ReportErrorSummary(const char *error_type, const char *file,
|
||||||
ReportErrorSummary(buff.data());
|
ReportErrorSummary(buff.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReportErrorSummary(const char *error_type, StackTrace *stack) {
|
|
||||||
if (!common_flags()->print_summary)
|
|
||||||
return;
|
|
||||||
AddressInfo ai;
|
|
||||||
#if !SANITIZER_GO
|
|
||||||
if (stack->size > 0 && Symbolizer::Get()->CanReturnFileLineInfo()) {
|
|
||||||
// Currently, we include the first stack frame into the report summary.
|
|
||||||
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
|
|
||||||
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
|
|
||||||
Symbolizer::Get()->SymbolizePC(pc, &ai, 1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
|
LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
|
||||||
full_name_ = internal_strdup(module_name);
|
full_name_ = internal_strdup(module_name);
|
||||||
base_address_ = base_address;
|
base_address_ = base_address;
|
||||||
n_ranges_ = 0;
|
n_ranges_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadedModule::addAddressRange(uptr beg, uptr end) {
|
void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
|
||||||
CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges);
|
CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges);
|
||||||
ranges_[n_ranges_].beg = beg;
|
ranges_[n_ranges_].beg = beg;
|
||||||
ranges_[n_ranges_].end = end;
|
ranges_[n_ranges_].end = end;
|
||||||
|
exec_[n_ranges_] = executable;
|
||||||
n_ranges_++;
|
n_ranges_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,11 +245,6 @@ void DecreaseTotalMmap(uptr size) {
|
||||||
atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
|
atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void (*sandboxing_callback)();
|
|
||||||
void SetSandboxingCallback(void (*f)()) {
|
|
||||||
sandboxing_callback = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
using namespace __sanitizer; // NOLINT
|
using namespace __sanitizer; // NOLINT
|
||||||
|
@ -298,13 +277,6 @@ void __sanitizer_set_report_path(const char *path) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NOINLINE
|
|
||||||
__sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args) {
|
|
||||||
PrepareForSandboxing(args);
|
|
||||||
if (sandboxing_callback)
|
|
||||||
sandboxing_callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
void __sanitizer_report_error_summary(const char *error_summary) {
|
void __sanitizer_report_error_summary(const char *error_summary) {
|
||||||
Printf("%s\n", error_summary);
|
Printf("%s\n", error_summary);
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,6 +163,9 @@ uptr ReadFileToBuffer(const char *file_name, char **buff,
|
||||||
// (or NULL if the mapping failes). Stores the size of mmaped region
|
// (or NULL if the mapping failes). Stores the size of mmaped region
|
||||||
// in '*buff_size'.
|
// in '*buff_size'.
|
||||||
void *MapFileToMemory(const char *file_name, uptr *buff_size);
|
void *MapFileToMemory(const char *file_name, uptr *buff_size);
|
||||||
|
void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset);
|
||||||
|
|
||||||
|
bool IsAccessibleMemoryRange(uptr beg, uptr size);
|
||||||
|
|
||||||
// Error report formatting.
|
// Error report formatting.
|
||||||
const char *StripPathPrefix(const char *filepath,
|
const char *StripPathPrefix(const char *filepath,
|
||||||
|
@ -173,7 +176,7 @@ void PrintModuleAndOffset(InternalScopedString *buffer,
|
||||||
const char *module, uptr offset);
|
const char *module, uptr offset);
|
||||||
|
|
||||||
// OS
|
// OS
|
||||||
void DisableCoreDumper();
|
void DisableCoreDumperIfNecessary();
|
||||||
void DumpProcessMap();
|
void DumpProcessMap();
|
||||||
bool FileExists(const char *filename);
|
bool FileExists(const char *filename);
|
||||||
const char *GetEnv(const char *name);
|
const char *GetEnv(const char *name);
|
||||||
|
@ -184,11 +187,17 @@ u32 GetUid();
|
||||||
void ReExec();
|
void ReExec();
|
||||||
bool StackSizeIsUnlimited();
|
bool StackSizeIsUnlimited();
|
||||||
void SetStackSizeLimitInBytes(uptr limit);
|
void SetStackSizeLimitInBytes(uptr limit);
|
||||||
|
bool AddressSpaceIsUnlimited();
|
||||||
|
void SetAddressSpaceUnlimited();
|
||||||
void AdjustStackSize(void *attr);
|
void AdjustStackSize(void *attr);
|
||||||
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args);
|
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args);
|
||||||
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args);
|
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args);
|
||||||
void SetSandboxingCallback(void (*f)());
|
void SetSandboxingCallback(void (*f)());
|
||||||
|
|
||||||
|
void CovUpdateMapping(uptr caller_pc = 0);
|
||||||
|
void CovBeforeFork();
|
||||||
|
void CovAfterFork(int child_pid);
|
||||||
|
|
||||||
void InitTlsSize();
|
void InitTlsSize();
|
||||||
uptr GetTlsSize();
|
uptr GetTlsSize();
|
||||||
|
|
||||||
|
@ -239,7 +248,7 @@ const int kMaxSummaryLength = 1024;
|
||||||
// and pass it to __sanitizer_report_error_summary.
|
// and pass it to __sanitizer_report_error_summary.
|
||||||
void ReportErrorSummary(const char *error_message);
|
void ReportErrorSummary(const char *error_message);
|
||||||
// Same as above, but construct error_message as:
|
// Same as above, but construct error_message as:
|
||||||
// error_type: file:line function
|
// error_type file:line function
|
||||||
void ReportErrorSummary(const char *error_type, const char *file,
|
void ReportErrorSummary(const char *error_type, const char *file,
|
||||||
int line, const char *function);
|
int line, const char *function);
|
||||||
void ReportErrorSummary(const char *error_type, StackTrace *trace);
|
void ReportErrorSummary(const char *error_type, StackTrace *trace);
|
||||||
|
@ -475,12 +484,17 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last,
|
||||||
class LoadedModule {
|
class LoadedModule {
|
||||||
public:
|
public:
|
||||||
LoadedModule(const char *module_name, uptr base_address);
|
LoadedModule(const char *module_name, uptr base_address);
|
||||||
void addAddressRange(uptr beg, uptr end);
|
void addAddressRange(uptr beg, uptr end, bool executable);
|
||||||
bool containsAddress(uptr address) const;
|
bool containsAddress(uptr address) const;
|
||||||
|
|
||||||
const char *full_name() const { return full_name_; }
|
const char *full_name() const { return full_name_; }
|
||||||
uptr base_address() const { return base_address_; }
|
uptr base_address() const { return base_address_; }
|
||||||
|
|
||||||
|
uptr n_ranges() const { return n_ranges_; }
|
||||||
|
uptr address_range_start(int i) const { return ranges_[i].beg; }
|
||||||
|
uptr address_range_end(int i) const { return ranges_[i].end; }
|
||||||
|
bool address_range_executable(int i) const { return exec_[i]; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct AddressRange {
|
struct AddressRange {
|
||||||
uptr beg;
|
uptr beg;
|
||||||
|
@ -490,6 +504,7 @@ class LoadedModule {
|
||||||
uptr base_address_;
|
uptr base_address_;
|
||||||
static const uptr kMaxNumberOfAddressRanges = 6;
|
static const uptr kMaxNumberOfAddressRanges = 6;
|
||||||
AddressRange ranges_[kMaxNumberOfAddressRanges];
|
AddressRange ranges_[kMaxNumberOfAddressRanges];
|
||||||
|
bool exec_[kMaxNumberOfAddressRanges];
|
||||||
uptr n_ranges_;
|
uptr n_ranges_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -529,10 +544,13 @@ F IndirectExternCall(F f) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SANITIZER_ANDROID
|
#if SANITIZER_ANDROID
|
||||||
|
// Initialize Android logging. Any writes before this are silently lost.
|
||||||
|
void AndroidLogInit();
|
||||||
void AndroidLogWrite(const char *buffer);
|
void AndroidLogWrite(const char *buffer);
|
||||||
void GetExtraActivationFlags(char *buf, uptr size);
|
void GetExtraActivationFlags(char *buf, uptr size);
|
||||||
void SanitizerInitializeUnwinder();
|
void SanitizerInitializeUnwinder();
|
||||||
#else
|
#else
|
||||||
|
INLINE void AndroidLogInit() {}
|
||||||
INLINE void AndroidLogWrite(const char *buffer_unused) {}
|
INLINE void AndroidLogWrite(const char *buffer_unused) {}
|
||||||
INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
|
INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
|
||||||
INLINE void SanitizerInitializeUnwinder() {}
|
INLINE void SanitizerInitializeUnwinder() {}
|
||||||
|
@ -544,4 +562,9 @@ inline void *operator new(__sanitizer::operator_new_size_type size,
|
||||||
return alloc.Allocate(size);
|
return alloc.Allocate(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct StackDepotStats {
|
||||||
|
uptr n_uniq_ids;
|
||||||
|
uptr allocated;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // SANITIZER_COMMON_H
|
#endif // SANITIZER_COMMON_H
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -457,6 +457,9 @@ static int printf_get_value_size(PrintfDirective *dir) {
|
||||||
case 8: \
|
case 8: \
|
||||||
va_arg(*aq, double); \
|
va_arg(*aq, double); \
|
||||||
break; \
|
break; \
|
||||||
|
case 12: \
|
||||||
|
va_arg(*aq, long double); \
|
||||||
|
break; \
|
||||||
case 16: \
|
case 16: \
|
||||||
va_arg(*aq, long double); \
|
va_arg(*aq, long double); \
|
||||||
break; \
|
break; \
|
||||||
|
|
|
@ -527,7 +527,7 @@ static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
|
||||||
desc->name = "<DECODED_IOCTL>";
|
desc->name = "<DECODED_IOCTL>";
|
||||||
desc->size = IOC_SIZE(req);
|
desc->size = IOC_SIZE(req);
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
if (desc->size > 1024) return false;
|
if (desc->size > 0xFFFF) return false;
|
||||||
unsigned dir = IOC_DIR(req);
|
unsigned dir = IOC_DIR(req);
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
case IOC_NONE:
|
case IOC_NONE:
|
||||||
|
@ -545,10 +545,10 @@ static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (desc->type != IOC_NONE && desc->size == 0) return false;
|
// Size can be 0 iff type is NONE.
|
||||||
char id = IOC_TYPE(req);
|
if ((desc->type == IOC_NONE) != (desc->size == 0)) return false;
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
if (!(id >= 'a' && id <= 'z') && !(id >= 'A' && id <= 'Z')) return false;
|
if (IOC_TYPE(req) == 0) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include "sanitizer_common.h"
|
#include "sanitizer_common.h"
|
||||||
#include "sanitizer_flags.h"
|
#include "sanitizer_flags.h"
|
||||||
|
#include "sanitizer_stacktrace.h"
|
||||||
|
#include "sanitizer_symbolizer.h"
|
||||||
|
|
||||||
namespace __sanitizer {
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
@ -39,4 +41,32 @@ bool ColorizeReports() {
|
||||||
return internal_strcmp(flag, "always") == 0 ||
|
return internal_strcmp(flag, "always") == 0 ||
|
||||||
(internal_strcmp(flag, "auto") == 0 && PrintsToTtyCached());
|
(internal_strcmp(flag, "auto") == 0 && PrintsToTtyCached());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void (*sandboxing_callback)();
|
||||||
|
void SetSandboxingCallback(void (*f)()) {
|
||||||
|
sandboxing_callback = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportErrorSummary(const char *error_type, StackTrace *stack) {
|
||||||
|
if (!common_flags()->print_summary)
|
||||||
|
return;
|
||||||
|
AddressInfo ai;
|
||||||
|
#if !SANITIZER_GO
|
||||||
|
if (stack->size > 0 && Symbolizer::GetOrInit()->CanReturnFileLineInfo()) {
|
||||||
|
// Currently, we include the first stack frame into the report summary.
|
||||||
|
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
|
||||||
|
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
|
||||||
|
Symbolizer::GetOrInit()->SymbolizePC(pc, &ai, 1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
|
void NOINLINE
|
||||||
|
__sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args) {
|
||||||
|
PrepareForSandboxing(args);
|
||||||
|
if (sandboxing_callback)
|
||||||
|
sandboxing_callback();
|
||||||
|
}
|
||||||
|
|
|
@ -829,6 +829,7 @@ POST_SYSCALL(stat)(long res, const void *filename, void *statbuf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !SANITIZER_ANDROID
|
||||||
PRE_SYSCALL(statfs)(const void *path, void *buf) {
|
PRE_SYSCALL(statfs)(const void *path, void *buf) {
|
||||||
if (path)
|
if (path)
|
||||||
PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
|
PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
|
||||||
|
@ -866,6 +867,7 @@ POST_SYSCALL(fstatfs64)(long res, long fd, long sz, void *buf) {
|
||||||
if (buf) POST_WRITE(buf, struct_statfs64_sz);
|
if (buf) POST_WRITE(buf, struct_statfs64_sz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // !SANITIZER_ANDROID
|
||||||
|
|
||||||
PRE_SYSCALL(lstat)(const void *filename, void *statbuf) {
|
PRE_SYSCALL(lstat)(const void *filename, void *statbuf) {
|
||||||
if (filename)
|
if (filename)
|
||||||
|
@ -1322,13 +1324,13 @@ PRE_SYSCALL(io_submit)(long ctx_id, long nr, __sanitizer_iocb **iocbpp) {
|
||||||
} else if (op == iocb_cmd_pread && buf && len) {
|
} else if (op == iocb_cmd_pread && buf && len) {
|
||||||
POST_WRITE(buf, len);
|
POST_WRITE(buf, len);
|
||||||
} else if (op == iocb_cmd_pwritev) {
|
} else if (op == iocb_cmd_pwritev) {
|
||||||
__sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf;
|
__sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
|
||||||
for (uptr v = 0; v < len; v++)
|
for (uptr v = 0; v < len; v++)
|
||||||
PRE_READ(iovec[i].iov_base, iovec[i].iov_len);
|
PRE_READ(iovec[v].iov_base, iovec[v].iov_len);
|
||||||
} else if (op == iocb_cmd_preadv) {
|
} else if (op == iocb_cmd_preadv) {
|
||||||
__sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf;
|
__sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
|
||||||
for (uptr v = 0; v < len; v++)
|
for (uptr v = 0; v < len; v++)
|
||||||
POST_WRITE(iovec[i].iov_base, iovec[i].iov_len);
|
POST_WRITE(iovec[v].iov_base, iovec[v].iov_len);
|
||||||
}
|
}
|
||||||
// See comment in io_getevents.
|
// See comment in io_getevents.
|
||||||
COMMON_SYSCALL_RELEASE(data);
|
COMMON_SYSCALL_RELEASE(data);
|
||||||
|
@ -2293,7 +2295,7 @@ PRE_SYSCALL(ni_syscall)() {}
|
||||||
POST_SYSCALL(ni_syscall)(long res) {}
|
POST_SYSCALL(ni_syscall)(long res) {}
|
||||||
|
|
||||||
PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
|
PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
|
||||||
#if defined(__i386) || defined (__x86_64)
|
#if !SANITIZER_ANDROID && (defined(__i386) || defined (__x86_64))
|
||||||
if (data) {
|
if (data) {
|
||||||
if (request == ptrace_setregs) {
|
if (request == ptrace_setregs) {
|
||||||
PRE_READ((void *)data, struct_user_regs_struct_sz);
|
PRE_READ((void *)data, struct_user_regs_struct_sz);
|
||||||
|
@ -2312,7 +2314,7 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {
|
POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {
|
||||||
#if defined(__i386) || defined (__x86_64)
|
#if !SANITIZER_ANDROID && (defined(__i386) || defined (__x86_64))
|
||||||
if (res >= 0 && data) {
|
if (res >= 0 && data) {
|
||||||
// Note that this is different from the interceptor in
|
// Note that this is different from the interceptor in
|
||||||
// sanitizer_common_interceptors.inc.
|
// sanitizer_common_interceptors.inc.
|
||||||
|
|
|
@ -1,214 +0,0 @@
|
||||||
//===-- sanitizer_coverage.cc ---------------------------------------------===//
|
|
||||||
//
|
|
||||||
// This file is distributed under the University of Illinois Open Source
|
|
||||||
// License. See LICENSE.TXT for details.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
//
|
|
||||||
// Sanitizer Coverage.
|
|
||||||
// This file implements run-time support for a poor man's coverage tool.
|
|
||||||
//
|
|
||||||
// Compiler instrumentation:
|
|
||||||
// For every interesting basic block the compiler injects the following code:
|
|
||||||
// if (*Guard) {
|
|
||||||
// __sanitizer_cov();
|
|
||||||
// *Guard = 1;
|
|
||||||
// }
|
|
||||||
// It's fine to call __sanitizer_cov more than once for a given block.
|
|
||||||
//
|
|
||||||
// Run-time:
|
|
||||||
// - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC).
|
|
||||||
// - __sanitizer_cov_dump: dump the coverage data to disk.
|
|
||||||
// For every module of the current process that has coverage data
|
|
||||||
// this will create a file module_name.PID.sancov. The file format is simple:
|
|
||||||
// it's just a sorted sequence of 4-byte offsets in the module.
|
|
||||||
//
|
|
||||||
// Eventually, this coverage implementation should be obsoleted by a more
|
|
||||||
// powerful general purpose Clang/LLVM coverage instrumentation.
|
|
||||||
// Consider this implementation as prototype.
|
|
||||||
//
|
|
||||||
// FIXME: support (or at least test with) dlclose.
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#include "sanitizer_allocator_internal.h"
|
|
||||||
#include "sanitizer_common.h"
|
|
||||||
#include "sanitizer_libc.h"
|
|
||||||
#include "sanitizer_mutex.h"
|
|
||||||
#include "sanitizer_procmaps.h"
|
|
||||||
#include "sanitizer_stacktrace.h"
|
|
||||||
#include "sanitizer_flags.h"
|
|
||||||
|
|
||||||
atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
|
|
||||||
|
|
||||||
// pc_array is the array containing the covered PCs.
|
|
||||||
// To make the pc_array thread- and async-signal-safe it has to be large enough.
|
|
||||||
// 128M counters "ought to be enough for anybody" (4M on 32-bit).
|
|
||||||
// pc_array is allocated with MmapNoReserveOrDie and so it uses only as
|
|
||||||
// much RAM as it really needs.
|
|
||||||
static const uptr kPcArraySize = FIRST_32_SECOND_64(1 << 22, 1 << 27);
|
|
||||||
static uptr *pc_array;
|
|
||||||
static atomic_uintptr_t pc_array_index;
|
|
||||||
|
|
||||||
static bool cov_sandboxed = false;
|
|
||||||
static int cov_fd = kInvalidFd;
|
|
||||||
static unsigned int cov_max_block_size = 0;
|
|
||||||
|
|
||||||
namespace __sanitizer {
|
|
||||||
|
|
||||||
// Simply add the pc into the vector under lock. If the function is called more
|
|
||||||
// than once for a given PC it will be inserted multiple times, which is fine.
|
|
||||||
static void CovAdd(uptr pc) {
|
|
||||||
if (!pc_array) return;
|
|
||||||
uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed);
|
|
||||||
CHECK_LT(idx, kPcArraySize);
|
|
||||||
pc_array[idx] = pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CovInit() {
|
|
||||||
pc_array = reinterpret_cast<uptr *>(
|
|
||||||
MmapNoReserveOrDie(sizeof(uptr) * kPcArraySize, "CovInit"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool CompareLess(const uptr &a, const uptr &b) {
|
|
||||||
return a < b;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block layout for packed file format: header, followed by module name (no
|
|
||||||
// trailing zero), followed by data blob.
|
|
||||||
struct CovHeader {
|
|
||||||
int pid;
|
|
||||||
unsigned int module_name_length;
|
|
||||||
unsigned int data_length;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void CovWritePacked(int pid, const char *module, const void *blob,
|
|
||||||
unsigned int blob_size) {
|
|
||||||
CHECK_GE(cov_fd, 0);
|
|
||||||
unsigned module_name_length = internal_strlen(module);
|
|
||||||
CovHeader header = {pid, module_name_length, blob_size};
|
|
||||||
|
|
||||||
if (cov_max_block_size == 0) {
|
|
||||||
// Writing to a file. Just go ahead.
|
|
||||||
internal_write(cov_fd, &header, sizeof(header));
|
|
||||||
internal_write(cov_fd, module, module_name_length);
|
|
||||||
internal_write(cov_fd, blob, blob_size);
|
|
||||||
} else {
|
|
||||||
// Writing to a socket. We want to split the data into appropriately sized
|
|
||||||
// blocks.
|
|
||||||
InternalScopedBuffer<char> block(cov_max_block_size);
|
|
||||||
CHECK_EQ((uptr)block.data(), (uptr)(CovHeader *)block.data());
|
|
||||||
uptr header_size_with_module = sizeof(header) + module_name_length;
|
|
||||||
CHECK_LT(header_size_with_module, cov_max_block_size);
|
|
||||||
unsigned int max_payload_size =
|
|
||||||
cov_max_block_size - header_size_with_module;
|
|
||||||
char *block_pos = block.data();
|
|
||||||
internal_memcpy(block_pos, &header, sizeof(header));
|
|
||||||
block_pos += sizeof(header);
|
|
||||||
internal_memcpy(block_pos, module, module_name_length);
|
|
||||||
block_pos += module_name_length;
|
|
||||||
char *block_data_begin = block_pos;
|
|
||||||
char *blob_pos = (char *)blob;
|
|
||||||
while (blob_size > 0) {
|
|
||||||
unsigned int payload_size = Min(blob_size, max_payload_size);
|
|
||||||
blob_size -= payload_size;
|
|
||||||
internal_memcpy(block_data_begin, blob_pos, payload_size);
|
|
||||||
blob_pos += payload_size;
|
|
||||||
((CovHeader *)block.data())->data_length = payload_size;
|
|
||||||
internal_write(cov_fd, block.data(),
|
|
||||||
header_size_with_module + payload_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump the coverage on disk.
|
|
||||||
static void CovDump() {
|
|
||||||
if (!common_flags()->coverage) return;
|
|
||||||
#if !SANITIZER_WINDOWS
|
|
||||||
if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
|
|
||||||
return;
|
|
||||||
uptr size = atomic_load(&pc_array_index, memory_order_relaxed);
|
|
||||||
InternalSort(&pc_array, size, CompareLess);
|
|
||||||
InternalMmapVector<u32> offsets(size);
|
|
||||||
const uptr *vb = pc_array;
|
|
||||||
const uptr *ve = vb + size;
|
|
||||||
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
|
|
||||||
uptr mb, me, off, prot;
|
|
||||||
InternalScopedBuffer<char> module(4096);
|
|
||||||
InternalScopedBuffer<char> path(4096 * 2);
|
|
||||||
for (int i = 0;
|
|
||||||
proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot);
|
|
||||||
i++) {
|
|
||||||
if ((prot & MemoryMappingLayout::kProtectionExecute) == 0)
|
|
||||||
continue;
|
|
||||||
while (vb < ve && *vb < mb) vb++;
|
|
||||||
if (vb >= ve) break;
|
|
||||||
if (*vb < me) {
|
|
||||||
offsets.clear();
|
|
||||||
const uptr *old_vb = vb;
|
|
||||||
CHECK_LE(off, *vb);
|
|
||||||
for (; vb < ve && *vb < me; vb++) {
|
|
||||||
uptr diff = *vb - (i ? mb : 0) + off;
|
|
||||||
CHECK_LE(diff, 0xffffffffU);
|
|
||||||
offsets.push_back(static_cast<u32>(diff));
|
|
||||||
}
|
|
||||||
char *module_name = StripModuleName(module.data());
|
|
||||||
if (cov_sandboxed) {
|
|
||||||
CovWritePacked(internal_getpid(), module_name, offsets.data(),
|
|
||||||
offsets.size() * sizeof(u32));
|
|
||||||
VReport(1, " CovDump: %zd PCs written to packed file\n", vb - old_vb);
|
|
||||||
} else {
|
|
||||||
// One file per module per process.
|
|
||||||
internal_snprintf((char *)path.data(), path.size(), "%s.%zd.sancov",
|
|
||||||
module_name, internal_getpid());
|
|
||||||
uptr fd = OpenFile(path.data(), true);
|
|
||||||
if (internal_iserror(fd)) {
|
|
||||||
Report(" CovDump: failed to open %s for writing\n", path.data());
|
|
||||||
} else {
|
|
||||||
internal_write(fd, offsets.data(), offsets.size() * sizeof(u32));
|
|
||||||
internal_close(fd);
|
|
||||||
VReport(1, " CovDump: %s: %zd PCs written\n", path.data(),
|
|
||||||
vb - old_vb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InternalFree(module_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cov_fd >= 0)
|
|
||||||
internal_close(cov_fd);
|
|
||||||
#endif // !SANITIZER_WINDOWS
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OpenPackedFileForWriting() {
|
|
||||||
CHECK(cov_fd == kInvalidFd);
|
|
||||||
InternalScopedBuffer<char> path(1024);
|
|
||||||
internal_snprintf((char *)path.data(), path.size(), "%zd.sancov.packed",
|
|
||||||
internal_getpid());
|
|
||||||
uptr fd = OpenFile(path.data(), true);
|
|
||||||
if (internal_iserror(fd)) {
|
|
||||||
Report(" Coverage: failed to open %s for writing\n", path.data());
|
|
||||||
Die();
|
|
||||||
}
|
|
||||||
cov_fd = fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
|
|
||||||
if (!args) return;
|
|
||||||
if (!common_flags()->coverage) return;
|
|
||||||
cov_sandboxed = args->coverage_sandboxed;
|
|
||||||
if (!cov_sandboxed) return;
|
|
||||||
cov_fd = args->coverage_fd;
|
|
||||||
cov_max_block_size = args->coverage_max_block_size;
|
|
||||||
if (cov_fd < 0)
|
|
||||||
// Pre-open the file now. The sandbox won't allow us to do it later.
|
|
||||||
OpenPackedFileForWriting();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace __sanitizer
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov() {
|
|
||||||
CovAdd(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()));
|
|
||||||
}
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
|
|
||||||
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { CovInit(); }
|
|
||||||
} // extern "C"
|
|
|
@ -0,0 +1,377 @@
|
||||||
|
//===-- sanitizer_coverage.cc ---------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Sanitizer Coverage.
|
||||||
|
// This file implements run-time support for a poor man's coverage tool.
|
||||||
|
//
|
||||||
|
// Compiler instrumentation:
|
||||||
|
// For every interesting basic block the compiler injects the following code:
|
||||||
|
// if (*Guard) {
|
||||||
|
// __sanitizer_cov();
|
||||||
|
// *Guard = 1;
|
||||||
|
// }
|
||||||
|
// It's fine to call __sanitizer_cov more than once for a given block.
|
||||||
|
//
|
||||||
|
// Run-time:
|
||||||
|
// - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC).
|
||||||
|
// - __sanitizer_cov_dump: dump the coverage data to disk.
|
||||||
|
// For every module of the current process that has coverage data
|
||||||
|
// this will create a file module_name.PID.sancov. The file format is simple:
|
||||||
|
// it's just a sorted sequence of 4-byte offsets in the module.
|
||||||
|
//
|
||||||
|
// Eventually, this coverage implementation should be obsoleted by a more
|
||||||
|
// powerful general purpose Clang/LLVM coverage instrumentation.
|
||||||
|
// Consider this implementation as prototype.
|
||||||
|
//
|
||||||
|
// FIXME: support (or at least test with) dlclose.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "sanitizer_allocator_internal.h"
|
||||||
|
#include "sanitizer_common.h"
|
||||||
|
#include "sanitizer_libc.h"
|
||||||
|
#include "sanitizer_mutex.h"
|
||||||
|
#include "sanitizer_procmaps.h"
|
||||||
|
#include "sanitizer_stacktrace.h"
|
||||||
|
#include "sanitizer_flags.h"
|
||||||
|
|
||||||
|
atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
|
||||||
|
|
||||||
|
// pc_array is the array containing the covered PCs.
|
||||||
|
// To make the pc_array thread- and async-signal-safe it has to be large enough.
|
||||||
|
// 128M counters "ought to be enough for anybody" (4M on 32-bit).
|
||||||
|
|
||||||
|
// With coverage_direct=1 in ASAN_OPTIONS, pc_array memory is mapped to a file.
|
||||||
|
// In this mode, __sanitizer_cov_dump does nothing, and CovUpdateMapping()
|
||||||
|
// dump current memory layout to another file.
|
||||||
|
|
||||||
|
static bool cov_sandboxed = false;
|
||||||
|
static int cov_fd = kInvalidFd;
|
||||||
|
static unsigned int cov_max_block_size = 0;
|
||||||
|
|
||||||
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
class CoverageData {
|
||||||
|
public:
|
||||||
|
void Init();
|
||||||
|
void BeforeFork();
|
||||||
|
void AfterFork(int child_pid);
|
||||||
|
void Extend(uptr npcs);
|
||||||
|
void Add(uptr pc);
|
||||||
|
|
||||||
|
uptr *data();
|
||||||
|
uptr size();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Maximal size pc array may ever grow.
|
||||||
|
// We MmapNoReserve this space to ensure that the array is contiguous.
|
||||||
|
static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27);
|
||||||
|
// The amount file mapping for the pc array is grown by.
|
||||||
|
static const uptr kPcArrayMmapSize = 64 * 1024;
|
||||||
|
|
||||||
|
// pc_array is allocated with MmapNoReserveOrDie and so it uses only as
|
||||||
|
// much RAM as it really needs.
|
||||||
|
uptr *pc_array;
|
||||||
|
// Index of the first available pc_array slot.
|
||||||
|
atomic_uintptr_t pc_array_index;
|
||||||
|
// Array size.
|
||||||
|
atomic_uintptr_t pc_array_size;
|
||||||
|
// Current file mapped size of the pc array.
|
||||||
|
uptr pc_array_mapped_size;
|
||||||
|
// Descriptor of the file mapped pc array.
|
||||||
|
int pc_fd;
|
||||||
|
StaticSpinMutex mu;
|
||||||
|
|
||||||
|
void DirectOpen();
|
||||||
|
void ReInit();
|
||||||
|
};
|
||||||
|
|
||||||
|
static CoverageData coverage_data;
|
||||||
|
|
||||||
|
void CoverageData::DirectOpen() {
|
||||||
|
InternalScopedString path(1024);
|
||||||
|
internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw",
|
||||||
|
common_flags()->coverage_dir, internal_getpid());
|
||||||
|
pc_fd = OpenFile(path.data(), true);
|
||||||
|
if (internal_iserror(pc_fd)) {
|
||||||
|
Report(" Coverage: failed to open %s for writing\n", path.data());
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
|
||||||
|
pc_array_mapped_size = 0;
|
||||||
|
CovUpdateMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageData::Init() {
|
||||||
|
pc_array = reinterpret_cast<uptr *>(
|
||||||
|
MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit"));
|
||||||
|
pc_fd = kInvalidFd;
|
||||||
|
if (common_flags()->coverage_direct) {
|
||||||
|
atomic_store(&pc_array_size, 0, memory_order_relaxed);
|
||||||
|
atomic_store(&pc_array_index, 0, memory_order_relaxed);
|
||||||
|
} else {
|
||||||
|
atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed);
|
||||||
|
atomic_store(&pc_array_index, 0, memory_order_relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageData::ReInit() {
|
||||||
|
internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize);
|
||||||
|
if (pc_fd != kInvalidFd) internal_close(pc_fd);
|
||||||
|
if (common_flags()->coverage_direct) {
|
||||||
|
// In memory-mapped mode we must extend the new file to the known array
|
||||||
|
// size.
|
||||||
|
uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
|
||||||
|
Init();
|
||||||
|
if (size) Extend(size);
|
||||||
|
} else {
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageData::BeforeFork() {
|
||||||
|
mu.Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverageData::AfterFork(int child_pid) {
|
||||||
|
// We are single-threaded so it's OK to release the lock early.
|
||||||
|
mu.Unlock();
|
||||||
|
if (child_pid == 0) ReInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend coverage PC array to fit additional npcs elements.
|
||||||
|
void CoverageData::Extend(uptr npcs) {
|
||||||
|
if (!common_flags()->coverage_direct) return;
|
||||||
|
SpinMutexLock l(&mu);
|
||||||
|
|
||||||
|
if (pc_fd == kInvalidFd) DirectOpen();
|
||||||
|
CHECK_NE(pc_fd, kInvalidFd);
|
||||||
|
|
||||||
|
uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
|
||||||
|
size += npcs * sizeof(uptr);
|
||||||
|
|
||||||
|
if (size > pc_array_mapped_size) {
|
||||||
|
uptr new_mapped_size = pc_array_mapped_size;
|
||||||
|
while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize;
|
||||||
|
|
||||||
|
// Extend the file and map the new space at the end of pc_array.
|
||||||
|
uptr res = internal_ftruncate(pc_fd, new_mapped_size);
|
||||||
|
int err;
|
||||||
|
if (internal_iserror(res, &err)) {
|
||||||
|
Printf("failed to extend raw coverage file: %d\n", err);
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
void *p = MapWritableFileToMemory(pc_array + pc_array_mapped_size,
|
||||||
|
new_mapped_size - pc_array_mapped_size,
|
||||||
|
pc_fd, pc_array_mapped_size);
|
||||||
|
CHECK_EQ(p, pc_array + pc_array_mapped_size);
|
||||||
|
pc_array_mapped_size = new_mapped_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_store(&pc_array_size, size, memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simply add the pc into the vector under lock. If the function is called more
|
||||||
|
// than once for a given PC it will be inserted multiple times, which is fine.
|
||||||
|
void CoverageData::Add(uptr pc) {
|
||||||
|
if (!pc_array) return;
|
||||||
|
uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed);
|
||||||
|
CHECK_LT(idx * sizeof(uptr),
|
||||||
|
atomic_load(&pc_array_size, memory_order_acquire));
|
||||||
|
pc_array[idx] = pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr *CoverageData::data() {
|
||||||
|
return pc_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr CoverageData::size() {
|
||||||
|
return atomic_load(&pc_array_index, memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block layout for packed file format: header, followed by module name (no
|
||||||
|
// trailing zero), followed by data blob.
|
||||||
|
struct CovHeader {
|
||||||
|
int pid;
|
||||||
|
unsigned int module_name_length;
|
||||||
|
unsigned int data_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void CovWritePacked(int pid, const char *module, const void *blob,
|
||||||
|
unsigned int blob_size) {
|
||||||
|
if (cov_fd < 0) return;
|
||||||
|
unsigned module_name_length = internal_strlen(module);
|
||||||
|
CovHeader header = {pid, module_name_length, blob_size};
|
||||||
|
|
||||||
|
if (cov_max_block_size == 0) {
|
||||||
|
// Writing to a file. Just go ahead.
|
||||||
|
internal_write(cov_fd, &header, sizeof(header));
|
||||||
|
internal_write(cov_fd, module, module_name_length);
|
||||||
|
internal_write(cov_fd, blob, blob_size);
|
||||||
|
} else {
|
||||||
|
// Writing to a socket. We want to split the data into appropriately sized
|
||||||
|
// blocks.
|
||||||
|
InternalScopedBuffer<char> block(cov_max_block_size);
|
||||||
|
CHECK_EQ((uptr)block.data(), (uptr)(CovHeader *)block.data());
|
||||||
|
uptr header_size_with_module = sizeof(header) + module_name_length;
|
||||||
|
CHECK_LT(header_size_with_module, cov_max_block_size);
|
||||||
|
unsigned int max_payload_size =
|
||||||
|
cov_max_block_size - header_size_with_module;
|
||||||
|
char *block_pos = block.data();
|
||||||
|
internal_memcpy(block_pos, &header, sizeof(header));
|
||||||
|
block_pos += sizeof(header);
|
||||||
|
internal_memcpy(block_pos, module, module_name_length);
|
||||||
|
block_pos += module_name_length;
|
||||||
|
char *block_data_begin = block_pos;
|
||||||
|
char *blob_pos = (char *)blob;
|
||||||
|
while (blob_size > 0) {
|
||||||
|
unsigned int payload_size = Min(blob_size, max_payload_size);
|
||||||
|
blob_size -= payload_size;
|
||||||
|
internal_memcpy(block_data_begin, blob_pos, payload_size);
|
||||||
|
blob_pos += payload_size;
|
||||||
|
((CovHeader *)block.data())->data_length = payload_size;
|
||||||
|
internal_write(cov_fd, block.data(),
|
||||||
|
header_size_with_module + payload_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If packed = false: <name>.<pid>.<sancov> (name = module name).
|
||||||
|
// If packed = true and name == 0: <pid>.<sancov>.<packed>.
|
||||||
|
// If packed = true and name != 0: <name>.<sancov>.<packed> (name is
|
||||||
|
// user-supplied).
|
||||||
|
static int CovOpenFile(bool packed, const char* name) {
|
||||||
|
InternalScopedBuffer<char> path(1024);
|
||||||
|
if (!packed) {
|
||||||
|
CHECK(name);
|
||||||
|
internal_snprintf((char *)path.data(), path.size(), "%s/%s.%zd.sancov",
|
||||||
|
common_flags()->coverage_dir, name, internal_getpid());
|
||||||
|
} else {
|
||||||
|
if (!name)
|
||||||
|
internal_snprintf((char *)path.data(), path.size(),
|
||||||
|
"%s/%zd.sancov.packed", common_flags()->coverage_dir,
|
||||||
|
internal_getpid());
|
||||||
|
else
|
||||||
|
internal_snprintf((char *)path.data(), path.size(), "%s/%s.sancov.packed",
|
||||||
|
common_flags()->coverage_dir, name);
|
||||||
|
}
|
||||||
|
uptr fd = OpenFile(path.data(), true);
|
||||||
|
if (internal_iserror(fd)) {
|
||||||
|
Report(" SanitizerCoverage: failed to open %s for writing\n", path.data());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump the coverage on disk.
|
||||||
|
static void CovDump() {
|
||||||
|
if (!common_flags()->coverage || common_flags()->coverage_direct) return;
|
||||||
|
#if !SANITIZER_WINDOWS
|
||||||
|
if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
|
||||||
|
return;
|
||||||
|
uptr size = coverage_data.size();
|
||||||
|
InternalMmapVector<u32> offsets(size);
|
||||||
|
uptr *vb = coverage_data.data();
|
||||||
|
uptr *ve = vb + size;
|
||||||
|
SortArray(vb, size);
|
||||||
|
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
|
||||||
|
uptr mb, me, off, prot;
|
||||||
|
InternalScopedBuffer<char> module(4096);
|
||||||
|
InternalScopedBuffer<char> path(4096 * 2);
|
||||||
|
for (int i = 0;
|
||||||
|
proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot);
|
||||||
|
i++) {
|
||||||
|
if ((prot & MemoryMappingLayout::kProtectionExecute) == 0)
|
||||||
|
continue;
|
||||||
|
while (vb < ve && *vb < mb) vb++;
|
||||||
|
if (vb >= ve) break;
|
||||||
|
if (*vb < me) {
|
||||||
|
offsets.clear();
|
||||||
|
const uptr *old_vb = vb;
|
||||||
|
CHECK_LE(off, *vb);
|
||||||
|
for (; vb < ve && *vb < me; vb++) {
|
||||||
|
uptr diff = *vb - (i ? mb : 0) + off;
|
||||||
|
CHECK_LE(diff, 0xffffffffU);
|
||||||
|
offsets.push_back(static_cast<u32>(diff));
|
||||||
|
}
|
||||||
|
char *module_name = StripModuleName(module.data());
|
||||||
|
if (cov_sandboxed) {
|
||||||
|
if (cov_fd >= 0) {
|
||||||
|
CovWritePacked(internal_getpid(), module_name, offsets.data(),
|
||||||
|
offsets.size() * sizeof(u32));
|
||||||
|
VReport(1, " CovDump: %zd PCs written to packed file\n", vb - old_vb);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// One file per module per process.
|
||||||
|
internal_snprintf((char *)path.data(), path.size(), "%s/%s.%zd.sancov",
|
||||||
|
common_flags()->coverage_dir, module_name,
|
||||||
|
internal_getpid());
|
||||||
|
int fd = CovOpenFile(false /* packed */, module_name);
|
||||||
|
if (fd > 0) {
|
||||||
|
internal_write(fd, offsets.data(), offsets.size() * sizeof(u32));
|
||||||
|
internal_close(fd);
|
||||||
|
VReport(1, " CovDump: %s: %zd PCs written\n", path.data(),
|
||||||
|
vb - old_vb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InternalFree(module_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cov_fd >= 0)
|
||||||
|
internal_close(cov_fd);
|
||||||
|
#endif // !SANITIZER_WINDOWS
|
||||||
|
}
|
||||||
|
|
||||||
|
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
|
||||||
|
if (!args) return;
|
||||||
|
if (!common_flags()->coverage) return;
|
||||||
|
cov_sandboxed = args->coverage_sandboxed;
|
||||||
|
if (!cov_sandboxed) return;
|
||||||
|
cov_fd = args->coverage_fd;
|
||||||
|
cov_max_block_size = args->coverage_max_block_size;
|
||||||
|
if (cov_fd < 0)
|
||||||
|
// Pre-open the file now. The sandbox won't allow us to do it later.
|
||||||
|
cov_fd = CovOpenFile(true /* packed */, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int MaybeOpenCovFile(const char *name) {
|
||||||
|
CHECK(name);
|
||||||
|
if (!common_flags()->coverage) return -1;
|
||||||
|
return CovOpenFile(true /* packed */, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CovBeforeFork() {
|
||||||
|
coverage_data.BeforeFork();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CovAfterFork(int child_pid) {
|
||||||
|
coverage_data.AfterFork(child_pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __sanitizer
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov() {
|
||||||
|
coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()));
|
||||||
|
}
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
|
||||||
|
coverage_data.Init();
|
||||||
|
}
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(uptr npcs) {
|
||||||
|
if (!common_flags()->coverage || !common_flags()->coverage_direct) return;
|
||||||
|
if (SANITIZER_ANDROID) {
|
||||||
|
// dlopen/dlclose interceptors do not work on Android, so we rely on
|
||||||
|
// Extend() calls to update .sancov.map.
|
||||||
|
CovUpdateMapping(GET_CALLER_PC());
|
||||||
|
}
|
||||||
|
coverage_data.Extend(npcs);
|
||||||
|
}
|
||||||
|
SANITIZER_INTERFACE_ATTRIBUTE
|
||||||
|
sptr __sanitizer_maybe_open_cov_file(const char *name) {
|
||||||
|
return MaybeOpenCovFile(name);
|
||||||
|
}
|
||||||
|
} // extern "C"
|
|
@ -0,0 +1,126 @@
|
||||||
|
//===-- sanitizer_coverage_mapping.cc -------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Mmap-based implementation of sanitizer coverage.
|
||||||
|
//
|
||||||
|
// This is part of the implementation of code coverage that does not require
|
||||||
|
// __sanitizer_cov_dump() call. Data is stored in 2 files per process.
|
||||||
|
//
|
||||||
|
// $pid.sancov.map describes process memory layout in the following text-based
|
||||||
|
// format:
|
||||||
|
// <pointer size in bits> // 1 line, 32 or 64
|
||||||
|
// <mapping start> <mapping end> <base address> <dso name> // repeated
|
||||||
|
// ...
|
||||||
|
// Mapping lines are NOT sorted. This file is updated every time memory layout
|
||||||
|
// is changed (i.e. in dlopen() and dlclose() interceptors).
|
||||||
|
//
|
||||||
|
// $pid.sancov.raw is a binary dump of PC values, sizeof(uptr) each. Again, not
|
||||||
|
// sorted. This file is extended by 64Kb at a time and mapped into memory. It
|
||||||
|
// contains one or more 0 words at the end, up to the next 64Kb aligned offset.
|
||||||
|
//
|
||||||
|
// To convert these 2 files to the usual .sancov format, run sancov.py rawunpack
|
||||||
|
// $pid.sancov.raw.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "sanitizer_allocator_internal.h"
|
||||||
|
#include "sanitizer_libc.h"
|
||||||
|
#include "sanitizer_procmaps.h"
|
||||||
|
|
||||||
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
static const uptr kMaxNumberOfModules = 1 << 14;
|
||||||
|
static const uptr kMaxTextSize = 64 * 1024;
|
||||||
|
|
||||||
|
struct CachedMapping {
|
||||||
|
public:
|
||||||
|
bool NeedsUpdate(uptr pc) {
|
||||||
|
int new_pid = internal_getpid();
|
||||||
|
if (last_pid == new_pid && pc && pc >= last_range_start &&
|
||||||
|
pc < last_range_end)
|
||||||
|
return false;
|
||||||
|
last_pid = new_pid;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetModuleRange(uptr start, uptr end) {
|
||||||
|
last_range_start = start;
|
||||||
|
last_range_end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uptr last_range_start, last_range_end;
|
||||||
|
int last_pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
static CachedMapping cached_mapping;
|
||||||
|
static StaticSpinMutex mapping_mu;
|
||||||
|
|
||||||
|
void CovUpdateMapping(uptr caller_pc) {
|
||||||
|
if (!common_flags()->coverage || !common_flags()->coverage_direct) return;
|
||||||
|
|
||||||
|
SpinMutexLock l(&mapping_mu);
|
||||||
|
|
||||||
|
if (!cached_mapping.NeedsUpdate(caller_pc))
|
||||||
|
return;
|
||||||
|
|
||||||
|
InternalScopedString text(kMaxTextSize);
|
||||||
|
InternalScopedBuffer<char> modules_data(kMaxNumberOfModules *
|
||||||
|
sizeof(LoadedModule));
|
||||||
|
LoadedModule *modules = (LoadedModule *)modules_data.data();
|
||||||
|
CHECK(modules);
|
||||||
|
int n_modules = GetListOfModules(modules, kMaxNumberOfModules,
|
||||||
|
/* filter */ 0);
|
||||||
|
|
||||||
|
text.append("%d\n", sizeof(uptr) * 8);
|
||||||
|
for (int i = 0; i < n_modules; ++i) {
|
||||||
|
char *module_name = StripModuleName(modules[i].full_name());
|
||||||
|
for (unsigned j = 0; j < modules[i].n_ranges(); ++j) {
|
||||||
|
if (modules[i].address_range_executable(j)) {
|
||||||
|
uptr start = modules[i].address_range_start(j);
|
||||||
|
uptr end = modules[i].address_range_end(j);
|
||||||
|
uptr base = modules[i].base_address();
|
||||||
|
text.append("%zx %zx %zx %s\n", start, end, base, module_name);
|
||||||
|
if (caller_pc && caller_pc >= start && caller_pc < end)
|
||||||
|
cached_mapping.SetModuleRange(start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InternalFree(module_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
int err;
|
||||||
|
InternalScopedString tmp_path(64 +
|
||||||
|
internal_strlen(common_flags()->coverage_dir));
|
||||||
|
uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(),
|
||||||
|
"%s/%zd.sancov.map.tmp", common_flags()->coverage_dir,
|
||||||
|
internal_getpid());
|
||||||
|
CHECK_LE(res, tmp_path.size());
|
||||||
|
uptr map_fd = OpenFile(tmp_path.data(), true);
|
||||||
|
if (internal_iserror(map_fd)) {
|
||||||
|
Report(" Coverage: failed to open %s for writing\n", tmp_path.data());
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
|
||||||
|
res = internal_write(map_fd, text.data(), text.length());
|
||||||
|
if (internal_iserror(res, &err)) {
|
||||||
|
Printf("sancov.map write failed: %d\n", err);
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
internal_close(map_fd);
|
||||||
|
|
||||||
|
InternalScopedString path(64 + internal_strlen(common_flags()->coverage_dir));
|
||||||
|
res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map",
|
||||||
|
common_flags()->coverage_dir, internal_getpid());
|
||||||
|
CHECK_LE(res, path.size());
|
||||||
|
res = internal_rename(tmp_path.data(), path.data());
|
||||||
|
if (internal_iserror(res, &err)) {
|
||||||
|
Printf("sancov.map rename failed: %d\n", err);
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __sanitizer
|
|
@ -185,8 +185,7 @@ u32 DD::allocateId(DDCallback *cb) {
|
||||||
id = id_gen++;
|
id = id_gen++;
|
||||||
}
|
}
|
||||||
CHECK_LE(id, kMaxMutex);
|
CHECK_LE(id, kMaxMutex);
|
||||||
VPrintf(3, "#%llu: DD::allocateId assign id %d\n",
|
VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
|
||||||
cb->lt->ctx, id);
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,11 @@ struct FlagDescription {
|
||||||
|
|
||||||
IntrusiveList<FlagDescription> flag_descriptions;
|
IntrusiveList<FlagDescription> flag_descriptions;
|
||||||
|
|
||||||
|
// If set, the tool will install its own SEGV signal handler by default.
|
||||||
|
#ifndef SANITIZER_NEEDS_SEGV
|
||||||
|
# define SANITIZER_NEEDS_SEGV 1
|
||||||
|
#endif
|
||||||
|
|
||||||
void SetCommonFlagsDefaults(CommonFlags *f) {
|
void SetCommonFlagsDefaults(CommonFlags *f) {
|
||||||
f->symbolize = true;
|
f->symbolize = true;
|
||||||
f->external_symbolizer_path = 0;
|
f->external_symbolizer_path = 0;
|
||||||
|
@ -53,7 +58,12 @@ void SetCommonFlagsDefaults(CommonFlags *f) {
|
||||||
f->legacy_pthread_cond = false;
|
f->legacy_pthread_cond = false;
|
||||||
f->intercept_tls_get_addr = false;
|
f->intercept_tls_get_addr = false;
|
||||||
f->coverage = false;
|
f->coverage = false;
|
||||||
|
f->coverage_direct = SANITIZER_ANDROID;
|
||||||
|
f->coverage_dir = ".";
|
||||||
f->full_address_space = false;
|
f->full_address_space = false;
|
||||||
|
f->suppressions = "";
|
||||||
|
f->print_suppressions = true;
|
||||||
|
f->disable_coredump = (SANITIZER_WORDSIZE == 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
|
void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
|
||||||
|
@ -125,9 +135,23 @@ void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
|
||||||
ParseFlag(str, &f->coverage, "coverage",
|
ParseFlag(str, &f->coverage, "coverage",
|
||||||
"If set, coverage information will be dumped at program shutdown (if the "
|
"If set, coverage information will be dumped at program shutdown (if the "
|
||||||
"coverage instrumentation was enabled at compile time).");
|
"coverage instrumentation was enabled at compile time).");
|
||||||
|
ParseFlag(str, &f->coverage_direct, "coverage_direct",
|
||||||
|
"If set, coverage information will be dumped directly to a memory "
|
||||||
|
"mapped file. This way data is not lost even if the process is "
|
||||||
|
"suddenly killed.");
|
||||||
|
ParseFlag(str, &f->coverage_dir, "coverage_dir",
|
||||||
|
"Target directory for coverage dumps. Defaults to the current "
|
||||||
|
"directory.");
|
||||||
ParseFlag(str, &f->full_address_space, "full_address_space",
|
ParseFlag(str, &f->full_address_space, "full_address_space",
|
||||||
"Sanitize complete address space; "
|
"Sanitize complete address space; "
|
||||||
"by default kernel area on 32-bit platforms will not be sanitized");
|
"by default kernel area on 32-bit platforms will not be sanitized");
|
||||||
|
ParseFlag(str, &f->suppressions, "suppressions", "Suppressions file name.");
|
||||||
|
ParseFlag(str, &f->print_suppressions, "print_suppressions",
|
||||||
|
"Print matched suppressions at exit.");
|
||||||
|
ParseFlag(str, &f->disable_coredump, "disable_coredump",
|
||||||
|
"Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
|
||||||
|
"dumping a 16T+ core file. Ignored on OSes that don't dump core by"
|
||||||
|
"default and for sanitizers that don't reserve lots of virtual memory.");
|
||||||
|
|
||||||
// Do a sanity check for certain flags.
|
// Do a sanity check for certain flags.
|
||||||
if (f->malloc_context_size < 1)
|
if (f->malloc_context_size < 1)
|
||||||
|
@ -143,14 +167,17 @@ static bool GetFlagValue(const char *env, const char *name,
|
||||||
pos = internal_strstr(env, name);
|
pos = internal_strstr(env, name);
|
||||||
if (pos == 0)
|
if (pos == 0)
|
||||||
return false;
|
return false;
|
||||||
if (pos != env && ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) {
|
const char *name_end = pos + internal_strlen(name);
|
||||||
|
if ((pos != env &&
|
||||||
|
((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) ||
|
||||||
|
*name_end != '=') {
|
||||||
// Seems to be middle of another flag name or value.
|
// Seems to be middle of another flag name or value.
|
||||||
env = pos + 1;
|
env = pos + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
pos = name_end;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pos += internal_strlen(name);
|
|
||||||
const char *end;
|
const char *end;
|
||||||
if (pos[0] != '=') {
|
if (pos[0] != '=') {
|
||||||
end = pos;
|
end = pos;
|
||||||
|
|
|
@ -52,7 +52,12 @@ struct CommonFlags {
|
||||||
bool help;
|
bool help;
|
||||||
uptr mmap_limit_mb;
|
uptr mmap_limit_mb;
|
||||||
bool coverage;
|
bool coverage;
|
||||||
|
bool coverage_direct;
|
||||||
|
const char *coverage_dir;
|
||||||
bool full_address_space;
|
bool full_address_space;
|
||||||
|
const char *suppressions;
|
||||||
|
bool print_suppressions;
|
||||||
|
bool disable_coredump;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline CommonFlags *common_flags() {
|
inline CommonFlags *common_flags() {
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
//===-- sanitizer_freebsd.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 Sanitizer runtime. It contains FreeBSD-specific
|
||||||
|
// definitions.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef SANITIZER_FREEBSD_H
|
||||||
|
#define SANITIZER_FREEBSD_H
|
||||||
|
|
||||||
|
#include "sanitizer_internal_defs.h"
|
||||||
|
|
||||||
|
// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
|
||||||
|
// 32-bit mode.
|
||||||
|
#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
|
||||||
|
# include <osreldate.h>
|
||||||
|
# if __FreeBSD_version <= 902001 // v9.2
|
||||||
|
# include <link.h>
|
||||||
|
# include <sys/param.h>
|
||||||
|
# include <ucontext.h>
|
||||||
|
|
||||||
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
typedef unsigned long long __xuint64_t;
|
||||||
|
|
||||||
|
typedef __int32_t __xregister_t;
|
||||||
|
|
||||||
|
typedef struct __xmcontext {
|
||||||
|
__xregister_t mc_onstack;
|
||||||
|
__xregister_t mc_gs;
|
||||||
|
__xregister_t mc_fs;
|
||||||
|
__xregister_t mc_es;
|
||||||
|
__xregister_t mc_ds;
|
||||||
|
__xregister_t mc_edi;
|
||||||
|
__xregister_t mc_esi;
|
||||||
|
__xregister_t mc_ebp;
|
||||||
|
__xregister_t mc_isp;
|
||||||
|
__xregister_t mc_ebx;
|
||||||
|
__xregister_t mc_edx;
|
||||||
|
__xregister_t mc_ecx;
|
||||||
|
__xregister_t mc_eax;
|
||||||
|
__xregister_t mc_trapno;
|
||||||
|
__xregister_t mc_err;
|
||||||
|
__xregister_t mc_eip;
|
||||||
|
__xregister_t mc_cs;
|
||||||
|
__xregister_t mc_eflags;
|
||||||
|
__xregister_t mc_esp;
|
||||||
|
__xregister_t mc_ss;
|
||||||
|
|
||||||
|
int mc_len;
|
||||||
|
int mc_fpformat;
|
||||||
|
int mc_ownedfp;
|
||||||
|
__xregister_t mc_flags;
|
||||||
|
|
||||||
|
int mc_fpstate[128] __aligned(16);
|
||||||
|
__xregister_t mc_fsbase;
|
||||||
|
__xregister_t mc_gsbase;
|
||||||
|
__xregister_t mc_xfpustate;
|
||||||
|
__xregister_t mc_xfpustate_len;
|
||||||
|
|
||||||
|
int mc_spare2[4];
|
||||||
|
} xmcontext_t;
|
||||||
|
|
||||||
|
typedef struct __xucontext {
|
||||||
|
sigset_t uc_sigmask;
|
||||||
|
xmcontext_t uc_mcontext;
|
||||||
|
|
||||||
|
struct __ucontext *uc_link;
|
||||||
|
stack_t uc_stack;
|
||||||
|
int uc_flags;
|
||||||
|
int __spare__[4];
|
||||||
|
} xucontext_t;
|
||||||
|
|
||||||
|
struct xkinfo_vmentry {
|
||||||
|
int kve_structsize;
|
||||||
|
int kve_type;
|
||||||
|
__xuint64_t kve_start;
|
||||||
|
__xuint64_t kve_end;
|
||||||
|
__xuint64_t kve_offset;
|
||||||
|
__xuint64_t kve_vn_fileid;
|
||||||
|
__uint32_t kve_vn_fsid;
|
||||||
|
int kve_flags;
|
||||||
|
int kve_resident;
|
||||||
|
int kve_private_resident;
|
||||||
|
int kve_protection;
|
||||||
|
int kve_ref_count;
|
||||||
|
int kve_shadow_count;
|
||||||
|
int kve_vn_type;
|
||||||
|
__xuint64_t kve_vn_size;
|
||||||
|
__uint32_t kve_vn_rdev;
|
||||||
|
__uint16_t kve_vn_mode;
|
||||||
|
__uint16_t kve_status;
|
||||||
|
int _kve_ispare[12];
|
||||||
|
char kve_path[PATH_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
__uint32_t p_type;
|
||||||
|
__uint32_t p_offset;
|
||||||
|
__uint32_t p_vaddr;
|
||||||
|
__uint32_t p_paddr;
|
||||||
|
__uint32_t p_filesz;
|
||||||
|
__uint32_t p_memsz;
|
||||||
|
__uint32_t p_flags;
|
||||||
|
__uint32_t p_align;
|
||||||
|
} XElf32_Phdr;
|
||||||
|
|
||||||
|
struct xdl_phdr_info {
|
||||||
|
Elf_Addr dlpi_addr;
|
||||||
|
const char *dlpi_name;
|
||||||
|
const XElf32_Phdr *dlpi_phdr;
|
||||||
|
Elf_Half dlpi_phnum;
|
||||||
|
unsigned long long int dlpi_adds;
|
||||||
|
unsigned long long int dlpi_subs;
|
||||||
|
size_t dlpi_tls_modid;
|
||||||
|
void *dlpi_tls_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*__xdl_iterate_hdr_callback)(struct xdl_phdr_info*, size_t, void*);
|
||||||
|
typedef int xdl_iterate_phdr_t(__xdl_iterate_hdr_callback, void*);
|
||||||
|
|
||||||
|
#define xdl_iterate_phdr(callback, param) \
|
||||||
|
(((xdl_iterate_phdr_t*) dl_iterate_phdr)((callback), (param)))
|
||||||
|
|
||||||
|
} // namespace __sanitizer
|
||||||
|
|
||||||
|
# endif // __FreeBSD_version <= 902001
|
||||||
|
#endif // SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
|
||||||
|
|
||||||
|
#endif // SANITIZER_FREEBSD_H
|
|
@ -32,9 +32,13 @@
|
||||||
# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
|
# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// If set, the tool will install its own SEGV signal handler.
|
// We can use .preinit_array section on Linux to call sanitizer initialization
|
||||||
#ifndef SANITIZER_NEEDS_SEGV
|
// functions very early in the process startup (unless PIC macro is defined).
|
||||||
# define SANITIZER_NEEDS_SEGV 1
|
// FIXME: do we have anything like this on Mac?
|
||||||
|
#if SANITIZER_LINUX && !SANITIZER_ANDROID && !defined(PIC)
|
||||||
|
# define SANITIZER_CAN_USE_PREINIT_ARRAY 1
|
||||||
|
#else
|
||||||
|
# define SANITIZER_CAN_USE_PREINIT_ARRAY 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// GCC does not understand __has_feature
|
// GCC does not understand __has_feature
|
||||||
|
|
|
@ -72,6 +72,7 @@ uptr internal_open(const char *filename, int flags, u32 mode);
|
||||||
|
|
||||||
uptr internal_read(fd_t fd, void *buf, uptr count);
|
uptr internal_read(fd_t fd, void *buf, uptr count);
|
||||||
uptr internal_write(fd_t fd, const void *buf, uptr count);
|
uptr internal_write(fd_t fd, const void *buf, uptr count);
|
||||||
|
uptr internal_ftruncate(fd_t fd, uptr size);
|
||||||
|
|
||||||
// OS
|
// OS
|
||||||
uptr internal_filesize(fd_t fd); // -1 on error.
|
uptr internal_filesize(fd_t fd); // -1 on error.
|
||||||
|
@ -81,6 +82,7 @@ uptr internal_fstat(fd_t fd, void *buf);
|
||||||
uptr internal_dup2(int oldfd, int newfd);
|
uptr internal_dup2(int oldfd, int newfd);
|
||||||
uptr internal_readlink(const char *path, char *buf, uptr bufsize);
|
uptr internal_readlink(const char *path, char *buf, uptr bufsize);
|
||||||
uptr internal_unlink(const char *path);
|
uptr internal_unlink(const char *path);
|
||||||
|
uptr internal_rename(const char *oldpath, const char *newpath);
|
||||||
void NORETURN internal__exit(int exitcode);
|
void NORETURN internal__exit(int exitcode);
|
||||||
uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
|
uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "sanitizer_platform.h"
|
#include "sanitizer_platform.h"
|
||||||
#if SANITIZER_LINUX
|
#if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||||
|
|
||||||
#include "sanitizer_libignore.h"
|
#include "sanitizer_libignore.h"
|
||||||
#include "sanitizer_flags.h"
|
#include "sanitizer_flags.h"
|
||||||
|
@ -101,4 +101,4 @@ void LibIgnore::OnLibraryUnloaded() {
|
||||||
|
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
#endif // #if SANITIZER_LINUX
|
#endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||||
|
|
|
@ -44,15 +44,16 @@
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <unwind.h>
|
|
||||||
|
|
||||||
#if SANITIZER_FREEBSD
|
#if SANITIZER_FREEBSD
|
||||||
|
#include <sys/sysctl.h>
|
||||||
#include <machine/atomic.h>
|
#include <machine/atomic.h>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
|
// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
|
||||||
// FreeBSD 9.2 and 10.0.
|
// FreeBSD 9.2 and 10.0.
|
||||||
#include <sys/umtx.h>
|
#include <sys/umtx.h>
|
||||||
}
|
}
|
||||||
|
extern char **environ; // provided by crt1
|
||||||
#endif // SANITIZER_FREEBSD
|
#endif // SANITIZER_FREEBSD
|
||||||
|
|
||||||
#if !SANITIZER_ANDROID
|
#if !SANITIZER_ANDROID
|
||||||
|
@ -132,7 +133,7 @@ uptr internal_open(const char *filename, int flags, u32 mode) {
|
||||||
|
|
||||||
uptr OpenFile(const char *filename, bool write) {
|
uptr OpenFile(const char *filename, bool write) {
|
||||||
return internal_open(filename,
|
return internal_open(filename,
|
||||||
write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660);
|
write ? O_RDWR | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660);
|
||||||
}
|
}
|
||||||
|
|
||||||
uptr internal_read(fd_t fd, void *buf, uptr count) {
|
uptr internal_read(fd_t fd, void *buf, uptr count) {
|
||||||
|
@ -149,6 +150,12 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uptr internal_ftruncate(fd_t fd, uptr size) {
|
||||||
|
sptr res;
|
||||||
|
HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, size));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && !SANITIZER_FREEBSD
|
#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && !SANITIZER_FREEBSD
|
||||||
static void stat64_to_stat(struct stat64 *in, struct stat *out) {
|
static void stat64_to_stat(struct stat64 *in, struct stat *out) {
|
||||||
internal_memset(out, 0, sizeof(*out));
|
internal_memset(out, 0, sizeof(*out));
|
||||||
|
@ -244,6 +251,15 @@ uptr internal_unlink(const char *path) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uptr internal_rename(const char *oldpath, const char *newpath) {
|
||||||
|
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
|
||||||
|
return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
|
||||||
|
(uptr)newpath);
|
||||||
|
#else
|
||||||
|
return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
uptr internal_sched_yield() {
|
uptr internal_sched_yield() {
|
||||||
return internal_syscall(SYSCALL(sched_yield));
|
return internal_syscall(SYSCALL(sched_yield));
|
||||||
}
|
}
|
||||||
|
@ -297,9 +313,20 @@ u64 NanoTime() {
|
||||||
return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
|
return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Like getenv, but reads env directly from /proc and does not use libc.
|
// Like getenv, but reads env directly from /proc (on Linux) or parses the
|
||||||
// This function should be called first inside __asan_init.
|
// 'environ' array (on FreeBSD) and does not use libc. This function should be
|
||||||
|
// called first inside __asan_init.
|
||||||
const char *GetEnv(const char *name) {
|
const char *GetEnv(const char *name) {
|
||||||
|
#if SANITIZER_FREEBSD
|
||||||
|
if (::environ != 0) {
|
||||||
|
uptr NameLen = internal_strlen(name);
|
||||||
|
for (char **Env = ::environ; *Env != 0; Env++) {
|
||||||
|
if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
|
||||||
|
return (*Env) + NameLen + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; // Not found.
|
||||||
|
#elif SANITIZER_LINUX
|
||||||
static char *environ;
|
static char *environ;
|
||||||
static uptr len;
|
static uptr len;
|
||||||
static bool inited;
|
static bool inited;
|
||||||
|
@ -323,6 +350,9 @@ const char *GetEnv(const char *name) {
|
||||||
p = endp + 1;
|
p = endp + 1;
|
||||||
}
|
}
|
||||||
return 0; // Not found.
|
return 0; // Not found.
|
||||||
|
#else
|
||||||
|
#error "Unsupported platform"
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -388,20 +418,6 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
||||||
}
|
}
|
||||||
#endif // SANITIZER_GO
|
#endif // SANITIZER_GO
|
||||||
|
|
||||||
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
|
|
||||||
// Some kinds of sandboxes may forbid filesystem access, so we won't be able
|
|
||||||
// to read the file mappings from /proc/self/maps. Luckily, neither the
|
|
||||||
// process will be able to load additional libraries, so it's fine to use the
|
|
||||||
// cached mappings.
|
|
||||||
MemoryMappingLayout::CacheMemoryMappings();
|
|
||||||
// Same for /proc/self/exe in the symbolizer.
|
|
||||||
#if !SANITIZER_GO
|
|
||||||
if (Symbolizer *sym = Symbolizer::GetOrNull())
|
|
||||||
sym->PrepareForSandboxing();
|
|
||||||
CovPrepareForSandboxing(args);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MutexState {
|
enum MutexState {
|
||||||
MtxUnlocked = 0,
|
MtxUnlocked = 0,
|
||||||
MtxLocked = 1,
|
MtxLocked = 1,
|
||||||
|
@ -506,7 +522,11 @@ uptr internal_sigaltstack(const struct sigaltstack *ss,
|
||||||
}
|
}
|
||||||
|
|
||||||
int internal_fork() {
|
int internal_fork() {
|
||||||
|
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
|
||||||
|
return internal_syscall(SYSCALL(clone), SIGCHLD, 0);
|
||||||
|
#else
|
||||||
return internal_syscall(SYSCALL(fork));
|
return internal_syscall(SYSCALL(fork));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SANITIZER_LINUX
|
#if SANITIZER_LINUX
|
||||||
|
@ -660,24 +680,32 @@ static char proc_self_exe_cache_str[kMaxPathLength];
|
||||||
static uptr proc_self_exe_cache_len = 0;
|
static uptr proc_self_exe_cache_len = 0;
|
||||||
|
|
||||||
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
|
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
|
||||||
|
if (proc_self_exe_cache_len > 0) {
|
||||||
|
// If available, use the cached module name.
|
||||||
|
uptr module_name_len =
|
||||||
|
internal_snprintf(buf, buf_len, "%s", proc_self_exe_cache_str);
|
||||||
|
CHECK_LT(module_name_len, buf_len);
|
||||||
|
return module_name_len;
|
||||||
|
}
|
||||||
|
#if SANITIZER_FREEBSD
|
||||||
|
const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
|
||||||
|
size_t Size = buf_len;
|
||||||
|
bool IsErr = (sysctl(Mib, 4, buf, &Size, NULL, 0) != 0);
|
||||||
|
int readlink_error = IsErr ? errno : 0;
|
||||||
|
uptr module_name_len = Size;
|
||||||
|
#else
|
||||||
uptr module_name_len = internal_readlink(
|
uptr module_name_len = internal_readlink(
|
||||||
"/proc/self/exe", buf, buf_len);
|
"/proc/self/exe", buf, buf_len);
|
||||||
int readlink_error;
|
int readlink_error;
|
||||||
if (internal_iserror(module_name_len, &readlink_error)) {
|
bool IsErr = internal_iserror(module_name_len, &readlink_error);
|
||||||
if (proc_self_exe_cache_len) {
|
#endif
|
||||||
// If available, use the cached module name.
|
if (IsErr) {
|
||||||
CHECK_LE(proc_self_exe_cache_len, buf_len);
|
// We can't read /proc/self/exe for some reason, assume the name of the
|
||||||
internal_strncpy(buf, proc_self_exe_cache_str, buf_len);
|
// binary is unknown.
|
||||||
module_name_len = internal_strlen(proc_self_exe_cache_str);
|
Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, "
|
||||||
} else {
|
"some stack frames may not be symbolized\n", readlink_error);
|
||||||
// We can't read /proc/self/exe for some reason, assume the name of the
|
module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe");
|
||||||
// binary is unknown.
|
|
||||||
Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, "
|
|
||||||
"some stack frames may not be symbolized\n", readlink_error);
|
|
||||||
module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe");
|
|
||||||
}
|
|
||||||
CHECK_LT(module_name_len, buf_len);
|
CHECK_LT(module_name_len, buf_len);
|
||||||
buf[module_name_len] = '\0';
|
|
||||||
}
|
}
|
||||||
return module_name_len;
|
return module_name_len;
|
||||||
}
|
}
|
||||||
|
@ -806,11 +834,19 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||||||
#endif // defined(__x86_64__) && SANITIZER_LINUX
|
#endif // defined(__x86_64__) && SANITIZER_LINUX
|
||||||
|
|
||||||
#if SANITIZER_ANDROID
|
#if SANITIZER_ANDROID
|
||||||
|
static atomic_uint8_t android_log_initialized;
|
||||||
|
|
||||||
|
void AndroidLogInit() {
|
||||||
|
atomic_store(&android_log_initialized, 1, memory_order_release);
|
||||||
|
}
|
||||||
// This thing is not, strictly speaking, async signal safe, but it does not seem
|
// This thing is not, strictly speaking, async signal safe, but it does not seem
|
||||||
// to cause any issues. Alternative is writing to log devices directly, but
|
// to cause any issues. Alternative is writing to log devices directly, but
|
||||||
// their location and message format might change in the future, so we'd really
|
// their location and message format might change in the future, so we'd really
|
||||||
// like to avoid that.
|
// like to avoid that.
|
||||||
void AndroidLogWrite(const char *buffer) {
|
void AndroidLogWrite(const char *buffer) {
|
||||||
|
if (!atomic_load(&android_log_initialized, memory_order_acquire))
|
||||||
|
return;
|
||||||
|
|
||||||
char *copy = internal_strdup(buffer);
|
char *copy = internal_strdup(buffer);
|
||||||
char *p = copy;
|
char *p = copy;
|
||||||
char *q;
|
char *q;
|
||||||
|
|
|
@ -15,23 +15,25 @@
|
||||||
|
|
||||||
#include "sanitizer_common.h"
|
#include "sanitizer_common.h"
|
||||||
#include "sanitizer_flags.h"
|
#include "sanitizer_flags.h"
|
||||||
|
#include "sanitizer_freebsd.h"
|
||||||
#include "sanitizer_linux.h"
|
#include "sanitizer_linux.h"
|
||||||
#include "sanitizer_placement_new.h"
|
#include "sanitizer_placement_new.h"
|
||||||
#include "sanitizer_procmaps.h"
|
#include "sanitizer_procmaps.h"
|
||||||
#include "sanitizer_stacktrace.h"
|
#include "sanitizer_stacktrace.h"
|
||||||
#include "sanitizer_atomic.h"
|
#include "sanitizer_atomic.h"
|
||||||
|
#include "sanitizer_symbolizer.h"
|
||||||
|
|
||||||
|
#if SANITIZER_ANDROID || SANITIZER_FREEBSD
|
||||||
|
#include <dlfcn.h> // for dlsym()
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#if SANITIZER_FREEBSD
|
|
||||||
#define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
|
|
||||||
#endif
|
|
||||||
#include <unwind.h>
|
|
||||||
|
|
||||||
#if SANITIZER_FREEBSD
|
#if SANITIZER_FREEBSD
|
||||||
#include <pthread_np.h>
|
#include <pthread_np.h>
|
||||||
|
#include <osreldate.h>
|
||||||
#define pthread_getattr_np pthread_attr_get_np
|
#define pthread_getattr_np pthread_attr_get_np
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -147,127 +149,6 @@ bool SanitizerGetThreadName(char *name, int max_len) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------- SlowUnwindStack -----------------------------------
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uptr absolute_pc;
|
|
||||||
uptr stack_top;
|
|
||||||
uptr stack_size;
|
|
||||||
} backtrace_frame_t;
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
typedef void *(*acquire_my_map_info_list_func)();
|
|
||||||
typedef void (*release_my_map_info_list_func)(void *map);
|
|
||||||
typedef sptr (*unwind_backtrace_signal_arch_func)(
|
|
||||||
void *siginfo, void *sigcontext, void *map_info_list,
|
|
||||||
backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
|
|
||||||
acquire_my_map_info_list_func acquire_my_map_info_list;
|
|
||||||
release_my_map_info_list_func release_my_map_info_list;
|
|
||||||
unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
|
|
||||||
} // extern "C"
|
|
||||||
|
|
||||||
#if SANITIZER_ANDROID
|
|
||||||
void SanitizerInitializeUnwinder() {
|
|
||||||
void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
|
|
||||||
if (!p) {
|
|
||||||
VReport(1,
|
|
||||||
"Failed to open libcorkscrew.so. You may see broken stack traces "
|
|
||||||
"in SEGV reports.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
acquire_my_map_info_list =
|
|
||||||
(acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
|
|
||||||
release_my_map_info_list =
|
|
||||||
(release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
|
|
||||||
unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
|
|
||||||
p, "unwind_backtrace_signal_arch");
|
|
||||||
if (!acquire_my_map_info_list || !release_my_map_info_list ||
|
|
||||||
!unwind_backtrace_signal_arch) {
|
|
||||||
VReport(1,
|
|
||||||
"Failed to find one of the required symbols in libcorkscrew.so. "
|
|
||||||
"You may see broken stack traces in SEGV reports.");
|
|
||||||
acquire_my_map_info_list = NULL;
|
|
||||||
unwind_backtrace_signal_arch = NULL;
|
|
||||||
release_my_map_info_list = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __arm__
|
|
||||||
#define UNWIND_STOP _URC_END_OF_STACK
|
|
||||||
#define UNWIND_CONTINUE _URC_NO_REASON
|
|
||||||
#else
|
|
||||||
#define UNWIND_STOP _URC_NORMAL_STOP
|
|
||||||
#define UNWIND_CONTINUE _URC_NO_REASON
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
|
|
||||||
#ifdef __arm__
|
|
||||||
uptr val;
|
|
||||||
_Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
|
|
||||||
15 /* r15 = PC */, _UVRSD_UINT32, &val);
|
|
||||||
CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
|
|
||||||
// Clear the Thumb bit.
|
|
||||||
return val & ~(uptr)1;
|
|
||||||
#else
|
|
||||||
return _Unwind_GetIP(ctx);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UnwindTraceArg {
|
|
||||||
StackTrace *stack;
|
|
||||||
uptr max_depth;
|
|
||||||
};
|
|
||||||
|
|
||||||
_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
|
|
||||||
UnwindTraceArg *arg = (UnwindTraceArg*)param;
|
|
||||||
CHECK_LT(arg->stack->size, arg->max_depth);
|
|
||||||
uptr pc = Unwind_GetIP(ctx);
|
|
||||||
arg->stack->trace[arg->stack->size++] = pc;
|
|
||||||
if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
|
|
||||||
return UNWIND_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
|
|
||||||
CHECK_GE(max_depth, 2);
|
|
||||||
size = 0;
|
|
||||||
UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
|
|
||||||
_Unwind_Backtrace(Unwind_Trace, &arg);
|
|
||||||
// We need to pop a few frames so that pc is on top.
|
|
||||||
uptr to_pop = LocatePcInTrace(pc);
|
|
||||||
// trace[0] belongs to the current function so we always pop it.
|
|
||||||
if (to_pop == 0)
|
|
||||||
to_pop = 1;
|
|
||||||
PopStackFrames(to_pop);
|
|
||||||
trace[0] = pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
|
|
||||||
uptr max_depth) {
|
|
||||||
CHECK_GE(max_depth, 2);
|
|
||||||
if (!unwind_backtrace_signal_arch) {
|
|
||||||
SlowUnwindStack(pc, max_depth);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *map = acquire_my_map_info_list();
|
|
||||||
CHECK(map);
|
|
||||||
InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax);
|
|
||||||
// siginfo argument appears to be unused.
|
|
||||||
sptr res = unwind_backtrace_signal_arch(/* siginfo */ NULL, context, map,
|
|
||||||
frames.data(),
|
|
||||||
/* ignore_depth */ 0, max_depth);
|
|
||||||
release_my_map_info_list(map);
|
|
||||||
if (res < 0) return;
|
|
||||||
CHECK_LE((uptr)res, kStackTraceMax);
|
|
||||||
|
|
||||||
size = 0;
|
|
||||||
// +2 compensate for libcorkscrew unwinder returning addresses of call
|
|
||||||
// instructions instead of raw return addresses.
|
|
||||||
for (sptr i = 0; i < res; ++i)
|
|
||||||
trace[size++] = frames[i].absolute_pc + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !SANITIZER_FREEBSD
|
#if !SANITIZER_FREEBSD
|
||||||
static uptr g_tls_size;
|
static uptr g_tls_size;
|
||||||
#endif
|
#endif
|
||||||
|
@ -299,11 +180,11 @@ void InitTlsSize() {
|
||||||
static atomic_uintptr_t kThreadDescriptorSize;
|
static atomic_uintptr_t kThreadDescriptorSize;
|
||||||
|
|
||||||
uptr ThreadDescriptorSize() {
|
uptr ThreadDescriptorSize() {
|
||||||
char buf[64];
|
|
||||||
uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed);
|
uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed);
|
||||||
if (val)
|
if (val)
|
||||||
return val;
|
return val;
|
||||||
#ifdef _CS_GNU_LIBC_VERSION
|
#ifdef _CS_GNU_LIBC_VERSION
|
||||||
|
char buf[64];
|
||||||
uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
|
uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
|
||||||
if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) {
|
if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) {
|
||||||
char *end;
|
char *end;
|
||||||
|
@ -468,6 +349,10 @@ uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
|
||||||
#else // SANITIZER_ANDROID
|
#else // SANITIZER_ANDROID
|
||||||
# if !SANITIZER_FREEBSD
|
# if !SANITIZER_FREEBSD
|
||||||
typedef ElfW(Phdr) Elf_Phdr;
|
typedef ElfW(Phdr) Elf_Phdr;
|
||||||
|
# elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
|
||||||
|
# define Elf_Phdr XElf32_Phdr
|
||||||
|
# define dl_phdr_info xdl_phdr_info
|
||||||
|
# define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b))
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
struct DlIteratePhdrData {
|
struct DlIteratePhdrData {
|
||||||
|
@ -504,7 +389,8 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
|
||||||
if (phdr->p_type == PT_LOAD) {
|
if (phdr->p_type == PT_LOAD) {
|
||||||
uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
|
uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
|
||||||
uptr cur_end = cur_beg + phdr->p_memsz;
|
uptr cur_end = cur_beg + phdr->p_memsz;
|
||||||
cur_module->addAddressRange(cur_beg, cur_end);
|
bool executable = phdr->p_flags & PF_X;
|
||||||
|
cur_module->addAddressRange(cur_beg, cur_end, executable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -527,6 +413,19 @@ void SetIndirectCallWrapper(uptr wrapper) {
|
||||||
indirect_call_wrapper = wrapper;
|
indirect_call_wrapper = wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
|
||||||
|
// Some kinds of sandboxes may forbid filesystem access, so we won't be able
|
||||||
|
// to read the file mappings from /proc/self/maps. Luckily, neither the
|
||||||
|
// process will be able to load additional libraries, so it's fine to use the
|
||||||
|
// cached mappings.
|
||||||
|
MemoryMappingLayout::CacheMemoryMappings();
|
||||||
|
// Same for /proc/self/exe in the symbolizer.
|
||||||
|
#if !SANITIZER_GO
|
||||||
|
Symbolizer::GetOrInit()->PrepareForSandboxing();
|
||||||
|
CovPrepareForSandboxing(args);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||||
|
|
|
@ -129,6 +129,14 @@ int internal_fork() {
|
||||||
return fork();
|
return fork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uptr internal_rename(const char *oldpath, const char *newpath) {
|
||||||
|
return rename(oldpath, newpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr internal_ftruncate(fd_t fd, uptr size) {
|
||||||
|
return ftruncate(fd, size);
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------- sanitizer_common.h
|
// ----------------- sanitizer_common.h
|
||||||
bool FileExists(const char *filename) {
|
bool FileExists(const char *filename) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
//===-- sanitizer_persistent_allocator.cc -----------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||||
|
// run-time libraries.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#include "sanitizer_persistent_allocator.h"
|
||||||
|
|
||||||
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
PersistentAllocator thePersistentAllocator;
|
||||||
|
|
||||||
|
} // namespace __sanitizer
|
|
@ -0,0 +1,69 @@
|
||||||
|
//===-- sanitizer_persistent_allocator.h ------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// A fast memory allocator that does not support free() nor realloc().
|
||||||
|
// All allocations are forever.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#ifndef SANITIZER_PERSISTENT_ALLOCATOR_H
|
||||||
|
#define SANITIZER_PERSISTENT_ALLOCATOR_H
|
||||||
|
|
||||||
|
#include "sanitizer_internal_defs.h"
|
||||||
|
#include "sanitizer_mutex.h"
|
||||||
|
#include "sanitizer_atomic.h"
|
||||||
|
#include "sanitizer_common.h"
|
||||||
|
|
||||||
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
class PersistentAllocator {
|
||||||
|
public:
|
||||||
|
void *alloc(uptr size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void *tryAlloc(uptr size);
|
||||||
|
StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator.
|
||||||
|
atomic_uintptr_t region_pos; // Region allocator for Node's.
|
||||||
|
atomic_uintptr_t region_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void *PersistentAllocator::tryAlloc(uptr size) {
|
||||||
|
// Optimisic lock-free allocation, essentially try to bump the region ptr.
|
||||||
|
for (;;) {
|
||||||
|
uptr cmp = atomic_load(®ion_pos, memory_order_acquire);
|
||||||
|
uptr end = atomic_load(®ion_end, memory_order_acquire);
|
||||||
|
if (cmp == 0 || cmp + size > end) return 0;
|
||||||
|
if (atomic_compare_exchange_weak(®ion_pos, &cmp, cmp + size,
|
||||||
|
memory_order_acquire))
|
||||||
|
return (void *)cmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void *PersistentAllocator::alloc(uptr size) {
|
||||||
|
// First, try to allocate optimisitically.
|
||||||
|
void *s = tryAlloc(size);
|
||||||
|
if (s) return s;
|
||||||
|
// If failed, lock, retry and alloc new superblock.
|
||||||
|
SpinMutexLock l(&mtx);
|
||||||
|
for (;;) {
|
||||||
|
s = tryAlloc(size);
|
||||||
|
if (s) return s;
|
||||||
|
atomic_store(®ion_pos, 0, memory_order_relaxed);
|
||||||
|
uptr allocsz = 64 * 1024;
|
||||||
|
if (allocsz < size) allocsz = size;
|
||||||
|
uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
|
||||||
|
atomic_store(®ion_end, mem + allocsz, memory_order_release);
|
||||||
|
atomic_store(®ion_pos, mem, memory_order_release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern PersistentAllocator thePersistentAllocator;
|
||||||
|
inline void *PersistentAlloc(uptr sz) {
|
||||||
|
return thePersistentAllocator.alloc(sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __sanitizer
|
||||||
|
|
||||||
|
#endif // SANITIZER_PERSISTENT_ALLOCATOR_H
|
|
@ -27,6 +27,12 @@
|
||||||
# define SI_LINUX_NOT_ANDROID 0
|
# define SI_LINUX_NOT_ANDROID 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if SANITIZER_FREEBSD
|
||||||
|
# define SI_FREEBSD 1
|
||||||
|
#else
|
||||||
|
# define SI_FREEBSD 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if SANITIZER_LINUX
|
#if SANITIZER_LINUX
|
||||||
# define SI_LINUX 1
|
# define SI_LINUX 1
|
||||||
#else
|
#else
|
||||||
|
@ -73,11 +79,11 @@
|
||||||
#define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS
|
||||||
|
|
||||||
#define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX
|
#define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX_NOT_ANDROID
|
||||||
|
|
||||||
#ifndef SANITIZER_INTERCEPT_PRINTF
|
#ifndef SANITIZER_INTERCEPT_PRINTF
|
||||||
# define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS
|
# define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS
|
||||||
# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX
|
# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SANITIZER_INTERCEPT_FREXP 1
|
#define SANITIZER_INTERCEPT_FREXP 1
|
||||||
|
@ -86,10 +92,10 @@
|
||||||
#define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \
|
#define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \
|
||||||
SI_MAC || SI_LINUX_NOT_ANDROID
|
SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_GETPWENT SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_GETPWENT SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_GETPWENT_R SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_GETPWENT_R SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_SETPWENT SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_SETPWENT SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_LINUX
|
#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_LINUX
|
||||||
#define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS
|
||||||
|
@ -102,9 +108,12 @@
|
||||||
#define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX
|
#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX
|
||||||
|
#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_LINUX_NOT_ANDROID
|
||||||
|
#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_LINUX_NOT_ANDROID
|
||||||
|
#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX
|
#define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS
|
||||||
|
@ -117,13 +126,13 @@
|
||||||
(defined(__i386) || defined (__x86_64)) // NOLINT
|
(defined(__i386) || defined (__x86_64)) // NOLINT
|
||||||
#define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX
|
#define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX
|
#define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
|
@ -140,19 +149,21 @@
|
||||||
#define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_SIGSETOPS SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_SIGSETOPS \
|
||||||
|
SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
|
#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
|
||||||
#define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_STATFS SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_STATFS SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_STATFS64 \
|
#define SANITIZER_INTERCEPT_STATFS64 \
|
||||||
(SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
|
(SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_ETHER SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_ETHER_NTOA_ATON SI_NOT_WINDOWS
|
||||||
|
#define SANITIZER_INTERCEPT_ETHER_HOST SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_SHMCTL \
|
#define SANITIZER_INTERCEPT_SHMCTL \
|
||||||
(SI_LINUX_NOT_ANDROID && SANITIZER_WORDSIZE == 64)
|
(SI_LINUX_NOT_ANDROID && SANITIZER_WORDSIZE == 64)
|
||||||
|
@ -161,6 +172,19 @@
|
||||||
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
|
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
|
||||||
SI_MAC || SI_LINUX_NOT_ANDROID
|
SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED SI_NOT_WINDOWS
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE SI_NOT_WINDOWS
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL \
|
||||||
|
SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING \
|
||||||
|
SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST SI_LINUX_NOT_ANDROID
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP SI_LINUX_NOT_ANDROID
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED SI_NOT_WINDOWS
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP SI_LINUX_NOT_ANDROID
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED SI_NOT_WINDOWS
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK SI_LINUX_NOT_ANDROID
|
||||||
|
#define SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS
|
||||||
|
@ -168,6 +192,7 @@
|
||||||
#define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX
|
#define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX
|
||||||
|
#define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID
|
||||||
|
@ -176,7 +201,7 @@
|
||||||
// FIXME: getline seems to be available on OSX 10.7
|
// FIXME: getline seems to be available on OSX 10.7
|
||||||
#define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID
|
||||||
|
|
||||||
#define SANITIZER_INTERCEPT__EXIT SI_LINUX
|
#define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD
|
||||||
|
|
||||||
#define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID
|
||||||
|
@ -201,5 +226,10 @@
|
||||||
#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID
|
#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID
|
||||||
#define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS
|
||||||
#define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS
|
#define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS
|
||||||
|
#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE SI_LINUX_NOT_ANDROID || SI_MAC
|
||||||
|
#define SANITIZER_INTERCEPT_GETPASS SI_LINUX_NOT_ANDROID || SI_MAC
|
||||||
|
#define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID
|
||||||
|
|
||||||
|
#define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS
|
||||||
|
|
||||||
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
|
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
// are not defined anywhere in userspace headers. Fake them. This seems to work
|
// are not defined anywhere in userspace headers. Fake them. This seems to work
|
||||||
// fine with newer headers, too.
|
// fine with newer headers, too.
|
||||||
#include <asm/posix_types.h>
|
#include <asm/posix_types.h>
|
||||||
#if defined(__x86_64__)
|
#if defined(__x86_64__) || defined(__mips__)
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#else
|
#else
|
||||||
#define ino_t __kernel_ino_t
|
#define ino_t __kernel_ino_t
|
||||||
|
@ -48,21 +48,19 @@
|
||||||
|
|
||||||
#include <linux/aio_abi.h>
|
#include <linux/aio_abi.h>
|
||||||
|
|
||||||
#if SANITIZER_ANDROID
|
|
||||||
#include <asm/statfs.h>
|
|
||||||
#else
|
|
||||||
#include <sys/statfs.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !SANITIZER_ANDROID
|
#if !SANITIZER_ANDROID
|
||||||
|
#include <sys/statfs.h>
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace __sanitizer {
|
namespace __sanitizer {
|
||||||
|
#if !SANITIZER_ANDROID
|
||||||
unsigned struct_statfs64_sz = sizeof(struct statfs64);
|
unsigned struct_statfs64_sz = sizeof(struct statfs64);
|
||||||
|
#endif
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)
|
#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)\
|
||||||
|
&& !defined(__mips__)
|
||||||
COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
|
COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/timeb.h>
|
|
||||||
#include <sys/times.h>
|
#include <sys/times.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
@ -43,6 +42,7 @@
|
||||||
|
|
||||||
#if !SANITIZER_ANDROID
|
#if !SANITIZER_ANDROID
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
|
#include <sys/timeb.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SANITIZER_LINUX
|
#if SANITIZER_LINUX
|
||||||
|
@ -189,13 +189,14 @@ namespace __sanitizer {
|
||||||
unsigned struct_tms_sz = sizeof(struct tms);
|
unsigned struct_tms_sz = sizeof(struct tms);
|
||||||
unsigned struct_sigevent_sz = sizeof(struct sigevent);
|
unsigned struct_sigevent_sz = sizeof(struct sigevent);
|
||||||
unsigned struct_sched_param_sz = sizeof(struct sched_param);
|
unsigned struct_sched_param_sz = sizeof(struct sched_param);
|
||||||
unsigned struct_statfs_sz = sizeof(struct statfs);
|
|
||||||
|
|
||||||
#if SANITIZER_MAC && !SANITIZER_IOS
|
#if SANITIZER_MAC && !SANITIZER_IOS
|
||||||
unsigned struct_statfs64_sz = sizeof(struct statfs64);
|
unsigned struct_statfs64_sz = sizeof(struct statfs64);
|
||||||
#endif // SANITIZER_MAC && !SANITIZER_IOS
|
#endif // SANITIZER_MAC && !SANITIZER_IOS
|
||||||
|
|
||||||
#if !SANITIZER_ANDROID
|
#if !SANITIZER_ANDROID
|
||||||
|
unsigned struct_statfs_sz = sizeof(struct statfs);
|
||||||
unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
|
unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
|
||||||
unsigned ucontext_t_sz = sizeof(ucontext_t);
|
unsigned ucontext_t_sz = sizeof(ucontext_t);
|
||||||
#endif // !SANITIZER_ANDROID
|
#endif // !SANITIZER_ANDROID
|
||||||
|
@ -287,6 +288,7 @@ namespace __sanitizer {
|
||||||
int ptrace_setfpregs = PTRACE_SETFPREGS;
|
int ptrace_setfpregs = PTRACE_SETFPREGS;
|
||||||
int ptrace_getfpxregs = PTRACE_GETFPXREGS;
|
int ptrace_getfpxregs = PTRACE_GETFPXREGS;
|
||||||
int ptrace_setfpxregs = PTRACE_SETFPXREGS;
|
int ptrace_setfpxregs = PTRACE_SETFPXREGS;
|
||||||
|
int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
|
||||||
#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
|
#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
|
||||||
(defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
|
(defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
|
||||||
int ptrace_getsiginfo = PTRACE_GETSIGINFO;
|
int ptrace_getsiginfo = PTRACE_GETSIGINFO;
|
||||||
|
@ -396,7 +398,7 @@ namespace __sanitizer {
|
||||||
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
|
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
unsigned IOCTL_NOT_PRESENT = 0;
|
const unsigned IOCTL_NOT_PRESENT = 0;
|
||||||
|
|
||||||
unsigned IOCTL_FIOASYNC = FIOASYNC;
|
unsigned IOCTL_FIOASYNC = FIOASYNC;
|
||||||
unsigned IOCTL_FIOCLEX = FIOCLEX;
|
unsigned IOCTL_FIOCLEX = FIOCLEX;
|
||||||
|
@ -1056,6 +1058,10 @@ CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
|
||||||
|
|
||||||
CHECK_TYPE_SIZE(clock_t);
|
CHECK_TYPE_SIZE(clock_t);
|
||||||
|
|
||||||
|
#if SANITIZER_LINUX
|
||||||
|
CHECK_TYPE_SIZE(clockid_t);
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !SANITIZER_ANDROID
|
#if !SANITIZER_ANDROID
|
||||||
CHECK_TYPE_SIZE(ifaddrs);
|
CHECK_TYPE_SIZE(ifaddrs);
|
||||||
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
|
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
|
||||||
|
@ -1086,11 +1092,13 @@ CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
|
||||||
COMPILER_CHECK(sizeof(__sanitizer_mallinfo) == sizeof(struct mallinfo));
|
COMPILER_CHECK(sizeof(__sanitizer_mallinfo) == sizeof(struct mallinfo));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !SANITIZER_ANDROID
|
||||||
CHECK_TYPE_SIZE(timeb);
|
CHECK_TYPE_SIZE(timeb);
|
||||||
CHECK_SIZE_AND_OFFSET(timeb, time);
|
CHECK_SIZE_AND_OFFSET(timeb, time);
|
||||||
CHECK_SIZE_AND_OFFSET(timeb, millitm);
|
CHECK_SIZE_AND_OFFSET(timeb, millitm);
|
||||||
CHECK_SIZE_AND_OFFSET(timeb, timezone);
|
CHECK_SIZE_AND_OFFSET(timeb, timezone);
|
||||||
CHECK_SIZE_AND_OFFSET(timeb, dstflag);
|
CHECK_SIZE_AND_OFFSET(timeb, dstflag);
|
||||||
|
#endif
|
||||||
|
|
||||||
CHECK_TYPE_SIZE(passwd);
|
CHECK_TYPE_SIZE(passwd);
|
||||||
CHECK_SIZE_AND_OFFSET(passwd, pw_name);
|
CHECK_SIZE_AND_OFFSET(passwd, pw_name);
|
||||||
|
|
|
@ -37,11 +37,11 @@ namespace __sanitizer {
|
||||||
extern unsigned struct_itimerspec_sz;
|
extern unsigned struct_itimerspec_sz;
|
||||||
extern unsigned struct_sigevent_sz;
|
extern unsigned struct_sigevent_sz;
|
||||||
extern unsigned struct_sched_param_sz;
|
extern unsigned struct_sched_param_sz;
|
||||||
extern unsigned struct_statfs_sz;
|
|
||||||
extern unsigned struct_statfs64_sz;
|
extern unsigned struct_statfs64_sz;
|
||||||
extern unsigned struct_sockaddr_sz;
|
|
||||||
|
|
||||||
#if !SANITIZER_ANDROID
|
#if !SANITIZER_ANDROID
|
||||||
|
extern unsigned struct_statfs_sz;
|
||||||
|
extern unsigned struct_sockaddr_sz;
|
||||||
extern unsigned ucontext_t_sz;
|
extern unsigned ucontext_t_sz;
|
||||||
#endif // !SANITIZER_ANDROID
|
#endif // !SANITIZER_ANDROID
|
||||||
|
|
||||||
|
@ -65,6 +65,13 @@ namespace __sanitizer {
|
||||||
#elif defined(__powerpc64__)
|
#elif defined(__powerpc64__)
|
||||||
const unsigned struct_kernel_stat_sz = 144;
|
const unsigned struct_kernel_stat_sz = 144;
|
||||||
const unsigned struct_kernel_stat64_sz = 104;
|
const unsigned struct_kernel_stat64_sz = 104;
|
||||||
|
#elif defined(__mips__)
|
||||||
|
#if SANITIZER_WORDSIZE == 64
|
||||||
|
const unsigned struct_kernel_stat_sz = 216;
|
||||||
|
#else
|
||||||
|
const unsigned struct_kernel_stat_sz = 144;
|
||||||
|
#endif
|
||||||
|
const unsigned struct_kernel_stat64_sz = 104;
|
||||||
#endif
|
#endif
|
||||||
struct __sanitizer_perf_event_attr {
|
struct __sanitizer_perf_event_attr {
|
||||||
unsigned type;
|
unsigned type;
|
||||||
|
@ -160,6 +167,12 @@ namespace __sanitizer {
|
||||||
unsigned __seq;
|
unsigned __seq;
|
||||||
u64 __unused1;
|
u64 __unused1;
|
||||||
u64 __unused2;
|
u64 __unused2;
|
||||||
|
#elif defined(__mips__)
|
||||||
|
unsigned int mode;
|
||||||
|
unsigned short __seq;
|
||||||
|
unsigned short __pad1;
|
||||||
|
unsigned long __unused1;
|
||||||
|
unsigned long __unused2;
|
||||||
#else
|
#else
|
||||||
unsigned short mode;
|
unsigned short mode;
|
||||||
unsigned short __pad1;
|
unsigned short __pad1;
|
||||||
|
@ -188,15 +201,15 @@ namespace __sanitizer {
|
||||||
u64 shm_ctime;
|
u64 shm_ctime;
|
||||||
#else
|
#else
|
||||||
uptr shm_atime;
|
uptr shm_atime;
|
||||||
#ifndef _LP64
|
#if !defined(_LP64) && !defined(__mips__)
|
||||||
uptr __unused1;
|
uptr __unused1;
|
||||||
#endif
|
#endif
|
||||||
uptr shm_dtime;
|
uptr shm_dtime;
|
||||||
#ifndef _LP64
|
#if !defined(_LP64) && !defined(__mips__)
|
||||||
uptr __unused2;
|
uptr __unused2;
|
||||||
#endif
|
#endif
|
||||||
uptr shm_ctime;
|
uptr shm_ctime;
|
||||||
#ifndef _LP64
|
#if !defined(_LP64) && !defined(__mips__)
|
||||||
uptr __unused3;
|
uptr __unused3;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -438,8 +451,13 @@ namespace __sanitizer {
|
||||||
typedef long __sanitizer_clock_t;
|
typedef long __sanitizer_clock_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if SANITIZER_LINUX
|
||||||
|
typedef int __sanitizer_clockid_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if SANITIZER_LINUX || SANITIZER_FREEBSD
|
#if SANITIZER_LINUX || SANITIZER_FREEBSD
|
||||||
#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)
|
#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)\
|
||||||
|
|| defined(__mips__)
|
||||||
typedef unsigned __sanitizer___kernel_uid_t;
|
typedef unsigned __sanitizer___kernel_uid_t;
|
||||||
typedef unsigned __sanitizer___kernel_gid_t;
|
typedef unsigned __sanitizer___kernel_gid_t;
|
||||||
#else
|
#else
|
||||||
|
@ -452,7 +470,7 @@ namespace __sanitizer {
|
||||||
typedef long __sanitizer___kernel_off_t;
|
typedef long __sanitizer___kernel_off_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__powerpc__) || defined(__aarch64__)
|
#if defined(__powerpc__) || defined(__aarch64__) || defined(__mips__)
|
||||||
typedef unsigned int __sanitizer___kernel_old_uid_t;
|
typedef unsigned int __sanitizer___kernel_old_uid_t;
|
||||||
typedef unsigned int __sanitizer___kernel_old_gid_t;
|
typedef unsigned int __sanitizer___kernel_old_gid_t;
|
||||||
#else
|
#else
|
||||||
|
@ -492,6 +510,9 @@ namespace __sanitizer {
|
||||||
|
|
||||||
// Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
|
// Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
|
||||||
struct __sanitizer_sigaction {
|
struct __sanitizer_sigaction {
|
||||||
|
#if defined(__mips__) && !SANITIZER_FREEBSD
|
||||||
|
unsigned int sa_flags;
|
||||||
|
#endif
|
||||||
union {
|
union {
|
||||||
void (*sigaction)(int sig, void *siginfo, void *uctx);
|
void (*sigaction)(int sig, void *siginfo, void *uctx);
|
||||||
void (*handler)(int sig);
|
void (*handler)(int sig);
|
||||||
|
@ -501,10 +522,15 @@ namespace __sanitizer {
|
||||||
__sanitizer_sigset_t sa_mask;
|
__sanitizer_sigset_t sa_mask;
|
||||||
#else
|
#else
|
||||||
__sanitizer_sigset_t sa_mask;
|
__sanitizer_sigset_t sa_mask;
|
||||||
|
#ifndef __mips__
|
||||||
int sa_flags;
|
int sa_flags;
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#if SANITIZER_LINUX
|
#if SANITIZER_LINUX
|
||||||
void (*sa_restorer)();
|
void (*sa_restorer)();
|
||||||
|
#endif
|
||||||
|
#if defined(__mips__) && (SANITIZER_WORDSIZE == 32)
|
||||||
|
int sa_resv[1];
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -676,6 +702,7 @@ namespace __sanitizer {
|
||||||
extern int ptrace_setsiginfo;
|
extern int ptrace_setsiginfo;
|
||||||
extern int ptrace_getregset;
|
extern int ptrace_getregset;
|
||||||
extern int ptrace_setregset;
|
extern int ptrace_setregset;
|
||||||
|
extern int ptrace_geteventmsg;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
|
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
|
||||||
|
@ -718,7 +745,7 @@ struct __sanitizer_obstack {
|
||||||
|
|
||||||
#define IOC_NRBITS 8
|
#define IOC_NRBITS 8
|
||||||
#define IOC_TYPEBITS 8
|
#define IOC_TYPEBITS 8
|
||||||
#if defined(__powerpc__) || defined(__powerpc64__)
|
#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__)
|
||||||
#define IOC_SIZEBITS 13
|
#define IOC_SIZEBITS 13
|
||||||
#define IOC_DIRBITS 3
|
#define IOC_DIRBITS 3
|
||||||
#define IOC_NONE 1U
|
#define IOC_NONE 1U
|
||||||
|
@ -832,7 +859,7 @@ struct __sanitizer_obstack {
|
||||||
|
|
||||||
// A special value to mark ioctls that are not present on the target platform,
|
// A special value to mark ioctls that are not present on the target platform,
|
||||||
// when it can not be determined without including any system headers.
|
// when it can not be determined without including any system headers.
|
||||||
extern unsigned IOCTL_NOT_PRESENT;
|
extern const unsigned IOCTL_NOT_PRESENT;
|
||||||
|
|
||||||
extern unsigned IOCTL_FIOASYNC;
|
extern unsigned IOCTL_FIOASYNC;
|
||||||
extern unsigned IOCTL_FIOCLEX;
|
extern unsigned IOCTL_FIOCLEX;
|
||||||
|
|
|
@ -204,6 +204,17 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) {
|
||||||
return internal_iserror(map) ? 0 : (void *)map;
|
return internal_iserror(map) ? 0 : (void *)map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) {
|
||||||
|
uptr flags = MAP_SHARED;
|
||||||
|
if (addr) flags |= MAP_FIXED;
|
||||||
|
uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
|
||||||
|
if (internal_iserror(p)) {
|
||||||
|
Printf("could not map writable file (%zd, %zu, %zu): %zd\n", fd, offset,
|
||||||
|
size, p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (void *)p;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
|
static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
|
||||||
uptr start2, uptr end2) {
|
uptr start2, uptr end2) {
|
||||||
|
|
|
@ -42,30 +42,49 @@ void FlushUnneededShadowMemory(uptr addr, uptr size) {
|
||||||
madvise((void*)addr, size, MADV_DONTNEED);
|
madvise((void*)addr, size, MADV_DONTNEED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisableCoreDumper() {
|
static rlim_t getlim(int res) {
|
||||||
struct rlimit nocore;
|
rlimit rlim;
|
||||||
nocore.rlim_cur = 0;
|
CHECK_EQ(0, getrlimit(res, &rlim));
|
||||||
nocore.rlim_max = 0;
|
return rlim.rlim_cur;
|
||||||
setrlimit(RLIMIT_CORE, &nocore);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StackSizeIsUnlimited() {
|
static void setlim(int res, rlim_t lim) {
|
||||||
struct rlimit rlim;
|
// The following magic is to prevent clang from replacing it with memset.
|
||||||
CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim));
|
volatile struct rlimit rlim;
|
||||||
return ((uptr)rlim.rlim_cur == (uptr)-1);
|
rlim.rlim_cur = lim;
|
||||||
}
|
rlim.rlim_max = lim;
|
||||||
|
if (setrlimit(res, (struct rlimit*)&rlim)) {
|
||||||
void SetStackSizeLimitInBytes(uptr limit) {
|
|
||||||
struct rlimit rlim;
|
|
||||||
rlim.rlim_cur = limit;
|
|
||||||
rlim.rlim_max = limit;
|
|
||||||
if (setrlimit(RLIMIT_STACK, &rlim)) {
|
|
||||||
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
|
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
|
||||||
Die();
|
Die();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisableCoreDumperIfNecessary() {
|
||||||
|
if (common_flags()->disable_coredump) {
|
||||||
|
setlim(RLIMIT_CORE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StackSizeIsUnlimited() {
|
||||||
|
rlim_t stack_size = getlim(RLIMIT_STACK);
|
||||||
|
return (stack_size == RLIM_INFINITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetStackSizeLimitInBytes(uptr limit) {
|
||||||
|
setlim(RLIMIT_STACK, (rlim_t)limit);
|
||||||
CHECK(!StackSizeIsUnlimited());
|
CHECK(!StackSizeIsUnlimited());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AddressSpaceIsUnlimited() {
|
||||||
|
rlim_t as_size = getlim(RLIMIT_AS);
|
||||||
|
return (as_size == RLIM_INFINITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAddressSpaceUnlimited() {
|
||||||
|
setlim(RLIMIT_AS, RLIM_INFINITY);
|
||||||
|
CHECK(AddressSpaceIsUnlimited());
|
||||||
|
}
|
||||||
|
|
||||||
void SleepForSeconds(int seconds) {
|
void SleepForSeconds(int seconds) {
|
||||||
sleep(seconds);
|
sleep(seconds);
|
||||||
}
|
}
|
||||||
|
@ -127,7 +146,9 @@ static void MaybeInstallSigaction(int signum,
|
||||||
struct sigaction sigact;
|
struct sigaction sigact;
|
||||||
internal_memset(&sigact, 0, sizeof(sigact));
|
internal_memset(&sigact, 0, sizeof(sigact));
|
||||||
sigact.sa_sigaction = (sa_sigaction_t)handler;
|
sigact.sa_sigaction = (sa_sigaction_t)handler;
|
||||||
sigact.sa_flags = SA_SIGINFO;
|
// Do not block the signal from being received in that signal's handler.
|
||||||
|
// Clients are responsible for handling this correctly.
|
||||||
|
sigact.sa_flags = SA_SIGINFO | SA_NODEFER;
|
||||||
if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
|
if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
|
||||||
CHECK_EQ(0, internal_sigaction(signum, &sigact, 0));
|
CHECK_EQ(0, internal_sigaction(signum, &sigact, 0));
|
||||||
VReport(1, "Installed the sigaction for signal %d\n", signum);
|
VReport(1, "Installed the sigaction for signal %d\n", signum);
|
||||||
|
@ -143,6 +164,28 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) {
|
||||||
}
|
}
|
||||||
#endif // SANITIZER_GO
|
#endif // SANITIZER_GO
|
||||||
|
|
||||||
|
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
|
||||||
|
uptr page_size = GetPageSizeCached();
|
||||||
|
// Checking too large memory ranges is slow.
|
||||||
|
CHECK_LT(size, page_size * 10);
|
||||||
|
int sock_pair[2];
|
||||||
|
if (pipe(sock_pair))
|
||||||
|
return false;
|
||||||
|
uptr bytes_written =
|
||||||
|
internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
|
||||||
|
int write_errno;
|
||||||
|
bool result;
|
||||||
|
if (internal_iserror(bytes_written, &write_errno)) {
|
||||||
|
CHECK_EQ(EFAULT, write_errno);
|
||||||
|
result = false;
|
||||||
|
} else {
|
||||||
|
result = (bytes_written == size);
|
||||||
|
}
|
||||||
|
internal_close(sock_pair[0]);
|
||||||
|
internal_close(sock_pair[1]);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
#endif // SANITIZER_POSIX
|
#endif // SANITIZER_POSIX
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#if SANITIZER_WINDOWS && !defined(va_copy)
|
#if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \
|
||||||
|
!defined(va_copy)
|
||||||
# define va_copy(dst, src) ((dst) = (src))
|
# define va_copy(dst, src) ((dst) = (src))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,9 @@ struct ProcSelfMapsBuff {
|
||||||
uptr mmaped_size;
|
uptr mmaped_size;
|
||||||
uptr len;
|
uptr len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reads process memory map in an OS-specific way.
|
||||||
|
void ReadProcMaps(ProcSelfMapsBuff *proc_maps);
|
||||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||||
|
|
||||||
class MemoryMappingLayout {
|
class MemoryMappingLayout {
|
||||||
|
@ -55,7 +58,7 @@ class MemoryMappingLayout {
|
||||||
// platform-specific files.
|
// platform-specific files.
|
||||||
# if SANITIZER_FREEBSD || SANITIZER_LINUX
|
# if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||||
ProcSelfMapsBuff proc_self_maps_;
|
ProcSelfMapsBuff proc_self_maps_;
|
||||||
char *current_;
|
const char *current_;
|
||||||
|
|
||||||
// Static mappings cache.
|
// Static mappings cache.
|
||||||
static ProcSelfMapsBuff cached_proc_self_maps_;
|
static ProcSelfMapsBuff cached_proc_self_maps_;
|
||||||
|
@ -84,6 +87,11 @@ void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size);
|
||||||
// Returns code range for the specified module.
|
// Returns code range for the specified module.
|
||||||
bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end);
|
bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end);
|
||||||
|
|
||||||
|
bool IsDecimal(char c);
|
||||||
|
uptr ParseDecimal(const char **p);
|
||||||
|
bool IsHex(char c);
|
||||||
|
uptr ParseHex(const char **p);
|
||||||
|
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
#endif // SANITIZER_PROCMAPS_H
|
#endif // SANITIZER_PROCMAPS_H
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
//===-- sanitizer_procmaps_common.cc --------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Information about the process mappings (common parts).
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "sanitizer_platform.h"
|
||||||
|
#if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||||
|
#include "sanitizer_common.h"
|
||||||
|
#include "sanitizer_placement_new.h"
|
||||||
|
#include "sanitizer_procmaps.h"
|
||||||
|
|
||||||
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
// Linker initialized.
|
||||||
|
ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
|
||||||
|
StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
|
||||||
|
|
||||||
|
static int TranslateDigit(char c) {
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a number and promote 'p' up to the first non-digit character.
|
||||||
|
static uptr ParseNumber(const char **p, int base) {
|
||||||
|
uptr n = 0;
|
||||||
|
int d;
|
||||||
|
CHECK(base >= 2 && base <= 16);
|
||||||
|
while ((d = TranslateDigit(**p)) >= 0 && d < base) {
|
||||||
|
n = n * base + d;
|
||||||
|
(*p)++;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDecimal(char c) {
|
||||||
|
int d = TranslateDigit(c);
|
||||||
|
return d >= 0 && d < 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr ParseDecimal(const char **p) {
|
||||||
|
return ParseNumber(p, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsHex(char c) {
|
||||||
|
int d = TranslateDigit(c);
|
||||||
|
return d >= 0 && d < 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr ParseHex(const char **p) {
|
||||||
|
return ParseNumber(p, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
|
||||||
|
ReadProcMaps(&proc_self_maps_);
|
||||||
|
if (cache_enabled) {
|
||||||
|
if (proc_self_maps_.mmaped_size == 0) {
|
||||||
|
LoadFromCache();
|
||||||
|
CHECK_GT(proc_self_maps_.len, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CHECK_GT(proc_self_maps_.mmaped_size, 0);
|
||||||
|
}
|
||||||
|
Reset();
|
||||||
|
// FIXME: in the future we may want to cache the mappings on demand only.
|
||||||
|
if (cache_enabled)
|
||||||
|
CacheMemoryMappings();
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryMappingLayout::~MemoryMappingLayout() {
|
||||||
|
// Only unmap the buffer if it is different from the cached one. Otherwise
|
||||||
|
// it will be unmapped when the cache is refreshed.
|
||||||
|
if (proc_self_maps_.data != cached_proc_self_maps_.data) {
|
||||||
|
UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryMappingLayout::Reset() {
|
||||||
|
current_ = proc_self_maps_.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void MemoryMappingLayout::CacheMemoryMappings() {
|
||||||
|
SpinMutexLock l(&cache_lock_);
|
||||||
|
// Don't invalidate the cache if the mappings are unavailable.
|
||||||
|
ProcSelfMapsBuff old_proc_self_maps;
|
||||||
|
old_proc_self_maps = cached_proc_self_maps_;
|
||||||
|
ReadProcMaps(&cached_proc_self_maps_);
|
||||||
|
if (cached_proc_self_maps_.mmaped_size == 0) {
|
||||||
|
cached_proc_self_maps_ = old_proc_self_maps;
|
||||||
|
} else {
|
||||||
|
if (old_proc_self_maps.mmaped_size) {
|
||||||
|
UnmapOrDie(old_proc_self_maps.data,
|
||||||
|
old_proc_self_maps.mmaped_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryMappingLayout::LoadFromCache() {
|
||||||
|
SpinMutexLock l(&cache_lock_);
|
||||||
|
if (cached_proc_self_maps_.data) {
|
||||||
|
proc_self_maps_ = cached_proc_self_maps_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
|
||||||
|
uptr max_modules,
|
||||||
|
string_predicate_t filter) {
|
||||||
|
Reset();
|
||||||
|
uptr cur_beg, cur_end, cur_offset, prot;
|
||||||
|
InternalScopedBuffer<char> module_name(kMaxPathLength);
|
||||||
|
uptr n_modules = 0;
|
||||||
|
for (uptr i = 0; n_modules < max_modules &&
|
||||||
|
Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),
|
||||||
|
module_name.size(), &prot);
|
||||||
|
i++) {
|
||||||
|
const char *cur_name = module_name.data();
|
||||||
|
if (cur_name[0] == '\0')
|
||||||
|
continue;
|
||||||
|
if (filter && !filter(cur_name))
|
||||||
|
continue;
|
||||||
|
void *mem = &modules[n_modules];
|
||||||
|
// Don't subtract 'cur_beg' from the first entry:
|
||||||
|
// * If a binary is compiled w/o -pie, then the first entry in
|
||||||
|
// process maps is likely the binary itself (all dynamic libs
|
||||||
|
// are mapped higher in address space). For such a binary,
|
||||||
|
// instruction offset in binary coincides with the actual
|
||||||
|
// instruction address in virtual memory (as code section
|
||||||
|
// is mapped to a fixed memory range).
|
||||||
|
// * If a binary is compiled with -pie, all the modules are
|
||||||
|
// mapped high at address space (in particular, higher than
|
||||||
|
// shadow memory of the tool), so the module can't be the
|
||||||
|
// first entry.
|
||||||
|
uptr base_address = (i ? cur_beg : 0) - cur_offset;
|
||||||
|
LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address);
|
||||||
|
cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
|
||||||
|
n_modules++;
|
||||||
|
}
|
||||||
|
return n_modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
|
||||||
|
char *smaps = 0;
|
||||||
|
uptr smaps_cap = 0;
|
||||||
|
uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",
|
||||||
|
&smaps, &smaps_cap, 64<<20);
|
||||||
|
uptr start = 0;
|
||||||
|
bool file = false;
|
||||||
|
const char *pos = smaps;
|
||||||
|
while (pos < smaps + smaps_len) {
|
||||||
|
if (IsHex(pos[0])) {
|
||||||
|
start = ParseHex(&pos);
|
||||||
|
for (; *pos != '/' && *pos > '\n'; pos++) {}
|
||||||
|
file = *pos == '/';
|
||||||
|
} else if (internal_strncmp(pos, "Rss:", 4) == 0) {
|
||||||
|
while (!IsDecimal(*pos)) pos++;
|
||||||
|
uptr rss = ParseDecimal(&pos) * 1024;
|
||||||
|
cb(start, rss, file, stats, stats_size);
|
||||||
|
}
|
||||||
|
while (*pos++ != '\n') {}
|
||||||
|
}
|
||||||
|
UnmapOrDie(smaps, smaps_cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __sanitizer
|
||||||
|
|
||||||
|
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
|
@ -0,0 +1,86 @@
|
||||||
|
//===-- sanitizer_procmaps_freebsd.cc -------------------------------------===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Information about the process mappings (FreeBSD-specific parts).
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "sanitizer_platform.h"
|
||||||
|
#if SANITIZER_FREEBSD
|
||||||
|
#include "sanitizer_common.h"
|
||||||
|
#include "sanitizer_freebsd.h"
|
||||||
|
#include "sanitizer_procmaps.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <sys/user.h>
|
||||||
|
|
||||||
|
// Fix 'kinfo_vmentry' definition on FreeBSD prior v9.2 in 32-bit mode.
|
||||||
|
#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
|
||||||
|
# include <osreldate.h>
|
||||||
|
# if __FreeBSD_version <= 902001 // v9.2
|
||||||
|
# define kinfo_vmentry xkinfo_vmentry
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
|
||||||
|
const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
|
||||||
|
size_t Size = 0;
|
||||||
|
int Err = sysctl(Mib, 4, NULL, &Size, NULL, 0);
|
||||||
|
CHECK_EQ(Err, 0);
|
||||||
|
CHECK_GT(Size, 0);
|
||||||
|
|
||||||
|
size_t MmapedSize = Size * 4 / 3;
|
||||||
|
void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
|
||||||
|
Size = MmapedSize;
|
||||||
|
Err = sysctl(Mib, 4, VmMap, &Size, NULL, 0);
|
||||||
|
CHECK_EQ(Err, 0);
|
||||||
|
|
||||||
|
proc_maps->data = (char*)VmMap;
|
||||||
|
proc_maps->mmaped_size = MmapedSize;
|
||||||
|
proc_maps->len = Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
||||||
|
char filename[], uptr filename_size,
|
||||||
|
uptr *protection) {
|
||||||
|
char *last = proc_self_maps_.data + proc_self_maps_.len;
|
||||||
|
if (current_ >= last) return false;
|
||||||
|
uptr dummy;
|
||||||
|
if (!start) start = &dummy;
|
||||||
|
if (!end) end = &dummy;
|
||||||
|
if (!offset) offset = &dummy;
|
||||||
|
if (!protection) protection = &dummy;
|
||||||
|
struct kinfo_vmentry *VmEntry = (struct kinfo_vmentry*)current_;
|
||||||
|
|
||||||
|
*start = (uptr)VmEntry->kve_start;
|
||||||
|
*end = (uptr)VmEntry->kve_end;
|
||||||
|
*offset = (uptr)VmEntry->kve_offset;
|
||||||
|
|
||||||
|
*protection = 0;
|
||||||
|
if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
|
||||||
|
*protection |= kProtectionRead;
|
||||||
|
if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
|
||||||
|
*protection |= kProtectionWrite;
|
||||||
|
if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
|
||||||
|
*protection |= kProtectionExecute;
|
||||||
|
|
||||||
|
if (filename != NULL && filename_size > 0) {
|
||||||
|
internal_snprintf(filename,
|
||||||
|
Min(filename_size, (uptr)PATH_MAX),
|
||||||
|
"%s", VmEntry->kve_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_ += VmEntry->kve_structsize;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __sanitizer
|
||||||
|
|
||||||
|
#endif // SANITIZER_FREEBSD
|
|
@ -9,151 +9,20 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "sanitizer_platform.h"
|
#include "sanitizer_platform.h"
|
||||||
#if SANITIZER_FREEBSD || SANITIZER_LINUX
|
#if SANITIZER_LINUX
|
||||||
#include "sanitizer_common.h"
|
#include "sanitizer_common.h"
|
||||||
#include "sanitizer_placement_new.h"
|
|
||||||
#include "sanitizer_procmaps.h"
|
#include "sanitizer_procmaps.h"
|
||||||
|
|
||||||
#if SANITIZER_FREEBSD
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/sysctl.h>
|
|
||||||
#include <sys/user.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace __sanitizer {
|
namespace __sanitizer {
|
||||||
|
|
||||||
// Linker initialized.
|
void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
|
||||||
ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
|
|
||||||
StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
|
|
||||||
|
|
||||||
static void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
|
|
||||||
#if SANITIZER_FREEBSD
|
|
||||||
const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
|
|
||||||
size_t Size = 0;
|
|
||||||
int Err = sysctl(Mib, 4, NULL, &Size, NULL, 0);
|
|
||||||
CHECK_EQ(Err, 0);
|
|
||||||
CHECK_GT(Size, 0);
|
|
||||||
|
|
||||||
size_t MmapedSize = Size * 4 / 3;
|
|
||||||
void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
|
|
||||||
Size = MmapedSize;
|
|
||||||
Err = sysctl(Mib, 4, VmMap, &Size, NULL, 0);
|
|
||||||
CHECK_EQ(Err, 0);
|
|
||||||
|
|
||||||
proc_maps->data = (char*)VmMap;
|
|
||||||
proc_maps->mmaped_size = MmapedSize;
|
|
||||||
proc_maps->len = Size;
|
|
||||||
#else
|
|
||||||
proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
|
proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
|
||||||
&proc_maps->mmaped_size, 1 << 26);
|
&proc_maps->mmaped_size, 1 << 26);
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
|
|
||||||
ReadProcMaps(&proc_self_maps_);
|
|
||||||
if (cache_enabled) {
|
|
||||||
if (proc_self_maps_.mmaped_size == 0) {
|
|
||||||
LoadFromCache();
|
|
||||||
CHECK_GT(proc_self_maps_.len, 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CHECK_GT(proc_self_maps_.mmaped_size, 0);
|
|
||||||
}
|
|
||||||
Reset();
|
|
||||||
// FIXME: in the future we may want to cache the mappings on demand only.
|
|
||||||
if (cache_enabled)
|
|
||||||
CacheMemoryMappings();
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryMappingLayout::~MemoryMappingLayout() {
|
|
||||||
// Only unmap the buffer if it is different from the cached one. Otherwise
|
|
||||||
// it will be unmapped when the cache is refreshed.
|
|
||||||
if (proc_self_maps_.data != cached_proc_self_maps_.data) {
|
|
||||||
UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryMappingLayout::Reset() {
|
|
||||||
current_ = proc_self_maps_.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void MemoryMappingLayout::CacheMemoryMappings() {
|
|
||||||
SpinMutexLock l(&cache_lock_);
|
|
||||||
// Don't invalidate the cache if the mappings are unavailable.
|
|
||||||
ProcSelfMapsBuff old_proc_self_maps;
|
|
||||||
old_proc_self_maps = cached_proc_self_maps_;
|
|
||||||
ReadProcMaps(&cached_proc_self_maps_);
|
|
||||||
if (cached_proc_self_maps_.mmaped_size == 0) {
|
|
||||||
cached_proc_self_maps_ = old_proc_self_maps;
|
|
||||||
} else {
|
|
||||||
if (old_proc_self_maps.mmaped_size) {
|
|
||||||
UnmapOrDie(old_proc_self_maps.data,
|
|
||||||
old_proc_self_maps.mmaped_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryMappingLayout::LoadFromCache() {
|
|
||||||
SpinMutexLock l(&cache_lock_);
|
|
||||||
if (cached_proc_self_maps_.data) {
|
|
||||||
proc_self_maps_ = cached_proc_self_maps_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !SANITIZER_FREEBSD
|
|
||||||
// Parse a hex value in str and update str.
|
|
||||||
static uptr ParseHex(char **str) {
|
|
||||||
uptr x = 0;
|
|
||||||
char *s;
|
|
||||||
for (s = *str; ; s++) {
|
|
||||||
char c = *s;
|
|
||||||
uptr v = 0;
|
|
||||||
if (c >= '0' && c <= '9')
|
|
||||||
v = c - '0';
|
|
||||||
else if (c >= 'a' && c <= 'f')
|
|
||||||
v = c - 'a' + 10;
|
|
||||||
else if (c >= 'A' && c <= 'F')
|
|
||||||
v = c - 'A' + 10;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
x = x * 16 + v;
|
|
||||||
}
|
|
||||||
*str = s;
|
|
||||||
return x;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsOneOf(char c, char c1, char c2) {
|
static bool IsOneOf(char c, char c1, char c2) {
|
||||||
return c == c1 || c == c2;
|
return c == c1 || c == c2;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static bool IsDecimal(char c) {
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsHex(char c) {
|
|
||||||
return (c >= '0' && c <= '9')
|
|
||||||
|| (c >= 'a' && c <= 'f');
|
|
||||||
}
|
|
||||||
|
|
||||||
static uptr ReadHex(const char *p) {
|
|
||||||
uptr v = 0;
|
|
||||||
for (; IsHex(p[0]); p++) {
|
|
||||||
if (p[0] >= '0' && p[0] <= '9')
|
|
||||||
v = v * 16 + p[0] - '0';
|
|
||||||
else
|
|
||||||
v = v * 16 + p[0] - 'a' + 10;
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uptr ReadDecimal(const char *p) {
|
|
||||||
uptr v = 0;
|
|
||||||
for (; IsDecimal(p[0]); p++)
|
|
||||||
v = v * 10 + p[0] - '0';
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
||||||
char filename[], uptr filename_size,
|
char filename[], uptr filename_size,
|
||||||
|
@ -165,29 +34,6 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
||||||
if (!end) end = &dummy;
|
if (!end) end = &dummy;
|
||||||
if (!offset) offset = &dummy;
|
if (!offset) offset = &dummy;
|
||||||
if (!protection) protection = &dummy;
|
if (!protection) protection = &dummy;
|
||||||
#if SANITIZER_FREEBSD
|
|
||||||
struct kinfo_vmentry *VmEntry = (struct kinfo_vmentry*)current_;
|
|
||||||
|
|
||||||
*start = (uptr)VmEntry->kve_start;
|
|
||||||
*end = (uptr)VmEntry->kve_end;
|
|
||||||
*offset = (uptr)VmEntry->kve_offset;
|
|
||||||
|
|
||||||
*protection = 0;
|
|
||||||
if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
|
|
||||||
*protection |= kProtectionRead;
|
|
||||||
if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
|
|
||||||
*protection |= kProtectionWrite;
|
|
||||||
if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
|
|
||||||
*protection |= kProtectionExecute;
|
|
||||||
|
|
||||||
if (filename != NULL && filename_size > 0) {
|
|
||||||
internal_snprintf(filename,
|
|
||||||
Min(filename_size, (uptr)PATH_MAX),
|
|
||||||
"%s", VmEntry->kve_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
current_ += VmEntry->kve_structsize;
|
|
||||||
#else // !SANITIZER_FREEBSD
|
|
||||||
char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
|
char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
|
||||||
if (next_line == 0)
|
if (next_line == 0)
|
||||||
next_line = last;
|
next_line = last;
|
||||||
|
@ -234,69 +80,9 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
||||||
if (filename && i < filename_size)
|
if (filename && i < filename_size)
|
||||||
filename[i] = 0;
|
filename[i] = 0;
|
||||||
current_ = next_line + 1;
|
current_ = next_line + 1;
|
||||||
#endif // !SANITIZER_FREEBSD
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
|
|
||||||
uptr max_modules,
|
|
||||||
string_predicate_t filter) {
|
|
||||||
Reset();
|
|
||||||
uptr cur_beg, cur_end, cur_offset;
|
|
||||||
InternalScopedBuffer<char> module_name(kMaxPathLength);
|
|
||||||
uptr n_modules = 0;
|
|
||||||
for (uptr i = 0; n_modules < max_modules &&
|
|
||||||
Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),
|
|
||||||
module_name.size(), 0);
|
|
||||||
i++) {
|
|
||||||
const char *cur_name = module_name.data();
|
|
||||||
if (cur_name[0] == '\0')
|
|
||||||
continue;
|
|
||||||
if (filter && !filter(cur_name))
|
|
||||||
continue;
|
|
||||||
void *mem = &modules[n_modules];
|
|
||||||
// Don't subtract 'cur_beg' from the first entry:
|
|
||||||
// * If a binary is compiled w/o -pie, then the first entry in
|
|
||||||
// process maps is likely the binary itself (all dynamic libs
|
|
||||||
// are mapped higher in address space). For such a binary,
|
|
||||||
// instruction offset in binary coincides with the actual
|
|
||||||
// instruction address in virtual memory (as code section
|
|
||||||
// is mapped to a fixed memory range).
|
|
||||||
// * If a binary is compiled with -pie, all the modules are
|
|
||||||
// mapped high at address space (in particular, higher than
|
|
||||||
// shadow memory of the tool), so the module can't be the
|
|
||||||
// first entry.
|
|
||||||
uptr base_address = (i ? cur_beg : 0) - cur_offset;
|
|
||||||
LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address);
|
|
||||||
cur_module->addAddressRange(cur_beg, cur_end);
|
|
||||||
n_modules++;
|
|
||||||
}
|
|
||||||
return n_modules;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
|
|
||||||
char *smaps = 0;
|
|
||||||
uptr smaps_cap = 0;
|
|
||||||
uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",
|
|
||||||
&smaps, &smaps_cap, 64<<20);
|
|
||||||
uptr start = 0;
|
|
||||||
bool file = false;
|
|
||||||
const char *pos = smaps;
|
|
||||||
while (pos < smaps + smaps_len) {
|
|
||||||
if (IsHex(pos[0])) {
|
|
||||||
start = ReadHex(pos);
|
|
||||||
for (; *pos != '/' && *pos > '\n'; pos++) {}
|
|
||||||
file = *pos == '/';
|
|
||||||
} else if (internal_strncmp(pos, "Rss:", 4) == 0) {
|
|
||||||
for (; *pos < '0' || *pos > '9'; pos++) {}
|
|
||||||
uptr rss = ReadDecimal(pos) * 1024;
|
|
||||||
cb(start, rss, file, stats, stats_size);
|
|
||||||
}
|
|
||||||
while (*pos++ != '\n') {}
|
|
||||||
}
|
|
||||||
UnmapOrDie(smaps, smaps_cap);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
#endif // SANITIZER_LINUX
|
||||||
|
|
|
@ -73,14 +73,16 @@ template<u32 kLCSegment, typename SegmentCommand>
|
||||||
bool MemoryMappingLayout::NextSegmentLoad(
|
bool MemoryMappingLayout::NextSegmentLoad(
|
||||||
uptr *start, uptr *end, uptr *offset,
|
uptr *start, uptr *end, uptr *offset,
|
||||||
char filename[], uptr filename_size, uptr *protection) {
|
char filename[], uptr filename_size, uptr *protection) {
|
||||||
if (protection)
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
const char* lc = current_load_cmd_addr_;
|
const char* lc = current_load_cmd_addr_;
|
||||||
current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize;
|
current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize;
|
||||||
if (((const load_command *)lc)->cmd == kLCSegment) {
|
if (((const load_command *)lc)->cmd == kLCSegment) {
|
||||||
const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_);
|
const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_);
|
||||||
const SegmentCommand* sc = (const SegmentCommand *)lc;
|
const SegmentCommand* sc = (const SegmentCommand *)lc;
|
||||||
if (start) *start = sc->vmaddr + dlloff;
|
if (start) *start = sc->vmaddr + dlloff;
|
||||||
|
if (protection) {
|
||||||
|
// Return the initial protection.
|
||||||
|
*protection = sc->initprot;
|
||||||
|
}
|
||||||
if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
|
if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
|
||||||
if (offset) {
|
if (offset) {
|
||||||
if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
|
if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
|
||||||
|
@ -155,12 +157,12 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
|
||||||
uptr max_modules,
|
uptr max_modules,
|
||||||
string_predicate_t filter) {
|
string_predicate_t filter) {
|
||||||
Reset();
|
Reset();
|
||||||
uptr cur_beg, cur_end;
|
uptr cur_beg, cur_end, prot;
|
||||||
InternalScopedBuffer<char> module_name(kMaxPathLength);
|
InternalScopedBuffer<char> module_name(kMaxPathLength);
|
||||||
uptr n_modules = 0;
|
uptr n_modules = 0;
|
||||||
for (uptr i = 0; n_modules < max_modules &&
|
for (uptr i = 0; n_modules < max_modules &&
|
||||||
Next(&cur_beg, &cur_end, 0, module_name.data(),
|
Next(&cur_beg, &cur_end, 0, module_name.data(),
|
||||||
module_name.size(), 0);
|
module_name.size(), &prot);
|
||||||
i++) {
|
i++) {
|
||||||
const char *cur_name = module_name.data();
|
const char *cur_name = module_name.data();
|
||||||
if (cur_name[0] == '\0')
|
if (cur_name[0] == '\0')
|
||||||
|
@ -176,7 +178,7 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
|
||||||
cur_module = new(mem) LoadedModule(cur_name, cur_beg);
|
cur_module = new(mem) LoadedModule(cur_name, cur_beg);
|
||||||
n_modules++;
|
n_modules++;
|
||||||
}
|
}
|
||||||
cur_module->addAddressRange(cur_beg, cur_end);
|
cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
|
||||||
}
|
}
|
||||||
return n_modules;
|
return n_modules;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,16 @@
|
||||||
#include "sanitizer_common.h"
|
#include "sanitizer_common.h"
|
||||||
|
|
||||||
namespace __sanitizer {
|
namespace __sanitizer {
|
||||||
class AnsiColorDecorator {
|
class SanitizerCommonDecorator {
|
||||||
// FIXME: This is not portable. It assumes the special strings are printed to
|
// FIXME: This is not portable. It assumes the special strings are printed to
|
||||||
// stdout, which is not the case on Windows (see SetConsoleTextAttribute()).
|
// stdout, which is not the case on Windows (see SetConsoleTextAttribute()).
|
||||||
public:
|
public:
|
||||||
explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { }
|
SanitizerCommonDecorator() : ansi_(ColorizeReports()) {}
|
||||||
const char *Bold() const { return ansi_ ? "\033[1m" : ""; }
|
const char *Bold() const { return ansi_ ? "\033[1m" : ""; }
|
||||||
|
const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; }
|
||||||
|
const char *Warning() { return Red(); }
|
||||||
|
const char *EndWarning() { return Default(); }
|
||||||
|
protected:
|
||||||
const char *Black() const { return ansi_ ? "\033[1m\033[30m" : ""; }
|
const char *Black() const { return ansi_ ? "\033[1m\033[30m" : ""; }
|
||||||
const char *Red() const { return ansi_ ? "\033[1m\033[31m" : ""; }
|
const char *Red() const { return ansi_ ? "\033[1m\033[31m" : ""; }
|
||||||
const char *Green() const { return ansi_ ? "\033[1m\033[32m" : ""; }
|
const char *Green() const { return ansi_ ? "\033[1m\033[32m" : ""; }
|
||||||
|
@ -32,19 +36,10 @@ class AnsiColorDecorator {
|
||||||
const char *Magenta() const { return ansi_ ? "\033[1m\033[35m" : ""; }
|
const char *Magenta() const { return ansi_ ? "\033[1m\033[35m" : ""; }
|
||||||
const char *Cyan() const { return ansi_ ? "\033[1m\033[36m" : ""; }
|
const char *Cyan() const { return ansi_ ? "\033[1m\033[36m" : ""; }
|
||||||
const char *White() const { return ansi_ ? "\033[1m\033[37m" : ""; }
|
const char *White() const { return ansi_ ? "\033[1m\033[37m" : ""; }
|
||||||
const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; }
|
|
||||||
private:
|
private:
|
||||||
bool ansi_;
|
bool ansi_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SanitizerCommonDecorator: protected AnsiColorDecorator {
|
|
||||||
public:
|
|
||||||
SanitizerCommonDecorator()
|
|
||||||
: __sanitizer::AnsiColorDecorator(ColorizeReports()) { }
|
|
||||||
const char *Warning() { return Red(); }
|
|
||||||
const char *EndWarning() { return Default(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
#endif // SANITIZER_REPORT_DECORATOR_H
|
#endif // SANITIZER_REPORT_DECORATOR_H
|
||||||
|
|
|
@ -10,193 +10,128 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "sanitizer_stackdepot.h"
|
#include "sanitizer_stackdepot.h"
|
||||||
|
|
||||||
#include "sanitizer_common.h"
|
#include "sanitizer_common.h"
|
||||||
#include "sanitizer_internal_defs.h"
|
#include "sanitizer_stackdepotbase.h"
|
||||||
#include "sanitizer_mutex.h"
|
|
||||||
#include "sanitizer_atomic.h"
|
|
||||||
|
|
||||||
namespace __sanitizer {
|
namespace __sanitizer {
|
||||||
|
|
||||||
const int kTabSize = 1024 * 1024; // Hash table size.
|
struct StackDepotDesc {
|
||||||
const int kPartBits = 8;
|
const uptr *stack;
|
||||||
const int kPartShift = sizeof(u32) * 8 - kPartBits - 1;
|
|
||||||
const int kPartCount = 1 << kPartBits; // Number of subparts in the table.
|
|
||||||
const int kPartSize = kTabSize / kPartCount;
|
|
||||||
const int kMaxId = 1 << kPartShift;
|
|
||||||
|
|
||||||
struct StackDesc {
|
|
||||||
StackDesc *link;
|
|
||||||
u32 id;
|
|
||||||
u32 hash;
|
|
||||||
uptr size;
|
uptr size;
|
||||||
uptr stack[1]; // [size]
|
u32 hash() const {
|
||||||
|
// murmur2
|
||||||
|
const u32 m = 0x5bd1e995;
|
||||||
|
const u32 seed = 0x9747b28c;
|
||||||
|
const u32 r = 24;
|
||||||
|
u32 h = seed ^ (size * sizeof(uptr));
|
||||||
|
for (uptr i = 0; i < size; i++) {
|
||||||
|
u32 k = stack[i];
|
||||||
|
k *= m;
|
||||||
|
k ^= k >> r;
|
||||||
|
k *= m;
|
||||||
|
h *= m;
|
||||||
|
h ^= k;
|
||||||
|
}
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= m;
|
||||||
|
h ^= h >> 15;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
bool is_valid() { return size > 0 && stack; }
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct {
|
struct StackDepotNode {
|
||||||
StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator.
|
StackDepotNode *link;
|
||||||
atomic_uintptr_t region_pos; // Region allocator for StackDesc's.
|
u32 id;
|
||||||
atomic_uintptr_t region_end;
|
atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20;
|
||||||
atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's.
|
uptr size;
|
||||||
atomic_uint32_t seq[kPartCount]; // Unique id generators.
|
uptr stack[1]; // [size]
|
||||||
} depot;
|
|
||||||
|
|
||||||
static StackDepotStats stats;
|
static const u32 kTabSizeLog = 20;
|
||||||
|
// Lower kTabSizeLog bits are equal for all items in one bucket.
|
||||||
|
// We use these bits to store the per-stack use counter.
|
||||||
|
static const u32 kUseCountBits = kTabSizeLog;
|
||||||
|
static const u32 kMaxUseCount = 1 << kUseCountBits;
|
||||||
|
static const u32 kUseCountMask = (1 << kUseCountBits) - 1;
|
||||||
|
static const u32 kHashMask = ~kUseCountMask;
|
||||||
|
|
||||||
|
typedef StackDepotDesc args_type;
|
||||||
|
bool eq(u32 hash, const args_type &args) const {
|
||||||
|
u32 hash_bits =
|
||||||
|
atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask;
|
||||||
|
if ((hash & kHashMask) != hash_bits || args.size != size) return false;
|
||||||
|
uptr i = 0;
|
||||||
|
for (; i < size; i++) {
|
||||||
|
if (stack[i] != args.stack[i]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static uptr storage_size(const args_type &args) {
|
||||||
|
return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr);
|
||||||
|
}
|
||||||
|
void store(const args_type &args, u32 hash) {
|
||||||
|
atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
|
||||||
|
size = args.size;
|
||||||
|
internal_memcpy(stack, args.stack, size * sizeof(uptr));
|
||||||
|
}
|
||||||
|
args_type load() const {
|
||||||
|
args_type ret = {&stack[0], size};
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
StackDepotHandle get_handle() { return StackDepotHandle(this); }
|
||||||
|
|
||||||
|
typedef StackDepotHandle handle_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
COMPILER_CHECK(StackDepotNode::kMaxUseCount == (u32)kStackDepotMaxUseCount);
|
||||||
|
|
||||||
|
u32 StackDepotHandle::id() { return node_->id; }
|
||||||
|
int StackDepotHandle::use_count() {
|
||||||
|
return atomic_load(&node_->hash_and_use_count, memory_order_relaxed) &
|
||||||
|
StackDepotNode::kUseCountMask;
|
||||||
|
}
|
||||||
|
void StackDepotHandle::inc_use_count_unsafe() {
|
||||||
|
u32 prev =
|
||||||
|
atomic_fetch_add(&node_->hash_and_use_count, 1, memory_order_relaxed) &
|
||||||
|
StackDepotNode::kUseCountMask;
|
||||||
|
CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount);
|
||||||
|
}
|
||||||
|
uptr StackDepotHandle::size() { return node_->size; }
|
||||||
|
uptr *StackDepotHandle::stack() { return &node_->stack[0]; }
|
||||||
|
|
||||||
|
// FIXME(dvyukov): this single reserved bit is used in TSan.
|
||||||
|
typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog>
|
||||||
|
StackDepot;
|
||||||
|
static StackDepot theDepot;
|
||||||
|
|
||||||
StackDepotStats *StackDepotGetStats() {
|
StackDepotStats *StackDepotGetStats() {
|
||||||
return &stats;
|
return theDepot.GetStats();
|
||||||
}
|
|
||||||
|
|
||||||
static u32 hash(const uptr *stack, uptr size) {
|
|
||||||
// murmur2
|
|
||||||
const u32 m = 0x5bd1e995;
|
|
||||||
const u32 seed = 0x9747b28c;
|
|
||||||
const u32 r = 24;
|
|
||||||
u32 h = seed ^ (size * sizeof(uptr));
|
|
||||||
for (uptr i = 0; i < size; i++) {
|
|
||||||
u32 k = stack[i];
|
|
||||||
k *= m;
|
|
||||||
k ^= k >> r;
|
|
||||||
k *= m;
|
|
||||||
h *= m;
|
|
||||||
h ^= k;
|
|
||||||
}
|
|
||||||
h ^= h >> 13;
|
|
||||||
h *= m;
|
|
||||||
h ^= h >> 15;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static StackDesc *tryallocDesc(uptr memsz) {
|
|
||||||
// Optimisic lock-free allocation, essentially try to bump the region ptr.
|
|
||||||
for (;;) {
|
|
||||||
uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire);
|
|
||||||
uptr end = atomic_load(&depot.region_end, memory_order_acquire);
|
|
||||||
if (cmp == 0 || cmp + memsz > end)
|
|
||||||
return 0;
|
|
||||||
if (atomic_compare_exchange_weak(
|
|
||||||
&depot.region_pos, &cmp, cmp + memsz,
|
|
||||||
memory_order_acquire))
|
|
||||||
return (StackDesc*)cmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static StackDesc *allocDesc(uptr size) {
|
|
||||||
// First, try to allocate optimisitically.
|
|
||||||
uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr);
|
|
||||||
StackDesc *s = tryallocDesc(memsz);
|
|
||||||
if (s)
|
|
||||||
return s;
|
|
||||||
// If failed, lock, retry and alloc new superblock.
|
|
||||||
SpinMutexLock l(&depot.mtx);
|
|
||||||
for (;;) {
|
|
||||||
s = tryallocDesc(memsz);
|
|
||||||
if (s)
|
|
||||||
return s;
|
|
||||||
atomic_store(&depot.region_pos, 0, memory_order_relaxed);
|
|
||||||
uptr allocsz = 64 * 1024;
|
|
||||||
if (allocsz < memsz)
|
|
||||||
allocsz = memsz;
|
|
||||||
uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
|
|
||||||
stats.mapped += allocsz;
|
|
||||||
atomic_store(&depot.region_end, mem + allocsz, memory_order_release);
|
|
||||||
atomic_store(&depot.region_pos, mem, memory_order_release);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) {
|
|
||||||
// Searches linked list s for the stack, returns its id.
|
|
||||||
for (; s; s = s->link) {
|
|
||||||
if (s->hash == hash && s->size == size) {
|
|
||||||
uptr i = 0;
|
|
||||||
for (; i < size; i++) {
|
|
||||||
if (stack[i] != s->stack[i])
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (i == size)
|
|
||||||
return s->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static StackDesc *lock(atomic_uintptr_t *p) {
|
|
||||||
// Uses the pointer lsb as mutex.
|
|
||||||
for (int i = 0;; i++) {
|
|
||||||
uptr cmp = atomic_load(p, memory_order_relaxed);
|
|
||||||
if ((cmp & 1) == 0
|
|
||||||
&& atomic_compare_exchange_weak(p, &cmp, cmp | 1,
|
|
||||||
memory_order_acquire))
|
|
||||||
return (StackDesc*)cmp;
|
|
||||||
if (i < 10)
|
|
||||||
proc_yield(10);
|
|
||||||
else
|
|
||||||
internal_sched_yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unlock(atomic_uintptr_t *p, StackDesc *s) {
|
|
||||||
DCHECK_EQ((uptr)s & 1, 0);
|
|
||||||
atomic_store(p, (uptr)s, memory_order_release);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 StackDepotPut(const uptr *stack, uptr size) {
|
u32 StackDepotPut(const uptr *stack, uptr size) {
|
||||||
if (stack == 0 || size == 0)
|
StackDepotDesc desc = {stack, size};
|
||||||
return 0;
|
StackDepotHandle h = theDepot.Put(desc);
|
||||||
uptr h = hash(stack, size);
|
return h.valid() ? h.id() : 0;
|
||||||
atomic_uintptr_t *p = &depot.tab[h % kTabSize];
|
}
|
||||||
uptr v = atomic_load(p, memory_order_consume);
|
|
||||||
StackDesc *s = (StackDesc*)(v & ~1);
|
StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size) {
|
||||||
// First, try to find the existing stack.
|
StackDepotDesc desc = {stack, size};
|
||||||
u32 id = find(s, stack, size, h);
|
return theDepot.Put(desc);
|
||||||
if (id)
|
|
||||||
return id;
|
|
||||||
// If failed, lock, retry and insert new.
|
|
||||||
StackDesc *s2 = lock(p);
|
|
||||||
if (s2 != s) {
|
|
||||||
id = find(s2, stack, size, h);
|
|
||||||
if (id) {
|
|
||||||
unlock(p, s2);
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uptr part = (h % kTabSize) / kPartSize;
|
|
||||||
id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1;
|
|
||||||
stats.n_uniq_ids++;
|
|
||||||
CHECK_LT(id, kMaxId);
|
|
||||||
id |= part << kPartShift;
|
|
||||||
CHECK_NE(id, 0);
|
|
||||||
CHECK_EQ(id & (1u << 31), 0);
|
|
||||||
s = allocDesc(size);
|
|
||||||
s->id = id;
|
|
||||||
s->hash = h;
|
|
||||||
s->size = size;
|
|
||||||
internal_memcpy(s->stack, stack, size * sizeof(uptr));
|
|
||||||
s->link = s2;
|
|
||||||
unlock(p, s);
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const uptr *StackDepotGet(u32 id, uptr *size) {
|
const uptr *StackDepotGet(u32 id, uptr *size) {
|
||||||
if (id == 0)
|
StackDepotDesc desc = theDepot.Get(id);
|
||||||
return 0;
|
*size = desc.size;
|
||||||
CHECK_EQ(id & (1u << 31), 0);
|
return desc.stack;
|
||||||
// High kPartBits contain part id, so we need to scan at most kPartSize lists.
|
}
|
||||||
uptr part = id >> kPartShift;
|
|
||||||
for (int i = 0; i != kPartSize; i++) {
|
void StackDepotLockAll() {
|
||||||
uptr idx = part * kPartSize + i;
|
theDepot.LockAll();
|
||||||
CHECK_LT(idx, kTabSize);
|
}
|
||||||
atomic_uintptr_t *p = &depot.tab[idx];
|
|
||||||
uptr v = atomic_load(p, memory_order_consume);
|
void StackDepotUnlockAll() {
|
||||||
StackDesc *s = (StackDesc*)(v & ~1);
|
theDepot.UnlockAll();
|
||||||
for (; s; s = s->link) {
|
|
||||||
if (s->id == id) {
|
|
||||||
*size = s->size;
|
|
||||||
return s->stack;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*size = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StackDepotReverseMap::IdDescPair::IdComparator(
|
bool StackDepotReverseMap::IdDescPair::IdComparator(
|
||||||
|
@ -207,10 +142,10 @@ bool StackDepotReverseMap::IdDescPair::IdComparator(
|
||||||
|
|
||||||
StackDepotReverseMap::StackDepotReverseMap()
|
StackDepotReverseMap::StackDepotReverseMap()
|
||||||
: map_(StackDepotGetStats()->n_uniq_ids + 100) {
|
: map_(StackDepotGetStats()->n_uniq_ids + 100) {
|
||||||
for (int idx = 0; idx < kTabSize; idx++) {
|
for (int idx = 0; idx < StackDepot::kTabSize; idx++) {
|
||||||
atomic_uintptr_t *p = &depot.tab[idx];
|
atomic_uintptr_t *p = &theDepot.tab[idx];
|
||||||
uptr v = atomic_load(p, memory_order_consume);
|
uptr v = atomic_load(p, memory_order_consume);
|
||||||
StackDesc *s = (StackDesc*)(v & ~1);
|
StackDepotNode *s = (StackDepotNode*)(v & ~1);
|
||||||
for (; s; s = s->link) {
|
for (; s; s = s->link) {
|
||||||
IdDescPair pair = {s->id, s};
|
IdDescPair pair = {s->id, s};
|
||||||
map_.push_back(pair);
|
map_.push_back(pair);
|
||||||
|
@ -228,7 +163,7 @@ const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) {
|
||||||
*size = 0;
|
*size = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
StackDesc *desc = map_[idx].desc;
|
StackDepotNode *desc = map_[idx].desc;
|
||||||
*size = desc->size;
|
*size = desc->size;
|
||||||
return desc->stack;
|
return desc->stack;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,20 +17,29 @@
|
||||||
namespace __sanitizer {
|
namespace __sanitizer {
|
||||||
|
|
||||||
// StackDepot efficiently stores huge amounts of stack traces.
|
// StackDepot efficiently stores huge amounts of stack traces.
|
||||||
|
struct StackDepotNode;
|
||||||
|
struct StackDepotHandle {
|
||||||
|
StackDepotNode *node_;
|
||||||
|
StackDepotHandle() : node_(0) {}
|
||||||
|
explicit StackDepotHandle(StackDepotNode *node) : node_(node) {}
|
||||||
|
bool valid() { return node_; }
|
||||||
|
u32 id();
|
||||||
|
int use_count();
|
||||||
|
void inc_use_count_unsafe();
|
||||||
|
uptr size();
|
||||||
|
uptr *stack();
|
||||||
|
};
|
||||||
|
|
||||||
// Maps stack trace to an unique id.
|
const int kStackDepotMaxUseCount = 1U << 20;
|
||||||
|
|
||||||
|
StackDepotStats *StackDepotGetStats();
|
||||||
u32 StackDepotPut(const uptr *stack, uptr size);
|
u32 StackDepotPut(const uptr *stack, uptr size);
|
||||||
|
StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size);
|
||||||
// Retrieves a stored stack trace by the id.
|
// Retrieves a stored stack trace by the id.
|
||||||
const uptr *StackDepotGet(u32 id, uptr *size);
|
const uptr *StackDepotGet(u32 id, uptr *size);
|
||||||
|
|
||||||
struct StackDepotStats {
|
void StackDepotLockAll();
|
||||||
uptr n_uniq_ids;
|
void StackDepotUnlockAll();
|
||||||
uptr mapped;
|
|
||||||
};
|
|
||||||
|
|
||||||
StackDepotStats *StackDepotGetStats();
|
|
||||||
|
|
||||||
struct StackDesc;
|
|
||||||
|
|
||||||
// Instantiating this class creates a snapshot of StackDepot which can be
|
// Instantiating this class creates a snapshot of StackDepot which can be
|
||||||
// efficiently queried with StackDepotGet(). You can use it concurrently with
|
// efficiently queried with StackDepotGet(). You can use it concurrently with
|
||||||
|
@ -44,7 +53,7 @@ class StackDepotReverseMap {
|
||||||
private:
|
private:
|
||||||
struct IdDescPair {
|
struct IdDescPair {
|
||||||
u32 id;
|
u32 id;
|
||||||
StackDesc *desc;
|
StackDepotNode *desc;
|
||||||
|
|
||||||
static bool IdComparator(const IdDescPair &a, const IdDescPair &b);
|
static bool IdComparator(const IdDescPair &a, const IdDescPair &b);
|
||||||
};
|
};
|
||||||
|
@ -55,6 +64,7 @@ class StackDepotReverseMap {
|
||||||
StackDepotReverseMap(const StackDepotReverseMap&);
|
StackDepotReverseMap(const StackDepotReverseMap&);
|
||||||
void operator=(const StackDepotReverseMap&);
|
void operator=(const StackDepotReverseMap&);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace __sanitizer
|
} // namespace __sanitizer
|
||||||
|
|
||||||
#endif // SANITIZER_STACKDEPOT_H
|
#endif // SANITIZER_STACKDEPOT_H
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
//===-- sanitizer_stackdepotbase.h ------------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Implementation of a mapping from arbitrary values to unique 32-bit
|
||||||
|
// identifiers.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
#ifndef SANITIZER_STACKDEPOTBASE_H
|
||||||
|
#define SANITIZER_STACKDEPOTBASE_H
|
||||||
|
|
||||||
|
#include "sanitizer_internal_defs.h"
|
||||||
|
#include "sanitizer_mutex.h"
|
||||||
|
#include "sanitizer_atomic.h"
|
||||||
|
#include "sanitizer_persistent_allocator.h"
|
||||||
|
|
||||||
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||||
|
class StackDepotBase {
|
||||||
|
public:
|
||||||
|
typedef typename Node::args_type args_type;
|
||||||
|
typedef typename Node::handle_type handle_type;
|
||||||
|
// Maps stack trace to an unique id.
|
||||||
|
handle_type Put(args_type args, bool *inserted = 0);
|
||||||
|
// Retrieves a stored stack trace by the id.
|
||||||
|
args_type Get(u32 id);
|
||||||
|
|
||||||
|
StackDepotStats *GetStats() { return &stats; }
|
||||||
|
|
||||||
|
void LockAll();
|
||||||
|
void UnlockAll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Node *find(Node *s, args_type args, u32 hash);
|
||||||
|
static Node *lock(atomic_uintptr_t *p);
|
||||||
|
static void unlock(atomic_uintptr_t *p, Node *s);
|
||||||
|
|
||||||
|
static const int kTabSize = 1 << kTabSizeLog; // Hash table size.
|
||||||
|
static const int kPartBits = 8;
|
||||||
|
static const int kPartShift = sizeof(u32) * 8 - kPartBits - kReservedBits;
|
||||||
|
static const int kPartCount =
|
||||||
|
1 << kPartBits; // Number of subparts in the table.
|
||||||
|
static const int kPartSize = kTabSize / kPartCount;
|
||||||
|
static const int kMaxId = 1 << kPartShift;
|
||||||
|
|
||||||
|
atomic_uintptr_t tab[kTabSize]; // Hash table of Node's.
|
||||||
|
atomic_uint32_t seq[kPartCount]; // Unique id generators.
|
||||||
|
|
||||||
|
StackDepotStats stats;
|
||||||
|
|
||||||
|
friend class StackDepotReverseMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||||
|
Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::find(Node *s,
|
||||||
|
args_type args,
|
||||||
|
u32 hash) {
|
||||||
|
// Searches linked list s for the stack, returns its id.
|
||||||
|
for (; s; s = s->link) {
|
||||||
|
if (s->eq(hash, args)) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||||
|
Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::lock(
|
||||||
|
atomic_uintptr_t *p) {
|
||||||
|
// Uses the pointer lsb as mutex.
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
uptr cmp = atomic_load(p, memory_order_relaxed);
|
||||||
|
if ((cmp & 1) == 0 &&
|
||||||
|
atomic_compare_exchange_weak(p, &cmp, cmp | 1, memory_order_acquire))
|
||||||
|
return (Node *)cmp;
|
||||||
|
if (i < 10)
|
||||||
|
proc_yield(10);
|
||||||
|
else
|
||||||
|
internal_sched_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||||
|
void StackDepotBase<Node, kReservedBits, kTabSizeLog>::unlock(
|
||||||
|
atomic_uintptr_t *p, Node *s) {
|
||||||
|
DCHECK_EQ((uptr)s & 1, 0);
|
||||||
|
atomic_store(p, (uptr)s, memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||||
|
typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::handle_type
|
||||||
|
StackDepotBase<Node, kReservedBits, kTabSizeLog>::Put(args_type args,
|
||||||
|
bool *inserted) {
|
||||||
|
if (inserted) *inserted = false;
|
||||||
|
if (!args.is_valid()) return handle_type();
|
||||||
|
uptr h = args.hash();
|
||||||
|
atomic_uintptr_t *p = &tab[h % kTabSize];
|
||||||
|
uptr v = atomic_load(p, memory_order_consume);
|
||||||
|
Node *s = (Node *)(v & ~1);
|
||||||
|
// First, try to find the existing stack.
|
||||||
|
Node *node = find(s, args, h);
|
||||||
|
if (node) return node->get_handle();
|
||||||
|
// If failed, lock, retry and insert new.
|
||||||
|
Node *s2 = lock(p);
|
||||||
|
if (s2 != s) {
|
||||||
|
node = find(s2, args, h);
|
||||||
|
if (node) {
|
||||||
|
unlock(p, s2);
|
||||||
|
return node->get_handle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uptr part = (h % kTabSize) / kPartSize;
|
||||||
|
u32 id = atomic_fetch_add(&seq[part], 1, memory_order_relaxed) + 1;
|
||||||
|
stats.n_uniq_ids++;
|
||||||
|
CHECK_LT(id, kMaxId);
|
||||||
|
id |= part << kPartShift;
|
||||||
|
CHECK_NE(id, 0);
|
||||||
|
CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
|
||||||
|
uptr memsz = Node::storage_size(args);
|
||||||
|
s = (Node *)PersistentAlloc(memsz);
|
||||||
|
stats.allocated += memsz;
|
||||||
|
s->id = id;
|
||||||
|
s->store(args, h);
|
||||||
|
s->link = s2;
|
||||||
|
unlock(p, s);
|
||||||
|
if (inserted) *inserted = true;
|
||||||
|
return s->get_handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||||
|
typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::args_type
|
||||||
|
StackDepotBase<Node, kReservedBits, kTabSizeLog>::Get(u32 id) {
|
||||||
|
if (id == 0) {
|
||||||
|
return args_type();
|
||||||
|
}
|
||||||
|
CHECK_EQ(id & (((u32)-1) >> kReservedBits), id);
|
||||||
|
// High kPartBits contain part id, so we need to scan at most kPartSize lists.
|
||||||
|
uptr part = id >> kPartShift;
|
||||||
|
for (int i = 0; i != kPartSize; i++) {
|
||||||
|
uptr idx = part * kPartSize + i;
|
||||||
|
CHECK_LT(idx, kTabSize);
|
||||||
|
atomic_uintptr_t *p = &tab[idx];
|
||||||
|
uptr v = atomic_load(p, memory_order_consume);
|
||||||
|
Node *s = (Node *)(v & ~1);
|
||||||
|
for (; s; s = s->link) {
|
||||||
|
if (s->id == id) {
|
||||||
|
return s->load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||||
|
void StackDepotBase<Node, kReservedBits, kTabSizeLog>::LockAll() {
|
||||||
|
for (int i = 0; i < kTabSize; ++i) {
|
||||||
|
lock(&tab[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Node, int kReservedBits, int kTabSizeLog>
|
||||||
|
void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
|
||||||
|
for (int i = 0; i < kTabSize; ++i) {
|
||||||
|
atomic_uintptr_t *p = &tab[i];
|
||||||
|
uptr s = atomic_load(p, memory_order_relaxed);
|
||||||
|
unlock(p, (Node *)(s & ~1UL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __sanitizer
|
||||||
|
#endif // SANITIZER_STACKDEPOTBASE_H
|
|
@ -19,7 +19,8 @@ uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
|
||||||
#if defined(__arm__)
|
#if defined(__arm__)
|
||||||
// Cancel Thumb bit.
|
// Cancel Thumb bit.
|
||||||
pc = pc & (~1);
|
pc = pc & (~1);
|
||||||
#elif defined(__powerpc__) || defined(__powerpc64__)
|
#endif
|
||||||
|
#if defined(__powerpc__) || defined(__powerpc64__)
|
||||||
// PCs are always 4 byte aligned.
|
// PCs are always 4 byte aligned.
|
||||||
return pc - 4;
|
return pc - 4;
|
||||||
#elif defined(__sparc__)
|
#elif defined(__sparc__)
|
||||||
|
@ -33,19 +34,39 @@ uptr StackTrace::GetCurrentPc() {
|
||||||
return GET_CALLER_PC();
|
return GET_CALLER_PC();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if given pointer points into allocated stack area.
|
||||||
|
static inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) {
|
||||||
|
return frame > stack_bottom && frame < stack_top - 2 * sizeof (uhwptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In GCC on ARM bp points to saved lr, not fp, so we should check the next
|
||||||
|
// cell in stack to be a saved frame pointer. GetCanonicFrame returns the
|
||||||
|
// pointer to saved frame pointer in any case.
|
||||||
|
static inline uhwptr *GetCanonicFrame(uptr bp,
|
||||||
|
uptr stack_top,
|
||||||
|
uptr stack_bottom) {
|
||||||
|
#ifdef __arm__
|
||||||
|
if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0;
|
||||||
|
uhwptr *bp_prev = (uhwptr *)bp;
|
||||||
|
if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev;
|
||||||
|
return bp_prev - 1;
|
||||||
|
#else
|
||||||
|
return (uhwptr*)bp;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void StackTrace::FastUnwindStack(uptr pc, uptr bp,
|
void StackTrace::FastUnwindStack(uptr pc, uptr bp,
|
||||||
uptr stack_top, uptr stack_bottom,
|
uptr stack_top, uptr stack_bottom,
|
||||||
uptr max_depth) {
|
uptr max_depth) {
|
||||||
CHECK_GE(max_depth, 2);
|
CHECK_GE(max_depth, 2);
|
||||||
trace[0] = pc;
|
trace[0] = pc;
|
||||||
size = 1;
|
size = 1;
|
||||||
uhwptr *frame = (uhwptr *)bp;
|
|
||||||
uhwptr *prev_frame = frame - 1;
|
|
||||||
if (stack_top < 4096) return; // Sanity check for stack top.
|
if (stack_top < 4096) return; // Sanity check for stack top.
|
||||||
|
uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom);
|
||||||
|
uhwptr *prev_frame = 0;
|
||||||
// Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
|
// Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
|
||||||
while (frame > prev_frame &&
|
while (frame > prev_frame &&
|
||||||
frame < (uhwptr *)stack_top - 2 &&
|
IsValidFrame((uptr)frame, stack_top, stack_bottom) &&
|
||||||
frame > (uhwptr *)stack_bottom &&
|
|
||||||
IsAligned((uptr)frame, sizeof(*frame)) &&
|
IsAligned((uptr)frame, sizeof(*frame)) &&
|
||||||
size < max_depth) {
|
size < max_depth) {
|
||||||
uhwptr pc1 = frame[1];
|
uhwptr pc1 = frame[1];
|
||||||
|
@ -53,7 +74,7 @@ void StackTrace::FastUnwindStack(uptr pc, uptr bp,
|
||||||
trace[size++] = (uptr) pc1;
|
trace[size++] = (uptr) pc1;
|
||||||
}
|
}
|
||||||
prev_frame = frame;
|
prev_frame = frame;
|
||||||
frame = (uhwptr *)frame[0];
|
frame = GetCanonicFrame((uptr)frame[0], stack_top, stack_bottom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,9 +50,12 @@ struct StackTrace {
|
||||||
|
|
||||||
static bool WillUseFastUnwind(bool request_fast_unwind) {
|
static bool WillUseFastUnwind(bool request_fast_unwind) {
|
||||||
// Check if fast unwind is available. Fast unwind is the only option on Mac.
|
// Check if fast unwind is available. Fast unwind is the only option on Mac.
|
||||||
|
// It is also the only option on FreeBSD as the slow unwinding that
|
||||||
|
// leverages _Unwind_Backtrace() yields the call stack of the signal's
|
||||||
|
// handler and not of the code that raised the signal (as it does on Linux).
|
||||||
if (!SANITIZER_CAN_FAST_UNWIND)
|
if (!SANITIZER_CAN_FAST_UNWIND)
|
||||||
return false;
|
return false;
|
||||||
else if (SANITIZER_MAC)
|
else if (SANITIZER_MAC != 0 || SANITIZER_FREEBSD != 0)
|
||||||
return true;
|
return true;
|
||||||
return request_fast_unwind;
|
return request_fast_unwind;
|
||||||
}
|
}
|
||||||
|
@ -83,6 +86,10 @@ struct StackTrace {
|
||||||
uptr local_stack; \
|
uptr local_stack; \
|
||||||
uptr sp = (uptr)&local_stack
|
uptr sp = (uptr)&local_stack
|
||||||
|
|
||||||
|
#define GET_CALLER_PC_BP \
|
||||||
|
uptr bp = GET_CURRENT_FRAME(); \
|
||||||
|
uptr pc = GET_CALLER_PC();
|
||||||
|
|
||||||
// Use this macro if you want to print stack trace with the current
|
// Use this macro if you want to print stack trace with the current
|
||||||
// function in the top frame.
|
// function in the top frame.
|
||||||
#define GET_CURRENT_PC_BP_SP \
|
#define GET_CURRENT_PC_BP_SP \
|
||||||
|
|
|
@ -35,6 +35,14 @@ void StackTrace::PrintStack(const uptr *addr, uptr size) {
|
||||||
uptr pc = GetPreviousInstructionPc(addr[i]);
|
uptr pc = GetPreviousInstructionPc(addr[i]);
|
||||||
uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
|
uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
|
||||||
pc, addr_frames.data(), addr_frames.size());
|
pc, addr_frames.data(), addr_frames.size());
|
||||||
|
if (addr_frames_num == 0) {
|
||||||
|
frame_desc.clear();
|
||||||
|
PrintStackFramePrefix(&frame_desc, frame_num, pc);
|
||||||
|
frame_desc.append(" (<unknown module>)");
|
||||||
|
Printf("%s\n", frame_desc.data());
|
||||||
|
frame_num++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (uptr j = 0; j < addr_frames_num; j++) {
|
for (uptr j = 0; j < addr_frames_num; j++) {
|
||||||
AddressInfo &info = addr_frames[j];
|
AddressInfo &info = addr_frames[j];
|
||||||
frame_desc.clear();
|
frame_desc.clear();
|
||||||
|
|
|
@ -13,13 +13,15 @@
|
||||||
|
|
||||||
#include "sanitizer_allocator_internal.h"
|
#include "sanitizer_allocator_internal.h"
|
||||||
#include "sanitizer_common.h"
|
#include "sanitizer_common.h"
|
||||||
|
#include "sanitizer_flags.h"
|
||||||
#include "sanitizer_libc.h"
|
#include "sanitizer_libc.h"
|
||||||
|
#include "sanitizer_placement_new.h"
|
||||||
|
|
||||||
namespace __sanitizer {
|
namespace __sanitizer {
|
||||||
|
|
||||||
static const char *const kTypeStrings[SuppressionTypeCount] = {
|
static const char *const kTypeStrings[SuppressionTypeCount] = {
|
||||||
"none", "race", "mutex", "thread",
|
"none", "race", "mutex", "thread", "signal",
|
||||||
"signal", "leak", "called_from_lib", "deadlock"};
|
"leak", "called_from_lib", "deadlock", "vptr_check"};
|
||||||
|
|
||||||
bool TemplateMatch(char *templ, const char *str) {
|
bool TemplateMatch(char *templ, const char *str) {
|
||||||
if (str == 0 || str[0] == 0)
|
if (str == 0 || str[0] == 0)
|
||||||
|
@ -63,6 +65,33 @@ bool TemplateMatch(char *templ, const char *str) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALIGNED(64) static char placeholder[sizeof(SuppressionContext)];
|
||||||
|
static SuppressionContext *suppression_ctx = 0;
|
||||||
|
|
||||||
|
SuppressionContext *SuppressionContext::Get() {
|
||||||
|
CHECK(suppression_ctx);
|
||||||
|
return suppression_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuppressionContext::InitIfNecessary() {
|
||||||
|
if (suppression_ctx)
|
||||||
|
return;
|
||||||
|
suppression_ctx = new(placeholder) SuppressionContext;
|
||||||
|
if (common_flags()->suppressions[0] == '\0')
|
||||||
|
return;
|
||||||
|
char *suppressions_from_file;
|
||||||
|
uptr buffer_size;
|
||||||
|
uptr contents_size =
|
||||||
|
ReadFileToBuffer(common_flags()->suppressions, &suppressions_from_file,
|
||||||
|
&buffer_size, 1 << 26 /* max_len */);
|
||||||
|
if (contents_size == 0) {
|
||||||
|
Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
|
||||||
|
common_flags()->suppressions);
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
suppression_ctx->Parse(suppressions_from_file);
|
||||||
|
}
|
||||||
|
|
||||||
bool SuppressionContext::Match(const char *str, SuppressionType type,
|
bool SuppressionContext::Match(const char *str, SuppressionType type,
|
||||||
Suppression **s) {
|
Suppression **s) {
|
||||||
can_parse_ = false;
|
can_parse_ = false;
|
||||||
|
|
|
@ -25,6 +25,7 @@ enum SuppressionType {
|
||||||
SuppressionLeak,
|
SuppressionLeak,
|
||||||
SuppressionLib,
|
SuppressionLib,
|
||||||
SuppressionDeadlock,
|
SuppressionDeadlock,
|
||||||
|
SuppressionVptrCheck,
|
||||||
SuppressionTypeCount
|
SuppressionTypeCount
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,14 +38,21 @@ struct Suppression {
|
||||||
|
|
||||||
class SuppressionContext {
|
class SuppressionContext {
|
||||||
public:
|
public:
|
||||||
SuppressionContext() : suppressions_(1), can_parse_(true) {}
|
|
||||||
void Parse(const char *str);
|
void Parse(const char *str);
|
||||||
bool Match(const char* str, SuppressionType type, Suppression **s);
|
bool Match(const char* str, SuppressionType type, Suppression **s);
|
||||||
uptr SuppressionCount() const;
|
uptr SuppressionCount() const;
|
||||||
const Suppression *SuppressionAt(uptr i) const;
|
const Suppression *SuppressionAt(uptr i) const;
|
||||||
void GetMatched(InternalMmapVector<Suppression *> *matched);
|
void GetMatched(InternalMmapVector<Suppression *> *matched);
|
||||||
|
|
||||||
|
// Create a SuppressionContext singleton if it hasn't been created earlier.
|
||||||
|
// Not thread safe. Must be called early during initialization (but after
|
||||||
|
// runtime flags are parsed).
|
||||||
|
static void InitIfNecessary();
|
||||||
|
// Returns a SuppressionContext singleton.
|
||||||
|
static SuppressionContext *Get();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SuppressionContext() : suppressions_(1), can_parse_(true) {}
|
||||||
InternalMmapVector<Suppression> suppressions_;
|
InternalMmapVector<Suppression> suppressions_;
|
||||||
bool can_parse_;
|
bool can_parse_;
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue