[libsanitizer merge from upstream r218156]

From-SVN: r215527
This commit is contained in:
Kostya Serebryany 2014-09-23 17:59:53 +00:00 committed by Kostya Serebryany
parent e8ee40544a
commit 866e32ad33
163 changed files with 6382 additions and 3778 deletions

View File

@ -1,3 +1,12 @@
2014-09-23 Kostya Serebryany <kcc@google.com>
Update to match the changed asan API.
* asan.c (asan_global_struct): Update the __asan_global definition
to match the new API.
(asan_add_global): Ditto.
* sanitizer.def (BUILT_IN_ASAN_INIT): Rename __asan_init_v3
to __asan_init_v4.
2014-09-23 Michael Meissner <meissner@linux.vnet.ibm.com> 2014-09-23 Michael Meissner <meissner@linux.vnet.ibm.com>
* config/rs6000/rs6000.md (f32_vsx): New mode attributes to * config/rs6000/rs6000.md (f32_vsx): New mode attributes to

View File

@ -230,6 +230,9 @@ along with GCC; see the file COPYING3. If not see
// 1 if it has dynamic initialization, 0 otherwise. // 1 if it has dynamic initialization, 0 otherwise.
uptr __has_dynamic_init; uptr __has_dynamic_init;
// A pointer to struct that contains source location, could be NULL.
__asan_global_source_location *__location;
} }
A destructor function that calls the runtime asan library function A destructor function that calls the runtime asan library function
@ -2136,19 +2139,20 @@ asan_dynamic_init_call (bool after_p)
const void *__name; const void *__name;
const void *__module_name; const void *__module_name;
uptr __has_dynamic_init; uptr __has_dynamic_init;
__asan_global_source_location *__location;
} type. */ } type. */
static tree static tree
asan_global_struct (void) asan_global_struct (void)
{ {
static const char *field_names[6] static const char *field_names[7]
= { "__beg", "__size", "__size_with_redzone", = { "__beg", "__size", "__size_with_redzone",
"__name", "__module_name", "__has_dynamic_init" }; "__name", "__module_name", "__has_dynamic_init", "__location"};
tree fields[6], ret; tree fields[7], ret;
int i; int i;
ret = make_node (RECORD_TYPE); ret = make_node (RECORD_TYPE);
for (i = 0; i < 6; i++) for (i = 0; i < 7; i++)
{ {
fields[i] fields[i]
= build_decl (UNKNOWN_LOCATION, FIELD_DECL, = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
@ -2220,6 +2224,8 @@ asan_add_global (tree decl, tree type, vec<constructor_elt, va_gc> *v)
int has_dynamic_init = vnode ? vnode->dynamically_initialized : 0; int has_dynamic_init = vnode ? vnode->dynamically_initialized : 0;
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE, CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
build_int_cst (uptr, has_dynamic_init)); build_int_cst (uptr, has_dynamic_init));
CONSTRUCTOR_APPEND_ELT (vinner, NULL_TREE,
build_int_cst (uptr, 0));
init = build_constructor (type, vinner); init = build_constructor (type, vinner);
CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init); CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init);
} }

View File

@ -27,7 +27,7 @@ along with GCC; see the file COPYING3. If not see
for other FEs by asan.c. */ for other FEs by asan.c. */
/* Address Sanitizer */ /* Address Sanitizer */
DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init_v3", DEF_SANITIZER_BUILTIN(BUILT_IN_ASAN_INIT, "__asan_init_v4",
BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST) BT_FN_VOID, ATTR_NOTHROW_LEAF_LIST)
/* Do not reorder the BUILT_IN_ASAN_{REPORT,CHECK}* builtins, e.g. cfgcleanup.c /* Do not reorder the BUILT_IN_ASAN_{REPORT,CHECK}* builtins, e.g. cfgcleanup.c
relies on this order. */ relies on this order. */

View File

@ -1,3 +1,17 @@
2014-09-19 Kostya Serebryany <kcc@google.com>
* All source files: Merge from upstream r218156.
* asan/Makefile.am (asan_files): Added new files.
* asan/Makefile.in: Regenerate.
* ubsan/Makefile.am (ubsan_files): Added new files.
* ubsan/Makefile.in: Regenerate.
* tsan/Makefile.am (tsan_files): Added new files.
* tsan/Makefile.in: Regenerate.
* sanitizer_common/Makefile.am (sanitizer_common_files): Added new
files.
* sanitizer_common/Makefile.in: Regenerate.
* asan/libtool-version: Bump the libasan SONAME.
2014-09-10 Jakub Jelinek <jakub@redhat.com> 2014-09-10 Jakub Jelinek <jakub@redhat.com>
* ubsan/ubsan_handlers.cc, ubsan/ubsan_handlers.h: Cherry pick * ubsan/ubsan_handlers.cc, ubsan/ubsan_handlers.h: Cherry pick

View File

@ -1,4 +1,4 @@
209283 218156
The first line of this file holds the svn revision number of the The first line of this file holds the svn revision number of the
last merge done from the master library sources. last merge done from the master library sources.

View File

@ -17,7 +17,7 @@ nodist_toolexeclib_HEADERS = libasan_preinit.o
asan_files = \ asan_files = \
asan_activation.cc \ asan_activation.cc \
asan_allocator2.cc \ asan_allocator2.cc \
asan_dll_thunk.cc \ asan_debugging.cc \
asan_fake_stack.cc \ asan_fake_stack.cc \
asan_globals.cc \ asan_globals.cc \
asan_interceptors.cc \ asan_interceptors.cc \
@ -34,7 +34,9 @@ asan_files = \
asan_stack.cc \ asan_stack.cc \
asan_stats.cc \ asan_stats.cc \
asan_thread.cc \ asan_thread.cc \
asan_win.cc asan_win.cc \
asan_win_dll_thunk.cc \
asan_win_dynamic_runtime_thunk.cc
libasan_la_SOURCES = $(asan_files) libasan_la_SOURCES = $(asan_files)
libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la

View File

@ -89,12 +89,13 @@ libasan_la_DEPENDENCIES = \
$(top_builddir)/lsan/libsanitizer_lsan.la $(am__append_2) \ $(top_builddir)/lsan/libsanitizer_lsan.la $(am__append_2) \
$(am__append_3) $(am__DEPENDENCIES_1) $(am__append_3) $(am__DEPENDENCIES_1)
am__objects_1 = asan_activation.lo asan_allocator2.lo \ am__objects_1 = asan_activation.lo asan_allocator2.lo \
asan_dll_thunk.lo asan_fake_stack.lo asan_globals.lo \ asan_debugging.lo asan_fake_stack.lo asan_globals.lo \
asan_interceptors.lo asan_linux.lo asan_mac.lo \ asan_interceptors.lo asan_linux.lo asan_mac.lo \
asan_malloc_linux.lo asan_malloc_mac.lo asan_malloc_win.lo \ asan_malloc_linux.lo asan_malloc_mac.lo asan_malloc_win.lo \
asan_new_delete.lo asan_poisoning.lo asan_posix.lo \ asan_new_delete.lo asan_poisoning.lo asan_posix.lo \
asan_report.lo asan_rtl.lo asan_stack.lo asan_stats.lo \ asan_report.lo asan_rtl.lo asan_stack.lo asan_stats.lo \
asan_thread.lo asan_win.lo asan_thread.lo asan_win.lo asan_win_dll_thunk.lo \
asan_win_dynamic_runtime_thunk.lo
am_libasan_la_OBJECTS = $(am__objects_1) am_libasan_la_OBJECTS = $(am__objects_1)
libasan_la_OBJECTS = $(am_libasan_la_OBJECTS) libasan_la_OBJECTS = $(am_libasan_la_OBJECTS)
libasan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ libasan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
@ -275,7 +276,7 @@ nodist_toolexeclib_HEADERS = libasan_preinit.o
asan_files = \ asan_files = \
asan_activation.cc \ asan_activation.cc \
asan_allocator2.cc \ asan_allocator2.cc \
asan_dll_thunk.cc \ asan_debugging.cc \
asan_fake_stack.cc \ asan_fake_stack.cc \
asan_globals.cc \ asan_globals.cc \
asan_interceptors.cc \ asan_interceptors.cc \
@ -292,7 +293,9 @@ asan_files = \
asan_stack.cc \ asan_stack.cc \
asan_stats.cc \ asan_stats.cc \
asan_thread.cc \ asan_thread.cc \
asan_win.cc asan_win.cc \
asan_win_dll_thunk.cc \
asan_win_dynamic_runtime_thunk.cc
libasan_la_SOURCES = $(asan_files) libasan_la_SOURCES = $(asan_files)
libasan_la_LIBADD = \ libasan_la_LIBADD = \
@ -416,7 +419,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator2.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_dll_thunk.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_debugging.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@
@ -434,6 +437,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dll_thunk.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@
.cc.o: .cc.o:
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<

View File

@ -140,6 +140,8 @@ struct AsanThreadLocalMallocStorage {
void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
AllocType alloc_type); AllocType alloc_type);
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type); void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
void asan_sized_free(void *ptr, uptr size, StackTrace *stack,
AllocType alloc_type);
void *asan_malloc(uptr size, StackTrace *stack); void *asan_malloc(uptr size, StackTrace *stack);
void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack); void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack);

View File

@ -19,6 +19,7 @@
#include "asan_report.h" #include "asan_report.h"
#include "asan_stack.h" #include "asan_stack.h"
#include "asan_thread.h" #include "asan_thread.h"
#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_list.h" #include "sanitizer_common/sanitizer_list.h"
@ -165,23 +166,6 @@ struct AsanChunk: ChunkBase {
} }
return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log)); return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
} }
// If we don't use stack depot, we store the alloc/free stack traces
// in the chunk itself.
u32 *AllocStackBeg() {
return (u32*)(Beg() - RZLog2Size(rz_log));
}
uptr AllocStackSize() {
CHECK_LE(RZLog2Size(rz_log), kChunkHeaderSize);
return (RZLog2Size(rz_log) - kChunkHeaderSize) / sizeof(u32);
}
u32 *FreeStackBeg() {
return (u32*)(Beg() + kChunkHeader2Size);
}
uptr FreeStackSize() {
if (user_requested_size < kChunkHeader2Size) return 0;
uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY);
return (available - kChunkHeader2Size) / sizeof(u32);
}
bool AddrIsInside(uptr addr, bool locked_version = false) { bool AddrIsInside(uptr addr, bool locked_version = false) {
return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
} }
@ -461,12 +445,17 @@ static void QuarantineChunk(AsanChunk *m, void *ptr,
} }
} }
static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) { static void Deallocate(void *ptr, uptr delete_size, StackTrace *stack,
AllocType alloc_type) {
uptr p = reinterpret_cast<uptr>(ptr); uptr p = reinterpret_cast<uptr>(ptr);
if (p == 0) return; if (p == 0) return;
uptr chunk_beg = p - kChunkHeaderSize; uptr chunk_beg = p - kChunkHeaderSize;
AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg); AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
if (delete_size && flags()->new_delete_type_mismatch &&
delete_size != m->UsedSize()) {
ReportNewDeleteSizeMismatch(p, delete_size, stack);
}
ASAN_FREE_HOOK(ptr); ASAN_FREE_HOOK(ptr);
// Must mark the chunk as quarantined before any changes to its metadata. // Must mark the chunk as quarantined before any changes to its metadata.
AtomicallySetQuarantineFlag(m, ptr, stack); AtomicallySetQuarantineFlag(m, ptr, stack);
@ -493,7 +482,7 @@ static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
// If realloc() races with free(), we may start copying freed memory. // If realloc() races with free(), we may start copying freed memory.
// However, we will report racy double-free later anyway. // However, we will report racy double-free later anyway.
REAL(memcpy)(new_ptr, old_ptr, memcpy_size); REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
Deallocate(old_ptr, stack, FROM_MALLOC); Deallocate(old_ptr, 0, stack, FROM_MALLOC);
} }
return new_ptr; return new_ptr;
} }
@ -592,7 +581,12 @@ void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
} }
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) { void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
Deallocate(ptr, stack, alloc_type); Deallocate(ptr, 0, stack, alloc_type);
}
void asan_sized_free(void *ptr, uptr size, StackTrace *stack,
AllocType alloc_type) {
Deallocate(ptr, size, stack, alloc_type);
} }
void *asan_malloc(uptr size, StackTrace *stack) { void *asan_malloc(uptr size, StackTrace *stack) {
@ -614,7 +608,7 @@ void *asan_realloc(void *p, uptr size, StackTrace *stack) {
if (p == 0) if (p == 0)
return Allocate(size, 8, stack, FROM_MALLOC, true); return Allocate(size, 8, stack, FROM_MALLOC, true);
if (size == 0) { if (size == 0) {
Deallocate(p, stack, FROM_MALLOC); Deallocate(p, 0, stack, FROM_MALLOC);
return 0; return 0;
} }
return Reallocate(p, size, stack); return Reallocate(p, size, stack);
@ -758,23 +752,23 @@ using namespace __asan; // NOLINT
// ASan allocator doesn't reserve extra bytes, so normally we would // ASan allocator doesn't reserve extra bytes, so normally we would
// just return "size". We don't want to expose our redzone sizes, etc here. // just return "size". We don't want to expose our redzone sizes, etc here.
uptr __asan_get_estimated_allocated_size(uptr size) { uptr __sanitizer_get_estimated_allocated_size(uptr size) {
return size; return size;
} }
int __asan_get_ownership(const void *p) { int __sanitizer_get_ownership(const void *p) {
uptr ptr = reinterpret_cast<uptr>(p); uptr ptr = reinterpret_cast<uptr>(p);
return (AllocationSize(ptr) > 0); return (AllocationSize(ptr) > 0);
} }
uptr __asan_get_allocated_size(const void *p) { uptr __sanitizer_get_allocated_size(const void *p) {
if (p == 0) return 0; if (p == 0) return 0;
uptr ptr = reinterpret_cast<uptr>(p); uptr ptr = reinterpret_cast<uptr>(p);
uptr allocated_size = AllocationSize(ptr); uptr allocated_size = AllocationSize(ptr);
// Die if p is not malloced or if it is already freed. // Die if p is not malloced or if it is already freed.
if (allocated_size == 0) { if (allocated_size == 0) {
GET_STACK_TRACE_FATAL_HERE; GET_STACK_TRACE_FATAL_HERE;
ReportAsanGetAllocatedSizeNotOwned(ptr, &stack); ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack);
} }
return allocated_size; return allocated_size;
} }
@ -783,12 +777,12 @@ uptr __asan_get_allocated_size(const void *p) {
// Provide default (no-op) implementation of malloc hooks. // Provide default (no-op) implementation of malloc hooks.
extern "C" { extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
void __asan_malloc_hook(void *ptr, uptr size) { void __sanitizer_malloc_hook(void *ptr, uptr size) {
(void)ptr; (void)ptr;
(void)size; (void)size;
} }
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
void __asan_free_hook(void *ptr) { void __sanitizer_free_hook(void *ptr) {
(void)ptr; (void)ptr;
} }
} // extern "C" } // extern "C"

View File

@ -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__

View 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;
}

View File

@ -50,12 +50,13 @@ struct Flags {
bool print_stats; bool print_stats;
bool print_legend; bool print_legend;
bool atexit; bool atexit;
bool disable_core;
bool allow_reexec; bool allow_reexec;
bool print_full_thread_history; bool print_full_thread_history;
bool poison_heap; bool poison_heap;
bool poison_partial; bool poison_partial;
bool poison_array_cookie;
bool alloc_dealloc_mismatch; bool alloc_dealloc_mismatch;
bool new_delete_type_mismatch;
bool strict_memcmp; bool strict_memcmp;
bool strict_init_order; bool strict_init_order;
bool start_deactivated; bool start_deactivated;

View File

@ -20,6 +20,7 @@
#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
namespace __asan { namespace __asan {
@ -43,6 +44,14 @@ typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals;
// Lazy-initialized and never deleted. // Lazy-initialized and never deleted.
static VectorOfGlobals *dynamic_init_globals; static VectorOfGlobals *dynamic_init_globals;
// We want to remember where a certain range of globals was registered.
struct GlobalRegistrationSite {
u32 stack_id;
Global *g_first, *g_last;
};
typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector;
static GlobalRegistrationSiteVector *global_registration_site_vector;
ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
FastPoisonShadow(g->beg, g->size_with_redzone, value); FastPoisonShadow(g->beg, g->size_with_redzone, value);
} }
@ -61,9 +70,14 @@ ALWAYS_INLINE void PoisonRedZones(const Global &g) {
} }
static void ReportGlobal(const Global &g, const char *prefix) { static void ReportGlobal(const Global &g, const char *prefix) {
Report("%s Global: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n", Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
prefix, (void*)g.beg, g.size, g.size_with_redzone, g.name, prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
g.module_name, g.has_dynamic_init); g.module_name, g.has_dynamic_init);
if (g.location) {
Report(" location (%p): name=%s[%p], %d %d\n", g.location,
g.location->filename, g.location->filename, g.location->line_no,
g.location->column_no);
}
} }
bool DescribeAddressIfGlobal(uptr addr, uptr size) { bool DescribeAddressIfGlobal(uptr addr, uptr size) {
@ -79,6 +93,16 @@ bool DescribeAddressIfGlobal(uptr addr, uptr size) {
return res; return res;
} }
u32 FindRegistrationSite(const Global *g) {
CHECK(global_registration_site_vector);
for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
GlobalRegistrationSite &grs = (*global_registration_site_vector)[i];
if (g >= grs.g_first && g <= grs.g_last)
return grs.stack_id;
}
return 0;
}
// Register a global variable. // Register a global variable.
// This function may be called more than once for every global // This function may be called more than once for every global
// so we store the globals in a map. // so we store the globals in a map.
@ -99,7 +123,8 @@ static void RegisterGlobal(const Global *g) {
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
if (g->beg == l->g->beg && if (g->beg == l->g->beg &&
(flags()->detect_odr_violation >= 2 || g->size != l->g->size)) (flags()->detect_odr_violation >= 2 || g->size != l->g->size))
ReportODRViolation(g, l->g); ReportODRViolation(g, FindRegistrationSite(g),
l->g, FindRegistrationSite(l->g));
} }
} }
} }
@ -155,7 +180,18 @@ using namespace __asan; // NOLINT
// Register an array of globals. // Register an array of globals.
void __asan_register_globals(__asan_global *globals, uptr n) { void __asan_register_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return; if (!flags()->report_globals) return;
GET_STACK_TRACE_FATAL_HERE;
u32 stack_id = StackDepotPut(stack.trace, stack.size);
BlockingMutexLock lock(&mu_for_globals); BlockingMutexLock lock(&mu_for_globals);
if (!global_registration_site_vector)
global_registration_site_vector =
new(allocator_for_globals) GlobalRegistrationSiteVector(128);
GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]};
global_registration_site_vector->push_back(site);
if (flags()->report_globals >= 2) {
PRINT_CURRENT_STACK();
Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]);
}
for (uptr i = 0; i < n; i++) { for (uptr i = 0; i < n; i++) {
RegisterGlobal(&globals[i]); RegisterGlobal(&globals[i]);
} }

View 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

View File

@ -144,6 +144,9 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
} while (false) } while (false)
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res) CovUpdateMapping()
#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CovUpdateMapping()
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited)
#include "sanitizer_common/sanitizer_common_interceptors.inc" #include "sanitizer_common/sanitizer_common_interceptors.inc"
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s) #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s)
@ -291,37 +294,29 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
} }
#endif #endif
// intercept mlock and friends. #if SANITIZER_WINDOWS
// Since asan maps 16T of RAM, mlock is completely unfriendly to asan. INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
// All functions return 0 (success). CHECK(REAL(RaiseException));
static void MlockIsUnsupported() { __asan_handle_no_return();
static bool printed = false; REAL(RaiseException)(a, b, c, d);
if (printed) return;
printed = true;
VPrintf(1,
"INFO: AddressSanitizer ignores "
"mlock/mlockall/munlock/munlockall\n");
} }
INTERCEPTOR(int, mlock, const void *addr, uptr len) { INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
MlockIsUnsupported(); CHECK(REAL(_except_handler3));
return 0; __asan_handle_no_return();
return REAL(_except_handler3)(a, b, c, d);
} }
INTERCEPTOR(int, munlock, const void *addr, uptr len) { #if ASAN_DYNAMIC
MlockIsUnsupported(); // This handler is named differently in -MT and -MD CRTs.
return 0; #define _except_handler4 _except_handler4_common
} #endif
INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
INTERCEPTOR(int, mlockall, int flags) { CHECK(REAL(_except_handler4));
MlockIsUnsupported(); __asan_handle_no_return();
return 0; return REAL(_except_handler4)(a, b, c, d);
}
INTERCEPTOR(int, munlockall, void) {
MlockIsUnsupported();
return 0;
} }
#endif
static inline int CharCmp(unsigned char c1, unsigned char c2) { static inline int CharCmp(unsigned char c1, unsigned char c2) {
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
@ -523,7 +518,7 @@ INTERCEPTOR(char*, strdup, const char *s) {
} }
#endif #endif
INTERCEPTOR(uptr, strlen, const char *s) { INTERCEPTOR(SIZE_T, strlen, const char *s) {
if (UNLIKELY(!asan_inited)) return internal_strlen(s); if (UNLIKELY(!asan_inited)) return internal_strlen(s);
// strlen is called from malloc_default_purgeable_zone() // strlen is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac. // in __asan::ReplaceSystemAlloc() on Mac.
@ -531,15 +526,15 @@ INTERCEPTOR(uptr, strlen, const char *s) {
return REAL(strlen)(s); return REAL(strlen)(s);
} }
ENSURE_ASAN_INITED(); ENSURE_ASAN_INITED();
uptr length = REAL(strlen)(s); SIZE_T length = REAL(strlen)(s);
if (flags()->replace_str) { if (flags()->replace_str) {
ASAN_READ_RANGE(s, length + 1); ASAN_READ_RANGE(s, length + 1);
} }
return length; return length;
} }
INTERCEPTOR(uptr, wcslen, const wchar_t *s) { INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) {
uptr length = REAL(wcslen)(s); SIZE_T length = REAL(wcslen)(s);
if (!asan_init_is_running) { if (!asan_init_is_running) {
ENSURE_ASAN_INITED(); ENSURE_ASAN_INITED();
ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t)); ASAN_READ_RANGE(s, (length + 1) * sizeof(wchar_t));
@ -691,6 +686,16 @@ INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
} }
#endif // ASAN_INTERCEPT___CXA_ATEXIT #endif // ASAN_INTERCEPT___CXA_ATEXIT
#if ASAN_INTERCEPT_FORK
INTERCEPTOR(int, fork, void) {
ENSURE_ASAN_INITED();
if (common_flags()->coverage) CovBeforeFork();
int pid = REAL(fork)();
if (common_flags()->coverage) CovAfterFork(pid);
return pid;
}
#endif // ASAN_INTERCEPT_FORK
#if SANITIZER_WINDOWS #if SANITIZER_WINDOWS
INTERCEPTOR_WINAPI(DWORD, CreateThread, INTERCEPTOR_WINAPI(DWORD, CreateThread,
void* security, uptr stack_size, void* security, uptr stack_size,
@ -712,6 +717,9 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
namespace __asan { namespace __asan {
void InitializeWindowsInterceptors() { void InitializeWindowsInterceptors() {
ASAN_INTERCEPT_FUNC(CreateThread); ASAN_INTERCEPT_FUNC(CreateThread);
ASAN_INTERCEPT_FUNC(RaiseException);
ASAN_INTERCEPT_FUNC(_except_handler3);
ASAN_INTERCEPT_FUNC(_except_handler4);
} }
} // namespace __asan } // namespace __asan
@ -759,14 +767,6 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(strtoll); ASAN_INTERCEPT_FUNC(strtoll);
#endif #endif
#if ASAN_INTERCEPT_MLOCKX
// Intercept mlock/munlock.
ASAN_INTERCEPT_FUNC(mlock);
ASAN_INTERCEPT_FUNC(munlock);
ASAN_INTERCEPT_FUNC(mlockall);
ASAN_INTERCEPT_FUNC(munlockall);
#endif
// Intecept signal- and jump-related functions. // Intecept signal- and jump-related functions.
ASAN_INTERCEPT_FUNC(longjmp); ASAN_INTERCEPT_FUNC(longjmp);
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
@ -789,7 +789,7 @@ void InitializeAsanInterceptors() {
// Intercept exception handling functions. // Intercept exception handling functions.
#if ASAN_INTERCEPT___CXA_THROW #if ASAN_INTERCEPT___CXA_THROW
INTERCEPT_FUNCTION(__cxa_throw); ASAN_INTERCEPT_FUNC(__cxa_throw);
#endif #endif
// Intercept threading-related functions // Intercept threading-related functions
@ -802,6 +802,10 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(__cxa_atexit); ASAN_INTERCEPT_FUNC(__cxa_atexit);
#endif #endif
#if ASAN_INTERCEPT_FORK
ASAN_INTERCEPT_FUNC(fork);
#endif
// Some Windows-specific interceptors. // Some Windows-specific interceptors.
#if SANITIZER_WINDOWS #if SANITIZER_WINDOWS
InitializeWindowsInterceptors(); InitializeWindowsInterceptors();

View File

@ -24,14 +24,14 @@
# define ASAN_INTERCEPT_STRDUP 1 # define ASAN_INTERCEPT_STRDUP 1
# define ASAN_INTERCEPT_INDEX 1 # define ASAN_INTERCEPT_INDEX 1
# define ASAN_INTERCEPT_PTHREAD_CREATE 1 # define ASAN_INTERCEPT_PTHREAD_CREATE 1
# define ASAN_INTERCEPT_MLOCKX 1 # define ASAN_INTERCEPT_FORK 1
#else #else
# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0 # define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
# define ASAN_INTERCEPT__LONGJMP 0 # define ASAN_INTERCEPT__LONGJMP 0
# define ASAN_INTERCEPT_STRDUP 0 # define ASAN_INTERCEPT_STRDUP 0
# define ASAN_INTERCEPT_INDEX 0 # define ASAN_INTERCEPT_INDEX 0
# define ASAN_INTERCEPT_PTHREAD_CREATE 0 # define ASAN_INTERCEPT_PTHREAD_CREATE 0
# define ASAN_INTERCEPT_MLOCKX 0 # define ASAN_INTERCEPT_FORK 0
#endif #endif
#if SANITIZER_FREEBSD || SANITIZER_LINUX #if SANITIZER_FREEBSD || SANITIZER_LINUX
@ -64,7 +64,9 @@
# define ASAN_INTERCEPT_SIGLONGJMP 0 # define ASAN_INTERCEPT_SIGLONGJMP 0
#endif #endif
#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS // Android bug: https://code.google.com/p/android/issues/detail?id=61799
#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS && \
!(SANITIZER_ANDROID && defined(__i386))
# define ASAN_INTERCEPT___CXA_THROW 1 # define ASAN_INTERCEPT___CXA_THROW 1
#else #else
# define ASAN_INTERCEPT___CXA_THROW 0 # define ASAN_INTERCEPT___CXA_THROW 0
@ -80,7 +82,7 @@ DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size)
DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size) DECLARE_REAL(void*, memcpy, void *to, const void *from, uptr size)
DECLARE_REAL(void*, memset, void *block, int c, uptr size) DECLARE_REAL(void*, memset, void *block, int c, uptr size)
DECLARE_REAL(char*, strchr, const char *str, int c) DECLARE_REAL(char*, strchr, const char *str, int c)
DECLARE_REAL(uptr, strlen, const char *s) DECLARE_REAL(SIZE_T, strlen, const char *s)
DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size) DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size)
DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen) DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen)
DECLARE_REAL(char*, strstr, const char *s1, const char *s2) DECLARE_REAL(char*, strstr, const char *s1, const char *s2)

View File

@ -15,21 +15,24 @@
#include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_internal_defs.h"
#include "asan_init_version.h"
using __sanitizer::uptr; using __sanitizer::uptr;
extern "C" { extern "C" {
// This function should be called at the very beginning of the process, // This function should be called at the very beginning of the process,
// before any instrumented code is executed and before any call to malloc. // before any instrumented code is executed and before any call to malloc.
// Every time the asan ABI changes we also change the version number in this // Please note that __asan_init is a macro that is replaced with
// name. Objects build with incompatible asan ABI version // __asan_init_vXXX at compile-time.
// will not link with run-time. SANITIZER_INTERFACE_ATTRIBUTE void __asan_init();
// Changes between ABI versions:
// v1=>v2: added 'module_name' to __asan_global // This structure is used to describe the source location of a place where
// v2=>v3: stack frame description (created by the compiler) // global was defined.
// contains the function PC as the 3-rd field (see struct __asan_global_source_location {
// DescribeAddressIfStack). const char *filename;
SANITIZER_INTERFACE_ATTRIBUTE void __asan_init_v3(); int line_no;
#define __asan_init __asan_init_v3 int column_no;
};
// This structure describes an instrumented global variable. // This structure describes an instrumented global variable.
struct __asan_global { struct __asan_global {
@ -40,6 +43,8 @@ extern "C" {
const char *module_name; // Module name as a C string. This pointer is a const char *module_name; // Module name as a C string. This pointer is a
// unique identifier of a module. // unique identifier of a module.
uptr has_dynamic_init; // Non-zero if the global has dynamic initializer. uptr has_dynamic_init; // Non-zero if the global has dynamic initializer.
__asan_global_source_location *location; // Source location of a global,
// or NULL if it is unknown.
}; };
// These two functions should be called by the instrumented code. // These two functions should be called by the instrumented code.
@ -83,6 +88,17 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
void __asan_describe_address(uptr addr); void __asan_describe_address(uptr addr);
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size,
u32 *thread_id);
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size,
u32 *thread_id);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset);
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
void __asan_report_error(uptr pc, uptr bp, uptr sp, void __asan_report_error(uptr pc, uptr bp, uptr sp,
uptr addr, int is_write, uptr access_size); uptr addr, int is_write, uptr access_size);
@ -97,25 +113,11 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
/* OPTIONAL */ void __asan_on_error(); /* OPTIONAL */ void __asan_on_error();
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_estimated_allocated_size(uptr size);
SANITIZER_INTERFACE_ATTRIBUTE int __asan_get_ownership(const void *p);
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_allocated_size(const void *p);
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_current_allocated_bytes();
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_heap_size();
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_free_bytes();
SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_unmapped_bytes();
SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats(); SANITIZER_INTERFACE_ATTRIBUTE void __asan_print_accumulated_stats();
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
/* OPTIONAL */ const char* __asan_default_options(); /* OPTIONAL */ const char* __asan_default_options();
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
/* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
/* OPTIONAL */ void __asan_free_hook(void *ptr);
// Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return // Global flag, copy of ASAN_OPTIONS=detect_stack_use_after_return
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
extern int __asan_option_detect_stack_use_after_return; extern int __asan_option_detect_stack_use_after_return;
@ -142,6 +144,11 @@ extern "C" {
void* __asan_memset(void *s, int c, uptr n); void* __asan_memset(void *s, int c, uptr n);
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
void* __asan_memmove(void* dest, const void* src, uptr n); void* __asan_memmove(void* dest, const void* src, uptr n);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_poison_cxx_array_cookie(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_load_cxx_array_cookie(uptr *p);
} // extern "C" } // extern "C"
#endif // ASAN_INTERFACE_INTERNAL_H #endif // ASAN_INTERFACE_INTERNAL_H

View File

@ -43,10 +43,6 @@
# endif # endif
#endif #endif
#ifndef ASAN_USE_PREINIT_ARRAY
# define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID)
#endif
#ifndef ASAN_DYNAMIC #ifndef ASAN_DYNAMIC
# ifdef PIC # ifdef PIC
# define ASAN_DYNAMIC 1 # define ASAN_DYNAMIC 1
@ -96,6 +92,8 @@ void AppendToErrorMessageBuffer(const char *buffer);
void ParseExtraActivationFlags(); void ParseExtraActivationFlags();
void *AsanDlSymNext(const char *sym);
// Platform-specific options. // Platform-specific options.
#if SANITIZER_MAC #if SANITIZER_MAC
bool PlatformHasDifferentMemcpyAndMemmove(); bool PlatformHasDifferentMemcpyAndMemmove();
@ -108,9 +106,9 @@ bool PlatformHasDifferentMemcpyAndMemmove();
// Add convenient macro for interface functions that may be represented as // Add convenient macro for interface functions that may be represented as
// weak hooks. // weak hooks.
#define ASAN_MALLOC_HOOK(ptr, size) \ #define ASAN_MALLOC_HOOK(ptr, size) \
if (&__asan_malloc_hook) __asan_malloc_hook(ptr, size) if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size)
#define ASAN_FREE_HOOK(ptr) \ #define ASAN_FREE_HOOK(ptr) \
if (&__asan_free_hook) __asan_free_hook(ptr) if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr)
#define ASAN_ON_ERROR() \ #define ASAN_ON_ERROR() \
if (&__asan_on_error) __asan_on_error() if (&__asan_on_error) __asan_on_error()
@ -134,6 +132,7 @@ const int kAsanContiguousContainerOOBMagic = 0xfc;
const int kAsanStackUseAfterScopeMagic = 0xf8; const int kAsanStackUseAfterScopeMagic = 0xf8;
const int kAsanGlobalRedzoneMagic = 0xf9; const int kAsanGlobalRedzoneMagic = 0xf9;
const int kAsanInternalHeapMagic = 0xfe; const int kAsanInternalHeapMagic = 0xfe;
const int kAsanArrayCookieMagic = 0xac;
static const uptr kCurrentStackFrameMagic = 0x41B58AB3; static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
static const uptr kRetiredStackFrameMagic = 0x45E0360E; static const uptr kRetiredStackFrameMagic = 0x45E0360E;

View File

@ -17,6 +17,7 @@
#include "asan_internal.h" #include "asan_internal.h"
#include "asan_thread.h" #include "asan_thread.h"
#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_freebsd.h"
#include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_procmaps.h"
@ -25,6 +26,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/types.h> #include <sys/types.h>
#include <dlfcn.h>
#include <fcntl.h> #include <fcntl.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h> #include <stdio.h>
@ -40,19 +42,14 @@
extern "C" void* _DYNAMIC; extern "C" void* _DYNAMIC;
#else #else
#include <sys/ucontext.h> #include <sys/ucontext.h>
#include <dlfcn.h>
#include <link.h> #include <link.h>
#endif #endif
// x86_64 FreeBSD 9.2 and older define 64-bit register names in both 64-bit // x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
// and 32-bit modes. // 32-bit mode.
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \
#include <sys/param.h> __FreeBSD_version <= 902001 // v9.2
# if __FreeBSD_version <= 902001 // v9.2 #define ucontext_t xucontext_t
# define mc_eip mc_rip
# define mc_ebp mc_rbp
# define mc_esp mc_rsp
# endif
#endif #endif
typedef enum { typedef enum {
@ -241,6 +238,10 @@ void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
} }
#endif #endif
void *AsanDlSymNext(const char *sym) {
return dlsym(RTLD_NEXT, sym);
}
} // namespace __asan } // namespace __asan
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX #endif // SANITIZER_FREEBSD || SANITIZER_LINUX

View File

@ -372,32 +372,44 @@ void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
work(); \ work(); \
} }
// Forces the compiler to generate a frame pointer in the function.
#define ENABLE_FRAME_POINTER \
do { \
volatile uptr enable_fp; \
enable_fp = GET_CURRENT_FRAME(); \
} while (0)
INTERCEPTOR(void, dispatch_async, INTERCEPTOR(void, dispatch_async,
dispatch_queue_t dq, void(^work)(void)) { dispatch_queue_t dq, void(^work)(void)) {
ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work); GET_ASAN_BLOCK(work);
REAL(dispatch_async)(dq, asan_block); REAL(dispatch_async)(dq, asan_block);
} }
INTERCEPTOR(void, dispatch_group_async, INTERCEPTOR(void, dispatch_group_async,
dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) { dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work); GET_ASAN_BLOCK(work);
REAL(dispatch_group_async)(dg, dq, asan_block); REAL(dispatch_group_async)(dg, dq, asan_block);
} }
INTERCEPTOR(void, dispatch_after, INTERCEPTOR(void, dispatch_after,
dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) { dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work); GET_ASAN_BLOCK(work);
REAL(dispatch_after)(when, queue, asan_block); REAL(dispatch_after)(when, queue, asan_block);
} }
INTERCEPTOR(void, dispatch_source_set_cancel_handler, INTERCEPTOR(void, dispatch_source_set_cancel_handler,
dispatch_source_t ds, void(^work)(void)) { dispatch_source_t ds, void(^work)(void)) {
ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work); GET_ASAN_BLOCK(work);
REAL(dispatch_source_set_cancel_handler)(ds, asan_block); REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
} }
INTERCEPTOR(void, dispatch_source_set_event_handler, INTERCEPTOR(void, dispatch_source_set_event_handler,
dispatch_source_t ds, void(^work)(void)) { dispatch_source_t ds, void(^work)(void)) {
ENABLE_FRAME_POINTER;
GET_ASAN_BLOCK(work); GET_ASAN_BLOCK(work);
REAL(dispatch_source_set_event_handler)(ds, asan_block); REAL(dispatch_source_set_event_handler)(ds, asan_block);
} }

View File

@ -21,41 +21,6 @@
#include "asan_internal.h" #include "asan_internal.h"
#include "asan_stack.h" #include "asan_stack.h"
#if SANITIZER_ANDROID
DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size)
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size)
DECLARE_REAL_AND_INTERCEPTOR(void*, realloc, void *ptr, uptr size)
DECLARE_REAL_AND_INTERCEPTOR(void*, memalign, uptr boundary, uptr size)
struct MallocDebug {
void* (*malloc)(uptr bytes);
void (*free)(void* mem);
void* (*calloc)(uptr n_elements, uptr elem_size);
void* (*realloc)(void* oldMem, uptr bytes);
void* (*memalign)(uptr alignment, uptr bytes);
};
const MallocDebug asan_malloc_dispatch ALIGNED(32) = {
WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign)
};
extern "C" const MallocDebug* __libc_malloc_dispatch;
namespace __asan {
void ReplaceSystemMalloc() {
__libc_malloc_dispatch = &asan_malloc_dispatch;
}
} // namespace __asan
#else // ANDROID
namespace __asan {
void ReplaceSystemMalloc() {
}
} // namespace __asan
#endif // ANDROID
// ---------------------- Replacement functions ---------------- {{{1 // ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT using namespace __asan; // NOLINT
@ -100,6 +65,11 @@ INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
return asan_memalign(boundary, size, &stack, FROM_MALLOC); return asan_memalign(boundary, size, &stack, FROM_MALLOC);
} }
INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
GET_STACK_TRACE_MALLOC;
return asan_memalign(boundary, size, &stack, FROM_MALLOC);
}
INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) { INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
GET_STACK_TRACE_MALLOC; GET_STACK_TRACE_MALLOC;
void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC); void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
@ -151,4 +121,64 @@ INTERCEPTOR(void, malloc_stats, void) {
__asan_print_accumulated_stats(); __asan_print_accumulated_stats();
} }
#if SANITIZER_ANDROID
// Format of __libc_malloc_dispatch has changed in Android L.
// While we are moving towards a solution that does not depend on bionic
// internals, here is something to support both K* and L releases.
struct MallocDebugK {
void *(*malloc)(uptr bytes);
void (*free)(void *mem);
void *(*calloc)(uptr n_elements, uptr elem_size);
void *(*realloc)(void *oldMem, uptr bytes);
void *(*memalign)(uptr alignment, uptr bytes);
uptr (*malloc_usable_size)(void *mem);
};
struct MallocDebugL {
void *(*calloc)(uptr n_elements, uptr elem_size);
void (*free)(void *mem);
fake_mallinfo (*mallinfo)(void);
void *(*malloc)(uptr bytes);
uptr (*malloc_usable_size)(void *mem);
void *(*memalign)(uptr alignment, uptr bytes);
int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
void* (*pvalloc)(uptr size);
void *(*realloc)(void *oldMem, uptr bytes);
void* (*valloc)(uptr size);
};
ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
WRAP(malloc), WRAP(free), WRAP(calloc),
WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
WRAP(calloc), WRAP(free), WRAP(mallinfo),
WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign),
WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc),
WRAP(valloc)};
namespace __asan {
void ReplaceSystemMalloc() {
void **__libc_malloc_dispatch_p =
(void **)AsanDlSymNext("__libc_malloc_dispatch");
if (__libc_malloc_dispatch_p) {
// Decide on K vs L dispatch format by the presence of
// __libc_malloc_default_dispatch export in libc.
void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
if (default_dispatch_p)
*__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
else
*__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
}
}
} // namespace __asan
#else // SANITIZER_ANDROID
namespace __asan {
void ReplaceSystemMalloc() {
}
} // namespace __asan
#endif // SANITIZER_ANDROID
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX #endif // SANITIZER_FREEBSD || SANITIZER_LINUX

View File

@ -21,71 +21,77 @@
#include <stddef.h> #include <stddef.h>
// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT using namespace __asan; // NOLINT
// FIXME: Simply defining functions with the same signature in *.obj // MT: Simply defining functions with the same signature in *.obj
// files overrides the standard functions in *.lib // files overrides the standard functions in the CRT.
// This works well for simple helloworld-like tests but might need to be // MD: Memory allocation functions are defined in the CRT .dll,
// revisited in the future. // so we have to intercept them before they are called for the first time.
#if ASAN_DYNAMIC
# define ALLOCATION_FUNCTION_ATTRIBUTE
#else
# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
#endif
extern "C" { extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void free(void *ptr) { void free(void *ptr) {
GET_STACK_TRACE_FREE; GET_STACK_TRACE_FREE;
return asan_free(ptr, &stack, FROM_MALLOC); return asan_free(ptr, &stack, FROM_MALLOC);
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void _free_dbg(void* ptr, int) { void _free_dbg(void *ptr, int) {
free(ptr); free(ptr);
} }
ALLOCATION_FUNCTION_ATTRIBUTE
void cfree(void *ptr) { void cfree(void *ptr) {
CHECK(!"cfree() should not be used on Windows?"); CHECK(!"cfree() should not be used on Windows");
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void *malloc(size_t size) { void *malloc(size_t size) {
GET_STACK_TRACE_MALLOC; GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack); return asan_malloc(size, &stack);
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void* _malloc_dbg(size_t size, int , const char*, int) { void *_malloc_dbg(size_t size, int, const char *, int) {
return malloc(size); return malloc(size);
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void *calloc(size_t nmemb, size_t size) { void *calloc(size_t nmemb, size_t size) {
GET_STACK_TRACE_MALLOC; GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack); return asan_calloc(nmemb, size, &stack);
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void* _calloc_dbg(size_t n, size_t size, int, const char*, int) { void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
return calloc(n, size); return calloc(nmemb, size);
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) { void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
return calloc(nmemb, size); return calloc(nmemb, size);
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void *realloc(void *ptr, size_t size) { void *realloc(void *ptr, size_t size) {
GET_STACK_TRACE_MALLOC; GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack); return asan_realloc(ptr, size, &stack);
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void *_realloc_dbg(void *ptr, size_t size, int) { void *_realloc_dbg(void *ptr, size_t size, int) {
CHECK(!"_realloc_dbg should not exist!"); CHECK(!"_realloc_dbg should not exist!");
return 0; return 0;
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void* _recalloc(void* p, size_t n, size_t elem_size) { void *_recalloc(void *p, size_t n, size_t elem_size) {
if (!p) if (!p)
return calloc(n, elem_size); return calloc(n, elem_size);
const size_t size = n * elem_size; const size_t size = n * elem_size;
@ -94,23 +100,23 @@ void* _recalloc(void* p, size_t n, size_t elem_size) {
return realloc(p, size); return realloc(p, size);
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
size_t _msize(void *ptr) { size_t _msize(void *ptr) {
GET_CURRENT_PC_BP_SP; GET_CURRENT_PC_BP_SP;
(void)sp; (void)sp;
return asan_malloc_usable_size(ptr, pc, bp); return asan_malloc_usable_size(ptr, pc, bp);
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand(void *memblock, size_t size) { void *_expand(void *memblock, size_t size) {
// _expand is used in realloc-like functions to resize the buffer if possible. // _expand is used in realloc-like functions to resize the buffer if possible.
// We don't want memory to stand still while resizing buffers, so return 0. // We don't want memory to stand still while resizing buffers, so return 0.
return 0; return 0;
} }
SANITIZER_INTERFACE_ATTRIBUTE ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand_dbg(void *memblock, size_t size) { void *_expand_dbg(void *memblock, size_t size) {
return 0; return _expand(memblock, size);
} }
// TODO(timurrrr): Might want to add support for _aligned_* allocation // TODO(timurrrr): Might want to add support for _aligned_* allocation
@ -131,37 +137,38 @@ int _CrtSetReportMode(int, int) {
} }
} // extern "C" } // extern "C"
using __interception::GetRealFunctionAddress;
// We don't want to include "windows.h" in this file to avoid extra attributes
// set on malloc/free etc (e.g. dllimport), so declare a few things manually:
extern "C" int __stdcall VirtualProtect(void* addr, size_t size,
DWORD prot, DWORD *old_prot);
const int PAGE_EXECUTE_READWRITE = 0x40;
namespace __asan { namespace __asan {
void ReplaceSystemMalloc() { void ReplaceSystemMalloc() {
#if defined(_DLL) #if defined(ASAN_DYNAMIC)
# ifdef _WIN64 // We don't check the result because CRT might not be used in the process.
# error ReplaceSystemMalloc was not tested on x64 __interception::OverrideFunction("free", (uptr)free);
# endif __interception::OverrideFunction("malloc", (uptr)malloc);
char *crt_malloc; __interception::OverrideFunction("_malloc_crt", (uptr)malloc);
if (GetRealFunctionAddress("malloc", (void**)&crt_malloc)) { __interception::OverrideFunction("calloc", (uptr)calloc);
// Replace malloc in the CRT dll with a jump to our malloc. __interception::OverrideFunction("_calloc_crt", (uptr)calloc);
DWORD old_prot, unused; __interception::OverrideFunction("realloc", (uptr)realloc);
CHECK(VirtualProtect(crt_malloc, 16, PAGE_EXECUTE_READWRITE, &old_prot)); __interception::OverrideFunction("_realloc_crt", (uptr)realloc);
REAL(memset)(crt_malloc, 0xCC /* int 3 */, 16); // just in case. __interception::OverrideFunction("_recalloc", (uptr)_recalloc);
__interception::OverrideFunction("_recalloc_crt", (uptr)_recalloc);
__interception::OverrideFunction("_msize", (uptr)_msize);
__interception::OverrideFunction("_expand", (uptr)_expand);
ptrdiff_t jmp_offset = (char*)malloc - (char*)crt_malloc - 5; // Override different versions of 'operator new' and 'operator delete'.
crt_malloc[0] = 0xE9; // jmp, should be followed by an offset. // No need to override the nothrow versions as they just wrap the throw
REAL(memcpy)(crt_malloc + 1, &jmp_offset, sizeof(jmp_offset)); // versions.
// FIXME: Unfortunately, MSVC miscompiles the statements that take the
CHECK(VirtualProtect(crt_malloc, 16, old_prot, &unused)); // addresses of the array versions of these operators,
// see https://connect.microsoft.com/VisualStudio/feedbackdetail/view/946992
// FYI: FlushInstructionCache is needed on Itanium etc but not on x86/x64. // We might want to try to work around this by [inline] assembly or compiling
} // parts of the RTL with Clang.
void *(*op_new)(size_t sz) = operator new;
// FIXME: investigate whether anything else is needed. void (*op_delete)(void *p) = operator delete;
void *(*op_array_new)(size_t sz) = operator new[];
void (*op_array_delete)(void *p) = operator delete[];
__interception::OverrideFunction("??2@YAPAXI@Z", (uptr)op_new);
__interception::OverrideFunction("??3@YAXPAX@Z", (uptr)op_delete);
__interception::OverrideFunction("??_U@YAPAXI@Z", (uptr)op_array_new);
__interception::OverrideFunction("??_V@YAXPAX@Z", (uptr)op_array_delete);
#endif #endif
} }
} // namespace __asan } // namespace __asan

View File

@ -18,6 +18,13 @@
#include <stddef.h> #include <stddef.h>
// C++ operators can't have visibility attributes on Windows.
#if SANITIZER_WINDOWS
# define CXX_OPERATOR_ATTRIBUTE
#else
# define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE
#endif
using namespace __asan; // NOLINT using namespace __asan; // NOLINT
// This code has issues on OSX. // This code has issues on OSX.
@ -49,14 +56,14 @@ struct nothrow_t {};
#endif // __FreeBSD_version #endif // __FreeBSD_version
#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32 #endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
INTERCEPTOR_ATTRIBUTE CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); } void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
INTERCEPTOR_ATTRIBUTE CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); } void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); }
INTERCEPTOR_ATTRIBUTE CXX_OPERATOR_ATTRIBUTE
void *operator new(size_t size, std::nothrow_t const&) void *operator new(size_t size, std::nothrow_t const&)
{ OPERATOR_NEW_BODY(FROM_NEW); } { OPERATOR_NEW_BODY(FROM_NEW); }
INTERCEPTOR_ATTRIBUTE CXX_OPERATOR_ATTRIBUTE
void *operator new[](size_t size, std::nothrow_t const&) void *operator new[](size_t size, std::nothrow_t const&)
{ OPERATOR_NEW_BODY(FROM_NEW_BR); } { OPERATOR_NEW_BODY(FROM_NEW_BR); }
@ -80,22 +87,32 @@ INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
asan_free(ptr, &stack, type); asan_free(ptr, &stack, type);
#if !SANITIZER_MAC #if !SANITIZER_MAC
INTERCEPTOR_ATTRIBUTE CXX_OPERATOR_ATTRIBUTE
void operator delete(void *ptr) throw() { void operator delete(void *ptr) throw() {
OPERATOR_DELETE_BODY(FROM_NEW); OPERATOR_DELETE_BODY(FROM_NEW);
} }
INTERCEPTOR_ATTRIBUTE CXX_OPERATOR_ATTRIBUTE
void operator delete[](void *ptr) throw() { void operator delete[](void *ptr) throw() {
OPERATOR_DELETE_BODY(FROM_NEW_BR); OPERATOR_DELETE_BODY(FROM_NEW_BR);
} }
INTERCEPTOR_ATTRIBUTE CXX_OPERATOR_ATTRIBUTE
void operator delete(void *ptr, std::nothrow_t const&) { void operator delete(void *ptr, std::nothrow_t const&) {
OPERATOR_DELETE_BODY(FROM_NEW); OPERATOR_DELETE_BODY(FROM_NEW);
} }
INTERCEPTOR_ATTRIBUTE CXX_OPERATOR_ATTRIBUTE
void operator delete[](void *ptr, std::nothrow_t const&) { void operator delete[](void *ptr, std::nothrow_t const&) {
OPERATOR_DELETE_BODY(FROM_NEW_BR); OPERATOR_DELETE_BODY(FROM_NEW_BR);
} }
CXX_OPERATOR_ATTRIBUTE
void operator delete(void *ptr, size_t size) throw() {
GET_STACK_TRACE_FREE;
asan_sized_free(ptr, size, &stack, FROM_NEW);
}
CXX_OPERATOR_ATTRIBUTE
void operator delete[](void *ptr, size_t size) throw() {
GET_STACK_TRACE_FREE;
asan_sized_free(ptr, size, &stack, FROM_NEW_BR);
}
#else // SANITIZER_MAC #else // SANITIZER_MAC
INTERCEPTOR(void, _ZdlPv, void *ptr) { INTERCEPTOR(void, _ZdlPv, void *ptr) {

View File

@ -225,6 +225,35 @@ void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
*p = x; *p = x;
} }
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void __asan_poison_cxx_array_cookie(uptr p) {
if (SANITIZER_WORDSIZE != 64) return;
if (!flags()->poison_array_cookie) return;
uptr s = MEM_TO_SHADOW(p);
*reinterpret_cast<u8*>(s) = kAsanArrayCookieMagic;
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_load_cxx_array_cookie(uptr *p) {
if (SANITIZER_WORDSIZE != 64) return *p;
if (!flags()->poison_array_cookie) return *p;
uptr s = MEM_TO_SHADOW(reinterpret_cast<uptr>(p));
u8 sval = *reinterpret_cast<u8*>(s);
if (sval == kAsanArrayCookieMagic) return *p;
// If sval is not kAsanArrayCookieMagic it can only be freed memory,
// which means that we are going to get double-free. So, return 0 to avoid
// infinite loop of destructors. We don't want to report a double-free here
// though, so print a warning just in case.
// CHECK_EQ(sval, kAsanHeapFreeMagic);
if (sval == kAsanHeapFreeMagic) {
Report("AddressSanitizer: loaded array cookie from free-d memory; "
"expect a double-free report\n");
return 0;
}
// FIXME: apparently it can be something else; need to find a reproducer.
return *p;
}
// This is a simplified version of __asan_(un)poison_memory_region, which // This is a simplified version of __asan_(un)poison_memory_region, which
// assumes that left border of region to be poisoned is properly aligned. // assumes that left border of region to be poisoned is properly aligned.
static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {

View File

@ -33,7 +33,6 @@ void PoisonShadowPartialRightRedzone(uptr addr,
ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
u8 value) { u8 value) {
DCHECK(flags()->poison_heap); DCHECK(flags()->poison_heap);
uptr PageSize = GetPageSizeCached();
uptr shadow_beg = MEM_TO_SHADOW(aligned_beg); uptr shadow_beg = MEM_TO_SHADOW(aligned_beg);
uptr shadow_end = MEM_TO_SHADOW( uptr shadow_end = MEM_TO_SHADOW(
aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1; aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1;
@ -46,8 +45,9 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
} else { } else {
uptr page_beg = RoundUpTo(shadow_beg, PageSize); uptr page_size = GetPageSizeCached();
uptr page_end = RoundDownTo(shadow_end, PageSize); uptr page_beg = RoundUpTo(shadow_beg, page_size);
uptr page_end = RoundDownTo(shadow_end, page_size);
if (page_beg >= page_end) { if (page_beg >= page_end) {
REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg); REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);

View File

@ -48,7 +48,7 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
(code == si_SEGV_MAPERR || code == si_SEGV_ACCERR)) (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
ReportStackOverflow(pc, sp, bp, context, addr); ReportStackOverflow(pc, sp, bp, context, addr);
else else
ReportSIGSEGV(pc, sp, bp, context, addr); ReportSIGSEGV("SEGV", pc, sp, bp, context, addr);
} }
// ---------------------- TSD ---------------- {{{1 // ---------------------- TSD ---------------- {{{1

View File

@ -8,22 +8,12 @@
// This file is a part of AddressSanitizer, an address sanity checker. // This file is a part of AddressSanitizer, an address sanity checker.
// //
// Call __asan_init at the very early stage of process startup. // Call __asan_init at the very early stage of process startup.
// On Linux we use .preinit_array section (unless PIC macro is defined).
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "asan_internal.h" #include "asan_internal.h"
#if ASAN_USE_PREINIT_ARRAY && !defined(PIC) #if SANITIZER_CAN_USE_PREINIT_ARRAY
// On Linux, we force __asan_init to be called before anyone else
// by placing it into .preinit_array section.
// FIXME: do we have anything like this on Mac?
// The symbol is called __local_asan_preinit, because it's not intended to be // The symbol is called __local_asan_preinit, because it's not intended to be
// exported. // exported.
__attribute__((section(".preinit_array"), used)) __attribute__((section(".preinit_array"), used))
void (*__local_asan_preinit)(void) = __asan_init; void (*__local_asan_preinit)(void) = __asan_init;
#elif SANITIZER_WINDOWS && defined(_DLL)
// On Windows, when using dynamic CRT (/MD), we can put a pointer
// to __asan_init into the global list of C initializers.
// See crt0dat.c in the CRT sources for the details.
#pragma section(".CRT$XIB", long, read) // NOLINT
__declspec(allocate(".CRT$XIB")) void (*__asan_preinit)() = __asan_init;
#endif #endif

View File

@ -57,6 +57,7 @@ class Decorator: public __sanitizer::SanitizerCommonDecorator {
switch (byte) { switch (byte) {
case kAsanHeapLeftRedzoneMagic: case kAsanHeapLeftRedzoneMagic:
case kAsanHeapRightRedzoneMagic: case kAsanHeapRightRedzoneMagic:
case kAsanArrayCookieMagic:
return Red(); return Red();
case kAsanHeapFreeMagic: case kAsanHeapFreeMagic:
return Magenta(); return Magenta();
@ -141,6 +142,8 @@ static void PrintLegend(InternalScopedString *str) {
kAsanUserPoisonedMemoryMagic); kAsanUserPoisonedMemoryMagic);
PrintShadowByte(str, " Container overflow: ", PrintShadowByte(str, " Container overflow: ",
kAsanContiguousContainerOOBMagic); kAsanContiguousContainerOOBMagic);
PrintShadowByte(str, " Array cookie: ",
kAsanArrayCookieMagic);
PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic); PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
} }
@ -195,7 +198,7 @@ static const char *MaybeDemangleGlobalName(const char *name) {
else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?') else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
should_demangle = true; should_demangle = true;
return should_demangle ? Symbolizer::Get()->Demangle(name) : name; return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
} }
// Check if the global is a zero-terminated ASCII string. If so, print it. // Check if the global is a zero-terminated ASCII string. If so, print it.
@ -210,6 +213,26 @@ static void PrintGlobalNameIfASCII(InternalScopedString *str,
(char *)g.beg); (char *)g.beg);
} }
static const char *GlobalFilename(const __asan_global &g) {
const char *res = g.module_name;
// Prefer the filename from source location, if is available.
if (g.location)
res = g.location->filename;
CHECK(res);
return res;
}
static void PrintGlobalLocation(InternalScopedString *str,
const __asan_global &g) {
str->append("%s", GlobalFilename(g));
if (!g.location)
return;
if (g.location->line_no)
str->append(":%d", g.location->line_no);
if (g.location->column_no)
str->append(":%d", g.location->column_no);
}
bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
const __asan_global &g) { const __asan_global &g) {
static const uptr kMinimalDistanceFromAnotherGlobal = 64; static const uptr kMinimalDistanceFromAnotherGlobal = 64;
@ -230,8 +253,10 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
// Can it happen? // Can it happen?
str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg); str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
} }
str.append(" of global variable '%s' from '%s' (0x%zx) of size %zu\n", str.append(" of global variable '%s' defined in '",
MaybeDemangleGlobalName(g.name), g.module_name, g.beg, g.size); MaybeDemangleGlobalName(g.name));
PrintGlobalLocation(&str, g);
str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
str.append("%s", d.EndLocation()); str.append("%s", d.EndLocation());
PrintGlobalNameIfASCII(&str, g); PrintGlobalNameIfASCII(&str, g);
Printf("%s", str.data()); Printf("%s", str.data());
@ -317,12 +342,27 @@ void PrintAccessAndVarIntersection(const char *var_name,
Printf("%s", str.data()); Printf("%s", str.data());
} }
struct StackVarDescr { bool ParseFrameDescription(const char *frame_descr,
uptr beg; InternalMmapVector<StackVarDescr> *vars) {
uptr size; char *p;
const char *name_pos; uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
uptr name_len; CHECK_GT(n_objects, 0);
};
for (uptr i = 0; i < n_objects; i++) {
uptr beg = (uptr)internal_simple_strtoll(p, &p, 10);
uptr size = (uptr)internal_simple_strtoll(p, &p, 10);
uptr len = (uptr)internal_simple_strtoll(p, &p, 10);
if (beg == 0 || size == 0 || *p != ' ') {
return false;
}
p++;
StackVarDescr var = {beg, size, p, len};
vars->push_back(var);
p += len;
}
return true;
}
bool DescribeAddressIfStack(uptr addr, uptr access_size) { bool DescribeAddressIfStack(uptr addr, uptr access_size) {
AsanThread *t = FindThreadByStackAddress(addr); AsanThread *t = FindThreadByStackAddress(addr);
@ -364,32 +404,19 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
alloca_stack.size = 1; alloca_stack.size = 1;
Printf("%s", d.EndLocation()); Printf("%s", d.EndLocation());
alloca_stack.Print(); alloca_stack.Print();
InternalMmapVector<StackVarDescr> vars(16);
if (!ParseFrameDescription(frame_descr, &vars)) {
Printf("AddressSanitizer can't parse the stack frame "
"descriptor: |%s|\n", frame_descr);
// 'addr' is a stack address, so return true even if we can't parse frame
return true;
}
uptr n_objects = vars.size();
// Report the number of stack objects. // Report the number of stack objects.
char *p;
uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
CHECK_GT(n_objects, 0);
Printf(" This frame has %zu object(s):\n", n_objects); Printf(" This frame has %zu object(s):\n", n_objects);
// Report all objects in this frame. // Report all objects in this frame.
InternalScopedBuffer<StackVarDescr> vars(n_objects);
for (uptr i = 0; i < n_objects; i++) {
uptr beg, size;
uptr len;
beg = (uptr)internal_simple_strtoll(p, &p, 10);
size = (uptr)internal_simple_strtoll(p, &p, 10);
len = (uptr)internal_simple_strtoll(p, &p, 10);
if (beg == 0 || size == 0 || *p != ' ') {
Printf("AddressSanitizer can't parse the stack frame "
"descriptor: |%s|\n", frame_descr);
break;
}
p++;
vars[i].beg = beg;
vars[i].size = size;
vars[i].name_pos = p;
vars[i].name_len = len;
p += len;
}
for (uptr i = 0; i < n_objects; i++) { for (uptr i = 0; i < n_objects; i++) {
buf[0] = 0; buf[0] = 0;
internal_strncat(buf, vars[i].name_pos, internal_strncat(buf, vars[i].name_pos,
@ -401,8 +428,12 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
prev_var_end, next_var_beg); prev_var_end, next_var_beg);
} }
Printf("HINT: this may be a false positive if your program uses " Printf("HINT: this may be a false positive if your program uses "
"some custom stack unwind mechanism or swapcontext\n" "some custom stack unwind mechanism or swapcontext\n");
" (longjmp and C++ exceptions *are* supported)\n"); if (SANITIZER_WINDOWS)
Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
else
Printf(" (longjmp and C++ exceptions *are* supported)\n");
DescribeThread(t); DescribeThread(t);
return true; return true;
} }
@ -531,7 +562,7 @@ class ScopedInErrorReport {
// Do not print more than one report, otherwise they will mix up. // Do not print more than one report, otherwise they will mix up.
// Error reporting functions shouldn't return at this situation, as // Error reporting functions shouldn't return at this situation, as
// they are defined as no-return. // they are defined as no-return.
Report("AddressSanitizer: while reporting a bug found another one." Report("AddressSanitizer: while reporting a bug found another one. "
"Ignoring.\n"); "Ignoring.\n");
u32 current_tid = GetCurrentTidOrInvalid(); u32 current_tid = GetCurrentTidOrInvalid();
if (current_tid != reporting_thread_tid) { if (current_tid != reporting_thread_tid) {
@ -578,8 +609,8 @@ void ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr) {
Printf("%s", d.Warning()); Printf("%s", d.Warning());
Report( Report(
"ERROR: AddressSanitizer: stack-overflow on address %p" "ERROR: AddressSanitizer: stack-overflow on address %p"
" (pc %p sp %p bp %p T%d)\n", " (pc %p bp %p sp %p T%d)\n",
(void *)addr, (void *)pc, (void *)sp, (void *)bp, (void *)addr, (void *)pc, (void *)bp, (void *)sp,
GetCurrentTidOrInvalid()); GetCurrentTidOrInvalid());
Printf("%s", d.EndWarning()); Printf("%s", d.EndWarning());
GET_STACK_TRACE_SIGNAL(pc, bp, context); GET_STACK_TRACE_SIGNAL(pc, bp, context);
@ -587,15 +618,19 @@ void ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr) {
ReportErrorSummary("stack-overflow", &stack); ReportErrorSummary("stack-overflow", &stack);
} }
void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, void *context, uptr addr) { void ReportSIGSEGV(const char *description, uptr pc, uptr sp, uptr bp,
void *context, uptr addr) {
ScopedInErrorReport in_report; ScopedInErrorReport in_report;
Decorator d; Decorator d;
Printf("%s", d.Warning()); Printf("%s", d.Warning());
Report( Report(
"ERROR: AddressSanitizer: SEGV on unknown address %p" "ERROR: AddressSanitizer: %s on unknown address %p"
" (pc %p sp %p bp %p T%d)\n", " (pc %p bp %p sp %p T%d)\n",
(void *)addr, (void *)pc, (void *)sp, (void *)bp, description, (void *)addr, (void *)pc, (void *)bp, (void *)sp,
GetCurrentTidOrInvalid()); GetCurrentTidOrInvalid());
if (pc < GetPageSizeCached()) {
Report("Hint: pc points to the zero page.\n");
}
Printf("%s", d.EndWarning()); Printf("%s", d.EndWarning());
GET_STACK_TRACE_SIGNAL(pc, bp, context); GET_STACK_TRACE_SIGNAL(pc, bp, context);
stack.Print(); stack.Print();
@ -621,6 +656,30 @@ void ReportDoubleFree(uptr addr, StackTrace *free_stack) {
ReportErrorSummary("double-free", &stack); ReportErrorSummary("double-free", &stack);
} }
void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
StackTrace *free_stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
char tname[128];
u32 curr_tid = GetCurrentTidOrInvalid();
Report("ERROR: AddressSanitizer: new-delete-type-mismatch on %p in "
"thread T%d%s:\n",
addr, curr_tid,
ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
Printf("%s object passed to delete has wrong type:\n", d.EndWarning());
Printf(" size of the allocated type: %zd bytes;\n"
" size of the deallocated type: %zd bytes.\n",
asan_mz_size(reinterpret_cast<void*>(addr)), delete_size);
CHECK_GT(free_stack->size, 0);
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
stack.Print();
DescribeHeapAddress(addr, 1);
ReportErrorSummary("new-delete-type-mismatch", &stack);
Report("HINT: if you don't care about these warnings you may set "
"ASAN_OPTIONS=new_delete_type_mismatch=0\n");
}
void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) { void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) {
ScopedInErrorReport in_report; ScopedInErrorReport in_report;
Decorator d; Decorator d;
@ -674,17 +733,17 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
ReportErrorSummary("bad-malloc_usable_size", stack); ReportErrorSummary("bad-malloc_usable_size", stack);
} }
void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
ScopedInErrorReport in_report; ScopedInErrorReport in_report;
Decorator d; Decorator d;
Printf("%s", d.Warning()); Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: attempting to call " Report("ERROR: AddressSanitizer: attempting to call "
"__asan_get_allocated_size() for pointer which is " "__sanitizer_get_allocated_size() for pointer which is "
"not owned: %p\n", addr); "not owned: %p\n", addr);
Printf("%s", d.EndWarning()); Printf("%s", d.EndWarning());
stack->Print(); stack->Print();
DescribeHeapAddress(addr, 1); DescribeHeapAddress(addr, 1);
ReportErrorSummary("bad-__asan_get_allocated_size", stack); ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
} }
void ReportStringFunctionMemoryRangesOverlap( void ReportStringFunctionMemoryRangesOverlap(
@ -733,17 +792,36 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack); ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
} }
void ReportODRViolation(const __asan_global *g1, const __asan_global *g2) { void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2) {
ScopedInErrorReport in_report; ScopedInErrorReport in_report;
Decorator d; Decorator d;
Printf("%s", d.Warning()); Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg); Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg);
Printf("%s", d.EndWarning()); Printf("%s", d.EndWarning());
Printf(" [1] size=%zd %s %s\n", g1->size, g1->name, g1->module_name); InternalScopedString g1_loc(256), g2_loc(256);
Printf(" [2] size=%zd %s %s\n", g2->size, g2->name, g2->module_name); PrintGlobalLocation(&g1_loc, *g1);
PrintGlobalLocation(&g2_loc, *g2);
Printf(" [1] size=%zd '%s' %s\n", g1->size,
MaybeDemangleGlobalName(g1->name), g1_loc.data());
Printf(" [2] size=%zd '%s' %s\n", g2->size,
MaybeDemangleGlobalName(g2->name), g2_loc.data());
if (stack_id1 && stack_id2) {
Printf("These globals were registered at these points:\n");
Printf(" [1]:\n");
uptr stack_size;
const uptr *stack_trace = StackDepotGet(stack_id1, &stack_size);
StackTrace::PrintStack(stack_trace, stack_size);
Printf(" [2]:\n");
stack_trace = StackDepotGet(stack_id2, &stack_size);
StackTrace::PrintStack(stack_trace, stack_size);
}
Report("HINT: if you don't care about these warnings you may set " Report("HINT: if you don't care about these warnings you may set "
"ASAN_OPTIONS=detect_odr_violation=0\n"); "ASAN_OPTIONS=detect_odr_violation=0\n");
ReportErrorSummary("odr-violation", g1->module_name, 0, g1->name); InternalScopedString error_msg(256);
error_msg.append("odr-violation: global '%s' at %s",
MaybeDemangleGlobalName(g1->name), g1_loc.data());
ReportErrorSummary(error_msg.data());
} }
// ----------------------- CheckForInvalidPointerPair ----------- {{{1 // ----------------------- CheckForInvalidPointerPair ----------- {{{1
@ -831,6 +909,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
switch (*shadow_addr) { switch (*shadow_addr) {
case kAsanHeapLeftRedzoneMagic: case kAsanHeapLeftRedzoneMagic:
case kAsanHeapRightRedzoneMagic: case kAsanHeapRightRedzoneMagic:
case kAsanArrayCookieMagic:
bug_descr = "heap-buffer-overflow"; bug_descr = "heap-buffer-overflow";
break; break;
case kAsanHeapFreeMagic: case kAsanHeapFreeMagic:
@ -867,7 +946,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
Decorator d; Decorator d;
Printf("%s", d.Warning()); Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: %s on address " Report("ERROR: AddressSanitizer: %s on address "
"%p at pc 0x%zx bp 0x%zx sp 0x%zx\n", "%p at pc %p bp %p sp %p\n",
bug_descr, (void*)addr, pc, bp, sp); bug_descr, (void*)addr, pc, bp, sp);
Printf("%s", d.EndWarning()); Printf("%s", d.EndWarning());
@ -899,7 +978,10 @@ void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
} }
void __asan_describe_address(uptr addr) { void __asan_describe_address(uptr addr) {
// Thread registry must be locked while we're describing an address.
asanThreadRegistry().Lock();
DescribeAddress(addr, 1); DescribeAddress(addr, 1);
asanThreadRegistry().Unlock();
} }
extern "C" { extern "C" {

View File

@ -16,6 +16,13 @@
namespace __asan { namespace __asan {
struct StackVarDescr {
uptr beg;
uptr size;
const char *name_pos;
uptr name_len;
};
// The following functions prints address description depending // The following functions prints address description depending
// on the memory type (shadow/heap/stack/global). // on the memory type (shadow/heap/stack/global).
void DescribeHeapAddress(uptr addr, uptr access_size); void DescribeHeapAddress(uptr addr, uptr access_size);
@ -23,6 +30,8 @@ bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
const __asan_global &g); const __asan_global &g);
bool DescribeAddressIfShadow(uptr addr); bool DescribeAddressIfShadow(uptr addr);
bool ParseFrameDescription(const char *frame_descr,
InternalMmapVector<StackVarDescr> *vars);
bool DescribeAddressIfStack(uptr addr, uptr access_size); bool DescribeAddressIfStack(uptr addr, uptr access_size);
// Determines memory type on its own. // Determines memory type on its own.
void DescribeAddress(uptr addr, uptr access_size); void DescribeAddress(uptr addr, uptr access_size);
@ -32,8 +41,10 @@ void DescribeThread(AsanThreadContext *context);
// Different kinds of error reports. // Different kinds of error reports.
void NORETURN void NORETURN
ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr); ReportStackOverflow(uptr pc, uptr sp, uptr bp, void *context, uptr addr);
void NORETURN void NORETURN ReportSIGSEGV(const char *description, uptr pc, uptr sp, uptr bp,
ReportSIGSEGV(uptr pc, uptr sp, uptr bp, void *context, uptr addr); void *context, uptr addr);
void NORETURN ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
StackTrace *free_stack);
void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack); void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack);
void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack); void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack);
void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack, void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
@ -41,8 +52,8 @@ void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
AllocType dealloc_type); AllocType dealloc_type);
void NORETURN ReportMallocUsableSizeNotOwned(uptr addr, void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
StackTrace *stack); StackTrace *stack);
void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr, void NORETURN
StackTrace *stack); ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack);
void NORETURN ReportStringFunctionMemoryRangesOverlap( void NORETURN ReportStringFunctionMemoryRangesOverlap(
const char *function, const char *offset1, uptr length1, const char *function, const char *offset1, uptr length1,
const char *offset2, uptr length2, StackTrace *stack); const char *offset2, uptr length2, StackTrace *stack);
@ -53,7 +64,8 @@ ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, uptr old_mid,
uptr new_mid, StackTrace *stack); uptr new_mid, StackTrace *stack);
void NORETURN void NORETURN
ReportODRViolation(const __asan_global *g1, const __asan_global *g2); ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2);
// Mac-specific errors and warnings. // Mac-specific errors and warnings.
void WarnMacFreeUnallocated( void WarnMacFreeUnallocated(

View File

@ -171,11 +171,6 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
"If set, prints ASan exit stats even after program terminates " "If set, prints ASan exit stats even after program terminates "
"successfully."); "successfully.");
ParseFlag(str, &f->disable_core, "disable_core",
"Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
"dumping a 16T+ core file. "
"Ignored on OSes that don't dump core by default.");
ParseFlag(str, &f->allow_reexec, "allow_reexec", ParseFlag(str, &f->allow_reexec, "allow_reexec",
"Allow the tool to re-exec the program. This may interfere badly with " "Allow the tool to re-exec the program. This may interfere badly with "
"the debugger."); "the debugger.");
@ -189,6 +184,9 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
"Poison (or not) the heap memory on [de]allocation. Zero value is useful " "Poison (or not) the heap memory on [de]allocation. Zero value is useful "
"for benchmarking the allocator or instrumentator."); "for benchmarking the allocator or instrumentator.");
ParseFlag(str, &f->poison_array_cookie, "poison_array_cookie",
"Poison (or not) the array cookie after operator new[].");
ParseFlag(str, &f->poison_partial, "poison_partial", ParseFlag(str, &f->poison_partial, "poison_partial",
"If true, poison partially addressable 8-byte aligned words " "If true, poison partially addressable 8-byte aligned words "
"(default=true). This flag affects heap and global buffers, but not " "(default=true). This flag affects heap and global buffers, but not "
@ -196,6 +194,10 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch", ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch",
"Report errors on malloc/delete, new/free, new/delete[], etc."); "Report errors on malloc/delete, new/free, new/delete[], etc.");
ParseFlag(str, &f->new_delete_type_mismatch, "new_delete_type_mismatch",
"Report errors on mismatch betwen size of new and delete.");
ParseFlag(str, &f->strict_memcmp, "strict_memcmp", ParseFlag(str, &f->strict_memcmp, "strict_memcmp",
"If true, assume that memcmp(p1, p2, n) always reads n bytes before " "If true, assume that memcmp(p1, p2, n) always reads n bytes before "
"comparing p1 and p2."); "comparing p1 and p2.");
@ -262,21 +264,23 @@ void InitializeFlags(Flags *f, const char *env) {
f->print_stats = false; f->print_stats = false;
f->print_legend = true; f->print_legend = true;
f->atexit = false; f->atexit = false;
f->disable_core = (SANITIZER_WORDSIZE == 64);
f->allow_reexec = true; f->allow_reexec = true;
f->print_full_thread_history = true; f->print_full_thread_history = true;
f->poison_heap = true; f->poison_heap = true;
f->poison_array_cookie = true;
f->poison_partial = true; f->poison_partial = true;
// Turn off alloc/dealloc mismatch checker on Mac and Windows for now. // Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
// https://code.google.com/p/address-sanitizer/issues/detail?id=131 // https://code.google.com/p/address-sanitizer/issues/detail?id=131
// https://code.google.com/p/address-sanitizer/issues/detail?id=309 // https://code.google.com/p/address-sanitizer/issues/detail?id=309
// TODO(glider,timurrrr): Fix known issues and enable this back. // TODO(glider,timurrrr): Fix known issues and enable this back.
f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0); f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0);
f->new_delete_type_mismatch = true;
f->strict_memcmp = true; f->strict_memcmp = true;
f->strict_init_order = false; f->strict_init_order = false;
f->start_deactivated = false; f->start_deactivated = false;
f->detect_invalid_pointer_pairs = 0; f->detect_invalid_pointer_pairs = 0;
f->detect_container_overflow = true; f->detect_container_overflow = true;
f->detect_odr_violation = 2;
// Override from compile definition. // Override from compile definition.
ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefinition()); ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefinition());
@ -456,13 +460,6 @@ static NOINLINE void force_interface_symbols() {
case 15: __asan_set_error_report_callback(0); break; case 15: __asan_set_error_report_callback(0); break;
case 16: __asan_handle_no_return(); break; case 16: __asan_handle_no_return(); break;
case 17: __asan_address_is_poisoned(0); break; case 17: __asan_address_is_poisoned(0); break;
case 18: __asan_get_allocated_size(0); break;
case 19: __asan_get_current_allocated_bytes(); break;
case 20: __asan_get_estimated_allocated_size(0); break;
case 21: __asan_get_free_bytes(); break;
case 22: __asan_get_heap_size(); break;
case 23: __asan_get_ownership(0); break;
case 24: __asan_get_unmapped_bytes(); break;
case 25: __asan_poison_memory_region(0, 0); break; case 25: __asan_poison_memory_region(0, 0); break;
case 26: __asan_unpoison_memory_region(0, 0); break; case 26: __asan_unpoison_memory_region(0, 0); break;
case 27: __asan_set_error_exit_code(0); break; case 27: __asan_set_error_exit_code(0); break;
@ -593,6 +590,11 @@ static void AsanInitInternal() {
InitializeAsanInterceptors(); InitializeAsanInterceptors();
// Enable system log ("adb logcat") on Android.
// Doing this before interceptors are initialized crashes in:
// AsanInitInternal -> android_log_write -> __interceptor_strcmp
AndroidLogInit();
ReplaceSystemMalloc(); ReplaceSystemMalloc();
uptr shadow_start = kLowShadowBeg; uptr shadow_start = kLowShadowBeg;
@ -601,7 +603,8 @@ static void AsanInitInternal() {
bool full_shadow_is_available = bool full_shadow_is_available =
MemoryRangeIsAvailable(shadow_start, kHighShadowEnd); MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
#if SANITIZER_LINUX && defined(__x86_64__) && !ASAN_FIXED_MAPPING #if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
!ASAN_FIXED_MAPPING
if (!full_shadow_is_available) { if (!full_shadow_is_available) {
kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0; kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0; kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
@ -611,9 +614,7 @@ static void AsanInitInternal() {
if (common_flags()->verbosity) if (common_flags()->verbosity)
PrintAddressSpaceLayout(); PrintAddressSpaceLayout();
if (flags()->disable_core) { DisableCoreDumperIfNecessary();
DisableCoreDumper();
}
if (full_shadow_is_available) { if (full_shadow_is_available) {
// mmap the low shadow plus at least one page at the left. // mmap the low shadow plus at least one page at the left.
@ -648,12 +649,8 @@ static void AsanInitInternal() {
AsanTSDInit(PlatformTSDDtor); AsanTSDInit(PlatformTSDDtor);
InstallDeadlySignalHandlers(AsanOnSIGSEGV); InstallDeadlySignalHandlers(AsanOnSIGSEGV);
// Allocator should be initialized before starting external symbolizer, as
// fork() on Mac locks the allocator.
InitializeAllocator(); InitializeAllocator();
Symbolizer::Init(common_flags()->external_symbolizer_path);
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
// should be set to 1 prior to initializing the threads. // should be set to 1 prior to initializing the threads.
asan_inited = 1; asan_inited = 1;
@ -682,7 +679,7 @@ static void AsanInitInternal() {
SanitizerInitializeUnwinder(); SanitizerInitializeUnwinder();
#if CAN_SANITIZE_LEAKS #if CAN_SANITIZE_LEAKS
__lsan::InitCommonLsan(); __lsan::InitCommonLsan(false);
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
Atexit(__lsan::DoLeakCheck); Atexit(__lsan::DoLeakCheck);
} }

View File

@ -13,6 +13,7 @@
#include "asan_internal.h" #include "asan_internal.h"
#include "asan_stats.h" #include "asan_stats.h"
#include "asan_thread.h" #include "asan_thread.h"
#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stackdepot.h"
@ -127,8 +128,8 @@ static void PrintAccumulatedStats() {
BlockingMutexLock lock(&print_lock); BlockingMutexLock lock(&print_lock);
stats.Print(); stats.Print();
StackDepotStats *stack_depot_stats = StackDepotGetStats(); StackDepotStats *stack_depot_stats = StackDepotGetStats();
Printf("Stats: StackDepot: %zd ids; %zdM mapped\n", Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20); stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
PrintInternalAllocatorStats(); PrintInternalAllocatorStats();
} }
@ -137,7 +138,7 @@ static void PrintAccumulatedStats() {
// ---------------------- Interface ---------------- {{{1 // ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT using namespace __asan; // NOLINT
uptr __asan_get_current_allocated_bytes() { uptr __sanitizer_get_current_allocated_bytes() {
AsanStats stats; AsanStats stats;
GetAccumulatedStats(&stats); GetAccumulatedStats(&stats);
uptr malloced = stats.malloced; uptr malloced = stats.malloced;
@ -147,13 +148,13 @@ uptr __asan_get_current_allocated_bytes() {
return (malloced > freed) ? malloced - freed : 1; return (malloced > freed) ? malloced - freed : 1;
} }
uptr __asan_get_heap_size() { uptr __sanitizer_get_heap_size() {
AsanStats stats; AsanStats stats;
GetAccumulatedStats(&stats); GetAccumulatedStats(&stats);
return stats.mmaped - stats.munmaped; return stats.mmaped - stats.munmaped;
} }
uptr __asan_get_free_bytes() { uptr __sanitizer_get_free_bytes() {
AsanStats stats; AsanStats stats;
GetAccumulatedStats(&stats); GetAccumulatedStats(&stats);
uptr total_free = stats.mmaped uptr total_free = stats.mmaped
@ -167,7 +168,7 @@ uptr __asan_get_free_bytes() {
return (total_free > total_used) ? total_free - total_used : 1; return (total_free > total_used) ? total_free - total_used : 1;
} }
uptr __asan_get_unmapped_bytes() { uptr __sanitizer_get_unmapped_bytes() {
return 0; return 0;
} }

View File

@ -139,7 +139,10 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
} }
void AsanThread::Init() { void AsanThread::Init() {
fake_stack_ = 0; // Will be initialized lazily if needed.
CHECK_EQ(this->stack_size(), 0U);
SetThreadStackAndTls(); SetThreadStackAndTls();
CHECK_GT(this->stack_size(), 0U);
CHECK(AddrIsInMem(stack_bottom_)); CHECK(AddrIsInMem(stack_bottom_));
CHECK(AddrIsInMem(stack_top_ - 1)); CHECK(AddrIsInMem(stack_top_ - 1));
ClearShadowForThreadStackAndTLS(); ClearShadowForThreadStackAndTLS();
@ -147,7 +150,6 @@ void AsanThread::Init() {
VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
(void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_, (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
&local); &local);
fake_stack_ = 0; // Will be initialized lazily if needed.
AsanPlatformThreadInit(); AsanPlatformThreadInit();
} }

View File

@ -19,6 +19,7 @@
#include "asan_interceptors.h" #include "asan_interceptors.h"
#include "asan_internal.h" #include "asan_internal.h"
#include "asan_report.h"
#include "asan_thread.h" #include "asan_thread.h"
#include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_mutex.h"
@ -68,7 +69,7 @@ void *AsanDoesNotSupportStaticLinkage() {
return 0; return 0;
} }
void AsanCheckDynamicRTPrereqs() { UNIMPLEMENTED(); } void AsanCheckDynamicRTPrereqs() {}
void AsanCheckIncompatibleRT() {} void AsanCheckIncompatibleRT() {}
@ -84,6 +85,67 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
CONTEXT *context = info->ContextRecord;
uptr pc = (uptr)exception_record->ExceptionAddress;
#ifdef _WIN64
uptr bp = (uptr)context->Rbp, sp = (uptr)context->Rsp;
#else
uptr bp = (uptr)context->Ebp, sp = (uptr)context->Esp;
#endif
if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) {
const char *description =
(exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
? "access-violation"
: "in-page-error";
uptr access_addr = exception_record->ExceptionInformation[1];
ReportSIGSEGV(description, pc, sp, bp, context, access_addr);
}
// FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
return default_seh_handler(info);
}
// We want to install our own exception handler (EH) to print helpful reports
// on access violations and whatnot. Unfortunately, the CRT initializers assume
// they are run before any user code and drop any previously-installed EHs on
// the floor, so we can't install our handler inside __asan_init.
// (See crt0dat.c in the CRT sources for the details)
//
// Things get even more complicated with the dynamic runtime, as it finishes its
// initialization before the .exe module CRT begins to initialize.
//
// For the static runtime (-MT), it's enough to put a callback to
// __asan_set_seh_filter in the last section for C initializers.
//
// For the dynamic runtime (-MD), we want link the same
// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
// will be called for each instrumented module. This ensures that at least one
// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
int __asan_set_seh_filter() {
// We should only store the previous handler if it's not our own handler in
// order to avoid loops in the EH chain.
auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
if (prev_seh_handler != &SEHHandler)
default_seh_handler = prev_seh_handler;
return 0;
}
#if !ASAN_DYNAMIC
// Put a pointer to __asan_set_seh_filter at the end of the global list
// of C initializers, after the default EH is set by the CRT.
#pragma section(".CRT$XIZ", long, read) // NOLINT
static __declspec(allocate(".CRT$XIZ"))
int (*__intercept_seh)() = __asan_set_seh_filter;
#endif
} // namespace __asan } // namespace __asan
#endif // _WIN32 #endif // _WIN32

View File

@ -1,4 +1,4 @@
//===-- asan_dll_thunk.cc -------------------------------------------------===// //===-- asan_win_dll_thunk.cc ---------------------------------------------===//
// //
// This file is distributed under the University of Illinois Open Source // This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details. // License. See LICENSE.TXT for details.
@ -18,9 +18,10 @@
// Using #ifdef rather than relying on Makefiles etc. // Using #ifdef rather than relying on Makefiles etc.
// simplifies the build procedure. // simplifies the build procedure.
#ifdef ASAN_DLL_THUNK #ifdef ASAN_DLL_THUNK
#include "asan_init_version.h"
#include "sanitizer_common/sanitizer_interception.h" #include "sanitizer_common/sanitizer_interception.h"
// ----------------- Helper functions and macros --------------------- {{{1 // ---------- Function interception helper functions and macros ----------- {{{1
extern "C" { extern "C" {
void *__stdcall GetModuleHandleA(const char *module_name); void *__stdcall GetModuleHandleA(const char *module_name);
void *__stdcall GetProcAddress(void *module, const char *proc_name); void *__stdcall GetProcAddress(void *module, const char *proc_name);
@ -34,68 +35,143 @@ static void *getRealProcAddressOrDie(const char *name) {
return ret; return ret;
} }
// We need to intercept some functions (e.g. ASan interface, memory allocator --
// let's call them "hooks") exported by the DLL thunk and forward the hooks to
// the runtime in the main module.
// However, we don't want to keep two lists of these hooks.
// To avoid that, the list of hooks should be defined using the
// INTERCEPT_WHEN_POSSIBLE macro. Then, all these hooks can be intercepted
// at once by calling INTERCEPT_HOOKS().
// Use macro+template magic to automatically generate the list of hooks.
// Each hook at line LINE defines a template class with a static
// FunctionInterceptor<LINE>::Execute() method intercepting the hook.
// The default implementation of FunctionInterceptor<LINE> is to call
// the Execute() method corresponding to the previous line.
template<int LINE>
struct FunctionInterceptor {
static void Execute() { FunctionInterceptor<LINE-1>::Execute(); }
};
// There shouldn't be any hooks with negative definition line number.
template<>
struct FunctionInterceptor<0> {
static void Execute() {}
};
#define INTERCEPT_WHEN_POSSIBLE(main_function, dll_function) \
template<> struct FunctionInterceptor<__LINE__> { \
static void Execute() { \
void *wrapper = getRealProcAddressOrDie(main_function); \
if (!__interception::OverrideFunction((uptr)dll_function, \
(uptr)wrapper, 0)) \
abort(); \
FunctionInterceptor<__LINE__-1>::Execute(); \
} \
};
// Special case of hooks -- ASan own interface functions. Those are only called
// after __asan_init, thus an empty implementation is sufficient.
#define INTERFACE_FUNCTION(name) \
extern "C" __declspec(noinline) void name() { \
volatile int prevent_icf = (__LINE__ << 8); (void)prevent_icf; \
__debugbreak(); \
} \
INTERCEPT_WHEN_POSSIBLE(#name, name)
// INTERCEPT_HOOKS must be used after the last INTERCEPT_WHEN_POSSIBLE.
#define INTERCEPT_HOOKS FunctionInterceptor<__LINE__>::Execute
// We can't define our own version of strlen etc. because that would lead to
// link-time or even type mismatch errors. Instead, we can declare a function
// just to be able to get its address. Me may miss the first few calls to the
// functions since it can be called before __asan_init, but that would lead to
// false negatives in the startup code before user's global initializers, which
// isn't a big deal.
#define INTERCEPT_LIBRARY_FUNCTION(name) \
extern "C" void name(); \
INTERCEPT_WHEN_POSSIBLE(WRAPPER_NAME(name), name)
// Disable compiler warnings that show up if we declare our own version
// of a compiler intrinsic (e.g. strlen).
#pragma warning(disable: 4391)
#pragma warning(disable: 4392)
static void InterceptHooks();
// }}}
// ---------- Function wrapping helpers ----------------------------------- {{{1
#define WRAP_V_V(name) \ #define WRAP_V_V(name) \
extern "C" void name() { \ extern "C" void name() { \
typedef void (*fntype)(); \ typedef void (*fntype)(); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
fn(); \ fn(); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_V_W(name) \ #define WRAP_V_W(name) \
extern "C" void name(void *arg) { \ extern "C" void name(void *arg) { \
typedef void (*fntype)(void *arg); \ typedef void (*fntype)(void *arg); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
fn(arg); \ fn(arg); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_V_WW(name) \ #define WRAP_V_WW(name) \
extern "C" void name(void *arg1, void *arg2) { \ extern "C" void name(void *arg1, void *arg2) { \
typedef void (*fntype)(void *, void *); \ typedef void (*fntype)(void *, void *); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
fn(arg1, arg2); \ fn(arg1, arg2); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_V_WWW(name) \ #define WRAP_V_WWW(name) \
extern "C" void name(void *arg1, void *arg2, void *arg3) { \ extern "C" void name(void *arg1, void *arg2, void *arg3) { \
typedef void *(*fntype)(void *, void *, void *); \ typedef void *(*fntype)(void *, void *, void *); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
fn(arg1, arg2, arg3); \ fn(arg1, arg2, arg3); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_W_V(name) \ #define WRAP_W_V(name) \
extern "C" void *name() { \ extern "C" void *name() { \
typedef void *(*fntype)(); \ typedef void *(*fntype)(); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(); \ return fn(); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_W_W(name) \ #define WRAP_W_W(name) \
extern "C" void *name(void *arg) { \ extern "C" void *name(void *arg) { \
typedef void *(*fntype)(void *arg); \ typedef void *(*fntype)(void *arg); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg); \ return fn(arg); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_W_WW(name) \ #define WRAP_W_WW(name) \
extern "C" void *name(void *arg1, void *arg2) { \ extern "C" void *name(void *arg1, void *arg2) { \
typedef void *(*fntype)(void *, void *); \ typedef void *(*fntype)(void *, void *); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2); \ return fn(arg1, arg2); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_W_WWW(name) \ #define WRAP_W_WWW(name) \
extern "C" void *name(void *arg1, void *arg2, void *arg3) { \ extern "C" void *name(void *arg1, void *arg2, void *arg3) { \
typedef void *(*fntype)(void *, void *, void *); \ typedef void *(*fntype)(void *, void *, void *); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2, arg3); \ return fn(arg1, arg2, arg3); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_W_WWWW(name) \ #define WRAP_W_WWWW(name) \
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \ extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \
typedef void *(*fntype)(void *, void *, void *, void *); \ typedef void *(*fntype)(void *, void *, void *, void *); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2, arg3, arg4); \ return fn(arg1, arg2, arg3, arg4); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_W_WWWWW(name) \ #define WRAP_W_WWWWW(name) \
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
@ -103,7 +179,8 @@ static void *getRealProcAddressOrDie(const char *name) {
typedef void *(*fntype)(void *, void *, void *, void *, void *); \ typedef void *(*fntype)(void *, void *, void *, void *, void *); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2, arg3, arg4, arg5); \ return fn(arg1, arg2, arg3, arg4, arg5); \
} } \
INTERCEPT_WHEN_POSSIBLE(#name, name);
#define WRAP_W_WWWWWW(name) \ #define WRAP_W_WWWWWW(name) \
extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \
@ -111,48 +188,8 @@ static void *getRealProcAddressOrDie(const char *name) {
typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \ typedef void *(*fntype)(void *, void *, void *, void *, void *, void *); \
static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ static fntype fn = (fntype)getRealProcAddressOrDie(#name); \
return fn(arg1, arg2, arg3, arg4, arg5, arg6); \ return fn(arg1, arg2, arg3, arg4, arg5, arg6); \
} } \
// }}} INTERCEPT_WHEN_POSSIBLE(#name, name);
// --------- Interface interception helper functions and macros ----------- {{{1
// We need to intercept the ASan interface exported by the DLL thunk and forward
// all the functions to the runtime in the main module.
// However, we don't want to keep two lists of interface functions.
// To avoid that, the list of interface functions should be defined using the
// INTERFACE_FUNCTION macro. Then, all the interface can be intercepted at once
// by calling INTERCEPT_ASAN_INTERFACE().
// Use macro+template magic to automatically generate the list of interface
// functions. Each interface function at line LINE defines a template class
// with a static InterfaceInteceptor<LINE>::Execute() method intercepting the
// function. The default implementation of InterfaceInteceptor<LINE> is to call
// the Execute() method corresponding to the previous line.
template<int LINE>
struct InterfaceInteceptor {
static void Execute() { InterfaceInteceptor<LINE-1>::Execute(); }
};
// There shouldn't be any interface function with negative line number.
template<>
struct InterfaceInteceptor<0> {
static void Execute() {}
};
#define INTERFACE_FUNCTION(name) \
extern "C" void name() { __debugbreak(); } \
template<> struct InterfaceInteceptor<__LINE__> { \
static void Execute() { \
void *wrapper = getRealProcAddressOrDie(#name); \
if (!__interception::OverrideFunction((uptr)name, (uptr)wrapper, 0)) \
abort(); \
InterfaceInteceptor<__LINE__-1>::Execute(); \
} \
};
// INTERCEPT_ASAN_INTERFACE must be used after the last INTERFACE_FUNCTION.
#define INTERCEPT_ASAN_INTERFACE InterfaceInteceptor<__LINE__>::Execute
static void InterceptASanInterface();
// }}} // }}}
// ----------------- ASan own interface functions -------------------- // ----------------- ASan own interface functions --------------------
@ -165,17 +202,18 @@ extern "C" {
// Manually wrap __asan_init as we need to initialize // Manually wrap __asan_init as we need to initialize
// __asan_option_detect_stack_use_after_return afterwards. // __asan_option_detect_stack_use_after_return afterwards.
void __asan_init_v3() { void __asan_init() {
typedef void (*fntype)(); typedef void (*fntype)();
static fntype fn = 0; static fntype fn = 0;
// __asan_init is expected to be called by only one thread.
if (fn) return; if (fn) return;
fn = (fntype)getRealProcAddressOrDie("__asan_init_v3"); fn = (fntype)getRealProcAddressOrDie(__asan_init_name);
fn(); fn();
__asan_option_detect_stack_use_after_return = __asan_option_detect_stack_use_after_return =
(__asan_should_detect_stack_use_after_return() != 0); (__asan_should_detect_stack_use_after_return() != 0);
InterceptASanInterface(); InterceptHooks();
} }
} }
@ -195,6 +233,20 @@ INTERFACE_FUNCTION(__asan_report_load8)
INTERFACE_FUNCTION(__asan_report_load16) INTERFACE_FUNCTION(__asan_report_load16)
INTERFACE_FUNCTION(__asan_report_load_n) INTERFACE_FUNCTION(__asan_report_load_n)
INTERFACE_FUNCTION(__asan_store1)
INTERFACE_FUNCTION(__asan_store2)
INTERFACE_FUNCTION(__asan_store4)
INTERFACE_FUNCTION(__asan_store8)
INTERFACE_FUNCTION(__asan_store16)
INTERFACE_FUNCTION(__asan_storeN)
INTERFACE_FUNCTION(__asan_load1)
INTERFACE_FUNCTION(__asan_load2)
INTERFACE_FUNCTION(__asan_load4)
INTERFACE_FUNCTION(__asan_load8)
INTERFACE_FUNCTION(__asan_load16)
INTERFACE_FUNCTION(__asan_loadN)
INTERFACE_FUNCTION(__asan_memcpy); INTERFACE_FUNCTION(__asan_memcpy);
INTERFACE_FUNCTION(__asan_memset); INTERFACE_FUNCTION(__asan_memset);
INTERFACE_FUNCTION(__asan_memmove); INTERFACE_FUNCTION(__asan_memmove);
@ -211,6 +263,9 @@ INTERFACE_FUNCTION(__asan_unpoison_stack_memory)
INTERFACE_FUNCTION(__asan_poison_memory_region) INTERFACE_FUNCTION(__asan_poison_memory_region)
INTERFACE_FUNCTION(__asan_unpoison_memory_region) INTERFACE_FUNCTION(__asan_unpoison_memory_region)
INTERFACE_FUNCTION(__asan_address_is_poisoned)
INTERFACE_FUNCTION(__asan_region_is_poisoned)
INTERFACE_FUNCTION(__asan_get_current_fake_stack) INTERFACE_FUNCTION(__asan_get_current_fake_stack)
INTERFACE_FUNCTION(__asan_addr_is_in_fake_stack) INTERFACE_FUNCTION(__asan_addr_is_in_fake_stack)
@ -237,6 +292,8 @@ INTERFACE_FUNCTION(__asan_stack_free_8)
INTERFACE_FUNCTION(__asan_stack_free_9) INTERFACE_FUNCTION(__asan_stack_free_9)
INTERFACE_FUNCTION(__asan_stack_free_10) INTERFACE_FUNCTION(__asan_stack_free_10)
INTERFACE_FUNCTION(__sanitizer_cov_module_init)
// TODO(timurrrr): Add more interface functions on the as-needed basis. // TODO(timurrrr): Add more interface functions on the as-needed basis.
// ----------------- Memory allocation functions --------------------- // ----------------- Memory allocation functions ---------------------
@ -263,8 +320,55 @@ WRAP_W_W(_expand_dbg)
// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc). // TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc).
void InterceptASanInterface() { INTERCEPT_LIBRARY_FUNCTION(atoi);
INTERCEPT_ASAN_INTERFACE(); INTERCEPT_LIBRARY_FUNCTION(atol);
INTERCEPT_LIBRARY_FUNCTION(_except_handler3);
// _except_handler4 checks -GS cookie which is different for each module, so we
// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4).
INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
__asan_handle_no_return();
return REAL(_except_handler4)(a, b, c, d);
} }
INTERCEPT_LIBRARY_FUNCTION(frexp);
INTERCEPT_LIBRARY_FUNCTION(longjmp);
INTERCEPT_LIBRARY_FUNCTION(memchr);
INTERCEPT_LIBRARY_FUNCTION(memcmp);
INTERCEPT_LIBRARY_FUNCTION(memcpy);
INTERCEPT_LIBRARY_FUNCTION(memmove);
INTERCEPT_LIBRARY_FUNCTION(memset);
INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT
INTERCEPT_LIBRARY_FUNCTION(strchr);
INTERCEPT_LIBRARY_FUNCTION(strcmp);
INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT
INTERCEPT_LIBRARY_FUNCTION(strlen);
INTERCEPT_LIBRARY_FUNCTION(strncat);
INTERCEPT_LIBRARY_FUNCTION(strncmp);
INTERCEPT_LIBRARY_FUNCTION(strncpy);
INTERCEPT_LIBRARY_FUNCTION(strnlen);
INTERCEPT_LIBRARY_FUNCTION(strtol);
INTERCEPT_LIBRARY_FUNCTION(wcslen);
// Must be after all the interceptor declarations due to the way INTERCEPT_HOOKS
// is defined.
void InterceptHooks() {
INTERCEPT_HOOKS();
INTERCEPT_FUNCTION(_except_handler4);
}
// We want to call __asan_init before C/C++ initializers/constructors are
// executed, otherwise functions like memset might be invoked.
// For some strange reason, merely linking in asan_preinit.cc doesn't work
// as the callback is never called... Is link.exe doing something too smart?
// In DLLs, the callbacks are expected to return 0,
// otherwise CRT initialization fails.
static int call_asan_init() {
__asan_init();
return 0;
}
#pragma section(".CRT$XIB", long, read) // NOLINT
__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = call_asan_init;
#endif // ASAN_DLL_THUNK #endif // ASAN_DLL_THUNK

View 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

View File

@ -3,4 +3,4 @@
# a separate file so that version updates don't involve re-running # a separate file so that version updates don't involve re-running
# automake. # automake.
# CURRENT:REVISION:AGE # CURRENT:REVISION:AGE
1:0:0 2:0:0

View 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

View File

@ -60,6 +60,22 @@ extern "C" {
// Print the description of addr (useful when debugging in gdb). // Print the description of addr (useful when debugging in gdb).
void __asan_describe_address(void *addr); void __asan_describe_address(void *addr);
// Useful for calling from the debugger to get the allocation stack trace
// and thread ID for a heap address. Stores up to 'size' frames into 'trace',
// returns the number of stored frames or 0 on error.
size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size,
int *thread_id);
// Useful for calling from the debugger to get the free stack trace
// and thread ID for a heap address. Stores up to 'size' frames into 'trace',
// returns the number of stored frames or 0 on error.
size_t __asan_get_free_stack(void *addr, void **trace, size_t size,
int *thread_id);
// Useful for calling from the debugger to get the current shadow memory
// mapping.
void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset);
// This is an internal function that is called to report an error. // This is an internal function that is called to report an error.
// However it is still a part of the interface because users may want to // However it is still a part of the interface because users may want to
// set a breakpoint on this function in a debugger. // set a breakpoint on this function in a debugger.
@ -81,32 +97,6 @@ extern "C" {
// the program crashes before ASan report is printed. // the program crashes before ASan report is printed.
void __asan_on_error(); void __asan_on_error();
// Returns the estimated number of bytes that will be reserved by allocator
// for request of "size" bytes. If ASan allocator can't allocate that much
// memory, returns the maximal possible allocation size, otherwise returns
// "size".
size_t __asan_get_estimated_allocated_size(size_t size);
// Returns 1 if p was returned by the ASan allocator and is not yet freed.
// Otherwise returns 0.
int __asan_get_ownership(const void *p);
// Returns the number of bytes reserved for the pointer p.
// Requires (get_ownership(p) == true) or (p == 0).
size_t __asan_get_allocated_size(const void *p);
// Number of bytes, allocated and not yet freed by the application.
size_t __asan_get_current_allocated_bytes();
// Number of bytes, mmaped by asan allocator to fulfill allocation requests.
// Generally, for request of X bytes, allocator can reserve and add to free
// lists a large number of chunks of size X to use them for future requests.
// All these chunks count toward the heap size. Currently, allocator never
// releases memory to OS (instead, it just puts freed chunks to free lists).
size_t __asan_get_heap_size();
// Number of bytes, mmaped by asan allocator, which can be used to fulfill
// allocation requests. When a user program frees memory chunk, it can first
// fall into quarantine and will count toward __asan_get_free_bytes() later.
size_t __asan_get_free_bytes();
// Number of bytes in unmapped pages, that are released to OS. Currently,
// always returns 0.
size_t __asan_get_unmapped_bytes();
// Prints accumulated stats to stderr. Used for debugging. // Prints accumulated stats to stderr. Used for debugging.
void __asan_print_accumulated_stats(); void __asan_print_accumulated_stats();
@ -114,14 +104,6 @@ extern "C" {
// a string containing ASan runtime options. See asan_flags.h for details. // a string containing ASan runtime options. See asan_flags.h for details.
const char* __asan_default_options(); const char* __asan_default_options();
// Malloc hooks that may be optionally provided by user.
// __asan_malloc_hook(ptr, size) is called immediately after
// allocation of "size" bytes, which returned "ptr".
// __asan_free_hook(ptr) is called immediately before
// deallocation of "ptr".
void __asan_malloc_hook(void *ptr, size_t size);
void __asan_free_hook(void *ptr);
// The following 2 functions facilitate garbage collection in presence of // The following 2 functions facilitate garbage collection in presence of
// asan's fake stack. // asan's fake stack.

View File

@ -64,6 +64,10 @@ extern "C" {
void __sanitizer_cov_init(); void __sanitizer_cov_init();
// Record and dump coverage info. // Record and dump coverage info.
void __sanitizer_cov_dump(); void __sanitizer_cov_dump();
// Open <name>.sancov.packed in the coverage directory and return the file
// descriptor. Returns -1 on failure, or if coverage dumping is disabled.
// This is intended for use by sandboxing code.
intptr_t __sanitizer_maybe_open_cov_file(const char *name);
// Annotate the current state of a contiguous container, such as // Annotate the current state of a contiguous container, such as
// std::vector, std::string or similar. // std::vector, std::string or similar.

View File

@ -17,10 +17,6 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/* Returns a string describing a stack origin.
Return NULL if the origin is invalid, or is not a stack origin. */
const char *__msan_get_origin_descr_if_stack(uint32_t id);
/* Set raw origin for the memory range. */ /* Set raw origin for the memory range. */
void __msan_set_origin(const volatile void *a, size_t size, uint32_t origin); void __msan_set_origin(const volatile void *a, size_t size, uint32_t origin);
@ -91,55 +87,10 @@ extern "C" {
a string containing Msan runtime options. See msan_flags.h for details. */ a string containing Msan runtime options. See msan_flags.h for details. */
const char* __msan_default_options(); const char* __msan_default_options();
// Sets the callback to be called right before death on error. /* Sets the callback to be called right before death on error.
// Passing 0 will unset the callback. Passing 0 will unset the callback. */
void __msan_set_death_callback(void (*callback)(void)); void __msan_set_death_callback(void (*callback)(void));
/***********************************/
/* Allocator statistics interface. */
/* Returns the estimated number of bytes that will be reserved by allocator
for request of "size" bytes. If Msan allocator can't allocate that much
memory, returns the maximal possible allocation size, otherwise returns
"size". */
size_t __msan_get_estimated_allocated_size(size_t size);
/* Returns true if p was returned by the Msan allocator and
is not yet freed. */
int __msan_get_ownership(const volatile void *p);
/* Returns the number of bytes reserved for the pointer p.
Requires (get_ownership(p) == true) or (p == 0). */
size_t __msan_get_allocated_size(const volatile void *p);
/* Number of bytes, allocated and not yet freed by the application. */
size_t __msan_get_current_allocated_bytes();
/* Number of bytes, mmaped by msan allocator to fulfill allocation requests.
Generally, for request of X bytes, allocator can reserve and add to free
lists a large number of chunks of size X to use them for future requests.
All these chunks count toward the heap size. Currently, allocator never
releases memory to OS (instead, it just puts freed chunks to free
lists). */
size_t __msan_get_heap_size();
/* Number of bytes, mmaped by msan allocator, which can be used to fulfill
allocation requests. When a user program frees memory chunk, it can first
fall into quarantine and will count toward __msan_get_free_bytes()
later. */
size_t __msan_get_free_bytes();
/* Number of bytes in unmapped pages, that are released to OS. Currently,
always returns 0. */
size_t __msan_get_unmapped_bytes();
/* Malloc hooks that may be optionally provided by user.
__msan_malloc_hook(ptr, size) is called immediately after
allocation of "size" bytes, which returned "ptr".
__msan_free_hook(ptr) is called immediately before
deallocation of "ptr". */
void __msan_malloc_hook(const volatile void *ptr, size_t size);
void __msan_free_hook(const volatile void *ptr);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View File

@ -120,19 +120,23 @@ const interpose_substitution substitution_##func_name[] \
# define DECLARE_WRAPPER(ret_type, func, ...) # define DECLARE_WRAPPER(ret_type, func, ...)
#elif defined(_WIN32) #elif defined(_WIN32)
# if defined(_DLL) // DLL CRT # define WRAP(x) __asan_wrap_##x
# define WRAP(x) x # define WRAPPER_NAME(x) "__asan_wrap_"#x
# define WRAPPER_NAME(x) #x # define INTERCEPTOR_ATTRIBUTE __declspec(dllexport)
# define INTERCEPTOR_ATTRIBUTE
# else // Static CRT
# define WRAP(x) wrap_##x
# define WRAPPER_NAME(x) "wrap_"#x
# define INTERCEPTOR_ATTRIBUTE
# endif
# define DECLARE_WRAPPER(ret_type, func, ...) \ # define DECLARE_WRAPPER(ret_type, func, ...) \
extern "C" ret_type func(__VA_ARGS__); extern "C" ret_type func(__VA_ARGS__);
# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \ # define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__); extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__);
#elif defined(__FreeBSD__)
# define WRAP(x) __interceptor_ ## x
# define WRAPPER_NAME(x) "__interceptor_" #x
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
// FreeBSD's dynamic linker (incompliantly) gives non-weak symbols higher
// priority than weak ones so weak aliases won't work for indirect calls
// in position-independent (-fPIC / -fPIE) mode.
# define DECLARE_WRAPPER(ret_type, func, ...) \
extern "C" ret_type func(__VA_ARGS__) \
__attribute__((alias("__interceptor_" #func), visibility("default")));
#else #else
# define WRAP(x) __interceptor_ ## x # define WRAP(x) __interceptor_ ## x
# define WRAPPER_NAME(x) "__interceptor_" #x # define WRAPPER_NAME(x) "__interceptor_" #x

View File

@ -17,20 +17,6 @@
namespace __interception { namespace __interception {
bool GetRealFunctionAddress(const char *func_name, uptr *func_addr) {
const char *DLLS[] = {
"msvcr80.dll",
"msvcr90.dll",
"kernel32.dll",
NULL
};
*func_addr = 0;
for (size_t i = 0; *func_addr == 0 && DLLS[i]; ++i) {
*func_addr = (uptr)GetProcAddress(GetModuleHandleA(DLLS[i]), func_name);
}
return (*func_addr != 0);
}
// FIXME: internal_str* and internal_mem* functions should be moved from the // FIXME: internal_str* and internal_mem* functions should be moved from the
// ASan sources into interception/. // ASan sources into interception/.
@ -108,9 +94,11 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) {
case 0x458B: // 8B 45 XX = mov eax, dword ptr [ebp+XXh] case 0x458B: // 8B 45 XX = mov eax, dword ptr [ebp+XXh]
case 0x5D8B: // 8B 5D XX = mov ebx, dword ptr [ebp+XXh] case 0x5D8B: // 8B 5D XX = mov ebx, dword ptr [ebp+XXh]
case 0xEC83: // 83 EC XX = sub esp, XX case 0xEC83: // 83 EC XX = sub esp, XX
case 0x75FF: // FF 75 XX = push dword ptr [ebp+XXh]
cursor += 3; cursor += 3;
continue; continue;
case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX case 0xC1F7: // F7 C1 XX YY ZZ WW = test ecx, WWZZYYXX
case 0x25FF: // FF 25 XX YY ZZ WW = jmp dword ptr ds:[WWZZYYXX]
cursor += 6; cursor += 6;
continue; continue;
case 0x3D83: // 83 3D XX YY ZZ WW TT = cmp TT, WWZZYYXX case 0x3D83: // 83 3D XX YY ZZ WW TT = cmp TT, WWZZYYXX
@ -119,6 +107,7 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) {
} }
switch (0x00FFFFFF & *(unsigned int*)(code + cursor)) { switch (0x00FFFFFF & *(unsigned int*)(code + cursor)) {
case 0x24448A: // 8A 44 24 XX = mov eal, dword ptr [esp+XXh] case 0x24448A: // 8A 44 24 XX = mov eal, dword ptr [esp+XXh]
case 0x24448B: // 8B 44 24 XX = mov eax, dword ptr [esp+XXh]
case 0x244C8B: // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh] case 0x244C8B: // 8B 4C 24 XX = mov ecx, dword ptr [esp+XXh]
case 0x24548B: // 8B 54 24 XX = mov edx, dword ptr [esp+XXh] case 0x24548B: // 8B 54 24 XX = mov edx, dword ptr [esp+XXh]
case 0x24748B: // 8B 74 24 XX = mov esi, dword ptr [esp+XXh] case 0x24748B: // 8B 74 24 XX = mov esi, dword ptr [esp+XXh]
@ -131,8 +120,9 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) {
// FIXME: Unknown instruction failures might happen when we add a new // FIXME: Unknown instruction failures might happen when we add a new
// interceptor or a new compiler version. In either case, they should result // interceptor or a new compiler version. In either case, they should result
// in visible and readable error messages. However, merely calling abort() // in visible and readable error messages. However, merely calling abort()
// or __debugbreak() leads to an infinite recursion in CheckFailed. // leads to an infinite recursion in CheckFailed.
// Do we have a good way to abort with an error message here? // Do we have a good way to abort with an error message here?
__debugbreak();
return 0; return 0;
} }
@ -189,6 +179,33 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
return true; return true;
} }
static const void **InterestingDLLsAvailable() {
const char *InterestingDLLs[] = { "kernel32.dll", "msvcr120.dll", NULL };
static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
if (!result[0]) {
for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
if (HMODULE h = GetModuleHandleA(InterestingDLLs[i]))
result[j++] = (void *)h;
}
}
return (const void **)&result[0];
}
static bool GetFunctionAddressInDLLs(const char *func_name, uptr *func_addr) {
*func_addr = 0;
const void **DLLs = InterestingDLLsAvailable();
for (size_t i = 0; *func_addr == 0 && DLLs[i]; ++i)
*func_addr = (uptr)GetProcAddress((HMODULE)DLLs[i], func_name);
return (*func_addr != 0);
}
bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func) {
uptr orig_func;
if (!GetFunctionAddressInDLLs(name, &orig_func))
return false;
return OverrideFunction(orig_func, new_func, orig_old_func);
}
} // namespace __interception } // namespace __interception
#endif // _WIN32 #endif // _WIN32

View File

@ -20,27 +20,29 @@
#define INTERCEPTION_WIN_H #define INTERCEPTION_WIN_H
namespace __interception { namespace __interception {
// returns true if a function with the given name was found. // All the functions in the OverrideFunction() family return true on success,
bool GetRealFunctionAddress(const char *func_name, uptr *func_addr); // false on failure (including "couldn't find the function").
// returns true if the old function existed, false on failure. // Overrides a function by its address.
bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func); bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func = 0);
// Overrides a function in a system DLL or DLL CRT by its exported name.
bool OverrideFunction(const char *name, uptr new_func, uptr *orig_old_func = 0);
} // namespace __interception } // namespace __interception
#if defined(_DLL) #if defined(INTERCEPTION_DYNAMIC_CRT)
# define INTERCEPT_FUNCTION_WIN(func) \ #define INTERCEPT_FUNCTION_WIN(func) \
::__interception::GetRealFunctionAddress( \ ::__interception::OverrideFunction(#func, \
#func, (::__interception::uptr*)&REAL(func)) (::__interception::uptr)WRAP(func), \
(::__interception::uptr *)&REAL(func))
#else #else
# define INTERCEPT_FUNCTION_WIN(func) \ #define INTERCEPT_FUNCTION_WIN(func) \
::__interception::OverrideFunction( \ ::__interception::OverrideFunction((::__interception::uptr)func, \
(::__interception::uptr)func, \ (::__interception::uptr)WRAP(func), \
(::__interception::uptr)WRAP(func), \ (::__interception::uptr *)&REAL(func))
(::__interception::uptr*)&REAL(func))
#endif #endif
#define INTERCEPT_FUNCTION_VER_WIN(func, symver) \ #define INTERCEPT_FUNCTION_VER_WIN(func, symver) INTERCEPT_FUNCTION_WIN(func)
INTERCEPT_FUNCTION_WIN(func)
#endif // INTERCEPTION_WIN_H #endif // INTERCEPTION_WIN_H
#endif // _WIN32 #endif // _WIN32

View File

@ -23,16 +23,6 @@ bool lsan_init_is_running;
namespace __lsan { namespace __lsan {
static void InitializeCommonFlags() {
CommonFlags *cf = common_flags();
SetCommonFlagsDefaults(cf);
cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
cf->malloc_context_size = 30;
cf->detect_leaks = true;
ParseCommonFlagsFromString(cf, GetEnv("LSAN_OPTIONS"));
}
///// Interface to the common LSan module. ///// ///// Interface to the common LSan module. /////
bool WordIsPoisoned(uptr addr) { bool WordIsPoisoned(uptr addr) {
return false; return false;
@ -48,7 +38,7 @@ extern "C" void __lsan_init() {
return; return;
lsan_init_is_running = true; lsan_init_is_running = true;
SanitizerToolName = "LeakSanitizer"; SanitizerToolName = "LeakSanitizer";
InitializeCommonFlags(); InitCommonLsan(true);
InitializeAllocator(); InitializeAllocator();
InitTlsSize(); InitTlsSize();
InitializeInterceptors(); InitializeInterceptors();
@ -58,11 +48,14 @@ extern "C" void __lsan_init() {
ThreadStart(tid, GetTid()); ThreadStart(tid, GetTid());
SetCurrentThread(tid); SetCurrentThread(tid);
Symbolizer::Init(common_flags()->external_symbolizer_path);
InitCommonLsan();
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
Atexit(DoLeakCheck); Atexit(DoLeakCheck);
lsan_inited = true; lsan_inited = true;
lsan_init_is_running = false; lsan_init_is_running = false;
} }
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_print_stack_trace() {
GET_STACK_TRACE_FATAL;
stack.Print();
}

View File

@ -13,6 +13,26 @@
#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_stacktrace.h"
#define GET_STACK_TRACE(max_size, fast) \
StackTrace stack; \
{ \
uptr stack_top = 0, stack_bottom = 0; \
ThreadContext *t; \
if (fast && (t = CurrentThreadContext())) { \
stack_top = t->stack_end(); \
stack_bottom = t->stack_begin(); \
} \
stack.Unwind(max_size, StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
/* context */ 0, stack_top, stack_bottom, fast); \
}
#define GET_STACK_TRACE_FATAL \
GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_MALLOC \
GET_STACK_TRACE(__sanitizer::common_flags()->malloc_context_size, \
common_flags()->fast_unwind_on_malloc)
namespace __lsan { namespace __lsan {
void InitializeInterceptors(); void InitializeInterceptors();

View File

@ -13,6 +13,7 @@
#include "lsan_allocator.h" #include "lsan_allocator.h"
#include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator.h"
#include "sanitizer_common/sanitizer_allocator_interface.h"
#include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_stacktrace.h"
@ -51,7 +52,7 @@ void AllocatorThreadFinish() {
allocator.SwallowCache(&cache); allocator.SwallowCache(&cache);
} }
static ChunkMetadata *Metadata(void *p) { static ChunkMetadata *Metadata(const void *p) {
return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p)); return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
} }
@ -85,10 +86,12 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
if (cleared && allocator.FromPrimary(p)) if (cleared && allocator.FromPrimary(p))
memset(p, 0, size); memset(p, 0, size);
RegisterAllocation(stack, p, size); RegisterAllocation(stack, p, size);
if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
return p; return p;
} }
void Deallocate(void *p) { void Deallocate(void *p) {
if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
RegisterDeallocation(p); RegisterDeallocation(p);
allocator.Deallocate(&cache, p); allocator.Deallocate(&cache, p);
} }
@ -111,7 +114,7 @@ void GetAllocatorCacheRange(uptr *begin, uptr *end) {
*end = *begin + sizeof(cache); *end = *begin + sizeof(cache);
} }
uptr GetMallocUsableSize(void *p) { uptr GetMallocUsableSize(const void *p) {
ChunkMetadata *m = Metadata(p); ChunkMetadata *m = Metadata(p);
if (!m) return 0; if (!m) return 0;
return m->requested_size; return m->requested_size;
@ -198,3 +201,38 @@ IgnoreObjectResult IgnoreObjectLocked(const void *p) {
} }
} }
} // namespace __lsan } // namespace __lsan
using namespace __lsan;
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
uptr __sanitizer_get_current_allocated_bytes() {
uptr stats[AllocatorStatCount];
allocator.GetStats(stats);
return stats[AllocatorStatAllocated];
}
SANITIZER_INTERFACE_ATTRIBUTE
uptr __sanitizer_get_heap_size() {
uptr stats[AllocatorStatCount];
allocator.GetStats(stats);
return stats[AllocatorStatMapped];
}
SANITIZER_INTERFACE_ATTRIBUTE
uptr __sanitizer_get_free_bytes() { return 0; }
SANITIZER_INTERFACE_ATTRIBUTE
uptr __sanitizer_get_unmapped_bytes() { return 0; }
SANITIZER_INTERFACE_ATTRIBUTE
uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
SANITIZER_INTERFACE_ATTRIBUTE
int __sanitizer_get_ownership(const void *p) { return Metadata(p) != 0; }
SANITIZER_INTERFACE_ATTRIBUTE
uptr __sanitizer_get_allocated_size(const void *p) {
return GetMallocUsableSize(p);
}
} // extern "C"

View File

@ -23,7 +23,7 @@ void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
void Deallocate(void *p); void Deallocate(void *p);
void *Reallocate(const StackTrace &stack, void *p, uptr new_size, void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
uptr alignment); uptr alignment);
uptr GetMallocUsableSize(void *p); uptr GetMallocUsableSize(const void *p);
template<typename Callable> template<typename Callable>
void ForEachChunk(const Callable &callback); void ForEachChunk(const Callable &callback);

View File

@ -34,15 +34,13 @@ bool DisabledInThisThread() { return disable_counter > 0; }
Flags lsan_flags; Flags lsan_flags;
static void InitializeFlags() { static void InitializeFlags(bool standalone) {
Flags *f = flags(); Flags *f = flags();
// Default values. // Default values.
f->report_objects = false; f->report_objects = false;
f->resolution = 0; f->resolution = 0;
f->max_leaks = 0; f->max_leaks = 0;
f->exitcode = 23; f->exitcode = 23;
f->print_suppressions = true;
f->suppressions="";
f->use_registers = true; f->use_registers = true;
f->use_globals = true; f->use_globals = true;
f->use_stacks = true; f->use_stacks = true;
@ -70,9 +68,18 @@ static void InitializeFlags() {
ParseFlag(options, &f->log_pointers, "log_pointers", ""); ParseFlag(options, &f->log_pointers, "log_pointers", "");
ParseFlag(options, &f->log_threads, "log_threads", ""); ParseFlag(options, &f->log_threads, "log_threads", "");
ParseFlag(options, &f->exitcode, "exitcode", ""); ParseFlag(options, &f->exitcode, "exitcode", "");
ParseFlag(options, &f->print_suppressions, "print_suppressions", "");
ParseFlag(options, &f->suppressions, "suppressions", "");
} }
// Set defaults for common flags (only in standalone mode) and parse
// them from LSAN_OPTIONS.
CommonFlags *cf = common_flags();
if (standalone) {
SetCommonFlagsDefaults(cf);
cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
cf->malloc_context_size = 30;
cf->detect_leaks = true;
}
ParseCommonFlagsFromString(cf, options);
} }
#define LOG_POINTERS(...) \ #define LOG_POINTERS(...) \
@ -85,24 +92,14 @@ static void InitializeFlags() {
if (flags()->log_threads) Report(__VA_ARGS__); \ if (flags()->log_threads) Report(__VA_ARGS__); \
} while (0); } while (0);
SuppressionContext *suppression_ctx; static bool suppressions_inited = false;
void InitializeSuppressions() { void InitializeSuppressions() {
CHECK(!suppression_ctx); CHECK(!suppressions_inited);
ALIGNED(64) static char placeholder[sizeof(SuppressionContext)]; SuppressionContext::InitIfNecessary();
suppression_ctx = new(placeholder) SuppressionContext;
char *suppressions_from_file;
uptr buffer_size;
if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file,
&buffer_size, 1 << 26 /* max_len */))
suppression_ctx->Parse(suppressions_from_file);
if (flags()->suppressions[0] && !buffer_size) {
Printf("LeakSanitizer: failed to read suppressions file '%s'\n",
flags()->suppressions);
Die();
}
if (&__lsan_default_suppressions) if (&__lsan_default_suppressions)
suppression_ctx->Parse(__lsan_default_suppressions()); SuppressionContext::Get()->Parse(__lsan_default_suppressions());
suppressions_inited = true;
} }
struct RootRegion { struct RootRegion {
@ -118,8 +115,8 @@ void InitializeRootRegions() {
root_regions = new(placeholder) InternalMmapVector<RootRegion>(1); root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
} }
void InitCommonLsan() { void InitCommonLsan(bool standalone) {
InitializeFlags(); InitializeFlags(standalone);
InitializeRootRegions(); InitializeRootRegions();
if (common_flags()->detect_leaks) { if (common_flags()->detect_leaks) {
// Initialization which can fail or print warnings should only be done if // Initialization which can fail or print warnings should only be done if
@ -129,9 +126,9 @@ void InitCommonLsan() {
} }
} }
class Decorator: private __sanitizer::AnsiColorDecorator { class Decorator: public __sanitizer::SanitizerCommonDecorator {
public: public:
Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } Decorator() : SanitizerCommonDecorator() { }
const char *Error() { return Red(); } const char *Error() { return Red(); }
const char *Leak() { return Blue(); } const char *Leak() { return Blue(); }
const char *End() { return Default(); } const char *End() { return Default(); }
@ -387,7 +384,7 @@ static void CollectLeaksCb(uptr chunk, void *arg) {
static void PrintMatchedSuppressions() { static void PrintMatchedSuppressions() {
InternalMmapVector<Suppression *> matched(1); InternalMmapVector<Suppression *> matched(1);
suppression_ctx->GetMatched(&matched); SuppressionContext::Get()->GetMatched(&matched);
if (!matched.size()) if (!matched.size())
return; return;
const char *line = "-----------------------------------------------------"; const char *line = "-----------------------------------------------------";
@ -448,7 +445,7 @@ void DoLeakCheck() {
Printf("%s", d.End()); Printf("%s", d.End());
param.leak_report.ReportTopLeaks(flags()->max_leaks); param.leak_report.ReportTopLeaks(flags()->max_leaks);
} }
if (flags()->print_suppressions) if (common_flags()->print_suppressions)
PrintMatchedSuppressions(); PrintMatchedSuppressions();
if (unsuppressed_count > 0) { if (unsuppressed_count > 0) {
param.leak_report.PrintSummary(); param.leak_report.PrintSummary();
@ -463,20 +460,22 @@ static Suppression *GetSuppressionForAddr(uptr addr) {
// Suppress by module name. // Suppress by module name.
const char *module_name; const char *module_name;
uptr module_offset; uptr module_offset;
if (Symbolizer::Get()->GetModuleNameAndOffsetForPC(addr, &module_name, if (Symbolizer::GetOrInit()
&module_offset) && ->GetModuleNameAndOffsetForPC(addr, &module_name, &module_offset) &&
suppression_ctx->Match(module_name, SuppressionLeak, &s)) SuppressionContext::Get()->Match(module_name, SuppressionLeak, &s))
return s; return s;
// Suppress by file or function name. // Suppress by file or function name.
static const uptr kMaxAddrFrames = 16; static const uptr kMaxAddrFrames = 16;
InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames); InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo(); for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo();
uptr addr_frames_num = Symbolizer::Get()->SymbolizePC( uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
addr, addr_frames.data(), kMaxAddrFrames); addr, addr_frames.data(), kMaxAddrFrames);
for (uptr i = 0; i < addr_frames_num; i++) { for (uptr i = 0; i < addr_frames_num; i++) {
if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) || if (SuppressionContext::Get()->Match(addr_frames[i].function,
suppression_ctx->Match(addr_frames[i].file, SuppressionLeak, &s)) SuppressionLeak, &s) ||
SuppressionContext::Get()->Match(addr_frames[i].file, SuppressionLeak,
&s))
return s; return s;
} }
return 0; return 0;

View File

@ -49,10 +49,6 @@ struct Flags {
int max_leaks; int max_leaks;
// If nonzero kill the process with this exit code upon finding leaks. // If nonzero kill the process with this exit code upon finding leaks.
int exitcode; int exitcode;
// Print matched suppressions after leak checking.
bool print_suppressions;
// Suppressions file name.
const char* suppressions;
// Flags controlling the root set of reachable memory. // Flags controlling the root set of reachable memory.
// Global variables (.data and .bss). // Global variables (.data and .bss).
@ -133,7 +129,7 @@ enum IgnoreObjectResult {
}; };
// Functions called from the parent tool. // Functions called from the parent tool.
void InitCommonLsan(); void InitCommonLsan(bool standalone);
void DoLeakCheck(); void DoLeakCheck();
bool DisabledInThisThread(); bool DisabledInThisThread();

View File

@ -32,21 +32,6 @@ int pthread_key_create(unsigned *key, void (*destructor)(void* v));
int pthread_setspecific(unsigned key, const void *v); int pthread_setspecific(unsigned key, const void *v);
} }
#define GET_STACK_TRACE \
StackTrace stack; \
{ \
uptr stack_top = 0, stack_bottom = 0; \
ThreadContext *t; \
bool fast = common_flags()->fast_unwind_on_malloc; \
if (fast && (t = CurrentThreadContext())) { \
stack_top = t->stack_end(); \
stack_bottom = t->stack_begin(); \
} \
stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \
StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), 0, \
stack_top, stack_bottom, fast); \
}
#define ENSURE_LSAN_INITED do { \ #define ENSURE_LSAN_INITED do { \
CHECK(!lsan_init_is_running); \ CHECK(!lsan_init_is_running); \
if (!lsan_inited) \ if (!lsan_inited) \
@ -63,7 +48,7 @@ namespace std {
INTERCEPTOR(void*, malloc, uptr size) { INTERCEPTOR(void*, malloc, uptr size) {
ENSURE_LSAN_INITED; ENSURE_LSAN_INITED;
GET_STACK_TRACE; GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, 1, kAlwaysClearMemory); return Allocate(stack, size, 1, kAlwaysClearMemory);
} }
@ -86,26 +71,32 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
} }
if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0;
ENSURE_LSAN_INITED; ENSURE_LSAN_INITED;
GET_STACK_TRACE; GET_STACK_TRACE_MALLOC;
size *= nmemb; size *= nmemb;
return Allocate(stack, size, 1, true); return Allocate(stack, size, 1, true);
} }
INTERCEPTOR(void*, realloc, void *q, uptr size) { INTERCEPTOR(void*, realloc, void *q, uptr size) {
ENSURE_LSAN_INITED; ENSURE_LSAN_INITED;
GET_STACK_TRACE; GET_STACK_TRACE_MALLOC;
return Reallocate(stack, q, size, 1); return Reallocate(stack, q, size, 1);
} }
INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
ENSURE_LSAN_INITED; ENSURE_LSAN_INITED;
GET_STACK_TRACE; GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, alignment, kAlwaysClearMemory);
}
INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) {
ENSURE_LSAN_INITED;
GET_STACK_TRACE_MALLOC;
return Allocate(stack, size, alignment, kAlwaysClearMemory); return Allocate(stack, size, alignment, kAlwaysClearMemory);
} }
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
ENSURE_LSAN_INITED; ENSURE_LSAN_INITED;
GET_STACK_TRACE; GET_STACK_TRACE_MALLOC;
*memptr = Allocate(stack, size, alignment, kAlwaysClearMemory); *memptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
// FIXME: Return ENOMEM if user requested more than max alloc size. // FIXME: Return ENOMEM if user requested more than max alloc size.
return 0; return 0;
@ -113,7 +104,7 @@ INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
INTERCEPTOR(void*, valloc, uptr size) { INTERCEPTOR(void*, valloc, uptr size) {
ENSURE_LSAN_INITED; ENSURE_LSAN_INITED;
GET_STACK_TRACE; GET_STACK_TRACE_MALLOC;
if (size == 0) if (size == 0)
size = GetPageSizeCached(); size = GetPageSizeCached();
return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory);
@ -140,7 +131,7 @@ INTERCEPTOR(int, mallopt, int cmd, int value) {
INTERCEPTOR(void*, pvalloc, uptr size) { INTERCEPTOR(void*, pvalloc, uptr size) {
ENSURE_LSAN_INITED; ENSURE_LSAN_INITED;
GET_STACK_TRACE; GET_STACK_TRACE_MALLOC;
uptr PageSize = GetPageSizeCached(); uptr PageSize = GetPageSizeCached();
size = RoundUpTo(size, PageSize); size = RoundUpTo(size, PageSize);
if (size == 0) { if (size == 0) {
@ -154,7 +145,7 @@ INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free));
#define OPERATOR_NEW_BODY \ #define OPERATOR_NEW_BODY \
ENSURE_LSAN_INITED; \ ENSURE_LSAN_INITED; \
GET_STACK_TRACE; \ GET_STACK_TRACE_MALLOC; \
return Allocate(stack, size, 1, kAlwaysClearMemory); return Allocate(stack, size, 1, kAlwaysClearMemory);
INTERCEPTOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE

View File

@ -12,11 +12,7 @@
#include "lsan.h" #include "lsan.h"
#ifndef LSAN_USE_PREINIT_ARRAY #if SANITIZER_CAN_USE_PREINIT_ARRAY
#define LSAN_USE_PREINIT_ARRAY 1
#endif
#if LSAN_USE_PREINIT_ARRAY && !defined(PIC)
// We force __lsan_init to be called before anyone else by placing it into // We force __lsan_init to be called before anyone else by placing it into
// .preinit_array section. // .preinit_array section.
__attribute__((section(".preinit_array"), used)) __attribute__((section(".preinit_array"), used))

View File

@ -21,7 +21,8 @@ sanitizer_common_files = \
sanitizer_allocator.cc \ sanitizer_allocator.cc \
sanitizer_common.cc \ sanitizer_common.cc \
sanitizer_common_libcdep.cc \ sanitizer_common_libcdep.cc \
sanitizer_coverage.cc \ sanitizer_coverage_libcdep.cc \
sanitizer_coverage_mapping_libcdep.cc \
sanitizer_deadlock_detector1.cc \ sanitizer_deadlock_detector1.cc \
sanitizer_deadlock_detector2.cc \ sanitizer_deadlock_detector2.cc \
sanitizer_flags.cc \ sanitizer_flags.cc \
@ -30,11 +31,14 @@ sanitizer_common_files = \
sanitizer_linux.cc \ sanitizer_linux.cc \
sanitizer_linux_libcdep.cc \ sanitizer_linux_libcdep.cc \
sanitizer_mac.cc \ sanitizer_mac.cc \
sanitizer_persistent_allocator.cc \
sanitizer_platform_limits_linux.cc \ sanitizer_platform_limits_linux.cc \
sanitizer_platform_limits_posix.cc \ sanitizer_platform_limits_posix.cc \
sanitizer_posix.cc \ sanitizer_posix.cc \
sanitizer_posix_libcdep.cc \ sanitizer_posix_libcdep.cc \
sanitizer_printf.cc \ sanitizer_printf.cc \
sanitizer_procmaps_common.cc \
sanitizer_procmaps_freebsd.cc \
sanitizer_procmaps_linux.cc \ sanitizer_procmaps_linux.cc \
sanitizer_procmaps_mac.cc \ sanitizer_procmaps_mac.cc \
sanitizer_stackdepot.cc \ sanitizer_stackdepot.cc \
@ -49,8 +53,10 @@ sanitizer_common_files = \
sanitizer_symbolizer_win.cc \ sanitizer_symbolizer_win.cc \
sanitizer_thread_registry.cc \ sanitizer_thread_registry.cc \
sanitizer_tls_get_addr.cc \ sanitizer_tls_get_addr.cc \
sanitizer_unwind_posix_libcdep.cc \
sanitizer_win.cc sanitizer_win.cc
libsanitizer_common_la_SOURCES = $(sanitizer_common_files) libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
# Work around what appears to be a GNU make bug handling MAKEFLAGS # Work around what appears to be a GNU make bug handling MAKEFLAGS

View File

@ -64,14 +64,17 @@ CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES) LTLIBRARIES = $(noinst_LTLIBRARIES)
libsanitizer_common_la_LIBADD = libsanitizer_common_la_LIBADD =
am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \ am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
sanitizer_common_libcdep.lo sanitizer_coverage.lo \ sanitizer_common_libcdep.lo sanitizer_coverage_libcdep.lo \
sanitizer_coverage_mapping_libcdep.lo \
sanitizer_deadlock_detector1.lo \ sanitizer_deadlock_detector1.lo \
sanitizer_deadlock_detector2.lo sanitizer_flags.lo \ sanitizer_deadlock_detector2.lo sanitizer_flags.lo \
sanitizer_libc.lo sanitizer_libignore.lo sanitizer_linux.lo \ sanitizer_libc.lo sanitizer_libignore.lo sanitizer_linux.lo \
sanitizer_linux_libcdep.lo sanitizer_mac.lo \ sanitizer_linux_libcdep.lo sanitizer_mac.lo \
sanitizer_persistent_allocator.lo \
sanitizer_platform_limits_linux.lo \ sanitizer_platform_limits_linux.lo \
sanitizer_platform_limits_posix.lo sanitizer_posix.lo \ sanitizer_platform_limits_posix.lo sanitizer_posix.lo \
sanitizer_posix_libcdep.lo sanitizer_printf.lo \ sanitizer_posix_libcdep.lo sanitizer_printf.lo \
sanitizer_procmaps_common.lo sanitizer_procmaps_freebsd.lo \
sanitizer_procmaps_linux.lo sanitizer_procmaps_mac.lo \ sanitizer_procmaps_linux.lo sanitizer_procmaps_mac.lo \
sanitizer_stackdepot.lo sanitizer_stacktrace.lo \ sanitizer_stackdepot.lo sanitizer_stacktrace.lo \
sanitizer_stacktrace_libcdep.lo \ sanitizer_stacktrace_libcdep.lo \
@ -81,7 +84,8 @@ am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
sanitizer_symbolizer_libcdep.lo \ sanitizer_symbolizer_libcdep.lo \
sanitizer_symbolizer_posix_libcdep.lo \ sanitizer_symbolizer_posix_libcdep.lo \
sanitizer_symbolizer_win.lo sanitizer_thread_registry.lo \ sanitizer_symbolizer_win.lo sanitizer_thread_registry.lo \
sanitizer_tls_get_addr.lo sanitizer_win.lo sanitizer_tls_get_addr.lo sanitizer_unwind_posix_libcdep.lo \
sanitizer_win.lo
am_libsanitizer_common_la_OBJECTS = $(am__objects_1) am_libsanitizer_common_la_OBJECTS = $(am__objects_1)
libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS) libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS)
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
@ -256,7 +260,8 @@ sanitizer_common_files = \
sanitizer_allocator.cc \ sanitizer_allocator.cc \
sanitizer_common.cc \ sanitizer_common.cc \
sanitizer_common_libcdep.cc \ sanitizer_common_libcdep.cc \
sanitizer_coverage.cc \ sanitizer_coverage_libcdep.cc \
sanitizer_coverage_mapping_libcdep.cc \
sanitizer_deadlock_detector1.cc \ sanitizer_deadlock_detector1.cc \
sanitizer_deadlock_detector2.cc \ sanitizer_deadlock_detector2.cc \
sanitizer_flags.cc \ sanitizer_flags.cc \
@ -265,11 +270,14 @@ sanitizer_common_files = \
sanitizer_linux.cc \ sanitizer_linux.cc \
sanitizer_linux_libcdep.cc \ sanitizer_linux_libcdep.cc \
sanitizer_mac.cc \ sanitizer_mac.cc \
sanitizer_persistent_allocator.cc \
sanitizer_platform_limits_linux.cc \ sanitizer_platform_limits_linux.cc \
sanitizer_platform_limits_posix.cc \ sanitizer_platform_limits_posix.cc \
sanitizer_posix.cc \ sanitizer_posix.cc \
sanitizer_posix_libcdep.cc \ sanitizer_posix_libcdep.cc \
sanitizer_printf.cc \ sanitizer_printf.cc \
sanitizer_procmaps_common.cc \
sanitizer_procmaps_freebsd.cc \
sanitizer_procmaps_linux.cc \ sanitizer_procmaps_linux.cc \
sanitizer_procmaps_mac.cc \ sanitizer_procmaps_mac.cc \
sanitizer_stackdepot.cc \ sanitizer_stackdepot.cc \
@ -284,6 +292,7 @@ sanitizer_common_files = \
sanitizer_symbolizer_win.cc \ sanitizer_symbolizer_win.cc \
sanitizer_thread_registry.cc \ sanitizer_thread_registry.cc \
sanitizer_tls_get_addr.cc \ sanitizer_tls_get_addr.cc \
sanitizer_unwind_posix_libcdep.cc \
sanitizer_win.cc sanitizer_win.cc
libsanitizer_common_la_SOURCES = $(sanitizer_common_files) libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
@ -382,7 +391,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common_libcdep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage_mapping_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector1.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector1.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector2.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector2.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@
@ -391,11 +401,14 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_libcdep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_persistent_allocator.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_posix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix_libcdep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_printf.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_printf.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_common.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_freebsd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_mac.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@
@ -410,6 +423,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_tls_get_addr.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_tls_get_addr.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_posix_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@
.cc.o: .cc.o:

View File

@ -271,9 +271,9 @@ class AllocatorGlobalStats : public AllocatorStats {
if (stats == this) if (stats == this)
break; break;
} }
// All stats must be positive. // All stats must be non-negative.
for (int i = 0; i < AllocatorStatCount; i++) for (int i = 0; i < AllocatorStatCount; i++)
s[i] = ((sptr)s[i]) > 0 ? s[i] : 1; s[i] = ((sptr)s[i]) >= 0 ? s[i] : 0;
} }
private: private:

View File

@ -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

View File

@ -23,38 +23,25 @@ typedef CompactSizeClassMap InternalSizeClassMap;
static const uptr kInternalAllocatorSpace = 0; static const uptr kInternalAllocatorSpace = 0;
static const u64 kInternalAllocatorSize = SANITIZER_MMAP_RANGE_SIZE; static const u64 kInternalAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
#if SANITIZER_WORDSIZE == 32
static const uptr kInternalAllocatorRegionSizeLog = 20; static const uptr kInternalAllocatorRegionSizeLog = 20;
#if SANITIZER_WORDSIZE == 32
static const uptr kInternalAllocatorNumRegions = static const uptr kInternalAllocatorNumRegions =
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog; kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
typedef FlatByteMap<kInternalAllocatorNumRegions> ByteMap; typedef FlatByteMap<kInternalAllocatorNumRegions> ByteMap;
#else #else
static const uptr kInternalAllocatorRegionSizeLog = 24;
static const uptr kInternalAllocatorNumRegions = static const uptr kInternalAllocatorNumRegions =
kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog; kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog;
typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap; typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap;
#endif #endif
typedef SizeClassAllocator32< typedef SizeClassAllocator32<
kInternalAllocatorSpace, kInternalAllocatorSize, 16, InternalSizeClassMap, kInternalAllocatorSpace, kInternalAllocatorSize, 0, InternalSizeClassMap,
kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator; kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator;
typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator> typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator>
InternalAllocatorCache; InternalAllocatorCache;
// We don't want our internal allocator to do any map/unmap operations from
// LargeMmapAllocator.
struct CrashOnMapUnmap {
void OnMap(uptr p, uptr size) const {
RAW_CHECK_MSG(0, "Unexpected mmap in InternalAllocator!\n");
}
void OnUnmap(uptr p, uptr size) const {
RAW_CHECK_MSG(0, "Unexpected munmap in InternalAllocator!\n");
}
};
typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache, typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache,
LargeMmapAllocator<CrashOnMapUnmap> > LargeMmapAllocator<> > InternalAllocator;
InternalAllocator;
void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0); void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0);
void InternalFree(void *p, InternalAllocatorCache *cache = 0); void InternalFree(void *p, InternalAllocatorCache *cache = 0);

View File

@ -31,33 +31,20 @@ long long _InterlockedCompareExchange64( // NOLINT
long long volatile *Destination, // NOLINT long long volatile *Destination, // NOLINT
long long Exchange, long long Comparand); // NOLINT long long Exchange, long long Comparand); // NOLINT
#pragma intrinsic(_InterlockedCompareExchange64) #pragma intrinsic(_InterlockedCompareExchange64)
#ifdef _WIN64
extern "C" long long _InterlockedExchangeAdd64( // NOLINT
long long volatile * Addend, long long Value); // NOLINT
#pragma intrinsic(_InterlockedExchangeAdd64)
extern "C" void *_InterlockedCompareExchangePointer( extern "C" void *_InterlockedCompareExchangePointer(
void *volatile *Destination, void *volatile *Destination,
void *Exchange, void *Comparand); void *Exchange, void *Comparand);
#pragma intrinsic(_InterlockedCompareExchangePointer) #pragma intrinsic(_InterlockedCompareExchangePointer)
#else
// There's no _InterlockedCompareExchangePointer intrinsic on x86,
// so call _InterlockedCompareExchange instead.
extern "C" extern "C"
long __cdecl _InterlockedCompareExchange( // NOLINT long __cdecl _InterlockedCompareExchange( // NOLINT
long volatile *Destination, // NOLINT long volatile *Destination, // NOLINT
long Exchange, long Comparand); // NOLINT long Exchange, long Comparand); // NOLINT
#pragma intrinsic(_InterlockedCompareExchange) #pragma intrinsic(_InterlockedCompareExchange)
inline static void *_InterlockedCompareExchangePointer( #ifdef _WIN64
void *volatile *Destination, extern "C" long long _InterlockedExchangeAdd64( // NOLINT
void *Exchange, void *Comparand) { long long volatile * Addend, long long Value); // NOLINT
return reinterpret_cast<void*>( #pragma intrinsic(_InterlockedExchangeAdd64)
_InterlockedCompareExchange(
reinterpret_cast<long volatile*>(Destination), // NOLINT
reinterpret_cast<long>(Exchange), // NOLINT
reinterpret_cast<long>(Comparand))); // NOLINT
}
#endif #endif
namespace __sanitizer { namespace __sanitizer {

View File

@ -12,8 +12,6 @@
#include "sanitizer_common.h" #include "sanitizer_common.h"
#include "sanitizer_flags.h" #include "sanitizer_flags.h"
#include "sanitizer_libc.h" #include "sanitizer_libc.h"
#include "sanitizer_stacktrace.h"
#include "sanitizer_symbolizer.h"
namespace __sanitizer { namespace __sanitizer {
@ -195,31 +193,17 @@ void ReportErrorSummary(const char *error_type, const char *file,
ReportErrorSummary(buff.data()); ReportErrorSummary(buff.data());
} }
void ReportErrorSummary(const char *error_type, StackTrace *stack) {
if (!common_flags()->print_summary)
return;
AddressInfo ai;
#if !SANITIZER_GO
if (stack->size > 0 && Symbolizer::Get()->CanReturnFileLineInfo()) {
// Currently, we include the first stack frame into the report summary.
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
Symbolizer::Get()->SymbolizePC(pc, &ai, 1);
}
#endif
ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
}
LoadedModule::LoadedModule(const char *module_name, uptr base_address) { LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
full_name_ = internal_strdup(module_name); full_name_ = internal_strdup(module_name);
base_address_ = base_address; base_address_ = base_address;
n_ranges_ = 0; n_ranges_ = 0;
} }
void LoadedModule::addAddressRange(uptr beg, uptr end) { void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges);
ranges_[n_ranges_].beg = beg; ranges_[n_ranges_].beg = beg;
ranges_[n_ranges_].end = end; ranges_[n_ranges_].end = end;
exec_[n_ranges_] = executable;
n_ranges_++; n_ranges_++;
} }
@ -261,11 +245,6 @@ void DecreaseTotalMmap(uptr size) {
atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed); atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
} }
static void (*sandboxing_callback)();
void SetSandboxingCallback(void (*f)()) {
sandboxing_callback = f;
}
} // namespace __sanitizer } // namespace __sanitizer
using namespace __sanitizer; // NOLINT using namespace __sanitizer; // NOLINT
@ -298,13 +277,6 @@ void __sanitizer_set_report_path(const char *path) {
} }
} }
void NOINLINE
__sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args) {
PrepareForSandboxing(args);
if (sandboxing_callback)
sandboxing_callback();
}
void __sanitizer_report_error_summary(const char *error_summary) { void __sanitizer_report_error_summary(const char *error_summary) {
Printf("%s\n", error_summary); Printf("%s\n", error_summary);
} }

View File

@ -163,6 +163,9 @@ uptr ReadFileToBuffer(const char *file_name, char **buff,
// (or NULL if the mapping failes). Stores the size of mmaped region // (or NULL if the mapping failes). Stores the size of mmaped region
// in '*buff_size'. // in '*buff_size'.
void *MapFileToMemory(const char *file_name, uptr *buff_size); void *MapFileToMemory(const char *file_name, uptr *buff_size);
void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset);
bool IsAccessibleMemoryRange(uptr beg, uptr size);
// Error report formatting. // Error report formatting.
const char *StripPathPrefix(const char *filepath, const char *StripPathPrefix(const char *filepath,
@ -173,7 +176,7 @@ void PrintModuleAndOffset(InternalScopedString *buffer,
const char *module, uptr offset); const char *module, uptr offset);
// OS // OS
void DisableCoreDumper(); void DisableCoreDumperIfNecessary();
void DumpProcessMap(); void DumpProcessMap();
bool FileExists(const char *filename); bool FileExists(const char *filename);
const char *GetEnv(const char *name); const char *GetEnv(const char *name);
@ -184,11 +187,17 @@ u32 GetUid();
void ReExec(); void ReExec();
bool StackSizeIsUnlimited(); bool StackSizeIsUnlimited();
void SetStackSizeLimitInBytes(uptr limit); void SetStackSizeLimitInBytes(uptr limit);
bool AddressSpaceIsUnlimited();
void SetAddressSpaceUnlimited();
void AdjustStackSize(void *attr); void AdjustStackSize(void *attr);
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args); void PrepareForSandboxing(__sanitizer_sandbox_arguments *args);
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args); void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args);
void SetSandboxingCallback(void (*f)()); void SetSandboxingCallback(void (*f)());
void CovUpdateMapping(uptr caller_pc = 0);
void CovBeforeFork();
void CovAfterFork(int child_pid);
void InitTlsSize(); void InitTlsSize();
uptr GetTlsSize(); uptr GetTlsSize();
@ -239,7 +248,7 @@ const int kMaxSummaryLength = 1024;
// and pass it to __sanitizer_report_error_summary. // and pass it to __sanitizer_report_error_summary.
void ReportErrorSummary(const char *error_message); void ReportErrorSummary(const char *error_message);
// Same as above, but construct error_message as: // Same as above, but construct error_message as:
// error_type: file:line function // error_type file:line function
void ReportErrorSummary(const char *error_type, const char *file, void ReportErrorSummary(const char *error_type, const char *file,
int line, const char *function); int line, const char *function);
void ReportErrorSummary(const char *error_type, StackTrace *trace); void ReportErrorSummary(const char *error_type, StackTrace *trace);
@ -475,12 +484,17 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last,
class LoadedModule { class LoadedModule {
public: public:
LoadedModule(const char *module_name, uptr base_address); LoadedModule(const char *module_name, uptr base_address);
void addAddressRange(uptr beg, uptr end); void addAddressRange(uptr beg, uptr end, bool executable);
bool containsAddress(uptr address) const; bool containsAddress(uptr address) const;
const char *full_name() const { return full_name_; } const char *full_name() const { return full_name_; }
uptr base_address() const { return base_address_; } uptr base_address() const { return base_address_; }
uptr n_ranges() const { return n_ranges_; }
uptr address_range_start(int i) const { return ranges_[i].beg; }
uptr address_range_end(int i) const { return ranges_[i].end; }
bool address_range_executable(int i) const { return exec_[i]; }
private: private:
struct AddressRange { struct AddressRange {
uptr beg; uptr beg;
@ -490,6 +504,7 @@ class LoadedModule {
uptr base_address_; uptr base_address_;
static const uptr kMaxNumberOfAddressRanges = 6; static const uptr kMaxNumberOfAddressRanges = 6;
AddressRange ranges_[kMaxNumberOfAddressRanges]; AddressRange ranges_[kMaxNumberOfAddressRanges];
bool exec_[kMaxNumberOfAddressRanges];
uptr n_ranges_; uptr n_ranges_;
}; };
@ -529,10 +544,13 @@ F IndirectExternCall(F f) {
#endif #endif
#if SANITIZER_ANDROID #if SANITIZER_ANDROID
// Initialize Android logging. Any writes before this are silently lost.
void AndroidLogInit();
void AndroidLogWrite(const char *buffer); void AndroidLogWrite(const char *buffer);
void GetExtraActivationFlags(char *buf, uptr size); void GetExtraActivationFlags(char *buf, uptr size);
void SanitizerInitializeUnwinder(); void SanitizerInitializeUnwinder();
#else #else
INLINE void AndroidLogInit() {}
INLINE void AndroidLogWrite(const char *buffer_unused) {} INLINE void AndroidLogWrite(const char *buffer_unused) {}
INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; } INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
INLINE void SanitizerInitializeUnwinder() {} INLINE void SanitizerInitializeUnwinder() {}
@ -544,4 +562,9 @@ inline void *operator new(__sanitizer::operator_new_size_type size,
return alloc.Allocate(size); return alloc.Allocate(size);
} }
struct StackDepotStats {
uptr n_uniq_ids;
uptr allocated;
};
#endif // SANITIZER_COMMON_H #endif // SANITIZER_COMMON_H

View File

@ -457,6 +457,9 @@ static int printf_get_value_size(PrintfDirective *dir) {
case 8: \ case 8: \
va_arg(*aq, double); \ va_arg(*aq, double); \
break; \ break; \
case 12: \
va_arg(*aq, long double); \
break; \
case 16: \ case 16: \
va_arg(*aq, long double); \ va_arg(*aq, long double); \
break; \ break; \

View File

@ -527,7 +527,7 @@ static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
desc->name = "<DECODED_IOCTL>"; desc->name = "<DECODED_IOCTL>";
desc->size = IOC_SIZE(req); desc->size = IOC_SIZE(req);
// Sanity check. // Sanity check.
if (desc->size > 1024) return false; if (desc->size > 0xFFFF) return false;
unsigned dir = IOC_DIR(req); unsigned dir = IOC_DIR(req);
switch (dir) { switch (dir) {
case IOC_NONE: case IOC_NONE:
@ -545,10 +545,10 @@ static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
default: default:
return false; return false;
} }
if (desc->type != IOC_NONE && desc->size == 0) return false; // Size can be 0 iff type is NONE.
char id = IOC_TYPE(req); if ((desc->type == IOC_NONE) != (desc->size == 0)) return false;
// Sanity check. // Sanity check.
if (!(id >= 'a' && id <= 'z') && !(id >= 'A' && id <= 'Z')) return false; if (IOC_TYPE(req) == 0) return false;
return true; return true;
} }

View File

@ -11,6 +11,8 @@
#include "sanitizer_common.h" #include "sanitizer_common.h"
#include "sanitizer_flags.h" #include "sanitizer_flags.h"
#include "sanitizer_stacktrace.h"
#include "sanitizer_symbolizer.h"
namespace __sanitizer { namespace __sanitizer {
@ -39,4 +41,32 @@ bool ColorizeReports() {
return internal_strcmp(flag, "always") == 0 || return internal_strcmp(flag, "always") == 0 ||
(internal_strcmp(flag, "auto") == 0 && PrintsToTtyCached()); (internal_strcmp(flag, "auto") == 0 && PrintsToTtyCached());
} }
static void (*sandboxing_callback)();
void SetSandboxingCallback(void (*f)()) {
sandboxing_callback = f;
}
void ReportErrorSummary(const char *error_type, StackTrace *stack) {
if (!common_flags()->print_summary)
return;
AddressInfo ai;
#if !SANITIZER_GO
if (stack->size > 0 && Symbolizer::GetOrInit()->CanReturnFileLineInfo()) {
// Currently, we include the first stack frame into the report summary.
// Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
Symbolizer::GetOrInit()->SymbolizePC(pc, &ai, 1);
}
#endif
ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
}
} // namespace __sanitizer } // namespace __sanitizer
void NOINLINE
__sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args) {
PrepareForSandboxing(args);
if (sandboxing_callback)
sandboxing_callback();
}

View File

@ -829,6 +829,7 @@ POST_SYSCALL(stat)(long res, const void *filename, void *statbuf) {
} }
} }
#if !SANITIZER_ANDROID
PRE_SYSCALL(statfs)(const void *path, void *buf) { PRE_SYSCALL(statfs)(const void *path, void *buf) {
if (path) if (path)
PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1); PRE_READ(path, __sanitizer::internal_strlen((const char *)path) + 1);
@ -866,6 +867,7 @@ POST_SYSCALL(fstatfs64)(long res, long fd, long sz, void *buf) {
if (buf) POST_WRITE(buf, struct_statfs64_sz); if (buf) POST_WRITE(buf, struct_statfs64_sz);
} }
} }
#endif // !SANITIZER_ANDROID
PRE_SYSCALL(lstat)(const void *filename, void *statbuf) { PRE_SYSCALL(lstat)(const void *filename, void *statbuf) {
if (filename) if (filename)
@ -1322,13 +1324,13 @@ PRE_SYSCALL(io_submit)(long ctx_id, long nr, __sanitizer_iocb **iocbpp) {
} else if (op == iocb_cmd_pread && buf && len) { } else if (op == iocb_cmd_pread && buf && len) {
POST_WRITE(buf, len); POST_WRITE(buf, len);
} else if (op == iocb_cmd_pwritev) { } else if (op == iocb_cmd_pwritev) {
__sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf; __sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
for (uptr v = 0; v < len; v++) for (uptr v = 0; v < len; v++)
PRE_READ(iovec[i].iov_base, iovec[i].iov_len); PRE_READ(iovec[v].iov_base, iovec[v].iov_len);
} else if (op == iocb_cmd_preadv) { } else if (op == iocb_cmd_preadv) {
__sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf; __sanitizer_iovec *iovec = (__sanitizer_iovec*)buf;
for (uptr v = 0; v < len; v++) for (uptr v = 0; v < len; v++)
POST_WRITE(iovec[i].iov_base, iovec[i].iov_len); POST_WRITE(iovec[v].iov_base, iovec[v].iov_len);
} }
// See comment in io_getevents. // See comment in io_getevents.
COMMON_SYSCALL_RELEASE(data); COMMON_SYSCALL_RELEASE(data);
@ -2293,7 +2295,7 @@ PRE_SYSCALL(ni_syscall)() {}
POST_SYSCALL(ni_syscall)(long res) {} POST_SYSCALL(ni_syscall)(long res) {}
PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
#if defined(__i386) || defined (__x86_64) #if !SANITIZER_ANDROID && (defined(__i386) || defined (__x86_64))
if (data) { if (data) {
if (request == ptrace_setregs) { if (request == ptrace_setregs) {
PRE_READ((void *)data, struct_user_regs_struct_sz); PRE_READ((void *)data, struct_user_regs_struct_sz);
@ -2312,7 +2314,7 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
} }
POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {
#if defined(__i386) || defined (__x86_64) #if !SANITIZER_ANDROID && (defined(__i386) || defined (__x86_64))
if (res >= 0 && data) { if (res >= 0 && data) {
// Note that this is different from the interceptor in // Note that this is different from the interceptor in
// sanitizer_common_interceptors.inc. // sanitizer_common_interceptors.inc.

View File

@ -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"

View 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"

View File

@ -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

View File

@ -185,8 +185,7 @@ u32 DD::allocateId(DDCallback *cb) {
id = id_gen++; id = id_gen++;
} }
CHECK_LE(id, kMaxMutex); CHECK_LE(id, kMaxMutex);
VPrintf(3, "#%llu: DD::allocateId assign id %d\n", VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
cb->lt->ctx, id);
return id; return id;
} }

View File

@ -27,6 +27,11 @@ struct FlagDescription {
IntrusiveList<FlagDescription> flag_descriptions; IntrusiveList<FlagDescription> flag_descriptions;
// If set, the tool will install its own SEGV signal handler by default.
#ifndef SANITIZER_NEEDS_SEGV
# define SANITIZER_NEEDS_SEGV 1
#endif
void SetCommonFlagsDefaults(CommonFlags *f) { void SetCommonFlagsDefaults(CommonFlags *f) {
f->symbolize = true; f->symbolize = true;
f->external_symbolizer_path = 0; f->external_symbolizer_path = 0;
@ -53,7 +58,12 @@ void SetCommonFlagsDefaults(CommonFlags *f) {
f->legacy_pthread_cond = false; f->legacy_pthread_cond = false;
f->intercept_tls_get_addr = false; f->intercept_tls_get_addr = false;
f->coverage = false; f->coverage = false;
f->coverage_direct = SANITIZER_ANDROID;
f->coverage_dir = ".";
f->full_address_space = false; f->full_address_space = false;
f->suppressions = "";
f->print_suppressions = true;
f->disable_coredump = (SANITIZER_WORDSIZE == 64);
} }
void ParseCommonFlagsFromString(CommonFlags *f, const char *str) { void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
@ -125,9 +135,23 @@ void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
ParseFlag(str, &f->coverage, "coverage", ParseFlag(str, &f->coverage, "coverage",
"If set, coverage information will be dumped at program shutdown (if the " "If set, coverage information will be dumped at program shutdown (if the "
"coverage instrumentation was enabled at compile time)."); "coverage instrumentation was enabled at compile time).");
ParseFlag(str, &f->coverage_direct, "coverage_direct",
"If set, coverage information will be dumped directly to a memory "
"mapped file. This way data is not lost even if the process is "
"suddenly killed.");
ParseFlag(str, &f->coverage_dir, "coverage_dir",
"Target directory for coverage dumps. Defaults to the current "
"directory.");
ParseFlag(str, &f->full_address_space, "full_address_space", ParseFlag(str, &f->full_address_space, "full_address_space",
"Sanitize complete address space; " "Sanitize complete address space; "
"by default kernel area on 32-bit platforms will not be sanitized"); "by default kernel area on 32-bit platforms will not be sanitized");
ParseFlag(str, &f->suppressions, "suppressions", "Suppressions file name.");
ParseFlag(str, &f->print_suppressions, "print_suppressions",
"Print matched suppressions at exit.");
ParseFlag(str, &f->disable_coredump, "disable_coredump",
"Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
"dumping a 16T+ core file. Ignored on OSes that don't dump core by"
"default and for sanitizers that don't reserve lots of virtual memory.");
// Do a sanity check for certain flags. // Do a sanity check for certain flags.
if (f->malloc_context_size < 1) if (f->malloc_context_size < 1)
@ -143,14 +167,17 @@ static bool GetFlagValue(const char *env, const char *name,
pos = internal_strstr(env, name); pos = internal_strstr(env, name);
if (pos == 0) if (pos == 0)
return false; return false;
if (pos != env && ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) { const char *name_end = pos + internal_strlen(name);
if ((pos != env &&
((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) ||
*name_end != '=') {
// Seems to be middle of another flag name or value. // Seems to be middle of another flag name or value.
env = pos + 1; env = pos + 1;
continue; continue;
} }
pos = name_end;
break; break;
} }
pos += internal_strlen(name);
const char *end; const char *end;
if (pos[0] != '=') { if (pos[0] != '=') {
end = pos; end = pos;

View File

@ -52,7 +52,12 @@ struct CommonFlags {
bool help; bool help;
uptr mmap_limit_mb; uptr mmap_limit_mb;
bool coverage; bool coverage;
bool coverage_direct;
const char *coverage_dir;
bool full_address_space; bool full_address_space;
const char *suppressions;
bool print_suppressions;
bool disable_coredump;
}; };
inline CommonFlags *common_flags() { inline CommonFlags *common_flags() {

View 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

View File

@ -32,9 +32,13 @@
# define SANITIZER_SUPPORTS_WEAK_HOOKS 0 # define SANITIZER_SUPPORTS_WEAK_HOOKS 0
#endif #endif
// If set, the tool will install its own SEGV signal handler. // We can use .preinit_array section on Linux to call sanitizer initialization
#ifndef SANITIZER_NEEDS_SEGV // functions very early in the process startup (unless PIC macro is defined).
# define SANITIZER_NEEDS_SEGV 1 // FIXME: do we have anything like this on Mac?
#if SANITIZER_LINUX && !SANITIZER_ANDROID && !defined(PIC)
# define SANITIZER_CAN_USE_PREINIT_ARRAY 1
#else
# define SANITIZER_CAN_USE_PREINIT_ARRAY 0
#endif #endif
// GCC does not understand __has_feature // GCC does not understand __has_feature

View File

@ -72,6 +72,7 @@ uptr internal_open(const char *filename, int flags, u32 mode);
uptr internal_read(fd_t fd, void *buf, uptr count); uptr internal_read(fd_t fd, void *buf, uptr count);
uptr internal_write(fd_t fd, const void *buf, uptr count); uptr internal_write(fd_t fd, const void *buf, uptr count);
uptr internal_ftruncate(fd_t fd, uptr size);
// OS // OS
uptr internal_filesize(fd_t fd); // -1 on error. uptr internal_filesize(fd_t fd); // -1 on error.
@ -81,6 +82,7 @@ uptr internal_fstat(fd_t fd, void *buf);
uptr internal_dup2(int oldfd, int newfd); uptr internal_dup2(int oldfd, int newfd);
uptr internal_readlink(const char *path, char *buf, uptr bufsize); uptr internal_readlink(const char *path, char *buf, uptr bufsize);
uptr internal_unlink(const char *path); uptr internal_unlink(const char *path);
uptr internal_rename(const char *oldpath, const char *newpath);
void NORETURN internal__exit(int exitcode); void NORETURN internal__exit(int exitcode);
uptr internal_lseek(fd_t fd, OFF_T offset, int whence); uptr internal_lseek(fd_t fd, OFF_T offset, int whence);

View File

@ -6,7 +6,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "sanitizer_platform.h" #include "sanitizer_platform.h"
#if SANITIZER_LINUX #if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_libignore.h" #include "sanitizer_libignore.h"
#include "sanitizer_flags.h" #include "sanitizer_flags.h"
@ -101,4 +101,4 @@ void LibIgnore::OnLibraryUnloaded() {
} // namespace __sanitizer } // namespace __sanitizer
#endif // #if SANITIZER_LINUX #endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX

View File

@ -44,15 +44,16 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <unwind.h>
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD
#include <sys/sysctl.h>
#include <machine/atomic.h> #include <machine/atomic.h>
extern "C" { extern "C" {
// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on // <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
// FreeBSD 9.2 and 10.0. // FreeBSD 9.2 and 10.0.
#include <sys/umtx.h> #include <sys/umtx.h>
} }
extern char **environ; // provided by crt1
#endif // SANITIZER_FREEBSD #endif // SANITIZER_FREEBSD
#if !SANITIZER_ANDROID #if !SANITIZER_ANDROID
@ -132,7 +133,7 @@ uptr internal_open(const char *filename, int flags, u32 mode) {
uptr OpenFile(const char *filename, bool write) { uptr OpenFile(const char *filename, bool write) {
return internal_open(filename, return internal_open(filename,
write ? O_WRONLY | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); write ? O_RDWR | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660);
} }
uptr internal_read(fd_t fd, void *buf, uptr count) { uptr internal_read(fd_t fd, void *buf, uptr count) {
@ -149,6 +150,12 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) {
return res; return res;
} }
uptr internal_ftruncate(fd_t fd, uptr size) {
sptr res;
HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, size));
return res;
}
#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && !SANITIZER_FREEBSD #if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && !SANITIZER_FREEBSD
static void stat64_to_stat(struct stat64 *in, struct stat *out) { static void stat64_to_stat(struct stat64 *in, struct stat *out) {
internal_memset(out, 0, sizeof(*out)); internal_memset(out, 0, sizeof(*out));
@ -244,6 +251,15 @@ uptr internal_unlink(const char *path) {
#endif #endif
} }
uptr internal_rename(const char *oldpath, const char *newpath) {
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
(uptr)newpath);
#else
return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
#endif
}
uptr internal_sched_yield() { uptr internal_sched_yield() {
return internal_syscall(SYSCALL(sched_yield)); return internal_syscall(SYSCALL(sched_yield));
} }
@ -297,9 +313,20 @@ u64 NanoTime() {
return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000; return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
} }
// Like getenv, but reads env directly from /proc and does not use libc. // Like getenv, but reads env directly from /proc (on Linux) or parses the
// This function should be called first inside __asan_init. // 'environ' array (on FreeBSD) and does not use libc. This function should be
// called first inside __asan_init.
const char *GetEnv(const char *name) { const char *GetEnv(const char *name) {
#if SANITIZER_FREEBSD
if (::environ != 0) {
uptr NameLen = internal_strlen(name);
for (char **Env = ::environ; *Env != 0; Env++) {
if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
return (*Env) + NameLen + 1;
}
}
return 0; // Not found.
#elif SANITIZER_LINUX
static char *environ; static char *environ;
static uptr len; static uptr len;
static bool inited; static bool inited;
@ -323,6 +350,9 @@ const char *GetEnv(const char *name) {
p = endp + 1; p = endp + 1;
} }
return 0; // Not found. return 0; // Not found.
#else
#error "Unsupported platform"
#endif
} }
extern "C" { extern "C" {
@ -388,20 +418,6 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
} }
#endif // SANITIZER_GO #endif // SANITIZER_GO
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
// Some kinds of sandboxes may forbid filesystem access, so we won't be able
// to read the file mappings from /proc/self/maps. Luckily, neither the
// process will be able to load additional libraries, so it's fine to use the
// cached mappings.
MemoryMappingLayout::CacheMemoryMappings();
// Same for /proc/self/exe in the symbolizer.
#if !SANITIZER_GO
if (Symbolizer *sym = Symbolizer::GetOrNull())
sym->PrepareForSandboxing();
CovPrepareForSandboxing(args);
#endif
}
enum MutexState { enum MutexState {
MtxUnlocked = 0, MtxUnlocked = 0,
MtxLocked = 1, MtxLocked = 1,
@ -506,7 +522,11 @@ uptr internal_sigaltstack(const struct sigaltstack *ss,
} }
int internal_fork() { int internal_fork() {
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
return internal_syscall(SYSCALL(clone), SIGCHLD, 0);
#else
return internal_syscall(SYSCALL(fork)); return internal_syscall(SYSCALL(fork));
#endif
} }
#if SANITIZER_LINUX #if SANITIZER_LINUX
@ -660,24 +680,32 @@ static char proc_self_exe_cache_str[kMaxPathLength];
static uptr proc_self_exe_cache_len = 0; static uptr proc_self_exe_cache_len = 0;
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
if (proc_self_exe_cache_len > 0) {
// If available, use the cached module name.
uptr module_name_len =
internal_snprintf(buf, buf_len, "%s", proc_self_exe_cache_str);
CHECK_LT(module_name_len, buf_len);
return module_name_len;
}
#if SANITIZER_FREEBSD
const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
size_t Size = buf_len;
bool IsErr = (sysctl(Mib, 4, buf, &Size, NULL, 0) != 0);
int readlink_error = IsErr ? errno : 0;
uptr module_name_len = Size;
#else
uptr module_name_len = internal_readlink( uptr module_name_len = internal_readlink(
"/proc/self/exe", buf, buf_len); "/proc/self/exe", buf, buf_len);
int readlink_error; int readlink_error;
if (internal_iserror(module_name_len, &readlink_error)) { bool IsErr = internal_iserror(module_name_len, &readlink_error);
if (proc_self_exe_cache_len) { #endif
// If available, use the cached module name. if (IsErr) {
CHECK_LE(proc_self_exe_cache_len, buf_len); // We can't read /proc/self/exe for some reason, assume the name of the
internal_strncpy(buf, proc_self_exe_cache_str, buf_len); // binary is unknown.
module_name_len = internal_strlen(proc_self_exe_cache_str); Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, "
} else { "some stack frames may not be symbolized\n", readlink_error);
// We can't read /proc/self/exe for some reason, assume the name of the module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe");
// binary is unknown.
Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, "
"some stack frames may not be symbolized\n", readlink_error);
module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe");
}
CHECK_LT(module_name_len, buf_len); CHECK_LT(module_name_len, buf_len);
buf[module_name_len] = '\0';
} }
return module_name_len; return module_name_len;
} }
@ -806,11 +834,19 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
#endif // defined(__x86_64__) && SANITIZER_LINUX #endif // defined(__x86_64__) && SANITIZER_LINUX
#if SANITIZER_ANDROID #if SANITIZER_ANDROID
static atomic_uint8_t android_log_initialized;
void AndroidLogInit() {
atomic_store(&android_log_initialized, 1, memory_order_release);
}
// This thing is not, strictly speaking, async signal safe, but it does not seem // This thing is not, strictly speaking, async signal safe, but it does not seem
// to cause any issues. Alternative is writing to log devices directly, but // to cause any issues. Alternative is writing to log devices directly, but
// their location and message format might change in the future, so we'd really // their location and message format might change in the future, so we'd really
// like to avoid that. // like to avoid that.
void AndroidLogWrite(const char *buffer) { void AndroidLogWrite(const char *buffer) {
if (!atomic_load(&android_log_initialized, memory_order_acquire))
return;
char *copy = internal_strdup(buffer); char *copy = internal_strdup(buffer);
char *p = copy; char *p = copy;
char *q; char *q;

View File

@ -15,23 +15,25 @@
#include "sanitizer_common.h" #include "sanitizer_common.h"
#include "sanitizer_flags.h" #include "sanitizer_flags.h"
#include "sanitizer_freebsd.h"
#include "sanitizer_linux.h" #include "sanitizer_linux.h"
#include "sanitizer_placement_new.h" #include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h" #include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h" #include "sanitizer_stacktrace.h"
#include "sanitizer_atomic.h" #include "sanitizer_atomic.h"
#include "sanitizer_symbolizer.h"
#if SANITIZER_ANDROID || SANITIZER_FREEBSD
#include <dlfcn.h> // for dlsym()
#endif
#include <dlfcn.h>
#include <pthread.h> #include <pthread.h>
#include <signal.h> #include <signal.h>
#include <sys/resource.h> #include <sys/resource.h>
#if SANITIZER_FREEBSD
#define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
#endif
#include <unwind.h>
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD
#include <pthread_np.h> #include <pthread_np.h>
#include <osreldate.h>
#define pthread_getattr_np pthread_attr_get_np #define pthread_getattr_np pthread_attr_get_np
#endif #endif
@ -147,127 +149,6 @@ bool SanitizerGetThreadName(char *name, int max_len) {
#endif #endif
} }
//------------------------- SlowUnwindStack -----------------------------------
typedef struct {
uptr absolute_pc;
uptr stack_top;
uptr stack_size;
} backtrace_frame_t;
extern "C" {
typedef void *(*acquire_my_map_info_list_func)();
typedef void (*release_my_map_info_list_func)(void *map);
typedef sptr (*unwind_backtrace_signal_arch_func)(
void *siginfo, void *sigcontext, void *map_info_list,
backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
acquire_my_map_info_list_func acquire_my_map_info_list;
release_my_map_info_list_func release_my_map_info_list;
unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
} // extern "C"
#if SANITIZER_ANDROID
void SanitizerInitializeUnwinder() {
void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
if (!p) {
VReport(1,
"Failed to open libcorkscrew.so. You may see broken stack traces "
"in SEGV reports.");
return;
}
acquire_my_map_info_list =
(acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
release_my_map_info_list =
(release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
p, "unwind_backtrace_signal_arch");
if (!acquire_my_map_info_list || !release_my_map_info_list ||
!unwind_backtrace_signal_arch) {
VReport(1,
"Failed to find one of the required symbols in libcorkscrew.so. "
"You may see broken stack traces in SEGV reports.");
acquire_my_map_info_list = NULL;
unwind_backtrace_signal_arch = NULL;
release_my_map_info_list = NULL;
}
}
#endif
#ifdef __arm__
#define UNWIND_STOP _URC_END_OF_STACK
#define UNWIND_CONTINUE _URC_NO_REASON
#else
#define UNWIND_STOP _URC_NORMAL_STOP
#define UNWIND_CONTINUE _URC_NO_REASON
#endif
uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
#ifdef __arm__
uptr val;
_Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
15 /* r15 = PC */, _UVRSD_UINT32, &val);
CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
// Clear the Thumb bit.
return val & ~(uptr)1;
#else
return _Unwind_GetIP(ctx);
#endif
}
struct UnwindTraceArg {
StackTrace *stack;
uptr max_depth;
};
_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
UnwindTraceArg *arg = (UnwindTraceArg*)param;
CHECK_LT(arg->stack->size, arg->max_depth);
uptr pc = Unwind_GetIP(ctx);
arg->stack->trace[arg->stack->size++] = pc;
if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
return UNWIND_CONTINUE;
}
void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
CHECK_GE(max_depth, 2);
size = 0;
UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
_Unwind_Backtrace(Unwind_Trace, &arg);
// We need to pop a few frames so that pc is on top.
uptr to_pop = LocatePcInTrace(pc);
// trace[0] belongs to the current function so we always pop it.
if (to_pop == 0)
to_pop = 1;
PopStackFrames(to_pop);
trace[0] = pc;
}
void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
uptr max_depth) {
CHECK_GE(max_depth, 2);
if (!unwind_backtrace_signal_arch) {
SlowUnwindStack(pc, max_depth);
return;
}
void *map = acquire_my_map_info_list();
CHECK(map);
InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax);
// siginfo argument appears to be unused.
sptr res = unwind_backtrace_signal_arch(/* siginfo */ NULL, context, map,
frames.data(),
/* ignore_depth */ 0, max_depth);
release_my_map_info_list(map);
if (res < 0) return;
CHECK_LE((uptr)res, kStackTraceMax);
size = 0;
// +2 compensate for libcorkscrew unwinder returning addresses of call
// instructions instead of raw return addresses.
for (sptr i = 0; i < res; ++i)
trace[size++] = frames[i].absolute_pc + 2;
}
#if !SANITIZER_FREEBSD #if !SANITIZER_FREEBSD
static uptr g_tls_size; static uptr g_tls_size;
#endif #endif
@ -299,11 +180,11 @@ void InitTlsSize() {
static atomic_uintptr_t kThreadDescriptorSize; static atomic_uintptr_t kThreadDescriptorSize;
uptr ThreadDescriptorSize() { uptr ThreadDescriptorSize() {
char buf[64];
uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed); uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed);
if (val) if (val)
return val; return val;
#ifdef _CS_GNU_LIBC_VERSION #ifdef _CS_GNU_LIBC_VERSION
char buf[64];
uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf)); uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) { if (len < sizeof(buf) && internal_strncmp(buf, "glibc 2.", 8) == 0) {
char *end; char *end;
@ -468,6 +349,10 @@ uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
#else // SANITIZER_ANDROID #else // SANITIZER_ANDROID
# if !SANITIZER_FREEBSD # if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr; typedef ElfW(Phdr) Elf_Phdr;
# elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
# define Elf_Phdr XElf32_Phdr
# define dl_phdr_info xdl_phdr_info
# define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b))
# endif # endif
struct DlIteratePhdrData { struct DlIteratePhdrData {
@ -504,7 +389,8 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
if (phdr->p_type == PT_LOAD) { if (phdr->p_type == PT_LOAD) {
uptr cur_beg = info->dlpi_addr + phdr->p_vaddr; uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
uptr cur_end = cur_beg + phdr->p_memsz; uptr cur_end = cur_beg + phdr->p_memsz;
cur_module->addAddressRange(cur_beg, cur_end); bool executable = phdr->p_flags & PF_X;
cur_module->addAddressRange(cur_beg, cur_end, executable);
} }
} }
return 0; return 0;
@ -527,6 +413,19 @@ void SetIndirectCallWrapper(uptr wrapper) {
indirect_call_wrapper = wrapper; indirect_call_wrapper = wrapper;
} }
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
// Some kinds of sandboxes may forbid filesystem access, so we won't be able
// to read the file mappings from /proc/self/maps. Luckily, neither the
// process will be able to load additional libraries, so it's fine to use the
// cached mappings.
MemoryMappingLayout::CacheMemoryMappings();
// Same for /proc/self/exe in the symbolizer.
#if !SANITIZER_GO
Symbolizer::GetOrInit()->PrepareForSandboxing();
CovPrepareForSandboxing(args);
#endif
}
} // namespace __sanitizer } // namespace __sanitizer
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX #endif // SANITIZER_FREEBSD || SANITIZER_LINUX

View File

@ -129,6 +129,14 @@ int internal_fork() {
return fork(); return fork();
} }
uptr internal_rename(const char *oldpath, const char *newpath) {
return rename(oldpath, newpath);
}
uptr internal_ftruncate(fd_t fd, uptr size) {
return ftruncate(fd, size);
}
// ----------------- sanitizer_common.h // ----------------- sanitizer_common.h
bool FileExists(const char *filename) { bool FileExists(const char *filename) {
struct stat st; struct stat st;

View File

@ -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

View File

@ -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(&region_pos, memory_order_acquire);
uptr end = atomic_load(&region_end, memory_order_acquire);
if (cmp == 0 || cmp + size > end) return 0;
if (atomic_compare_exchange_weak(&region_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(&region_pos, 0, memory_order_relaxed);
uptr allocsz = 64 * 1024;
if (allocsz < size) allocsz = size;
uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
atomic_store(&region_end, mem + allocsz, memory_order_release);
atomic_store(&region_pos, mem, memory_order_release);
}
}
extern PersistentAllocator thePersistentAllocator;
inline void *PersistentAlloc(uptr sz) {
return thePersistentAllocator.alloc(sz);
}
} // namespace __sanitizer
#endif // SANITIZER_PERSISTENT_ALLOCATOR_H

View File

@ -27,6 +27,12 @@
# define SI_LINUX_NOT_ANDROID 0 # define SI_LINUX_NOT_ANDROID 0
#endif #endif
#if SANITIZER_FREEBSD
# define SI_FREEBSD 1
#else
# define SI_FREEBSD 0
#endif
#if SANITIZER_LINUX #if SANITIZER_LINUX
# define SI_LINUX 1 # define SI_LINUX 1
#else #else
@ -73,11 +79,11 @@
#define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX #define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX_NOT_ANDROID
#ifndef SANITIZER_INTERCEPT_PRINTF #ifndef SANITIZER_INTERCEPT_PRINTF
# define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS
# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX # define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID
#endif #endif
#define SANITIZER_INTERCEPT_FREXP 1 #define SANITIZER_INTERCEPT_FREXP 1
@ -86,10 +92,10 @@
#define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \ #define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \
SI_MAC || SI_LINUX_NOT_ANDROID SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETPWENT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPWENT SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETPWENT_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETPWENT_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SETPWENT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SETPWENT SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_LINUX #define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_LINUX
#define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS
@ -102,9 +108,12 @@
#define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX #define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX
#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX #define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS
@ -117,13 +126,13 @@
(defined(__i386) || defined (__x86_64)) // NOLINT (defined(__i386) || defined (__x86_64)) // NOLINT
#define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX #define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX #define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID
@ -140,19 +149,21 @@
#define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SIGSETOPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGSETOPS \
SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX #define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
#define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STATFS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STATFS SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STATFS64 \ #define SANITIZER_INTERCEPT_STATFS64 \
(SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_ETHER SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ETHER_NTOA_ATON SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_ETHER_HOST SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SHMCTL \ #define SANITIZER_INTERCEPT_SHMCTL \
(SI_LINUX_NOT_ANDROID && SANITIZER_WORDSIZE == 64) (SI_LINUX_NOT_ANDROID && SANITIZER_WORDSIZE == 64)
@ -161,6 +172,19 @@
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \ #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
SI_MAC || SI_LINUX_NOT_ANDROID SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL \
SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING \
SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS
@ -168,6 +192,7 @@
#define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX #define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX
#define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID
@ -176,7 +201,7 @@
// FIXME: getline seems to be available on OSX 10.7 // FIXME: getline seems to be available on OSX 10.7
#define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT__EXIT SI_LINUX #define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD
#define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID
@ -201,5 +226,10 @@
#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE SI_LINUX_NOT_ANDROID || SI_MAC
#define SANITIZER_INTERCEPT_GETPASS SI_LINUX_NOT_ANDROID || SI_MAC
#define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H

View File

@ -27,7 +27,7 @@
// are not defined anywhere in userspace headers. Fake them. This seems to work // are not defined anywhere in userspace headers. Fake them. This seems to work
// fine with newer headers, too. // fine with newer headers, too.
#include <asm/posix_types.h> #include <asm/posix_types.h>
#if defined(__x86_64__) #if defined(__x86_64__) || defined(__mips__)
#include <sys/stat.h> #include <sys/stat.h>
#else #else
#define ino_t __kernel_ino_t #define ino_t __kernel_ino_t
@ -48,21 +48,19 @@
#include <linux/aio_abi.h> #include <linux/aio_abi.h>
#if SANITIZER_ANDROID
#include <asm/statfs.h>
#else
#include <sys/statfs.h>
#endif
#if !SANITIZER_ANDROID #if !SANITIZER_ANDROID
#include <sys/statfs.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#endif #endif
namespace __sanitizer { namespace __sanitizer {
#if !SANITIZER_ANDROID
unsigned struct_statfs64_sz = sizeof(struct statfs64); unsigned struct_statfs64_sz = sizeof(struct statfs64);
#endif
} // namespace __sanitizer } // namespace __sanitizer
#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__) #if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)\
&& !defined(__mips__)
COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat)); COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
#endif #endif

View File

@ -33,7 +33,6 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/timeb.h>
#include <sys/times.h> #include <sys/times.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/utsname.h> #include <sys/utsname.h>
@ -43,6 +42,7 @@
#if !SANITIZER_ANDROID #if !SANITIZER_ANDROID
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/timeb.h>
#endif #endif
#if SANITIZER_LINUX #if SANITIZER_LINUX
@ -189,13 +189,14 @@ namespace __sanitizer {
unsigned struct_tms_sz = sizeof(struct tms); unsigned struct_tms_sz = sizeof(struct tms);
unsigned struct_sigevent_sz = sizeof(struct sigevent); unsigned struct_sigevent_sz = sizeof(struct sigevent);
unsigned struct_sched_param_sz = sizeof(struct sched_param); unsigned struct_sched_param_sz = sizeof(struct sched_param);
unsigned struct_statfs_sz = sizeof(struct statfs);
#if SANITIZER_MAC && !SANITIZER_IOS #if SANITIZER_MAC && !SANITIZER_IOS
unsigned struct_statfs64_sz = sizeof(struct statfs64); unsigned struct_statfs64_sz = sizeof(struct statfs64);
#endif // SANITIZER_MAC && !SANITIZER_IOS #endif // SANITIZER_MAC && !SANITIZER_IOS
#if !SANITIZER_ANDROID #if !SANITIZER_ANDROID
unsigned struct_statfs_sz = sizeof(struct statfs);
unsigned struct_sockaddr_sz = sizeof(struct sockaddr); unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
unsigned ucontext_t_sz = sizeof(ucontext_t); unsigned ucontext_t_sz = sizeof(ucontext_t);
#endif // !SANITIZER_ANDROID #endif // !SANITIZER_ANDROID
@ -287,6 +288,7 @@ namespace __sanitizer {
int ptrace_setfpregs = PTRACE_SETFPREGS; int ptrace_setfpregs = PTRACE_SETFPREGS;
int ptrace_getfpxregs = PTRACE_GETFPXREGS; int ptrace_getfpxregs = PTRACE_GETFPXREGS;
int ptrace_setfpxregs = PTRACE_SETFPXREGS; int ptrace_setfpxregs = PTRACE_SETFPXREGS;
int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \ #if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
(defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO)) (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
int ptrace_getsiginfo = PTRACE_GETSIGINFO; int ptrace_getsiginfo = PTRACE_GETSIGINFO;
@ -396,7 +398,7 @@ namespace __sanitizer {
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req); unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
#endif #endif
unsigned IOCTL_NOT_PRESENT = 0; const unsigned IOCTL_NOT_PRESENT = 0;
unsigned IOCTL_FIOASYNC = FIOASYNC; unsigned IOCTL_FIOASYNC = FIOASYNC;
unsigned IOCTL_FIOCLEX = FIOCLEX; unsigned IOCTL_FIOCLEX = FIOCLEX;
@ -1056,6 +1058,10 @@ CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
CHECK_TYPE_SIZE(clock_t); CHECK_TYPE_SIZE(clock_t);
#if SANITIZER_LINUX
CHECK_TYPE_SIZE(clockid_t);
#endif
#if !SANITIZER_ANDROID #if !SANITIZER_ANDROID
CHECK_TYPE_SIZE(ifaddrs); CHECK_TYPE_SIZE(ifaddrs);
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
@ -1086,11 +1092,13 @@ CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
COMPILER_CHECK(sizeof(__sanitizer_mallinfo) == sizeof(struct mallinfo)); COMPILER_CHECK(sizeof(__sanitizer_mallinfo) == sizeof(struct mallinfo));
#endif #endif
#if !SANITIZER_ANDROID
CHECK_TYPE_SIZE(timeb); CHECK_TYPE_SIZE(timeb);
CHECK_SIZE_AND_OFFSET(timeb, time); CHECK_SIZE_AND_OFFSET(timeb, time);
CHECK_SIZE_AND_OFFSET(timeb, millitm); CHECK_SIZE_AND_OFFSET(timeb, millitm);
CHECK_SIZE_AND_OFFSET(timeb, timezone); CHECK_SIZE_AND_OFFSET(timeb, timezone);
CHECK_SIZE_AND_OFFSET(timeb, dstflag); CHECK_SIZE_AND_OFFSET(timeb, dstflag);
#endif
CHECK_TYPE_SIZE(passwd); CHECK_TYPE_SIZE(passwd);
CHECK_SIZE_AND_OFFSET(passwd, pw_name); CHECK_SIZE_AND_OFFSET(passwd, pw_name);

View File

@ -37,11 +37,11 @@ namespace __sanitizer {
extern unsigned struct_itimerspec_sz; extern unsigned struct_itimerspec_sz;
extern unsigned struct_sigevent_sz; extern unsigned struct_sigevent_sz;
extern unsigned struct_sched_param_sz; extern unsigned struct_sched_param_sz;
extern unsigned struct_statfs_sz;
extern unsigned struct_statfs64_sz; extern unsigned struct_statfs64_sz;
extern unsigned struct_sockaddr_sz;
#if !SANITIZER_ANDROID #if !SANITIZER_ANDROID
extern unsigned struct_statfs_sz;
extern unsigned struct_sockaddr_sz;
extern unsigned ucontext_t_sz; extern unsigned ucontext_t_sz;
#endif // !SANITIZER_ANDROID #endif // !SANITIZER_ANDROID
@ -65,6 +65,13 @@ namespace __sanitizer {
#elif defined(__powerpc64__) #elif defined(__powerpc64__)
const unsigned struct_kernel_stat_sz = 144; const unsigned struct_kernel_stat_sz = 144;
const unsigned struct_kernel_stat64_sz = 104; const unsigned struct_kernel_stat64_sz = 104;
#elif defined(__mips__)
#if SANITIZER_WORDSIZE == 64
const unsigned struct_kernel_stat_sz = 216;
#else
const unsigned struct_kernel_stat_sz = 144;
#endif
const unsigned struct_kernel_stat64_sz = 104;
#endif #endif
struct __sanitizer_perf_event_attr { struct __sanitizer_perf_event_attr {
unsigned type; unsigned type;
@ -160,6 +167,12 @@ namespace __sanitizer {
unsigned __seq; unsigned __seq;
u64 __unused1; u64 __unused1;
u64 __unused2; u64 __unused2;
#elif defined(__mips__)
unsigned int mode;
unsigned short __seq;
unsigned short __pad1;
unsigned long __unused1;
unsigned long __unused2;
#else #else
unsigned short mode; unsigned short mode;
unsigned short __pad1; unsigned short __pad1;
@ -188,15 +201,15 @@ namespace __sanitizer {
u64 shm_ctime; u64 shm_ctime;
#else #else
uptr shm_atime; uptr shm_atime;
#ifndef _LP64 #if !defined(_LP64) && !defined(__mips__)
uptr __unused1; uptr __unused1;
#endif #endif
uptr shm_dtime; uptr shm_dtime;
#ifndef _LP64 #if !defined(_LP64) && !defined(__mips__)
uptr __unused2; uptr __unused2;
#endif #endif
uptr shm_ctime; uptr shm_ctime;
#ifndef _LP64 #if !defined(_LP64) && !defined(__mips__)
uptr __unused3; uptr __unused3;
#endif #endif
#endif #endif
@ -438,8 +451,13 @@ namespace __sanitizer {
typedef long __sanitizer_clock_t; typedef long __sanitizer_clock_t;
#endif #endif
#if SANITIZER_LINUX
typedef int __sanitizer_clockid_t;
#endif
#if SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX || SANITIZER_FREEBSD
#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__) #if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)\
|| defined(__mips__)
typedef unsigned __sanitizer___kernel_uid_t; typedef unsigned __sanitizer___kernel_uid_t;
typedef unsigned __sanitizer___kernel_gid_t; typedef unsigned __sanitizer___kernel_gid_t;
#else #else
@ -452,7 +470,7 @@ namespace __sanitizer {
typedef long __sanitizer___kernel_off_t; typedef long __sanitizer___kernel_off_t;
#endif #endif
#if defined(__powerpc__) || defined(__aarch64__) #if defined(__powerpc__) || defined(__aarch64__) || defined(__mips__)
typedef unsigned int __sanitizer___kernel_old_uid_t; typedef unsigned int __sanitizer___kernel_old_uid_t;
typedef unsigned int __sanitizer___kernel_old_gid_t; typedef unsigned int __sanitizer___kernel_old_gid_t;
#else #else
@ -492,6 +510,9 @@ namespace __sanitizer {
// Linux system headers define the 'sa_handler' and 'sa_sigaction' macros. // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
struct __sanitizer_sigaction { struct __sanitizer_sigaction {
#if defined(__mips__) && !SANITIZER_FREEBSD
unsigned int sa_flags;
#endif
union { union {
void (*sigaction)(int sig, void *siginfo, void *uctx); void (*sigaction)(int sig, void *siginfo, void *uctx);
void (*handler)(int sig); void (*handler)(int sig);
@ -501,10 +522,15 @@ namespace __sanitizer {
__sanitizer_sigset_t sa_mask; __sanitizer_sigset_t sa_mask;
#else #else
__sanitizer_sigset_t sa_mask; __sanitizer_sigset_t sa_mask;
#ifndef __mips__
int sa_flags; int sa_flags;
#endif #endif
#endif
#if SANITIZER_LINUX #if SANITIZER_LINUX
void (*sa_restorer)(); void (*sa_restorer)();
#endif
#if defined(__mips__) && (SANITIZER_WORDSIZE == 32)
int sa_resv[1];
#endif #endif
}; };
@ -676,6 +702,7 @@ namespace __sanitizer {
extern int ptrace_setsiginfo; extern int ptrace_setsiginfo;
extern int ptrace_getregset; extern int ptrace_getregset;
extern int ptrace_setregset; extern int ptrace_setregset;
extern int ptrace_geteventmsg;
#endif #endif
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID #if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
@ -718,7 +745,7 @@ struct __sanitizer_obstack {
#define IOC_NRBITS 8 #define IOC_NRBITS 8
#define IOC_TYPEBITS 8 #define IOC_TYPEBITS 8
#if defined(__powerpc__) || defined(__powerpc64__) #if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__)
#define IOC_SIZEBITS 13 #define IOC_SIZEBITS 13
#define IOC_DIRBITS 3 #define IOC_DIRBITS 3
#define IOC_NONE 1U #define IOC_NONE 1U
@ -832,7 +859,7 @@ struct __sanitizer_obstack {
// A special value to mark ioctls that are not present on the target platform, // A special value to mark ioctls that are not present on the target platform,
// when it can not be determined without including any system headers. // when it can not be determined without including any system headers.
extern unsigned IOCTL_NOT_PRESENT; extern const unsigned IOCTL_NOT_PRESENT;
extern unsigned IOCTL_FIOASYNC; extern unsigned IOCTL_FIOASYNC;
extern unsigned IOCTL_FIOCLEX; extern unsigned IOCTL_FIOCLEX;

View File

@ -204,6 +204,17 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) {
return internal_iserror(map) ? 0 : (void *)map; return internal_iserror(map) ? 0 : (void *)map;
} }
void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) {
uptr flags = MAP_SHARED;
if (addr) flags |= MAP_FIXED;
uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
if (internal_iserror(p)) {
Printf("could not map writable file (%zd, %zu, %zu): %zd\n", fd, offset,
size, p);
return 0;
}
return (void *)p;
}
static inline bool IntervalsAreSeparate(uptr start1, uptr end1, static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
uptr start2, uptr end2) { uptr start2, uptr end2) {

View File

@ -42,30 +42,49 @@ void FlushUnneededShadowMemory(uptr addr, uptr size) {
madvise((void*)addr, size, MADV_DONTNEED); madvise((void*)addr, size, MADV_DONTNEED);
} }
void DisableCoreDumper() { static rlim_t getlim(int res) {
struct rlimit nocore; rlimit rlim;
nocore.rlim_cur = 0; CHECK_EQ(0, getrlimit(res, &rlim));
nocore.rlim_max = 0; return rlim.rlim_cur;
setrlimit(RLIMIT_CORE, &nocore);
} }
bool StackSizeIsUnlimited() { static void setlim(int res, rlim_t lim) {
struct rlimit rlim; // The following magic is to prevent clang from replacing it with memset.
CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim)); volatile struct rlimit rlim;
return ((uptr)rlim.rlim_cur == (uptr)-1); rlim.rlim_cur = lim;
} rlim.rlim_max = lim;
if (setrlimit(res, (struct rlimit*)&rlim)) {
void SetStackSizeLimitInBytes(uptr limit) {
struct rlimit rlim;
rlim.rlim_cur = limit;
rlim.rlim_max = limit;
if (setrlimit(RLIMIT_STACK, &rlim)) {
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno); Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
Die(); Die();
} }
}
void DisableCoreDumperIfNecessary() {
if (common_flags()->disable_coredump) {
setlim(RLIMIT_CORE, 0);
}
}
bool StackSizeIsUnlimited() {
rlim_t stack_size = getlim(RLIMIT_STACK);
return (stack_size == RLIM_INFINITY);
}
void SetStackSizeLimitInBytes(uptr limit) {
setlim(RLIMIT_STACK, (rlim_t)limit);
CHECK(!StackSizeIsUnlimited()); CHECK(!StackSizeIsUnlimited());
} }
bool AddressSpaceIsUnlimited() {
rlim_t as_size = getlim(RLIMIT_AS);
return (as_size == RLIM_INFINITY);
}
void SetAddressSpaceUnlimited() {
setlim(RLIMIT_AS, RLIM_INFINITY);
CHECK(AddressSpaceIsUnlimited());
}
void SleepForSeconds(int seconds) { void SleepForSeconds(int seconds) {
sleep(seconds); sleep(seconds);
} }
@ -127,7 +146,9 @@ static void MaybeInstallSigaction(int signum,
struct sigaction sigact; struct sigaction sigact;
internal_memset(&sigact, 0, sizeof(sigact)); internal_memset(&sigact, 0, sizeof(sigact));
sigact.sa_sigaction = (sa_sigaction_t)handler; sigact.sa_sigaction = (sa_sigaction_t)handler;
sigact.sa_flags = SA_SIGINFO; // Do not block the signal from being received in that signal's handler.
// Clients are responsible for handling this correctly.
sigact.sa_flags = SA_SIGINFO | SA_NODEFER;
if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
CHECK_EQ(0, internal_sigaction(signum, &sigact, 0)); CHECK_EQ(0, internal_sigaction(signum, &sigact, 0));
VReport(1, "Installed the sigaction for signal %d\n", signum); VReport(1, "Installed the sigaction for signal %d\n", signum);
@ -143,6 +164,28 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) {
} }
#endif // SANITIZER_GO #endif // SANITIZER_GO
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
uptr page_size = GetPageSizeCached();
// Checking too large memory ranges is slow.
CHECK_LT(size, page_size * 10);
int sock_pair[2];
if (pipe(sock_pair))
return false;
uptr bytes_written =
internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
int write_errno;
bool result;
if (internal_iserror(bytes_written, &write_errno)) {
CHECK_EQ(EFAULT, write_errno);
result = false;
} else {
result = (bytes_written == size);
}
internal_close(sock_pair[0]);
internal_close(sock_pair[1]);
return result;
}
} // namespace __sanitizer } // namespace __sanitizer
#endif // SANITIZER_POSIX #endif // SANITIZER_POSIX

View File

@ -20,7 +20,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#if SANITIZER_WINDOWS && !defined(va_copy) #if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \
!defined(va_copy)
# define va_copy(dst, src) ((dst) = (src)) # define va_copy(dst, src) ((dst) = (src))
#endif #endif

View File

@ -24,6 +24,9 @@ struct ProcSelfMapsBuff {
uptr mmaped_size; uptr mmaped_size;
uptr len; uptr len;
}; };
// Reads process memory map in an OS-specific way.
void ReadProcMaps(ProcSelfMapsBuff *proc_maps);
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX #endif // SANITIZER_FREEBSD || SANITIZER_LINUX
class MemoryMappingLayout { class MemoryMappingLayout {
@ -55,7 +58,7 @@ class MemoryMappingLayout {
// platform-specific files. // platform-specific files.
# if SANITIZER_FREEBSD || SANITIZER_LINUX # if SANITIZER_FREEBSD || SANITIZER_LINUX
ProcSelfMapsBuff proc_self_maps_; ProcSelfMapsBuff proc_self_maps_;
char *current_; const char *current_;
// Static mappings cache. // Static mappings cache.
static ProcSelfMapsBuff cached_proc_self_maps_; static ProcSelfMapsBuff cached_proc_self_maps_;
@ -84,6 +87,11 @@ void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size);
// Returns code range for the specified module. // Returns code range for the specified module.
bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end); bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end);
bool IsDecimal(char c);
uptr ParseDecimal(const char **p);
bool IsHex(char c);
uptr ParseHex(const char **p);
} // namespace __sanitizer } // namespace __sanitizer
#endif // SANITIZER_PROCMAPS_H #endif // SANITIZER_PROCMAPS_H

View 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

View 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

View File

@ -9,151 +9,20 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "sanitizer_platform.h" #include "sanitizer_platform.h"
#if SANITIZER_FREEBSD || SANITIZER_LINUX #if SANITIZER_LINUX
#include "sanitizer_common.h" #include "sanitizer_common.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h" #include "sanitizer_procmaps.h"
#if SANITIZER_FREEBSD
#include <unistd.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#endif
namespace __sanitizer { namespace __sanitizer {
// Linker initialized. void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
static void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
#if SANITIZER_FREEBSD
const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid() };
size_t Size = 0;
int Err = sysctl(Mib, 4, NULL, &Size, NULL, 0);
CHECK_EQ(Err, 0);
CHECK_GT(Size, 0);
size_t MmapedSize = Size * 4 / 3;
void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
Size = MmapedSize;
Err = sysctl(Mib, 4, VmMap, &Size, NULL, 0);
CHECK_EQ(Err, 0);
proc_maps->data = (char*)VmMap;
proc_maps->mmaped_size = MmapedSize;
proc_maps->len = Size;
#else
proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data, proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
&proc_maps->mmaped_size, 1 << 26); &proc_maps->mmaped_size, 1 << 26);
#endif
}
MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
ReadProcMaps(&proc_self_maps_);
if (cache_enabled) {
if (proc_self_maps_.mmaped_size == 0) {
LoadFromCache();
CHECK_GT(proc_self_maps_.len, 0);
}
} else {
CHECK_GT(proc_self_maps_.mmaped_size, 0);
}
Reset();
// FIXME: in the future we may want to cache the mappings on demand only.
if (cache_enabled)
CacheMemoryMappings();
}
MemoryMappingLayout::~MemoryMappingLayout() {
// Only unmap the buffer if it is different from the cached one. Otherwise
// it will be unmapped when the cache is refreshed.
if (proc_self_maps_.data != cached_proc_self_maps_.data) {
UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
}
}
void MemoryMappingLayout::Reset() {
current_ = proc_self_maps_.data;
}
// static
void MemoryMappingLayout::CacheMemoryMappings() {
SpinMutexLock l(&cache_lock_);
// Don't invalidate the cache if the mappings are unavailable.
ProcSelfMapsBuff old_proc_self_maps;
old_proc_self_maps = cached_proc_self_maps_;
ReadProcMaps(&cached_proc_self_maps_);
if (cached_proc_self_maps_.mmaped_size == 0) {
cached_proc_self_maps_ = old_proc_self_maps;
} else {
if (old_proc_self_maps.mmaped_size) {
UnmapOrDie(old_proc_self_maps.data,
old_proc_self_maps.mmaped_size);
}
}
}
void MemoryMappingLayout::LoadFromCache() {
SpinMutexLock l(&cache_lock_);
if (cached_proc_self_maps_.data) {
proc_self_maps_ = cached_proc_self_maps_;
}
}
#if !SANITIZER_FREEBSD
// Parse a hex value in str and update str.
static uptr ParseHex(char **str) {
uptr x = 0;
char *s;
for (s = *str; ; s++) {
char c = *s;
uptr v = 0;
if (c >= '0' && c <= '9')
v = c - '0';
else if (c >= 'a' && c <= 'f')
v = c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
v = c - 'A' + 10;
else
break;
x = x * 16 + v;
}
*str = s;
return x;
} }
static bool IsOneOf(char c, char c1, char c2) { static bool IsOneOf(char c, char c1, char c2) {
return c == c1 || c == c2; return c == c1 || c == c2;
} }
#endif
static bool IsDecimal(char c) {
return c >= '0' && c <= '9';
}
static bool IsHex(char c) {
return (c >= '0' && c <= '9')
|| (c >= 'a' && c <= 'f');
}
static uptr ReadHex(const char *p) {
uptr v = 0;
for (; IsHex(p[0]); p++) {
if (p[0] >= '0' && p[0] <= '9')
v = v * 16 + p[0] - '0';
else
v = v * 16 + p[0] - 'a' + 10;
}
return v;
}
static uptr ReadDecimal(const char *p) {
uptr v = 0;
for (; IsDecimal(p[0]); p++)
v = v * 10 + p[0] - '0';
return v;
}
bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset, bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
char filename[], uptr filename_size, char filename[], uptr filename_size,
@ -165,29 +34,6 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
if (!end) end = &dummy; if (!end) end = &dummy;
if (!offset) offset = &dummy; if (!offset) offset = &dummy;
if (!protection) protection = &dummy; if (!protection) protection = &dummy;
#if SANITIZER_FREEBSD
struct kinfo_vmentry *VmEntry = (struct kinfo_vmentry*)current_;
*start = (uptr)VmEntry->kve_start;
*end = (uptr)VmEntry->kve_end;
*offset = (uptr)VmEntry->kve_offset;
*protection = 0;
if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
*protection |= kProtectionRead;
if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
*protection |= kProtectionWrite;
if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
*protection |= kProtectionExecute;
if (filename != NULL && filename_size > 0) {
internal_snprintf(filename,
Min(filename_size, (uptr)PATH_MAX),
"%s", VmEntry->kve_path);
}
current_ += VmEntry->kve_structsize;
#else // !SANITIZER_FREEBSD
char *next_line = (char*)internal_memchr(current_, '\n', last - current_); char *next_line = (char*)internal_memchr(current_, '\n', last - current_);
if (next_line == 0) if (next_line == 0)
next_line = last; next_line = last;
@ -234,69 +80,9 @@ bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
if (filename && i < filename_size) if (filename && i < filename_size)
filename[i] = 0; filename[i] = 0;
current_ = next_line + 1; current_ = next_line + 1;
#endif // !SANITIZER_FREEBSD
return true; return true;
} }
uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
uptr max_modules,
string_predicate_t filter) {
Reset();
uptr cur_beg, cur_end, cur_offset;
InternalScopedBuffer<char> module_name(kMaxPathLength);
uptr n_modules = 0;
for (uptr i = 0; n_modules < max_modules &&
Next(&cur_beg, &cur_end, &cur_offset, module_name.data(),
module_name.size(), 0);
i++) {
const char *cur_name = module_name.data();
if (cur_name[0] == '\0')
continue;
if (filter && !filter(cur_name))
continue;
void *mem = &modules[n_modules];
// Don't subtract 'cur_beg' from the first entry:
// * If a binary is compiled w/o -pie, then the first entry in
// process maps is likely the binary itself (all dynamic libs
// are mapped higher in address space). For such a binary,
// instruction offset in binary coincides with the actual
// instruction address in virtual memory (as code section
// is mapped to a fixed memory range).
// * If a binary is compiled with -pie, all the modules are
// mapped high at address space (in particular, higher than
// shadow memory of the tool), so the module can't be the
// first entry.
uptr base_address = (i ? cur_beg : 0) - cur_offset;
LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address);
cur_module->addAddressRange(cur_beg, cur_end);
n_modules++;
}
return n_modules;
}
void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
char *smaps = 0;
uptr smaps_cap = 0;
uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",
&smaps, &smaps_cap, 64<<20);
uptr start = 0;
bool file = false;
const char *pos = smaps;
while (pos < smaps + smaps_len) {
if (IsHex(pos[0])) {
start = ReadHex(pos);
for (; *pos != '/' && *pos > '\n'; pos++) {}
file = *pos == '/';
} else if (internal_strncmp(pos, "Rss:", 4) == 0) {
for (; *pos < '0' || *pos > '9'; pos++) {}
uptr rss = ReadDecimal(pos) * 1024;
cb(start, rss, file, stats, stats_size);
}
while (*pos++ != '\n') {}
}
UnmapOrDie(smaps, smaps_cap);
}
} // namespace __sanitizer } // namespace __sanitizer
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX #endif // SANITIZER_LINUX

View File

@ -73,14 +73,16 @@ template<u32 kLCSegment, typename SegmentCommand>
bool MemoryMappingLayout::NextSegmentLoad( bool MemoryMappingLayout::NextSegmentLoad(
uptr *start, uptr *end, uptr *offset, uptr *start, uptr *end, uptr *offset,
char filename[], uptr filename_size, uptr *protection) { char filename[], uptr filename_size, uptr *protection) {
if (protection)
UNIMPLEMENTED();
const char* lc = current_load_cmd_addr_; const char* lc = current_load_cmd_addr_;
current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize; current_load_cmd_addr_ += ((const load_command *)lc)->cmdsize;
if (((const load_command *)lc)->cmd == kLCSegment) { if (((const load_command *)lc)->cmd == kLCSegment) {
const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_); const sptr dlloff = _dyld_get_image_vmaddr_slide(current_image_);
const SegmentCommand* sc = (const SegmentCommand *)lc; const SegmentCommand* sc = (const SegmentCommand *)lc;
if (start) *start = sc->vmaddr + dlloff; if (start) *start = sc->vmaddr + dlloff;
if (protection) {
// Return the initial protection.
*protection = sc->initprot;
}
if (end) *end = sc->vmaddr + sc->vmsize + dlloff; if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
if (offset) { if (offset) {
if (current_filetype_ == /*MH_EXECUTE*/ 0x2) { if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
@ -155,12 +157,12 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
uptr max_modules, uptr max_modules,
string_predicate_t filter) { string_predicate_t filter) {
Reset(); Reset();
uptr cur_beg, cur_end; uptr cur_beg, cur_end, prot;
InternalScopedBuffer<char> module_name(kMaxPathLength); InternalScopedBuffer<char> module_name(kMaxPathLength);
uptr n_modules = 0; uptr n_modules = 0;
for (uptr i = 0; n_modules < max_modules && for (uptr i = 0; n_modules < max_modules &&
Next(&cur_beg, &cur_end, 0, module_name.data(), Next(&cur_beg, &cur_end, 0, module_name.data(),
module_name.size(), 0); module_name.size(), &prot);
i++) { i++) {
const char *cur_name = module_name.data(); const char *cur_name = module_name.data();
if (cur_name[0] == '\0') if (cur_name[0] == '\0')
@ -176,7 +178,7 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
cur_module = new(mem) LoadedModule(cur_name, cur_beg); cur_module = new(mem) LoadedModule(cur_name, cur_beg);
n_modules++; n_modules++;
} }
cur_module->addAddressRange(cur_beg, cur_end); cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
} }
return n_modules; return n_modules;
} }

View File

@ -18,12 +18,16 @@
#include "sanitizer_common.h" #include "sanitizer_common.h"
namespace __sanitizer { namespace __sanitizer {
class AnsiColorDecorator { class SanitizerCommonDecorator {
// FIXME: This is not portable. It assumes the special strings are printed to // FIXME: This is not portable. It assumes the special strings are printed to
// stdout, which is not the case on Windows (see SetConsoleTextAttribute()). // stdout, which is not the case on Windows (see SetConsoleTextAttribute()).
public: public:
explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { } SanitizerCommonDecorator() : ansi_(ColorizeReports()) {}
const char *Bold() const { return ansi_ ? "\033[1m" : ""; } const char *Bold() const { return ansi_ ? "\033[1m" : ""; }
const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; }
const char *Warning() { return Red(); }
const char *EndWarning() { return Default(); }
protected:
const char *Black() const { return ansi_ ? "\033[1m\033[30m" : ""; } const char *Black() const { return ansi_ ? "\033[1m\033[30m" : ""; }
const char *Red() const { return ansi_ ? "\033[1m\033[31m" : ""; } const char *Red() const { return ansi_ ? "\033[1m\033[31m" : ""; }
const char *Green() const { return ansi_ ? "\033[1m\033[32m" : ""; } const char *Green() const { return ansi_ ? "\033[1m\033[32m" : ""; }
@ -32,19 +36,10 @@ class AnsiColorDecorator {
const char *Magenta() const { return ansi_ ? "\033[1m\033[35m" : ""; } const char *Magenta() const { return ansi_ ? "\033[1m\033[35m" : ""; }
const char *Cyan() const { return ansi_ ? "\033[1m\033[36m" : ""; } const char *Cyan() const { return ansi_ ? "\033[1m\033[36m" : ""; }
const char *White() const { return ansi_ ? "\033[1m\033[37m" : ""; } const char *White() const { return ansi_ ? "\033[1m\033[37m" : ""; }
const char *Default() const { return ansi_ ? "\033[1m\033[0m" : ""; }
private: private:
bool ansi_; bool ansi_;
}; };
class SanitizerCommonDecorator: protected AnsiColorDecorator {
public:
SanitizerCommonDecorator()
: __sanitizer::AnsiColorDecorator(ColorizeReports()) { }
const char *Warning() { return Red(); }
const char *EndWarning() { return Default(); }
};
} // namespace __sanitizer } // namespace __sanitizer
#endif // SANITIZER_REPORT_DECORATOR_H #endif // SANITIZER_REPORT_DECORATOR_H

View File

@ -10,193 +10,128 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "sanitizer_stackdepot.h" #include "sanitizer_stackdepot.h"
#include "sanitizer_common.h" #include "sanitizer_common.h"
#include "sanitizer_internal_defs.h" #include "sanitizer_stackdepotbase.h"
#include "sanitizer_mutex.h"
#include "sanitizer_atomic.h"
namespace __sanitizer { namespace __sanitizer {
const int kTabSize = 1024 * 1024; // Hash table size. struct StackDepotDesc {
const int kPartBits = 8; const uptr *stack;
const int kPartShift = sizeof(u32) * 8 - kPartBits - 1;
const int kPartCount = 1 << kPartBits; // Number of subparts in the table.
const int kPartSize = kTabSize / kPartCount;
const int kMaxId = 1 << kPartShift;
struct StackDesc {
StackDesc *link;
u32 id;
u32 hash;
uptr size; uptr size;
uptr stack[1]; // [size] u32 hash() const {
// murmur2
const u32 m = 0x5bd1e995;
const u32 seed = 0x9747b28c;
const u32 r = 24;
u32 h = seed ^ (size * sizeof(uptr));
for (uptr i = 0; i < size; i++) {
u32 k = stack[i];
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
}
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
bool is_valid() { return size > 0 && stack; }
}; };
static struct { struct StackDepotNode {
StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator. StackDepotNode *link;
atomic_uintptr_t region_pos; // Region allocator for StackDesc's. u32 id;
atomic_uintptr_t region_end; atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20;
atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's. uptr size;
atomic_uint32_t seq[kPartCount]; // Unique id generators. uptr stack[1]; // [size]
} depot;
static StackDepotStats stats; static const u32 kTabSizeLog = 20;
// Lower kTabSizeLog bits are equal for all items in one bucket.
// We use these bits to store the per-stack use counter.
static const u32 kUseCountBits = kTabSizeLog;
static const u32 kMaxUseCount = 1 << kUseCountBits;
static const u32 kUseCountMask = (1 << kUseCountBits) - 1;
static const u32 kHashMask = ~kUseCountMask;
typedef StackDepotDesc args_type;
bool eq(u32 hash, const args_type &args) const {
u32 hash_bits =
atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask;
if ((hash & kHashMask) != hash_bits || args.size != size) return false;
uptr i = 0;
for (; i < size; i++) {
if (stack[i] != args.stack[i]) return false;
}
return true;
}
static uptr storage_size(const args_type &args) {
return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr);
}
void store(const args_type &args, u32 hash) {
atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
size = args.size;
internal_memcpy(stack, args.stack, size * sizeof(uptr));
}
args_type load() const {
args_type ret = {&stack[0], size};
return ret;
}
StackDepotHandle get_handle() { return StackDepotHandle(this); }
typedef StackDepotHandle handle_type;
};
COMPILER_CHECK(StackDepotNode::kMaxUseCount == (u32)kStackDepotMaxUseCount);
u32 StackDepotHandle::id() { return node_->id; }
int StackDepotHandle::use_count() {
return atomic_load(&node_->hash_and_use_count, memory_order_relaxed) &
StackDepotNode::kUseCountMask;
}
void StackDepotHandle::inc_use_count_unsafe() {
u32 prev =
atomic_fetch_add(&node_->hash_and_use_count, 1, memory_order_relaxed) &
StackDepotNode::kUseCountMask;
CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount);
}
uptr StackDepotHandle::size() { return node_->size; }
uptr *StackDepotHandle::stack() { return &node_->stack[0]; }
// FIXME(dvyukov): this single reserved bit is used in TSan.
typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog>
StackDepot;
static StackDepot theDepot;
StackDepotStats *StackDepotGetStats() { StackDepotStats *StackDepotGetStats() {
return &stats; return theDepot.GetStats();
}
static u32 hash(const uptr *stack, uptr size) {
// murmur2
const u32 m = 0x5bd1e995;
const u32 seed = 0x9747b28c;
const u32 r = 24;
u32 h = seed ^ (size * sizeof(uptr));
for (uptr i = 0; i < size; i++) {
u32 k = stack[i];
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
}
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
static StackDesc *tryallocDesc(uptr memsz) {
// Optimisic lock-free allocation, essentially try to bump the region ptr.
for (;;) {
uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire);
uptr end = atomic_load(&depot.region_end, memory_order_acquire);
if (cmp == 0 || cmp + memsz > end)
return 0;
if (atomic_compare_exchange_weak(
&depot.region_pos, &cmp, cmp + memsz,
memory_order_acquire))
return (StackDesc*)cmp;
}
}
static StackDesc *allocDesc(uptr size) {
// First, try to allocate optimisitically.
uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr);
StackDesc *s = tryallocDesc(memsz);
if (s)
return s;
// If failed, lock, retry and alloc new superblock.
SpinMutexLock l(&depot.mtx);
for (;;) {
s = tryallocDesc(memsz);
if (s)
return s;
atomic_store(&depot.region_pos, 0, memory_order_relaxed);
uptr allocsz = 64 * 1024;
if (allocsz < memsz)
allocsz = memsz;
uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
stats.mapped += allocsz;
atomic_store(&depot.region_end, mem + allocsz, memory_order_release);
atomic_store(&depot.region_pos, mem, memory_order_release);
}
}
static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) {
// Searches linked list s for the stack, returns its id.
for (; s; s = s->link) {
if (s->hash == hash && s->size == size) {
uptr i = 0;
for (; i < size; i++) {
if (stack[i] != s->stack[i])
break;
}
if (i == size)
return s->id;
}
}
return 0;
}
static StackDesc *lock(atomic_uintptr_t *p) {
// Uses the pointer lsb as mutex.
for (int i = 0;; i++) {
uptr cmp = atomic_load(p, memory_order_relaxed);
if ((cmp & 1) == 0
&& atomic_compare_exchange_weak(p, &cmp, cmp | 1,
memory_order_acquire))
return (StackDesc*)cmp;
if (i < 10)
proc_yield(10);
else
internal_sched_yield();
}
}
static void unlock(atomic_uintptr_t *p, StackDesc *s) {
DCHECK_EQ((uptr)s & 1, 0);
atomic_store(p, (uptr)s, memory_order_release);
} }
u32 StackDepotPut(const uptr *stack, uptr size) { u32 StackDepotPut(const uptr *stack, uptr size) {
if (stack == 0 || size == 0) StackDepotDesc desc = {stack, size};
return 0; StackDepotHandle h = theDepot.Put(desc);
uptr h = hash(stack, size); return h.valid() ? h.id() : 0;
atomic_uintptr_t *p = &depot.tab[h % kTabSize]; }
uptr v = atomic_load(p, memory_order_consume);
StackDesc *s = (StackDesc*)(v & ~1); StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size) {
// First, try to find the existing stack. StackDepotDesc desc = {stack, size};
u32 id = find(s, stack, size, h); return theDepot.Put(desc);
if (id)
return id;
// If failed, lock, retry and insert new.
StackDesc *s2 = lock(p);
if (s2 != s) {
id = find(s2, stack, size, h);
if (id) {
unlock(p, s2);
return id;
}
}
uptr part = (h % kTabSize) / kPartSize;
id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1;
stats.n_uniq_ids++;
CHECK_LT(id, kMaxId);
id |= part << kPartShift;
CHECK_NE(id, 0);
CHECK_EQ(id & (1u << 31), 0);
s = allocDesc(size);
s->id = id;
s->hash = h;
s->size = size;
internal_memcpy(s->stack, stack, size * sizeof(uptr));
s->link = s2;
unlock(p, s);
return id;
} }
const uptr *StackDepotGet(u32 id, uptr *size) { const uptr *StackDepotGet(u32 id, uptr *size) {
if (id == 0) StackDepotDesc desc = theDepot.Get(id);
return 0; *size = desc.size;
CHECK_EQ(id & (1u << 31), 0); return desc.stack;
// High kPartBits contain part id, so we need to scan at most kPartSize lists. }
uptr part = id >> kPartShift;
for (int i = 0; i != kPartSize; i++) { void StackDepotLockAll() {
uptr idx = part * kPartSize + i; theDepot.LockAll();
CHECK_LT(idx, kTabSize); }
atomic_uintptr_t *p = &depot.tab[idx];
uptr v = atomic_load(p, memory_order_consume); void StackDepotUnlockAll() {
StackDesc *s = (StackDesc*)(v & ~1); theDepot.UnlockAll();
for (; s; s = s->link) {
if (s->id == id) {
*size = s->size;
return s->stack;
}
}
}
*size = 0;
return 0;
} }
bool StackDepotReverseMap::IdDescPair::IdComparator( bool StackDepotReverseMap::IdDescPair::IdComparator(
@ -207,10 +142,10 @@ bool StackDepotReverseMap::IdDescPair::IdComparator(
StackDepotReverseMap::StackDepotReverseMap() StackDepotReverseMap::StackDepotReverseMap()
: map_(StackDepotGetStats()->n_uniq_ids + 100) { : map_(StackDepotGetStats()->n_uniq_ids + 100) {
for (int idx = 0; idx < kTabSize; idx++) { for (int idx = 0; idx < StackDepot::kTabSize; idx++) {
atomic_uintptr_t *p = &depot.tab[idx]; atomic_uintptr_t *p = &theDepot.tab[idx];
uptr v = atomic_load(p, memory_order_consume); uptr v = atomic_load(p, memory_order_consume);
StackDesc *s = (StackDesc*)(v & ~1); StackDepotNode *s = (StackDepotNode*)(v & ~1);
for (; s; s = s->link) { for (; s; s = s->link) {
IdDescPair pair = {s->id, s}; IdDescPair pair = {s->id, s};
map_.push_back(pair); map_.push_back(pair);
@ -228,7 +163,7 @@ const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) {
*size = 0; *size = 0;
return 0; return 0;
} }
StackDesc *desc = map_[idx].desc; StackDepotNode *desc = map_[idx].desc;
*size = desc->size; *size = desc->size;
return desc->stack; return desc->stack;
} }

View File

@ -17,20 +17,29 @@
namespace __sanitizer { namespace __sanitizer {
// StackDepot efficiently stores huge amounts of stack traces. // StackDepot efficiently stores huge amounts of stack traces.
struct StackDepotNode;
struct StackDepotHandle {
StackDepotNode *node_;
StackDepotHandle() : node_(0) {}
explicit StackDepotHandle(StackDepotNode *node) : node_(node) {}
bool valid() { return node_; }
u32 id();
int use_count();
void inc_use_count_unsafe();
uptr size();
uptr *stack();
};
// Maps stack trace to an unique id. const int kStackDepotMaxUseCount = 1U << 20;
StackDepotStats *StackDepotGetStats();
u32 StackDepotPut(const uptr *stack, uptr size); u32 StackDepotPut(const uptr *stack, uptr size);
StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size);
// Retrieves a stored stack trace by the id. // Retrieves a stored stack trace by the id.
const uptr *StackDepotGet(u32 id, uptr *size); const uptr *StackDepotGet(u32 id, uptr *size);
struct StackDepotStats { void StackDepotLockAll();
uptr n_uniq_ids; void StackDepotUnlockAll();
uptr mapped;
};
StackDepotStats *StackDepotGetStats();
struct StackDesc;
// Instantiating this class creates a snapshot of StackDepot which can be // Instantiating this class creates a snapshot of StackDepot which can be
// efficiently queried with StackDepotGet(). You can use it concurrently with // efficiently queried with StackDepotGet(). You can use it concurrently with
@ -44,7 +53,7 @@ class StackDepotReverseMap {
private: private:
struct IdDescPair { struct IdDescPair {
u32 id; u32 id;
StackDesc *desc; StackDepotNode *desc;
static bool IdComparator(const IdDescPair &a, const IdDescPair &b); static bool IdComparator(const IdDescPair &a, const IdDescPair &b);
}; };
@ -55,6 +64,7 @@ class StackDepotReverseMap {
StackDepotReverseMap(const StackDepotReverseMap&); StackDepotReverseMap(const StackDepotReverseMap&);
void operator=(const StackDepotReverseMap&); void operator=(const StackDepotReverseMap&);
}; };
} // namespace __sanitizer } // namespace __sanitizer
#endif // SANITIZER_STACKDEPOT_H #endif // SANITIZER_STACKDEPOT_H

View 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

View File

@ -19,7 +19,8 @@ uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
#if defined(__arm__) #if defined(__arm__)
// Cancel Thumb bit. // Cancel Thumb bit.
pc = pc & (~1); pc = pc & (~1);
#elif defined(__powerpc__) || defined(__powerpc64__) #endif
#if defined(__powerpc__) || defined(__powerpc64__)
// PCs are always 4 byte aligned. // PCs are always 4 byte aligned.
return pc - 4; return pc - 4;
#elif defined(__sparc__) #elif defined(__sparc__)
@ -33,19 +34,39 @@ uptr StackTrace::GetCurrentPc() {
return GET_CALLER_PC(); return GET_CALLER_PC();
} }
// Check if given pointer points into allocated stack area.
static inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) {
return frame > stack_bottom && frame < stack_top - 2 * sizeof (uhwptr);
}
// In GCC on ARM bp points to saved lr, not fp, so we should check the next
// cell in stack to be a saved frame pointer. GetCanonicFrame returns the
// pointer to saved frame pointer in any case.
static inline uhwptr *GetCanonicFrame(uptr bp,
uptr stack_top,
uptr stack_bottom) {
#ifdef __arm__
if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0;
uhwptr *bp_prev = (uhwptr *)bp;
if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev;
return bp_prev - 1;
#else
return (uhwptr*)bp;
#endif
}
void StackTrace::FastUnwindStack(uptr pc, uptr bp, void StackTrace::FastUnwindStack(uptr pc, uptr bp,
uptr stack_top, uptr stack_bottom, uptr stack_top, uptr stack_bottom,
uptr max_depth) { uptr max_depth) {
CHECK_GE(max_depth, 2); CHECK_GE(max_depth, 2);
trace[0] = pc; trace[0] = pc;
size = 1; size = 1;
uhwptr *frame = (uhwptr *)bp;
uhwptr *prev_frame = frame - 1;
if (stack_top < 4096) return; // Sanity check for stack top. if (stack_top < 4096) return; // Sanity check for stack top.
uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom);
uhwptr *prev_frame = 0;
// Avoid infinite loop when frame == frame[0] by using frame > prev_frame. // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
while (frame > prev_frame && while (frame > prev_frame &&
frame < (uhwptr *)stack_top - 2 && IsValidFrame((uptr)frame, stack_top, stack_bottom) &&
frame > (uhwptr *)stack_bottom &&
IsAligned((uptr)frame, sizeof(*frame)) && IsAligned((uptr)frame, sizeof(*frame)) &&
size < max_depth) { size < max_depth) {
uhwptr pc1 = frame[1]; uhwptr pc1 = frame[1];
@ -53,7 +74,7 @@ void StackTrace::FastUnwindStack(uptr pc, uptr bp,
trace[size++] = (uptr) pc1; trace[size++] = (uptr) pc1;
} }
prev_frame = frame; prev_frame = frame;
frame = (uhwptr *)frame[0]; frame = GetCanonicFrame((uptr)frame[0], stack_top, stack_bottom);
} }
} }

View File

@ -50,9 +50,12 @@ struct StackTrace {
static bool WillUseFastUnwind(bool request_fast_unwind) { static bool WillUseFastUnwind(bool request_fast_unwind) {
// Check if fast unwind is available. Fast unwind is the only option on Mac. // Check if fast unwind is available. Fast unwind is the only option on Mac.
// It is also the only option on FreeBSD as the slow unwinding that
// leverages _Unwind_Backtrace() yields the call stack of the signal's
// handler and not of the code that raised the signal (as it does on Linux).
if (!SANITIZER_CAN_FAST_UNWIND) if (!SANITIZER_CAN_FAST_UNWIND)
return false; return false;
else if (SANITIZER_MAC) else if (SANITIZER_MAC != 0 || SANITIZER_FREEBSD != 0)
return true; return true;
return request_fast_unwind; return request_fast_unwind;
} }
@ -83,6 +86,10 @@ struct StackTrace {
uptr local_stack; \ uptr local_stack; \
uptr sp = (uptr)&local_stack uptr sp = (uptr)&local_stack
#define GET_CALLER_PC_BP \
uptr bp = GET_CURRENT_FRAME(); \
uptr pc = GET_CALLER_PC();
// Use this macro if you want to print stack trace with the current // Use this macro if you want to print stack trace with the current
// function in the top frame. // function in the top frame.
#define GET_CURRENT_PC_BP_SP \ #define GET_CURRENT_PC_BP_SP \

View File

@ -35,6 +35,14 @@ void StackTrace::PrintStack(const uptr *addr, uptr size) {
uptr pc = GetPreviousInstructionPc(addr[i]); uptr pc = GetPreviousInstructionPc(addr[i]);
uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC( uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
pc, addr_frames.data(), addr_frames.size()); pc, addr_frames.data(), addr_frames.size());
if (addr_frames_num == 0) {
frame_desc.clear();
PrintStackFramePrefix(&frame_desc, frame_num, pc);
frame_desc.append(" (<unknown module>)");
Printf("%s\n", frame_desc.data());
frame_num++;
continue;
}
for (uptr j = 0; j < addr_frames_num; j++) { for (uptr j = 0; j < addr_frames_num; j++) {
AddressInfo &info = addr_frames[j]; AddressInfo &info = addr_frames[j];
frame_desc.clear(); frame_desc.clear();

View File

@ -13,13 +13,15 @@
#include "sanitizer_allocator_internal.h" #include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h" #include "sanitizer_common.h"
#include "sanitizer_flags.h"
#include "sanitizer_libc.h" #include "sanitizer_libc.h"
#include "sanitizer_placement_new.h"
namespace __sanitizer { namespace __sanitizer {
static const char *const kTypeStrings[SuppressionTypeCount] = { static const char *const kTypeStrings[SuppressionTypeCount] = {
"none", "race", "mutex", "thread", "none", "race", "mutex", "thread", "signal",
"signal", "leak", "called_from_lib", "deadlock"}; "leak", "called_from_lib", "deadlock", "vptr_check"};
bool TemplateMatch(char *templ, const char *str) { bool TemplateMatch(char *templ, const char *str) {
if (str == 0 || str[0] == 0) if (str == 0 || str[0] == 0)
@ -63,6 +65,33 @@ bool TemplateMatch(char *templ, const char *str) {
return true; return true;
} }
ALIGNED(64) static char placeholder[sizeof(SuppressionContext)];
static SuppressionContext *suppression_ctx = 0;
SuppressionContext *SuppressionContext::Get() {
CHECK(suppression_ctx);
return suppression_ctx;
}
void SuppressionContext::InitIfNecessary() {
if (suppression_ctx)
return;
suppression_ctx = new(placeholder) SuppressionContext;
if (common_flags()->suppressions[0] == '\0')
return;
char *suppressions_from_file;
uptr buffer_size;
uptr contents_size =
ReadFileToBuffer(common_flags()->suppressions, &suppressions_from_file,
&buffer_size, 1 << 26 /* max_len */);
if (contents_size == 0) {
Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
common_flags()->suppressions);
Die();
}
suppression_ctx->Parse(suppressions_from_file);
}
bool SuppressionContext::Match(const char *str, SuppressionType type, bool SuppressionContext::Match(const char *str, SuppressionType type,
Suppression **s) { Suppression **s) {
can_parse_ = false; can_parse_ = false;

View File

@ -25,6 +25,7 @@ enum SuppressionType {
SuppressionLeak, SuppressionLeak,
SuppressionLib, SuppressionLib,
SuppressionDeadlock, SuppressionDeadlock,
SuppressionVptrCheck,
SuppressionTypeCount SuppressionTypeCount
}; };
@ -37,14 +38,21 @@ struct Suppression {
class SuppressionContext { class SuppressionContext {
public: public:
SuppressionContext() : suppressions_(1), can_parse_(true) {}
void Parse(const char *str); void Parse(const char *str);
bool Match(const char* str, SuppressionType type, Suppression **s); bool Match(const char* str, SuppressionType type, Suppression **s);
uptr SuppressionCount() const; uptr SuppressionCount() const;
const Suppression *SuppressionAt(uptr i) const; const Suppression *SuppressionAt(uptr i) const;
void GetMatched(InternalMmapVector<Suppression *> *matched); void GetMatched(InternalMmapVector<Suppression *> *matched);
// Create a SuppressionContext singleton if it hasn't been created earlier.
// Not thread safe. Must be called early during initialization (but after
// runtime flags are parsed).
static void InitIfNecessary();
// Returns a SuppressionContext singleton.
static SuppressionContext *Get();
private: private:
SuppressionContext() : suppressions_(1), can_parse_(true) {}
InternalMmapVector<Suppression> suppressions_; InternalMmapVector<Suppression> suppressions_;
bool can_parse_; bool can_parse_;

Some files were not shown because too many files have changed in this diff Show More