[libsanitizer merge from upstream r218156]
From-SVN: r215527
This commit is contained in:
parent
e8ee40544a
commit
866e32ad33
163 changed files with 6382 additions and 3778 deletions
|
@ -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>
|
||||
|
||||
* 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.
|
||||
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
|
||||
|
@ -2136,19 +2139,20 @@ asan_dynamic_init_call (bool after_p)
|
|||
const void *__name;
|
||||
const void *__module_name;
|
||||
uptr __has_dynamic_init;
|
||||
__asan_global_source_location *__location;
|
||||
} type. */
|
||||
|
||||
static tree
|
||||
asan_global_struct (void)
|
||||
{
|
||||
static const char *field_names[6]
|
||||
static const char *field_names[7]
|
||||
= { "__beg", "__size", "__size_with_redzone",
|
||||
"__name", "__module_name", "__has_dynamic_init" };
|
||||
tree fields[6], ret;
|
||||
"__name", "__module_name", "__has_dynamic_init", "__location"};
|
||||
tree fields[7], ret;
|
||||
int i;
|
||||
|
||||
ret = make_node (RECORD_TYPE);
|
||||
for (i = 0; i < 6; i++)
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
fields[i]
|
||||
= 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;
|
||||
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
|
||||
build_int_cst (uptr, has_dynamic_init));
|
||||
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
|
||||
build_int_cst (uptr, 0));
|
||||
init = build_constructor (type, vinner);
|
||||
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. */
|
||||
|
||||
/* 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)
|
||||
/* Do not reorder the BUILT_IN_ASAN_{REPORT,CHECK}* builtins, e.g. cfgcleanup.c
|
||||
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>
|
||||
|
||||
* 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
|
||||
last merge done from the master library sources.
|
||||
|
|
|
@ -17,7 +17,7 @@ nodist_toolexeclib_HEADERS = libasan_preinit.o
|
|||
asan_files = \
|
||||
asan_activation.cc \
|
||||
asan_allocator2.cc \
|
||||
asan_dll_thunk.cc \
|
||||
asan_debugging.cc \
|
||||
asan_fake_stack.cc \
|
||||
asan_globals.cc \
|
||||
asan_interceptors.cc \
|
||||
|
@ -34,7 +34,9 @@ asan_files = \
|
|||
asan_stack.cc \
|
||||
asan_stats.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_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) \
|
||||
$(am__append_3) $(am__DEPENDENCIES_1)
|
||||
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_malloc_linux.lo asan_malloc_mac.lo asan_malloc_win.lo \
|
||||
asan_new_delete.lo asan_poisoning.lo asan_posix.lo \
|
||||
asan_report.lo asan_rtl.lo asan_stack.lo asan_stats.lo \
|
||||
asan_thread.lo asan_win.lo
|
||||
asan_thread.lo asan_win.lo asan_win_dll_thunk.lo \
|
||||
asan_win_dynamic_runtime_thunk.lo
|
||||
am_libasan_la_OBJECTS = $(am__objects_1)
|
||||
libasan_la_OBJECTS = $(am_libasan_la_OBJECTS)
|
||||
libasan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
|
||||
|
@ -275,7 +276,7 @@ nodist_toolexeclib_HEADERS = libasan_preinit.o
|
|||
asan_files = \
|
||||
asan_activation.cc \
|
||||
asan_allocator2.cc \
|
||||
asan_dll_thunk.cc \
|
||||
asan_debugging.cc \
|
||||
asan_fake_stack.cc \
|
||||
asan_globals.cc \
|
||||
asan_interceptors.cc \
|
||||
|
@ -292,7 +293,9 @@ asan_files = \
|
|||
asan_stack.cc \
|
||||
asan_stats.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_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_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_globals.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_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_dll_thunk.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@
|
||||
|
||||
.cc.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,
|
||||
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_calloc(uptr nmemb, uptr size, StackTrace *stack);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_interface.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
#include "sanitizer_common/sanitizer_list.h"
|
||||
|
@ -165,23 +166,6 @@ struct AsanChunk: ChunkBase {
|
|||
}
|
||||
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) {
|
||||
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);
|
||||
if (p == 0) return;
|
||||
|
||||
uptr chunk_beg = p - kChunkHeaderSize;
|
||||
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);
|
||||
// Must mark the chunk as quarantined before any changes to its metadata.
|
||||
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.
|
||||
// However, we will report racy double-free later anyway.
|
||||
REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
|
||||
Deallocate(old_ptr, stack, FROM_MALLOC);
|
||||
Deallocate(old_ptr, 0, stack, FROM_MALLOC);
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
|
@ -614,7 +608,7 @@ void *asan_realloc(void *p, uptr size, StackTrace *stack) {
|
|||
if (p == 0)
|
||||
return Allocate(size, 8, stack, FROM_MALLOC, true);
|
||||
if (size == 0) {
|
||||
Deallocate(p, stack, FROM_MALLOC);
|
||||
Deallocate(p, 0, stack, FROM_MALLOC);
|
||||
return 0;
|
||||
}
|
||||
return Reallocate(p, size, stack);
|
||||
|
@ -758,23 +752,23 @@ using namespace __asan; // NOLINT
|
|||
|
||||
// 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.
|
||||
uptr __asan_get_estimated_allocated_size(uptr size) {
|
||||
uptr __sanitizer_get_estimated_allocated_size(uptr size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
int __asan_get_ownership(const void *p) {
|
||||
int __sanitizer_get_ownership(const void *p) {
|
||||
uptr ptr = reinterpret_cast<uptr>(p);
|
||||
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;
|
||||
uptr ptr = reinterpret_cast<uptr>(p);
|
||||
uptr allocated_size = AllocationSize(ptr);
|
||||
// Die if p is not malloced or if it is already freed.
|
||||
if (allocated_size == 0) {
|
||||
GET_STACK_TRACE_FATAL_HERE;
|
||||
ReportAsanGetAllocatedSizeNotOwned(ptr, &stack);
|
||||
ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack);
|
||||
}
|
||||
return allocated_size;
|
||||
}
|
||||
|
@ -783,12 +777,12 @@ uptr __asan_get_allocated_size(const void *p) {
|
|||
// Provide default (no-op) implementation of malloc hooks.
|
||||
extern "C" {
|
||||
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)size;
|
||||
}
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __asan_free_hook(void *ptr) {
|
||||
void __sanitizer_free_hook(void *ptr) {
|
||||
(void)ptr;
|
||||
}
|
||||
} // 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__
|
72
libsanitizer/asan/asan_debugging.cc
Normal file
72
libsanitizer/asan/asan_debugging.cc
Normal file
|
@ -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_legend;
|
||||
bool atexit;
|
||||
bool disable_core;
|
||||
bool allow_reexec;
|
||||
bool print_full_thread_history;
|
||||
bool poison_heap;
|
||||
bool poison_partial;
|
||||
bool poison_array_cookie;
|
||||
bool alloc_dealloc_mismatch;
|
||||
bool new_delete_type_mismatch;
|
||||
bool strict_memcmp;
|
||||
bool strict_init_order;
|
||||
bool start_deactivated;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include "sanitizer_common/sanitizer_placement_new.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
|
@ -43,6 +44,14 @@ typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals;
|
|||
// Lazy-initialized and never deleted.
|
||||
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) {
|
||||
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) {
|
||||
Report("%s Global: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
|
||||
prefix, (void*)g.beg, g.size, g.size_with_redzone, g.name,
|
||||
Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
|
||||
prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
|
||||
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) {
|
||||
|
@ -79,6 +93,16 @@ bool DescribeAddressIfGlobal(uptr addr, uptr size) {
|
|||
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.
|
||||
// This function may be called more than once for every global
|
||||
// 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) {
|
||||
if (g->beg == l->g->beg &&
|
||||
(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.
|
||||
void __asan_register_globals(__asan_global *globals, uptr n) {
|
||||
if (!flags()->report_globals) return;
|
||||
GET_STACK_TRACE_FATAL_HERE;
|
||||
u32 stack_id = StackDepotPut(stack.trace, stack.size);
|
||||
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++) {
|
||||
RegisterGlobal(&globals[i]);
|
||||
}
|
||||
|
|
30
libsanitizer/asan/asan_init_version.h
Normal file
30
libsanitizer/asan/asan_init_version.h
Normal file
|
@ -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)
|
||||
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
|
||||
#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"
|
||||
|
||||
#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
|
||||
|
||||
// intercept mlock and friends.
|
||||
// Since asan maps 16T of RAM, mlock is completely unfriendly to asan.
|
||||
// All functions return 0 (success).
|
||||
static void MlockIsUnsupported() {
|
||||
static bool printed = false;
|
||||
if (printed) return;
|
||||
printed = true;
|
||||
VPrintf(1,
|
||||
"INFO: AddressSanitizer ignores "
|
||||
"mlock/mlockall/munlock/munlockall\n");
|
||||
#if SANITIZER_WINDOWS
|
||||
INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(RaiseException));
|
||||
__asan_handle_no_return();
|
||||
REAL(RaiseException)(a, b, c, d);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, mlock, const void *addr, uptr len) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(_except_handler3));
|
||||
__asan_handle_no_return();
|
||||
return REAL(_except_handler3)(a, b, c, d);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, munlock, const void *addr, uptr len) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, mlockall, int flags) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, munlockall, void) {
|
||||
MlockIsUnsupported();
|
||||
return 0;
|
||||
#if ASAN_DYNAMIC
|
||||
// This handler is named differently in -MT and -MD CRTs.
|
||||
#define _except_handler4 _except_handler4_common
|
||||
#endif
|
||||
INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
|
||||
CHECK(REAL(_except_handler4));
|
||||
__asan_handle_no_return();
|
||||
return REAL(_except_handler4)(a, b, c, d);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int CharCmp(unsigned char c1, unsigned char c2) {
|
||||
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
|
||||
|
@ -523,7 +518,7 @@ INTERCEPTOR(char*, strdup, const char *s) {
|
|||
}
|
||||
#endif
|
||||
|
||||
INTERCEPTOR(uptr, strlen, const char *s) {
|
||||
INTERCEPTOR(SIZE_T, strlen, const char *s) {
|
||||
if (UNLIKELY(!asan_inited)) return internal_strlen(s);
|
||||
// strlen is called from malloc_default_purgeable_zone()
|
||||
// in __asan::ReplaceSystemAlloc() on Mac.
|
||||
|
@ -531,15 +526,15 @@ INTERCEPTOR(uptr, strlen, const char *s) {
|
|||
return REAL(strlen)(s);
|
||||
}
|
||||
ENSURE_ASAN_INITED();
|
||||
uptr length = REAL(strlen)(s);
|
||||
SIZE_T length = REAL(strlen)(s);
|
||||
if (flags()->replace_str) {
|
||||
ASAN_READ_RANGE(s, length + 1);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
INTERCEPTOR(uptr, wcslen, const wchar_t *s) {
|
||||
uptr length = REAL(wcslen)(s);
|
||||
INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) {
|
||||
SIZE_T length = REAL(wcslen)(s);
|
||||
if (!asan_init_is_running) {
|
||||
ENSURE_ASAN_INITED();
|
||||
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
|
||||
|
||||
#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
|
||||
INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
||||
void* security, uptr stack_size,
|
||||
|
@ -712,6 +717,9 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
|
|||
namespace __asan {
|
||||
void InitializeWindowsInterceptors() {
|
||||
ASAN_INTERCEPT_FUNC(CreateThread);
|
||||
ASAN_INTERCEPT_FUNC(RaiseException);
|
||||
ASAN_INTERCEPT_FUNC(_except_handler3);
|
||||
ASAN_INTERCEPT_FUNC(_except_handler4);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
@ -759,14 +767,6 @@ void InitializeAsanInterceptors() {
|
|||
ASAN_INTERCEPT_FUNC(strtoll);
|
||||
#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.
|
||||
ASAN_INTERCEPT_FUNC(longjmp);
|
||||
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
|
||||
|
@ -789,7 +789,7 @@ void InitializeAsanInterceptors() {
|
|||
|
||||
// Intercept exception handling functions.
|
||||
#if ASAN_INTERCEPT___CXA_THROW
|
||||
INTERCEPT_FUNCTION(__cxa_throw);
|
||||
ASAN_INTERCEPT_FUNC(__cxa_throw);
|
||||
#endif
|
||||
|
||||
// Intercept threading-related functions
|
||||
|
@ -802,6 +802,10 @@ void InitializeAsanInterceptors() {
|
|||
ASAN_INTERCEPT_FUNC(__cxa_atexit);
|
||||
#endif
|
||||
|
||||
#if ASAN_INTERCEPT_FORK
|
||||
ASAN_INTERCEPT_FUNC(fork);
|
||||
#endif
|
||||
|
||||
// Some Windows-specific interceptors.
|
||||
#if SANITIZER_WINDOWS
|
||||
InitializeWindowsInterceptors();
|
||||
|
|
|
@ -24,14 +24,14 @@
|
|||
# define ASAN_INTERCEPT_STRDUP 1
|
||||
# define ASAN_INTERCEPT_INDEX 1
|
||||
# define ASAN_INTERCEPT_PTHREAD_CREATE 1
|
||||
# define ASAN_INTERCEPT_MLOCKX 1
|
||||
# define ASAN_INTERCEPT_FORK 1
|
||||
#else
|
||||
# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
|
||||
# define ASAN_INTERCEPT__LONGJMP 0
|
||||
# define ASAN_INTERCEPT_STRDUP 0
|
||||
# define ASAN_INTERCEPT_INDEX 0
|
||||
# define ASAN_INTERCEPT_PTHREAD_CREATE 0
|
||||
# define ASAN_INTERCEPT_MLOCKX 0
|
||||
# define ASAN_INTERCEPT_FORK 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
|
@ -64,7 +64,9 @@
|
|||
# define ASAN_INTERCEPT_SIGLONGJMP 0
|
||||
#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
|
||||
#else
|
||||
# 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*, memset, void *block, int c, uptr size)
|
||||
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(uptr, strnlen, const char *s, uptr maxlen)
|
||||
DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
|
||||
|
|
|
@ -15,21 +15,24 @@
|
|||
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
|
||||
#include "asan_init_version.h"
|
||||
|
||||
using __sanitizer::uptr;
|
||||
|
||||
extern "C" {
|
||||
// This function should be called at the very beginning of the process,
|
||||
// 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
|
||||
// name. Objects build with incompatible asan ABI version
|
||||
// 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).
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3();
|
||||
#define __asan_init __asan_init_v3
|
||||
// Please note that __asan_init is a macro that is replaced with
|
||||
// __asan_init_vXXX at compile-time.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_init();
|
||||
|
||||
// This structure is used to describe the source location of a place where
|
||||
// global was defined.
|
||||
struct __asan_global_source_location {
|
||||
const char *filename;
|
||||
int line_no;
|
||||
int column_no;
|
||||
};
|
||||
|
||||
// This structure describes an instrumented global variable.
|
||||
struct __asan_global {
|
||||
|
@ -40,6 +43,8 @@ extern "C" {
|
|||
const char *module_name; // Module name as a C string. This pointer is a
|
||||
// unique identifier of a module.
|
||||
uptr has_dynamic_init; // Non-zero if the global has dynamic initializer.
|
||||
__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.
|
||||
|
@ -83,6 +88,17 @@ extern "C" {
|
|||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
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
|
||||
void __asan_report_error(uptr pc, uptr bp, uptr sp,
|
||||
uptr addr, int is_write, uptr access_size);
|
||||
|
@ -97,25 +113,11 @@ extern "C" {
|
|||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
/* 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 SANITIZER_WEAK_ATTRIBUTE
|
||||
/* OPTIONAL */ const char* __asan_default_options();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
/* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
/* OPTIONAL */ void __asan_free_hook(void *ptr);
|
||||
|
||||
// Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
extern int __asan_option_detect_stack_use_after_return;
|
||||
|
@ -142,6 +144,11 @@ extern "C" {
|
|||
void* __asan_memset(void *s, int c, uptr n);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
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"
|
||||
|
||||
#endif // ASAN_INTERFACE_INTERNAL_H
|
||||
|
|
|
@ -43,10 +43,6 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ASAN_USE_PREINIT_ARRAY
|
||||
# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID)
|
||||
#endif
|
||||
|
||||
#ifndef ASAN_DYNAMIC
|
||||
# ifdef PIC
|
||||
# define ASAN_DYNAMIC 1
|
||||
|
@ -96,6 +92,8 @@ void AppendToErrorMessageBuffer(const char *buffer);
|
|||
|
||||
void ParseExtraActivationFlags();
|
||||
|
||||
void *AsanDlSymNext(const char *sym);
|
||||
|
||||
// Platform-specific options.
|
||||
#if SANITIZER_MAC
|
||||
bool PlatformHasDifferentMemcpyAndMemmove();
|
||||
|
@ -108,9 +106,9 @@ bool PlatformHasDifferentMemcpyAndMemmove();
|
|||
// Add convenient macro for interface functions that may be represented as
|
||||
// weak hooks.
|
||||
#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) \
|
||||
if (&__asan_free_hook) __asan_free_hook(ptr)
|
||||
if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr)
|
||||
#define ASAN_ON_ERROR() \
|
||||
if (&__asan_on_error) __asan_on_error()
|
||||
|
||||
|
@ -134,6 +132,7 @@ const int kAsanContiguousContainerOOBMagic = 0xfc;
|
|||
const int kAsanStackUseAfterScopeMagic = 0xf8;
|
||||
const int kAsanGlobalRedzoneMagic = 0xf9;
|
||||
const int kAsanInternalHeapMagic = 0xfe;
|
||||
const int kAsanArrayCookieMagic = 0xac;
|
||||
|
||||
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
|
||||
static const uptr kRetiredStackFrameMagic = 0x45E0360E;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "asan_internal.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_freebsd.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_procmaps.h"
|
||||
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
@ -40,19 +42,14 @@
|
|||
extern "C" void* _DYNAMIC;
|
||||
#else
|
||||
#include <sys/ucontext.h>
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
#endif
|
||||
|
||||
// x86_64 FreeBSD 9.2 and older define 64-bit register names in both 64-bit
|
||||
// and 32-bit modes.
|
||||
#if SANITIZER_FREEBSD
|
||||
#include <sys/param.h>
|
||||
# if __FreeBSD_version <= 902001 // v9.2
|
||||
# define mc_eip mc_rip
|
||||
# define mc_ebp mc_rbp
|
||||
# define mc_esp mc_rsp
|
||||
# endif
|
||||
// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
|
||||
// 32-bit mode.
|
||||
#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \
|
||||
__FreeBSD_version <= 902001 // v9.2
|
||||
#define ucontext_t xucontext_t
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
|
@ -241,6 +238,10 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void *AsanDlSymNext(const char *sym) {
|
||||
return dlsym(RTLD_NEXT, sym);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
|
|
|
@ -372,32 +372,44 @@ void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
|
|||
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,
|
||||
dispatch_queue_t dq, void(^work)(void)) {
|
||||
ENABLE_FRAME_POINTER;
|
||||
GET_ASAN_BLOCK(work);
|
||||
REAL(dispatch_async)(dq, asan_block);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, dispatch_group_async,
|
||||
dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
|
||||
ENABLE_FRAME_POINTER;
|
||||
GET_ASAN_BLOCK(work);
|
||||
REAL(dispatch_group_async)(dg, dq, asan_block);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, dispatch_after,
|
||||
dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
|
||||
ENABLE_FRAME_POINTER;
|
||||
GET_ASAN_BLOCK(work);
|
||||
REAL(dispatch_after)(when, queue, asan_block);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, dispatch_source_set_cancel_handler,
|
||||
dispatch_source_t ds, void(^work)(void)) {
|
||||
ENABLE_FRAME_POINTER;
|
||||
GET_ASAN_BLOCK(work);
|
||||
REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, dispatch_source_set_event_handler,
|
||||
dispatch_source_t ds, void(^work)(void)) {
|
||||
ENABLE_FRAME_POINTER;
|
||||
GET_ASAN_BLOCK(work);
|
||||
REAL(dispatch_source_set_event_handler)(ds, asan_block);
|
||||
}
|
||||
|
|
|
@ -21,41 +21,6 @@
|
|||
#include "asan_internal.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
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
|
@ -100,6 +65,11 @@ INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
|
|||
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) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
|
||||
|
@ -151,4 +121,64 @@ INTERCEPTOR(void, malloc_stats, void) {
|
|||
__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
|
||||
|
|
|
@ -21,71 +21,77 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
// ---------------------- Replacement functions ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
// FIXME: Simply defining functions with the same signature in *.obj
|
||||
// files overrides the standard functions in *.lib
|
||||
// This works well for simple helloworld-like tests but might need to be
|
||||
// revisited in the future.
|
||||
// MT: Simply defining functions with the same signature in *.obj
|
||||
// files overrides the standard functions in the CRT.
|
||||
// MD: Memory allocation functions are defined in the CRT .dll,
|
||||
// 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" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void free(void *ptr) {
|
||||
GET_STACK_TRACE_FREE;
|
||||
return asan_free(ptr, &stack, FROM_MALLOC);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void _free_dbg(void* ptr, int) {
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void _free_dbg(void *ptr, int) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
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) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_malloc(size, &stack);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void* _malloc_dbg(size_t size, int , const char*, int) {
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *_malloc_dbg(size_t size, int, const char *, int) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *calloc(size_t nmemb, size_t size) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_calloc(nmemb, size, &stack);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void* _calloc_dbg(size_t n, size_t size, int, const char*, int) {
|
||||
return calloc(n, size);
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
|
||||
return calloc(nmemb, size);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
|
||||
return calloc(nmemb, size);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *realloc(void *ptr, size_t size) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_realloc(ptr, size, &stack);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *_realloc_dbg(void *ptr, size_t size, int) {
|
||||
CHECK(!"_realloc_dbg should not exist!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void* _recalloc(void* p, size_t n, size_t elem_size) {
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *_recalloc(void *p, size_t n, size_t elem_size) {
|
||||
if (!p)
|
||||
return calloc(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);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
size_t _msize(void *ptr) {
|
||||
GET_CURRENT_PC_BP_SP;
|
||||
(void)sp;
|
||||
return asan_malloc_usable_size(ptr, pc, bp);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *_expand(void *memblock, size_t size) {
|
||||
// _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.
|
||||
return 0;
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
ALLOCATION_FUNCTION_ATTRIBUTE
|
||||
void *_expand_dbg(void *memblock, size_t size) {
|
||||
return 0;
|
||||
return _expand(memblock, size);
|
||||
}
|
||||
|
||||
// TODO(timurrrr): Might want to add support for _aligned_* allocation
|
||||
|
@ -131,37 +137,38 @@ int _CrtSetReportMode(int, int) {
|
|||
}
|
||||
} // 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 {
|
||||
void ReplaceSystemMalloc() {
|
||||
#if defined(_DLL)
|
||||
# ifdef _WIN64
|
||||
# error ReplaceSystemMalloc was not tested on x64
|
||||
# endif
|
||||
char *crt_malloc;
|
||||
if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) {
|
||||
// Replace malloc in the CRT dll with a jump to our malloc.
|
||||
DWORD old_prot, unused;
|
||||
CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot));
|
||||
REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case.
|
||||
#if defined(ASAN_DYNAMIC)
|
||||
// We don't check the result because CRT might not be used in the process.
|
||||
__interception::OverrideFunction("free", (uptr)free);
|
||||
__interception::OverrideFunction("malloc", (uptr)malloc);
|
||||
__interception::OverrideFunction("_malloc_crt", (uptr)malloc);
|
||||
__interception::OverrideFunction("calloc", (uptr)calloc);
|
||||
__interception::OverrideFunction("_calloc_crt", (uptr)calloc);
|
||||
__interception::OverrideFunction("realloc", (uptr)realloc);
|
||||
__interception::OverrideFunction("_realloc_crt", (uptr)realloc);
|
||||
__interception::OverrideFunction("_recalloc", (uptr)_recalloc);
|
||||
__interception::OverrideFunction("_recalloc_crt", (uptr)_recalloc);
|
||||
__interception::OverrideFunction("_msize", (uptr)_msize);
|
||||
__interception::OverrideFunction("_expand", (uptr)_expand);
|
||||
|
||||
ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5;
|
||||
crt_malloc[0] = 0xE9; // jmp, should be followed by an offset.
|
||||
REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset));
|
||||
|
||||
CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused));
|
||||
|
||||
// FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64.
|
||||
}
|
||||
|
||||
// FIXME: investigate whether anything else is needed.
|
||||
// Override different versions of 'operator new' and 'operator delete'.
|
||||
// No need to override the nothrow versions as they just wrap the throw
|
||||
// versions.
|
||||
// FIXME: Unfortunately, MSVC miscompiles the statements that take the
|
||||
// addresses of the array versions of these operators,
|
||||
// see https://connect.microsoft.com/VisualStudio/feedbackdetail/view/946992
|
||||
// We might want to try to work around this by [inline] assembly or compiling
|
||||
// parts of the RTL with Clang.
|
||||
void *(*op_new)(size_t sz) = operator new;
|
||||
void (*op_delete)(void *p) = operator delete;
|
||||
void *(*op_array_new)(size_t sz) = operator new[];
|
||||
void (*op_array_delete)(void *p) = operator delete[];
|
||||
__interception::OverrideFunction("??2@YAPAXI@Z", (uptr)op_new);
|
||||
__interception::OverrideFunction("??3@YAXPAX@Z", (uptr)op_delete);
|
||||
__interception::OverrideFunction("??_U@YAPAXI@Z", (uptr)op_array_new);
|
||||
__interception::OverrideFunction("??_V@YAXPAX@Z", (uptr)op_array_delete);
|
||||
#endif
|
||||
}
|
||||
} // namespace __asan
|
||||
|
|
|
@ -18,6 +18,13 @@
|
|||
|
||||
#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
|
||||
|
||||
// This code has issues on OSX.
|
||||
|
@ -49,14 +56,14 @@ struct nothrow_t {};
|
|||
#endif // __FreeBSD_version
|
||||
#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
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); }
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void *operator new(size_t size, std::nothrow_t const&)
|
||||
{ OPERATOR_NEW_BODY(FROM_NEW); }
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void *operator new[](size_t size, std::nothrow_t const&)
|
||||
{ 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);
|
||||
|
||||
#if !SANITIZER_MAC
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete(void *ptr) throw() {
|
||||
OPERATOR_DELETE_BODY(FROM_NEW);
|
||||
}
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete[](void *ptr) throw() {
|
||||
OPERATOR_DELETE_BODY(FROM_NEW_BR);
|
||||
}
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete(void *ptr, std::nothrow_t const&) {
|
||||
OPERATOR_DELETE_BODY(FROM_NEW);
|
||||
}
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete[](void *ptr, std::nothrow_t const&) {
|
||||
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
|
||||
INTERCEPTOR(void, _ZdlPv, void *ptr) {
|
||||
|
|
|
@ -225,6 +225,35 @@ void __sanitizer_unaligned_store64(uu64 *p, u64 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
|
||||
// assumes that left border of region to be poisoned is properly aligned.
|
||||
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,
|
||||
u8 value) {
|
||||
DCHECK(flags()->poison_heap);
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
uptr shadow_beg = MEM_TO_SHADOW(aligned_beg);
|
||||
uptr shadow_end = MEM_TO_SHADOW(
|
||||
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) {
|
||||
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
|
||||
} else {
|
||||
uptr page_beg = RoundUpTo(shadow_beg, PageSize);
|
||||
uptr page_end = RoundDownTo(shadow_end, PageSize);
|
||||
uptr page_size = GetPageSizeCached();
|
||||
uptr page_beg = RoundUpTo(shadow_beg, page_size);
|
||||
uptr page_end = RoundDownTo(shadow_end, page_size);
|
||||
|
||||
if (page_beg >= page_end) {
|
||||
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))
|
||||
ReportStackOverflow(pc, sp, bp, context, addr);
|
||||
else
|
||||
ReportSIGSEGV(pc, sp, bp, context, addr);
|
||||
ReportSIGSEGV("SEGV", pc, sp, bp, context, addr);
|
||||
}
|
||||
|
||||
// ---------------------- TSD ---------------- {{{1
|
||||
|
|
|
@ -8,22 +8,12 @@
|
|||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// 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"
|
||||
|
||||
#if ASAN_USE_PREINIT_ARRAY && !defined(PIC)
|
||||
// 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?
|
||||
#if SANITIZER_CAN_USE_PREINIT_ARRAY
|
||||
// The symbol is called __local_asan_preinit, because it's not intended to be
|
||||
// exported.
|
||||
__attribute__((section(".preinit_array"), used))
|
||||
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
|
||||
|
|
|
@ -57,6 +57,7 @@ class Decorator: public __sanitizer::SanitizerCommonDecorator {
|
|||
switch (byte) {
|
||||
case kAsanHeapLeftRedzoneMagic:
|
||||
case kAsanHeapRightRedzoneMagic:
|
||||
case kAsanArrayCookieMagic:
|
||||
return Red();
|
||||
case kAsanHeapFreeMagic:
|
||||
return Magenta();
|
||||
|
@ -141,6 +142,8 @@ static void PrintLegend(InternalScopedString *str) {
|
|||
kAsanUserPoisonedMemoryMagic);
|
||||
PrintShadowByte(str, " Container overflow: ",
|
||||
kAsanContiguousContainerOOBMagic);
|
||||
PrintShadowByte(str, " Array cookie: ",
|
||||
kAsanArrayCookieMagic);
|
||||
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] == '?')
|
||||
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.
|
||||
|
@ -210,6 +213,26 @@ static void PrintGlobalNameIfASCII(InternalScopedString *str,
|
|||
(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,
|
||||
const __asan_global &g) {
|
||||
static const uptr kMinimalDistanceFromAnotherGlobal = 64;
|
||||
|
@ -230,8 +253,10 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
|||
// Can it happen?
|
||||
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",
|
||||
MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size);
|
||||
str.append(" of global variable '%s' defined in '",
|
||||
MaybeDemangleGlobalName(g.name));
|
||||
PrintGlobalLocation(&str, g);
|
||||
str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
|
||||
str.append("%s", d.EndLocation());
|
||||
PrintGlobalNameIfASCII(&str, g);
|
||||
Printf("%s", str.data());
|
||||
|
@ -317,12 +342,27 @@ void PrintAccessAndVarIntersection(const char *var_name,
|
|||
Printf("%s", str.data());
|
||||
}
|
||||
|
||||
struct StackVarDescr {
|
||||
uptr beg;
|
||||
uptr size;
|
||||
const char *name_pos;
|
||||
uptr name_len;
|
||||
};
|
||||
bool ParseFrameDescription(const char *frame_descr,
|
||||
InternalMmapVector<StackVarDescr> *vars) {
|
||||
char *p;
|
||||
uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
|
||||
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) {
|
||||
AsanThread *t = FindThreadByStackAddress(addr);
|
||||
|
@ -364,32 +404,19 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
|||
alloca_stack.size = 1;
|
||||
Printf("%s", d.EndLocation());
|
||||
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.
|
||||
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);
|
||||
|
||||
// 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++) {
|
||||
buf[0] = 0;
|
||||
internal_strncat(buf, vars[i].name_pos,
|
||||
|
@ -401,8 +428,12 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
|||
prev_var_end, next_var_beg);
|
||||
}
|
||||
Printf("HINT: this may be a false positive if your program uses "
|
||||
"some custom stack unwind mechanism or swapcontext\n"
|
||||
" (longjmp and C++ exceptions *are* supported)\n");
|
||||
"some custom stack unwind mechanism or swapcontext\n");
|
||||
if (SANITIZER_WINDOWS)
|
||||
Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
|
||||
else
|
||||
Printf(" (longjmp and C++ exceptions *are* supported)\n");
|
||||
|
||||
DescribeThread(t);
|
||||
return true;
|
||||
}
|
||||
|
@ -531,7 +562,7 @@ class ScopedInErrorReport {
|
|||
// Do not print more than one report, otherwise they will mix up.
|
||||
// Error reporting functions shouldn't return at this situation, as
|
||||
// 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");
|
||||
u32 current_tid = GetCurrentTidOrInvalid();
|
||||
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());
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: stack-overflow on address %p"
|
||||
" (pc %p sp %p bp %p T%d)\n",
|
||||
(void *)addr, (void *)pc, (void *)sp, (void *)bp,
|
||||
" (pc %p bp %p sp %p T%d)\n",
|
||||
(void *)addr, (void *)pc, (void *)bp, (void *)sp,
|
||||
GetCurrentTidOrInvalid());
|
||||
Printf("%s", d.EndWarning());
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report(
|
||||
"ERROR: AddressSanitizer: SEGV on unknown address %p"
|
||||
" (pc %p sp %p bp %p T%d)\n",
|
||||
(void *)addr, (void *)pc, (void *)sp, (void *)bp,
|
||||
"ERROR: AddressSanitizer: %s on unknown address %p"
|
||||
" (pc %p bp %p sp %p T%d)\n",
|
||||
description, (void *)addr, (void *)pc, (void *)bp, (void *)sp,
|
||||
GetCurrentTidOrInvalid());
|
||||
if (pc < GetPageSizeCached()) {
|
||||
Report("Hint: pc points to the zero page.\n");
|
||||
}
|
||||
Printf("%s", d.EndWarning());
|
||||
GET_STACK_TRACE_SIGNAL(pc, bp, context);
|
||||
stack.Print();
|
||||
|
@ -621,6 +656,30 @@ void ReportDoubleFree(uptr addr, StackTrace *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) {
|
||||
ScopedInErrorReport in_report;
|
||||
Decorator d;
|
||||
|
@ -674,17 +733,17 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
|
|||
ReportErrorSummary("bad-malloc_usable_size", stack);
|
||||
}
|
||||
|
||||
void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
|
||||
void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
|
||||
ScopedInErrorReport in_report;
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
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);
|
||||
Printf("%s", d.EndWarning());
|
||||
stack->Print();
|
||||
DescribeHeapAddress(addr, 1);
|
||||
ReportErrorSummary("bad-__asan_get_allocated_size", stack);
|
||||
ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
|
||||
}
|
||||
|
||||
void ReportStringFunctionMemoryRangesOverlap(
|
||||
|
@ -733,17 +792,36 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
|
|||
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;
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg);
|
||||
Printf("%s", d.EndWarning());
|
||||
Printf(" [1] size=%zd %s %s\n", g1->size, g1->name, g1->module_name);
|
||||
Printf(" [2] size=%zd %s %s\n", g2->size, g2->name, g2->module_name);
|
||||
InternalScopedString g1_loc(256), g2_loc(256);
|
||||
PrintGlobalLocation(&g1_loc, *g1);
|
||||
PrintGlobalLocation(&g2_loc, *g2);
|
||||
Printf(" [1] size=%zd '%s' %s\n", g1->size,
|
||||
MaybeDemangleGlobalName(g1->name), g1_loc.data());
|
||||
Printf(" [2] size=%zd '%s' %s\n", g2->size,
|
||||
MaybeDemangleGlobalName(g2->name), g2_loc.data());
|
||||
if (stack_id1 && stack_id2) {
|
||||
Printf("These globals were registered at these points:\n");
|
||||
Printf(" [1]:\n");
|
||||
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 "
|
||||
"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
|
||||
|
@ -831,6 +909,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
|||
switch (*shadow_addr) {
|
||||
case kAsanHeapLeftRedzoneMagic:
|
||||
case kAsanHeapRightRedzoneMagic:
|
||||
case kAsanArrayCookieMagic:
|
||||
bug_descr = "heap-buffer-overflow";
|
||||
break;
|
||||
case kAsanHeapFreeMagic:
|
||||
|
@ -867,7 +946,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
|||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
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);
|
||||
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) {
|
||||
// Thread registry must be locked while we're describing an address.
|
||||
asanThreadRegistry().Lock();
|
||||
DescribeAddress(addr, 1);
|
||||
asanThreadRegistry().Unlock();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
|
|
@ -16,6 +16,13 @@
|
|||
|
||||
namespace __asan {
|
||||
|
||||
struct StackVarDescr {
|
||||
uptr beg;
|
||||
uptr size;
|
||||
const char *name_pos;
|
||||
uptr name_len;
|
||||
};
|
||||
|
||||
// The following functions prints address description depending
|
||||
// on the memory type (shadow/heap/stack/global).
|
||||
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,
|
||||
const __asan_global &g);
|
||||
bool DescribeAddressIfShadow(uptr addr);
|
||||
bool ParseFrameDescription(const char *frame_descr,
|
||||
InternalMmapVector<StackVarDescr> *vars);
|
||||
bool DescribeAddressIfStack(uptr addr, uptr access_size);
|
||||
// Determines memory type on its own.
|
||||
void DescribeAddress(uptr addr, uptr access_size);
|
||||
|
@ -32,8 +41,10 @@ void DescribeThread(AsanThreadContext *context);
|
|||
// Different kinds of error reports.
|
||||
void NORETURN
|
||||
ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr);
|
||||
void NORETURN
|
||||
ReportSIGSEGV(uptr pc, uptr sp, uptr bp, void *context, uptr addr);
|
||||
void NORETURN ReportSIGSEGV(const char *description, uptr pc, uptr sp, uptr bp,
|
||||
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 ReportFreeNotMalloced(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);
|
||||
void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
|
||||
StackTrace *stack);
|
||||
void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr,
|
||||
StackTrace *stack);
|
||||
void NORETURN
|
||||
ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack);
|
||||
void NORETURN ReportStringFunctionMemoryRangesOverlap(
|
||||
const char *function, const char *offset1, uptr length1,
|
||||
const char *offset2, uptr length2, StackTrace *stack);
|
||||
|
@ -53,7 +64,8 @@ ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, uptr old_mid,
|
|||
uptr new_mid, StackTrace *stack);
|
||||
|
||||
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.
|
||||
void WarnMacFreeUnallocated(
|
||||
|
|
|
@ -171,11 +171,6 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
|
|||
"If set, prints ASan exit stats even after program terminates "
|
||||
"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",
|
||||
"Allow the tool to re-exec the program. This may interfere badly with "
|
||||
"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 "
|
||||
"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",
|
||||
"If true, poison partially addressable 8-byte aligned words "
|
||||
"(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",
|
||||
"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",
|
||||
"If true, assume that memcmp(p1, p2, n) always reads n bytes before "
|
||||
"comparing p1 and p2.");
|
||||
|
@ -262,21 +264,23 @@ void InitializeFlags(Flags *f, const char *env) {
|
|||
f->print_stats = false;
|
||||
f->print_legend = true;
|
||||
f->atexit = false;
|
||||
f->disable_core = (SANITIZER_WORDSIZE == 64);
|
||||
f->allow_reexec = true;
|
||||
f->print_full_thread_history = true;
|
||||
f->poison_heap = true;
|
||||
f->poison_array_cookie = true;
|
||||
f->poison_partial = true;
|
||||
// Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=131
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=309
|
||||
// TODO(glider,timurrrr): Fix known issues and enable this back.
|
||||
f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0);
|
||||
f->new_delete_type_mismatch = true;
|
||||
f->strict_memcmp = true;
|
||||
f->strict_init_order = false;
|
||||
f->start_deactivated = false;
|
||||
f->detect_invalid_pointer_pairs = 0;
|
||||
f->detect_container_overflow = true;
|
||||
f->detect_odr_violation = 2;
|
||||
|
||||
// Override from compile definition.
|
||||
ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefinition());
|
||||
|
@ -456,13 +460,6 @@ static NOINLINE void force_interface_symbols() {
|
|||
case 15: __asan_set_error_report_callback(0); break;
|
||||
case 16: __asan_handle_no_return(); 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 26: __asan_unpoison_memory_region(0, 0); break;
|
||||
case 27: __asan_set_error_exit_code(0); break;
|
||||
|
@ -593,6 +590,11 @@ static void AsanInitInternal() {
|
|||
|
||||
InitializeAsanInterceptors();
|
||||
|
||||
// Enable system log ("adb logcat") on Android.
|
||||
// Doing this before interceptors are initialized crashes in:
|
||||
// AsanInitInternal -> android_log_write -> __interceptor_strcmp
|
||||
AndroidLogInit();
|
||||
|
||||
ReplaceSystemMalloc();
|
||||
|
||||
uptr shadow_start = kLowShadowBeg;
|
||||
|
@ -601,7 +603,8 @@ static void AsanInitInternal() {
|
|||
bool full_shadow_is_available =
|
||||
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) {
|
||||
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
|
||||
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
|
||||
|
@ -611,9 +614,7 @@ static void AsanInitInternal() {
|
|||
if (common_flags()->verbosity)
|
||||
PrintAddressSpaceLayout();
|
||||
|
||||
if (flags()->disable_core) {
|
||||
DisableCoreDumper();
|
||||
}
|
||||
DisableCoreDumperIfNecessary();
|
||||
|
||||
if (full_shadow_is_available) {
|
||||
// mmap the low shadow plus at least one page at the left.
|
||||
|
@ -648,12 +649,8 @@ static void AsanInitInternal() {
|
|||
AsanTSDInit(PlatformTSDDtor);
|
||||
InstallDeadlySignalHandlers(AsanOnSIGSEGV);
|
||||
|
||||
// Allocator should be initialized before starting external symbolizer, as
|
||||
// fork() on Mac locks the allocator.
|
||||
InitializeAllocator();
|
||||
|
||||
Symbolizer::Init(common_flags()->external_symbolizer_path);
|
||||
|
||||
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
|
||||
// should be set to 1 prior to initializing the threads.
|
||||
asan_inited = 1;
|
||||
|
@ -682,7 +679,7 @@ static void AsanInitInternal() {
|
|||
SanitizerInitializeUnwinder();
|
||||
|
||||
#if CAN_SANITIZE_LEAKS
|
||||
__lsan::InitCommonLsan();
|
||||
__lsan::InitCommonLsan(false);
|
||||
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
|
||||
Atexit(__lsan::DoLeakCheck);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "asan_internal.h"
|
||||
#include "asan_stats.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_interface.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
|
||||
|
@ -127,8 +128,8 @@ static void PrintAccumulatedStats() {
|
|||
BlockingMutexLock lock(&print_lock);
|
||||
stats.Print();
|
||||
StackDepotStats *stack_depot_stats = StackDepotGetStats();
|
||||
Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
|
||||
stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20);
|
||||
Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
|
||||
stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
|
||||
PrintInternalAllocatorStats();
|
||||
}
|
||||
|
||||
|
@ -137,7 +138,7 @@ static void PrintAccumulatedStats() {
|
|||
// ---------------------- Interface ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
uptr __asan_get_current_allocated_bytes() {
|
||||
uptr __sanitizer_get_current_allocated_bytes() {
|
||||
AsanStats stats;
|
||||
GetAccumulatedStats(&stats);
|
||||
uptr malloced = stats.malloced;
|
||||
|
@ -147,13 +148,13 @@ uptr __asan_get_current_allocated_bytes() {
|
|||
return (malloced > freed) ? malloced - freed : 1;
|
||||
}
|
||||
|
||||
uptr __asan_get_heap_size() {
|
||||
uptr __sanitizer_get_heap_size() {
|
||||
AsanStats stats;
|
||||
GetAccumulatedStats(&stats);
|
||||
return stats.mmaped - stats.munmaped;
|
||||
}
|
||||
|
||||
uptr __asan_get_free_bytes() {
|
||||
uptr __sanitizer_get_free_bytes() {
|
||||
AsanStats stats;
|
||||
GetAccumulatedStats(&stats);
|
||||
uptr total_free = stats.mmaped
|
||||
|
@ -167,7 +168,7 @@ uptr __asan_get_free_bytes() {
|
|||
return (total_free > total_used) ? total_free - total_used : 1;
|
||||
}
|
||||
|
||||
uptr __asan_get_unmapped_bytes() {
|
||||
uptr __sanitizer_get_unmapped_bytes() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -139,7 +139,10 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
|
|||
}
|
||||
|
||||
void AsanThread::Init() {
|
||||
fake_stack_ = 0; // Will be initialized lazily if needed.
|
||||
CHECK_EQ(this->stack_size(), 0U);
|
||||
SetThreadStackAndTls();
|
||||
CHECK_GT(this->stack_size(), 0U);
|
||||
CHECK(AddrIsInMem(stack_bottom_));
|
||||
CHECK(AddrIsInMem(stack_top_ - 1));
|
||||
ClearShadowForThreadStackAndTLS();
|
||||
|
@ -147,7 +150,6 @@ void AsanThread::Init() {
|
|||
VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
|
||||
(void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
|
||||
&local);
|
||||
fake_stack_ = 0; // Will be initialized lazily if needed.
|
||||
AsanPlatformThreadInit();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_thread.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_mutex.h"
|
||||
|
@ -68,7 +69,7 @@ void *AsanDoesNotSupportStaticLinkage() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void AsanCheckDynamicRTPrereqs() { UNIMPLEMENTED(); }
|
||||
void AsanCheckDynamicRTPrereqs() {}
|
||||
|
||||
void AsanCheckIncompatibleRT() {}
|
||||
|
||||
|
@ -84,6 +85,67 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
|
|||
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
|
||||
|
||||
#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
|
||||
// License. See LICENSE.TXT for details.
|
||||
|
@ -18,9 +18,10 @@
|
|||
// Using #ifdef rather than relying on Makefiles etc.
|
||||
// simplifies the build procedure.
|
||||
#ifdef ASAN_DLL_THUNK
|
||||
#include "asan_init_version.h"
|
||||
#include "sanitizer_common/sanitizer_interception.h"
|
||||
|
||||
// ----------------- Helper functions and macros --------------------- {{{1
|
||||
// ---------- Function interception helper functions and macros ----------- {{{1
|
||||
extern "C" {
|
||||
void *__stdcall GetModuleHandleA(const char *module_name);
|
||||
void *__stdcall GetProcAddress(void *module, const char *proc_name);
|
||||
|
@ -34,68 +35,143 @@ static void *getRealProcAddressOrDie(const char *name) {
|
|||
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) \
|
||||
extern "C" void name() { \
|
||||
typedef void (*fntype)(); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_V_W(name) \
|
||||
extern "C" void name(void *arg) { \
|
||||
typedef void (*fntype)(void *arg); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(arg); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_V_WW(name) \
|
||||
extern "C" void name(void *arg1, void *arg2) { \
|
||||
typedef void (*fntype)(void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(arg1, arg2); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_V_WWW(name) \
|
||||
extern "C" void name(void *arg1, void *arg2, void *arg3) { \
|
||||
typedef void *(*fntype)(void *, void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
fn(arg1, arg2, arg3); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_W_V(name) \
|
||||
extern "C" void *name() { \
|
||||
typedef void *(*fntype)(); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_W_W(name) \
|
||||
extern "C" void *name(void *arg) { \
|
||||
typedef void *(*fntype)(void *arg); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_W_WW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2) { \
|
||||
typedef void *(*fntype)(void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_W_WWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
|
||||
typedef void *(*fntype)(void *, void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_W_WWWW(name) \
|
||||
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
|
||||
typedef void *(*fntype)(void *, void *, void *, void *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_W_WWWWW(name) \
|
||||
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 *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4, arg5); \
|
||||
}
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
|
||||
#define WRAP_W_WWWWWW(name) \
|
||||
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 *); \
|
||||
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
|
||||
return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
|
||||
}
|
||||
// }}}
|
||||
|
||||
// --------- 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();
|
||||
} \
|
||||
INTERCEPT_WHEN_POSSIBLE(#name, name);
|
||||
// }}}
|
||||
|
||||
// ----------------- ASan own interface functions --------------------
|
||||
|
@ -165,17 +202,18 @@ extern "C" {
|
|||
|
||||
// Manually wrap __asan_init as we need to initialize
|
||||
// __asan_option_detect_stack_use_after_return afterwards.
|
||||
void __asan_init_v3() {
|
||||
void __asan_init() {
|
||||
typedef void (*fntype)();
|
||||
static fntype fn = 0;
|
||||
// __asan_init is expected to be called by only one thread.
|
||||
if (fn) return;
|
||||
|
||||
fn = (fntype)getRealProcAddressOrDie("__asan_init_v3");
|
||||
fn = (fntype)getRealProcAddressOrDie(__asan_init_name);
|
||||
fn();
|
||||
__asan_option_detect_stack_use_after_return =
|
||||
(__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_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_memset);
|
||||
INTERFACE_FUNCTION(__asan_memmove);
|
||||
|
@ -211,6 +263,9 @@ INTERFACE_FUNCTION(__asan_unpoison_stack_memory)
|
|||
INTERFACE_FUNCTION(__asan_poison_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_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_10)
|
||||
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_module_init)
|
||||
|
||||
// TODO(timurrrr): Add more interface functions on the as-needed basis.
|
||||
|
||||
// ----------------- 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).
|
||||
|
||||
void InterceptASanInterface() {
|
||||
INTERCEPT_ASAN_INTERFACE();
|
||||
INTERCEPT_LIBRARY_FUNCTION(atoi);
|
||||
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
|
50
libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc
Normal file
50
libsanitizer/asan/asan_win_dynamic_runtime_thunk.cc
Normal file
|
@ -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
|
||||
# automake.
|
||||
# CURRENT:REVISION:AGE
|
||||
1:0:0
|
||||
2:0:0
|
||||
|
|
64
libsanitizer/include/sanitizer/allocator_interface.h
Normal file
64
libsanitizer/include/sanitizer/allocator_interface.h
Normal file
|
@ -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).
|
||||
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.
|
||||
// However it is still a part of the interface because users may want to
|
||||
// set a breakpoint on this function in a debugger.
|
||||
|
@ -81,32 +97,6 @@ extern "C" {
|
|||
// the program crashes before ASan report is printed.
|
||||
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.
|
||||
void __asan_print_accumulated_stats();
|
||||
|
||||
|
@ -114,14 +104,6 @@ extern "C" {
|
|||
// a string containing ASan runtime options. See asan_flags.h for details.
|
||||
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
|
||||
// asan's fake stack.
|
||||
|
||||
|
|
|
@ -64,6 +64,10 @@ extern "C" {
|
|||
void __sanitizer_cov_init();
|
||||
// Record and dump coverage info.
|
||||
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
|
||||
// std::vector, std::string or similar.
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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. */
|
||||
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. */
|
||||
const char* __msan_default_options();
|
||||
|
||||
// Sets the callback to be called right before death on error.
|
||||
// Passing 0 will unset the callback.
|
||||
/* Sets the callback to be called right before death on error.
|
||||
Passing 0 will unset the callback. */
|
||||
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
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
@ -120,19 +120,23 @@ const interpose_substitution substitution_##func_name[] \
|
|||
# define DECLARE_WRAPPER(ret_type, func, ...)
|
||||
|
||||
#elif defined(_WIN32)
|
||||
# if defined(_DLL) // DLL CRT
|
||||
# define WRAP(x) x
|
||||
# define WRAPPER_NAME(x) #x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
# else // Static CRT
|
||||
# define WRAP(x) wrap_##x
|
||||
# define WRAPPER_NAME(x) "wrap_"#x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
# endif
|
||||
# define WRAP(x) __asan_wrap_##x
|
||||
# define WRAPPER_NAME(x) "__asan_wrap_"#x
|
||||
# define INTERCEPTOR_ATTRIBUTE __declspec(dllexport)
|
||||
# define DECLARE_WRAPPER(ret_type, func, ...) \
|
||||
extern "C" ret_type func(__VA_ARGS__);
|
||||
# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
|
||||
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
|
||||
# define WRAP(x) __interceptor_ ## x
|
||||
# define WRAPPER_NAME(x) "__interceptor_" #x
|
||||
|
|
|
@ -17,20 +17,6 @@
|
|||
|
||||
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
|
||||
// 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 0x5D8B: // 8B 5D XX = mov ebx, dword ptr [ebp+XXh]
|
||||
case 0xEC83: // 83 EC XX = sub esp, XX
|
||||
case 0x75FF: // FF 75 XX = push dword ptr [ebp+XXh]
|
||||
cursor += 3;
|
||||
continue;
|
||||
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;
|
||||
continue;
|
||||
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)) {
|
||||
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 0x24548B: // 8B 54 24 XX = mov edx, 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
|
||||
// interceptor or a new compiler version. In either case, they should result
|
||||
// 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?
|
||||
__debugbreak();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -189,6 +179,33 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
|
|||
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
|
||||
|
||||
#endif // _WIN32
|
||||
|
|
|
@ -20,27 +20,29 @@
|
|||
#define INTERCEPTION_WIN_H
|
||||
|
||||
namespace __interception {
|
||||
// returns true if a function with the given name was found.
|
||||
bool GetRealFunctionAddress(const char *func_name, uptr *func_addr);
|
||||
// All the functions in the OverrideFunction() family return true on success,
|
||||
// false on failure (including "couldn't find the function").
|
||||
|
||||
// returns true if the old function existed, false on failure.
|
||||
bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func);
|
||||
// Overrides a function by its address.
|
||||
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
|
||||
|
||||
#if defined(_DLL)
|
||||
# define INTERCEPT_FUNCTION_WIN(func) \
|
||||
::__interception::GetRealFunctionAddress( \
|
||||
#func, (::__interception::uptr*)&REAL(func))
|
||||
#if defined(INTERCEPTION_DYNAMIC_CRT)
|
||||
#define INTERCEPT_FUNCTION_WIN(func) \
|
||||
::__interception::OverrideFunction(#func, \
|
||||
(::__interception::uptr)WRAP(func), \
|
||||
(::__interception::uptr *)&REAL(func))
|
||||
#else
|
||||
# define INTERCEPT_FUNCTION_WIN(func) \
|
||||
::__interception::OverrideFunction( \
|
||||
(::__interception::uptr)func, \
|
||||
(::__interception::uptr)WRAP(func), \
|
||||
(::__interception::uptr*)&REAL(func))
|
||||
#define INTERCEPT_FUNCTION_WIN(func) \
|
||||
::__interception::OverrideFunction((::__interception::uptr)func, \
|
||||
(::__interception::uptr)WRAP(func), \
|
||||
(::__interception::uptr *)&REAL(func))
|
||||
#endif
|
||||
|
||||
#define INTERCEPT_FUNCTION_VER_WIN(func, symver) \
|
||||
INTERCEPT_FUNCTION_WIN(func)
|
||||
#define INTERCEPT_FUNCTION_VER_WIN(func, symver) INTERCEPT_FUNCTION_WIN(func)
|
||||
|
||||
#endif // INTERCEPTION_WIN_H
|
||||
#endif // _WIN32
|
||||
|
|
|
@ -23,16 +23,6 @@ bool lsan_init_is_running;
|
|||
|
||||
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. /////
|
||||
bool WordIsPoisoned(uptr addr) {
|
||||
return false;
|
||||
|
@ -48,7 +38,7 @@ extern "C" void __lsan_init() {
|
|||
return;
|
||||
lsan_init_is_running = true;
|
||||
SanitizerToolName = "LeakSanitizer";
|
||||
InitializeCommonFlags();
|
||||
InitCommonLsan(true);
|
||||
InitializeAllocator();
|
||||
InitTlsSize();
|
||||
InitializeInterceptors();
|
||||
|
@ -58,11 +48,14 @@ extern "C" void __lsan_init() {
|
|||
ThreadStart(tid, GetTid());
|
||||
SetCurrentThread(tid);
|
||||
|
||||
Symbolizer::Init(common_flags()->external_symbolizer_path);
|
||||
|
||||
InitCommonLsan();
|
||||
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
|
||||
Atexit(DoLeakCheck);
|
||||
lsan_inited = true;
|
||||
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_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 {
|
||||
|
||||
void InitializeInterceptors();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "lsan_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_stackdepot.h"
|
||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
|
@ -51,7 +52,7 @@ void AllocatorThreadFinish() {
|
|||
allocator.SwallowCache(&cache);
|
||||
}
|
||||
|
||||
static ChunkMetadata *Metadata(void *p) {
|
||||
static ChunkMetadata *Metadata(const void *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))
|
||||
memset(p, 0, size);
|
||||
RegisterAllocation(stack, p, size);
|
||||
if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
void Deallocate(void *p) {
|
||||
if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
|
||||
RegisterDeallocation(p);
|
||||
allocator.Deallocate(&cache, p);
|
||||
}
|
||||
|
@ -111,7 +114,7 @@ void GetAllocatorCacheRange(uptr *begin, uptr *end) {
|
|||
*end = *begin + sizeof(cache);
|
||||
}
|
||||
|
||||
uptr GetMallocUsableSize(void *p) {
|
||||
uptr GetMallocUsableSize(const void *p) {
|
||||
ChunkMetadata *m = Metadata(p);
|
||||
if (!m) return 0;
|
||||
return m->requested_size;
|
||||
|
@ -198,3 +201,38 @@ IgnoreObjectResult IgnoreObjectLocked(const void *p) {
|
|||
}
|
||||
}
|
||||
} // 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 *Reallocate(const StackTrace &stack, void *p, uptr new_size,
|
||||
uptr alignment);
|
||||
uptr GetMallocUsableSize(void *p);
|
||||
uptr GetMallocUsableSize(const void *p);
|
||||
|
||||
template<typename Callable>
|
||||
void ForEachChunk(const Callable &callback);
|
||||
|
|
|
@ -34,15 +34,13 @@ bool DisabledInThisThread() { return disable_counter > 0; }
|
|||
|
||||
Flags lsan_flags;
|
||||
|
||||
static void InitializeFlags() {
|
||||
static void InitializeFlags(bool standalone) {
|
||||
Flags *f = flags();
|
||||
// Default values.
|
||||
f->report_objects = false;
|
||||
f->resolution = 0;
|
||||
f->max_leaks = 0;
|
||||
f->exitcode = 23;
|
||||
f->print_suppressions = true;
|
||||
f->suppressions="";
|
||||
f->use_registers = true;
|
||||
f->use_globals = true;
|
||||
f->use_stacks = true;
|
||||
|
@ -70,9 +68,18 @@ static void InitializeFlags() {
|
|||
ParseFlag(options, &f->log_pointers, "log_pointers", "");
|
||||
ParseFlag(options, &f->log_threads, "log_threads", "");
|
||||
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(...) \
|
||||
|
@ -85,24 +92,14 @@ static void InitializeFlags() {
|
|||
if (flags()->log_threads) Report(__VA_ARGS__); \
|
||||
} while (0);
|
||||
|
||||
SuppressionContext *suppression_ctx;
|
||||
static bool suppressions_inited = false;
|
||||
|
||||
void InitializeSuppressions() {
|
||||
CHECK(!suppression_ctx);
|
||||
ALIGNED(64) static char placeholder[sizeof(SuppressionContext)];
|
||||
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();
|
||||
}
|
||||
CHECK(!suppressions_inited);
|
||||
SuppressionContext::InitIfNecessary();
|
||||
if (&__lsan_default_suppressions)
|
||||
suppression_ctx->Parse(__lsan_default_suppressions());
|
||||
SuppressionContext::Get()->Parse(__lsan_default_suppressions());
|
||||
suppressions_inited = true;
|
||||
}
|
||||
|
||||
struct RootRegion {
|
||||
|
@ -118,8 +115,8 @@ void InitializeRootRegions() {
|
|||
root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
|
||||
}
|
||||
|
||||
void InitCommonLsan() {
|
||||
InitializeFlags();
|
||||
void InitCommonLsan(bool standalone) {
|
||||
InitializeFlags(standalone);
|
||||
InitializeRootRegions();
|
||||
if (common_flags()->detect_leaks) {
|
||||
// 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:
|
||||
Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
|
||||
Decorator() : SanitizerCommonDecorator() { }
|
||||
const char *Error() { return Red(); }
|
||||
const char *Leak() { return Blue(); }
|
||||
const char *End() { return Default(); }
|
||||
|
@ -387,7 +384,7 @@ static void CollectLeaksCb(uptr chunk, void *arg) {
|
|||
|
||||
static void PrintMatchedSuppressions() {
|
||||
InternalMmapVector<Suppression *> matched(1);
|
||||
suppression_ctx->GetMatched(&matched);
|
||||
SuppressionContext::Get()->GetMatched(&matched);
|
||||
if (!matched.size())
|
||||
return;
|
||||
const char *line = "-----------------------------------------------------";
|
||||
|
@ -448,7 +445,7 @@ void DoLeakCheck() {
|
|||
Printf("%s", d.End());
|
||||
param.leak_report.ReportTopLeaks(flags()->max_leaks);
|
||||
}
|
||||
if (flags()->print_suppressions)
|
||||
if (common_flags()->print_suppressions)
|
||||
PrintMatchedSuppressions();
|
||||
if (unsuppressed_count > 0) {
|
||||
param.leak_report.PrintSummary();
|
||||
|
@ -463,20 +460,22 @@ static Suppression *GetSuppressionForAddr(uptr addr) {
|
|||
// Suppress by module name.
|
||||
const char *module_name;
|
||||
uptr module_offset;
|
||||
if (Symbolizer::Get()->GetModuleNameAndOffsetForPC(addr, &module_name,
|
||||
&module_offset) &&
|
||||
suppression_ctx->Match(module_name, SuppressionLeak, &s))
|
||||
if (Symbolizer::GetOrInit()
|
||||
->GetModuleNameAndOffsetForPC(addr, &module_name, &module_offset) &&
|
||||
SuppressionContext::Get()->Match(module_name, SuppressionLeak, &s))
|
||||
return s;
|
||||
|
||||
// Suppress by file or function name.
|
||||
static const uptr kMaxAddrFrames = 16;
|
||||
InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
|
||||
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);
|
||||
for (uptr i = 0; i < addr_frames_num; i++) {
|
||||
if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) ||
|
||||
suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s))
|
||||
if (SuppressionContext::Get()->Match(addr_frames[i].function,
|
||||
SuppressionLeak, &s) ||
|
||||
SuppressionContext::Get()->Match(addr_frames[i].file, SuppressionLeak,
|
||||
&s))
|
||||
return s;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -49,10 +49,6 @@ struct Flags {
|
|||
int max_leaks;
|
||||
// If nonzero kill the process with this exit code upon finding leaks.
|
||||
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.
|
||||
// Global variables (.data and .bss).
|
||||
|
@ -133,7 +129,7 @@ enum IgnoreObjectResult {
|
|||
};
|
||||
|
||||
// Functions called from the parent tool.
|
||||
void InitCommonLsan();
|
||||
void InitCommonLsan(bool standalone);
|
||||
void DoLeakCheck();
|
||||
bool DisabledInThisThread();
|
||||
|
||||
|
|
|
@ -32,21 +32,6 @@ int pthread_key_create(unsigned *key, void (*destructor)(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 { \
|
||||
CHECK(!lsan_init_is_running); \
|
||||
if (!lsan_inited) \
|
||||
|
@ -63,7 +48,7 @@ namespace std {
|
|||
|
||||
INTERCEPTOR(void*, malloc, uptr size) {
|
||||
ENSURE_LSAN_INITED;
|
||||
GET_STACK_TRACE;
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return Allocate(stack, size, 1, kAlwaysClearMemory);
|
||||
}
|
||||
|
||||
|
@ -86,26 +71,32 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
|
|||
}
|
||||
if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0;
|
||||
ENSURE_LSAN_INITED;
|
||||
GET_STACK_TRACE;
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
size *= nmemb;
|
||||
return Allocate(stack, size, 1, true);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, realloc, void *q, uptr size) {
|
||||
ENSURE_LSAN_INITED;
|
||||
GET_STACK_TRACE;
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return Reallocate(stack, q, size, 1);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
|
||||
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);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
|
||||
ENSURE_LSAN_INITED;
|
||||
GET_STACK_TRACE;
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
*memptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
|
||||
// FIXME: Return ENOMEM if user requested more than max alloc size.
|
||||
return 0;
|
||||
|
@ -113,7 +104,7 @@ INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
|
|||
|
||||
INTERCEPTOR(void*, valloc, uptr size) {
|
||||
ENSURE_LSAN_INITED;
|
||||
GET_STACK_TRACE;
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
if (size == 0)
|
||||
size = GetPageSizeCached();
|
||||
return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory);
|
||||
|
@ -140,7 +131,7 @@ INTERCEPTOR(int, mallopt, int cmd, int value) {
|
|||
|
||||
INTERCEPTOR(void*, pvalloc, uptr size) {
|
||||
ENSURE_LSAN_INITED;
|
||||
GET_STACK_TRACE;
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
size = RoundUpTo(size, PageSize);
|
||||
if (size == 0) {
|
||||
|
@ -154,7 +145,7 @@ INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free));
|
|||
|
||||
#define OPERATOR_NEW_BODY \
|
||||
ENSURE_LSAN_INITED; \
|
||||
GET_STACK_TRACE; \
|
||||
GET_STACK_TRACE_MALLOC; \
|
||||
return Allocate(stack, size, 1, kAlwaysClearMemory);
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE
|
||||
|
|
|
@ -12,11 +12,7 @@
|
|||
|
||||
#include "lsan.h"
|
||||
|
||||
#ifndef LSAN_USE_PREINIT_ARRAY
|
||||
#define LSAN_USE_PREINIT_ARRAY 1
|
||||
#endif
|
||||
|
||||
#if LSAN_USE_PREINIT_ARRAY && !defined(PIC)
|
||||
#if SANITIZER_CAN_USE_PREINIT_ARRAY
|
||||
// We force __lsan_init to be called before anyone else by placing it into
|
||||
// .preinit_array section.
|
||||
__attribute__((section(".preinit_array"), used))
|
||||
|
|
|
@ -21,7 +21,8 @@ sanitizer_common_files = \
|
|||
sanitizer_allocator.cc \
|
||||
sanitizer_common.cc \
|
||||
sanitizer_common_libcdep.cc \
|
||||
sanitizer_coverage.cc \
|
||||
sanitizer_coverage_libcdep.cc \
|
||||
sanitizer_coverage_mapping_libcdep.cc \
|
||||
sanitizer_deadlock_detector1.cc \
|
||||
sanitizer_deadlock_detector2.cc \
|
||||
sanitizer_flags.cc \
|
||||
|
@ -30,11 +31,14 @@ sanitizer_common_files = \
|
|||
sanitizer_linux.cc \
|
||||
sanitizer_linux_libcdep.cc \
|
||||
sanitizer_mac.cc \
|
||||
sanitizer_persistent_allocator.cc \
|
||||
sanitizer_platform_limits_linux.cc \
|
||||
sanitizer_platform_limits_posix.cc \
|
||||
sanitizer_posix.cc \
|
||||
sanitizer_posix_libcdep.cc \
|
||||
sanitizer_printf.cc \
|
||||
sanitizer_procmaps_common.cc \
|
||||
sanitizer_procmaps_freebsd.cc \
|
||||
sanitizer_procmaps_linux.cc \
|
||||
sanitizer_procmaps_mac.cc \
|
||||
sanitizer_stackdepot.cc \
|
||||
|
@ -49,8 +53,10 @@ sanitizer_common_files = \
|
|||
sanitizer_symbolizer_win.cc \
|
||||
sanitizer_thread_registry.cc \
|
||||
sanitizer_tls_get_addr.cc \
|
||||
sanitizer_unwind_posix_libcdep.cc \
|
||||
sanitizer_win.cc
|
||||
|
||||
|
||||
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
|
||||
|
||||
# Work around what appears to be a GNU make bug handling MAKEFLAGS
|
||||
|
|
|
@ -64,14 +64,17 @@ CONFIG_CLEAN_VPATH_FILES =
|
|||
LTLIBRARIES = $(noinst_LTLIBRARIES)
|
||||
libsanitizer_common_la_LIBADD =
|
||||
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_detector2.lo sanitizer_flags.lo \
|
||||
sanitizer_libc.lo sanitizer_libignore.lo sanitizer_linux.lo \
|
||||
sanitizer_linux_libcdep.lo sanitizer_mac.lo \
|
||||
sanitizer_persistent_allocator.lo \
|
||||
sanitizer_platform_limits_linux.lo \
|
||||
sanitizer_platform_limits_posix.lo sanitizer_posix.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_stackdepot.lo sanitizer_stacktrace.lo \
|
||||
sanitizer_stacktrace_libcdep.lo \
|
||||
|
@ -81,7 +84,8 @@ am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
|
|||
sanitizer_symbolizer_libcdep.lo \
|
||||
sanitizer_symbolizer_posix_libcdep.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)
|
||||
libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS)
|
||||
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
|
||||
|
@ -256,7 +260,8 @@ sanitizer_common_files = \
|
|||
sanitizer_allocator.cc \
|
||||
sanitizer_common.cc \
|
||||
sanitizer_common_libcdep.cc \
|
||||
sanitizer_coverage.cc \
|
||||
sanitizer_coverage_libcdep.cc \
|
||||
sanitizer_coverage_mapping_libcdep.cc \
|
||||
sanitizer_deadlock_detector1.cc \
|
||||
sanitizer_deadlock_detector2.cc \
|
||||
sanitizer_flags.cc \
|
||||
|
@ -265,11 +270,14 @@ sanitizer_common_files = \
|
|||
sanitizer_linux.cc \
|
||||
sanitizer_linux_libcdep.cc \
|
||||
sanitizer_mac.cc \
|
||||
sanitizer_persistent_allocator.cc \
|
||||
sanitizer_platform_limits_linux.cc \
|
||||
sanitizer_platform_limits_posix.cc \
|
||||
sanitizer_posix.cc \
|
||||
sanitizer_posix_libcdep.cc \
|
||||
sanitizer_printf.cc \
|
||||
sanitizer_procmaps_common.cc \
|
||||
sanitizer_procmaps_freebsd.cc \
|
||||
sanitizer_procmaps_linux.cc \
|
||||
sanitizer_procmaps_mac.cc \
|
||||
sanitizer_stackdepot.cc \
|
||||
|
@ -284,6 +292,7 @@ sanitizer_common_files = \
|
|||
sanitizer_symbolizer_win.cc \
|
||||
sanitizer_thread_registry.cc \
|
||||
sanitizer_tls_get_addr.cc \
|
||||
sanitizer_unwind_posix_libcdep.cc \
|
||||
sanitizer_win.cc
|
||||
|
||||
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_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_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_detector2.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_libcdep.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_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_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_mac.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_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_unwind_posix_libcdep.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@
|
||||
|
||||
.cc.o:
|
||||
|
|
|
@ -271,9 +271,9 @@ class AllocatorGlobalStats : public AllocatorStats {
|
|||
if (stats == this)
|
||||
break;
|
||||
}
|
||||
// All stats must be positive.
|
||||
// All stats must be non-negative.
|
||||
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:
|
||||
|
|
|
@ -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 u64 kInternalAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
|
||||
#if SANITIZER_WORDSIZE == 32
|
||||
static const uptr kInternalAllocatorRegionSizeLog = 20;
|
||||
#if SANITIZER_WORDSIZE == 32
|
||||
static const uptr kInternalAllocatorNumRegions =
|
||||
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
|
||||
typedef FlatByteMap<kInternalAllocatorNumRegions> ByteMap;
|
||||
#else
|
||||
static const uptr kInternalAllocatorRegionSizeLog = 24;
|
||||
static const uptr kInternalAllocatorNumRegions =
|
||||
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
|
||||
typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap;
|
||||
#endif
|
||||
typedef SizeClassAllocator32<
|
||||
kInternalAllocatorSpace, kInternalAllocatorSize, 16, InternalSizeClassMap,
|
||||
kInternalAllocatorSpace, kInternalAllocatorSize, 0, InternalSizeClassMap,
|
||||
kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator;
|
||||
|
||||
typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator>
|
||||
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,
|
||||
LargeMmapAllocator<CrashOnMapUnmap> >
|
||||
InternalAllocator;
|
||||
LargeMmapAllocator<> > InternalAllocator;
|
||||
|
||||
void *InternalAlloc(uptr size, 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 Exchange, long long Comparand); // NOLINT
|
||||
#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(
|
||||
void *volatile *Destination,
|
||||
void *Exchange, void *Comparand);
|
||||
#pragma intrinsic(_InterlockedCompareExchangePointer)
|
||||
#else
|
||||
// There's no _InterlockedCompareExchangePointer intrinsic on x86,
|
||||
// so call _InterlockedCompareExchange instead.
|
||||
extern "C"
|
||||
long __cdecl _InterlockedCompareExchange( // NOLINT
|
||||
long volatile *Destination, // NOLINT
|
||||
long Exchange, long Comparand); // NOLINT
|
||||
#pragma intrinsic(_InterlockedCompareExchange)
|
||||
|
||||
inline static void *_InterlockedCompareExchangePointer(
|
||||
void *volatile *Destination,
|
||||
void *Exchange, void *Comparand) {
|
||||
return reinterpret_cast<void*>(
|
||||
_InterlockedCompareExchange(
|
||||
reinterpret_cast<long volatile*>(Destination), // NOLINT
|
||||
reinterpret_cast<long>(Exchange), // NOLINT
|
||||
reinterpret_cast<long>(Comparand))); // NOLINT
|
||||
}
|
||||
#ifdef _WIN64
|
||||
extern "C" long long _InterlockedExchangeAdd64( // NOLINT
|
||||
long long volatile * Addend, long long Value); // NOLINT
|
||||
#pragma intrinsic(_InterlockedExchangeAdd64)
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_stacktrace.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
|
@ -195,31 +193,17 @@ void ReportErrorSummary(const char *error_type, const char *file,
|
|||
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) {
|
||||
full_name_ = internal_strdup(module_name);
|
||||
base_address_ = base_address;
|
||||
n_ranges_ = 0;
|
||||
}
|
||||
|
||||
void LoadedModule::addAddressRange(uptr beg, uptr end) {
|
||||
void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
|
||||
CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges);
|
||||
ranges_[n_ranges_].beg = beg;
|
||||
ranges_[n_ranges_].end = end;
|
||||
exec_[n_ranges_] = executable;
|
||||
n_ranges_++;
|
||||
}
|
||||
|
||||
|
@ -261,11 +245,6 @@ void DecreaseTotalMmap(uptr size) {
|
|||
atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
|
||||
}
|
||||
|
||||
static void (*sandboxing_callback)();
|
||||
void SetSandboxingCallback(void (*f)()) {
|
||||
sandboxing_callback = f;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
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) {
|
||||
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
|
||||
// in '*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.
|
||||
const char *StripPathPrefix(const char *filepath,
|
||||
|
@ -173,7 +176,7 @@ void PrintModuleAndOffset(InternalScopedString *buffer,
|
|||
const char *module, uptr offset);
|
||||
|
||||
// OS
|
||||
void DisableCoreDumper();
|
||||
void DisableCoreDumperIfNecessary();
|
||||
void DumpProcessMap();
|
||||
bool FileExists(const char *filename);
|
||||
const char *GetEnv(const char *name);
|
||||
|
@ -184,11 +187,17 @@ u32 GetUid();
|
|||
void ReExec();
|
||||
bool StackSizeIsUnlimited();
|
||||
void SetStackSizeLimitInBytes(uptr limit);
|
||||
bool AddressSpaceIsUnlimited();
|
||||
void SetAddressSpaceUnlimited();
|
||||
void AdjustStackSize(void *attr);
|
||||
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args);
|
||||
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args);
|
||||
void SetSandboxingCallback(void (*f)());
|
||||
|
||||
void CovUpdateMapping(uptr caller_pc = 0);
|
||||
void CovBeforeFork();
|
||||
void CovAfterFork(int child_pid);
|
||||
|
||||
void InitTlsSize();
|
||||
uptr GetTlsSize();
|
||||
|
||||
|
@ -239,7 +248,7 @@ const int kMaxSummaryLength = 1024;
|
|||
// and pass it to __sanitizer_report_error_summary.
|
||||
void ReportErrorSummary(const char *error_message);
|
||||
// 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,
|
||||
int line, const char *function);
|
||||
void ReportErrorSummary(const char *error_type, StackTrace *trace);
|
||||
|
@ -475,12 +484,17 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last,
|
|||
class LoadedModule {
|
||||
public:
|
||||
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;
|
||||
|
||||
const char *full_name() const { return full_name_; }
|
||||
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:
|
||||
struct AddressRange {
|
||||
uptr beg;
|
||||
|
@ -490,6 +504,7 @@ class LoadedModule {
|
|||
uptr base_address_;
|
||||
static const uptr kMaxNumberOfAddressRanges = 6;
|
||||
AddressRange ranges_[kMaxNumberOfAddressRanges];
|
||||
bool exec_[kMaxNumberOfAddressRanges];
|
||||
uptr n_ranges_;
|
||||
};
|
||||
|
||||
|
@ -529,10 +544,13 @@ F IndirectExternCall(F f) {
|
|||
#endif
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
// Initialize Android logging. Any writes before this are silently lost.
|
||||
void AndroidLogInit();
|
||||
void AndroidLogWrite(const char *buffer);
|
||||
void GetExtraActivationFlags(char *buf, uptr size);
|
||||
void SanitizerInitializeUnwinder();
|
||||
#else
|
||||
INLINE void AndroidLogInit() {}
|
||||
INLINE void AndroidLogWrite(const char *buffer_unused) {}
|
||||
INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
|
||||
INLINE void SanitizerInitializeUnwinder() {}
|
||||
|
@ -544,4 +562,9 @@ inline void *operator new(__sanitizer::operator_new_size_type size,
|
|||
return alloc.Allocate(size);
|
||||
}
|
||||
|
||||
struct StackDepotStats {
|
||||
uptr n_uniq_ids;
|
||||
uptr allocated;
|
||||
};
|
||||
|
||||
#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: \
|
||||
va_arg(*aq, double); \
|
||||
break; \
|
||||
case 12: \
|
||||
va_arg(*aq, long double); \
|
||||
break; \
|
||||
case 16: \
|
||||
va_arg(*aq, long double); \
|
||||
break; \
|
||||
|
|
|
@ -527,7 +527,7 @@ static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
|
|||
desc->name = "<DECODED_IOCTL>";
|
||||
desc->size = IOC_SIZE(req);
|
||||
// Sanity check.
|
||||
if (desc->size > 1024) return false;
|
||||
if (desc->size > 0xFFFF) return false;
|
||||
unsigned dir = IOC_DIR(req);
|
||||
switch (dir) {
|
||||
case IOC_NONE:
|
||||
|
@ -545,10 +545,10 @@ static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
if (desc->type != IOC_NONE && desc->size == 0) return false;
|
||||
char id = IOC_TYPE(req);
|
||||
// Size can be 0 iff type is NONE.
|
||||
if ((desc->type == IOC_NONE) != (desc->size == 0)) return false;
|
||||
// Sanity check.
|
||||
if (!(id >= 'a' && id <= 'z') && !(id >= 'A' && id <= 'Z')) return false;
|
||||
if (IOC_TYPE(req) == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_stacktrace.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
|
@ -39,4 +41,32 @@ bool ColorizeReports() {
|
|||
return internal_strcmp(flag, "always") == 0 ||
|
||||
(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
|
||||
|
||||
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) {
|
||||
if (path)
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif // !SANITIZER_ANDROID
|
||||
|
||||
PRE_SYSCALL(lstat)(const void *filename, void *statbuf) {
|
||||
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) {
|
||||
POST_WRITE(buf, len);
|
||||
} 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++)
|
||||
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) {
|
||||
__sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf;
|
||||
__sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
|
||||
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.
|
||||
COMMON_SYSCALL_RELEASE(data);
|
||||
|
@ -2293,7 +2295,7 @@ PRE_SYSCALL(ni_syscall)() {}
|
|||
POST_SYSCALL(ni_syscall)(long res) {}
|
||||
|
||||
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 (request == ptrace_setregs) {
|
||||
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) {
|
||||
#if defined(__i386) || defined (__x86_64)
|
||||
#if !SANITIZER_ANDROID && (defined(__i386) || defined (__x86_64))
|
||||
if (res >= 0 && data) {
|
||||
// Note that this is different from the interceptor in
|
||||
// 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"
|
377
libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc
Normal file
377
libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc
Normal file
|
@ -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++;
|
||||
}
|
||||
CHECK_LE(id, kMaxMutex);
|
||||
VPrintf(3, "#%llu: DD::allocateId assign id %d\n",
|
||||
cb->lt->ctx, id);
|
||||
VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,11 @@ struct FlagDescription {
|
|||
|
||||
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) {
|
||||
f->symbolize = true;
|
||||
f->external_symbolizer_path = 0;
|
||||
|
@ -53,7 +58,12 @@ void SetCommonFlagsDefaults(CommonFlags *f) {
|
|||
f->legacy_pthread_cond = false;
|
||||
f->intercept_tls_get_addr = false;
|
||||
f->coverage = false;
|
||||
f->coverage_direct = SANITIZER_ANDROID;
|
||||
f->coverage_dir = ".";
|
||||
f->full_address_space = false;
|
||||
f->suppressions = "";
|
||||
f->print_suppressions = true;
|
||||
f->disable_coredump = (SANITIZER_WORDSIZE == 64);
|
||||
}
|
||||
|
||||
void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
|
||||
|
@ -125,9 +135,23 @@ void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
|
|||
ParseFlag(str, &f->coverage, "coverage",
|
||||
"If set, coverage information will be dumped at program shutdown (if the "
|
||||
"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",
|
||||
"Sanitize complete address space; "
|
||||
"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.
|
||||
if (f->malloc_context_size < 1)
|
||||
|
@ -143,14 +167,17 @@ static bool GetFlagValue(const char *env, const char *name,
|
|||
pos = internal_strstr(env, name);
|
||||
if (pos == 0)
|
||||
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.
|
||||
env = pos + 1;
|
||||
continue;
|
||||
}
|
||||
pos = name_end;
|
||||
break;
|
||||
}
|
||||
pos += internal_strlen(name);
|
||||
const char *end;
|
||||
if (pos[0] != '=') {
|
||||
end = pos;
|
||||
|
|
|
@ -52,7 +52,12 @@ struct CommonFlags {
|
|||
bool help;
|
||||
uptr mmap_limit_mb;
|
||||
bool coverage;
|
||||
bool coverage_direct;
|
||||
const char *coverage_dir;
|
||||
bool full_address_space;
|
||||
const char *suppressions;
|
||||
bool print_suppressions;
|
||||
bool disable_coredump;
|
||||
};
|
||||
|
||||
inline CommonFlags *common_flags() {
|
||||
|
|
135
libsanitizer/sanitizer_common/sanitizer_freebsd.h
Normal file
135
libsanitizer/sanitizer_common/sanitizer_freebsd.h
Normal file
|
@ -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
|
||||
#endif
|
||||
|
||||
// If set, the tool will install its own SEGV signal handler.
|
||||
#ifndef SANITIZER_NEEDS_SEGV
|
||||
# define SANITIZER_NEEDS_SEGV 1
|
||||
// We can use .preinit_array section on Linux to call sanitizer initialization
|
||||
// functions very early in the process startup (unless PIC macro is defined).
|
||||
// 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
|
||||
|
||||
// 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_write(fd_t fd, const void *buf, uptr count);
|
||||
uptr internal_ftruncate(fd_t fd, uptr size);
|
||||
|
||||
// OS
|
||||
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_readlink(const char *path, char *buf, uptr bufsize);
|
||||
uptr internal_unlink(const char *path);
|
||||
uptr internal_rename(const char *oldpath, const char *newpath);
|
||||
void NORETURN internal__exit(int exitcode);
|
||||
uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_LINUX
|
||||
#if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
|
||||
#include "sanitizer_libignore.h"
|
||||
#include "sanitizer_flags.h"
|
||||
|
@ -101,4 +101,4 @@ void LibIgnore::OnLibraryUnloaded() {
|
|||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // #if SANITIZER_LINUX
|
||||
#endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
|
|
|
@ -44,15 +44,16 @@
|
|||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <unwind.h>
|
||||
|
||||
#if SANITIZER_FREEBSD
|
||||
#include <sys/sysctl.h>
|
||||
#include <machine/atomic.h>
|
||||
extern "C" {
|
||||
// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
|
||||
// FreeBSD 9.2 and 10.0.
|
||||
#include <sys/umtx.h>
|
||||
}
|
||||
extern char **environ; // provided by crt1
|
||||
#endif // SANITIZER_FREEBSD
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
|
@ -132,7 +133,7 @@ uptr internal_open(const char *filename, int flags, u32 mode) {
|
|||
|
||||
uptr OpenFile(const char *filename, bool write) {
|
||||
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) {
|
||||
|
@ -149,6 +150,12 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) {
|
|||
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
|
||||
static void stat64_to_stat(struct stat64 *in, struct stat *out) {
|
||||
internal_memset(out, 0, sizeof(*out));
|
||||
|
@ -244,6 +251,15 @@ uptr internal_unlink(const char *path) {
|
|||
#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() {
|
||||
return internal_syscall(SYSCALL(sched_yield));
|
||||
}
|
||||
|
@ -297,9 +313,20 @@ u64 NanoTime() {
|
|||
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.
|
||||
// This function should be called first inside __asan_init.
|
||||
// Like getenv, but reads env directly from /proc (on Linux) or parses the
|
||||
// 'environ' array (on FreeBSD) and does not use libc. This function should be
|
||||
// called first inside __asan_init.
|
||||
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 uptr len;
|
||||
static bool inited;
|
||||
|
@ -323,6 +350,9 @@ const char *GetEnv(const char *name) {
|
|||
p = endp + 1;
|
||||
}
|
||||
return 0; // Not found.
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
@ -388,20 +418,6 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
|||
}
|
||||
#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 {
|
||||
MtxUnlocked = 0,
|
||||
MtxLocked = 1,
|
||||
|
@ -506,7 +522,11 @@ uptr internal_sigaltstack(const struct sigaltstack *ss,
|
|||
}
|
||||
|
||||
int internal_fork() {
|
||||
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
|
||||
return internal_syscall(SYSCALL(clone), SIGCHLD, 0);
|
||||
#else
|
||||
return internal_syscall(SYSCALL(fork));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
|
@ -660,24 +680,32 @@ static char proc_self_exe_cache_str[kMaxPathLength];
|
|||
static uptr proc_self_exe_cache_len = 0;
|
||||
|
||||
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(
|
||||
"/proc/self/exe", buf, buf_len);
|
||||
int readlink_error;
|
||||
if (internal_iserror(module_name_len, &readlink_error)) {
|
||||
if (proc_self_exe_cache_len) {
|
||||
// If available, use the cached module name.
|
||||
CHECK_LE(proc_self_exe_cache_len, buf_len);
|
||||
internal_strncpy(buf, proc_self_exe_cache_str, buf_len);
|
||||
module_name_len = internal_strlen(proc_self_exe_cache_str);
|
||||
} else {
|
||||
// We can't read /proc/self/exe for some reason, assume the name of the
|
||||
// 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");
|
||||
}
|
||||
bool IsErr = internal_iserror(module_name_len, &readlink_error);
|
||||
#endif
|
||||
if (IsErr) {
|
||||
// We can't read /proc/self/exe for some reason, assume the name of the
|
||||
// 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);
|
||||
buf[module_name_len] = '\0';
|
||||
}
|
||||
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
|
||||
|
||||
#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
|
||||
// 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
|
||||
// like to avoid that.
|
||||
void AndroidLogWrite(const char *buffer) {
|
||||
if (!atomic_load(&android_log_initialized, memory_order_acquire))
|
||||
return;
|
||||
|
||||
char *copy = internal_strdup(buffer);
|
||||
char *p = copy;
|
||||
char *q;
|
||||
|
|
|
@ -15,23 +15,25 @@
|
|||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_freebsd.h"
|
||||
#include "sanitizer_linux.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_procmaps.h"
|
||||
#include "sanitizer_stacktrace.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 <signal.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
|
||||
#include <pthread_np.h>
|
||||
#include <osreldate.h>
|
||||
#define pthread_getattr_np pthread_attr_get_np
|
||||
#endif
|
||||
|
||||
|
@ -147,127 +149,6 @@ bool SanitizerGetThreadName(char *name, int max_len) {
|
|||
#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
|
||||
static uptr g_tls_size;
|
||||
#endif
|
||||
|
@ -299,11 +180,11 @@ void InitTlsSize() {
|
|||
static atomic_uintptr_t kThreadDescriptorSize;
|
||||
|
||||
uptr ThreadDescriptorSize() {
|
||||
char buf[64];
|
||||
uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed);
|
||||
if (val)
|
||||
return val;
|
||||
#ifdef _CS_GNU_LIBC_VERSION
|
||||
char buf[64];
|
||||
uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
|
||||
if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) {
|
||||
char *end;
|
||||
|
@ -468,6 +349,10 @@ uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
|
|||
#else // SANITIZER_ANDROID
|
||||
# if !SANITIZER_FREEBSD
|
||||
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
|
||||
|
||||
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) {
|
||||
uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
|
||||
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;
|
||||
|
@ -527,6 +413,19 @@ void SetIndirectCallWrapper(uptr 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
|
||||
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
|
|
|
@ -129,6 +129,14 @@ int internal_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
|
||||
bool FileExists(const char *filename) {
|
||||
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
|
||||
#endif
|
||||
|
||||
#if SANITIZER_FREEBSD
|
||||
# define SI_FREEBSD 1
|
||||
#else
|
||||
# define SI_FREEBSD 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
# define SI_LINUX 1
|
||||
#else
|
||||
|
@ -73,11 +79,11 @@
|
|||
#define SANITIZER_INTERCEPT_STRPTIME 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
|
||||
# define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS
|
||||
# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX
|
||||
# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID
|
||||
#endif
|
||||
|
||||
#define SANITIZER_INTERCEPT_FREXP 1
|
||||
|
@ -86,10 +92,10 @@
|
|||
#define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS
|
||||
#define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \
|
||||
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_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_GETITIMER SI_NOT_WINDOWS
|
||||
#define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS
|
||||
|
@ -102,9 +108,12 @@
|
|||
#define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS
|
||||
#define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS
|
||||
#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_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_RECVMSG SI_NOT_WINDOWS
|
||||
#define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS
|
||||
|
@ -117,13 +126,13 @@
|
|||
(defined(__i386) || defined (__x86_64)) // NOLINT
|
||||
#define SANITIZER_INTERCEPT_SETLOCALE 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_MBSTOWCS SI_NOT_WINDOWS
|
||||
#define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS
|
||||
#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_CANONICALIZE_FILE_NAME 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_SIGWAITINFO 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_SIGPROCMASK SI_NOT_WINDOWS
|
||||
#define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
|
||||
#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 \
|
||||
(SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID
|
||||
#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_SHMCTL \
|
||||
(SI_LINUX_NOT_ANDROID && SANITIZER_WORDSIZE == 64)
|
||||
|
@ -161,6 +172,19 @@
|
|||
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
|
||||
SI_MAC || 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_R SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS
|
||||
|
@ -168,6 +192,7 @@
|
|||
#define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS
|
||||
#define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS
|
||||
#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_RAND_R SI_MAC || 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
|
||||
#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_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID
|
||||
|
@ -201,5 +226,10 @@
|
|||
#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_FFLUSH 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
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
// are not defined anywhere in userspace headers. Fake them. This seems to work
|
||||
// fine with newer headers, too.
|
||||
#include <asm/posix_types.h>
|
||||
#if defined(__x86_64__)
|
||||
#if defined(__x86_64__) || defined(__mips__)
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#define ino_t __kernel_ino_t
|
||||
|
@ -48,21 +48,19 @@
|
|||
|
||||
#include <linux/aio_abi.h>
|
||||
|
||||
#if SANITIZER_ANDROID
|
||||
#include <asm/statfs.h>
|
||||
#else
|
||||
#include <sys/statfs.h>
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
#include <sys/statfs.h>
|
||||
#include <linux/perf_event.h>
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
#if !SANITIZER_ANDROID
|
||||
unsigned struct_statfs64_sz = sizeof(struct statfs64);
|
||||
#endif
|
||||
} // 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));
|
||||
#endif
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/times.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
|
@ -43,6 +42,7 @@
|
|||
|
||||
#if !SANITIZER_ANDROID
|
||||
#include <sys/mount.h>
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
|
@ -189,13 +189,14 @@ namespace __sanitizer {
|
|||
unsigned struct_tms_sz = sizeof(struct tms);
|
||||
unsigned struct_sigevent_sz = sizeof(struct sigevent);
|
||||
unsigned struct_sched_param_sz = sizeof(struct sched_param);
|
||||
unsigned struct_statfs_sz = sizeof(struct statfs);
|
||||
|
||||
|
||||
#if SANITIZER_MAC && !SANITIZER_IOS
|
||||
unsigned struct_statfs64_sz = sizeof(struct statfs64);
|
||||
#endif // SANITIZER_MAC && !SANITIZER_IOS
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
unsigned struct_statfs_sz = sizeof(struct statfs);
|
||||
unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
|
||||
unsigned ucontext_t_sz = sizeof(ucontext_t);
|
||||
#endif // !SANITIZER_ANDROID
|
||||
|
@ -287,6 +288,7 @@ namespace __sanitizer {
|
|||
int ptrace_setfpregs = PTRACE_SETFPREGS;
|
||||
int ptrace_getfpxregs = PTRACE_GETFPXREGS;
|
||||
int ptrace_setfpxregs = PTRACE_SETFPXREGS;
|
||||
int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
|
||||
#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
|
||||
(defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
|
||||
int ptrace_getsiginfo = PTRACE_GETSIGINFO;
|
||||
|
@ -396,7 +398,7 @@ namespace __sanitizer {
|
|||
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
|
||||
#endif
|
||||
|
||||
unsigned IOCTL_NOT_PRESENT = 0;
|
||||
const unsigned IOCTL_NOT_PRESENT = 0;
|
||||
|
||||
unsigned IOCTL_FIOASYNC = FIOASYNC;
|
||||
unsigned IOCTL_FIOCLEX = FIOCLEX;
|
||||
|
@ -1056,6 +1058,10 @@ CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
|
|||
|
||||
CHECK_TYPE_SIZE(clock_t);
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
CHECK_TYPE_SIZE(clockid_t);
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
CHECK_TYPE_SIZE(ifaddrs);
|
||||
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));
|
||||
#endif
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
CHECK_TYPE_SIZE(timeb);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, time);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, millitm);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, timezone);
|
||||
CHECK_SIZE_AND_OFFSET(timeb, dstflag);
|
||||
#endif
|
||||
|
||||
CHECK_TYPE_SIZE(passwd);
|
||||
CHECK_SIZE_AND_OFFSET(passwd, pw_name);
|
||||
|
|
|
@ -37,11 +37,11 @@ namespace __sanitizer {
|
|||
extern unsigned struct_itimerspec_sz;
|
||||
extern unsigned struct_sigevent_sz;
|
||||
extern unsigned struct_sched_param_sz;
|
||||
extern unsigned struct_statfs_sz;
|
||||
extern unsigned struct_statfs64_sz;
|
||||
extern unsigned struct_sockaddr_sz;
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
extern unsigned struct_statfs_sz;
|
||||
extern unsigned struct_sockaddr_sz;
|
||||
extern unsigned ucontext_t_sz;
|
||||
#endif // !SANITIZER_ANDROID
|
||||
|
||||
|
@ -65,6 +65,13 @@ namespace __sanitizer {
|
|||
#elif defined(__powerpc64__)
|
||||
const unsigned struct_kernel_stat_sz = 144;
|
||||
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
|
||||
struct __sanitizer_perf_event_attr {
|
||||
unsigned type;
|
||||
|
@ -160,6 +167,12 @@ namespace __sanitizer {
|
|||
unsigned __seq;
|
||||
u64 __unused1;
|
||||
u64 __unused2;
|
||||
#elif defined(__mips__)
|
||||
unsigned int mode;
|
||||
unsigned short __seq;
|
||||
unsigned short __pad1;
|
||||
unsigned long __unused1;
|
||||
unsigned long __unused2;
|
||||
#else
|
||||
unsigned short mode;
|
||||
unsigned short __pad1;
|
||||
|
@ -188,15 +201,15 @@ namespace __sanitizer {
|
|||
u64 shm_ctime;
|
||||
#else
|
||||
uptr shm_atime;
|
||||
#ifndef _LP64
|
||||
#if !defined(_LP64) && !defined(__mips__)
|
||||
uptr __unused1;
|
||||
#endif
|
||||
uptr shm_dtime;
|
||||
#ifndef _LP64
|
||||
#if !defined(_LP64) && !defined(__mips__)
|
||||
uptr __unused2;
|
||||
#endif
|
||||
uptr shm_ctime;
|
||||
#ifndef _LP64
|
||||
#if !defined(_LP64) && !defined(__mips__)
|
||||
uptr __unused3;
|
||||
#endif
|
||||
#endif
|
||||
|
@ -438,8 +451,13 @@ namespace __sanitizer {
|
|||
typedef long __sanitizer_clock_t;
|
||||
#endif
|
||||
|
||||
#if SANITIZER_LINUX
|
||||
typedef int __sanitizer_clockid_t;
|
||||
#endif
|
||||
|
||||
#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_gid_t;
|
||||
#else
|
||||
|
@ -452,7 +470,7 @@ namespace __sanitizer {
|
|||
typedef long __sanitizer___kernel_off_t;
|
||||
#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_gid_t;
|
||||
#else
|
||||
|
@ -492,6 +510,9 @@ namespace __sanitizer {
|
|||
|
||||
// Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
|
||||
struct __sanitizer_sigaction {
|
||||
#if defined(__mips__) && !SANITIZER_FREEBSD
|
||||
unsigned int sa_flags;
|
||||
#endif
|
||||
union {
|
||||
void (*sigaction)(int sig, void *siginfo, void *uctx);
|
||||
void (*handler)(int sig);
|
||||
|
@ -501,10 +522,15 @@ namespace __sanitizer {
|
|||
__sanitizer_sigset_t sa_mask;
|
||||
#else
|
||||
__sanitizer_sigset_t sa_mask;
|
||||
#ifndef __mips__
|
||||
int sa_flags;
|
||||
#endif
|
||||
#endif
|
||||
#if SANITIZER_LINUX
|
||||
void (*sa_restorer)();
|
||||
#endif
|
||||
#if defined(__mips__) && (SANITIZER_WORDSIZE == 32)
|
||||
int sa_resv[1];
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -676,6 +702,7 @@ namespace __sanitizer {
|
|||
extern int ptrace_setsiginfo;
|
||||
extern int ptrace_getregset;
|
||||
extern int ptrace_setregset;
|
||||
extern int ptrace_geteventmsg;
|
||||
#endif
|
||||
|
||||
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
|
||||
|
@ -718,7 +745,7 @@ struct __sanitizer_obstack {
|
|||
|
||||
#define IOC_NRBITS 8
|
||||
#define IOC_TYPEBITS 8
|
||||
#if defined(__powerpc__) || defined(__powerpc64__)
|
||||
#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__)
|
||||
#define IOC_SIZEBITS 13
|
||||
#define IOC_DIRBITS 3
|
||||
#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,
|
||||
// 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_FIOCLEX;
|
||||
|
|
|
@ -204,6 +204,17 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) {
|
|||
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,
|
||||
uptr start2, uptr end2) {
|
||||
|
|
|
@ -42,30 +42,49 @@ void FlushUnneededShadowMemory(uptr addr, uptr size) {
|
|||
madvise((void*)addr, size, MADV_DONTNEED);
|
||||
}
|
||||
|
||||
void DisableCoreDumper() {
|
||||
struct rlimit nocore;
|
||||
nocore.rlim_cur = 0;
|
||||
nocore.rlim_max = 0;
|
||||
setrlimit(RLIMIT_CORE, &nocore);
|
||||
static rlim_t getlim(int res) {
|
||||
rlimit rlim;
|
||||
CHECK_EQ(0, getrlimit(res, &rlim));
|
||||
return rlim.rlim_cur;
|
||||
}
|
||||
|
||||
bool StackSizeIsUnlimited() {
|
||||
struct rlimit rlim;
|
||||
CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim));
|
||||
return ((uptr)rlim.rlim_cur == (uptr)-1);
|
||||
}
|
||||
|
||||
void SetStackSizeLimitInBytes(uptr limit) {
|
||||
struct rlimit rlim;
|
||||
rlim.rlim_cur = limit;
|
||||
rlim.rlim_max = limit;
|
||||
if (setrlimit(RLIMIT_STACK, &rlim)) {
|
||||
static void setlim(int res, rlim_t lim) {
|
||||
// The following magic is to prevent clang from replacing it with memset.
|
||||
volatile struct rlimit rlim;
|
||||
rlim.rlim_cur = lim;
|
||||
rlim.rlim_max = lim;
|
||||
if (setrlimit(res, (struct rlimit*)&rlim)) {
|
||||
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
|
||||
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());
|
||||
}
|
||||
|
||||
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) {
|
||||
sleep(seconds);
|
||||
}
|
||||
|
@ -127,7 +146,9 @@ static void MaybeInstallSigaction(int signum,
|
|||
struct sigaction sigact;
|
||||
internal_memset(&sigact, 0, sizeof(sigact));
|
||||
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;
|
||||
CHECK_EQ(0, internal_sigaction(signum, &sigact, 0));
|
||||
VReport(1, "Installed the sigaction for signal %d\n", signum);
|
||||
|
@ -143,6 +164,28 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) {
|
|||
}
|
||||
#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
|
||||
|
||||
#endif // SANITIZER_POSIX
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
#include <stdio.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))
|
||||
#endif
|
||||
|
||||
|
|
|
@ -24,6 +24,9 @@ struct ProcSelfMapsBuff {
|
|||
uptr mmaped_size;
|
||||
uptr len;
|
||||
};
|
||||
|
||||
// Reads process memory map in an OS-specific way.
|
||||
void ReadProcMaps(ProcSelfMapsBuff *proc_maps);
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
|
||||
class MemoryMappingLayout {
|
||||
|
@ -55,7 +58,7 @@ class MemoryMappingLayout {
|
|||
// platform-specific files.
|
||||
# if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
ProcSelfMapsBuff proc_self_maps_;
|
||||
char *current_;
|
||||
const char *current_;
|
||||
|
||||
// Static mappings cache.
|
||||
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.
|
||||
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
|
||||
|
||||
#endif // SANITIZER_PROCMAPS_H
|
||||
|
|
176
libsanitizer/sanitizer_common/sanitizer_procmaps_common.cc
Normal file
176
libsanitizer/sanitizer_common/sanitizer_procmaps_common.cc
Normal file
|
@ -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
|
86
libsanitizer/sanitizer_common/sanitizer_procmaps_freebsd.cc
Normal file
86
libsanitizer/sanitizer_common/sanitizer_procmaps_freebsd.cc
Normal file
|
@ -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"
|
||||
#if SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
#if SANITIZER_LINUX
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_procmaps.h"
|
||||
|
||||
#if SANITIZER_FREEBSD
|
||||
#include <unistd.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Linker initialized.
|
||||
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
|
||||
void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
|
||||
proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
|
||||
&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) {
|
||||
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,
|
||||
char filename[], uptr filename_size,
|
||||
|
@ -165,29 +34,6 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
|||
if (!end) end = &dummy;
|
||||
if (!offset) offset = &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_);
|
||||
if (next_line == 0)
|
||||
next_line = last;
|
||||
|
@ -234,69 +80,9 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
|
|||
if (filename && i < filename_size)
|
||||
filename[i] = 0;
|
||||
current_ = next_line + 1;
|
||||
#endif // !SANITIZER_FREEBSD
|
||||
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
|
||||
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
#endif // SANITIZER_LINUX
|
||||
|
|
|
@ -73,14 +73,16 @@ template<u32 kLCSegment, typename SegmentCommand>
|
|||
bool MemoryMappingLayout::NextSegmentLoad(
|
||||
uptr *start, uptr *end, uptr *offset,
|
||||
char filename[], uptr filename_size, uptr *protection) {
|
||||
if (protection)
|
||||
UNIMPLEMENTED();
|
||||
const char* lc = current_load_cmd_addr_;
|
||||
current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize;
|
||||
if (((const load_command *)lc)->cmd == kLCSegment) {
|
||||
const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_);
|
||||
const SegmentCommand* sc = (const SegmentCommand *)lc;
|
||||
if (start) *start = sc->vmaddr + dlloff;
|
||||
if (protection) {
|
||||
// Return the initial protection.
|
||||
*protection = sc->initprot;
|
||||
}
|
||||
if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
|
||||
if (offset) {
|
||||
if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
|
||||
|
@ -155,12 +157,12 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
|
|||
uptr max_modules,
|
||||
string_predicate_t filter) {
|
||||
Reset();
|
||||
uptr cur_beg, cur_end;
|
||||
uptr cur_beg, cur_end, prot;
|
||||
InternalScopedBuffer<char> module_name(kMaxPathLength);
|
||||
uptr n_modules = 0;
|
||||
for (uptr i = 0; n_modules < max_modules &&
|
||||
Next(&cur_beg, &cur_end, 0, module_name.data(),
|
||||
module_name.size(), 0);
|
||||
module_name.size(), &prot);
|
||||
i++) {
|
||||
const char *cur_name = module_name.data();
|
||||
if (cur_name[0] == '\0')
|
||||
|
@ -176,7 +178,7 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
|
|||
cur_module = new(mem) LoadedModule(cur_name, cur_beg);
|
||||
n_modules++;
|
||||
}
|
||||
cur_module->addAddressRange(cur_beg, cur_end);
|
||||
cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
|
||||
}
|
||||
return n_modules;
|
||||
}
|
||||
|
|
|
@ -18,12 +18,16 @@
|
|||
#include "sanitizer_common.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
class AnsiColorDecorator {
|
||||
class SanitizerCommonDecorator {
|
||||
// FIXME: This is not portable. It assumes the special strings are printed to
|
||||
// stdout, which is not the case on Windows (see SetConsoleTextAttribute()).
|
||||
public:
|
||||
explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { }
|
||||
SanitizerCommonDecorator() : ansi_(ColorizeReports()) {}
|
||||
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 *Red() const { return ansi_ ? "\033[1m\033[31m" : ""; }
|
||||
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 *Cyan() const { return ansi_ ? "\033[1m\033[36m" : ""; }
|
||||
const char *White() const { return ansi_ ? "\033[1m\033[37m" : ""; }
|
||||
const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; }
|
||||
private:
|
||||
bool ansi_;
|
||||
};
|
||||
|
||||
class SanitizerCommonDecorator: protected AnsiColorDecorator {
|
||||
public:
|
||||
SanitizerCommonDecorator()
|
||||
: __sanitizer::AnsiColorDecorator(ColorizeReports()) { }
|
||||
const char *Warning() { return Red(); }
|
||||
const char *EndWarning() { return Default(); }
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_REPORT_DECORATOR_H
|
||||
|
|
|
@ -10,193 +10,128 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_stackdepot.h"
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_mutex.h"
|
||||
#include "sanitizer_atomic.h"
|
||||
#include "sanitizer_stackdepotbase.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
const int kTabSize = 1024 * 1024; // Hash table size.
|
||||
const int kPartBits = 8;
|
||||
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;
|
||||
struct StackDepotDesc {
|
||||
const uptr *stack;
|
||||
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 {
|
||||
StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator.
|
||||
atomic_uintptr_t region_pos; // Region allocator for StackDesc's.
|
||||
atomic_uintptr_t region_end;
|
||||
atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's.
|
||||
atomic_uint32_t seq[kPartCount]; // Unique id generators.
|
||||
} depot;
|
||||
struct StackDepotNode {
|
||||
StackDepotNode *link;
|
||||
u32 id;
|
||||
atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20;
|
||||
uptr size;
|
||||
uptr stack[1]; // [size]
|
||||
|
||||
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() {
|
||||
return &stats;
|
||||
}
|
||||
|
||||
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);
|
||||
return theDepot.GetStats();
|
||||
}
|
||||
|
||||
u32 StackDepotPut(const uptr *stack, uptr size) {
|
||||
if (stack == 0 || size == 0)
|
||||
return 0;
|
||||
uptr h = hash(stack, size);
|
||||
atomic_uintptr_t *p = &depot.tab[h % kTabSize];
|
||||
uptr v = atomic_load(p, memory_order_consume);
|
||||
StackDesc *s = (StackDesc*)(v & ~1);
|
||||
// First, try to find the existing stack.
|
||||
u32 id = find(s, stack, size, h);
|
||||
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;
|
||||
StackDepotDesc desc = {stack, size};
|
||||
StackDepotHandle h = theDepot.Put(desc);
|
||||
return h.valid() ? h.id() : 0;
|
||||
}
|
||||
|
||||
StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size) {
|
||||
StackDepotDesc desc = {stack, size};
|
||||
return theDepot.Put(desc);
|
||||
}
|
||||
|
||||
const uptr *StackDepotGet(u32 id, uptr *size) {
|
||||
if (id == 0)
|
||||
return 0;
|
||||
CHECK_EQ(id & (1u << 31), 0);
|
||||
// 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 = &depot.tab[idx];
|
||||
uptr v = atomic_load(p, memory_order_consume);
|
||||
StackDesc *s = (StackDesc*)(v & ~1);
|
||||
for (; s; s = s->link) {
|
||||
if (s->id == id) {
|
||||
*size = s->size;
|
||||
return s->stack;
|
||||
}
|
||||
}
|
||||
}
|
||||
*size = 0;
|
||||
return 0;
|
||||
StackDepotDesc desc = theDepot.Get(id);
|
||||
*size = desc.size;
|
||||
return desc.stack;
|
||||
}
|
||||
|
||||
void StackDepotLockAll() {
|
||||
theDepot.LockAll();
|
||||
}
|
||||
|
||||
void StackDepotUnlockAll() {
|
||||
theDepot.UnlockAll();
|
||||
}
|
||||
|
||||
bool StackDepotReverseMap::IdDescPair::IdComparator(
|
||||
|
@ -207,10 +142,10 @@ bool StackDepotReverseMap::IdDescPair::IdComparator(
|
|||
|
||||
StackDepotReverseMap::StackDepotReverseMap()
|
||||
: map_(StackDepotGetStats()->n_uniq_ids + 100) {
|
||||
for (int idx = 0; idx < kTabSize; idx++) {
|
||||
atomic_uintptr_t *p = &depot.tab[idx];
|
||||
for (int idx = 0; idx < StackDepot::kTabSize; idx++) {
|
||||
atomic_uintptr_t *p = &theDepot.tab[idx];
|
||||
uptr v = atomic_load(p, memory_order_consume);
|
||||
StackDesc *s = (StackDesc*)(v & ~1);
|
||||
StackDepotNode *s = (StackDepotNode*)(v & ~1);
|
||||
for (; s; s = s->link) {
|
||||
IdDescPair pair = {s->id, s};
|
||||
map_.push_back(pair);
|
||||
|
@ -228,7 +163,7 @@ const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) {
|
|||
*size = 0;
|
||||
return 0;
|
||||
}
|
||||
StackDesc *desc = map_[idx].desc;
|
||||
StackDepotNode *desc = map_[idx].desc;
|
||||
*size = desc->size;
|
||||
return desc->stack;
|
||||
}
|
||||
|
|
|
@ -17,20 +17,29 @@
|
|||
namespace __sanitizer {
|
||||
|
||||
// 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);
|
||||
StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size);
|
||||
// Retrieves a stored stack trace by the id.
|
||||
const uptr *StackDepotGet(u32 id, uptr *size);
|
||||
|
||||
struct StackDepotStats {
|
||||
uptr n_uniq_ids;
|
||||
uptr mapped;
|
||||
};
|
||||
|
||||
StackDepotStats *StackDepotGetStats();
|
||||
|
||||
struct StackDesc;
|
||||
void StackDepotLockAll();
|
||||
void StackDepotUnlockAll();
|
||||
|
||||
// Instantiating this class creates a snapshot of StackDepot which can be
|
||||
// efficiently queried with StackDepotGet(). You can use it concurrently with
|
||||
|
@ -44,7 +53,7 @@ class StackDepotReverseMap {
|
|||
private:
|
||||
struct IdDescPair {
|
||||
u32 id;
|
||||
StackDesc *desc;
|
||||
StackDepotNode *desc;
|
||||
|
||||
static bool IdComparator(const IdDescPair &a, const IdDescPair &b);
|
||||
};
|
||||
|
@ -55,6 +64,7 @@ class StackDepotReverseMap {
|
|||
StackDepotReverseMap(const StackDepotReverseMap&);
|
||||
void operator=(const StackDepotReverseMap&);
|
||||
};
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_STACKDEPOT_H
|
||||
|
|
174
libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h
Normal file
174
libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h
Normal file
|
@ -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__)
|
||||
// Cancel Thumb bit.
|
||||
pc = pc & (~1);
|
||||
#elif defined(__powerpc__) || defined(__powerpc64__)
|
||||
#endif
|
||||
#if defined(__powerpc__) || defined(__powerpc64__)
|
||||
// PCs are always 4 byte aligned.
|
||||
return pc - 4;
|
||||
#elif defined(__sparc__)
|
||||
|
@ -33,19 +34,39 @@ uptr StackTrace::GetCurrentPc() {
|
|||
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,
|
||||
uptr stack_top, uptr stack_bottom,
|
||||
uptr max_depth) {
|
||||
CHECK_GE(max_depth, 2);
|
||||
trace[0] = pc;
|
||||
size = 1;
|
||||
uhwptr *frame = (uhwptr *)bp;
|
||||
uhwptr *prev_frame = frame - 1;
|
||||
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.
|
||||
while (frame > prev_frame &&
|
||||
frame < (uhwptr *)stack_top - 2 &&
|
||||
frame > (uhwptr *)stack_bottom &&
|
||||
IsValidFrame((uptr)frame, stack_top, stack_bottom) &&
|
||||
IsAligned((uptr)frame, sizeof(*frame)) &&
|
||||
size < max_depth) {
|
||||
uhwptr pc1 = frame[1];
|
||||
|
@ -53,7 +74,7 @@ void StackTrace::FastUnwindStack(uptr pc, uptr bp,
|
|||
trace[size++] = (uptr) pc1;
|
||||
}
|
||||
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) {
|
||||
// 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)
|
||||
return false;
|
||||
else if (SANITIZER_MAC)
|
||||
else if (SANITIZER_MAC != 0 || SANITIZER_FREEBSD != 0)
|
||||
return true;
|
||||
return request_fast_unwind;
|
||||
}
|
||||
|
@ -83,6 +86,10 @@ struct StackTrace {
|
|||
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
|
||||
// function in the top frame.
|
||||
#define GET_CURRENT_PC_BP_SP \
|
||||
|
|
|
@ -35,6 +35,14 @@ void StackTrace::PrintStack(const uptr *addr, uptr size) {
|
|||
uptr pc = GetPreviousInstructionPc(addr[i]);
|
||||
uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
|
||||
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++) {
|
||||
AddressInfo &info = addr_frames[j];
|
||||
frame_desc.clear();
|
||||
|
|
|
@ -13,13 +13,15 @@
|
|||
|
||||
#include "sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_flags.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
static const char *const kTypeStrings[SuppressionTypeCount] = {
|
||||
"none", "race", "mutex", "thread",
|
||||
"signal", "leak", "called_from_lib", "deadlock"};
|
||||
"none", "race", "mutex", "thread", "signal",
|
||||
"leak", "called_from_lib", "deadlock", "vptr_check"};
|
||||
|
||||
bool TemplateMatch(char *templ, const char *str) {
|
||||
if (str == 0 || str[0] == 0)
|
||||
|
@ -63,6 +65,33 @@ bool TemplateMatch(char *templ, const char *str) {
|
|||
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,
|
||||
Suppression **s) {
|
||||
can_parse_ = false;
|
||||
|
|
|
@ -25,6 +25,7 @@ enum SuppressionType {
|
|||
SuppressionLeak,
|
||||
SuppressionLib,
|
||||
SuppressionDeadlock,
|
||||
SuppressionVptrCheck,
|
||||
SuppressionTypeCount
|
||||
};
|
||||
|
||||
|
@ -37,14 +38,21 @@ struct Suppression {
|
|||
|
||||
class SuppressionContext {
|
||||
public:
|
||||
SuppressionContext() : suppressions_(1), can_parse_(true) {}
|
||||
void Parse(const char *str);
|
||||
bool Match(const char* str, SuppressionType type, Suppression **s);
|
||||
uptr SuppressionCount() const;
|
||||
const Suppression *SuppressionAt(uptr i) const;
|
||||
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:
|
||||
SuppressionContext() : suppressions_(1), can_parse_(true) {}
|
||||
InternalMmapVector<Suppression> suppressions_;
|
||||
bool can_parse_;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue