summaryrefslogtreecommitdiff
path: root/libsanitizer/sanitizer_common
diff options
context:
space:
mode:
authorchefmax <chefmax@138bc75d-0d04-0410-961f-82ee72b054a4>2015-10-21 07:32:45 +0000
committerchefmax <chefmax@138bc75d-0d04-0410-961f-82ee72b054a4>2015-10-21 07:32:45 +0000
commit5645a48f7ebd0f97a072b7a2eb40b27cea9d4318 (patch)
tree2bdaf703dd35e1806b59bd7d74c7eee290a1054f /libsanitizer/sanitizer_common
parent397881d34f32eddf4a6665789f1a7cdd5ff3695e (diff)
downloadgcc-5645a48f7ebd0f97a072b7a2eb40b27cea9d4318.tar.gz
libsanitizer merge from upstream r250806.
libsanitizer/ 2015-10-20 Maxim Ostapenko <m.ostapenko@partner.samsung.com> * All source files: Merge from upstream r250806. * configure.ac (link_sanitizer_common): Add -lrt flag. * configure.tgt: Enable TSAN and LSAN for aarch64-linux targets. Set CXX_ABI_NEEDED=true for darwin. * asan/Makefile.am (asan_files): Add new files. (DEFS): Add DCAN_SANITIZE_UB=0 and remove unused and legacy DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0. * asan/Makefile.in: Regenerate. * ubsan/Makefile.am (ubsan_files): Add new files. (DEFS): Add DCAN_SANITIZE_UB=1. (libubsan_la_LIBADD): Add -lc++abi if CXX_ABI_NEEDED is true. * ubsan/Makefile.in: Regenerate. * tsan/Makefile.am (tsan_files): Add new files. (DEFS): Add DCAN_SANITIZE_UB=0. * tsan/Makefile.in: Regenerate. * sanitizer_common/Makefile.am (sanitizer_common_files): Add new files. * sanitizer_common/Makefile.in: Regenerate. * asan/libtool-version: Bump the libasan SONAME. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@229111 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libsanitizer/sanitizer_common')
-rw-r--r--libsanitizer/sanitizer_common/Makefile.am4
-rw-r--r--libsanitizer/sanitizer_common/Makefile.in15
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_addrhashmap.h8
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_allocator.cc26
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_allocator.h122
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_allocator_internal.h19
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_atomic.h18
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h36
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common.cc394
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common.h328
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc667
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc19
-rwxr-xr-xlibsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc24
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc110
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc8
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc753
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc74
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_deadlock_detector1.cc29
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_deadlock_detector_interface.h6
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_flag_parser.cc169
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_flag_parser.h120
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_flags.cc307
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_flags.h74
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_flags.inc192
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_interception.h23
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_interface_internal.h56
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_internal_defs.h72
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_lfstack.h8
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_libc.cc73
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_libc.h45
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_libignore.cc46
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_libignore.h9
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_linux.cc453
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_linux.h8
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc263
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_list.h35
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_mac.cc128
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_mac.h4
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_mutex.h6
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_persistent_allocator.h5
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_platform.h41
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h86
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc3
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc112
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h131
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_posix.cc245
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_posix.h79
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc106
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_printf.cc38
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_procmaps_common.cc19
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_procmaps_linux.cc4
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cc8
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_quarantine.h24
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stackdepot.cc13
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stackdepot.h7
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h10
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stacktrace.cc23
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stacktrace.h47
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc26
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc34
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h6
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stoptheworld.h3
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc293
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_suppressions.cc156
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_suppressions.h48
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer.cc79
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer.h141
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer_internal.h149
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc85
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.h12
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cc408
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc151
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.h46
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc729
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc326
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_syscall_generic.inc4
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_syscall_linux_aarch64.inc136
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_thread_registry.h7
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cc8
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h3
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_unwind_linux_libcdep.cc (renamed from libsanitizer/sanitizer_common/sanitizer_unwind_posix_libcdep.cc)23
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_win.cc476
82 files changed, 6422 insertions, 2679 deletions
diff --git a/libsanitizer/sanitizer_common/Makefile.am b/libsanitizer/sanitizer_common/Makefile.am
index d541717866b..ee7a3f1dd2c 100644
--- a/libsanitizer/sanitizer_common/Makefile.am
+++ b/libsanitizer/sanitizer_common/Makefile.am
@@ -27,6 +27,7 @@ sanitizer_common_files = \
sanitizer_deadlock_detector1.cc \
sanitizer_deadlock_detector2.cc \
sanitizer_flags.cc \
+ sanitizer_flag_parser.cc \
sanitizer_libc.cc \
sanitizer_libignore.cc \
sanitizer_linux.cc \
@@ -45,6 +46,7 @@ sanitizer_common_files = \
sanitizer_stackdepot.cc \
sanitizer_stacktrace.cc \
sanitizer_stacktrace_libcdep.cc \
+ sanitizer_symbolizer_mac.cc \
sanitizer_stacktrace_printer.cc \
sanitizer_stoptheworld_linux_libcdep.cc \
sanitizer_suppressions.cc \
@@ -55,7 +57,7 @@ sanitizer_common_files = \
sanitizer_symbolizer_win.cc \
sanitizer_thread_registry.cc \
sanitizer_tls_get_addr.cc \
- sanitizer_unwind_posix_libcdep.cc \
+ sanitizer_unwind_linux_libcdep.cc \
sanitizer_win.cc
diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in
index 661452448b2..4b008ad7ae6 100644
--- a/libsanitizer/sanitizer_common/Makefile.in
+++ b/libsanitizer/sanitizer_common/Makefile.in
@@ -85,7 +85,8 @@ am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
sanitizer_coverage_mapping_libcdep.lo \
sanitizer_deadlock_detector1.lo \
sanitizer_deadlock_detector2.lo sanitizer_flags.lo \
- sanitizer_libc.lo sanitizer_libignore.lo sanitizer_linux.lo \
+ sanitizer_flag_parser.lo sanitizer_libc.lo \
+ sanitizer_libignore.lo sanitizer_linux.lo \
sanitizer_linux_libcdep.lo sanitizer_mac.lo \
sanitizer_persistent_allocator.lo \
sanitizer_platform_limits_linux.lo \
@@ -94,7 +95,7 @@ am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
sanitizer_procmaps_common.lo sanitizer_procmaps_freebsd.lo \
sanitizer_procmaps_linux.lo sanitizer_procmaps_mac.lo \
sanitizer_stackdepot.lo sanitizer_stacktrace.lo \
- sanitizer_stacktrace_libcdep.lo \
+ sanitizer_stacktrace_libcdep.lo sanitizer_symbolizer_mac.lo \
sanitizer_stacktrace_printer.lo \
sanitizer_stoptheworld_linux_libcdep.lo \
sanitizer_suppressions.lo sanitizer_symbolizer.lo \
@@ -102,7 +103,7 @@ am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
sanitizer_symbolizer_libcdep.lo \
sanitizer_symbolizer_posix_libcdep.lo \
sanitizer_symbolizer_win.lo sanitizer_thread_registry.lo \
- sanitizer_tls_get_addr.lo sanitizer_unwind_posix_libcdep.lo \
+ sanitizer_tls_get_addr.lo sanitizer_unwind_linux_libcdep.lo \
sanitizer_win.lo
am_libsanitizer_common_la_OBJECTS = $(am__objects_1)
libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS)
@@ -290,6 +291,7 @@ sanitizer_common_files = \
sanitizer_deadlock_detector1.cc \
sanitizer_deadlock_detector2.cc \
sanitizer_flags.cc \
+ sanitizer_flag_parser.cc \
sanitizer_libc.cc \
sanitizer_libignore.cc \
sanitizer_linux.cc \
@@ -308,6 +310,7 @@ sanitizer_common_files = \
sanitizer_stackdepot.cc \
sanitizer_stacktrace.cc \
sanitizer_stacktrace_libcdep.cc \
+ sanitizer_symbolizer_mac.cc \
sanitizer_stacktrace_printer.cc \
sanitizer_stoptheworld_linux_libcdep.cc \
sanitizer_suppressions.cc \
@@ -318,7 +321,7 @@ sanitizer_common_files = \
sanitizer_symbolizer_win.cc \
sanitizer_thread_registry.cc \
sanitizer_tls_get_addr.cc \
- sanitizer_unwind_posix_libcdep.cc \
+ sanitizer_unwind_linux_libcdep.cc \
sanitizer_win.cc
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
@@ -421,6 +424,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage_mapping_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector1.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flag_parser.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libignore.Plo@am__quote@
@@ -446,11 +450,12 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.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_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_unwind_linux_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@
.cc.o:
diff --git a/libsanitizer/sanitizer_common/sanitizer_addrhashmap.h b/libsanitizer/sanitizer_common/sanitizer_addrhashmap.h
index 9e15d51c66c..3bc40ef64f0 100644
--- a/libsanitizer/sanitizer_common/sanitizer_addrhashmap.h
+++ b/libsanitizer/sanitizer_common/sanitizer_addrhashmap.h
@@ -141,7 +141,7 @@ bool AddrHashMap<T, kSize>::Handle::created() const {
template<typename T, uptr kSize>
bool AddrHashMap<T, kSize>::Handle::exists() const {
- return cell_ != 0;
+ return cell_ != nullptr;
}
template<typename T, uptr kSize>
@@ -158,7 +158,7 @@ void AddrHashMap<T, kSize>::acquire(Handle *h) {
h->created_ = false;
h->addidx_ = -1U;
h->bucket_ = b;
- h->cell_ = 0;
+ h->cell_ = nullptr;
// If we want to remove the element, we need exclusive access to the bucket,
// so skip the lock-free phase.
@@ -248,7 +248,7 @@ void AddrHashMap<T, kSize>::acquire(Handle *h) {
}
// Store in the add cells.
- if (add == 0) {
+ if (!add) {
// Allocate a new add array.
const uptr kInitSize = 64;
add = (AddBucket*)InternalAlloc(kInitSize);
@@ -280,7 +280,7 @@ void AddrHashMap<T, kSize>::acquire(Handle *h) {
template<typename T, uptr kSize>
void AddrHashMap<T, kSize>::release(Handle *h) {
- if (h->cell_ == 0)
+ if (!h->cell_)
return;
Bucket *b = h->bucket_;
Cell *c = h->cell_;
diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.cc b/libsanitizer/sanitizer_common/sanitizer_allocator.cc
index f4e3af1b06a..b5e0bab91f1 100644
--- a/libsanitizer/sanitizer_common/sanitizer_allocator.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_allocator.cc
@@ -9,10 +9,10 @@
// run-time libraries.
// This allocator is used inside run-times.
//===----------------------------------------------------------------------===//
+
#include "sanitizer_allocator.h"
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
namespace __sanitizer {
@@ -43,7 +43,7 @@ InternalAllocator *internal_allocator() {
return 0;
}
-#else // SANITIZER_GO
+#else // SANITIZER_GO
static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];
static atomic_uint8_t internal_allocator_initialized;
@@ -59,7 +59,7 @@ InternalAllocator *internal_allocator() {
SpinMutexLock l(&internal_alloc_init_mu);
if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) ==
0) {
- internal_allocator_instance->Init();
+ internal_allocator_instance->Init(/* may_return_null*/ false);
atomic_store(&internal_allocator_initialized, 1, memory_order_release);
}
}
@@ -76,29 +76,29 @@ static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache) {
}
static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
- if (cache == 0) {
+ if (!cache) {
SpinMutexLock l(&internal_allocator_cache_mu);
return internal_allocator()->Deallocate(&internal_allocator_cache, ptr);
}
internal_allocator()->Deallocate(cache, ptr);
}
-#endif // SANITIZER_GO
+#endif // SANITIZER_GO
const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull;
void *InternalAlloc(uptr size, InternalAllocatorCache *cache) {
if (size + sizeof(u64) < size)
- return 0;
+ return nullptr;
void *p = RawInternalAlloc(size + sizeof(u64), cache);
- if (p == 0)
- return 0;
+ if (!p)
+ return nullptr;
((u64*)p)[0] = kBlockMagic;
return (char*)p + sizeof(u64);
}
void InternalFree(void *addr, InternalAllocatorCache *cache) {
- if (addr == 0)
+ if (!addr)
return;
addr = (char*)addr - sizeof(u64);
CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
@@ -138,14 +138,12 @@ bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) {
return (max / size) < n;
}
-void *AllocatorReturnNull() {
- if (common_flags()->allocator_may_return_null)
- return 0;
+void NORETURN ReportAllocatorCannotReturnNull() {
Report("%s's allocator is terminating the process instead of returning 0\n",
SanitizerToolName);
Report("If you don't like this behavior set allocator_may_return_null=1\n");
CHECK(0);
- return 0;
+ Die();
}
-} // namespace __sanitizer
+} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.h b/libsanitizer/sanitizer_common/sanitizer_allocator.h
index dd5539a2087..d98528c722c 100644
--- a/libsanitizer/sanitizer_common/sanitizer_allocator.h
+++ b/libsanitizer/sanitizer_common/sanitizer_allocator.h
@@ -21,8 +21,8 @@
namespace __sanitizer {
-// Depending on allocator_may_return_null either return 0 or crash.
-void *AllocatorReturnNull();
+// Prints error message and kills the program.
+void NORETURN ReportAllocatorCannotReturnNull();
// SizeClassMap maps allocation sizes into size classes and back.
// Class 0 corresponds to size 0.
@@ -209,6 +209,7 @@ class AllocatorStats {
void Init() {
internal_memset(this, 0, sizeof(*this));
}
+ void InitLinkerInitialized() {}
void Add(AllocatorStat i, uptr v) {
v += atomic_load(&stats_[i], memory_order_relaxed);
@@ -238,11 +239,14 @@ class AllocatorStats {
// Global stats, used for aggregation and querying.
class AllocatorGlobalStats : public AllocatorStats {
public:
- void Init() {
- internal_memset(this, 0, sizeof(*this));
+ void InitLinkerInitialized() {
next_ = this;
prev_ = this;
}
+ void Init() {
+ internal_memset(this, 0, sizeof(*this));
+ InitLinkerInitialized();
+ }
void Register(AllocatorStats *s) {
SpinMutexLock l(&mu_);
@@ -317,7 +321,7 @@ class SizeClassAllocator64 {
void Init() {
CHECK_EQ(kSpaceBeg,
- reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize)));
+ reinterpret_cast<uptr>(MmapNoAccess(kSpaceBeg, kSpaceSize)));
MapWithCallback(kSpaceEnd, AdditionalSize());
}
@@ -341,7 +345,7 @@ class SizeClassAllocator64 {
CHECK_LT(class_id, kNumClasses);
RegionInfo *region = GetRegionInfo(class_id);
Batch *b = region->free_list.Pop();
- if (b == 0)
+ if (!b)
b = PopulateFreeList(stat, c, class_id, region);
region->n_allocated += b->count;
return b;
@@ -365,16 +369,16 @@ class SizeClassAllocator64 {
void *GetBlockBegin(const void *p) {
uptr class_id = GetSizeClass(p);
uptr size = SizeClassMap::Size(class_id);
- if (!size) return 0;
+ if (!size) return nullptr;
uptr chunk_idx = GetChunkIdx((uptr)p, size);
uptr reg_beg = (uptr)p & ~(kRegionSize - 1);
uptr beg = chunk_idx * size;
uptr next_beg = beg + size;
- if (class_id >= kNumClasses) return 0;
+ if (class_id >= kNumClasses) return nullptr;
RegionInfo *region = GetRegionInfo(class_id);
if (region->mapped_user >= next_beg)
return reinterpret_cast<void*>(reg_beg + beg);
- return 0;
+ return nullptr;
}
static uptr GetActuallyAllocatedSize(void *p) {
@@ -603,6 +607,7 @@ class TwoLevelByteMap {
internal_memset(map1_, 0, sizeof(map1_));
mu_.Init();
}
+
void TestOnlyUnmap() {
for (uptr i = 0; i < kSize1; i++) {
u8 *p = Get(i);
@@ -816,6 +821,10 @@ class SizeClassAllocator32 {
void PrintStats() {
}
+ static uptr AdditionalSize() {
+ return 0;
+ }
+
typedef SizeClassMap SizeClassMapT;
static const uptr kNumClasses = SizeClassMap::kNumClasses;
@@ -862,9 +871,9 @@ class SizeClassAllocator32 {
uptr reg = AllocateRegion(stat, class_id);
uptr n_chunks = kRegionSize / (size + kMetadataSize);
uptr max_count = SizeClassMap::MaxCached(class_id);
- Batch *b = 0;
+ Batch *b = nullptr;
for (uptr i = reg; i < reg + n_chunks * size; i += size) {
- if (b == 0) {
+ if (!b) {
if (SizeClassMap::SizeClassRequiresSeparateTransferBatch(class_id))
b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch)));
else
@@ -875,7 +884,7 @@ class SizeClassAllocator32 {
if (b->count == max_count) {
CHECK_GT(b->count, 0);
sci->free_list.push_back(b);
- b = 0;
+ b = nullptr;
}
}
if (b) {
@@ -1000,9 +1009,14 @@ struct SizeClassAllocatorLocalCache {
template <class MapUnmapCallback = NoOpMapUnmapCallback>
class LargeMmapAllocator {
public:
- void Init() {
- internal_memset(this, 0, sizeof(*this));
+ void InitLinkerInitialized(bool may_return_null) {
page_size_ = GetPageSizeCached();
+ atomic_store(&may_return_null_, may_return_null, memory_order_relaxed);
+ }
+
+ void Init(bool may_return_null) {
+ internal_memset(this, 0, sizeof(*this));
+ InitLinkerInitialized(may_return_null);
}
void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) {
@@ -1010,7 +1024,9 @@ class LargeMmapAllocator {
uptr map_size = RoundUpMapSize(size);
if (alignment > page_size_)
map_size += alignment;
- if (map_size < size) return AllocatorReturnNull(); // Overflow.
+ // Overflow.
+ if (map_size < size)
+ return ReturnNullOrDie();
uptr map_beg = reinterpret_cast<uptr>(
MmapOrDie(map_size, "LargeMmapAllocator"));
CHECK(IsAligned(map_beg, page_size_));
@@ -1046,6 +1062,16 @@ class LargeMmapAllocator {
return reinterpret_cast<void*>(res);
}
+ void *ReturnNullOrDie() {
+ if (atomic_load(&may_return_null_, memory_order_acquire))
+ return nullptr;
+ ReportAllocatorCannotReturnNull();
+ }
+
+ void SetMayReturnNull(bool may_return_null) {
+ atomic_store(&may_return_null_, may_return_null, memory_order_release);
+ }
+
void Deallocate(AllocatorStats *stat, void *p) {
Header *h = GetHeader(p);
{
@@ -1078,7 +1104,7 @@ class LargeMmapAllocator {
}
bool PointerIsMine(const void *p) {
- return GetBlockBegin(p) != 0;
+ return GetBlockBegin(p) != nullptr;
}
uptr GetActuallyAllocatedSize(void *p) {
@@ -1107,13 +1133,13 @@ class LargeMmapAllocator {
nearest_chunk = ch;
}
if (!nearest_chunk)
- return 0;
+ return nullptr;
Header *h = reinterpret_cast<Header *>(nearest_chunk);
CHECK_GE(nearest_chunk, h->map_beg);
CHECK_LT(nearest_chunk, h->map_beg + h->map_size);
CHECK_LE(nearest_chunk, p);
if (h->map_beg + h->map_size <= p)
- return 0;
+ return nullptr;
return GetUser(h);
}
@@ -1123,7 +1149,7 @@ class LargeMmapAllocator {
mutex_.CheckLocked();
uptr p = reinterpret_cast<uptr>(ptr);
uptr n = n_chunks_;
- if (!n) return 0;
+ if (!n) return nullptr;
if (!chunks_sorted_) {
// Do one-time sort. chunks_sorted_ is reset in Allocate/Deallocate.
SortArray(reinterpret_cast<uptr*>(chunks_), n);
@@ -1135,7 +1161,7 @@ class LargeMmapAllocator {
chunks_[n - 1]->map_size;
}
if (p < min_mmap_ || p >= max_mmap_)
- return 0;
+ return nullptr;
uptr beg = 0, end = n - 1;
// This loop is a log(n) lower_bound. It does not check for the exact match
// to avoid expensive cache-thrashing loads.
@@ -1156,7 +1182,7 @@ class LargeMmapAllocator {
Header *h = chunks_[beg];
if (h->map_beg + h->map_size <= p || p < h->map_beg)
- return 0;
+ return nullptr;
return GetUser(h);
}
@@ -1224,6 +1250,7 @@ class LargeMmapAllocator {
struct Stats {
uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64];
} stats;
+ atomic_uint8_t may_return_null_;
SpinMutex mutex_;
};
@@ -1237,19 +1264,32 @@ template <class PrimaryAllocator, class AllocatorCache,
class SecondaryAllocator> // NOLINT
class CombinedAllocator {
public:
- void Init() {
+ void InitCommon(bool may_return_null) {
primary_.Init();
- secondary_.Init();
+ atomic_store(&may_return_null_, may_return_null, memory_order_relaxed);
+ }
+
+ void InitLinkerInitialized(bool may_return_null) {
+ secondary_.InitLinkerInitialized(may_return_null);
+ stats_.InitLinkerInitialized();
+ InitCommon(may_return_null);
+ }
+
+ void Init(bool may_return_null) {
+ secondary_.Init(may_return_null);
stats_.Init();
+ InitCommon(may_return_null);
}
void *Allocate(AllocatorCache *cache, uptr size, uptr alignment,
- bool cleared = false) {
+ bool cleared = false, bool check_rss_limit = false) {
// Returning 0 on malloc(0) may break a lot of code.
if (size == 0)
size = 1;
if (size + alignment < size)
- return AllocatorReturnNull();
+ return ReturnNullOrDie();
+ if (check_rss_limit && RssLimitIsExceeded())
+ return ReturnNullOrDie();
if (alignment > 8)
size = RoundUpTo(size, alignment);
void *res;
@@ -1265,6 +1305,30 @@ class CombinedAllocator {
return res;
}
+ bool MayReturnNull() const {
+ return atomic_load(&may_return_null_, memory_order_acquire);
+ }
+
+ void *ReturnNullOrDie() {
+ if (MayReturnNull())
+ return nullptr;
+ ReportAllocatorCannotReturnNull();
+ }
+
+ void SetMayReturnNull(bool may_return_null) {
+ secondary_.SetMayReturnNull(may_return_null);
+ atomic_store(&may_return_null_, may_return_null, memory_order_release);
+ }
+
+ bool RssLimitIsExceeded() {
+ return atomic_load(&rss_limit_is_exceeded_, memory_order_acquire);
+ }
+
+ void SetRssLimitIsExceeded(bool rss_limit_is_exceeded) {
+ atomic_store(&rss_limit_is_exceeded_, rss_limit_is_exceeded,
+ memory_order_release);
+ }
+
void Deallocate(AllocatorCache *cache, void *p) {
if (!p) return;
if (primary_.PointerIsMine(p))
@@ -1279,7 +1343,7 @@ class CombinedAllocator {
return Allocate(cache, new_size, alignment);
if (!new_size) {
Deallocate(cache, p);
- return 0;
+ return nullptr;
}
CHECK(PointerIsMine(p));
uptr old_size = GetActuallyAllocatedSize(p);
@@ -1377,11 +1441,13 @@ class CombinedAllocator {
PrimaryAllocator primary_;
SecondaryAllocator secondary_;
AllocatorGlobalStats stats_;
+ atomic_uint8_t may_return_null_;
+ atomic_uint8_t rss_limit_is_exceeded_;
};
// Returns true if calloc(size, n) should return 0 due to overflow in size*n.
bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n);
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_ALLOCATOR_H
+#endif // SANITIZER_ALLOCATOR_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h b/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h
index 5e8cc107706..99d8516d8cf 100644
--- a/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h
+++ b/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h
@@ -1,4 +1,4 @@
-//===-- sanitizer_allocator_internal.h -------------------------- C++ -----===//
+//===-- sanitizer_allocator_internal.h --------------------------*- C++ -*-===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
@@ -43,10 +43,19 @@ typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator>
typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache,
LargeMmapAllocator<> > InternalAllocator;
-void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0);
-void InternalFree(void *p, InternalAllocatorCache *cache = 0);
+void *InternalAlloc(uptr size, InternalAllocatorCache *cache = nullptr);
+void InternalFree(void *p, InternalAllocatorCache *cache = nullptr);
InternalAllocator *internal_allocator();
-} // namespace __sanitizer
+enum InternalAllocEnum {
+ INTERNAL_ALLOC
+};
-#endif // SANITIZER_ALLOCATOR_INTERNAL_H
+} // namespace __sanitizer
+
+inline void *operator new(__sanitizer::operator_new_size_type size,
+ InternalAllocEnum) {
+ return InternalAlloc(size);
+}
+
+#endif // SANITIZER_ALLOCATOR_INTERNAL_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic.h b/libsanitizer/sanitizer_common/sanitizer_atomic.h
index 6387664f63b..4973b7d4e88 100644
--- a/libsanitizer/sanitizer_common/sanitizer_atomic.h
+++ b/libsanitizer/sanitizer_common/sanitizer_atomic.h
@@ -53,7 +53,7 @@ struct atomic_uintptr_t {
} // namespace __sanitizer
-#if defined(__GNUC__)
+#if defined(__clang__) || defined(__GNUC__)
# include "sanitizer_atomic_clang.h"
#elif defined(_MSC_VER)
# include "sanitizer_atomic_msvc.h"
@@ -61,4 +61,20 @@ struct atomic_uintptr_t {
# error "Unsupported compiler"
#endif
+namespace __sanitizer {
+
+// Clutter-reducing helpers.
+
+template<typename T>
+INLINE typename T::Type atomic_load_relaxed(const volatile T *a) {
+ return atomic_load(a, memory_order_relaxed);
+}
+
+template<typename T>
+INLINE void atomic_store_relaxed(volatile T *a, typename T::Type v) {
+ atomic_store(a, v, memory_order_relaxed);
+}
+
+} // namespace __sanitizer
+
#endif // SANITIZER_ATOMIC_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h
index 1198dec3e97..4ac3b90769f 100644
--- a/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h
+++ b/libsanitizer/sanitizer_common/sanitizer_atomic_msvc.h
@@ -19,6 +19,15 @@ extern "C" void _mm_mfence();
#pragma intrinsic(_mm_mfence)
extern "C" void _mm_pause();
#pragma intrinsic(_mm_pause)
+extern "C" char _InterlockedExchange8( // NOLINT
+ char volatile *Addend, char Value); // NOLINT
+#pragma intrinsic(_InterlockedExchange8)
+extern "C" short _InterlockedExchange16( // NOLINT
+ short volatile *Addend, short Value); // NOLINT
+#pragma intrinsic(_InterlockedExchange16)
+extern "C" long _InterlockedExchange( // NOLINT
+ long volatile *Addend, long Value); // NOLINT
+#pragma intrinsic(_InterlockedExchange)
extern "C" long _InterlockedExchangeAdd( // NOLINT
long volatile * Addend, long Value); // NOLINT
#pragma intrinsic(_InterlockedExchangeAdd)
@@ -143,28 +152,25 @@ INLINE u8 atomic_exchange(volatile atomic_uint8_t *a,
u8 v, memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
- __asm {
- mov eax, a
- mov cl, v
- xchg [eax], cl // NOLINT
- mov v, cl
- }
- return v;
+ return (u8)_InterlockedExchange8((volatile char*)&a->val_dont_use, v);
}
INLINE u16 atomic_exchange(volatile atomic_uint16_t *a,
u16 v, memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
- __asm {
- mov eax, a
- mov cx, v
- xchg [eax], cx // NOLINT
- mov v, cx
- }
- return v;
+ return (u16)_InterlockedExchange16((volatile short*)&a->val_dont_use, v);
+}
+
+INLINE u32 atomic_exchange(volatile atomic_uint32_t *a,
+ u32 v, memory_order mo) {
+ (void)mo;
+ DCHECK(!((uptr)a % sizeof(*a)));
+ return (u32)_InterlockedExchange((volatile long*)&a->val_dont_use, v);
}
+#ifndef _WIN64
+
INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a,
u8 *cmp,
u8 xchgv,
@@ -186,6 +192,8 @@ INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a,
return false;
}
+#endif
+
INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a,
uptr *cmp,
uptr xchg,
diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc
index f06efffb325..a7b5efb6bb4 100644
--- a/libsanitizer/sanitizer_common/sanitizer_common.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_common.cc
@@ -10,13 +10,19 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_common.h"
+#include "sanitizer_allocator_internal.h"
#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_symbolizer.h"
namespace __sanitizer {
const char *SanitizerToolName = "SanitizerTool";
+atomic_uint32_t current_verbosity;
+
uptr GetPageSizeCached() {
static uptr PageSize;
if (!PageSize)
@@ -24,19 +30,71 @@ uptr GetPageSizeCached() {
return PageSize;
}
+StaticSpinMutex report_file_mu;
+ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
-// By default, dump to stderr. If |log_to_file| is true and |report_fd_pid|
-// isn't equal to the current PID, try to obtain file descriptor by opening
-// file "report_path_prefix.<PID>".
-fd_t report_fd = kStderrFd;
+void RawWrite(const char *buffer) {
+ report_file.Write(buffer, internal_strlen(buffer));
+}
-// Set via __sanitizer_set_report_path.
-bool log_to_file = false;
-char report_path_prefix[sizeof(report_path_prefix)];
+void ReportFile::ReopenIfNecessary() {
+ mu->CheckLocked();
+ if (fd == kStdoutFd || fd == kStderrFd) return;
+
+ uptr pid = internal_getpid();
+ // If in tracer, use the parent's file.
+ if (pid == stoptheworld_tracer_pid)
+ pid = stoptheworld_tracer_ppid;
+ if (fd != kInvalidFd) {
+ // If the report file is already opened by the current process,
+ // do nothing. Otherwise the report file was opened by the parent
+ // process, close it now.
+ if (fd_pid == pid)
+ return;
+ else
+ CloseFile(fd);
+ }
-// PID of process that opened |report_fd|. If a fork() occurs, the PID of the
-// child thread will be different from |report_fd_pid|.
-uptr report_fd_pid = 0;
+ const char *exe_name = GetProcessName();
+ if (common_flags()->log_exe_name && exe_name) {
+ internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
+ exe_name, pid);
+ } else {
+ internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
+ }
+ fd = OpenFile(full_path, WrOnly);
+ if (fd == kInvalidFd) {
+ const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
+ WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
+ WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
+ Die();
+ }
+ fd_pid = pid;
+}
+
+void ReportFile::SetReportPath(const char *path) {
+ if (!path)
+ return;
+ uptr len = internal_strlen(path);
+ if (len > sizeof(path_prefix) - 100) {
+ Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
+ path[0], path[1], path[2], path[3],
+ path[4], path[5], path[6], path[7]);
+ Die();
+ }
+
+ SpinMutexLock l(mu);
+ if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
+ CloseFile(fd);
+ fd = kInvalidFd;
+ if (internal_strcmp(path, "stdout") == 0) {
+ fd = kStdoutFd;
+ } else if (internal_strcmp(path, "stderr") == 0) {
+ fd = kStderrFd;
+ } else {
+ internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
+ }
+}
// PID of the tracer task in StopTheWorld. It shares the address space with the
// main process, but has a different PID and thus requires special handling.
@@ -45,20 +103,47 @@ uptr stoptheworld_tracer_pid = 0;
// writing to the same log file.
uptr stoptheworld_tracer_ppid = 0;
-static DieCallbackType DieCallback;
-void SetDieCallback(DieCallbackType callback) {
- DieCallback = callback;
+static const int kMaxNumOfInternalDieCallbacks = 5;
+static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];
+
+bool AddDieCallback(DieCallbackType callback) {
+ for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
+ if (InternalDieCallbacks[i] == nullptr) {
+ InternalDieCallbacks[i] = callback;
+ return true;
+ }
+ }
+ return false;
}
-DieCallbackType GetDieCallback() {
- return DieCallback;
+bool RemoveDieCallback(DieCallbackType callback) {
+ for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
+ if (InternalDieCallbacks[i] == callback) {
+ internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
+ sizeof(InternalDieCallbacks[0]) *
+ (kMaxNumOfInternalDieCallbacks - i - 1));
+ InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
+ return true;
+ }
+ }
+ return false;
+}
+
+static DieCallbackType UserDieCallback;
+void SetUserDieCallback(DieCallbackType callback) {
+ UserDieCallback = callback;
}
void NORETURN Die() {
- if (DieCallback) {
- DieCallback();
+ if (UserDieCallback)
+ UserDieCallback();
+ for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
+ if (InternalDieCallbacks[i])
+ InternalDieCallbacks[i]();
}
- internal__exit(1);
+ if (common_flags()->abort_on_error)
+ Abort();
+ internal__exit(common_flags()->exitcode);
}
static CheckFailedCallbackType CheckFailedCallback;
@@ -76,37 +161,57 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
Die();
}
-uptr ReadFileToBuffer(const char *file_name, char **buff,
- uptr *buff_size, uptr max_len) {
+void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
+ error_t err) {
+ static int recursion_count;
+ if (recursion_count) {
+ // The Report() and CHECK calls below may call mmap recursively and fail.
+ // If we went into recursion, just die.
+ RawWrite("ERROR: Failed to mmap\n");
+ Die();
+ }
+ recursion_count++;
+ Report("ERROR: %s failed to "
+ "allocate 0x%zx (%zd) bytes of %s (error code: %d)\n",
+ SanitizerToolName, size, size, mem_type, err);
+ DumpProcessMap();
+ UNREACHABLE("unable to mmap");
+}
+
+bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
+ uptr *read_len, uptr max_len, error_t *errno_p) {
uptr PageSize = GetPageSizeCached();
uptr kMinFileLen = PageSize;
- uptr read_len = 0;
- *buff = 0;
+ *buff = nullptr;
*buff_size = 0;
+ *read_len = 0;
// The files we usually open are not seekable, so try different buffer sizes.
for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
- uptr openrv = OpenFile(file_name, /*write*/ false);
- if (internal_iserror(openrv)) return 0;
- fd_t fd = openrv;
+ fd_t fd = OpenFile(file_name, RdOnly, errno_p);
+ if (fd == kInvalidFd) return false;
UnmapOrDie(*buff, *buff_size);
*buff = (char*)MmapOrDie(size, __func__);
*buff_size = size;
+ *read_len = 0;
// Read up to one page at a time.
- read_len = 0;
bool reached_eof = false;
- while (read_len + PageSize <= size) {
- uptr just_read = internal_read(fd, *buff + read_len, PageSize);
+ while (*read_len + PageSize <= size) {
+ uptr just_read;
+ if (!ReadFromFile(fd, *buff + *read_len, PageSize, &just_read, errno_p)) {
+ UnmapOrDie(*buff, *buff_size);
+ return false;
+ }
if (just_read == 0) {
reached_eof = true;
break;
}
- read_len += just_read;
+ *read_len += just_read;
}
- internal_close(fd);
+ CloseFile(fd);
if (reached_eof) // We've read the whole file.
break;
}
- return read_len;
+ return true;
}
typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
@@ -143,62 +248,77 @@ void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {
const char *StripPathPrefix(const char *filepath,
const char *strip_path_prefix) {
- if (filepath == 0) return 0;
- if (strip_path_prefix == 0) return filepath;
- const char *pos = internal_strstr(filepath, strip_path_prefix);
- if (pos == 0) return filepath;
- pos += internal_strlen(strip_path_prefix);
- if (pos[0] == '.' && pos[1] == '/')
- pos += 2;
- return pos;
+ if (!filepath) return nullptr;
+ if (!strip_path_prefix) return filepath;
+ const char *res = filepath;
+ if (const char *pos = internal_strstr(filepath, strip_path_prefix))
+ res = pos + internal_strlen(strip_path_prefix);
+ if (res[0] == '.' && res[1] == '/')
+ res += 2;
+ return res;
}
const char *StripModuleName(const char *module) {
- if (module == 0)
- return 0;
- if (const char *slash_pos = internal_strrchr(module, '/'))
+ if (!module)
+ return nullptr;
+ if (SANITIZER_WINDOWS) {
+ // On Windows, both slash and backslash are possible.
+ // Pick the one that goes last.
+ if (const char *bslash_pos = internal_strrchr(module, '\\'))
+ return StripModuleName(bslash_pos + 1);
+ }
+ if (const char *slash_pos = internal_strrchr(module, '/')) {
return slash_pos + 1;
+ }
return module;
}
void ReportErrorSummary(const char *error_message) {
if (!common_flags()->print_summary)
return;
- InternalScopedBuffer<char> buff(kMaxSummaryLength);
- internal_snprintf(buff.data(), buff.size(),
- "SUMMARY: %s: %s", SanitizerToolName, error_message);
+ InternalScopedString buff(kMaxSummaryLength);
+ buff.append("SUMMARY: %s: %s", SanitizerToolName, error_message);
__sanitizer_report_error_summary(buff.data());
}
-void ReportErrorSummary(const char *error_type, const char *file,
- int line, const char *function) {
+#ifndef SANITIZER_GO
+void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
if (!common_flags()->print_summary)
return;
- InternalScopedBuffer<char> buff(kMaxSummaryLength);
- internal_snprintf(
- buff.data(), buff.size(), "%s %s:%d %s", error_type,
- file ? StripPathPrefix(file, common_flags()->strip_path_prefix) : "??",
- line, function ? function : "??");
+ InternalScopedString buff(kMaxSummaryLength);
+ buff.append("%s ", error_type);
+ RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
ReportErrorSummary(buff.data());
}
+#endif
-LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
+void LoadedModule::set(const char *module_name, uptr base_address) {
+ clear();
full_name_ = internal_strdup(module_name);
base_address_ = base_address;
- n_ranges_ = 0;
+}
+
+void LoadedModule::clear() {
+ InternalFree(full_name_);
+ full_name_ = nullptr;
+ while (!ranges_.empty()) {
+ AddressRange *r = ranges_.front();
+ ranges_.pop_front();
+ InternalFree(r);
+ }
}
void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
- CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges);
- ranges_[n_ranges_].beg = beg;
- ranges_[n_ranges_].end = end;
- exec_[n_ranges_] = executable;
- n_ranges_++;
+ void *mem = InternalAlloc(sizeof(AddressRange));
+ AddressRange *r = new(mem) AddressRange(beg, end, executable);
+ ranges_.push_back(r);
}
bool LoadedModule::containsAddress(uptr address) const {
- for (uptr i = 0; i < n_ranges_; i++) {
- if (ranges_[i].beg <= address && address < ranges_[i].end)
+ for (Iterator iter = ranges(); iter.hasNext();) {
+ const AddressRange *r = iter.next();
+ if (r->beg <= address && address < r->end)
return true;
}
return false;
@@ -210,12 +330,9 @@ void IncreaseTotalMmap(uptr size) {
if (!common_flags()->mmap_limit_mb) return;
uptr total_mmaped =
atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
- if ((total_mmaped >> 20) > common_flags()->mmap_limit_mb) {
- // Since for now mmap_limit_mb is not a user-facing flag, just CHECK.
- uptr mmap_limit_mb = common_flags()->mmap_limit_mb;
- common_flags()->mmap_limit_mb = 0; // Allow mmap in CHECK.
- RAW_CHECK(total_mmaped >> 20 < mmap_limit_mb);
- }
+ // Since for now mmap_limit_mb is not a user-facing flag, just kill
+ // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
+ RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
}
void DecreaseTotalMmap(uptr size) {
@@ -223,39 +340,130 @@ void DecreaseTotalMmap(uptr size) {
atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
}
-} // namespace __sanitizer
+bool TemplateMatch(const char *templ, const char *str) {
+ if ((!str) || str[0] == 0)
+ return false;
+ bool start = false;
+ if (templ && templ[0] == '^') {
+ start = true;
+ templ++;
+ }
+ bool asterisk = false;
+ while (templ && templ[0]) {
+ if (templ[0] == '*') {
+ templ++;
+ start = false;
+ asterisk = true;
+ continue;
+ }
+ if (templ[0] == '$')
+ return str[0] == 0 || asterisk;
+ if (str[0] == 0)
+ return false;
+ char *tpos = (char*)internal_strchr(templ, '*');
+ char *tpos1 = (char*)internal_strchr(templ, '$');
+ if ((!tpos) || (tpos1 && tpos1 < tpos))
+ tpos = tpos1;
+ if (tpos)
+ tpos[0] = 0;
+ const char *str0 = str;
+ const char *spos = internal_strstr(str, templ);
+ str = spos + internal_strlen(templ);
+ templ = tpos;
+ if (tpos)
+ tpos[0] = tpos == tpos1 ? '$' : '*';
+ if (!spos)
+ return false;
+ if (start && spos != str0)
+ return false;
+ start = false;
+ asterisk = false;
+ }
+ return true;
+}
-using namespace __sanitizer; // NOLINT
+static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
-extern "C" {
-void __sanitizer_set_report_path(const char *path) {
+char *FindPathToBinary(const char *name) {
+ const char *path = GetEnv("PATH");
if (!path)
- return;
- uptr len = internal_strlen(path);
- if (len > sizeof(report_path_prefix) - 100) {
- Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
- path[0], path[1], path[2], path[3],
- path[4], path[5], path[6], path[7]);
- Die();
+ return nullptr;
+ uptr name_len = internal_strlen(name);
+ InternalScopedBuffer<char> buffer(kMaxPathLength);
+ const char *beg = path;
+ while (true) {
+ const char *end = internal_strchrnul(beg, kPathSeparator);
+ uptr prefix_len = end - beg;
+ if (prefix_len + name_len + 2 <= kMaxPathLength) {
+ internal_memcpy(buffer.data(), beg, prefix_len);
+ buffer[prefix_len] = '/';
+ internal_memcpy(&buffer[prefix_len + 1], name, name_len);
+ buffer[prefix_len + 1 + name_len] = '\0';
+ if (FileExists(buffer.data()))
+ return internal_strdup(buffer.data());
+ }
+ if (*end == '\0') break;
+ beg = end + 1;
}
- if (report_fd != kStdoutFd &&
- report_fd != kStderrFd &&
- report_fd != kInvalidFd)
- internal_close(report_fd);
- report_fd = kInvalidFd;
- log_to_file = false;
- if (internal_strcmp(path, "stdout") == 0) {
- report_fd = kStdoutFd;
- } else if (internal_strcmp(path, "stderr") == 0) {
- report_fd = kStderrFd;
- } else {
- internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix));
- report_path_prefix[len] = '\0';
- log_to_file = true;
+ return nullptr;
+}
+
+static char binary_name_cache_str[kMaxPathLength];
+static char process_name_cache_str[kMaxPathLength];
+
+const char *GetProcessName() {
+ return process_name_cache_str;
+}
+
+static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
+ ReadLongProcessName(buf, buf_len);
+ char *s = const_cast<char *>(StripModuleName(buf));
+ uptr len = internal_strlen(s);
+ if (s != buf) {
+ internal_memmove(buf, s, len);
+ buf[len] = '\0';
}
+ return len;
+}
+
+void UpdateProcessName() {
+ ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
+}
+
+// Call once to make sure that binary_name_cache_str is initialized
+void CacheBinaryName() {
+ if (binary_name_cache_str[0] != '\0')
+ return;
+ ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
+ ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
+}
+
+uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
+ CacheBinaryName();
+ uptr name_len = internal_strlen(binary_name_cache_str);
+ name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
+ if (buf_len == 0)
+ return 0;
+ internal_memcpy(buf, binary_name_cache_str, name_len);
+ buf[name_len] = '\0';
+ return name_len;
+}
+
+} // namespace __sanitizer
+
+using namespace __sanitizer; // NOLINT
+
+extern "C" {
+void __sanitizer_set_report_path(const char *path) {
+ report_file.SetReportPath(path);
}
void __sanitizer_report_error_summary(const char *error_summary) {
Printf("%s\n", error_summary);
}
-} // extern "C"
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_set_death_callback(void (*callback)(void)) {
+ SetUserDieCallback(callback);
+}
+} // extern "C"
diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h
index a8924913872..637e229742d 100644
--- a/libsanitizer/sanitizer_common/sanitizer_common.h
+++ b/libsanitizer/sanitizer_common/sanitizer_common.h
@@ -5,8 +5,8 @@
//
//===----------------------------------------------------------------------===//
//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
+// This file is shared between run-time libraries of sanitizers.
+//
// It declares common functions and classes that are used in both runtimes.
// Implementation of some functions are provided in sanitizer_common, while
// others must be defined by run-time library itself.
@@ -14,13 +14,21 @@
#ifndef SANITIZER_COMMON_H
#define SANITIZER_COMMON_H
+#include "sanitizer_flags.h"
+#include "sanitizer_interface_internal.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
+#include "sanitizer_list.h"
#include "sanitizer_mutex.h"
-#include "sanitizer_flags.h"
+
+#ifdef _MSC_VER
+extern "C" void _ReadWriteBarrier();
+#pragma intrinsic(_ReadWriteBarrier)
+#endif
namespace __sanitizer {
struct StackTrace;
+struct AddressInfo;
// Constants.
const uptr kWordSize = SANITIZER_WORDSIZE / 8;
@@ -32,12 +40,27 @@ const uptr kWordSizeInBits = 8 * kWordSize;
const uptr kCacheLineSize = 64;
#endif
-const uptr kMaxPathLength = 512;
+const uptr kMaxPathLength = 4096;
+
+// 16K loaded modules should be enough for everyone.
+static const uptr kMaxNumberOfModules = 1 << 14;
const uptr kMaxThreadStackSize = 1 << 30; // 1Gb
+// Denotes fake PC values that come from JIT/JAVA/etc.
+// For such PC values __tsan_symbolize_external() will be called.
+const u64 kExternalPCBit = 1ULL << 60;
+
extern const char *SanitizerToolName; // Can be changed by the tool.
+extern atomic_uint32_t current_verbosity;
+INLINE void SetVerbosity(int verbosity) {
+ atomic_store(&current_verbosity, verbosity, memory_order_relaxed);
+}
+INLINE int Verbosity() {
+ return atomic_load(&current_verbosity, memory_order_relaxed);
+}
+
uptr GetPageSize();
uptr GetPageSizeCached();
uptr GetMmapGranularity();
@@ -53,17 +76,27 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
// Memory management
void *MmapOrDie(uptr size, const char *mem_type);
void UnmapOrDie(void *addr, uptr size);
-void *MmapFixedNoReserve(uptr fixed_addr, uptr size);
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size,
+ const char *name = nullptr);
void *MmapNoReserveOrDie(uptr size, const char *mem_type);
void *MmapFixedOrDie(uptr fixed_addr, uptr size);
-void *Mprotect(uptr fixed_addr, uptr size);
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr);
// Map aligned chunk of address space; size and alignment are powers of two.
void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type);
+// Disallow access to a memory range. Use MmapNoAccess to allocate an
+// unaccessible memory.
+bool MprotectNoAccess(uptr addr, uptr size);
+
// Used to check if we can map shadow memory to a fixed location.
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end);
void FlushUnneededShadowMemory(uptr addr, uptr size);
void IncreaseTotalMmap(uptr size);
void DecreaseTotalMmap(uptr size);
+uptr GetRSS();
+void NoHugePagesInRegion(uptr addr, uptr length);
+void DontDumpShadowMemory(uptr addr, uptr length);
+// Check if the built VMA size matches the runtime one.
+void CheckVMASize();
// InternalScopedBuffer can be used instead of large stack arrays to
// keep frame size low.
@@ -126,44 +159,93 @@ void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback);
// IO
void RawWrite(const char *buffer);
-bool PrintsToTty();
-// Caching version of PrintsToTty(). Not thread-safe.
-bool PrintsToTtyCached();
bool ColorizeReports();
void Printf(const char *format, ...);
void Report(const char *format, ...);
void SetPrintfAndReportCallback(void (*callback)(const char *));
#define VReport(level, ...) \
do { \
- if ((uptr)common_flags()->verbosity >= (level)) Report(__VA_ARGS__); \
+ if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \
} while (0)
#define VPrintf(level, ...) \
do { \
- if ((uptr)common_flags()->verbosity >= (level)) Printf(__VA_ARGS__); \
+ if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \
} while (0)
// Can be used to prevent mixing error reports from different sanitizers.
extern StaticSpinMutex CommonSanitizerReportMutex;
-void MaybeOpenReportFile();
-extern fd_t report_fd;
-extern bool log_to_file;
-extern char report_path_prefix[4096];
-extern uptr report_fd_pid;
+
+struct ReportFile {
+ void Write(const char *buffer, uptr length);
+ bool SupportsColors();
+ void SetReportPath(const char *path);
+
+ // Don't use fields directly. They are only declared public to allow
+ // aggregate initialization.
+
+ // Protects fields below.
+ StaticSpinMutex *mu;
+ // Opened file descriptor. Defaults to stderr. It may be equal to
+ // kInvalidFd, in which case new file will be opened when necessary.
+ fd_t fd;
+ // Path prefix of report file, set via __sanitizer_set_report_path.
+ char path_prefix[kMaxPathLength];
+ // Full path to report, obtained as <path_prefix>.PID
+ char full_path[kMaxPathLength];
+ // PID of the process that opened fd. If a fork() occurs,
+ // the PID of child will be different from fd_pid.
+ uptr fd_pid;
+
+ private:
+ void ReopenIfNecessary();
+};
+extern ReportFile report_file;
+
extern uptr stoptheworld_tracer_pid;
extern uptr stoptheworld_tracer_ppid;
-uptr OpenFile(const char *filename, bool write);
+enum FileAccessMode {
+ RdOnly,
+ WrOnly,
+ RdWr
+};
+
+// Returns kInvalidFd on error.
+fd_t OpenFile(const char *filename, FileAccessMode mode,
+ error_t *errno_p = nullptr);
+void CloseFile(fd_t);
+
+// Return true on success, false on error.
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size,
+ uptr *bytes_read = nullptr, error_t *error_p = nullptr);
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size,
+ uptr *bytes_written = nullptr, error_t *error_p = nullptr);
+
+bool RenameFile(const char *oldpath, const char *newpath,
+ error_t *error_p = nullptr);
+
+// Scoped file handle closer.
+struct FileCloser {
+ explicit FileCloser(fd_t fd) : fd(fd) {}
+ ~FileCloser() { CloseFile(fd); }
+ fd_t fd;
+};
+
+bool SupportsColoredOutput(fd_t fd);
+
// Opens the file 'file_name" and reads up to 'max_len' bytes.
// The resulting buffer is mmaped and stored in '*buff'.
-// The size of the mmaped region is stored in '*buff_size',
-// Returns the number of read bytes or 0 if file can not be opened.
-uptr ReadFileToBuffer(const char *file_name, char **buff,
- uptr *buff_size, uptr max_len);
+// The size of the mmaped region is stored in '*buff_size'.
+// The total number of read bytes is stored in '*read_len'.
+// Returns true if file was successfully opened and read.
+bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
+ uptr *read_len, uptr max_len = 1 << 26,
+ error_t *errno_p = nullptr);
// Maps given file to virtual memory, and returns pointer to it
-// (or NULL if the mapping failes). Stores the size of mmaped region
+// (or NULL if mapping fails). Stores the size of mmaped region
// in '*buff_size'.
void *MapFileToMemory(const char *file_name, uptr *buff_size);
-void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset);
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset);
bool IsAccessibleMemoryRange(uptr beg, uptr size);
@@ -174,6 +256,12 @@ const char *StripPathPrefix(const char *filepath,
const char *StripModuleName(const char *module);
// OS
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
+uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len);
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len);
+const char *GetProcessName();
+void UpdateProcessName();
+void CacheBinaryName();
void DisableCoreDumperIfNecessary();
void DumpProcessMap();
bool FileExists(const char *filename);
@@ -181,6 +269,9 @@ const char *GetEnv(const char *name);
bool SetEnv(const char *name, const char *value);
const char *GetPwd();
char *FindPathToBinary(const char *name);
+bool IsPathSeparator(const char c);
+bool IsAbsolutePath(const char *path);
+
u32 GetUid();
void ReExec();
bool StackSizeIsUnlimited();
@@ -192,10 +283,13 @@ void PrepareForSandboxing(__sanitizer_sandbox_arguments *args);
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args);
void SetSandboxingCallback(void (*f)());
-void CovUpdateMapping(uptr caller_pc = 0);
+void CoverageUpdateMapping();
void CovBeforeFork();
void CovAfterFork(int child_pid);
+void InitializeCoverage(bool enabled, const char *coverage_dir);
+void ReInitializeCoverage(bool enabled, const char *coverage_dir);
+
void InitTlsSize();
uptr GetTlsSize();
@@ -205,12 +299,15 @@ void SleepForMillis(int millis);
u64 NanoTime();
int Atexit(void (*function)(void));
void SortArray(uptr *array, uptr size);
+bool TemplateMatch(const char *templ, const char *str);
// Exit
void NORETURN Abort();
void NORETURN Die();
void NORETURN
CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2);
+void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
+ error_t err);
// Set the name of the current thread to 'name', return true on succees.
// The name may be truncated to a system-dependent limit.
@@ -222,12 +319,26 @@ bool SanitizerGetThreadName(char *name, int max_len);
// Specific tools may override behavior of "Die" and "CheckFailed" functions
// to do tool-specific job.
typedef void (*DieCallbackType)(void);
-void SetDieCallback(DieCallbackType);
-DieCallbackType GetDieCallback();
+
+// It's possible to add several callbacks that would be run when "Die" is
+// called. The callbacks will be run in the opposite order. The tools are
+// strongly recommended to setup all callbacks during initialization, when there
+// is only a single thread.
+bool AddDieCallback(DieCallbackType callback);
+bool RemoveDieCallback(DieCallbackType callback);
+
+void SetUserDieCallback(DieCallbackType callback);
+
typedef void (*CheckFailedCallbackType)(const char *, int, const char *,
u64, u64);
void SetCheckFailedCallback(CheckFailedCallbackType callback);
+// Callback will be called if soft_rss_limit_mb is given and the limit is
+// exceeded (exceeded==true) or if rss went down below the limit
+// (exceeded==false).
+// The callback should be registered once at the tool init time.
+void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded));
+
// Functions related to signal handling.
typedef void (*SignalHandlerType)(int, void *, void *);
bool IsDeadlySignal(int signum);
@@ -243,9 +354,9 @@ const int kMaxSummaryLength = 1024;
// and pass it to __sanitizer_report_error_summary.
void ReportErrorSummary(const char *error_message);
// Same as above, but construct error_message as:
-// error_type file:line function
-void ReportErrorSummary(const char *error_type, const char *file,
- int line, const char *function);
+// error_type file:line[:column][ function]
+void ReportErrorSummary(const char *error_type, const AddressInfo &info);
+// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
void ReportErrorSummary(const char *error_type, StackTrace *trace);
// Math
@@ -264,7 +375,11 @@ INLINE uptr MostSignificantSetBitIndex(uptr x) {
CHECK_NE(x, 0U);
unsigned long up; // NOLINT
#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
+# ifdef _WIN64
+ up = SANITIZER_WORDSIZE - 1 - __builtin_clzll(x);
+# else
up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x);
+# endif
#elif defined(_WIN64)
_BitScanReverse64(&up, x);
#else
@@ -277,7 +392,11 @@ INLINE uptr LeastSignificantSetBitIndex(uptr x) {
CHECK_NE(x, 0U);
unsigned long up; // NOLINT
#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
+# ifdef _WIN64
+ up = __builtin_ctzll(x);
+# else
up = __builtin_ctzl(x);
+# endif
#elif defined(_WIN64)
_BitScanForward64(&up, x);
#else
@@ -297,7 +416,7 @@ INLINE uptr RoundUpToPowerOfTwo(uptr size) {
uptr up = MostSignificantSetBitIndex(size);
CHECK(size < (1ULL << (up + 1)));
CHECK(size > (1ULL << up));
- return 1UL << (up + 1);
+ return 1ULL << (up + 1);
}
INLINE uptr RoundUpTo(uptr size, uptr boundary) {
@@ -315,17 +434,7 @@ INLINE bool IsAligned(uptr a, uptr alignment) {
INLINE uptr Log2(uptr x) {
CHECK(IsPowerOfTwo(x));
-#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
- return __builtin_ctzl(x);
-#elif defined(_WIN64)
- unsigned long ret; // NOLINT
- _BitScanForward64(&ret, x);
- return ret;
-#else
- unsigned long ret; // NOLINT
- _BitScanForward(&ret, x);
- return ret;
-#endif
+ return LeastSignificantSetBitIndex(x);
}
// Don't use std::min, std::max or std::swap, to minimize dependency
@@ -354,14 +463,14 @@ INLINE int ToLower(int c) {
// small vectors.
// WARNING: The current implementation supports only POD types.
template<typename T>
-class InternalMmapVector {
+class InternalMmapVectorNoCtor {
public:
- explicit InternalMmapVector(uptr initial_capacity) {
+ void Initialize(uptr initial_capacity) {
capacity_ = Max(initial_capacity, (uptr)1);
size_ = 0;
- data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVector");
+ data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVectorNoCtor");
}
- ~InternalMmapVector() {
+ void Destroy() {
UnmapOrDie(data_, capacity_ * sizeof(T));
}
T &operator[](uptr i) {
@@ -394,11 +503,15 @@ class InternalMmapVector {
const T *data() const {
return data_;
}
+ T *data() {
+ return data_;
+ }
uptr capacity() const {
return capacity_;
}
void clear() { size_ = 0; }
+ bool empty() const { return size() == 0; }
private:
void Resize(uptr new_capacity) {
@@ -412,15 +525,24 @@ class InternalMmapVector {
UnmapOrDie(old_data, capacity_ * sizeof(T));
capacity_ = new_capacity;
}
- // Disallow evil constructors.
- InternalMmapVector(const InternalMmapVector&);
- void operator=(const InternalMmapVector&);
T *data_;
uptr capacity_;
uptr size_;
};
+template<typename T>
+class InternalMmapVector : public InternalMmapVectorNoCtor<T> {
+ public:
+ explicit InternalMmapVector(uptr initial_capacity) {
+ InternalMmapVectorNoCtor<T>::Initialize(initial_capacity);
+ }
+ ~InternalMmapVector() { InternalMmapVectorNoCtor<T>::Destroy(); }
+ // Disallow evil constructors.
+ InternalMmapVector(const InternalMmapVector&);
+ void operator=(const InternalMmapVector&);
+};
+
// HeapSort for arrays and InternalMmapVector.
template<class Container, class Compare>
void InternalSort(Container *v, uptr size, Compare comp) {
@@ -478,29 +600,32 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last,
// executable or a shared object).
class LoadedModule {
public:
- LoadedModule(const char *module_name, uptr base_address);
+ LoadedModule() : full_name_(nullptr), base_address_(0) { ranges_.clear(); }
+ void set(const char *module_name, uptr base_address);
+ void clear();
void addAddressRange(uptr beg, uptr end, bool executable);
bool containsAddress(uptr address) const;
const char *full_name() const { return full_name_; }
uptr base_address() const { return base_address_; }
- uptr n_ranges() const { return n_ranges_; }
- uptr address_range_start(int i) const { return ranges_[i].beg; }
- uptr address_range_end(int i) const { return ranges_[i].end; }
- bool address_range_executable(int i) const { return exec_[i]; }
-
- private:
struct AddressRange {
+ AddressRange *next;
uptr beg;
uptr end;
+ bool executable;
+
+ AddressRange(uptr beg, uptr end, bool executable)
+ : next(nullptr), beg(beg), end(end), executable(executable) {}
};
- char *full_name_;
+
+ typedef IntrusiveList<AddressRange>::ConstIterator Iterator;
+ Iterator ranges() const { return Iterator(&ranges_); }
+
+ private:
+ char *full_name_; // Owned.
uptr base_address_;
- static const uptr kMaxNumberOfAddressRanges = 6;
- AddressRange ranges_[kMaxNumberOfAddressRanges];
- bool exec_[kMaxNumberOfAddressRanges];
- uptr n_ranges_;
+ IntrusiveList<AddressRange> ranges_;
};
// OS-dependent function that fills array with descriptions of at most
@@ -511,45 +636,80 @@ typedef bool (*string_predicate_t)(const char *);
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter);
-#if SANITIZER_POSIX
-const uptr kPthreadDestructorIterations = 4;
-#else
-// Unused on Windows.
-const uptr kPthreadDestructorIterations = 0;
-#endif
-
// Callback type for iterating over a set of memory ranges.
typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg);
-#if (SANITIZER_FREEBSD || SANITIZER_LINUX) && !defined(SANITIZER_GO)
-extern uptr indirect_call_wrapper;
-void SetIndirectCallWrapper(uptr wrapper);
+enum AndroidApiLevel {
+ ANDROID_NOT_ANDROID = 0,
+ ANDROID_KITKAT = 19,
+ ANDROID_LOLLIPOP_MR1 = 22,
+ ANDROID_POST_LOLLIPOP = 23
+};
-template <typename F>
-F IndirectExternCall(F f) {
- typedef F (*WrapF)(F);
- return indirect_call_wrapper ? ((WrapF)indirect_call_wrapper)(f) : f;
-}
+#if SANITIZER_LINUX
+// Initialize Android logging. Any writes before this are silently lost.
+void AndroidLogInit();
+void WriteToSyslog(const char *buffer);
#else
-INLINE void SetIndirectCallWrapper(uptr wrapper) {}
-template <typename F>
-F IndirectExternCall(F f) {
- return f;
-}
+INLINE void AndroidLogInit() {}
+INLINE void WriteToSyslog(const char *buffer) {}
#endif
#if SANITIZER_ANDROID
-// Initialize Android logging. Any writes before this are silently lost.
-void AndroidLogInit();
-void AndroidLogWrite(const char *buffer);
void GetExtraActivationFlags(char *buf, uptr size);
void SanitizerInitializeUnwinder();
+AndroidApiLevel AndroidGetApiLevel();
#else
-INLINE void AndroidLogInit() {}
INLINE void AndroidLogWrite(const char *buffer_unused) {}
INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
INLINE void SanitizerInitializeUnwinder() {}
+INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; }
+#endif
+
+INLINE uptr GetPthreadDestructorIterations() {
+#if SANITIZER_ANDROID
+ return (AndroidGetApiLevel() == ANDROID_LOLLIPOP_MR1) ? 8 : 4;
+#elif SANITIZER_POSIX
+ return 4;
+#else
+// Unused on Windows.
+ return 0;
+#endif
+}
+
+void *internal_start_thread(void(*func)(void*), void *arg);
+void internal_join_thread(void *th);
+void MaybeStartBackgroudThread();
+
+// Make the compiler think that something is going on there.
+// Use this inside a loop that looks like memset/memcpy/etc to prevent the
+// compiler from recognising it and turning it into an actual call to
+// memset/memcpy/etc.
+static inline void SanitizerBreakOptimization(void *arg) {
+#if _MSC_VER && !defined(__clang__)
+ _ReadWriteBarrier();
+#else
+ __asm__ __volatile__("" : : "r" (arg) : "memory");
#endif
+}
+
+struct SignalContext {
+ void *context;
+ uptr addr;
+ uptr pc;
+ uptr sp;
+ uptr bp;
+
+ SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
+ context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
+ }
+
+ // Creates signal context in a platform-specific manner.
+ static SignalContext Create(void *siginfo, void *context);
+};
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
+
} // namespace __sanitizer
inline void *operator new(__sanitizer::operator_new_size_type size,
diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc
index 10f321838e8..9b8c77e6901 100644
--- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc
@@ -15,10 +15,12 @@
// COMMON_INTERCEPTOR_READ_RANGE
// COMMON_INTERCEPTOR_WRITE_RANGE
// COMMON_INTERCEPTOR_INITIALIZE_RANGE
+// COMMON_INTERCEPTOR_DIR_ACQUIRE
// COMMON_INTERCEPTOR_FD_ACQUIRE
// COMMON_INTERCEPTOR_FD_RELEASE
// COMMON_INTERCEPTOR_FD_ACCESS
// COMMON_INTERCEPTOR_SET_THREAD_NAME
+// COMMON_INTERCEPTOR_ON_DLOPEN
// COMMON_INTERCEPTOR_ON_EXIT
// COMMON_INTERCEPTOR_MUTEX_LOCK
// COMMON_INTERCEPTOR_MUTEX_UNLOCK
@@ -27,6 +29,7 @@
// COMMON_INTERCEPTOR_HANDLE_RECVMSG
// COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED
//===----------------------------------------------------------------------===//
+
#include "interception/interception.h"
#include "sanitizer_addrhashmap.h"
#include "sanitizer_placement_new.h"
@@ -35,12 +38,31 @@
#include <stdarg.h>
+#if SANITIZER_INTERCEPTOR_HOOKS
+#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) \
+ do { \
+ if (f) \
+ f(__VA_ARGS__); \
+ } while (false);
+#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) \
+ extern "C" { \
+ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void f(__VA_ARGS__); \
+ } // extern "C"
+#else
+#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...)
+#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...)
+
+#endif // SANITIZER_INTERCEPTOR_HOOKS
+
#if SANITIZER_WINDOWS && !defined(va_copy)
#define va_copy(dst, src) ((dst) = (src))
#endif // _WIN32
#if SANITIZER_FREEBSD
#define pthread_setname_np pthread_set_name_np
+#define inet_aton __inet_aton
+#define inet_pton __inet_pton
+#define iconv __bsd_iconv
#endif
#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE
@@ -80,7 +102,7 @@
#endif
#ifndef COMMON_INTERCEPTOR_LIBRARY_LOADED
-#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, map) {}
+#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) {}
#endif
#ifndef COMMON_INTERCEPTOR_LIBRARY_UNLOADED
@@ -96,6 +118,29 @@
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0)
#endif
+#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n) \
+ COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \
+ common_flags()->strict_string_checks ? (len) + 1 : (n) )
+
+#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \
+ COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
+
+#ifndef COMMON_INTERCEPTOR_ON_DLOPEN
+#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0;
+#endif
+
+#ifndef COMMON_INTERCEPTOR_ACQUIRE
+#define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_RELEASE
+#define COMMON_INTERCEPTOR_RELEASE(ctx, u) {}
+#endif
+
struct FileMetadata {
// For open_memstream().
char **addr;
@@ -149,7 +194,8 @@ UNUSED static void DeleteInterceptorMetadata(void *addr) {
INTERCEPTOR(char*, textdomain, const char *domainname) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname);
- char* domain = REAL(textdomain)(domainname);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0);
+ char *domain = REAL(textdomain)(domainname);
if (domain) {
COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1);
}
@@ -165,9 +211,14 @@ static inline int CharCmpX(unsigned char c1, unsigned char c2) {
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
}
+DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, uptr called_pc,
+ const char *s1, const char *s2)
+
INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2);
+ CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, GET_CALLER_PC(), s1,
+ s2);
unsigned char c1, c2;
uptr i;
for (i = 0;; i++) {
@@ -175,16 +226,21 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
c2 = (unsigned char)s2[i];
if (c1 != c2 || c1 == '\0') break;
}
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
return CharCmpX(c1, c2);
}
+DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, uptr called_pc,
+ const char *s1, const char *s2, uptr n)
+
INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
return internal_strncmp(s1, s2, size);
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size);
+ CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, GET_CALLER_PC(), s1,
+ s2, size);
unsigned char c1 = 0, c2 = 0;
uptr i;
for (i = 0; i < size; i++) {
@@ -221,8 +277,8 @@ INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) {
c2 = (unsigned char)s2[i];
if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
}
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
return CharCaseCmp(c1, c2);
}
@@ -248,12 +304,145 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) {
#define INIT_STRNCASECMP
#endif
+#if SANITIZER_INTERCEPT_STRSTR || SANITIZER_INTERCEPT_STRCASESTR
+static inline void StrstrCheck(void *ctx, char *r, const char *s1,
+ const char *s2) {
+ uptr len1 = REAL(strlen)(s1);
+ uptr len2 = REAL(strlen)(s2);
+ COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s1, len1,
+ r ? r - s1 + len2 : len1 + 1);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1);
+}
+#endif
+
+#if SANITIZER_INTERCEPT_STRSTR
+INTERCEPTOR(char*, strstr, const char *s1, const char *s2) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_strstr(s1, s2);
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2);
+ char *r = REAL(strstr)(s1, s2);
+ if (common_flags()->intercept_strstr)
+ StrstrCheck(ctx, r, s1, s2);
+ return r;
+}
+
+#define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr);
+#else
+#define INIT_STRSTR
+#endif
+
+#if SANITIZER_INTERCEPT_STRCASESTR
+INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2);
+ char *r = REAL(strcasestr)(s1, s2);
+ if (common_flags()->intercept_strstr)
+ StrstrCheck(ctx, r, s1, s2);
+ return r;
+}
+
+#define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr);
+#else
+#define INIT_STRCASESTR
+#endif
+
+#if SANITIZER_INTERCEPT_STRSPN
+INTERCEPTOR(SIZE_T, strspn, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strspn, s1, s2);
+ SIZE_T r = REAL(strspn)(s1, s2);
+ if (common_flags()->intercept_strspn) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
+ }
+ return r;
+}
+
+INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2);
+ SIZE_T r = REAL(strcspn)(s1, s2);
+ if (common_flags()->intercept_strspn) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
+ }
+ return r;
+}
+
+#define INIT_STRSPN \
+ COMMON_INTERCEPT_FUNCTION(strspn); \
+ COMMON_INTERCEPT_FUNCTION(strcspn);
+#else
+#define INIT_STRSPN
+#endif
+
+#if SANITIZER_INTERCEPT_STRPBRK
+INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2);
+ char *r = REAL(strpbrk)(s1, s2);
+ if (common_flags()->intercept_strpbrk) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1,
+ r ? r - s1 + 1 : REAL(strlen)(s1) + 1);
+ }
+ return r;
+}
+
+#define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk);
+#else
+#define INIT_STRPBRK
+#endif
+
+#if SANITIZER_INTERCEPT_MEMCMP
+
+DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, uptr called_pc,
+ const void *s1, const void *s2, uptr n)
+
+INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size);
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_memcmp(a1, a2, size);
+ CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1,
+ a2, size);
+ if (common_flags()->intercept_memcmp) {
+ if (common_flags()->strict_memcmp) {
+ // Check the entire regions even if the first bytes of the buffers are
+ // different.
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, a1, size);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, a2, size);
+ // Fallthrough to REAL(memcmp) below.
+ } else {
+ unsigned char c1 = 0, c2 = 0;
+ const unsigned char *s1 = (const unsigned char*)a1;
+ const unsigned char *s2 = (const unsigned char*)a2;
+ uptr i;
+ for (i = 0; i < size; i++) {
+ c1 = s1[i];
+ c2 = s2[i];
+ if (c1 != c2) break;
+ }
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size));
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size));
+ return CharCmpX(c1, c2);
+ }
+ }
+ return REAL(memcmp(a1, a2, size));
+}
+
+#define INIT_MEMCMP COMMON_INTERCEPT_FUNCTION(memcmp)
+#else
+#define INIT_MEMCMP
+#endif
+
#if SANITIZER_INTERCEPT_MEMCHR
INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n);
void *res = REAL(memchr)(s, c, n);
- uptr len = res ? (char*)res - (char*)s + 1 : n;
+ uptr len = res ? (char *)res - (const char *)s + 1 : n;
COMMON_INTERCEPTOR_READ_RANGE(ctx, s, len);
return res;
}
@@ -717,12 +906,12 @@ INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) {
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(strptime)(s, format, tm);
- if (res) {
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0);
+ if (res && tm) {
// Do not call unpoison_tm here, because strptime does not, in fact,
// initialize the entire struct tm. For example, tm_zone pointer is left
// uninitialized.
- if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
}
return res;
}
@@ -913,6 +1102,16 @@ INTERCEPTOR(int, vsnprintf, char *str, SIZE_T size, const char *format,
va_list ap)
VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap)
+#if SANITIZER_INTERCEPT_PRINTF_L
+INTERCEPTOR(int, vsnprintf_l, char *str, SIZE_T size, void *loc,
+ const char *format, va_list ap)
+VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf_l, str, size, loc, format, ap)
+
+INTERCEPTOR(int, snprintf_l, char *str, SIZE_T size, void *loc,
+ const char *format, ...)
+FORMAT_INTERCEPTOR_IMPL(snprintf_l, vsnprintf_l, str, size, loc, format)
+#endif // SANITIZER_INTERCEPT_PRINTF_L
+
INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap)
VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, format, ap)
@@ -989,6 +1188,14 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size,
#define INIT_PRINTF
#endif
+#if SANITIZER_INTERCEPT_PRINTF_L
+#define INIT_PRINTF_L \
+ COMMON_INTERCEPT_FUNCTION(snprintf_l); \
+ COMMON_INTERCEPT_FUNCTION(vsnprintf_l);
+#else
+#define INIT_PRINTF_L
+#endif
+
#if SANITIZER_INTERCEPT_ISOC99_PRINTF
#define INIT_ISOC99_PRINTF \
COMMON_INTERCEPT_FUNCTION(__isoc99_printf); \
@@ -1005,8 +1212,18 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size,
#if SANITIZER_INTERCEPT_IOCTL
#include "sanitizer_common_interceptors_ioctl.inc"
-INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) {
- void *ctx;
+INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) {
+ // We need a frame pointer, because we call into ioctl_common_[pre|post] which
+ // can trigger a report and we need to be able to unwind through this
+ // function. On Mac in debug mode we might not have a frame pointer, because
+ // ioctl_common_[pre|post] doesn't get inlined here.
+ ENABLE_FRAME_POINTER;
+
+ void *ctx;
+ va_list ap;
+ va_start(ap, request);
+ void *arg = va_arg(ap, void *);
+ va_end(ap);
COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg);
CHECK(ioctl_initialized);
@@ -1015,6 +1232,10 @@ INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) {
// This effectively disables ioctl handling in TSan.
if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg);
+ // Although request is unsigned long, the rest of the interceptor uses it
+ // as just "unsigned" to save space, because we know that all values fit in
+ // "unsigned" - they are compile-time constants.
+
const ioctl_desc *desc = ioctl_lookup(request);
ioctl_desc decoded_desc;
if (!desc) {
@@ -1097,14 +1318,14 @@ INTERCEPTOR(__sanitizer_passwd *, getpwnam, const char *name) {
COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name);
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
__sanitizer_passwd *res = REAL(getpwnam)(name);
- if (res != 0) unpoison_passwd(ctx, res);
+ if (res) unpoison_passwd(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_passwd *, getpwuid, u32 uid) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid);
__sanitizer_passwd *res = REAL(getpwuid)(uid);
- if (res != 0) unpoison_passwd(ctx, res);
+ if (res) unpoison_passwd(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) {
@@ -1112,14 +1333,14 @@ INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) {
COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name);
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
__sanitizer_group *res = REAL(getgrnam)(name);
- if (res != 0) unpoison_group(ctx, res);
+ if (res) unpoison_group(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_group *, getgrgid, u32 gid) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid);
__sanitizer_group *res = REAL(getgrgid)(gid);
- if (res != 0) unpoison_group(ctx, res);
+ if (res) unpoison_group(ctx, res);
return res;
}
#define INIT_GETPWNAM_AND_FRIENDS \
@@ -1208,14 +1429,14 @@ INTERCEPTOR(__sanitizer_passwd *, getpwent, int dummy) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getpwent, dummy);
__sanitizer_passwd *res = REAL(getpwent)(dummy);
- if (res != 0) unpoison_passwd(ctx, res);
+ if (res) unpoison_passwd(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_group *, getgrent, int dummy) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getgrent, dummy);
__sanitizer_group *res = REAL(getgrent)(dummy);
- if (res != 0) unpoison_group(ctx, res);;
+ if (res) unpoison_group(ctx, res);;
return res;
}
#define INIT_GETPWENT \
@@ -1230,14 +1451,14 @@ INTERCEPTOR(__sanitizer_passwd *, fgetpwent, void *fp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent, fp);
__sanitizer_passwd *res = REAL(fgetpwent)(fp);
- if (res != 0) unpoison_passwd(ctx, res);
+ if (res) unpoison_passwd(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_group *, fgetgrent, void *fp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent, fp);
__sanitizer_group *res = REAL(fgetgrent)(fp);
- if (res != 0) unpoison_group(ctx, res);
+ if (res) unpoison_group(ctx, res);
return res;
}
#define INIT_FGETPWENT \
@@ -1440,30 +1661,30 @@ static THREADLOCAL __sanitizer_glob_t *pglob_copy;
static void wrapped_gl_closedir(void *dir) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
- IndirectExternCall(pglob_copy->gl_closedir)(dir);
+ pglob_copy->gl_closedir(dir);
}
static void *wrapped_gl_readdir(void *dir) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
- return IndirectExternCall(pglob_copy->gl_readdir)(dir);
+ return pglob_copy->gl_readdir(dir);
}
static void *wrapped_gl_opendir(const char *s) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
- return IndirectExternCall(pglob_copy->gl_opendir)(s);
+ return pglob_copy->gl_opendir(s);
}
static int wrapped_gl_lstat(const char *s, void *st) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
- return IndirectExternCall(pglob_copy->gl_lstat)(s, st);
+ return pglob_copy->gl_lstat(s, st);
}
static int wrapped_gl_stat(const char *s, void *st) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(2);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1);
- return IndirectExternCall(pglob_copy->gl_stat)(s, st);
+ return pglob_copy->gl_stat(s, st);
}
INTERCEPTOR(int, glob, const char *pattern, int flags,
@@ -1471,6 +1692,7 @@ INTERCEPTOR(int, glob, const char *pattern, int flags,
__sanitizer_glob_t *pglob) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
__sanitizer_glob_t glob_copy = {
0, 0, 0,
0, wrapped_gl_closedir, wrapped_gl_readdir,
@@ -1501,6 +1723,7 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags,
__sanitizer_glob_t *pglob) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
__sanitizer_glob_t glob_copy = {
0, 0, 0,
0, wrapped_gl_closedir, wrapped_gl_readdir,
@@ -1647,6 +1870,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) {
INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0);
// FIXME: figure out read size based on the address family.
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
@@ -2137,6 +2361,16 @@ INTERCEPTOR(int, sysinfo, void *info) {
#endif
#if SANITIZER_INTERCEPT_READDIR
+INTERCEPTOR(__sanitizer_dirent *, opendir, const char *path) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, opendir, path);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ __sanitizer_dirent *res = REAL(opendir)(path);
+ if (res)
+ COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path);
+ return res;
+}
+
INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp);
@@ -2165,6 +2399,7 @@ INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry,
}
#define INIT_READDIR \
+ COMMON_INTERCEPT_FUNCTION(opendir); \
COMMON_INTERCEPT_FUNCTION(readdir); \
COMMON_INTERCEPT_FUNCTION(readdir_r);
#else
@@ -2306,6 +2541,37 @@ INTERCEPTOR(char *, get_current_dir_name, int fake) {
#define INIT_GET_CURRENT_DIR_NAME
#endif
+UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
+ CHECK(endptr);
+ if (nptr == *endptr) {
+ // No digits were found at strtol call, we need to find out the last
+ // symbol accessed by strtoll on our own.
+ // We get this symbol by skipping leading blanks and optional +/- sign.
+ while (IsSpace(*nptr)) nptr++;
+ if (*nptr == '+' || *nptr == '-') nptr++;
+ *endptr = const_cast<char *>(nptr);
+ }
+ CHECK(*endptr >= nptr);
+}
+
+UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr,
+ char **endptr, char *real_endptr, int base) {
+ if (endptr) {
+ *endptr = real_endptr;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+ }
+ // If base has unsupported value, strtol can exit with EINVAL
+ // without reading any characters. So do additional checks only
+ // if base is valid.
+ bool is_valid_base = (base == 0) || (2 <= base && base <= 36);
+ if (is_valid_base) {
+ FixRealStrtolEndptr(nptr, &real_endptr);
+ }
+ COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ?
+ (real_endptr - nptr) + 1 : 0);
+}
+
+
#if SANITIZER_INTERCEPT_STRTOIMAX
INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
void *ctx;
@@ -2313,8 +2579,9 @@ INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
- INTMAX_T res = REAL(strtoimax)(nptr, endptr, base);
- if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+ char *real_endptr;
+ INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return res;
}
@@ -2324,8 +2591,9 @@ INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
- INTMAX_T res = REAL(strtoumax)(nptr, endptr, base);
- if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+ char *real_endptr;
+ INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return res;
}
@@ -2456,7 +2724,7 @@ INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms,
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps);
- if (res != (SIZE_T) - 1 && dest && src) {
+ if (res != ((SIZE_T)-1) && dest && src) {
SIZE_T write_cnt = res + !*src;
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt);
}
@@ -2468,6 +2736,28 @@ INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms,
#define INIT_WCSNRTOMBS
#endif
+
+#if SANITIZER_INTERCEPT_WCRTOMB
+INTERCEPTOR(SIZE_T, wcrtomb, char *dest, wchar_t src, void *ps) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, wcrtomb, dest, src, ps);
+ if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+ SIZE_T res = REAL(wcrtomb)(dest, src, ps);
+ if (res != ((SIZE_T)-1) && dest) {
+ SIZE_T write_cnt = res;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt);
+ }
+ return res;
+}
+
+#define INIT_WCRTOMB COMMON_INTERCEPT_FUNCTION(wcrtomb);
+#else
+#define INIT_WCRTOMB
+#endif
+
#if SANITIZER_INTERCEPT_TCGETATTR
INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) {
void *ctx;
@@ -2496,7 +2786,7 @@ INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) {
// version of a versioned symbol. For realpath(), this gives us something
// (called __old_realpath) that does not handle NULL in the second argument.
// Handle it as part of the interceptor.
- char *allocated_path = 0;
+ char *allocated_path = nullptr;
if (!resolved_path)
allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1);
@@ -2558,6 +2848,19 @@ INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) {
#define INIT_SCHED_GETAFFINITY
#endif
+#if SANITIZER_INTERCEPT_SCHED_GETPARAM
+INTERCEPTOR(int, sched_getparam, int pid, void *param) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sched_getparam, pid, param);
+ int res = REAL(sched_getparam)(pid, param);
+ if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, struct_sched_param_sz);
+ return res;
+}
+#define INIT_SCHED_GETPARAM COMMON_INTERCEPT_FUNCTION(sched_getparam);
+#else
+#define INIT_SCHED_GETPARAM
+#endif
+
#if SANITIZER_INTERCEPT_STRERROR
INTERCEPTOR(char *, strerror, int errnum) {
void *ctx;
@@ -2631,7 +2934,7 @@ static THREADLOCAL scandir_compar_f scandir_compar;
static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen);
- return IndirectExternCall(scandir_filter)(dir);
+ return scandir_filter(dir);
}
static int wrapped_scandir_compar(const struct __sanitizer_dirent **a,
@@ -2641,7 +2944,7 @@ static int wrapped_scandir_compar(const struct __sanitizer_dirent **a,
COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen);
- return IndirectExternCall(scandir_compar)(a, b);
+ return scandir_compar(a, b);
}
INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist,
@@ -2654,10 +2957,11 @@ INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist,
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
- int res = REAL(scandir)(dirp, namelist, filter ? wrapped_scandir_filter : 0,
- compar ? wrapped_scandir_compar : 0);
- scandir_filter = 0;
- scandir_compar = 0;
+ int res = REAL(scandir)(dirp, namelist,
+ filter ? wrapped_scandir_filter : nullptr,
+ compar ? wrapped_scandir_compar : nullptr);
+ scandir_filter = nullptr;
+ scandir_compar = nullptr;
if (namelist && res > 0) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist));
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res);
@@ -2683,7 +2987,7 @@ static THREADLOCAL scandir64_compar_f scandir64_compar;
static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) {
COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen);
- return IndirectExternCall(scandir64_filter)(dir);
+ return scandir64_filter(dir);
}
static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a,
@@ -2693,7 +2997,7 @@ static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a,
COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen);
COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b));
COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen);
- return IndirectExternCall(scandir64_compar)(a, b);
+ return scandir64_compar(a, b);
}
INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist,
@@ -2707,10 +3011,11 @@ INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist,
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
int res =
- REAL(scandir64)(dirp, namelist, filter ? wrapped_scandir64_filter : 0,
- compar ? wrapped_scandir64_compar : 0);
- scandir64_filter = 0;
- scandir64_compar = 0;
+ REAL(scandir64)(dirp, namelist,
+ filter ? wrapped_scandir64_filter : nullptr,
+ compar ? wrapped_scandir64_compar : nullptr);
+ scandir64_filter = nullptr;
+ scandir64_compar = nullptr;
if (namelist && res > 0) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist));
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res);
@@ -3576,6 +3881,7 @@ INTERCEPTOR(char *, tempnam, char *dir, char *pfx) {
INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name);
return REAL(pthread_setname_np)(thread, name);
}
@@ -3792,25 +4098,33 @@ INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) {
}
return res;
}
-INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim,
- void *stream) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, __getdelim, lineptr, n, delim, stream);
- // FIXME: under ASan the call below may write to freed memory and corrupt
- // its metadata. See
- // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
- SSIZE_T res = REAL(__getdelim)(lineptr, n, delim, stream);
- if (res > 0) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr));
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n));
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1);
+
+// FIXME: under ASan the call below may write to freed memory and corrupt its
+// metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define GETDELIM_INTERCEPTOR_IMPL(vname) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, vname, lineptr, n, delim, stream); \
+ SSIZE_T res = REAL(vname)(lineptr, n, delim, stream); \
+ if (res > 0) { \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); \
+ } \
+ return res; \
}
- return res;
-}
+
+INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim,
+ void *stream)
+GETDELIM_INTERCEPTOR_IMPL(__getdelim)
+
+// There's no __getdelim() on FreeBSD so we supply the getdelim() interceptor
+// with its own body.
INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim,
- void *stream) {
- return __getdelim(lineptr, n, delim, stream);
-}
+ void *stream)
+GETDELIM_INTERCEPTOR_IMPL(getdelim)
+
#define INIT_GETLINE \
COMMON_INTERCEPT_FUNCTION(getline); \
COMMON_INTERCEPT_FUNCTION(__getdelim); \
@@ -3831,7 +4145,7 @@ INTERCEPTOR(SIZE_T, iconv, void *cd, char **inbuf, SIZE_T *inbytesleft,
COMMON_INTERCEPTOR_READ_RANGE(ctx, *inbuf, *inbytesleft);
if (outbytesleft)
COMMON_INTERCEPTOR_READ_RANGE(ctx, outbytesleft, sizeof(*outbytesleft));
- void *outbuf_orig = outbuf ? *outbuf : 0;
+ void *outbuf_orig = outbuf ? *outbuf : nullptr;
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
@@ -3866,11 +4180,19 @@ INTERCEPTOR(__sanitizer_clock_t, times, void *tms) {
#if SANITIZER_INTERCEPT_TLS_GET_ADDR
#define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_addr)
+// If you see any crashes around this functions, there are 2 known issues with
+// it: 1. __tls_get_addr can be called with mis-aligned stack due to:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066
+// 2. It can be called recursively if sanitizer code uses __tls_get_addr
+// to access thread local variables (it should not happen normally,
+// because sanitizers use initial-exec tls model).
INTERCEPTOR(void *, __tls_get_addr, void *arg) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg);
void *res = REAL(__tls_get_addr)(arg);
- DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res);
+ uptr tls_begin, tls_end;
+ COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end);
+ DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end);
if (dtv) {
// New DTLS block has been allocated.
COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size);
@@ -4106,9 +4428,9 @@ INTERCEPTOR(int, capset, void *hdrp, const void *datap) {
#endif
#if SANITIZER_INTERCEPT_AEABI_MEM
-DECLARE_REAL_AND_INTERCEPTOR(void *, memmove, void *, const void *, uptr);
-DECLARE_REAL_AND_INTERCEPTOR(void *, memcpy, void *, const void *, uptr);
-DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr);
+DECLARE_REAL_AND_INTERCEPTOR(void *, memmove, void *, const void *, uptr)
+DECLARE_REAL_AND_INTERCEPTOR(void *, memcpy, void *, const void *, uptr)
+DECLARE_REAL_AND_INTERCEPTOR(void *, memset, void *, int, uptr)
INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, uptr size) {
return WRAP(memmove)(to, from, size);
@@ -4448,7 +4770,7 @@ INTERCEPTOR(__sanitizer_FILE *, freopen, const char *path, const char *mode,
__sanitizer_FILE *fp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, freopen, path, mode, fp);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
__sanitizer_FILE *res = REAL(freopen)(path, mode, fp);
@@ -4479,7 +4801,7 @@ INTERCEPTOR(__sanitizer_FILE *, freopen64, const char *path, const char *mode,
__sanitizer_FILE *fp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, freopen64, path, mode, fp);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1);
COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
__sanitizer_FILE *res = REAL(freopen64)(path, mode, fp);
@@ -4608,15 +4930,14 @@ INTERCEPTOR(int, fflush, __sanitizer_FILE *fp) {
INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fclose, fp);
- if (fp) {
- COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
- const FileMetadata *m = GetInterceptorMetadata(fp);
- if (m) {
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size);
- DeleteInterceptorMetadata(fp);
- }
+ COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
+ const FileMetadata *m = GetInterceptorMetadata(fp);
+ int res = REAL(fclose)(fp);
+ if (m) {
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size);
+ DeleteInterceptorMetadata(fp);
}
- return REAL(fclose)(fp);
+ return res;
}
#define INIT_FCLOSE COMMON_INTERCEPT_FUNCTION(fclose);
#else
@@ -4627,6 +4948,8 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
void *ctx;
COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
+ if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
+ COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag);
void *res = REAL(dlopen)(filename, flag);
COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);
return res;
@@ -4697,7 +5020,7 @@ static void MlockIsUnsupported() {
static atomic_uint8_t printed;
if (atomic_exchange(&printed, 1, memory_order_relaxed))
return;
- VPrintf(1, "INFO: %s ignores mlock/mlockall/munlock/munlockall\n",
+ VPrintf(1, "%s ignores mlock/mlockall/munlock/munlockall\n",
SanitizerToolName);
}
@@ -4731,6 +5054,186 @@ INTERCEPTOR(int, munlockall, void) {
#define INIT_MLOCKX
#endif // SANITIZER_INTERCEPT_MLOCKX
+#if SANITIZER_INTERCEPT_FOPENCOOKIE
+struct WrappedCookie {
+ void *real_cookie;
+ __sanitizer_cookie_io_functions_t real_io_funcs;
+};
+
+static uptr wrapped_read(void *cookie, char *buf, uptr size) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_read real_read = wrapped_cookie->real_io_funcs.read;
+ return real_read ? real_read(wrapped_cookie->real_cookie, buf, size) : 0;
+}
+
+static uptr wrapped_write(void *cookie, const char *buf, uptr size) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_write real_write = wrapped_cookie->real_io_funcs.write;
+ return real_write ? real_write(wrapped_cookie->real_cookie, buf, size) : size;
+}
+
+static int wrapped_seek(void *cookie, u64 *offset, int whence) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(offset, sizeof(*offset));
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_seek real_seek = wrapped_cookie->real_io_funcs.seek;
+ return real_seek ? real_seek(wrapped_cookie->real_cookie, offset, whence)
+ : -1;
+}
+
+static int wrapped_close(void *cookie) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_close real_close = wrapped_cookie->real_io_funcs.close;
+ int res = real_close ? real_close(wrapped_cookie->real_cookie) : 0;
+ InternalFree(wrapped_cookie);
+ return res;
+}
+
+INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode,
+ __sanitizer_cookie_io_functions_t io_funcs) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fopencookie, cookie, mode, io_funcs);
+ WrappedCookie *wrapped_cookie =
+ (WrappedCookie *)InternalAlloc(sizeof(WrappedCookie));
+ wrapped_cookie->real_cookie = cookie;
+ wrapped_cookie->real_io_funcs = io_funcs;
+ __sanitizer_FILE *res =
+ REAL(fopencookie)(wrapped_cookie, mode, {wrapped_read, wrapped_write,
+ wrapped_seek, wrapped_close});
+ return res;
+}
+
+#define INIT_FOPENCOOKIE COMMON_INTERCEPT_FUNCTION(fopencookie);
+#else
+#define INIT_FOPENCOOKIE
+#endif // SANITIZER_INTERCEPT_FOPENCOOKIE
+
+#if SANITIZER_INTERCEPT_SEM
+INTERCEPTOR(int, sem_init, __sanitizer_sem_t *s, int pshared, unsigned value) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_init, s, pshared, value);
+ // Workaround a bug in glibc's "old" semaphore implementation by
+ // zero-initializing the sem_t contents. This has to be done here because
+ // interceptors bind to the lowest symbols version by default, hitting the
+ // buggy code path while the non-sanitized build of the same code works fine.
+ REAL(memset)(s, 0, sizeof(*s));
+ int res = REAL(sem_init)(s, pshared, value);
+ return res;
+}
+
+INTERCEPTOR(int, sem_destroy, __sanitizer_sem_t *s) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_destroy, s);
+ int res = REAL(sem_destroy)(s);
+ return res;
+}
+
+INTERCEPTOR(int, sem_wait, __sanitizer_sem_t *s) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_wait, s);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_wait)(s);
+ if (res == 0) {
+ COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sem_trywait, __sanitizer_sem_t *s) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_trywait, s);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_trywait)(s);
+ if (res == 0) {
+ COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sem_timedwait, __sanitizer_sem_t *s, void *abstime) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_timedwait, s, abstime);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, abstime, struct_timespec_sz);
+ int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_timedwait)(s, abstime);
+ if (res == 0) {
+ COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sem_post, __sanitizer_sem_t *s) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_post, s);
+ COMMON_INTERCEPTOR_RELEASE(ctx, (uptr)s);
+ int res = REAL(sem_post)(s);
+ return res;
+}
+
+INTERCEPTOR(int, sem_getvalue, __sanitizer_sem_t *s, int *sval) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sem_getvalue, s, sval);
+ int res = REAL(sem_getvalue)(s, sval);
+ if (res == 0) {
+ COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sval, sizeof(*sval));
+ }
+ return res;
+}
+#define INIT_SEM \
+ COMMON_INTERCEPT_FUNCTION(sem_init); \
+ COMMON_INTERCEPT_FUNCTION(sem_destroy); \
+ COMMON_INTERCEPT_FUNCTION(sem_wait); \
+ COMMON_INTERCEPT_FUNCTION(sem_trywait); \
+ COMMON_INTERCEPT_FUNCTION(sem_timedwait); \
+ COMMON_INTERCEPT_FUNCTION(sem_post); \
+ COMMON_INTERCEPT_FUNCTION(sem_getvalue);
+#else
+#define INIT_SEM
+#endif // SANITIZER_INTERCEPT_SEM
+
+#if SANITIZER_INTERCEPT_PTHREAD_SETCANCEL
+INTERCEPTOR(int, pthread_setcancelstate, int state, int *oldstate) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcancelstate, state, oldstate);
+ int res = REAL(pthread_setcancelstate)(state, oldstate);
+ if (res == 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldstate, sizeof(*oldstate));
+ return res;
+}
+
+INTERCEPTOR(int, pthread_setcanceltype, int type, int *oldtype) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcanceltype, type, oldtype);
+ int res = REAL(pthread_setcanceltype)(type, oldtype);
+ if (res == 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldtype, sizeof(*oldtype));
+ return res;
+}
+#define INIT_PTHREAD_SETCANCEL \
+ COMMON_INTERCEPT_FUNCTION(pthread_setcancelstate); \
+ COMMON_INTERCEPT_FUNCTION(pthread_setcanceltype);
+#else
+#define INIT_PTHREAD_SETCANCEL
+#endif
+
+#if SANITIZER_INTERCEPT_MINCORE
+INTERCEPTOR(int, mincore, void *addr, uptr length, unsigned char *vec) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, mincore, addr, length, vec);
+ int res = REAL(mincore)(addr, length, vec);
+ if (res == 0) {
+ uptr page_size = GetPageSizeCached();
+ uptr vec_size = ((length + page_size - 1) & (~(page_size - 1))) / page_size;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, vec, vec_size);
+ }
+ return res;
+}
+#define INIT_MINCORE COMMON_INTERCEPT_FUNCTION(mincore);
+#else
+#define INIT_MINCORE
+#endif
+
static void InitializeCommonInterceptors() {
static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
@@ -4740,7 +5243,12 @@ static void InitializeCommonInterceptors() {
INIT_STRNCMP;
INIT_STRCASECMP;
INIT_STRNCASECMP;
+ INIT_STRSTR;
+ INIT_STRCASESTR;
+ INIT_STRSPN;
+ INIT_STRPBRK;
INIT_MEMCHR;
+ INIT_MEMCMP;
INIT_MEMRCHR;
INIT_READ;
INIT_PREAD;
@@ -4760,6 +5268,7 @@ static void InitializeCommonInterceptors() {
INIT_SCANF;
INIT_ISOC99_SCANF;
INIT_PRINTF;
+ INIT_PRINTF_L;
INIT_ISOC99_PRINTF;
INIT_FREXP;
INIT_FREXPF_FREXPL;
@@ -4805,11 +5314,13 @@ static void InitializeCommonInterceptors() {
INIT_MBSNRTOWCS;
INIT_WCSTOMBS;
INIT_WCSNRTOMBS;
+ INIT_WCRTOMB;
INIT_TCGETATTR;
INIT_REALPATH;
INIT_CANONICALIZE_FILE_NAME;
INIT_CONFSTR;
INIT_SCHED_GETAFFINITY;
+ INIT_SCHED_GETPARAM;
INIT_STRERROR;
INIT_STRERROR_R;
INIT_XPG_STRERROR_R;
@@ -4892,4 +5403,8 @@ static void InitializeCommonInterceptors() {
INIT_GETPASS;
INIT_TIMERFD;
INIT_MLOCKX;
+ INIT_FOPENCOOKIE;
+ INIT_SEM;
+ INIT_PTHREAD_SETCANCEL;
+ INIT_MINCORE;
}
diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc
index 26cbe68dc99..9133be7097d 100644
--- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc
+++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_format.inc
@@ -11,6 +11,7 @@
// with a few common GNU extensions.
//
//===----------------------------------------------------------------------===//
+
#include <stdarg.h>
static const char *parse_number(const char *p, int *out) {
@@ -189,7 +190,7 @@ static const char *scanf_parse_next(const char *p, bool allowGnuMalloc,
continue;
}
if (*p == '\0') {
- return 0;
+ return nullptr;
}
// %n$
p = maybe_parse_param_index(p, &dir->argIdx);
@@ -204,7 +205,7 @@ static const char *scanf_parse_next(const char *p, bool allowGnuMalloc,
p = parse_number(p, &dir->fieldWidth);
CHECK(p);
if (dir->fieldWidth <= 0) // Width if at all must be non-zero
- return 0;
+ return nullptr;
}
// m
if (*p == 'm') {
@@ -224,8 +225,8 @@ static const char *scanf_parse_next(const char *p, bool allowGnuMalloc,
while (*p && *p != ']')
++p;
if (*p == 0)
- return 0; // unexpected end of string
- // Consume the closing ']'.
+ return nullptr; // unexpected end of string
+ // Consume the closing ']'.
++p;
}
// This is unfortunately ambiguous between old GNU extension
@@ -249,7 +250,7 @@ static const char *scanf_parse_next(const char *p, bool allowGnuMalloc,
while (*q && *q != ']' && *q != '%')
++q;
if (*q == 0 || *q == '%')
- return 0;
+ return nullptr;
p = q + 1; // Consume the closing ']'.
dir->maybeGnuMalloc = true;
}
@@ -393,7 +394,7 @@ static const char *printf_parse_next(const char *p, PrintfDirective *dir) {
continue;
}
if (*p == '\0') {
- return 0;
+ return nullptr;
}
// %n$
p = maybe_parse_param_index(p, &dir->precisionIdx);
@@ -406,7 +407,7 @@ static const char *printf_parse_next(const char *p, PrintfDirective *dir) {
p = maybe_parse_number_or_star(p, &dir->fieldWidth,
&dir->starredWidth);
if (!p)
- return 0;
+ return nullptr;
// Precision
if (*p == '.') {
++p;
@@ -414,7 +415,7 @@ static const char *printf_parse_next(const char *p, PrintfDirective *dir) {
p = maybe_parse_number_or_star(p, &dir->fieldPrecision,
&dir->starredPrecision);
if (!p)
- return 0;
+ return nullptr;
// m$
if (dir->starredPrecision) {
p = maybe_parse_param_index(p, &dir->precisionIdx);
@@ -554,4 +555,4 @@ static void printf_common(void *ctx, const char *format, va_list aq) {
}
}
-#endif // SANITIZER_INTERCEPT_PRINTF
+#endif // SANITIZER_INTERCEPT_PRINTF
diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
index ca264d899a7..6c5fda09fbf 100755
--- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
+++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
@@ -518,7 +518,7 @@ static const ioctl_desc *ioctl_table_lookup(unsigned req) {
if (left == right && ioctl_table[left].req == req)
return ioctl_table + left;
else
- return 0;
+ return nullptr;
}
static bool ioctl_decode(unsigned req, ioctl_desc *desc) {
@@ -565,7 +565,7 @@ static const ioctl_desc *ioctl_lookup(unsigned req) {
(desc->type == ioctl_desc::READWRITE || desc->type == ioctl_desc::WRITE ||
desc->type == ioctl_desc::READ))
return desc;
- return 0;
+ return nullptr;
}
static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d,
@@ -576,14 +576,10 @@ static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d,
}
if (desc->type != ioctl_desc::CUSTOM)
return;
- switch (request) {
- case 0x00008912: { // SIOCGIFCONF
- struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
- COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len));
- break;
- }
+ if (request == IOCTL_SIOCGIFCONF) {
+ struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len));
}
- return;
}
static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d,
@@ -595,12 +591,8 @@ static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d,
}
if (desc->type != ioctl_desc::CUSTOM)
return;
- switch (request) {
- case 0x00008912: { // SIOCGIFCONF
- struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
- break;
- }
+ if (request == IOCTL_SIOCGIFCONF) {
+ struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
}
- return;
}
diff --git a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc
index b5251444f0f..5a76c4ebd8b 100644
--- a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc
@@ -11,35 +11,31 @@
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
+#include "sanitizer_stackdepot.h"
#include "sanitizer_stacktrace.h"
#include "sanitizer_symbolizer.h"
+#if SANITIZER_POSIX
+#include "sanitizer_posix.h"
+#endif
+
namespace __sanitizer {
-bool PrintsToTty() {
- MaybeOpenReportFile();
- return internal_isatty(report_fd) != 0;
+bool ReportFile::SupportsColors() {
+ SpinMutexLock l(mu);
+ ReopenIfNecessary();
+ return SupportsColoredOutput(fd);
}
-bool PrintsToTtyCached() {
+bool ColorizeReports() {
// FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color
// printing on Windows.
if (SANITIZER_WINDOWS)
- return 0;
-
- static int cached = 0;
- static bool prints_to_tty;
- if (!cached) { // Not thread-safe.
- prints_to_tty = PrintsToTty();
- cached = 1;
- }
- return prints_to_tty;
-}
+ return false;
-bool ColorizeReports() {
const char *flag = common_flags()->color;
return internal_strcmp(flag, "always") == 0 ||
- (internal_strcmp(flag, "auto") == 0 && PrintsToTtyCached());
+ (internal_strcmp(flag, "auto") == 0 && report_file.SupportsColors());
}
static void (*sandboxing_callback)();
@@ -50,16 +46,82 @@ void SetSandboxingCallback(void (*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);
+ if (stack->size == 0) {
+ ReportErrorSummary(error_type);
+ return;
}
+ // 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]);
+ SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
+ ReportErrorSummary(error_type, frame->info);
+ frame->ClearAll();
+}
+
+static void (*SoftRssLimitExceededCallback)(bool exceeded);
+void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) {
+ CHECK_EQ(SoftRssLimitExceededCallback, nullptr);
+ SoftRssLimitExceededCallback = Callback;
+}
+
+void BackgroundThread(void *arg) {
+ uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb;
+ uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb;
+ uptr prev_reported_rss = 0;
+ uptr prev_reported_stack_depot_size = 0;
+ bool reached_soft_rss_limit = false;
+ while (true) {
+ SleepForMillis(100);
+ uptr current_rss_mb = GetRSS() >> 20;
+ if (Verbosity()) {
+ // If RSS has grown 10% since last time, print some information.
+ if (prev_reported_rss * 11 / 10 < current_rss_mb) {
+ Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb);
+ prev_reported_rss = current_rss_mb;
+ }
+ // If stack depot has grown 10% since last time, print it too.
+ StackDepotStats *stack_depot_stats = StackDepotGetStats();
+ if (prev_reported_stack_depot_size * 11 / 10 <
+ stack_depot_stats->allocated) {
+ Printf("%s: StackDepot: %zd ids; %zdM allocated\n",
+ SanitizerToolName,
+ stack_depot_stats->n_uniq_ids,
+ stack_depot_stats->allocated >> 20);
+ prev_reported_stack_depot_size = stack_depot_stats->allocated;
+ }
+ }
+ // Check RSS against the limit.
+ if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) {
+ Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n",
+ SanitizerToolName, hard_rss_limit_mb, current_rss_mb);
+ DumpProcessMap();
+ Die();
+ }
+ if (soft_rss_limit_mb) {
+ if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) {
+ reached_soft_rss_limit = true;
+ Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n",
+ SanitizerToolName, soft_rss_limit_mb, current_rss_mb);
+ if (SoftRssLimitExceededCallback)
+ SoftRssLimitExceededCallback(true);
+ } else if (soft_rss_limit_mb >= current_rss_mb &&
+ reached_soft_rss_limit) {
+ reached_soft_rss_limit = false;
+ if (SoftRssLimitExceededCallback)
+ SoftRssLimitExceededCallback(false);
+ }
+ }
+ }
+}
+
+void MaybeStartBackgroudThread() {
+#if SANITIZER_LINUX // Need to implement/test on other platforms.
+ // Start the background thread if one of the rss limits is given.
+ if (!common_flags()->hard_rss_limit_mb &&
+ !common_flags()->soft_rss_limit_mb) return;
+ if (!&real_pthread_create) return; // Can't spawn the thread anyway.
+ internal_start_thread(BackgroundThread, nullptr);
#endif
- ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
}
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc
index 104c27cd5d2..5e83ce9786c 100644
--- a/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc
+++ b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc
@@ -2297,7 +2297,9 @@ PRE_SYSCALL(ni_syscall)() {}
POST_SYSCALL(ni_syscall)(long res) {}
PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
-#if !SANITIZER_ANDROID && (defined(__i386) || defined (__x86_64))
+#if !SANITIZER_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__))
if (data) {
if (request == ptrace_setregs) {
PRE_READ((void *)data, struct_user_regs_struct_sz);
@@ -2316,7 +2318,9 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
}
POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {
-#if !SANITIZER_ANDROID && (defined(__i386) || defined (__x86_64))
+#if !SANITIZER_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__))
if (res >= 0 && data) {
// Note that this is different from the interceptor in
// sanitizer_common_interceptors.inc.
diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc
index 214e8a98d0f..108a6163d19 100644
--- a/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc
@@ -10,18 +10,24 @@
//
// Compiler instrumentation:
// For every interesting basic block the compiler injects the following code:
-// if (*Guard) {
-// __sanitizer_cov();
-// *Guard = 1;
+// if (Guard < 0) {
+// __sanitizer_cov(&Guard);
// }
+// At the module start up time __sanitizer_cov_module_init sets the guards
+// to consecutive negative numbers (-1, -2, -3, ...).
// 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).
+// and atomically set Guard to -Guard.
// - __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.
+// this will create a file module_name.PID.sancov.
+//
+// The file format is simple: the first 8 bytes is the magic,
+// one of 0xC0BFFFFFFFFFFF64 and 0xC0BFFFFFFFFFFF32. The last byte of the
+// magic defines the size of the following offsets.
+// The rest of the data is the offsets in the module.
//
// Eventually, this coverage implementation should be obsoleted by a more
// powerful general purpose Clang/LLVM coverage instrumentation.
@@ -39,7 +45,12 @@
#include "sanitizer_symbolizer.h"
#include "sanitizer_flags.h"
-atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
+static const u64 kMagic64 = 0xC0BFFFFFFFFFFF64ULL;
+static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL;
+
+static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
+
+static atomic_uintptr_t coverage_counter;
// 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.
@@ -50,29 +61,55 @@ atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
// dump current memory layout to another file.
static bool cov_sandboxed = false;
-static int cov_fd = kInvalidFd;
+static fd_t cov_fd = kInvalidFd;
static unsigned int cov_max_block_size = 0;
+static bool coverage_enabled = false;
+static const char *coverage_dir;
namespace __sanitizer {
class CoverageData {
public:
void Init();
+ void Enable();
+ void Disable();
+ void ReInit();
void BeforeFork();
void AfterFork(int child_pid);
void Extend(uptr npcs);
- void Add(uptr pc);
+ void Add(uptr pc, u32 *guard);
void IndirCall(uptr caller, uptr callee, uptr callee_cache[],
uptr cache_size);
void DumpCallerCalleePairs();
+ void DumpTrace();
+ void DumpAsBitSet();
+ void DumpCounters();
+ void DumpOffsets();
+ void DumpAll();
+
+ ALWAYS_INLINE
+ void TraceBasicBlock(s32 *id);
+
+ void InitializeGuardArray(s32 *guards);
+ void InitializeGuards(s32 *guards, uptr n, const char *module_name,
+ uptr caller_pc);
+ void InitializeCounters(u8 *counters, uptr n);
+ void ReinitializeGuards();
+ uptr GetNumberOf8bitCounters();
+ uptr Update8bitCounterBitsetAndClearCounters(u8 *bitset);
uptr *data();
uptr size();
private:
+ void DirectOpen();
+ void UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end);
+
// 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);
+ static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(
+ 1 << (SANITIZER_ANDROID ? 24 : (SANITIZER_WINDOWS ? 27 : 26)),
+ 1 << 27);
// The amount file mapping for the pc array is grown by.
static const uptr kPcArrayMmapSize = 64 * 1024;
@@ -86,7 +123,27 @@ class CoverageData {
// Current file mapped size of the pc array.
uptr pc_array_mapped_size;
// Descriptor of the file mapped pc array.
- int pc_fd;
+ fd_t pc_fd;
+
+ // Vector of coverage guard arrays, protected by mu.
+ InternalMmapVectorNoCtor<s32*> guard_array_vec;
+
+ struct NamedPcRange {
+ const char *copied_module_name;
+ uptr beg, end; // elements [beg,end) in pc_array.
+ };
+
+ // Vector of module and compilation unit pc ranges.
+ InternalMmapVectorNoCtor<NamedPcRange> comp_unit_name_vec;
+ InternalMmapVectorNoCtor<NamedPcRange> module_name_vec;
+
+ struct CounterAndSize {
+ u8 *counters;
+ uptr n;
+ };
+
+ InternalMmapVectorNoCtor<CounterAndSize> counters_vec;
+ uptr num_8bit_counters;
// Caller-Callee (cc) array, size and current index.
static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
@@ -94,59 +151,131 @@ class CoverageData {
atomic_uintptr_t cc_array_index;
atomic_uintptr_t cc_array_size;
+ // Tracing event array, size and current pointer.
+ // We record all events (basic block entries) in a global buffer of u32
+ // values. Each such value is the index in pc_array.
+ // So far the tracing is highly experimental:
+ // - not thread-safe;
+ // - does not support long traces;
+ // - not tuned for performance.
+ static const uptr kTrEventArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 30);
+ u32 *tr_event_array;
+ uptr tr_event_array_size;
+ u32 *tr_event_pointer;
+ static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27);
StaticSpinMutex mu;
-
- void DirectOpen();
- void ReInit();
};
static CoverageData coverage_data;
+void CovUpdateMapping(const char *path, uptr caller_pc = 0);
+
void CoverageData::DirectOpen() {
- InternalScopedString path(1024);
+ InternalScopedString path(kMaxPathLength);
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());
+ coverage_dir, internal_getpid());
+ pc_fd = OpenFile(path.data(), RdWr);
+ if (pc_fd == kInvalidFd) {
+ Report("Coverage: failed to open %s for reading/writing\n", path.data());
Die();
}
pc_array_mapped_size = 0;
- CovUpdateMapping();
+ CovUpdateMapping(coverage_dir);
}
void CoverageData::Init() {
+ pc_fd = kInvalidFd;
+}
+
+void CoverageData::Enable() {
+ if (pc_array)
+ return;
pc_array = reinterpret_cast<uptr *>(
MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit"));
- pc_fd = kInvalidFd;
+ atomic_store(&pc_array_index, 0, memory_order_relaxed);
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);
}
cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie(
sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array"));
atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
atomic_store(&cc_array_index, 0, memory_order_relaxed);
+
+ // Allocate tr_event_array with a guard page at the end.
+ tr_event_array = reinterpret_cast<u32 *>(MmapNoReserveOrDie(
+ sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(),
+ "CovInit::tr_event_array"));
+ MprotectNoAccess(
+ reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]),
+ GetMmapGranularity());
+ tr_event_array_size = kTrEventArrayMaxSize;
+ tr_event_pointer = tr_event_array;
+
+ num_8bit_counters = 0;
+}
+
+void CoverageData::InitializeGuardArray(s32 *guards) {
+ Enable(); // Make sure coverage is enabled at this point.
+ s32 n = guards[0];
+ for (s32 j = 1; j <= n; j++) {
+ uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed);
+ guards[j] = -static_cast<s32>(idx + 1);
+ }
+}
+
+void CoverageData::Disable() {
+ if (pc_array) {
+ UnmapOrDie(pc_array, sizeof(uptr) * kPcArrayMaxSize);
+ pc_array = nullptr;
+ }
+ if (cc_array) {
+ UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize);
+ cc_array = nullptr;
+ }
+ if (tr_event_array) {
+ UnmapOrDie(tr_event_array,
+ sizeof(tr_event_array[0]) * kTrEventArrayMaxSize +
+ GetMmapGranularity());
+ tr_event_array = nullptr;
+ tr_event_pointer = nullptr;
+ }
+ if (pc_fd != kInvalidFd) {
+ CloseFile(pc_fd);
+ pc_fd = kInvalidFd;
+ }
+}
+
+void CoverageData::ReinitializeGuards() {
+ // Assuming single thread.
+ atomic_store(&pc_array_index, 0, memory_order_relaxed);
+ for (uptr i = 0; i < guard_array_vec.size(); i++)
+ InitializeGuardArray(guard_array_vec[i]);
}
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();
+ Disable();
+ if (coverage_enabled) {
+ 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);
+ uptr npcs = size / sizeof(uptr);
+ Enable();
+ if (size) Extend(npcs);
+ if (coverage_enabled) CovUpdateMapping(coverage_dir);
+ } else {
+ Enable();
+ }
}
+ // Re-initialize the guards.
+ // We are single-threaded now, no need to grab any lock.
+ CHECK_EQ(atomic_load(&pc_array_index, memory_order_relaxed), 0);
+ ReinitializeGuards();
}
void CoverageData::BeforeFork() {
@@ -164,15 +293,16 @@ 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) {
+ if (coverage_enabled && size > pc_array_mapped_size) {
+ if (pc_fd == kInvalidFd) DirectOpen();
+ CHECK_NE(pc_fd, kInvalidFd);
+
uptr new_mapped_size = pc_array_mapped_size;
while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize;
+ CHECK_LE(new_mapped_size, sizeof(uptr) * kPcArrayMaxSize);
// Extend the file and map the new space at the end of pc_array.
uptr res = internal_ftruncate(pc_fd, new_mapped_size);
@@ -181,24 +311,100 @@ void CoverageData::Extend(uptr npcs) {
Printf("failed to extend raw coverage file: %d\n", err);
Die();
}
- void *p = MapWritableFileToMemory(pc_array + pc_array_mapped_size,
+
+ uptr next_map_base = ((uptr)pc_array) + pc_array_mapped_size;
+ void *p = MapWritableFileToMemory((void *)next_map_base,
new_mapped_size - pc_array_mapped_size,
pc_fd, pc_array_mapped_size);
- CHECK_EQ(p, pc_array + pc_array_mapped_size);
+ CHECK_EQ((uptr)p, next_map_base);
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) {
+void CoverageData::InitializeCounters(u8 *counters, uptr n) {
+ if (!counters) return;
+ CHECK_EQ(reinterpret_cast<uptr>(counters) % 16, 0);
+ n = RoundUpTo(n, 16); // The compiler must ensure that counters is 16-aligned.
+ SpinMutexLock l(&mu);
+ counters_vec.push_back({counters, n});
+ num_8bit_counters += n;
+}
+
+void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg,
+ uptr range_end) {
+ auto sym = Symbolizer::GetOrInit();
+ if (!sym)
+ return;
+ const char *module_name = sym->GetModuleNameForPc(caller_pc);
+ if (!module_name) return;
+ if (module_name_vec.empty() ||
+ module_name_vec.back().copied_module_name != module_name)
+ module_name_vec.push_back({module_name, range_beg, range_end});
+ else
+ module_name_vec.back().end = range_end;
+}
+
+void CoverageData::InitializeGuards(s32 *guards, uptr n,
+ const char *comp_unit_name,
+ uptr caller_pc) {
+ // The array 'guards' has n+1 elements, we use the element zero
+ // to store 'n'.
+ CHECK_LT(n, 1 << 30);
+ guards[0] = static_cast<s32>(n);
+ InitializeGuardArray(guards);
+ SpinMutexLock l(&mu);
+ uptr range_end = atomic_load(&pc_array_index, memory_order_relaxed);
+ uptr range_beg = range_end - n;
+ comp_unit_name_vec.push_back({comp_unit_name, range_beg, range_end});
+ guard_array_vec.push_back(guards);
+ UpdateModuleNameVec(caller_pc, range_beg, range_end);
+}
+
+static const uptr kBundleCounterBits = 16;
+
+// When coverage_order_pcs==true and SANITIZER_WORDSIZE==64
+// we insert the global counter into the first 16 bits of the PC.
+uptr BundlePcAndCounter(uptr pc, uptr counter) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return pc;
+ static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1;
+ if (counter > kMaxCounter)
+ counter = kMaxCounter;
+ CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits));
+ return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits));
+}
+
+uptr UnbundlePc(uptr bundle) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return bundle;
+ return (bundle << kBundleCounterBits) >> kBundleCounterBits;
+}
+
+uptr UnbundleCounter(uptr bundle) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return 0;
+ return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits);
+}
+
+// If guard is negative, atomically set it to -guard and store the PC in
+// pc_array.
+void CoverageData::Add(uptr pc, u32 *guard) {
+ atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard);
+ s32 guard_value = atomic_load(atomic_guard, memory_order_relaxed);
+ if (guard_value >= 0) return;
+
+ atomic_store(atomic_guard, -guard_value, memory_order_relaxed);
if (!pc_array) return;
- uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed);
+
+ uptr idx = -guard_value - 1;
+ if (idx >= atomic_load(&pc_array_index, memory_order_acquire))
+ return; // May happen after fork when pc_array_index becomes 0.
CHECK_LT(idx * sizeof(uptr),
atomic_load(&pc_array_size, memory_order_acquire));
- pc_array[idx] = pc;
+ uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+ pc_array[idx] = BundlePcAndCounter(pc, counter);
}
// Registers a pair caller=>callee.
@@ -226,13 +432,73 @@ void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[],
for (uptr i = 2; i < cache_size; i++) {
uptr was = 0;
if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee,
- memory_order_seq_cst))
+ memory_order_seq_cst)) {
+ atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
return;
+ }
if (was == callee) // Already have this callee.
return;
}
}
+uptr CoverageData::GetNumberOf8bitCounters() {
+ return num_8bit_counters;
+}
+
+// Map every 8bit counter to a 8-bit bitset and clear the counter.
+uptr CoverageData::Update8bitCounterBitsetAndClearCounters(u8 *bitset) {
+ uptr num_new_bits = 0;
+ uptr cur = 0;
+ // For better speed we map 8 counters to 8 bytes of bitset at once.
+ static const uptr kBatchSize = 8;
+ CHECK_EQ(reinterpret_cast<uptr>(bitset) % kBatchSize, 0);
+ for (uptr i = 0, len = counters_vec.size(); i < len; i++) {
+ u8 *c = counters_vec[i].counters;
+ uptr n = counters_vec[i].n;
+ CHECK_EQ(n % 16, 0);
+ CHECK_EQ(cur % kBatchSize, 0);
+ CHECK_EQ(reinterpret_cast<uptr>(c) % kBatchSize, 0);
+ if (!bitset) {
+ internal_bzero_aligned16(c, n);
+ cur += n;
+ continue;
+ }
+ for (uptr j = 0; j < n; j += kBatchSize, cur += kBatchSize) {
+ CHECK_LT(cur, num_8bit_counters);
+ u64 *pc64 = reinterpret_cast<u64*>(c + j);
+ u64 *pb64 = reinterpret_cast<u64*>(bitset + cur);
+ u64 c64 = *pc64;
+ u64 old_bits_64 = *pb64;
+ u64 new_bits_64 = old_bits_64;
+ if (c64) {
+ *pc64 = 0;
+ for (uptr k = 0; k < kBatchSize; k++) {
+ u64 x = (c64 >> (8 * k)) & 0xff;
+ if (x) {
+ u64 bit = 0;
+ /**/ if (x >= 128) bit = 128;
+ else if (x >= 32) bit = 64;
+ else if (x >= 16) bit = 32;
+ else if (x >= 8) bit = 16;
+ else if (x >= 4) bit = 8;
+ else if (x >= 3) bit = 4;
+ else if (x >= 2) bit = 2;
+ else if (x >= 1) bit = 1;
+ u64 mask = bit << (8 * k);
+ if (!(new_bits_64 & mask)) {
+ num_new_bits++;
+ new_bits_64 |= mask;
+ }
+ }
+ }
+ *pb64 = new_bits_64;
+ }
+ }
+ }
+ CHECK_EQ(cur, num_8bit_counters);
+ return num_new_bits;
+}
+
uptr *CoverageData::data() {
return pc_array;
}
@@ -251,15 +517,15 @@ struct CovHeader {
static void CovWritePacked(int pid, const char *module, const void *blob,
unsigned int blob_size) {
- if (cov_fd < 0) return;
+ if (cov_fd == kInvalidFd) 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);
+ WriteToFile(cov_fd, &header, sizeof(header));
+ WriteToFile(cov_fd, module, module_name_length);
+ WriteToFile(cov_fd, blob, blob_size);
} else {
// Writing to a socket. We want to split the data into appropriately sized
// blocks.
@@ -275,15 +541,14 @@ static void CovWritePacked(int pid, const char *module, const void *blob,
internal_memcpy(block_pos, module, module_name_length);
block_pos += module_name_length;
char *block_data_begin = block_pos;
- char *blob_pos = (char *)blob;
+ const char *blob_pos = (const 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);
+ WriteToFile(cov_fd, block.data(), header_size_with_module + payload_size);
}
}
}
@@ -292,29 +557,77 @@ static void CovWritePacked(int pid, const char *module, const void *blob,
// 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);
+static fd_t CovOpenFile(InternalScopedString *path, bool packed,
+ const char *name, const char *extension = "sancov") {
+ path->clear();
if (!packed) {
CHECK(name);
- internal_snprintf((char *)path.data(), path.size(), "%s/%s.%zd.sancov",
- common_flags()->coverage_dir, name, internal_getpid());
+ path->append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(),
+ extension);
} else {
if (!name)
- internal_snprintf((char *)path.data(), path.size(),
- "%s/%zd.sancov.packed", common_flags()->coverage_dir,
- internal_getpid());
+ path->append("%s/%zd.%s.packed", coverage_dir, internal_getpid(),
+ extension);
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;
+ path->append("%s/%s.%s.packed", coverage_dir, name, extension);
}
+ error_t err;
+ fd_t fd = OpenFile(path->data(), WrOnly, &err);
+ if (fd == kInvalidFd)
+ Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n",
+ path->data(), err);
return fd;
}
+// Dump trace PCs and trace events into two separate files.
+void CoverageData::DumpTrace() {
+ uptr max_idx = tr_event_pointer - tr_event_array;
+ if (!max_idx) return;
+ auto sym = Symbolizer::GetOrInit();
+ if (!sym)
+ return;
+ InternalScopedString out(32 << 20);
+ for (uptr i = 0, n = size(); i < n; i++) {
+ const char *module_name = "<unknown>";
+ uptr module_address = 0;
+ sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name,
+ &module_address);
+ out.append("%s 0x%zx\n", module_name, module_address);
+ }
+ InternalScopedString path(kMaxPathLength);
+ fd_t fd = CovOpenFile(&path, false, "trace-points");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, out.data(), out.length());
+ CloseFile(fd);
+
+ fd = CovOpenFile(&path, false, "trace-compunits");
+ if (fd == kInvalidFd) return;
+ out.clear();
+ for (uptr i = 0; i < comp_unit_name_vec.size(); i++)
+ out.append("%s\n", comp_unit_name_vec[i].copied_module_name);
+ WriteToFile(fd, out.data(), out.length());
+ CloseFile(fd);
+
+ fd = CovOpenFile(&path, false, "trace-events");
+ if (fd == kInvalidFd) return;
+ uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]);
+ u8 *event_bytes = reinterpret_cast<u8*>(tr_event_array);
+ // The trace file could be huge, and may not be written with a single syscall.
+ while (bytes_to_write) {
+ uptr actually_written;
+ if (WriteToFile(fd, event_bytes, bytes_to_write, &actually_written) &&
+ actually_written <= bytes_to_write) {
+ bytes_to_write -= actually_written;
+ event_bytes += actually_written;
+ } else {
+ break;
+ }
+ }
+ CloseFile(fd);
+ VReport(1, " CovDump: Trace: %zd PCs written\n", size());
+ VReport(1, " CovDump: Trace: %zd Events written\n", max_idx);
+}
+
// This function dumps the caller=>callee pairs into a file as a sequence of
// lines like "module_name offset".
void CoverageData::DumpCallerCalleePairs() {
@@ -347,88 +660,166 @@ void CoverageData::DumpCallerCalleePairs() {
callee_module_address);
}
}
- int fd = CovOpenFile(false, "caller-callee");
- if (fd < 0) return;
- internal_write(fd, out.data(), out.length());
- internal_close(fd);
+ InternalScopedString path(kMaxPathLength);
+ fd_t fd = CovOpenFile(&path, false, "caller-callee");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, out.data(), out.length());
+ CloseFile(fd);
VReport(1, " CovDump: %zd caller-callee pairs written\n", total);
}
-// 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));
- }
- const 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);
- }
+// Record the current PC into the event buffer.
+// Every event is a u32 value (index in tr_pc_array_index) so we compute
+// it once and then cache in the provided 'cache' storage.
+//
+// This function will eventually be inlined by the compiler.
+void CoverageData::TraceBasicBlock(s32 *id) {
+ // Will trap here if
+ // 1. coverage is not enabled at run-time.
+ // 2. The array tr_event_array is full.
+ *tr_event_pointer = static_cast<u32>(*id - 1);
+ tr_event_pointer++;
+}
+
+void CoverageData::DumpCounters() {
+ if (!common_flags()->coverage_counters) return;
+ uptr n = coverage_data.GetNumberOf8bitCounters();
+ if (!n) return;
+ InternalScopedBuffer<u8> bitset(n);
+ coverage_data.Update8bitCounterBitsetAndClearCounters(bitset.data());
+ InternalScopedString path(kMaxPathLength);
+
+ for (uptr m = 0; m < module_name_vec.size(); m++) {
+ auto r = module_name_vec[m];
+ CHECK(r.copied_module_name);
+ CHECK_LE(r.beg, r.end);
+ CHECK_LE(r.end, size());
+ const char *base_name = StripModuleName(r.copied_module_name);
+ fd_t fd =
+ CovOpenFile(&path, /* packed */ false, base_name, "counters-sancov");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, bitset.data() + r.beg, r.end - r.beg);
+ CloseFile(fd);
+ VReport(1, " CovDump: %zd counters written for '%s'\n", r.end - r.beg,
+ base_name);
+ }
+}
+
+void CoverageData::DumpAsBitSet() {
+ if (!common_flags()->coverage_bitset) return;
+ if (!size()) return;
+ InternalScopedBuffer<char> out(size());
+ InternalScopedString path(kMaxPathLength);
+ for (uptr m = 0; m < module_name_vec.size(); m++) {
+ uptr n_set_bits = 0;
+ auto r = module_name_vec[m];
+ CHECK(r.copied_module_name);
+ CHECK_LE(r.beg, r.end);
+ CHECK_LE(r.end, size());
+ for (uptr i = r.beg; i < r.end; i++) {
+ uptr pc = UnbundlePc(pc_array[i]);
+ out[i] = pc ? '1' : '0';
+ if (pc)
+ n_set_bits++;
+ }
+ const char *base_name = StripModuleName(r.copied_module_name);
+ fd_t fd = CovOpenFile(&path, /* packed */false, base_name, "bitset-sancov");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, out.data() + r.beg, r.end - r.beg);
+ CloseFile(fd);
+ VReport(1,
+ " CovDump: bitset of %zd bits written for '%s', %zd bits are set\n",
+ r.end - r.beg, base_name, n_set_bits);
+ }
+}
+
+void CoverageData::DumpOffsets() {
+ auto sym = Symbolizer::GetOrInit();
+ if (!common_flags()->coverage_pcs) return;
+ CHECK_NE(sym, nullptr);
+ InternalMmapVector<uptr> offsets(0);
+ InternalScopedString path(kMaxPathLength);
+ for (uptr m = 0; m < module_name_vec.size(); m++) {
+ offsets.clear();
+ uptr num_words_for_magic = SANITIZER_WORDSIZE == 64 ? 1 : 2;
+ for (uptr i = 0; i < num_words_for_magic; i++)
+ offsets.push_back(0);
+ auto r = module_name_vec[m];
+ CHECK(r.copied_module_name);
+ CHECK_LE(r.beg, r.end);
+ CHECK_LE(r.end, size());
+ for (uptr i = r.beg; i < r.end; i++) {
+ uptr pc = UnbundlePc(pc_array[i]);
+ uptr counter = UnbundleCounter(pc_array[i]);
+ if (!pc) continue; // Not visited.
+ uptr offset = 0;
+ sym->GetModuleNameAndOffsetForPC(pc, nullptr, &offset);
+ offsets.push_back(BundlePcAndCounter(offset, counter));
+ }
+
+ CHECK_GE(offsets.size(), num_words_for_magic);
+ SortArray(offsets.data(), offsets.size());
+ for (uptr i = 0; i < offsets.size(); i++)
+ offsets[i] = UnbundlePc(offsets[i]);
+
+ uptr num_offsets = offsets.size() - num_words_for_magic;
+ u64 *magic_p = reinterpret_cast<u64*>(offsets.data());
+ CHECK_EQ(*magic_p, 0ULL);
+ // FIXME: we may want to write 32-bit offsets even in 64-mode
+ // if all the offsets are small enough.
+ *magic_p = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32;
+
+ const char *module_name = StripModuleName(r.copied_module_name);
+ if (cov_sandboxed) {
+ if (cov_fd != kInvalidFd) {
+ CovWritePacked(internal_getpid(), module_name, offsets.data(),
+ offsets.size() * sizeof(offsets[0]));
+ VReport(1, " CovDump: %zd PCs written to packed file\n", num_offsets);
}
+ } else {
+ // One file per module per process.
+ fd_t fd = CovOpenFile(&path, false /* packed */, module_name);
+ if (fd == kInvalidFd) continue;
+ WriteToFile(fd, offsets.data(), offsets.size() * sizeof(offsets[0]));
+ CloseFile(fd);
+ VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), num_offsets);
}
}
- if (cov_fd >= 0)
- internal_close(cov_fd);
- coverage_data.DumpCallerCalleePairs();
-#endif // !SANITIZER_WINDOWS
+ if (cov_fd != kInvalidFd)
+ CloseFile(cov_fd);
+}
+
+void CoverageData::DumpAll() {
+ if (!coverage_enabled || common_flags()->coverage_direct) return;
+ if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
+ return;
+ DumpAsBitSet();
+ DumpCounters();
+ DumpTrace();
+ DumpOffsets();
+ DumpCallerCalleePairs();
}
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
if (!args) return;
- if (!common_flags()->coverage) return;
+ if (!coverage_enabled) 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)
+ if (args->coverage_fd >= 0) {
+ cov_fd = (fd_t)args->coverage_fd;
+ } else {
+ InternalScopedString path(kMaxPathLength);
// Pre-open the file now. The sandbox won't allow us to do it later.
- cov_fd = CovOpenFile(true /* packed */, 0);
+ cov_fd = CovOpenFile(&path, true /* packed */, nullptr);
+ }
}
-int MaybeOpenCovFile(const char *name) {
+fd_t MaybeOpenCovFile(const char *name) {
CHECK(name);
- if (!common_flags()->coverage) return -1;
- return CovOpenFile(true /* packed */, name);
+ if (!coverage_enabled) return kInvalidFd;
+ InternalScopedString path(kMaxPathLength);
+ return CovOpenFile(&path, true /* packed */, name);
}
void CovBeforeFork() {
@@ -439,32 +830,114 @@ void CovAfterFork(int child_pid) {
coverage_data.AfterFork(child_pid);
}
-} // namespace __sanitizer
+static void MaybeDumpCoverage() {
+ if (common_flags()->coverage)
+ __sanitizer_cov_dump();
+}
+
+void InitializeCoverage(bool enabled, const char *dir) {
+ if (coverage_enabled)
+ return; // May happen if two sanitizer enable coverage in the same process.
+ coverage_enabled = enabled;
+ coverage_dir = dir;
+ coverage_data.Init();
+ if (enabled) coverage_data.Enable();
+ if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump);
+ AddDieCallback(MaybeDumpCoverage);
+}
+
+void ReInitializeCoverage(bool enabled, const char *dir) {
+ coverage_enabled = enabled;
+ coverage_dir = dir;
+ coverage_data.ReInit();
+}
+
+void CoverageUpdateMapping() {
+ if (coverage_enabled)
+ CovUpdateMapping(coverage_dir);
+}
+
+} // namespace __sanitizer
extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov() {
- coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()));
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) {
+ coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
+ guard);
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) {
+ atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard);
+ if (static_cast<s32>(
+ __sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) < 0)
+ __sanitizer_cov(guard);
}
SANITIZER_INTERFACE_ATTRIBUTE void
__sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) {
coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
callee, callee_cache16, 16);
}
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
+ coverage_enabled = true;
+ coverage_dir = common_flags()->coverage_dir;
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) {
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
+ coverage_data.DumpAll();
+}
+SANITIZER_INTERFACE_ATTRIBUTE void
+__sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters,
+ const char *comp_unit_name) {
+ coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC());
+ coverage_data.InitializeCounters(counters, npcs);
+ if (!common_flags()->coverage_direct) return;
+ if (SANITIZER_ANDROID && coverage_enabled) {
// dlopen/dlclose interceptors do not work on Android, so we rely on
// Extend() calls to update .sancov.map.
- CovUpdateMapping(GET_CALLER_PC());
+ CovUpdateMapping(coverage_dir, GET_CALLER_PC());
}
coverage_data.Extend(npcs);
}
SANITIZER_INTERFACE_ATTRIBUTE
sptr __sanitizer_maybe_open_cov_file(const char *name) {
- return MaybeOpenCovFile(name);
+ return (sptr)MaybeOpenCovFile(name);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_total_unique_coverage() {
+ return atomic_load(&coverage_counter, memory_order_relaxed);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_func_enter(s32 *id) {
+ coverage_data.TraceBasicBlock(id);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cov_trace_basic_block(s32 *id) {
+ coverage_data.TraceBasicBlock(id);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_reset_coverage() {
+ coverage_data.ReinitializeGuards();
+ internal_bzero_aligned16(
+ coverage_data.data(),
+ RoundUpTo(coverage_data.size() * sizeof(coverage_data.data()[0]), 16));
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_coverage_guards(uptr **data) {
+ *data = coverage_data.data();
+ return coverage_data.size();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_number_of_counters() {
+ return coverage_data.GetNumberOf8bitCounters();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) {
+ return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset);
}
-} // extern "C"
+// Default empty implementations (weak). Users should redefine them.
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_cov_trace_cmp() {}
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_cov_trace_switch() {}
+} // extern "C"
diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
index b88814b8167..ebac6812881 100644
--- a/libsanitizer/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
@@ -33,7 +33,6 @@
namespace __sanitizer {
-static const uptr kMaxNumberOfModules = 1 << 14;
static const uptr kMaxTextSize = 64 * 1024;
struct CachedMapping {
@@ -60,8 +59,8 @@ struct CachedMapping {
static CachedMapping cached_mapping;
static StaticSpinMutex mapping_mu;
-void CovUpdateMapping(uptr caller_pc) {
- if (!common_flags()->coverage || !common_flags()->coverage_direct) return;
+void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) {
+ if (!common_flags()->coverage_direct) return;
SpinMutexLock l(&mapping_mu);
@@ -69,57 +68,58 @@ void CovUpdateMapping(uptr 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) {
- const 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);
+
+ {
+ InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules);
+ CHECK(modules.data());
+ int n_modules = GetListOfModules(modules.data(), kMaxNumberOfModules,
+ /* filter */ nullptr);
+
+ text.append("%d\n", sizeof(uptr) * 8);
+ for (int i = 0; i < n_modules; ++i) {
+ const char *module_name = StripModuleName(modules[i].full_name());
+ uptr base = modules[i].base_address();
+ for (auto iter = modules[i].ranges(); iter.hasNext();) {
+ const auto *range = iter.next();
+ if (range->executable) {
+ uptr start = range->beg;
+ uptr end = range->end;
+ 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);
+ }
}
+ modules[i].clear();
}
}
- int err;
- InternalScopedString tmp_path(64 +
- internal_strlen(common_flags()->coverage_dir));
+ error_t err;
+ InternalScopedString tmp_path(64 + internal_strlen(coverage_dir));
uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(),
- "%s/%zd.sancov.map.tmp", common_flags()->coverage_dir,
- internal_getpid());
+ "%s/%zd.sancov.map.tmp", 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());
+ fd_t map_fd = OpenFile(tmp_path.data(), WrOnly, &err);
+ if (map_fd == kInvalidFd) {
+ Report("Coverage: failed to open %s for writing: %d\n", tmp_path.data(),
+ err);
Die();
}
- res = internal_write(map_fd, text.data(), text.length());
- if (internal_iserror(res, &err)) {
+ if (!WriteToFile(map_fd, text.data(), text.length(), nullptr, &err)) {
Printf("sancov.map write failed: %d\n", err);
Die();
}
- internal_close(map_fd);
+ CloseFile(map_fd);
- InternalScopedString path(64 + internal_strlen(common_flags()->coverage_dir));
+ InternalScopedString path(64 + internal_strlen(coverage_dir));
res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map",
- common_flags()->coverage_dir, internal_getpid());
+ coverage_dir, internal_getpid());
CHECK_LE(res, path.size());
- res = internal_rename(tmp_path.data(), path.data());
- if (internal_iserror(res, &err)) {
+ if (!RenameFile(tmp_path.data(), path.data(), &err)) {
Printf("sancov.map rename failed: %d\n", err);
Die();
}
}
-} // namespace __sanitizer
+} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_deadlock_detector1.cc b/libsanitizer/sanitizer_common/sanitizer_deadlock_detector1.cc
index fc6f5dcfbb4..7a318c963f7 100644
--- a/libsanitizer/sanitizer_common/sanitizer_deadlock_detector1.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_deadlock_detector1.cc
@@ -38,19 +38,20 @@ struct DD : public DDetector {
explicit DD(const DDFlags *flags);
- DDPhysicalThread* CreatePhysicalThread();
- void DestroyPhysicalThread(DDPhysicalThread *pt);
+ DDPhysicalThread *CreatePhysicalThread() override;
+ void DestroyPhysicalThread(DDPhysicalThread *pt) override;
- DDLogicalThread* CreateLogicalThread(u64 ctx);
- void DestroyLogicalThread(DDLogicalThread *lt);
+ DDLogicalThread *CreateLogicalThread(u64 ctx) override;
+ void DestroyLogicalThread(DDLogicalThread *lt) override;
- void MutexInit(DDCallback *cb, DDMutex *m);
- void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock);
- void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock);
- void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock);
- void MutexDestroy(DDCallback *cb, DDMutex *m);
+ void MutexInit(DDCallback *cb, DDMutex *m) override;
+ void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override;
+ void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+ bool trylock) override;
+ void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override;
+ void MutexDestroy(DDCallback *cb, DDMutex *m) override;
- DDReport *GetReport(DDCallback *cb);
+ DDReport *GetReport(DDCallback *cb) override;
void MutexEnsureID(DDLogicalThread *lt, DDMutex *m);
void ReportDeadlock(DDCallback *cb, DDMutex *m);
@@ -68,7 +69,7 @@ DD::DD(const DDFlags *flags)
}
DDPhysicalThread* DD::CreatePhysicalThread() {
- return 0;
+ return nullptr;
}
void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
@@ -178,10 +179,10 @@ void DD::MutexDestroy(DDCallback *cb,
DDReport *DD::GetReport(DDCallback *cb) {
if (!cb->lt->report_pending)
- return 0;
+ return nullptr;
cb->lt->report_pending = false;
return &cb->lt->rep;
}
-} // namespace __sanitizer
-#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
+} // namespace __sanitizer
+#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
diff --git a/libsanitizer/sanitizer_common/sanitizer_deadlock_detector_interface.h b/libsanitizer/sanitizer_common/sanitizer_deadlock_detector_interface.h
index 59d8d93fe13..07c3755155f 100644
--- a/libsanitizer/sanitizer_common/sanitizer_deadlock_detector_interface.h
+++ b/libsanitizer/sanitizer_common/sanitizer_deadlock_detector_interface.h
@@ -70,10 +70,10 @@ struct DDCallback {
struct DDetector {
static DDetector *Create(const DDFlags *flags);
- virtual DDPhysicalThread* CreatePhysicalThread() { return 0; }
+ virtual DDPhysicalThread* CreatePhysicalThread() { return nullptr; }
virtual void DestroyPhysicalThread(DDPhysicalThread *pt) {}
- virtual DDLogicalThread* CreateLogicalThread(u64 ctx) { return 0; }
+ virtual DDLogicalThread* CreateLogicalThread(u64 ctx) { return nullptr; }
virtual void DestroyLogicalThread(DDLogicalThread *lt) {}
virtual void MutexInit(DDCallback *cb, DDMutex *m) {}
@@ -83,7 +83,7 @@ struct DDetector {
virtual void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {}
virtual void MutexDestroy(DDCallback *cb, DDMutex *m) {}
- virtual DDReport *GetReport(DDCallback *cb) { return 0; }
+ virtual DDReport *GetReport(DDCallback *cb) { return nullptr; }
};
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_flag_parser.cc b/libsanitizer/sanitizer_common/sanitizer_flag_parser.cc
new file mode 100644
index 00000000000..1fc6b2edb0c
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_flag_parser.cc
@@ -0,0 +1,169 @@
+//===-- sanitizer_flag_parser.cc ------------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_flag_parser.h"
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_flag_parser.h"
+
+namespace __sanitizer {
+
+LowLevelAllocator FlagParser::Alloc;
+
+class UnknownFlags {
+ static const int kMaxUnknownFlags = 20;
+ const char *unknown_flags_[kMaxUnknownFlags];
+ int n_unknown_flags_;
+
+ public:
+ void Add(const char *name) {
+ CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
+ unknown_flags_[n_unknown_flags_++] = name;
+ }
+
+ void Report() {
+ if (!n_unknown_flags_) return;
+ Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_);
+ for (int i = 0; i < n_unknown_flags_; ++i)
+ Printf(" %s\n", unknown_flags_[i]);
+ n_unknown_flags_ = 0;
+ }
+};
+
+UnknownFlags unknown_flags;
+
+void ReportUnrecognizedFlags() {
+ unknown_flags.Report();
+}
+
+char *FlagParser::ll_strndup(const char *s, uptr n) {
+ uptr len = internal_strnlen(s, n);
+ char *s2 = (char*)Alloc.Allocate(len + 1);
+ internal_memcpy(s2, s, len);
+ s2[len] = 0;
+ return s2;
+}
+
+void FlagParser::PrintFlagDescriptions() {
+ Printf("Available flags for %s:\n", SanitizerToolName);
+ for (int i = 0; i < n_flags_; ++i)
+ Printf("\t%s\n\t\t- %s\n", flags_[i].name, flags_[i].desc);
+}
+
+void FlagParser::fatal_error(const char *err) {
+ Printf("ERROR: %s\n", err);
+ Die();
+}
+
+bool FlagParser::is_space(char c) {
+ return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
+ c == '\r';
+}
+
+void FlagParser::skip_whitespace() {
+ while (is_space(buf_[pos_])) ++pos_;
+}
+
+void FlagParser::parse_flag() {
+ uptr name_start = pos_;
+ while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
+ if (buf_[pos_] != '=') fatal_error("expected '='");
+ char *name = ll_strndup(buf_ + name_start, pos_ - name_start);
+
+ uptr value_start = ++pos_;
+ char *value;
+ if (buf_[pos_] == '\'' || buf_[pos_] == '"') {
+ char quote = buf_[pos_++];
+ while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_;
+ if (buf_[pos_] == 0) fatal_error("unterminated string");
+ value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
+ ++pos_; // consume the closing quote
+ } else {
+ while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_;
+ if (buf_[pos_] != 0 && !is_space(buf_[pos_]))
+ fatal_error("expected separator or eol");
+ value = ll_strndup(buf_ + value_start, pos_ - value_start);
+ }
+
+ bool res = run_handler(name, value);
+ if (!res) fatal_error("Flag parsing failed.");
+}
+
+void FlagParser::parse_flags() {
+ while (true) {
+ skip_whitespace();
+ if (buf_[pos_] == 0) break;
+ parse_flag();
+ }
+
+ // Do a sanity check for certain flags.
+ if (common_flags_dont_use.malloc_context_size < 1)
+ common_flags_dont_use.malloc_context_size = 1;
+}
+
+void FlagParser::ParseString(const char *s) {
+ if (!s) return;
+ // Backup current parser state to allow nested ParseString() calls.
+ const char *old_buf_ = buf_;
+ uptr old_pos_ = pos_;
+ buf_ = s;
+ pos_ = 0;
+
+ parse_flags();
+
+ buf_ = old_buf_;
+ pos_ = old_pos_;
+}
+
+bool FlagParser::ParseFile(const char *path, bool ignore_missing) {
+ static const uptr kMaxIncludeSize = 1 << 15;
+ char *data;
+ uptr data_mapped_size;
+ error_t err;
+ uptr len;
+ if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len,
+ Max(kMaxIncludeSize, GetPageSizeCached()), &err)) {
+ if (ignore_missing)
+ return true;
+ Printf("Failed to read options from '%s': error %d\n", path, err);
+ return false;
+ }
+ ParseString(data);
+ UnmapOrDie(data, data_mapped_size);
+ return true;
+}
+
+bool FlagParser::run_handler(const char *name, const char *value) {
+ for (int i = 0; i < n_flags_; ++i) {
+ if (internal_strcmp(name, flags_[i].name) == 0)
+ return flags_[i].handler->Parse(value);
+ }
+ // Unrecognized flag. This is not a fatal error, we may print a warning later.
+ unknown_flags.Add(name);
+ return true;
+}
+
+void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
+ const char *desc) {
+ CHECK_LT(n_flags_, kMaxFlags);
+ flags_[n_flags_].name = name;
+ flags_[n_flags_].desc = desc;
+ flags_[n_flags_].handler = handler;
+ ++n_flags_;
+}
+
+FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
+ flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
+}
+
+} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_flag_parser.h b/libsanitizer/sanitizer_common/sanitizer_flag_parser.h
new file mode 100644
index 00000000000..7827d735770
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_flag_parser.h
@@ -0,0 +1,120 @@
+//===-- sanitizer_flag_parser.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 ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_FLAG_REGISTRY_H
+#define SANITIZER_FLAG_REGISTRY_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+class FlagHandlerBase {
+ public:
+ virtual bool Parse(const char *value) { return false; }
+};
+
+template <typename T>
+class FlagHandler : public FlagHandlerBase {
+ T *t_;
+
+ public:
+ explicit FlagHandler(T *t) : t_(t) {}
+ bool Parse(const char *value) final;
+};
+
+template <>
+inline bool FlagHandler<bool>::Parse(const char *value) {
+ if (internal_strcmp(value, "0") == 0 ||
+ internal_strcmp(value, "no") == 0 ||
+ internal_strcmp(value, "false") == 0) {
+ *t_ = false;
+ return true;
+ }
+ if (internal_strcmp(value, "1") == 0 ||
+ internal_strcmp(value, "yes") == 0 ||
+ internal_strcmp(value, "true") == 0) {
+ *t_ = true;
+ return true;
+ }
+ Printf("ERROR: Invalid value for bool option: '%s'\n", value);
+ return false;
+}
+
+template <>
+inline bool FlagHandler<const char *>::Parse(const char *value) {
+ *t_ = internal_strdup(value);
+ return true;
+}
+
+template <>
+inline bool FlagHandler<int>::Parse(const char *value) {
+ char *value_end;
+ *t_ = internal_simple_strtoll(value, &value_end, 10);
+ bool ok = *value_end == 0;
+ if (!ok) Printf("ERROR: Invalid value for int option: '%s'\n", value);
+ return ok;
+}
+
+template <>
+inline bool FlagHandler<uptr>::Parse(const char *value) {
+ char *value_end;
+ *t_ = internal_simple_strtoll(value, &value_end, 10);
+ bool ok = *value_end == 0;
+ if (!ok) Printf("ERROR: Invalid value for uptr option: '%s'\n", value);
+ return ok;
+}
+
+class FlagParser {
+ static const int kMaxFlags = 200;
+ struct Flag {
+ const char *name;
+ const char *desc;
+ FlagHandlerBase *handler;
+ } *flags_;
+ int n_flags_;
+
+ const char *buf_;
+ uptr pos_;
+
+ public:
+ FlagParser();
+ void RegisterHandler(const char *name, FlagHandlerBase *handler,
+ const char *desc);
+ void ParseString(const char *s);
+ bool ParseFile(const char *path, bool ignore_missing);
+ void PrintFlagDescriptions();
+
+ static LowLevelAllocator Alloc;
+
+ private:
+ void fatal_error(const char *err);
+ bool is_space(char c);
+ void skip_whitespace();
+ void parse_flags();
+ void parse_flag();
+ bool run_handler(const char *name, const char *value);
+ char *ll_strndup(const char *s, uptr n);
+};
+
+template <typename T>
+static void RegisterFlag(FlagParser *parser, const char *name, const char *desc,
+ T *var) {
+ FlagHandler<T> *fh = new (FlagParser::Alloc) FlagHandler<T>(var); // NOLINT
+ parser->RegisterHandler(name, fh, desc);
+}
+
+void ReportUnrecognizedFlags();
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FLAG_REGISTRY_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.cc b/libsanitizer/sanitizer_common/sanitizer_flags.cc
index d7e7118212e..a24ff900011 100644
--- a/libsanitizer/sanitizer_common/sanitizer_flags.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_flags.cc
@@ -14,6 +14,7 @@
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
#include "sanitizer_list.h"
+#include "sanitizer_flag_parser.h"
namespace __sanitizer {
@@ -32,274 +33,68 @@ IntrusiveList<FlagDescription> flag_descriptions;
# define SANITIZER_NEEDS_SEGV 1
#endif
-void SetCommonFlagsDefaults(CommonFlags *f) {
- f->symbolize = true;
- f->external_symbolizer_path = 0;
- f->allow_addr2line = false;
- f->strip_path_prefix = "";
- f->fast_unwind_on_check = false;
- f->fast_unwind_on_fatal = false;
- f->fast_unwind_on_malloc = true;
- f->handle_ioctl = false;
- f->malloc_context_size = 1;
- f->log_path = "stderr";
- f->verbosity = 0;
- f->detect_leaks = true;
- f->leak_check_at_exit = true;
- f->allocator_may_return_null = false;
- f->print_summary = true;
- f->check_printf = true;
- // TODO(glider): tools may want to set different defaults for handle_segv.
- f->handle_segv = SANITIZER_NEEDS_SEGV;
- f->allow_user_segv_handler = false;
- f->use_sigaltstack = true;
- f->detect_deadlocks = false;
- f->clear_shadow_mmap_threshold = 64 * 1024;
- f->color = "auto";
- f->legacy_pthread_cond = false;
- f->intercept_tls_get_addr = false;
- f->coverage = false;
- f->coverage_direct = SANITIZER_ANDROID;
- f->coverage_dir = ".";
- f->full_address_space = false;
- f->suppressions = "";
- f->print_suppressions = true;
- f->disable_coredump = (SANITIZER_WORDSIZE == 64);
- f->symbolize_inline_frames = true;
- f->stack_trace_format = "DEFAULT";
+void CommonFlags::SetDefaults() {
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
}
-void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
- ParseFlag(str, &f->symbolize, "symbolize",
- "If set, use the online symbolizer from common sanitizer runtime to turn "
- "virtual addresses to file/line locations.");
- ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path",
- "Path to external symbolizer. If empty, the tool will search $PATH for "
- "the symbolizer.");
- ParseFlag(str, &f->allow_addr2line, "allow_addr2line",
- "If set, allows online symbolizer to run addr2line binary to symbolize "
- "stack traces (addr2line will only be used if llvm-symbolizer binary is "
- "unavailable.");
- ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix",
- "Strips this prefix from file paths in error reports.");
- ParseFlag(str, &f->fast_unwind_on_check, "fast_unwind_on_check",
- "If available, use the fast frame-pointer-based unwinder on "
- "internal CHECK failures.");
- ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal",
- "If available, use the fast frame-pointer-based unwinder on fatal "
- "errors.");
- ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc",
- "If available, use the fast frame-pointer-based unwinder on "
- "malloc/free.");
- ParseFlag(str, &f->handle_ioctl, "handle_ioctl",
- "Intercept and handle ioctl requests.");
- ParseFlag(str, &f->malloc_context_size, "malloc_context_size",
- "Max number of stack frames kept for each allocation/deallocation.");
- ParseFlag(str, &f->log_path, "log_path",
- "Write logs to \"log_path.pid\". The special values are \"stdout\" and "
- "\"stderr\". The default is \"stderr\".");
- ParseFlag(str, &f->verbosity, "verbosity",
- "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).");
- ParseFlag(str, &f->detect_leaks, "detect_leaks",
- "Enable memory leak detection.");
- ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit",
- "Invoke leak checking in an atexit handler. Has no effect if "
- "detect_leaks=false, or if __lsan_do_leak_check() is called before the "
- "handler has a chance to run.");
- ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null",
- "If false, the allocator will crash instead of returning 0 on "
- "out-of-memory.");
- ParseFlag(str, &f->print_summary, "print_summary",
- "If false, disable printing error summaries in addition to error "
- "reports.");
- ParseFlag(str, &f->check_printf, "check_printf",
- "Check printf arguments.");
- ParseFlag(str, &f->handle_segv, "handle_segv",
- "If set, registers the tool's custom SEGV handler (both SIGBUS and "
- "SIGSEGV on OSX).");
- ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler",
- "If set, allows user to register a SEGV handler even if the tool "
- "registers one.");
- ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack",
- "If set, uses alternate stack for signal handling.");
- ParseFlag(str, &f->detect_deadlocks, "detect_deadlocks",
- "If set, deadlock detection is enabled.");
- ParseFlag(str, &f->clear_shadow_mmap_threshold,
- "clear_shadow_mmap_threshold",
- "Large shadow regions are zero-filled using mmap(NORESERVE) instead of "
- "memset(). This is the threshold size in bytes.");
- ParseFlag(str, &f->color, "color",
- "Colorize reports: (always|never|auto).");
- ParseFlag(str, &f->legacy_pthread_cond, "legacy_pthread_cond",
- "Enables support for dynamic libraries linked with libpthread 2.2.5.");
- ParseFlag(str, &f->intercept_tls_get_addr, "intercept_tls_get_addr",
- "Intercept __tls_get_addr.");
- ParseFlag(str, &f->help, "help", "Print the flag descriptions.");
- ParseFlag(str, &f->mmap_limit_mb, "mmap_limit_mb",
- "Limit the amount of mmap-ed memory (excluding shadow) in Mb; "
- "not a user-facing flag, used mosly for testing the tools");
- ParseFlag(str, &f->coverage, "coverage",
- "If set, coverage information will be dumped at program shutdown (if the "
- "coverage instrumentation was enabled at compile time).");
- ParseFlag(str, &f->coverage_direct, "coverage_direct",
- "If set, coverage information will be dumped directly to a memory "
- "mapped file. This way data is not lost even if the process is "
- "suddenly killed.");
- ParseFlag(str, &f->coverage_dir, "coverage_dir",
- "Target directory for coverage dumps. Defaults to the current "
- "directory.");
- ParseFlag(str, &f->full_address_space, "full_address_space",
- "Sanitize complete address space; "
- "by default kernel area on 32-bit platforms will not be sanitized");
- ParseFlag(str, &f->suppressions, "suppressions", "Suppressions file name.");
- ParseFlag(str, &f->print_suppressions, "print_suppressions",
- "Print matched suppressions at exit.");
- ParseFlag(str, &f->disable_coredump, "disable_coredump",
- "Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
- "dumping a 16T+ core file. Ignored on OSes that don't dump core by"
- "default and for sanitizers that don't reserve lots of virtual memory.");
- ParseFlag(str, &f->symbolize_inline_frames, "symbolize_inline_frames",
- "Print inlined frames in stacktraces. Defaults to true.");
- ParseFlag(str, &f->stack_trace_format, "stack_trace_format",
- "Format string used to render stack frames. "
- "See sanitizer_stacktrace_printer.h for the format description. "
- "Use DEFAULT to get default format.");
-
- // Do a sanity check for certain flags.
- if (f->malloc_context_size < 1)
- f->malloc_context_size = 1;
-}
-
-static bool GetFlagValue(const char *env, const char *name,
- const char **value, int *value_length) {
- if (env == 0)
- return false;
- const char *pos = 0;
- for (;;) {
- pos = internal_strstr(env, name);
- if (pos == 0)
- return false;
- const char *name_end = pos + internal_strlen(name);
- if ((pos != env &&
- ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) ||
- *name_end != '=') {
- // Seems to be middle of another flag name or value.
- env = pos + 1;
- continue;
- }
- pos = name_end;
- break;
- }
- const char *end;
- if (pos[0] != '=') {
- end = pos;
- } else {
- pos += 1;
- if (pos[0] == '"') {
- pos += 1;
- end = internal_strchr(pos, '"');
- } else if (pos[0] == '\'') {
- pos += 1;
- end = internal_strchr(pos, '\'');
- } else {
- // Read until the next space or colon.
- end = pos + internal_strcspn(pos, " :");
- }
- if (end == 0)
- end = pos + internal_strlen(pos);
- }
- *value = pos;
- *value_length = end - pos;
- return true;
-}
-
-static bool StartsWith(const char *flag, int flag_length, const char *value) {
- if (!flag || !value)
- return false;
- int value_length = internal_strlen(value);
- return (flag_length >= value_length) &&
- (0 == internal_strncmp(flag, value, value_length));
+void CommonFlags::CopyFrom(const CommonFlags &other) {
+ internal_memcpy(this, &other, sizeof(*this));
}
-static LowLevelAllocator allocator_for_flags;
-
-// The linear scan is suboptimal, but the number of flags is relatively small.
-bool FlagInDescriptionList(const char *name) {
- IntrusiveList<FlagDescription>::Iterator it(&flag_descriptions);
- while (it.hasNext()) {
- if (!internal_strcmp(it.next()->name, name)) return true;
+// Copy the string from "s" to "out", replacing "%b" with the binary basename.
+static void SubstituteBinaryName(const char *s, char *out, uptr out_size) {
+ char *out_end = out + out_size;
+ while (*s && out < out_end - 1) {
+ if (s[0] != '%' || s[1] != 'b') { *out++ = *s++; continue; }
+ const char *base = GetProcessName();
+ CHECK(base);
+ while (*base && out < out_end - 1)
+ *out++ = *base++;
+ s += 2; // skip "%b"
}
- return false;
-}
-
-void AddFlagDescription(const char *name, const char *description) {
- if (FlagInDescriptionList(name)) return;
- FlagDescription *new_description = new(allocator_for_flags) FlagDescription;
- new_description->name = name;
- new_description->description = description;
- flag_descriptions.push_back(new_description);
+ *out = '\0';
}
-// TODO(glider): put the descriptions inside CommonFlags.
-void PrintFlagDescriptions() {
- IntrusiveList<FlagDescription>::Iterator it(&flag_descriptions);
- Printf("Available flags for %s:\n", SanitizerToolName);
- while (it.hasNext()) {
- FlagDescription *descr = it.next();
- Printf("\t%s\n\t\t- %s\n", descr->name, descr->description);
+class FlagHandlerInclude : public FlagHandlerBase {
+ FlagParser *parser_;
+ bool ignore_missing_;
+
+ public:
+ explicit FlagHandlerInclude(FlagParser *parser, bool ignore_missing)
+ : parser_(parser), ignore_missing_(ignore_missing) {}
+ bool Parse(const char *value) final {
+ if (internal_strchr(value, '%')) {
+ char *buf = (char *)MmapOrDie(kMaxPathLength, "FlagHandlerInclude");
+ SubstituteBinaryName(value, buf, kMaxPathLength);
+ bool res = parser_->ParseFile(buf, ignore_missing_);
+ UnmapOrDie(buf, kMaxPathLength);
+ return res;
+ }
+ return parser_->ParseFile(value, ignore_missing_);
}
-}
-
-void ParseFlag(const char *env, bool *flag,
- const char *name, const char *descr) {
- const char *value;
- int value_length;
- AddFlagDescription(name, descr);
- if (!GetFlagValue(env, name, &value, &value_length))
- return;
- if (StartsWith(value, value_length, "0") ||
- StartsWith(value, value_length, "no") ||
- StartsWith(value, value_length, "false"))
- *flag = false;
- if (StartsWith(value, value_length, "1") ||
- StartsWith(value, value_length, "yes") ||
- StartsWith(value, value_length, "true"))
- *flag = true;
-}
+};
-void ParseFlag(const char *env, int *flag,
- const char *name, const char *descr) {
- const char *value;
- int value_length;
- AddFlagDescription(name, descr);
- if (!GetFlagValue(env, name, &value, &value_length))
- return;
- *flag = static_cast<int>(internal_atoll(value));
+void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf) {
+ FlagHandlerInclude *fh_include = new (FlagParser::Alloc) // NOLINT
+ FlagHandlerInclude(parser, /*ignore_missing*/ false);
+ parser->RegisterHandler("include", fh_include,
+ "read more options from the given file");
+ FlagHandlerInclude *fh_include_if_exists = new (FlagParser::Alloc) // NOLINT
+ FlagHandlerInclude(parser, /*ignore_missing*/ true);
+ parser->RegisterHandler(
+ "include_if_exists", fh_include_if_exists,
+ "read more options from the given file (if it exists)");
}
-void ParseFlag(const char *env, uptr *flag,
- const char *name, const char *descr) {
- const char *value;
- int value_length;
- AddFlagDescription(name, descr);
- if (!GetFlagValue(env, name, &value, &value_length))
- return;
- *flag = static_cast<uptr>(internal_atoll(value));
-}
+void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) {
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &cf->Name);
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
-void ParseFlag(const char *env, const char **flag,
- const char *name, const char *descr) {
- const char *value;
- int value_length;
- AddFlagDescription(name, descr);
- if (!GetFlagValue(env, name, &value, &value_length))
- return;
- // Copy the flag value. Don't use locks here, as flags are parsed at
- // tool startup.
- char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1));
- internal_memcpy(value_copy, value, value_length);
- value_copy[value_length] = '\0';
- *flag = value_copy;
+ RegisterIncludeFlags(parser, cf);
}
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.h b/libsanitizer/sanitizer_common/sanitizer_flags.h
index b7a50131811..f1b872b79e2 100644
--- a/libsanitizer/sanitizer_common/sanitizer_flags.h
+++ b/libsanitizer/sanitizer_common/sanitizer_flags.h
@@ -16,62 +16,38 @@
namespace __sanitizer {
-void ParseFlag(const char *env, bool *flag,
- const char *name, const char *descr);
-void ParseFlag(const char *env, int *flag,
- const char *name, const char *descr);
-void ParseFlag(const char *env, uptr *flag,
- const char *name, const char *descr);
-void ParseFlag(const char *env, const char **flag,
- const char *name, const char *descr);
-
struct CommonFlags {
- bool symbolize;
- const char *external_symbolizer_path;
- bool allow_addr2line;
- const char *strip_path_prefix;
- bool fast_unwind_on_check;
- bool fast_unwind_on_fatal;
- bool fast_unwind_on_malloc;
- bool handle_ioctl;
- int malloc_context_size;
- const char *log_path;
- int verbosity;
- bool detect_leaks;
- bool leak_check_at_exit;
- bool allocator_may_return_null;
- bool print_summary;
- bool check_printf;
- bool handle_segv;
- bool allow_user_segv_handler;
- bool use_sigaltstack;
- bool detect_deadlocks;
- uptr clear_shadow_mmap_threshold;
- const char *color;
- bool legacy_pthread_cond;
- bool intercept_tls_get_addr;
- bool help;
- uptr mmap_limit_mb;
- bool coverage;
- bool coverage_direct;
- const char *coverage_dir;
- bool full_address_space;
- const char *suppressions;
- bool print_suppressions;
- bool disable_coredump;
- bool symbolize_inline_frames;
- const char *stack_trace_format;
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
+
+ void SetDefaults();
+ void CopyFrom(const CommonFlags &other);
};
-inline CommonFlags *common_flags() {
- extern CommonFlags common_flags_dont_use;
+// Functions to get/set global CommonFlags shared by all sanitizer runtimes:
+extern CommonFlags common_flags_dont_use;
+inline const CommonFlags *common_flags() {
return &common_flags_dont_use;
}
-void SetCommonFlagsDefaults(CommonFlags *f);
-void ParseCommonFlagsFromString(CommonFlags *f, const char *str);
-void PrintFlagDescriptions();
+inline void SetCommonFlagsDefaults() {
+ common_flags_dont_use.SetDefaults();
+}
+
+// This function can only be used to setup tool-specific overrides for
+// CommonFlags defaults. Generally, it should only be used right after
+// SetCommonFlagsDefaults(), but before ParseCommonFlagsFromString(), and
+// only during the flags initialization (i.e. before they are used for
+// the first time).
+inline void OverrideCommonFlags(const CommonFlags &cf) {
+ common_flags_dont_use.CopyFrom(cf);
+}
+class FlagParser;
+void RegisterCommonFlags(FlagParser *parser,
+ CommonFlags *cf = &common_flags_dont_use);
+void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf);
} // namespace __sanitizer
#endif // SANITIZER_FLAGS_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.inc b/libsanitizer/sanitizer_common/sanitizer_flags.inc
new file mode 100644
index 00000000000..bca1c4b4d0f
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_flags.inc
@@ -0,0 +1,192 @@
+//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes common flags available in all sanitizers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef COMMON_FLAG
+#error "Define COMMON_FLAG prior to including this file!"
+#endif
+
+// COMMON_FLAG(Type, Name, DefaultValue, Description)
+// Supported types: bool, const char *, int, uptr.
+// Default value must be a compile-time constant.
+// Description must be a string literal.
+
+COMMON_FLAG(
+ bool, symbolize, true,
+ "If set, use the online symbolizer from common sanitizer runtime to turn "
+ "virtual addresses to file/line locations.")
+COMMON_FLAG(
+ const char *, external_symbolizer_path, nullptr,
+ "Path to external symbolizer. If empty, the tool will search $PATH for "
+ "the symbolizer.")
+COMMON_FLAG(
+ bool, allow_addr2line, false,
+ "If set, allows online symbolizer to run addr2line binary to symbolize "
+ "stack traces (addr2line will only be used if llvm-symbolizer binary is "
+ "unavailable.")
+COMMON_FLAG(const char *, strip_path_prefix, "",
+ "Strips this prefix from file paths in error reports.")
+COMMON_FLAG(bool, fast_unwind_on_check, false,
+ "If available, use the fast frame-pointer-based unwinder on "
+ "internal CHECK failures.")
+COMMON_FLAG(bool, fast_unwind_on_fatal, false,
+ "If available, use the fast frame-pointer-based unwinder on fatal "
+ "errors.")
+COMMON_FLAG(bool, fast_unwind_on_malloc, true,
+ "If available, use the fast frame-pointer-based unwinder on "
+ "malloc/free.")
+COMMON_FLAG(bool, handle_ioctl, false, "Intercept and handle ioctl requests.")
+COMMON_FLAG(int, malloc_context_size, 1,
+ "Max number of stack frames kept for each allocation/deallocation.")
+COMMON_FLAG(
+ const char *, log_path, "stderr",
+ "Write logs to \"log_path.pid\". The special values are \"stdout\" and "
+ "\"stderr\". The default is \"stderr\".")
+COMMON_FLAG(
+ bool, log_exe_name, false,
+ "Mention name of executable when reporting error and "
+ "append executable name to logs (as in \"log_path.exe_name.pid\").")
+COMMON_FLAG(
+ bool, log_to_syslog, SANITIZER_ANDROID,
+ "Write all sanitizer output to syslog in addition to other means of "
+ "logging.")
+COMMON_FLAG(
+ int, verbosity, 0,
+ "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).")
+COMMON_FLAG(bool, detect_leaks, true, "Enable memory leak detection.")
+COMMON_FLAG(
+ bool, leak_check_at_exit, true,
+ "Invoke leak checking in an atexit handler. Has no effect if "
+ "detect_leaks=false, or if __lsan_do_leak_check() is called before the "
+ "handler has a chance to run.")
+COMMON_FLAG(bool, allocator_may_return_null, false,
+ "If false, the allocator will crash instead of returning 0 on "
+ "out-of-memory.")
+COMMON_FLAG(bool, print_summary, true,
+ "If false, disable printing error summaries in addition to error "
+ "reports.")
+COMMON_FLAG(bool, check_printf, true, "Check printf arguments.")
+COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV,
+ "If set, registers the tool's custom SIGSEGV/SIGBUS handler.")
+COMMON_FLAG(bool, handle_abort, false,
+ "If set, registers the tool's custom SIGABRT handler.")
+COMMON_FLAG(bool, handle_sigfpe, true,
+ "If set, registers the tool's custom SIGFPE handler.")
+COMMON_FLAG(bool, allow_user_segv_handler, false,
+ "If set, allows user to register a SEGV handler even if the tool "
+ "registers one.")
+COMMON_FLAG(bool, use_sigaltstack, true,
+ "If set, uses alternate stack for signal handling.")
+COMMON_FLAG(bool, detect_deadlocks, false,
+ "If set, deadlock detection is enabled.")
+COMMON_FLAG(
+ uptr, clear_shadow_mmap_threshold, 64 * 1024,
+ "Large shadow regions are zero-filled using mmap(NORESERVE) instead of "
+ "memset(). This is the threshold size in bytes.")
+COMMON_FLAG(const char *, color, "auto",
+ "Colorize reports: (always|never|auto).")
+COMMON_FLAG(
+ bool, legacy_pthread_cond, false,
+ "Enables support for dynamic libraries linked with libpthread 2.2.5.")
+COMMON_FLAG(bool, intercept_tls_get_addr, false, "Intercept __tls_get_addr.")
+COMMON_FLAG(bool, help, false, "Print the flag descriptions.")
+COMMON_FLAG(uptr, mmap_limit_mb, 0,
+ "Limit the amount of mmap-ed memory (excluding shadow) in Mb; "
+ "not a user-facing flag, used mosly for testing the tools")
+COMMON_FLAG(uptr, hard_rss_limit_mb, 0,
+ "Hard RSS limit in Mb."
+ " If non-zero, a background thread is spawned at startup"
+ " which periodically reads RSS and aborts the process if the"
+ " limit is reached")
+COMMON_FLAG(uptr, soft_rss_limit_mb, 0,
+ "Soft RSS limit in Mb."
+ " If non-zero, a background thread is spawned at startup"
+ " which periodically reads RSS. If the limit is reached"
+ " all subsequent malloc/new calls will fail or return NULL"
+ " (depending on the value of allocator_may_return_null)"
+ " until the RSS goes below the soft limit."
+ " This limit does not affect memory allocations other than"
+ " malloc/new.")
+COMMON_FLAG(bool, can_use_proc_maps_statm, true,
+ "If false, do not attempt to read /proc/maps/statm."
+ " Mostly useful for testing sanitizers.")
+COMMON_FLAG(
+ bool, coverage, false,
+ "If set, coverage information will be dumped at program shutdown (if the "
+ "coverage instrumentation was enabled at compile time).")
+COMMON_FLAG(bool, coverage_pcs, true,
+ "If set (and if 'coverage' is set too), the coverage information "
+ "will be dumped as a set of PC offsets for every module.")
+COMMON_FLAG(bool, coverage_order_pcs, false,
+ "If true, the PCs will be dumped in the order they've"
+ " appeared during the execution.")
+COMMON_FLAG(bool, coverage_bitset, false,
+ "If set (and if 'coverage' is set too), the coverage information "
+ "will also be dumped as a bitset to a separate file.")
+COMMON_FLAG(bool, coverage_counters, false,
+ "If set (and if 'coverage' is set too), the bitmap that corresponds"
+ " to coverage counters will be dumped.")
+COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID,
+ "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.")
+COMMON_FLAG(const char *, coverage_dir, ".",
+ "Target directory for coverage dumps. Defaults to the current "
+ "directory.")
+COMMON_FLAG(bool, full_address_space, false,
+ "Sanitize complete address space; "
+ "by default kernel area on 32-bit platforms will not be sanitized")
+COMMON_FLAG(bool, print_suppressions, true,
+ "Print matched suppressions at exit.")
+COMMON_FLAG(
+ bool, disable_coredump, (SANITIZER_WORDSIZE == 64),
+ "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.")
+COMMON_FLAG(bool, use_madv_dontdump, true,
+ "If set, instructs kernel to not store the (huge) shadow "
+ "in core file.")
+COMMON_FLAG(bool, symbolize_inline_frames, true,
+ "Print inlined frames in stacktraces. Defaults to true.")
+COMMON_FLAG(bool, symbolize_vs_style, false,
+ "Print file locations in Visual Studio style (e.g: "
+ " file(10,42): ...")
+COMMON_FLAG(const char *, stack_trace_format, "DEFAULT",
+ "Format string used to render stack frames. "
+ "See sanitizer_stacktrace_printer.h for the format description. "
+ "Use DEFAULT to get default format.")
+COMMON_FLAG(bool, no_huge_pages_for_shadow, true,
+ "If true, the shadow is not allowed to use huge pages. ")
+COMMON_FLAG(bool, strict_string_checks, false,
+ "If set check that string arguments are properly null-terminated")
+COMMON_FLAG(bool, intercept_strstr, true,
+ "If set, uses custom wrappers for strstr and strcasestr functions "
+ "to find more errors.")
+COMMON_FLAG(bool, intercept_strspn, true,
+ "If set, uses custom wrappers for strspn and strcspn function "
+ "to find more errors.")
+COMMON_FLAG(bool, intercept_strpbrk, true,
+ "If set, uses custom wrappers for strpbrk function "
+ "to find more errors.")
+COMMON_FLAG(bool, intercept_memcmp, true,
+ "If set, uses custom wrappers for memcmp function "
+ "to find more errors.")
+COMMON_FLAG(bool, strict_memcmp, true,
+ "If true, assume that memcmp(p1, p2, n) always reads n bytes before "
+ "comparing p1 and p2.")
+COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer "
+ "mappings in /proc/self/maps with "
+ "user-readable names")
+COMMON_FLAG(int, exitcode, 1, "Override the program exit status if the tool "
+ "found an error")
+COMMON_FLAG(
+ bool, abort_on_error, SANITIZER_MAC,
+ "If set, the tool calls abort() instead of _exit() after printing the "
+ "error report.")
diff --git a/libsanitizer/sanitizer_common/sanitizer_interception.h b/libsanitizer/sanitizer_common/sanitizer_interception.h
deleted file mode 100644
index 2346fa859b1..00000000000
--- a/libsanitizer/sanitizer_common/sanitizer_interception.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//===-- sanitizer_interception.h --------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Common macro definitions for interceptors.
-// Always use this headers instead of interception/interception.h.
-//
-//===----------------------------------------------------------------------===//
-#ifndef SANITIZER_INTERCEPTION_H
-#define SANITIZER_INTERCEPTION_H
-
-#include "interception/interception.h"
-#include "sanitizer_common.h"
-
-#if SANITIZER_LINUX && !defined(SANITIZER_GO)
-#undef REAL
-#define REAL(x) IndirectExternCall(__interception::PTR_TO_REAL(x))
-#endif
-
-#endif // SANITIZER_INTERCEPTION_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_interface_internal.h b/libsanitizer/sanitizer_common/sanitizer_interface_internal.h
new file mode 100644
index 00000000000..fe472d8c527
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_interface_internal.h
@@ -0,0 +1,56 @@
+//===-- sanitizer_interface_internal.h --------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between run-time libraries of sanitizers.
+//
+// This header declares the sanitizer runtime interface functions.
+// The runtime library has to define these functions so the instrumented program
+// could call them.
+//
+// See also include/sanitizer/common_interface_defs.h
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_INTERFACE_INTERNAL_H
+#define SANITIZER_INTERFACE_INTERNAL_H
+
+#include "sanitizer_internal_defs.h"
+
+extern "C" {
+ // Tell the tools to write their reports to "path.<pid>" instead of stderr.
+ // The special values are "stdout" and "stderr".
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __sanitizer_set_report_path(const char *path);
+
+ typedef struct {
+ int coverage_sandboxed;
+ __sanitizer::sptr coverage_fd;
+ unsigned int coverage_max_block_size;
+ } __sanitizer_sandbox_arguments;
+
+ // Notify the tools that the sandbox is going to be turned on.
+ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
+ __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
+
+ // This function is called by the tool when it has just finished reporting
+ // an error. 'error_summary' is a one-line string that summarizes
+ // the error message. This function can be overridden by the client.
+ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+ void __sanitizer_report_error_summary(const char *error_summary);
+
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init();
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __sanitizer_annotate_contiguous_container(const void *beg,
+ const void *end,
+ const void *old_mid,
+ const void *new_mid);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
+ const void *end);
+} // extern "C"
+
+#endif // SANITIZER_INTERFACE_INTERNAL_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h
index f1f243c17e2..d76ed7570a7 100644
--- a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h
+++ b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h
@@ -13,6 +13,10 @@
#include "sanitizer_platform.h"
+#ifndef SANITIZER_DEBUG
+# define SANITIZER_DEBUG 0
+#endif
+
// Only use SANITIZER_*ATTRIBUTE* before the function return type!
#if SANITIZER_WINDOWS
# define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport)
@@ -26,7 +30,7 @@
# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
#endif
-#if SANITIZER_LINUX && !defined(SANITIZER_GO)
+#if (SANITIZER_LINUX || SANITIZER_WINDOWS) && !defined(SANITIZER_GO)
# define SANITIZER_SUPPORTS_WEAK_HOOKS 1
#else
# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
@@ -74,13 +78,22 @@ typedef signed char s8;
typedef signed short s16; // NOLINT
typedef signed int s32;
typedef signed long long s64; // NOLINT
+#if SANITIZER_WINDOWS
+// On Windows, files are HANDLE, which is a synonim of void*.
+// Use void* to avoid including <windows.h> everywhere.
+typedef void* fd_t;
+typedef unsigned error_t;
+#else
typedef int fd_t;
+typedef int error_t;
+#endif
// WARNING: OFF_T may be different from OS type off_t, depending on the value of
// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls
// like pread and mmap, as opposed to pread64 and mmap64.
-// Mac and Linux/x86-64 are special.
-#if SANITIZER_MAC || (SANITIZER_LINUX && defined(__x86_64__))
+// FreeBSD, Mac and Linux/x86-64 are special.
+#if SANITIZER_FREEBSD || SANITIZER_MAC || \
+ (SANITIZER_LINUX && defined(__x86_64__))
typedef u64 OFF_T;
#else
typedef uptr OFF_T;
@@ -94,41 +107,6 @@ typedef u32 operator_new_size_type;
#endif
} // namespace __sanitizer
-extern "C" {
- // Tell the tools to write their reports to "path.<pid>" instead of stderr.
- // The special values are "stdout" and "stderr".
- SANITIZER_INTERFACE_ATTRIBUTE
- void __sanitizer_set_report_path(const char *path);
-
- typedef struct {
- int coverage_sandboxed;
- __sanitizer::sptr coverage_fd;
- unsigned int coverage_max_block_size;
- } __sanitizer_sandbox_arguments;
-
- // Notify the tools that the sandbox is going to be turned on.
- SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
- __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
-
- // This function is called by the tool when it has just finished reporting
- // an error. 'error_summary' is a one-line string that summarizes
- // the error message. This function can be overridden by the client.
- SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
- void __sanitizer_report_error_summary(const char *error_summary);
-
- SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
- SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init();
- SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov();
- SANITIZER_INTERFACE_ATTRIBUTE
- void __sanitizer_annotate_contiguous_container(const void *beg,
- const void *end,
- const void *old_mid,
- const void *new_mid);
- SANITIZER_INTERFACE_ATTRIBUTE
- int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
- const void *end);
-} // extern "C"
-
using namespace __sanitizer; // NOLINT
// ----------- ATTENTION -------------
@@ -149,7 +127,6 @@ using namespace __sanitizer; // NOLINT
# define NOINLINE __declspec(noinline)
# define NORETURN __declspec(noreturn)
# define THREADLOCAL __declspec(thread)
-# define NOTHROW
# define LIKELY(x) (x)
# define UNLIKELY(x) (x)
# define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */
@@ -163,7 +140,6 @@ using namespace __sanitizer; // NOLINT
# define NOINLINE __attribute__((noinline))
# define NORETURN __attribute__((noreturn))
# define THREADLOCAL __thread
-# define NOTHROW throw()
# define LIKELY(x) __builtin_expect(!!(x), 1)
# define UNLIKELY(x) __builtin_expect(!!(x), 0)
# if defined(__i386__) || defined(__x86_64__)
@@ -182,6 +158,12 @@ using namespace __sanitizer; // NOLINT
# define USED
#endif
+#if !defined(_MSC_VER) || defined(__clang__) || MSC_PREREQ(1900)
+# define NOEXCEPT noexcept
+#else
+# define NOEXCEPT throw()
+#endif
+
// Unaligned versions of basic types.
typedef ALIGNED(1) u16 uu16;
typedef ALIGNED(1) u32 uu32;
@@ -238,7 +220,7 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
#define CHECK_GT(a, b) CHECK_IMPL((a), >, (b))
#define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b))
-#if TSAN_DEBUG
+#if SANITIZER_DEBUG
#define DCHECK(a) CHECK(a)
#define DCHECK_EQ(a, b) CHECK_EQ(a, b)
#define DCHECK_NE(a, b) CHECK_NE(a, b)
@@ -318,4 +300,12 @@ extern "C" void* _ReturnAddress(void);
} while (internal_iserror(res, &rverrno) && rverrno == EINTR); \
}
+// 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(); \
+ (void)enable_fp; \
+ } while (0)
+
#endif // SANITIZER_DEFS_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_lfstack.h b/libsanitizer/sanitizer_common/sanitizer_lfstack.h
index 8033b9a7f9c..8bd0e91c400 100644
--- a/libsanitizer/sanitizer_common/sanitizer_lfstack.h
+++ b/libsanitizer/sanitizer_common/sanitizer_lfstack.h
@@ -47,8 +47,8 @@ struct LFStack {
u64 cmp = atomic_load(&head_, memory_order_acquire);
for (;;) {
T *cur = (T*)(uptr)(cmp & kPtrMask);
- if (cur == 0)
- return 0;
+ if (!cur)
+ return nullptr;
T *nxt = cur->next;
u64 cnt = (cmp & kCounterMask);
u64 xch = (u64)(uptr)nxt | cnt;
@@ -66,6 +66,6 @@ struct LFStack {
atomic_uint64_t head_;
};
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // #ifndef SANITIZER_LFSTACK_H
+#endif // SANITIZER_LFSTACK_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.cc b/libsanitizer/sanitizer_common/sanitizer_libc.cc
index c13a66d88cd..05fdca622e4 100644
--- a/libsanitizer/sanitizer_common/sanitizer_libc.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_libc.cc
@@ -8,37 +8,37 @@
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries. See sanitizer_libc.h for details.
//===----------------------------------------------------------------------===//
+
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
namespace __sanitizer {
-// Make the compiler think that something is going on there.
-static inline void break_optimization(void *arg) {
-#if _MSC_VER
- // FIXME: make sure this is actually enough.
- __asm;
-#else
- __asm__ __volatile__("" : : "r" (arg) : "memory");
-#endif
-}
-
s64 internal_atoll(const char *nptr) {
- return internal_simple_strtoll(nptr, (char**)0, 10);
+ return internal_simple_strtoll(nptr, nullptr, 10);
}
void *internal_memchr(const void *s, int c, uptr n) {
- const char* t = (char*)s;
+ const char *t = (const char *)s;
for (uptr i = 0; i < n; ++i, ++t)
if (*t == c)
- return (void*)t;
- return 0;
+ return reinterpret_cast<void *>(const_cast<char *>(t));
+ return nullptr;
+}
+
+void *internal_memrchr(const void *s, int c, uptr n) {
+ const char *t = (const char *)s;
+ void *res = nullptr;
+ for (uptr i = 0; i < n; ++i, ++t) {
+ if (*t == c) res = reinterpret_cast<void *>(const_cast<char *>(t));
+ }
+ return res;
}
int internal_memcmp(const void* s1, const void* s2, uptr n) {
- const char* t1 = (char*)s1;
- const char* t2 = (char*)s2;
+ const char *t1 = (const char *)s1;
+ const char *t2 = (const char *)s2;
for (uptr i = 0; i < n; ++i, ++t1, ++t2)
if (*t1 != *t2)
return *t1 < *t2 ? -1 : 1;
@@ -47,7 +47,7 @@ int internal_memcmp(const void* s1, const void* s2, uptr n) {
void *internal_memcpy(void *dest, const void *src, uptr n) {
char *d = (char*)dest;
- char *s = (char*)src;
+ const char *s = (const char *)src;
for (uptr i = 0; i < n; ++i)
d[i] = s[i];
return dest;
@@ -55,7 +55,7 @@ void *internal_memcpy(void *dest, const void *src, uptr n) {
void *internal_memmove(void *dest, const void *src, uptr n) {
char *d = (char*)dest;
- char *s = (char*)src;
+ const char *s = (const char *)src;
sptr i, signed_n = (sptr)n;
CHECK_GE(signed_n, 0);
if (d < s) {
@@ -76,7 +76,8 @@ void internal_bzero_aligned16(void *s, uptr n) {
CHECK_EQ((reinterpret_cast<uptr>(s) | n) & 15, 0);
for (S16 *p = reinterpret_cast<S16*>(s), *end = p + n / 16; p < end; p++) {
p->a = p->b = 0;
- break_optimization(0); // Make sure this does not become memset.
+ // Make sure this does not become memset.
+ SanitizerBreakOptimization(nullptr);
}
}
@@ -95,7 +96,7 @@ void *internal_memset(void* s, int c, uptr n) {
uptr internal_strcspn(const char *s, const char *reject) {
uptr i;
for (i = 0; s[i]; i++) {
- if (internal_strchr(reject, s[i]) != 0)
+ if (internal_strchr(reject, s[i]))
return i;
}
return i;
@@ -109,6 +110,14 @@ char* internal_strdup(const char *s) {
return s2;
}
+char* internal_strndup(const char *s, uptr n) {
+ uptr len = internal_strnlen(s, n);
+ char *s2 = (char*)InternalAlloc(len + 1);
+ internal_memcpy(s2, s, len);
+ s2[len] = 0;
+ return s2;
+}
+
int internal_strcmp(const char *s1, const char *s2) {
while (true) {
unsigned c1 = *s1;
@@ -136,9 +145,9 @@ int internal_strncmp(const char *s1, const char *s2, uptr n) {
char* internal_strchr(const char *s, int c) {
while (true) {
if (*s == (char)c)
- return (char*)s;
+ return const_cast<char *>(s);
if (*s == 0)
- return 0;
+ return nullptr;
s++;
}
}
@@ -146,16 +155,16 @@ char* internal_strchr(const char *s, int c) {
char *internal_strchrnul(const char *s, int c) {
char *res = internal_strchr(s, c);
if (!res)
- res = (char*)s + internal_strlen(s);
+ res = const_cast<char *>(s) + internal_strlen(s);
return res;
}
char *internal_strrchr(const char *s, int c) {
- const char *res = 0;
+ const char *res = nullptr;
for (uptr i = 0; s[i]; i++) {
if (s[i] == c) res = s + i;
}
- return (char*)res;
+ return const_cast<char *>(res);
}
uptr internal_strlen(const char *s) {
@@ -191,12 +200,12 @@ char *internal_strstr(const char *haystack, const char *needle) {
// This is O(N^2), but we are not using it in hot places.
uptr len1 = internal_strlen(haystack);
uptr len2 = internal_strlen(needle);
- if (len1 < len2) return 0;
+ if (len1 < len2) return nullptr;
for (uptr pos = 0; pos <= len1 - len2; pos++) {
if (internal_memcmp(haystack + pos, needle, len2) == 0)
- return (char*)haystack + pos;
+ return const_cast<char *>(haystack) + pos;
}
- return 0;
+ return nullptr;
}
s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) {
@@ -205,7 +214,7 @@ s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) {
int sgn = 1;
u64 res = 0;
bool have_digits = false;
- char *old_nptr = (char*)nptr;
+ char *old_nptr = const_cast<char *>(nptr);
if (*nptr == '+') {
sgn = 1;
nptr++;
@@ -220,8 +229,8 @@ s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) {
have_digits = true;
nptr++;
}
- if (endptr != 0) {
- *endptr = (have_digits) ? (char*)nptr : old_nptr;
+ if (endptr) {
+ *endptr = (have_digits) ? const_cast<char *>(nptr) : old_nptr;
}
if (sgn > 0) {
return (s64)(Min((u64)INT64_MAX, res));
@@ -249,4 +258,4 @@ bool mem_is_zero(const char *beg, uptr size) {
return all == 0;
}
-} // namespace __sanitizer
+} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.h b/libsanitizer/sanitizer_common/sanitizer_libc.h
index 680b888e219..1b3f8edfadb 100644
--- a/libsanitizer/sanitizer_common/sanitizer_libc.h
+++ b/libsanitizer/sanitizer_common/sanitizer_libc.h
@@ -9,7 +9,9 @@
// run-time libraries.
// These tools can not use some of the libc functions directly because those
// functions are intercepted. Instead, we implement a tiny subset of libc here.
+// FIXME: Some of functions declared in this file are in fact POSIX, not libc.
//===----------------------------------------------------------------------===//
+
#ifndef SANITIZER_LIBC_H
#define SANITIZER_LIBC_H
@@ -24,6 +26,7 @@ namespace __sanitizer {
// String functions
s64 internal_atoll(const char *nptr);
void *internal_memchr(const void *s, int c, uptr n);
+void *internal_memrchr(const void *s, int c, uptr n);
int internal_memcmp(const void* s1, const void* s2, uptr n);
void *internal_memcpy(void *dest, const void *src, uptr n);
void *internal_memmove(void *dest, const void *src, uptr n);
@@ -36,6 +39,7 @@ char *internal_strchrnul(const char *s, int c);
int internal_strcmp(const char *s1, const char *s2);
uptr internal_strcspn(const char *s, const char *reject);
char *internal_strdup(const char *s);
+char *internal_strndup(const char *s, uptr n);
uptr internal_strlen(const char *s);
char *internal_strncat(char *dst, const char *src, uptr n);
int internal_strncmp(const char *s1, const char *s2, uptr n);
@@ -52,55 +56,26 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...);
// Optimized for the case when the result is true.
bool mem_is_zero(const char *mem, uptr size);
-
-// Memory
-uptr internal_mmap(void *addr, uptr length, int prot, int flags,
- int fd, u64 offset);
-uptr internal_munmap(void *addr, uptr length);
-
// I/O
-const fd_t kInvalidFd = -1;
+const fd_t kInvalidFd = (fd_t)-1;
const fd_t kStdinFd = 0;
-const fd_t kStdoutFd = 1;
-const fd_t kStderrFd = 2;
-uptr internal_close(fd_t fd);
-int internal_isatty(fd_t fd);
+const fd_t kStdoutFd = (fd_t)1;
+const fd_t kStderrFd = (fd_t)2;
-// Use __sanitizer::OpenFile() instead.
-uptr internal_open(const char *filename, int flags);
-uptr internal_open(const char *filename, int flags, u32 mode);
-
-uptr internal_read(fd_t fd, void *buf, uptr count);
-uptr internal_write(fd_t fd, const void *buf, uptr count);
uptr internal_ftruncate(fd_t fd, uptr size);
// OS
-uptr internal_filesize(fd_t fd); // -1 on error.
-uptr internal_stat(const char *path, void *buf);
-uptr internal_lstat(const char *path, void *buf);
-uptr internal_fstat(fd_t fd, void *buf);
-uptr internal_dup2(int oldfd, int newfd);
-uptr internal_readlink(const char *path, char *buf, uptr bufsize);
-uptr internal_unlink(const char *path);
-uptr internal_rename(const char *oldpath, const char *newpath);
void NORETURN internal__exit(int exitcode);
-uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
-uptr internal_ptrace(int request, int pid, void *addr, void *data);
-uptr internal_waitpid(int pid, int *status, int options);
uptr internal_getpid();
uptr internal_getppid();
-int internal_fork();
-
// Threading
uptr internal_sched_yield();
// Error handling
-bool internal_iserror(uptr retval, int *rverrno = 0);
-
-int internal_sigaction(int signum, const void *act, void *oldact);
+bool internal_iserror(uptr retval, int *rverrno = nullptr);
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_LIBC_H
+#endif // SANITIZER_LIBC_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_libignore.cc b/libsanitizer/sanitizer_common/sanitizer_libignore.cc
index 9877d668658..021ca618d64 100644
--- a/libsanitizer/sanitizer_common/sanitizer_libignore.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_libignore.cc
@@ -6,10 +6,12 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
+
#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_libignore.h"
#include "sanitizer_flags.h"
+#include "sanitizer_posix.h"
#include "sanitizer_procmaps.h"
namespace __sanitizer {
@@ -17,35 +19,29 @@ namespace __sanitizer {
LibIgnore::LibIgnore(LinkerInitialized) {
}
-void LibIgnore::Init(const SuppressionContext &supp) {
+void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
BlockingMutexLock lock(&mutex_);
- CHECK_EQ(count_, 0);
- const uptr n = supp.SuppressionCount();
- for (uptr i = 0; i < n; i++) {
- const Suppression *s = supp.SuppressionAt(i);
- if (s->type != SuppressionLib)
- continue;
- if (count_ >= kMaxLibs) {
- Report("%s: too many called_from_lib suppressions (max: %d)\n",
- SanitizerToolName, kMaxLibs);
- Die();
- }
- Lib *lib = &libs_[count_++];
- lib->templ = internal_strdup(s->templ);
- lib->name = 0;
- lib->loaded = false;
+ if (count_ >= kMaxLibs) {
+ Report("%s: too many ignored libraries (max: %d)\n", SanitizerToolName,
+ kMaxLibs);
+ Die();
}
+ Lib *lib = &libs_[count_++];
+ lib->templ = internal_strdup(name_templ);
+ lib->name = nullptr;
+ lib->real_name = nullptr;
+ lib->loaded = false;
}
void LibIgnore::OnLibraryLoaded(const char *name) {
BlockingMutexLock lock(&mutex_);
// Try to match suppressions with symlink target.
- InternalScopedBuffer<char> buf(4096);
- if (name != 0 && internal_readlink(name, buf.data(), buf.size() - 1) > 0 &&
- buf.data()[0]) {
+ InternalScopedString buf(kMaxPathLength);
+ if (name && internal_readlink(name, buf.data(), buf.size() - 1) > 0 &&
+ buf[0]) {
for (uptr i = 0; i < count_; i++) {
Lib *lib = &libs_[i];
- if (!lib->loaded && lib->real_name == 0 &&
+ if (!lib->loaded && (!lib->real_name) &&
TemplateMatch(lib->templ, name))
lib->real_name = internal_strdup(buf.data());
}
@@ -53,7 +49,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
// Scan suppressions list and find newly loaded and unloaded libraries.
MemoryMappingLayout proc_maps(/*cache_enabled*/false);
- InternalScopedBuffer<char> module(4096);
+ InternalScopedString module(kMaxPathLength);
for (uptr i = 0; i < count_; i++) {
Lib *lib = &libs_[i];
bool loaded = false;
@@ -63,7 +59,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
if ((prot & MemoryMappingLayout::kProtectionExecute) == 0)
continue;
if (TemplateMatch(lib->templ, module.data()) ||
- (lib->real_name != 0 &&
+ (lib->real_name &&
internal_strcmp(lib->real_name, module.data()) == 0)) {
if (loaded) {
Report("%s: called_from_lib suppression '%s' is matched against"
@@ -96,9 +92,9 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
}
void LibIgnore::OnLibraryUnloaded() {
- OnLibraryLoaded(0);
+ OnLibraryLoaded(nullptr);
}
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX
+#endif // #if SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/libsanitizer/sanitizer_common/sanitizer_libignore.h b/libsanitizer/sanitizer_common/sanitizer_libignore.h
index 2089c4bbeeb..84419d14fed 100644
--- a/libsanitizer/sanitizer_common/sanitizer_libignore.h
+++ b/libsanitizer/sanitizer_common/sanitizer_libignore.h
@@ -6,8 +6,8 @@
//===----------------------------------------------------------------------===//
//
// LibIgnore allows to ignore all interceptors called from a particular set
-// of dynamic libraries. LibIgnore remembers all "called_from_lib" suppressions
-// from the provided SuppressionContext; finds code ranges for the libraries;
+// of dynamic libraries. LibIgnore can be initialized with several templates
+// of names of libraries to be ignored. It finds code ranges for the libraries;
// and checks whether the provided PC value belongs to the code ranges.
//
//===----------------------------------------------------------------------===//
@@ -17,7 +17,6 @@
#include "sanitizer_internal_defs.h"
#include "sanitizer_common.h"
-#include "sanitizer_suppressions.h"
#include "sanitizer_atomic.h"
#include "sanitizer_mutex.h"
@@ -27,8 +26,8 @@ class LibIgnore {
public:
explicit LibIgnore(LinkerInitialized);
- // Fetches all "called_from_lib" suppressions from the SuppressionContext.
- void Init(const SuppressionContext &supp);
+ // Must be called during initialization.
+ void AddIgnoredLibrary(const char *name_templ);
// Must be called after a new dynamic library is loaded.
void OnLibraryLoaded(const char *name);
diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cc b/libsanitizer/sanitizer_common/sanitizer_linux.cc
index 9feb307db9f..00a6c1f094a 100644
--- a/libsanitizer/sanitizer_common/sanitizer_linux.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_linux.cc
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
+
#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_common.h"
@@ -28,12 +29,22 @@
#include <asm/param.h>
#endif
+// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat'
+// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To
+// access stat from asm/stat.h, without conflicting with definition in
+// sys/stat.h, we use this trick.
+#if defined(__mips64)
+#include <asm/unistd.h>
+#include <sys/types.h>
+#define stat kernel_stat
+#include <asm/stat.h>
+#undef stat
+#endif
+
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
-#if !SANITIZER_ANDROID
#include <link.h>
-#endif
#include <pthread.h>
#include <sched.h>
#include <sys/mman.h>
@@ -43,6 +54,7 @@
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <ucontext.h>
#include <unistd.h>
#if SANITIZER_FREEBSD
@@ -60,11 +72,6 @@ extern char **environ; // provided by crt1
#include <sys/signal.h>
#endif
-#if SANITIZER_ANDROID
-#include <android/log.h>
-#include <sys/system_properties.h>
-#endif
-
#if SANITIZER_LINUX
// <linux/time.h>
struct kernel_timeval {
@@ -90,19 +97,23 @@ namespace __sanitizer {
#if SANITIZER_LINUX && defined(__x86_64__)
#include "sanitizer_syscall_linux_x86_64.inc"
+#elif SANITIZER_LINUX && defined(__aarch64__)
+#include "sanitizer_syscall_linux_aarch64.inc"
#else
#include "sanitizer_syscall_generic.inc"
#endif
// --------------- sanitizer_libc.h
-uptr internal_mmap(void *addr, uptr length, int prot, int flags,
- int fd, u64 offset) {
+uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
+ OFF_T offset) {
#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd,
offset);
#else
+ // mmap2 specifies file offset in 4096-byte units.
+ CHECK(IsAligned(offset, 4096));
return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd,
- offset);
+ offset / 4096);
#endif
}
@@ -110,6 +121,10 @@ uptr internal_munmap(void *addr, uptr length) {
return internal_syscall(SYSCALL(munmap), (uptr)addr, length);
}
+int internal_mprotect(void *addr, uptr length, int prot) {
+ return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot);
+}
+
uptr internal_close(fd_t fd) {
return internal_syscall(SYSCALL(close), fd);
}
@@ -131,11 +146,6 @@ uptr internal_open(const char *filename, int flags, u32 mode) {
#endif
}
-uptr OpenFile(const char *filename, bool write) {
- return internal_open(filename,
- write ? O_RDWR | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660);
-}
-
uptr internal_read(fd_t fd, void *buf, uptr count) {
sptr res;
HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf,
@@ -152,7 +162,8 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) {
uptr internal_ftruncate(fd_t fd, uptr size) {
sptr res;
- HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, size));
+ HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
+ (OFF_T)size));
return res;
}
@@ -176,6 +187,26 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) {
}
#endif
+#if defined(__mips64)
+static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
+ internal_memset(out, 0, sizeof(*out));
+ out->st_dev = in->st_dev;
+ out->st_ino = in->st_ino;
+ out->st_mode = in->st_mode;
+ out->st_nlink = in->st_nlink;
+ out->st_uid = in->st_uid;
+ out->st_gid = in->st_gid;
+ out->st_rdev = in->st_rdev;
+ out->st_size = in->st_size;
+ out->st_blksize = in->st_blksize;
+ out->st_blocks = in->st_blocks;
+ out->st_atime = in->st_atime_nsec;
+ out->st_mtime = in->st_mtime_nsec;
+ out->st_ctime = in->st_ctime_nsec;
+ out->st_ino = in->st_ino;
+}
+#endif
+
uptr internal_stat(const char *path, void *buf) {
#if SANITIZER_FREEBSD
return internal_syscall(SYSCALL(stat), path, buf);
@@ -183,7 +214,15 @@ uptr internal_stat(const char *path, void *buf) {
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path,
(uptr)buf, 0);
#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
+# if defined(__mips64)
+ // For mips64, stat syscall fills buffer in the format of kernel_stat
+ struct kernel_stat kbuf;
+ int res = internal_syscall(SYSCALL(stat), path, &kbuf);
+ kernel_stat_to_stat(&kbuf, (struct stat *)buf);
+ return res;
+# else
return internal_syscall(SYSCALL(stat), (uptr)path, (uptr)buf);
+# endif
#else
struct stat64 buf64;
int res = internal_syscall(SYSCALL(stat64), path, &buf64);
@@ -331,23 +370,23 @@ const char *GetEnv(const char *name) {
if (!inited) {
inited = true;
uptr environ_size;
- len = ReadFileToBuffer("/proc/self/environ",
- &environ, &environ_size, 1 << 26);
+ if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len))
+ environ = nullptr;
}
- if (!environ || len == 0) return 0;
+ if (!environ || len == 0) return nullptr;
uptr namelen = internal_strlen(name);
const char *p = environ;
while (*p != '\0') { // will happen at the \0\0 that terminates the buffer
// proc file has the format NAME=value\0NAME=value\0NAME=value\0...
const char* endp =
(char*)internal_memchr(p, '\0', len - (p - environ));
- if (endp == 0) // this entry isn't NUL terminated
- return 0;
+ if (!endp) // this entry isn't NUL terminated
+ return nullptr;
else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match.
return p + namelen + 1; // point after =
p = endp + 1;
}
- return 0; // Not found.
+ return nullptr; // Not found.
#else
#error "Unsupported platform"
#endif
@@ -361,9 +400,13 @@ extern "C" {
static void ReadNullSepFileToArray(const char *path, char ***arr,
int arr_size) {
char *buff;
- uptr buff_size = 0;
+ uptr buff_size;
+ uptr buff_len;
*arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray");
- ReadFileToBuffer(path, &buff, &buff_size, 1024 * 1024);
+ if (!ReadFileToBuffer(path, &buff, &buff_size, &buff_len, 1024 * 1024)) {
+ (*arr)[0] = nullptr;
+ return;
+ }
(*arr)[0] = buff;
int count, i;
for (count = 1, i = 1; ; i++) {
@@ -374,7 +417,7 @@ static void ReadNullSepFileToArray(const char *path, char ***arr,
count++;
}
}
- (*arr)[count] = 0;
+ (*arr)[count] = nullptr;
}
#endif
@@ -405,32 +448,18 @@ void ReExec() {
Die();
}
-// Stub implementation of GetThreadStackAndTls for Go.
-#if SANITIZER_GO
-void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
- uptr *tls_addr, uptr *tls_size) {
- *stk_addr = 0;
- *stk_size = 0;
- *tls_addr = 0;
- *tls_size = 0;
-}
-#endif // SANITIZER_GO
-
enum MutexState {
MtxUnlocked = 0,
MtxLocked = 1,
MtxSleeping = 2
};
-BlockingMutex::BlockingMutex(LinkerInitialized) {
- CHECK_EQ(owner_, 0);
-}
-
BlockingMutex::BlockingMutex() {
internal_memset(this, 0, sizeof(*this));
}
void BlockingMutex::Lock() {
+ CHECK_EQ(owner_, 0);
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
return;
@@ -528,19 +557,21 @@ int internal_fork() {
}
#if SANITIZER_LINUX
+#define SA_RESTORER 0x04000000
// Doesn't set sa_restorer, use with caution (see below).
int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
__sanitizer_kernel_sigaction_t k_act, k_oldact;
internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t));
internal_memset(&k_oldact, 0, sizeof(__sanitizer_kernel_sigaction_t));
- const __sanitizer_sigaction *u_act = (__sanitizer_sigaction *)act;
+ const __sanitizer_sigaction *u_act = (const __sanitizer_sigaction *)act;
__sanitizer_sigaction *u_oldact = (__sanitizer_sigaction *)oldact;
if (u_act) {
k_act.handler = u_act->handler;
k_act.sigaction = u_act->sigaction;
internal_memcpy(&k_act.sa_mask, &u_act->sa_mask,
sizeof(__sanitizer_kernel_sigset_t));
- k_act.sa_flags = u_act->sa_flags;
+ // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL).
+ k_act.sa_flags = u_act->sa_flags | SA_RESTORER;
// FIXME: most often sa_restorer is unset, however the kernel requires it
// to point to a valid signal restorer that calls the rt_sigreturn syscall.
// If sa_restorer passed to the kernel is NULL, the program may crash upon
@@ -553,8 +584,8 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
}
uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum,
- (uptr)(u_act ? &k_act : NULL),
- (uptr)(u_oldact ? &k_oldact : NULL),
+ (uptr)(u_act ? &k_act : nullptr),
+ (uptr)(u_oldact ? &k_oldact : nullptr),
(uptr)sizeof(__sanitizer_kernel_sigset_t));
if ((result == 0) && u_oldact) {
@@ -674,45 +705,45 @@ uptr GetPageSize() {
#endif
}
-static char proc_self_exe_cache_str[kMaxPathLength];
-static uptr proc_self_exe_cache_len = 0;
-
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
- if (proc_self_exe_cache_len > 0) {
- // If available, use the cached module name.
- uptr module_name_len =
- internal_snprintf(buf, buf_len, "%s", proc_self_exe_cache_str);
- CHECK_LT(module_name_len, buf_len);
- return module_name_len;
- }
#if SANITIZER_FREEBSD
- const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ const int Mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ const char *default_module_name = "kern.proc.pathname";
size_t Size = buf_len;
- bool IsErr = (sysctl(Mib, 4, buf, &Size, NULL, 0) != 0);
+ bool IsErr = (sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0);
int readlink_error = IsErr ? errno : 0;
uptr module_name_len = Size;
#else
+ const char *default_module_name = "/proc/self/exe";
uptr module_name_len = internal_readlink(
- "/proc/self/exe", buf, buf_len);
+ default_module_name, buf, buf_len);
int readlink_error;
bool IsErr = internal_iserror(module_name_len, &readlink_error);
#endif
if (IsErr) {
- // We can't read /proc/self/exe for some reason, assume the name of the
- // binary is unknown.
- Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, "
+ // We can't read binary name for some reason, assume it's unknown.
+ Report("WARNING: reading executable name 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");
+ module_name_len = internal_snprintf(buf, buf_len, "%s",
+ default_module_name);
CHECK_LT(module_name_len, buf_len);
}
return module_name_len;
}
-void CacheBinaryName() {
- if (!proc_self_exe_cache_len) {
- proc_self_exe_cache_len =
- ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength);
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
+#if SANITIZER_LINUX
+ char *tmpbuf;
+ uptr tmpsize;
+ uptr tmplen;
+ if (ReadFileToBuffer("/proc/self/cmdline", &tmpbuf, &tmpsize, &tmplen,
+ 1024 * 1024)) {
+ internal_strncpy(buf, tmpbuf, buf_len);
+ UnmapOrDie(tmpbuf, tmpsize);
+ return internal_strlen(buf);
}
+#endif
+ return ReadBinaryName(buf, buf_len);
}
// Match full names of the form /path/to/base_name{-,.}*
@@ -730,6 +761,7 @@ bool LibraryNameIs(const char *full_name, const char *base_name) {
#if !SANITIZER_ANDROID
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {
+ CHECK_NE(map, nullptr);
#if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr;
typedef ElfW(Ehdr) Elf_Ehdr;
@@ -829,46 +861,283 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
: "rsp", "memory", "r11", "rcx");
return res;
}
-#endif // defined(__x86_64__) && SANITIZER_LINUX
+#elif defined(__mips__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ long long res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+ ((unsigned long long *)child_stack)[0] = (uptr)fn;
+ ((unsigned long long *)child_stack)[1] = (uptr)arg;
+ register void *a3 __asm__("$7") = newtls;
+ register int *a4 __asm__("$8") = child_tidptr;
+ // We don't have proper CFI directives here because it requires alot of code
+ // for very marginal benefits.
+ __asm__ __volatile__(
+ /* $v0 = syscall($v0 = __NR_clone,
+ * $a0 = flags,
+ * $a1 = child_stack,
+ * $a2 = parent_tidptr,
+ * $a3 = new_tls,
+ * $a4 = child_tidptr)
+ */
+ ".cprestore 16;\n"
+ "move $4,%1;\n"
+ "move $5,%2;\n"
+ "move $6,%3;\n"
+ "move $7,%4;\n"
+ /* Store the fifth argument on stack
+ * if we are using 32-bit abi.
+ */
+#if SANITIZER_WORDSIZE == 32
+ "lw %5,16($29);\n"
+#else
+ "move $8,%5;\n"
+#endif
+ "li $2,%6;\n"
+ "syscall;\n"
-#if SANITIZER_ANDROID
-static atomic_uint8_t android_log_initialized;
+ /* if ($v0 != 0)
+ * return;
+ */
+ "bnez $2,1f;\n"
+
+ /* Call "fn(arg)". */
+ "ld $25,0($29);\n"
+ "ld $4,8($29);\n"
+ "jal $25;\n"
-void AndroidLogInit() {
- atomic_store(&android_log_initialized, 1, memory_order_release);
+ /* Call _exit($v0). */
+ "move $4,$2;\n"
+ "li $2,%7;\n"
+ "syscall;\n"
+
+ /* Return to parent. */
+ "1:\n"
+ : "=r" (res)
+ : "r"(flags),
+ "r"(child_stack),
+ "r"(parent_tidptr),
+ "r"(a3),
+ "r"(a4),
+ "i"(__NR_clone),
+ "i"(__NR_exit)
+ : "memory", "$29" );
+ return res;
}
-// This thing is not, strictly speaking, async signal safe, but it does not seem
-// to cause any issues. Alternative is writing to log devices directly, but
-// their location and message format might change in the future, so we'd really
-// like to avoid that.
-void AndroidLogWrite(const char *buffer) {
- if (!atomic_load(&android_log_initialized, memory_order_acquire))
- return;
+#elif defined(__aarch64__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ long long res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+ ((unsigned long long *)child_stack)[0] = (uptr)fn;
+ ((unsigned long long *)child_stack)[1] = (uptr)arg;
- char *copy = internal_strdup(buffer);
- char *p = copy;
- char *q;
- // __android_log_write has an implicit message length limit.
- // Print one line at a time.
- do {
- q = internal_strchr(p, '\n');
- if (q) *q = '\0';
- __android_log_write(ANDROID_LOG_INFO, NULL, p);
- if (q) p = q + 1;
- } while (q);
- InternalFree(copy);
+ register int (*__fn)(void *) __asm__("x0") = fn;
+ register void *__stack __asm__("x1") = child_stack;
+ register int __flags __asm__("x2") = flags;
+ register void *__arg __asm__("x3") = arg;
+ register int *__ptid __asm__("x4") = parent_tidptr;
+ register void *__tls __asm__("x5") = newtls;
+ register int *__ctid __asm__("x6") = child_tidptr;
+
+ __asm__ __volatile__(
+ "mov x0,x2\n" /* flags */
+ "mov x2,x4\n" /* ptid */
+ "mov x3,x5\n" /* tls */
+ "mov x4,x6\n" /* ctid */
+ "mov x8,%9\n" /* clone */
+
+ "svc 0x0\n"
+
+ /* if (%r0 != 0)
+ * return %r0;
+ */
+ "cmp x0, #0\n"
+ "bne 1f\n"
+
+ /* In the child, now. Call "fn(arg)". */
+ "ldp x1, x0, [sp], #16\n"
+ "blr x1\n"
+
+ /* Call _exit(%r0). */
+ "mov x8, %10\n"
+ "svc 0x0\n"
+ "1:\n"
+
+ : "=r" (res)
+ : "i"(-EINVAL),
+ "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
+ "r"(__ptid), "r"(__tls), "r"(__ctid),
+ "i"(__NR_clone), "i"(__NR_exit)
+ : "x30", "memory");
+ return res;
}
+#endif // defined(__x86_64__) && SANITIZER_LINUX
+#if SANITIZER_ANDROID
+#define PROP_VALUE_MAX 92
+extern "C" SANITIZER_WEAK_ATTRIBUTE int __system_property_get(const char *name,
+ char *value);
void GetExtraActivationFlags(char *buf, uptr size) {
CHECK(size > PROP_VALUE_MAX);
+ CHECK(&__system_property_get);
__system_property_get("asan.options", buf);
}
+
+#if __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+ int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
+static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ // Any name starting with "lib" indicates a bug in L where library base names
+ // are returned instead of paths.
+ if (info->dlpi_name && info->dlpi_name[0] == 'l' &&
+ info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') {
+ *(bool *)data = true;
+ return 1;
+ }
+ return 0;
+}
+
+static atomic_uint32_t android_api_level;
+
+static AndroidApiLevel AndroidDetectApiLevel() {
+ if (!&dl_iterate_phdr)
+ return ANDROID_KITKAT; // K or lower
+ bool base_name_seen = false;
+ dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen);
+ if (base_name_seen)
+ return ANDROID_LOLLIPOP_MR1; // L MR1
+ return ANDROID_POST_LOLLIPOP; // post-L
+ // Plain L (API level 21) is completely broken wrt ASan and not very
+ // interesting to detect.
+}
+
+AndroidApiLevel AndroidGetApiLevel() {
+ AndroidApiLevel level =
+ (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
+ if (level) return level;
+ level = AndroidDetectApiLevel();
+ atomic_store(&android_api_level, level, memory_order_relaxed);
+ return level;
+}
+
#endif
bool IsDeadlySignal(int signum) {
- return (signum == SIGSEGV) && common_flags()->handle_segv;
+ if (common_flags()->handle_abort && signum == SIGABRT)
+ return true;
+ if (common_flags()->handle_sigfpe && signum == SIGFPE)
+ return true;
+ return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv;
+}
+
+#ifndef SANITIZER_GO
+void *internal_start_thread(void(*func)(void *arg), void *arg) {
+ // Start the thread with signals blocked, otherwise it can steal user signals.
+ __sanitizer_sigset_t set, old;
+ internal_sigfillset(&set);
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
+ // on any thread, setuid call hangs (see test/tsan/setuid.c).
+ internal_sigdelset(&set, 33);
+#endif
+ internal_sigprocmask(SIG_SETMASK, &set, &old);
+ void *th;
+ real_pthread_create(&th, nullptr, (void*(*)(void *arg))func, arg);
+ internal_sigprocmask(SIG_SETMASK, &old, nullptr);
+ return th;
+}
+
+void internal_join_thread(void *th) {
+ real_pthread_join(th, nullptr);
+}
+#else
+void *internal_start_thread(void (*func)(void *), void *arg) { return 0; }
+
+void internal_join_thread(void *th) {}
+#endif
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+#if defined(__arm__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.arm_pc;
+ *bp = ucontext->uc_mcontext.arm_fp;
+ *sp = ucontext->uc_mcontext.arm_sp;
+#elif defined(__aarch64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.pc;
+ *bp = ucontext->uc_mcontext.regs[29];
+ *sp = ucontext->uc_mcontext.sp;
+#elif defined(__hppa__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.sc_iaoq[0];
+ /* GCC uses %r3 whenever a frame pointer is needed. */
+ *bp = ucontext->uc_mcontext.sc_gr[3];
+ *sp = ucontext->uc_mcontext.sc_gr[30];
+#elif defined(__x86_64__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.mc_rip;
+ *bp = ucontext->uc_mcontext.mc_rbp;
+ *sp = ucontext->uc_mcontext.mc_rsp;
+# else
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.gregs[REG_RIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_RBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_RSP];
+# endif
+#elif defined(__i386__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.mc_eip;
+ *bp = ucontext->uc_mcontext.mc_ebp;
+ *sp = ucontext->uc_mcontext.mc_esp;
+# else
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.gregs[REG_EIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_EBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_ESP];
+# endif
+#elif defined(__powerpc__) || defined(__powerpc64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.regs->nip;
+ *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
+ // The powerpc{,64}-linux ABIs do not specify r31 as the frame
+ // pointer, but GCC always uses r31 when we need a frame pointer.
+ *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
+#elif defined(__sparc__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ uptr *stk_ptr;
+# if defined (__arch64__)
+ *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
+ *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
+ stk_ptr = (uptr *) (*sp + 2047);
+ *bp = stk_ptr[15];
+# else
+ *pc = ucontext->uc_mcontext.gregs[REG_PC];
+ *sp = ucontext->uc_mcontext.gregs[REG_O6];
+ stk_ptr = (uptr *) *sp;
+ *bp = stk_ptr[15];
+# endif
+#elif defined(__mips__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.pc;
+ *bp = ucontext->uc_mcontext.gregs[30];
+ *sp = ucontext->uc_mcontext.gregs[29];
+#else
+# error "Unsupported arch"
+#endif
}
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.h b/libsanitizer/sanitizer_common/sanitizer_linux.h
index 086834c3a2f..44977020bce 100644
--- a/libsanitizer/sanitizer_common/sanitizer_linux.h
+++ b/libsanitizer/sanitizer_common/sanitizer_linux.h
@@ -15,6 +15,7 @@
#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_posix.h"
#include "sanitizer_platform_limits_posix.h"
struct link_map; // Opaque type returned by dlopen().
@@ -41,7 +42,7 @@ uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
// internal_sigaction instead.
int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
-#if defined(__x86_64__)
+#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr);
#endif
@@ -78,11 +79,6 @@ uptr ThreadSelfOffset();
// information).
bool LibraryNameIs(const char *full_name, const char *base_name);
-// Read the name of the current binary from /proc/self/exe.
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
-// Cache the value of /proc/self/exe.
-void CacheBinaryName();
-
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr));
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc
index 36aaafdcc4b..d876e62fa74 100644
--- a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -11,8 +11,11 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
+
#if SANITIZER_FREEBSD || SANITIZER_LINUX
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_atomic.h"
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
#include "sanitizer_freebsd.h"
@@ -20,13 +23,12 @@
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
-#include "sanitizer_atomic.h"
-#include "sanitizer_symbolizer.h"
#if SANITIZER_ANDROID || SANITIZER_FREEBSD
#include <dlfcn.h> // for dlsym()
#endif
+#include <link.h>
#include <pthread.h>
#include <signal.h>
#include <sys/resource.h>
@@ -41,9 +43,18 @@
#include <sys/prctl.h>
#endif
+#if SANITIZER_ANDROID
+#include <android/api-level.h>
+#endif
+
+#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+#include <android/log.h>
+#else
+#include <syslog.h>
+#endif
+
#if !SANITIZER_ANDROID
#include <elf.h>
-#include <link.h>
#include <unistd.h>
#endif
@@ -53,11 +64,13 @@ namespace __sanitizer {
extern "C" {
SANITIZER_WEAK_ATTRIBUTE int
real_pthread_attr_getstack(void *attr, void **addr, size_t *size);
-} // extern "C"
+} // extern "C"
static int my_pthread_attr_getstack(void *attr, void **addr, size_t *size) {
- if (real_pthread_attr_getstack)
+#if !SANITIZER_GO
+ if (&real_pthread_attr_getstack)
return real_pthread_attr_getstack((pthread_attr_t *)attr, addr, size);
+#endif
return pthread_attr_getstack((pthread_attr_t *)attr, addr, size);
}
@@ -65,9 +78,12 @@ SANITIZER_WEAK_ATTRIBUTE int
real_sigaction(int signum, const void *act, void *oldact);
int internal_sigaction(int signum, const void *act, void *oldact) {
- if (real_sigaction)
+#if !SANITIZER_GO
+ if (&real_sigaction)
return real_sigaction(signum, act, oldact);
- return sigaction(signum, (struct sigaction *)act, (struct sigaction *)oldact);
+#endif
+ return sigaction(signum, (const struct sigaction *)act,
+ (struct sigaction *)oldact);
}
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
@@ -83,7 +99,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
uptr start, end, offset;
uptr prev_end = 0;
- while (proc_maps.Next(&start, &end, &offset, 0, 0, /* protection */0)) {
+ while (proc_maps.Next(&start, &end, &offset, nullptr, 0,
+ /* protection */nullptr)) {
if ((uptr)&rl < end)
break;
prev_end = end;
@@ -108,7 +125,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
pthread_attr_init(&attr);
CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
uptr stacksize = 0;
- void *stackaddr = 0;
+ void *stackaddr = nullptr;
my_pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize);
pthread_attr_destroy(&attr);
@@ -117,16 +134,18 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
*stack_bottom = (uptr)stackaddr;
}
+#if !SANITIZER_GO
bool SetEnv(const char *name, const char *value) {
void *f = dlsym(RTLD_NEXT, "setenv");
- if (f == 0)
+ if (!f)
return false;
typedef int(*setenv_ft)(const char *name, const char *value, int overwrite);
setenv_ft setenv_f;
CHECK_EQ(sizeof(setenv_f), sizeof(f));
internal_memcpy(&setenv_f, &f, sizeof(f));
- return IndirectExternCall(setenv_f)(name, value, 1) == 0;
+ return setenv_f(name, value, 1) == 0;
}
+#endif
bool SanitizerSetThreadName(const char *name) {
#ifdef PR_SET_NAME
@@ -149,7 +168,7 @@ bool SanitizerGetThreadName(char *name, int max_len) {
#endif
}
-#if !SANITIZER_FREEBSD
+#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
static uptr g_tls_size;
#endif
@@ -159,8 +178,24 @@ static uptr g_tls_size;
# define DL_INTERNAL_FUNCTION
#endif
+#if defined(__mips__)
+// TlsPreTcbSize includes size of struct pthread_descr and size of tcb
+// head structure. It lies before the static tls blocks.
+static uptr TlsPreTcbSize() {
+ const uptr kTcbHead = 16;
+ const uptr kTlsAlign = 16;
+ const uptr kTlsPreTcbSize =
+ (ThreadDescriptorSize() + kTcbHead + kTlsAlign - 1) & ~(kTlsAlign - 1);
+ InitTlsSize();
+ g_tls_size = (g_tls_size + kTlsPreTcbSize + kTlsAlign -1) & ~(kTlsAlign - 1);
+ return kTlsPreTcbSize;
+}
+#endif
+
void InitTlsSize() {
-#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID
+#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
+// all current supported platforms have 16 bytes stack alignment
+ const size_t kStackAlign = 16;
typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;
get_tls_func get_tls;
void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
@@ -170,12 +205,16 @@ void InitTlsSize() {
CHECK_NE(get_tls, 0);
size_t tls_size = 0;
size_t tls_align = 0;
- IndirectExternCall(get_tls)(&tls_size, &tls_align);
- g_tls_size = tls_size;
-#endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID
+ get_tls(&tls_size, &tls_align);
+ if (tls_align < kStackAlign)
+ tls_align = kStackAlign;
+ g_tls_size = RoundUpTo(tls_size, tls_align);
+#endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
}
-#if (defined(__x86_64__) || defined(__i386__)) && SANITIZER_LINUX
+#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) \
+ || defined(__aarch64__)) \
+ && SANITIZER_LINUX && !SANITIZER_ANDROID
// sizeof(struct thread) from glibc.
static atomic_uintptr_t kThreadDescriptorSize;
@@ -183,6 +222,7 @@ uptr ThreadDescriptorSize() {
uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed);
if (val)
return val;
+#if defined(__x86_64__) || defined(__i386__)
#ifdef _CS_GNU_LIBC_VERSION
char buf[64];
uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
@@ -205,6 +245,8 @@ uptr ThreadDescriptorSize() {
val = FIRST_32_SECOND_64(1168, 1776);
else if (minor <= 12)
val = FIRST_32_SECOND_64(1168, 2288);
+ else if (minor == 13)
+ val = FIRST_32_SECOND_64(1168, 2304);
else
val = FIRST_32_SECOND_64(1216, 2304);
}
@@ -213,6 +255,18 @@ uptr ThreadDescriptorSize() {
return val;
}
#endif
+#elif defined(__mips__)
+ // TODO(sagarthakur): add more values as per different glibc versions.
+ val = FIRST_32_SECOND_64(1152, 1776);
+ if (val)
+ atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+ return val;
+#elif defined(__aarch64__)
+ // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
+ val = 1776;
+ atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+ return val;
+#endif
return 0;
}
@@ -229,12 +283,26 @@ uptr ThreadSelf() {
asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
# elif defined(__x86_64__)
asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
+# elif defined(__mips__)
+ // MIPS uses TLS variant I. The thread pointer (in hardware register $29)
+ // points to the end of the TCB + 0x7000. The pthread_descr structure is
+ // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
+ // TCB and the size of pthread_descr.
+ const uptr kTlsTcbOffset = 0x7000;
+ uptr thread_pointer;
+ asm volatile(".set push;\
+ .set mips64r2;\
+ rdhwr %0,$29;\
+ .set pop" : "=r" (thread_pointer));
+ descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
+# elif defined(__aarch64__)
+ descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer());
# else
# error "unsupported CPU arch"
# endif
return descr_addr;
}
-#endif // (defined(__x86_64__) || defined(__i386__)) && SANITIZER_LINUX
+#endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX
#if SANITIZER_FREEBSD
static void **ThreadSelfSegbase() {
@@ -256,13 +324,17 @@ uptr ThreadSelf() {
}
#endif // SANITIZER_FREEBSD
+#if !SANITIZER_GO
static void GetTls(uptr *addr, uptr *size) {
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
# if defined(__x86_64__) || defined(__i386__)
*addr = ThreadSelf();
*size = GetTlsSize();
*addr -= *size;
*addr += ThreadDescriptorSize();
+# elif defined(__mips__) || defined(__aarch64__)
+ *addr = ThreadSelf();
+ *size = GetTlsSize();
# else
*addr = 0;
*size = 0;
@@ -280,13 +352,18 @@ static void GetTls(uptr *addr, uptr *size) {
*addr = (uptr) dtv[2];
*size = (*addr == 0) ? 0 : ((uptr) segbase[0] - (uptr) dtv[2]);
}
+#elif SANITIZER_ANDROID
+ *addr = 0;
+ *size = 0;
#else
# error "Unknown OS"
#endif
}
+#endif
+#if !SANITIZER_GO
uptr GetTlsSize() {
-#if SANITIZER_FREEBSD
+#if SANITIZER_FREEBSD || SANITIZER_ANDROID
uptr addr, size;
GetTls(&addr, &size);
return size;
@@ -294,9 +371,14 @@ uptr GetTlsSize() {
return g_tls_size;
#endif
}
+#endif
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
uptr *tls_addr, uptr *tls_size) {
+#if SANITIZER_GO
+ // Stub implementation for Go.
+ *stk_addr = *stk_size = *tls_addr = *tls_size = 0;
+#else
GetTls(tls_addr, tls_size);
uptr stack_top, stack_bottom;
@@ -313,8 +395,10 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
*tls_addr = *stk_addr + *stk_size;
}
}
+#endif
}
+#if !SANITIZER_GO
void AdjustStackSize(void *attr_) {
pthread_attr_t *attr = (pthread_attr_t *)attr_;
uptr stackaddr = 0;
@@ -339,14 +423,8 @@ void AdjustStackSize(void *attr_) {
}
}
}
+#endif // !SANITIZER_GO
-#if SANITIZER_ANDROID
-uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
- string_predicate_t filter) {
- MemoryMappingLayout memory_mapping(false);
- return memory_mapping.DumpListOfModules(modules, max_modules, filter);
-}
-#else // SANITIZER_ANDROID
# if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr;
# elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
@@ -367,22 +445,20 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
DlIteratePhdrData *data = (DlIteratePhdrData*)arg;
if (data->current_n == data->max_n)
return 0;
- InternalScopedBuffer<char> module_name(kMaxPathLength);
- module_name.data()[0] = '\0';
+ InternalScopedString module_name(kMaxPathLength);
if (data->first) {
data->first = false;
// First module is the binary itself.
- ReadBinaryName(module_name.data(), module_name.size());
+ ReadBinaryNameCached(module_name.data(), module_name.size());
} else if (info->dlpi_name) {
- internal_strncpy(module_name.data(), info->dlpi_name, module_name.size());
+ module_name.append("%s", info->dlpi_name);
}
- if (module_name.data()[0] == '\0')
+ if (module_name[0] == '\0')
return 0;
if (data->filter && !data->filter(module_name.data()))
return 0;
- void *mem = &data->modules[data->current_n];
- LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(),
- info->dlpi_addr);
+ LoadedModule *cur_module = &data->modules[data->current_n];
+ cur_module->set(module_name.data(), info->dlpi_addr);
data->current_n++;
for (int i = 0; i < info->dlpi_phnum; i++) {
const Elf_Phdr *phdr = &info->dlpi_phdr[i];
@@ -396,36 +472,117 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
return 0;
}
+#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+ int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter) {
+#if SANITIZER_ANDROID && __ANDROID_API__ <= 22
+ u32 api_level = AndroidGetApiLevel();
+ // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken.
+ // The runtime check allows the same library to work with
+ // both K and L (and future) Android releases.
+ if (api_level <= ANDROID_LOLLIPOP_MR1) { // L or earlier
+ MemoryMappingLayout memory_mapping(false);
+ return memory_mapping.DumpListOfModules(modules, max_modules, filter);
+ }
+#endif
CHECK(modules);
DlIteratePhdrData data = {modules, 0, true, max_modules, filter};
dl_iterate_phdr(dl_iterate_phdr_cb, &data);
return data.current_n;
}
-#endif // SANITIZER_ANDROID
-uptr indirect_call_wrapper;
+// getrusage does not give us the current RSS, only the max RSS.
+// Still, this is better than nothing if /proc/self/statm is not available
+// for some reason, e.g. due to a sandbox.
+static uptr GetRSSFromGetrusage() {
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage)) // Failed, probably due to a sandbox.
+ return 0;
+ return usage.ru_maxrss << 10; // ru_maxrss is in Kb.
+}
+
+uptr GetRSS() {
+ if (!common_flags()->can_use_proc_maps_statm)
+ return GetRSSFromGetrusage();
+ fd_t fd = OpenFile("/proc/self/statm", RdOnly);
+ if (fd == kInvalidFd)
+ return GetRSSFromGetrusage();
+ char buf[64];
+ uptr len = internal_read(fd, buf, sizeof(buf) - 1);
+ internal_close(fd);
+ if ((sptr)len <= 0)
+ return 0;
+ buf[len] = 0;
+ // The format of the file is:
+ // 1084 89 69 11 0 79 0
+ // We need the second number which is RSS in pages.
+ char *pos = buf;
+ // Skip the first number.
+ while (*pos >= '0' && *pos <= '9')
+ pos++;
+ // Skip whitespaces.
+ while (!(*pos >= '0' && *pos <= '9') && *pos != 0)
+ pos++;
+ // Read the number.
+ uptr rss = 0;
+ while (*pos >= '0' && *pos <= '9')
+ rss = rss * 10 + *pos++ - '0';
+ return rss * GetPageSizeCached();
+}
+
+// 64-bit Android targets don't provide the deprecated __android_log_write.
+// Starting with the L release, syslog() works and is preferable to
+// __android_log_write.
+#if SANITIZER_LINUX
+
+#if SANITIZER_ANDROID
+static atomic_uint8_t android_log_initialized;
-void SetIndirectCallWrapper(uptr wrapper) {
- CHECK(!indirect_call_wrapper);
- CHECK(wrapper);
- indirect_call_wrapper = wrapper;
+void AndroidLogInit() {
+ atomic_store(&android_log_initialized, 1, memory_order_release);
}
-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);
+static bool IsSyslogAvailable() {
+ return atomic_load(&android_log_initialized, memory_order_acquire);
+}
+#else
+void AndroidLogInit() {}
+
+static bool IsSyslogAvailable() { return true; }
+#endif // SANITIZER_ANDROID
+
+static void WriteOneLineToSyslog(const char *s) {
+#if SANITIZER_ANDROID &&__ANDROID_API__ < 21
+ __android_log_write(ANDROID_LOG_INFO, NULL, s);
+#else
+ syslog(LOG_INFO, "%s", s);
#endif
}
-} // namespace __sanitizer
+void WriteToSyslog(const char *buffer) {
+ if (!IsSyslogAvailable())
+ return;
+ char *copy = internal_strdup(buffer);
+ char *p = copy;
+ char *q;
+ // syslog, at least on Android, has an implicit message length limit.
+ // Print one line at a time.
+ do {
+ q = internal_strchr(p, '\n');
+ if (q)
+ *q = '\0';
+ WriteOneLineToSyslog(p);
+ if (q)
+ p = q + 1;
+ } while (q);
+ InternalFree(copy);
+}
+#endif // SANITIZER_LINUX
+
+} // namespace __sanitizer
-#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/libsanitizer/sanitizer_common/sanitizer_list.h b/libsanitizer/sanitizer_common/sanitizer_list.h
index b72c548e385..9216ede67fb 100644
--- a/libsanitizer/sanitizer_common/sanitizer_list.h
+++ b/libsanitizer/sanitizer_common/sanitizer_list.h
@@ -9,6 +9,7 @@
// ThreadSanitizer, etc run-times.
//
//===----------------------------------------------------------------------===//
+
#ifndef SANITIZER_LIST_H
#define SANITIZER_LIST_H
@@ -27,7 +28,7 @@ struct IntrusiveList {
friend class Iterator;
void clear() {
- first_ = last_ = 0;
+ first_ = last_ = nullptr;
size_ = 0;
}
@@ -36,11 +37,11 @@ struct IntrusiveList {
void push_back(Item *x) {
if (empty()) {
- x->next = 0;
+ x->next = nullptr;
first_ = last_ = x;
size_ = 1;
} else {
- x->next = 0;
+ x->next = nullptr;
last_->next = x;
last_ = x;
size_++;
@@ -49,7 +50,7 @@ struct IntrusiveList {
void push_front(Item *x) {
if (empty()) {
- x->next = 0;
+ x->next = nullptr;
first_ = last_ = x;
size_ = 1;
} else {
@@ -62,8 +63,8 @@ struct IntrusiveList {
void pop_front() {
CHECK(!empty());
first_ = first_->next;
- if (first_ == 0)
- last_ = 0;
+ if (!first_)
+ last_ = nullptr;
size_--;
}
@@ -113,27 +114,31 @@ struct IntrusiveList {
}
}
- class Iterator {
+ template<class ListTy, class ItemTy>
+ class IteratorBase {
public:
- explicit Iterator(IntrusiveList<Item> *list)
+ explicit IteratorBase(ListTy *list)
: list_(list), current_(list->first_) { }
- Item *next() {
- Item *ret = current_;
+ ItemTy *next() {
+ ItemTy *ret = current_;
if (current_) current_ = current_->next;
return ret;
}
- bool hasNext() const { return current_ != 0; }
+ bool hasNext() const { return current_ != nullptr; }
private:
- IntrusiveList<Item> *list_;
- Item *current_;
+ ListTy *list_;
+ ItemTy *current_;
};
+ typedef IteratorBase<IntrusiveList<Item>, Item> Iterator;
+ typedef IteratorBase<const IntrusiveList<Item>, const Item> ConstIterator;
+
// private, don't use directly.
uptr size_;
Item *first_;
Item *last_;
};
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_LIST_H
+#endif // SANITIZER_LIST_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_mac.cc
index 17b931cb535..8cc07f12788 100644
--- a/libsanitizer/sanitizer_common/sanitizer_mac.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_mac.cc
@@ -25,21 +25,30 @@
#include "sanitizer_libc.h"
#include "sanitizer_mac.h"
#include "sanitizer_placement_new.h"
+#include "sanitizer_platform_limits_posix.h"
#include "sanitizer_procmaps.h"
+#if !SANITIZER_IOS
#include <crt_externs.h> // for _NSGetEnviron
+#else
+extern char **environ;
+#endif
+
+#include <errno.h>
#include <fcntl.h>
+#include <libkern/OSAtomic.h>
+#include <mach-o/dyld.h>
+#include <mach/mach.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
+#include <stdlib.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
-#include <libkern/OSAtomic.h>
-#include <errno.h>
namespace __sanitizer {
@@ -55,6 +64,10 @@ uptr internal_munmap(void *addr, uptr length) {
return munmap(addr, length);
}
+int internal_mprotect(void *addr, uptr length, int prot) {
+ return mprotect(addr, length, prot);
+}
+
uptr internal_close(fd_t fd) {
return close(fd);
}
@@ -67,11 +80,6 @@ uptr internal_open(const char *filename, int flags, u32 mode) {
return open(filename, flags, mode);
}
-uptr OpenFile(const char *filename, bool write) {
- return internal_open(filename,
- write ? O_WRONLY | O_CREAT : O_RDONLY, 0660);
-}
-
uptr internal_read(fd_t fd, void *buf, uptr count) {
return read(fd, buf, count);
}
@@ -107,6 +115,10 @@ uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
return readlink(path, buf, bufsize);
}
+uptr internal_unlink(const char *path) {
+ return unlink(path);
+}
+
uptr internal_sched_yield() {
return sched_yield();
}
@@ -124,6 +136,13 @@ int internal_sigaction(int signum, const void *act, void *oldact) {
(struct sigaction *)act, (struct sigaction *)oldact);
}
+void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); }
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ return sigprocmask(how, set, oldset);
+}
+
int internal_fork() {
// TODO(glider): this may call user's pthread_atfork() handlers which is bad.
return fork();
@@ -174,7 +193,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
*stack_bottom = *stack_top - stacksize;
}
-const char *GetEnv(const char *name) {
+char **GetEnviron() {
+#if !SANITIZER_IOS
char ***env_ptr = _NSGetEnviron();
if (!env_ptr) {
Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
@@ -182,37 +202,53 @@ const char *GetEnv(const char *name) {
CHECK(env_ptr);
}
char **environ = *env_ptr;
+#endif
CHECK(environ);
+ return environ;
+}
+
+const char *GetEnv(const char *name) {
+ char **env = GetEnviron();
uptr name_len = internal_strlen(name);
- while (*environ != 0) {
- uptr len = internal_strlen(*environ);
+ while (*env != 0) {
+ uptr len = internal_strlen(*env);
if (len > name_len) {
- const char *p = *environ;
+ const char *p = *env;
if (!internal_memcmp(p, name, name_len) &&
p[name_len] == '=') { // Match.
- return *environ + name_len + 1; // String starting after =.
+ return *env + name_len + 1; // String starting after =.
}
}
- environ++;
+ env++;
}
return 0;
}
-void ReExec() {
- UNIMPLEMENTED();
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ CHECK_LE(kMaxPathLength, buf_len);
+
+ // On OS X the executable path is saved to the stack by dyld. Reading it
+ // from there is much faster than calling dladdr, especially for large
+ // binaries with symbols.
+ InternalScopedString exe_path(kMaxPathLength);
+ uint32_t size = exe_path.size();
+ if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
+ realpath(exe_path.data(), buf) != 0) {
+ return internal_strlen(buf);
+ }
+ return 0;
}
-void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
- (void)args;
- // Nothing here for now.
+uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
+ return ReadBinaryName(buf, buf_len);
}
-uptr GetPageSize() {
- return sysconf(_SC_PAGESIZE);
+void ReExec() {
+ UNIMPLEMENTED();
}
-BlockingMutex::BlockingMutex(LinkerInitialized) {
- // We assume that OS_SPINLOCK_INIT is zero
+uptr GetPageSize() {
+ return sysconf(_SC_PAGESIZE);
}
BlockingMutex::BlockingMutex() {
@@ -296,7 +332,11 @@ MacosVersion GetMacosVersionInternal() {
case '2': return MACOS_VERSION_MOUNTAIN_LION;
case '3': return MACOS_VERSION_MAVERICKS;
case '4': return MACOS_VERSION_YOSEMITE;
- default: return MACOS_VERSION_UNKNOWN;
+ default:
+ if (IsDigit(version[1]))
+ return MACOS_VERSION_UNKNOWN_NEWER;
+ else
+ return MACOS_VERSION_UNKNOWN;
}
}
default: return MACOS_VERSION_UNKNOWN;
@@ -315,6 +355,48 @@ MacosVersion GetMacosVersion() {
return result;
}
+uptr GetRSS() {
+ struct task_basic_info info;
+ unsigned count = TASK_BASIC_INFO_COUNT;
+ kern_return_t result =
+ task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count);
+ if (UNLIKELY(result != KERN_SUCCESS)) {
+ Report("Cannot get task info. Error: %d\n", result);
+ Die();
+ }
+ return info.resident_size;
+}
+
+void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
+void internal_join_thread(void *th) { }
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+ ucontext_t *ucontext = (ucontext_t*)context;
+# if defined(__aarch64__)
+ *pc = ucontext->uc_mcontext->__ss.__pc;
+# if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
+ *bp = ucontext->uc_mcontext->__ss.__fp;
+# else
+ *bp = ucontext->uc_mcontext->__ss.__lr;
+# endif
+ *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__x86_64__)
+ *pc = ucontext->uc_mcontext->__ss.__rip;
+ *bp = ucontext->uc_mcontext->__ss.__rbp;
+ *sp = ucontext->uc_mcontext->__ss.__rsp;
+# elif defined(__arm__)
+ *pc = ucontext->uc_mcontext->__ss.__pc;
+ *bp = ucontext->uc_mcontext->__ss.__r[7];
+ *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__i386__)
+ *pc = ucontext->uc_mcontext->__ss.__eip;
+ *bp = ucontext->uc_mcontext->__ss.__ebp;
+ *sp = ucontext->uc_mcontext->__ss.__esp;
+# else
+# error "Unknown architecture"
+# endif
+}
+
} // namespace __sanitizer
#endif // SANITIZER_MAC
diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.h b/libsanitizer/sanitizer_common/sanitizer_mac.h
index 47739f71c3c..2aac83e3657 100644
--- a/libsanitizer/sanitizer_common/sanitizer_mac.h
+++ b/libsanitizer/sanitizer_common/sanitizer_mac.h
@@ -13,6 +13,7 @@
#include "sanitizer_platform.h"
#if SANITIZER_MAC
+#include "sanitizer_posix.h"
namespace __sanitizer {
@@ -25,10 +26,13 @@ enum MacosVersion {
MACOS_VERSION_MOUNTAIN_LION,
MACOS_VERSION_MAVERICKS,
MACOS_VERSION_YOSEMITE,
+ MACOS_VERSION_UNKNOWN_NEWER
};
MacosVersion GetMacosVersion();
+char **GetEnviron();
+
} // namespace __sanitizer
#endif // SANITIZER_MAC
diff --git a/libsanitizer/sanitizer_common/sanitizer_mutex.h b/libsanitizer/sanitizer_common/sanitizer_mutex.h
index adc3add6008..75f495a4d65 100644
--- a/libsanitizer/sanitizer_common/sanitizer_mutex.h
+++ b/libsanitizer/sanitizer_common/sanitizer_mutex.h
@@ -71,7 +71,13 @@ class SpinMutex : public StaticSpinMutex {
class BlockingMutex {
public:
+#if SANITIZER_WINDOWS
+ // Windows does not currently support LinkerInitialized
explicit BlockingMutex(LinkerInitialized);
+#else
+ explicit constexpr BlockingMutex(LinkerInitialized)
+ : opaque_storage_ {0, }, owner_(0) {}
+#endif
BlockingMutex();
void Lock();
void Unlock();
diff --git a/libsanitizer/sanitizer_common/sanitizer_persistent_allocator.h b/libsanitizer/sanitizer_common/sanitizer_persistent_allocator.h
index e29b7bd57aa..71185033eff 100644
--- a/libsanitizer/sanitizer_common/sanitizer_persistent_allocator.h
+++ b/libsanitizer/sanitizer_common/sanitizer_persistent_allocator.h
@@ -8,6 +8,7 @@
// 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
@@ -34,7 +35,7 @@ inline void *PersistentAllocator::tryAlloc(uptr size) {
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 (cmp == 0 || cmp + size > end) return nullptr;
if (atomic_compare_exchange_weak(&region_pos, &cmp, cmp + size,
memory_order_acquire))
return (void *)cmp;
@@ -66,4 +67,4 @@ inline void *PersistentAlloc(uptr sz) {
} // namespace __sanitizer
-#endif // SANITIZER_PERSISTENT_ALLOCATOR_H
+#endif // SANITIZER_PERSISTENT_ALLOCATOR_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_platform.h b/libsanitizer/sanitizer_common/sanitizer_platform.h
index e0e740225e2..6a4eab333cd 100644
--- a/libsanitizer/sanitizer_common/sanitizer_platform.h
+++ b/libsanitizer/sanitizer_common/sanitizer_platform.h
@@ -36,9 +36,15 @@
# else
# define SANITIZER_IOS 0
# endif
+# if TARGET_IPHONE_SIMULATOR
+# define SANITIZER_IOSSIM 1
+# else
+# define SANITIZER_IOSSIM 0
+# endif
#else
# define SANITIZER_MAC 0
# define SANITIZER_IOS 0
+# define SANITIZER_IOSSIM 0
#endif
#if defined(_WIN32)
@@ -73,13 +79,23 @@
# define SANITIZER_X32 0
#endif
+// VMA size definition for architecture that support multiple sizes.
+// AArch64 has 3 VMA sizes: 39, 42 and 48.
+#if !defined(SANITIZER_AARCH64_VMA)
+# define SANITIZER_AARCH64_VMA 39
+#else
+# if SANITIZER_AARCH64_VMA != 39 && SANITIZER_AARCH64_VMA != 42
+# error "invalid SANITIZER_AARCH64_VMA size"
+# endif
+#endif
+
// By default we allow to use SizeClassAllocator64 on 64-bit platform.
// But in some cases (e.g. AArch64's 39-bit address space) SizeClassAllocator64
// does not work well and we need to fallback to SizeClassAllocator32.
// For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
// change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
#ifndef SANITIZER_CAN_USE_ALLOCATOR64
-# if defined(__aarch64__) || defined(__mips64)
+# if defined(__mips64) || (defined(__aarch64__) && SANITIZER_AARCH64_VMA == 39)
# define SANITIZER_CAN_USE_ALLOCATOR64 0
# else
# define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64)
@@ -91,7 +107,13 @@
// e.g. on AArch64 it is most likely (1ULL << 39). Larger values will still work
// but will consume more memory for TwoLevelByteMap.
#if defined(__aarch64__)
-# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 39)
+# if SANITIZER_AARCH64_VMA == 39
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 39)
+# elif SANITIZER_AARCH64_VMA == 42
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 42)
+# endif
+#elif defined(__mips__)
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
#else
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
#endif
@@ -120,8 +142,10 @@
#define SANITIZER_USES_UID16_SYSCALLS 0
#endif
-#ifdef __mips__
+#if defined(__mips__) || (defined(__aarch64__) && SANITIZER_AARCH64_VMA == 39)
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10)
+#elif defined(__aarch64__) && SANITIZER_AARCH64_VMA == 42
+# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 11)
#else
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
#endif
@@ -132,4 +156,15 @@
# define HAVE_TIRPC_RPC_XDR_H 0
#endif
+/// \macro MSC_PREREQ
+/// \brief Is the compiler MSVC of at least the specified version?
+/// The common \param version values to check for are:
+/// * 1800: Microsoft Visual Studio 2013 / 12.0
+/// * 1900: Microsoft Visual Studio 2015 / 14.0
+#ifdef _MSC_VER
+# define MSC_PREREQ(version) (_MSC_VER >= (version))
+#else
+# define MSC_PREREQ(version) 0
+#endif
+
#endif // SANITIZER_PLATFORM_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h
index fc2d7ba990c..c8adc5fcaed 100644
--- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h
@@ -52,10 +52,15 @@
#endif
#define SANITIZER_INTERCEPT_STRCMP 1
+#define SANITIZER_INTERCEPT_STRSTR 1
+#define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_STRSPN 1
+#define SANITIZER_INTERCEPT_STRPBRK 1
#define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MEMCMP 1
#define SANITIZER_INTERCEPT_MEMCHR 1
-#define SANITIZER_INTERCEPT_MEMRCHR SI_LINUX
+#define SANITIZER_INTERCEPT_MEMRCHR SI_FREEBSD || SI_LINUX
#define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS
@@ -68,7 +73,7 @@
#define SANITIZER_INTERCEPT_READV SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_WRITEV SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_PREADV SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_PREADV SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID
@@ -83,6 +88,7 @@
#ifndef SANITIZER_INTERCEPT_PRINTF
# define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_PRINTF_L SI_FREEBSD
# define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID
#endif
@@ -91,12 +97,13 @@
#define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \
- SI_MAC || SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_GETPWENT SI_MAC || SI_LINUX_NOT_ANDROID
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETPWENT \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_GETPWENT_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETPWENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SETPWENT SI_MAC || SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_LINUX
+#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_FREEBSD || SI_LINUX
#define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID
@@ -107,10 +114,10 @@
#define SANITIZER_INTERCEPT_GETNAMEINFO SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX
-#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_FREEBSD || SI_LINUX
+#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX_NOT_ANDROID
@@ -123,7 +130,8 @@
#define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \
- (defined(__i386) || defined (__x86_64)) // NOLINT
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__))
#define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID
@@ -131,21 +139,28 @@
#define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WCSNRTOMBS \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WCRTOMB \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_CONFSTR \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_SCANDIR SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCANDIR \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_WORDEXP (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_WORDEXP \
+ SI_FREEBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID
@@ -153,24 +168,25 @@
SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_BACKTRACE SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
#define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_STATFS SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATFS SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STATFS64 \
(SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_STATVFS SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_INITGROUPS 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_HOST \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_ETHER_R SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SHMCTL \
- (SI_LINUX_NOT_ANDROID && SANITIZER_WORDSIZE == 64)
+ ((SI_FREEBSD || SI_LINUX_NOT_ANDROID) && SANITIZER_WORDSIZE == 64)
#define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \
- SI_MAC || SI_LINUX_NOT_ANDROID
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE SI_NOT_WINDOWS
@@ -191,15 +207,16 @@
#define SANITIZER_INTERCEPT_SINCOS SI_LINUX
#define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX
+#define SANITIZER_INTERCEPT_LGAMMA_R SI_FREEBSD || SI_LINUX
#define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_RAND_R \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_ICONV SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS
// FIXME: getline seems to be available on OSX 10.7
-#define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD
@@ -207,17 +224,20 @@
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \
SI_FREEBSD || SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_TLS_GET_ADDR SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_TLS_GET_ADDR \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX
#define SANITIZER_INTERCEPT_GETXATTR SI_LINUX
#define SANITIZER_INTERCEPT_GETRESID SI_LINUX
-#define SANITIZER_INTERCEPT_GETIFADDRS SI_LINUX_NOT_ANDROID || SI_MAC
-#define SANITIZER_INTERCEPT_IF_INDEXTONAME SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_GETIFADDRS \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_IF_INDEXTONAME \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
#define SANITIZER_INTERCEPT_CAPGET SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_AEABI_MEM SI_LINUX && defined(__arm__)
#define SANITIZER_INTERCEPT___BZERO SI_MAC
-#define SANITIZER_INTERCEPT_FTIME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FTIME !SI_FREEBSD && SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TSEARCH SI_LINUX_NOT_ANDROID || SI_MAC
#define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID
@@ -233,5 +253,11 @@
#define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FOPENCOOKIE SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SEM SI_LINUX || SI_FREEBSD
+#define SANITIZER_INTERCEPT_PTHREAD_SETCANCEL SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_MINCORE SI_LINUX
+
+#define SANITIZER_INTERCEPTOR_HOOKS SI_LINUX
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc
index a1f04325033..8779d8adf72 100644
--- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc
@@ -36,7 +36,6 @@
#define uid_t __kernel_uid_t
#define gid_t __kernel_gid_t
#define off_t __kernel_off_t
-#define time_t __kernel_time_t
// This header seems to contain the definitions of _kernel_ stat* structs.
#include <asm/stat.h>
#undef ino_t
@@ -61,7 +60,7 @@ namespace __sanitizer {
} // namespace __sanitizer
#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)\
- && !defined(__mips__) && !defined(__sparc__)
+ && !defined(__mips__)
COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
#endif
diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc
index 2ef4ca22276..05f8c06a498 100644
--- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc
@@ -10,8 +10,8 @@
// Sizes and layouts of platform-specific POSIX data structures.
//===----------------------------------------------------------------------===//
-
#include "sanitizer_platform.h"
+
#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
// Tests in this file assume that off_t-dependent data structures match the
// libc ABI. For example, struct dirent here is what readdir() function (as
@@ -32,8 +32,6 @@
#include <grp.h>
#include <limits.h>
#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/route.h>
#include <netdb.h>
#include <poll.h>
#include <pthread.h>
@@ -52,6 +50,10 @@
#include <time.h>
#include <wchar.h>
+#if !SANITIZER_IOS
+#include <net/route.h>
+#endif
+
#if !SANITIZER_ANDROID
#include <sys/mount.h>
#include <sys/timeb.h>
@@ -73,6 +75,7 @@
#include <linux/sysctl.h>
#include <linux/utsname.h>
#include <linux/posix_types.h>
+#include <net/if_arp.h>
#endif
#if SANITIZER_FREEBSD
@@ -95,7 +98,6 @@
# include <sys/link_elf.h>
# include <netinet/ip_mroute.h>
# include <netinet/in.h>
-# include <netinet/ip_compat.h>
# include <net/ethernet.h>
# include <net/ppp_defs.h>
# include <glob.h>
@@ -115,6 +117,10 @@
#if SANITIZER_LINUX || SANITIZER_FREEBSD
# include <utime.h>
# include <sys/ptrace.h>
+# if defined(__mips64) || defined(__aarch64__)
+# include <asm/ptrace.h>
+# endif
+# include <semaphore.h>
#endif
#if !SANITIZER_ANDROID
@@ -142,6 +148,9 @@
#include <sys/shm.h>
#include <sys/statvfs.h>
#include <sys/timex.h>
+#if defined(__mips64)
+# include <sys/procfs.h>
+#endif
#include <sys/user.h>
#include <sys/ustat.h>
#include <linux/cyclades.h>
@@ -185,7 +194,7 @@ namespace __sanitizer {
unsigned struct_stat_sz = sizeof(struct stat);
#if !SANITIZER_IOS && !SANITIZER_FREEBSD
unsigned struct_stat64_sz = sizeof(struct stat64);
-#endif // !SANITIZER_IOS && !SANITIZER_FREEBSD
+#endif // !SANITIZER_IOS && !SANITIZER_FREEBSD
unsigned struct_rusage_sz = sizeof(struct rusage);
unsigned struct_tm_sz = sizeof(struct tm);
unsigned struct_passwd_sz = sizeof(struct passwd);
@@ -226,27 +235,27 @@ namespace __sanitizer {
unsigned struct_new_utsname_sz = sizeof(struct new_utsname);
unsigned struct_old_utsname_sz = sizeof(struct old_utsname);
unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname);
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX || SANITIZER_FREEBSD
unsigned struct_rlimit_sz = sizeof(struct rlimit);
unsigned struct_timespec_sz = sizeof(struct timespec);
unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned struct_ustat_sz = sizeof(struct ustat);
unsigned struct_rlimit64_sz = sizeof(struct rlimit64);
unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
unsigned struct_timex_sz = sizeof(struct timex);
unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
unsigned struct_statvfs_sz = sizeof(struct statvfs);
-#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
uptr sig_ign = (uptr)SIG_IGN;
uptr sig_dfl = (uptr)SIG_DFL;
@@ -280,30 +289,61 @@ namespace __sanitizer {
return 0;
}
+#if SANITIZER_LINUX
+unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr));
+#elif SANITIZER_FREEBSD
+unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
+#endif
+
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
int glob_nomatch = GLOB_NOMATCH;
int glob_altdirfunc = GLOB_ALTDIRFUNC;
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64))
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__))
+#if defined(__mips64) || defined(__powerpc64__)
+ unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs);
+ unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t);
+#elif defined(__aarch64__)
+ unsigned struct_user_regs_struct_sz = sizeof(struct user_pt_regs);
+ unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpsimd_state);
+#else
unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct);
unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct);
-#ifdef __x86_64
+#endif // __mips64 || __powerpc64__ || __aarch64__
+#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
+ defined(__aarch64__)
unsigned struct_user_fpxregs_struct_sz = 0;
#else
unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
-#endif
+#endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__
int ptrace_peektext = PTRACE_PEEKTEXT;
int ptrace_peekdata = PTRACE_PEEKDATA;
int ptrace_peekuser = PTRACE_PEEKUSER;
+#if defined(PT_GETREGS) && defined(PT_SETREGS)
int ptrace_getregs = PTRACE_GETREGS;
int ptrace_setregs = PTRACE_SETREGS;
+#else
+ int ptrace_getregs = -1;
+ int ptrace_setregs = -1;
+#endif
+#if defined(PT_GETFPREGS) && defined(PT_SETFPREGS)
int ptrace_getfpregs = PTRACE_GETFPREGS;
int ptrace_setfpregs = PTRACE_SETFPREGS;
+#else
+ int ptrace_getfpregs = -1;
+ int ptrace_setfpregs = -1;
+#endif
+#if defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS)
int ptrace_getfpxregs = PTRACE_GETFPXREGS;
int ptrace_setfpxregs = PTRACE_SETFPXREGS;
+#else
+ int ptrace_getfpxregs = -1;
+ int ptrace_setfpxregs = -1;
+#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS
int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
(defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
@@ -312,25 +352,25 @@ namespace __sanitizer {
#else
int ptrace_getsiginfo = -1;
int ptrace_setsiginfo = -1;
-#endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO
+#endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO
#if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET)
int ptrace_getregset = PTRACE_GETREGSET;
int ptrace_setregset = PTRACE_SETREGSET;
#else
int ptrace_getregset = -1;
int ptrace_setregset = -1;
-#endif // PTRACE_GETREGSET/PTRACE_SETREGSET
+#endif // PTRACE_GETREGSET/PTRACE_SETREGSET
#endif
unsigned path_max = PATH_MAX;
// ioctl arguments
- unsigned struct_arpreq_sz = sizeof(struct arpreq);
unsigned struct_ifreq_sz = sizeof(struct ifreq);
unsigned struct_termios_sz = sizeof(struct termios);
unsigned struct_winsize_sz = sizeof(struct winsize);
#if SANITIZER_LINUX
+ unsigned struct_arpreq_sz = sizeof(struct arpreq);
unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf);
unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession);
unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio);
@@ -357,7 +397,7 @@ namespace __sanitizer {
unsigned struct_vt_consize_sz = sizeof(struct vt_consize);
unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes);
unsigned struct_vt_stat_sz = sizeof(struct vt_stat);
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX || SANITIZER_FREEBSD
#if SOUND_VERSION >= 0x040000
@@ -377,7 +417,7 @@ namespace __sanitizer {
unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec);
unsigned struct_synth_info_sz = sizeof(struct synth_info);
unsigned struct_vt_mode_sz = sizeof(struct vt_mode);
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct);
@@ -402,12 +442,12 @@ namespace __sanitizer {
unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25);
unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc);
unsigned struct_unimapinit_sz = sizeof(struct unimapinit);
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
-#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
#if !SANITIZER_ANDROID && !SANITIZER_MAC
unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
@@ -622,7 +662,7 @@ namespace __sanitizer {
unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE;
unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS;
unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER;
-#endif // SOUND_VERSION
+#endif // SOUND_VERSION
unsigned IOCTL_TCFLSH = TCFLSH;
unsigned IOCTL_TCGETA = TCGETA;
unsigned IOCTL_TCGETS = TCGETS;
@@ -745,7 +785,7 @@ namespace __sanitizer {
unsigned IOCTL_VT_RELDISP = VT_RELDISP;
unsigned IOCTL_VT_SETMODE = VT_SETMODE;
unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH;
@@ -836,7 +876,7 @@ namespace __sanitizer {
unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI;
unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI;
unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL;
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
@@ -854,7 +894,7 @@ namespace __sanitizer {
unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP;
unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE;
-#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
const int errno_EINVAL = EINVAL;
// EOWNERDEAD is not present in some older platforms.
@@ -866,7 +906,7 @@ namespace __sanitizer {
const int si_SEGV_MAPERR = SEGV_MAPERR;
const int si_SEGV_ACCERR = SEGV_ACCERR;
-} // namespace __sanitizer
+} // namespace __sanitizer
COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
@@ -896,7 +936,7 @@ COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678));
COMPILER_CHECK(IOC_DIR(0x12345678) == _IOC_DIR(0x12345678));
COMPILER_CHECK(IOC_NR(0x12345678) == _IOC_NR(0x12345678));
COMPILER_CHECK(IOC_TYPE(0x12345678) == _IOC_TYPE(0x12345678));
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX || SANITIZER_FREEBSD
// There are more undocumented fields in dl_phdr_info that we are not interested
@@ -906,7 +946,7 @@ CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
CHECK_TYPE_SIZE(glob_t);
@@ -1103,14 +1143,14 @@ CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
# if SANITIZER_FREEBSD
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
# else
-COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)NULL)->ifa_dstaddr) ==
- sizeof(((ifaddrs *)NULL)->ifa_ifu));
+COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)nullptr)->ifa_dstaddr) ==
+ sizeof(((ifaddrs *)nullptr)->ifa_ifu));
COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) ==
offsetof(ifaddrs, ifa_ifu));
-# endif // SANITIZER_FREEBSD
+# endif // SANITIZER_FREEBSD
#else
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_LINUX
CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
#endif
@@ -1192,6 +1232,16 @@ CHECK_SIZE_AND_OFFSET(obstack, chunk_size);
CHECK_SIZE_AND_OFFSET(obstack, chunk);
CHECK_SIZE_AND_OFFSET(obstack, object_base);
CHECK_SIZE_AND_OFFSET(obstack, next_free);
+
+CHECK_TYPE_SIZE(cookie_io_functions_t);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, read);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, write);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close);
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+CHECK_TYPE_SIZE(sem_t);
#endif
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h
index 657cd85264e..53118b5bde5 100644
--- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -16,6 +16,15 @@
#include "sanitizer_internal_defs.h"
#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD
+// FreeBSD's dlopen() returns a pointer to an Obj_Entry structure that
+// incroporates the map structure.
+# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
+ ((link_map*)((handle) == nullptr ? nullptr : ((char*)(handle) + 544)))
+#else
+# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) ((link_map*)(handle))
+#endif // !SANITIZER_FREEBSD
+
namespace __sanitizer {
extern unsigned struct_utsname_sz;
extern unsigned struct_stat_sz;
@@ -72,14 +81,6 @@ namespace __sanitizer {
const unsigned struct_kernel_stat_sz = 144;
#endif
const unsigned struct_kernel_stat64_sz = 104;
-#elif defined(__sparc__) && defined(__arch64__)
- const unsigned struct___old_kernel_stat_sz = 0;
- const unsigned struct_kernel_stat_sz = 104;
- const unsigned struct_kernel_stat64_sz = 144;
-#elif defined(__sparc__) && !defined(__arch64__)
- const unsigned struct___old_kernel_stat_sz = 0;
- const unsigned struct_kernel_stat_sz = 64;
- const unsigned struct_kernel_stat64_sz = 104;
#endif
struct __sanitizer_perf_event_attr {
unsigned type;
@@ -102,7 +103,7 @@ namespace __sanitizer {
#if defined(__powerpc64__)
const unsigned struct___old_kernel_stat_sz = 0;
-#elif !defined(__sparc__)
+#else
const unsigned struct___old_kernel_stat_sz = 32;
#endif
@@ -147,6 +148,18 @@ namespace __sanitizer {
};
const unsigned old_sigset_t_sz = sizeof(unsigned long);
+
+ struct __sanitizer_sem_t {
+#if SANITIZER_ANDROID && defined(_LP64)
+ int data[4];
+#elif SANITIZER_ANDROID && !defined(_LP64)
+ int data;
+#elif SANITIZER_LINUX
+ uptr data[4];
+#elif SANITIZER_FREEBSD
+ u32 data[4];
+#endif
+ };
#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
#if SANITIZER_ANDROID
@@ -181,18 +194,6 @@ namespace __sanitizer {
unsigned short __pad1;
unsigned long __unused1;
unsigned long __unused2;
-#elif defined(__sparc__)
-# if defined(__arch64__)
- unsigned mode;
- unsigned short __pad1;
-# else
- unsigned short __pad1;
- unsigned short mode;
- unsigned short __pad2;
-# endif
- unsigned short __seq;
- unsigned long long __unused1;
- unsigned long long __unused2;
#else
unsigned short mode;
unsigned short __pad1;
@@ -210,26 +211,6 @@ namespace __sanitizer {
struct __sanitizer_shmid_ds {
__sanitizer_ipc_perm shm_perm;
- #if defined(__sparc__)
- # if !defined(__arch64__)
- u32 __pad1;
- # endif
- long shm_atime;
- # if !defined(__arch64__)
- u32 __pad2;
- # endif
- long shm_dtime;
- # if !defined(__arch64__)
- u32 __pad3;
- # endif
- long shm_ctime;
- uptr shm_segsz;
- int shm_cpid;
- int shm_lpid;
- unsigned long shm_nattch;
- unsigned long __glibc_reserved1;
- unsigned long __glibc_reserved2;
- #else
#ifndef __powerpc__
uptr shm_segsz;
#elif !defined(__powerpc64__)
@@ -267,7 +248,6 @@ namespace __sanitizer {
uptr __unused4;
uptr __unused5;
#endif
-#endif
};
#elif SANITIZER_FREEBSD
struct __sanitizer_ipc_perm {
@@ -351,7 +331,7 @@ namespace __sanitizer {
long pw_change;
char *pw_class;
#endif
-#if !SANITIZER_ANDROID
+#if !(SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32))
char *pw_gecos;
#endif
char *pw_dir;
@@ -413,7 +393,7 @@ namespace __sanitizer {
};
#endif
-#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_MAC || SANITIZER_FREEBSD
struct __sanitizer_msghdr {
void *msg_name;
unsigned msg_namelen;
@@ -550,6 +530,27 @@ namespace __sanitizer {
#endif
// Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
+#if SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 64)
+ struct __sanitizer_sigaction {
+ unsigned sa_flags;
+ union {
+ void (*sigaction)(int sig, void *siginfo, void *uctx);
+ void (*handler)(int sig);
+ };
+ __sanitizer_sigset_t sa_mask;
+ void (*sa_restorer)();
+ };
+#elif SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32)
+ struct __sanitizer_sigaction {
+ union {
+ void (*sigaction)(int sig, void *siginfo, void *uctx);
+ void (*handler)(int sig);
+ };
+ __sanitizer_sigset_t sa_mask;
+ uptr sa_flags;
+ void (*sa_restorer)();
+ };
+#else // !SANITIZER_ANDROID
struct __sanitizer_sigaction {
#if defined(__mips__) && !SANITIZER_FREEBSD
unsigned int sa_flags;
@@ -564,13 +565,9 @@ namespace __sanitizer {
#else
__sanitizer_sigset_t sa_mask;
#ifndef __mips__
-#if defined(__sparc__)
- unsigned long sa_flags;
-#else
int sa_flags;
#endif
#endif
-#endif
#if SANITIZER_LINUX
void (*sa_restorer)();
#endif
@@ -578,9 +575,14 @@ namespace __sanitizer {
int sa_resv[1];
#endif
};
+#endif // !SANITIZER_ANDROID
#if SANITIZER_FREEBSD
typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
+#elif defined(__mips__)
+ struct __sanitizer_kernel_sigset_t {
+ u8 sig[16];
+ };
#else
struct __sanitizer_kernel_sigset_t {
u8 sig[8];
@@ -617,6 +619,8 @@ namespace __sanitizer {
const void *dlpi_phdr;
short dlpi_phnum;
};
+
+ extern unsigned struct_ElfW_Phdr_sz;
#endif
struct __sanitizer_addrinfo {
@@ -729,7 +733,8 @@ namespace __sanitizer {
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64))
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__))
extern unsigned struct_user_regs_struct_sz;
extern unsigned struct_user_fpregs_struct_sz;
extern unsigned struct_user_fpxregs_struct_sz;
@@ -786,11 +791,25 @@ struct __sanitizer_obstack {
char *next_free;
uptr more_fields[7];
};
+
+typedef uptr (*__sanitizer_cookie_io_read)(void *cookie, char *buf, uptr size);
+typedef uptr (*__sanitizer_cookie_io_write)(void *cookie, const char *buf,
+ uptr size);
+typedef int (*__sanitizer_cookie_io_seek)(void *cookie, u64 *offset,
+ int whence);
+typedef int (*__sanitizer_cookie_io_close)(void *cookie);
+
+struct __sanitizer_cookie_io_functions_t {
+ __sanitizer_cookie_io_read read;
+ __sanitizer_cookie_io_write write;
+ __sanitizer_cookie_io_seek seek;
+ __sanitizer_cookie_io_close close;
+};
#endif
#define IOC_NRBITS 8
#define IOC_TYPEBITS 8
-#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__) || defined(__sparc__)
+#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__)
#define IOC_SIZEBITS 13
#define IOC_DIRBITS 3
#define IOC_NONE 1U
@@ -820,24 +839,14 @@ struct __sanitizer_obstack {
#define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK)
#define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK)
#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
-
-#if defined(__sparc__)
-// In sparc the 14 bits SIZE field overlaps with the
-// least significant bit of DIR, so either IOC_READ or
-// IOC_WRITE shall be 1 in order to get a non-zero SIZE.
-# define IOC_SIZE(nr) \
- ((((((nr) >> 29) & 0x7) & (4U|2U)) == 0)? \
- 0 : (((nr) >> 16) & 0x3fff))
-#else
#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
-#endif
- extern unsigned struct_arpreq_sz;
extern unsigned struct_ifreq_sz;
extern unsigned struct_termios_sz;
extern unsigned struct_winsize_sz;
#if SANITIZER_LINUX
+ extern unsigned struct_arpreq_sz;
extern unsigned struct_cdrom_msf_sz;
extern unsigned struct_cdrom_multisession_sz;
extern unsigned struct_cdrom_read_audio_sz;
diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.cc b/libsanitizer/sanitizer_common/sanitizer_posix.cc
index adb1b2a9d68..1438827e100 100644
--- a/libsanitizer/sanitizer_common/sanitizer_posix.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_posix.cc
@@ -7,17 +7,21 @@
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries and implements POSIX-specific functions from
-// sanitizer_libc.h.
+// sanitizer_posix.h.
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
+
#if SANITIZER_POSIX
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
+#include "sanitizer_posix.h"
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
+#include <fcntl.h>
+#include <signal.h>
#include <sys/mman.h>
#if SANITIZER_LINUX
@@ -28,6 +32,13 @@
#include <sys/personality.h>
#endif
+#if SANITIZER_FREEBSD
+// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
+// that, it was never implemented. So just define it to zero.
+#undef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
namespace __sanitizer {
// ------------- sanitizer_common.h
@@ -38,15 +49,15 @@ uptr GetMmapGranularity() {
#if SANITIZER_WORDSIZE == 32
// Take care of unusable kernel area in top gigabyte.
static uptr GetKernelAreaSize() {
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_X32
const uptr gbyte = 1UL << 30;
// Firstly check if there are writable segments
// mapped to top gigabyte (e.g. stack).
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
uptr end, prot;
- while (proc_maps.Next(/*start*/0, &end,
- /*offset*/0, /*filename*/0,
+ while (proc_maps.Next(/*start*/nullptr, &end,
+ /*offset*/nullptr, /*filename*/nullptr,
/*filename_size*/0, &prot)) {
if ((end >= 3 * gbyte)
&& (prot & MemoryMappingLayout::kProtectionWrite) != 0)
@@ -70,7 +81,7 @@ static uptr GetKernelAreaSize() {
return gbyte;
#else
return 0;
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_LINUX && !SANITIZER_X32
}
#endif // SANITIZER_WORDSIZE == 32
@@ -86,7 +97,7 @@ uptr GetMaxVirtualAddress() {
// Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit.
return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
# elif defined(__mips64)
- return (1ULL << 40) - 1;
+ return (1ULL << 40) - 1; // 0x000000ffffffffffUL;
# else
return (1ULL << 47) - 1; // 0x00007fffffffffffUL;
# endif
@@ -101,25 +112,12 @@ uptr GetMaxVirtualAddress() {
void *MmapOrDie(uptr size, const char *mem_type) {
size = RoundUpTo(size, GetPageSizeCached());
- uptr res = internal_mmap(0, size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON, -1, 0);
+ uptr res = internal_mmap(nullptr, size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
int reserrno;
- if (internal_iserror(res, &reserrno)) {
- static int recursion_count;
- if (recursion_count) {
- // The Report() and CHECK calls below may call mmap recursively and fail.
- // If we went into recursion, just die.
- RawWrite("ERROR: Failed to mmap\n");
- Die();
- }
- recursion_count++;
- Report("ERROR: %s failed to "
- "allocate 0x%zx (%zd) bytes of %s (errno: %d)\n",
- SanitizerToolName, size, size, mem_type, reserrno);
- DumpProcessMap();
- CHECK("unable to mmap" && 0);
- }
+ if (internal_iserror(res, &reserrno))
+ ReportMmapFailureAndDie(size, mem_type, reserrno);
IncreaseTotalMmap(size);
return (void *)res;
}
@@ -137,11 +135,11 @@ void UnmapOrDie(void *addr, uptr size) {
void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
uptr PageSize = GetPageSizeCached();
- uptr p = internal_mmap(0,
- RoundUpTo(size, PageSize),
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
- -1, 0);
+ uptr p = internal_mmap(nullptr,
+ RoundUpTo(size, PageSize),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
+ -1, 0);
int reserrno;
if (internal_iserror(p, &reserrno)) {
Report("ERROR: %s failed to "
@@ -153,22 +151,6 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
return (void *)p;
}
-void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
- uptr PageSize = GetPageSizeCached();
- uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
- RoundUpTo(size, PageSize),
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
- -1, 0);
- int reserrno;
- if (internal_iserror(p, &reserrno))
- Report("ERROR: %s failed to "
- "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
- SanitizerToolName, size, size, fixed_addr, reserrno);
- IncreaseTotalMmap(size);
- return (void *)p;
-}
-
void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
uptr PageSize = GetPageSizeCached();
uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
@@ -187,33 +169,72 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
return (void *)p;
}
-void *Mprotect(uptr fixed_addr, uptr size) {
- return (void *)internal_mmap((void*)fixed_addr, size,
- PROT_NONE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED |
- MAP_NORESERVE, -1, 0);
+bool MprotectNoAccess(uptr addr, uptr size) {
+ return 0 == internal_mprotect((void*)addr, size, PROT_NONE);
+}
+
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
+ int flags;
+ switch (mode) {
+ case RdOnly: flags = O_RDONLY; break;
+ case WrOnly: flags = O_WRONLY | O_CREAT; break;
+ case RdWr: flags = O_RDWR | O_CREAT; break;
+ }
+ fd_t res = internal_open(filename, flags, 0660);
+ if (internal_iserror(res, errno_p))
+ return kInvalidFd;
+ return res;
+}
+
+void CloseFile(fd_t fd) {
+ internal_close(fd);
+}
+
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+ error_t *error_p) {
+ uptr res = internal_read(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_read)
+ *bytes_read = res;
+ return true;
+}
+
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+ error_t *error_p) {
+ uptr res = internal_write(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_written)
+ *bytes_written = res;
+ return true;
+}
+
+bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
+ uptr res = internal_rename(oldpath, newpath);
+ return !internal_iserror(res, error_p);
}
void *MapFileToMemory(const char *file_name, uptr *buff_size) {
- uptr openrv = OpenFile(file_name, false);
- CHECK(!internal_iserror(openrv));
- fd_t fd = openrv;
+ fd_t fd = OpenFile(file_name, RdOnly);
+ CHECK(fd != kInvalidFd);
uptr fsize = internal_filesize(fd);
CHECK_NE(fsize, (uptr)-1);
CHECK_GT(fsize, 0);
*buff_size = RoundUpTo(fsize, GetPageSizeCached());
- uptr map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
- return internal_iserror(map) ? 0 : (void *)map;
+ uptr map = internal_mmap(nullptr, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ return internal_iserror(map) ? nullptr : (void *)map;
}
-void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) {
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T 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;
+ int mmap_errno = 0;
+ if (internal_iserror(p, &mmap_errno)) {
+ Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n",
+ fd, (long long)offset, size, p, mmap_errno);
+ return nullptr;
}
return (void *)p;
}
@@ -233,9 +254,11 @@ bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
uptr start, end;
while (proc_maps.Next(&start, &end,
- /*offset*/0, /*filename*/0, /*filename_size*/0,
- /*protection*/0)) {
- if (!IntervalsAreSeparate(start, end, range_start, range_end))
+ /*offset*/nullptr, /*filename*/nullptr,
+ /*filename_size*/0, /*protection*/nullptr)) {
+ if (start == end) continue; // Empty range.
+ CHECK_NE(0, end);
+ if (!IntervalsAreSeparate(start, end - 1, range_start, range_end))
return false;
}
return true;
@@ -247,8 +270,8 @@ void DumpProcessMap() {
const sptr kBufSize = 4095;
char *filename = (char*)MmapOrDie(kBufSize, __func__);
Report("Process memory map follows:\n");
- while (proc_maps.Next(&start, &end, /* file_offset */0,
- filename, kBufSize, /* protection */0)) {
+ while (proc_maps.Next(&start, &end, /* file_offset */nullptr,
+ filename, kBufSize, /* protection */nullptr)) {
Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename);
}
Report("End of process memory map.\n");
@@ -259,69 +282,28 @@ const char *GetPwd() {
return GetEnv("PWD");
}
-char *FindPathToBinary(const char *name) {
- const char *path = GetEnv("PATH");
- if (!path)
- return 0;
- uptr name_len = internal_strlen(name);
- InternalScopedBuffer<char> buffer(kMaxPathLength);
- const char *beg = path;
- while (true) {
- const char *end = internal_strchrnul(beg, ':');
- uptr prefix_len = end - beg;
- if (prefix_len + name_len + 2 <= kMaxPathLength) {
- internal_memcpy(buffer.data(), beg, prefix_len);
- buffer[prefix_len] = '/';
- internal_memcpy(&buffer[prefix_len + 1], name, name_len);
- buffer[prefix_len + 1 + name_len] = '\0';
- if (FileExists(buffer.data()))
- return internal_strdup(buffer.data());
- }
- if (*end == '\0') break;
- beg = end + 1;
- }
- return 0;
+bool IsPathSeparator(const char c) {
+ return c == '/';
}
-void MaybeOpenReportFile() {
- if (!log_to_file) return;
- uptr pid = internal_getpid();
- // If in tracer, use the parent's file.
- if (pid == stoptheworld_tracer_pid)
- pid = stoptheworld_tracer_ppid;
- if (report_fd_pid == pid) return;
- InternalScopedBuffer<char> report_path_full(4096);
- internal_snprintf(report_path_full.data(), report_path_full.size(),
- "%s.%zu", report_path_prefix, pid);
- uptr openrv = OpenFile(report_path_full.data(), true);
- if (internal_iserror(openrv)) {
- report_fd = kStderrFd;
- log_to_file = false;
- Report("ERROR: Can't open file: %s\n", report_path_full.data());
- Die();
- }
- if (report_fd != kInvalidFd) {
- // We're in the child. Close the parent's log.
- internal_close(report_fd);
- }
- report_fd = openrv;
- report_fd_pid = pid;
+bool IsAbsolutePath(const char *path) {
+ return path != nullptr && IsPathSeparator(path[0]);
}
-void RawWrite(const char *buffer) {
- static const char *kRawWriteError =
- "RawWrite can't output requested buffer!\n";
- uptr length = (uptr)internal_strlen(buffer);
- MaybeOpenReportFile();
- if (length != internal_write(report_fd, buffer, length)) {
- internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError));
+void ReportFile::Write(const char *buffer, uptr length) {
+ SpinMutexLock l(mu);
+ static const char *kWriteError =
+ "ReportFile::Write() can't output requested buffer!\n";
+ ReopenIfNecessary();
+ if (length != internal_write(fd, buffer, length)) {
+ internal_write(fd, kWriteError, internal_strlen(kWriteError));
Die();
}
}
bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
uptr s, e, off, prot;
- InternalScopedString buff(4096);
+ InternalScopedString buff(kMaxPathLength);
MemoryMappingLayout proc_maps(/*cache_enabled*/false);
while (proc_maps.Next(&s, &e, &off, buff.data(), buff.size(), &prot)) {
if ((prot & MemoryMappingLayout::kProtectionExecute) != 0
@@ -334,6 +316,29 @@ bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
return false;
}
-} // namespace __sanitizer
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+ uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
+ uptr pc, sp, bp;
+ GetPcSpBp(context, &pc, &sp, &bp);
+ return SignalContext(context, addr, pc, sp, bp);
+}
+
+// This function check is the built VMA matches the runtime one for
+// architectures with multiple VMA size.
+void CheckVMASize() {
+#ifdef __aarch64__
+ static const unsigned kBuiltVMA = SANITIZER_AARCH64_VMA;
+ unsigned maxRuntimeVMA =
+ (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+ if (kBuiltVMA != maxRuntimeVMA) {
+ Printf("WARNING: %s runtime VMA is not the one built for.\n",
+ SanitizerToolName);
+ Printf("\tBuilt VMA: %u bits\n", kBuiltVMA);
+ Printf("\tRuntime VMA: %u bits\n", maxRuntimeVMA);
+ }
+#endif
+}
+
+} // namespace __sanitizer
-#endif // SANITIZER_POSIX
+#endif // SANITIZER_POSIX
diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.h b/libsanitizer/sanitizer_common/sanitizer_posix.h
new file mode 100644
index 00000000000..8668a03e68f
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_posix.h
@@ -0,0 +1,79 @@
+//===-- sanitizer_posix.h -------------------------------------------------===//
+//
+// 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 and declares some useful POSIX-specific functions.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_POSIX_H
+#define SANITIZER_POSIX_H
+
+// ----------- ATTENTION -------------
+// This header should NOT include any other headers from sanitizer runtime.
+#include "sanitizer_internal_defs.h"
+
+#if !SANITIZER_POSIX
+// Make it hard to accidentally use any of functions declared in this file:
+#error This file should only be included on POSIX
+#endif
+
+namespace __sanitizer {
+
+// I/O
+// Don't use directly, use __sanitizer::OpenFile() instead.
+uptr internal_open(const char *filename, int flags);
+uptr internal_open(const char *filename, int flags, u32 mode);
+uptr internal_close(fd_t fd);
+
+uptr internal_read(fd_t fd, void *buf, uptr count);
+uptr internal_write(fd_t fd, const void *buf, uptr count);
+
+// Memory
+uptr internal_mmap(void *addr, uptr length, int prot, int flags,
+ int fd, OFF_T offset);
+uptr internal_munmap(void *addr, uptr length);
+int internal_mprotect(void *addr, uptr length, int prot);
+
+// OS
+uptr internal_filesize(fd_t fd); // -1 on error.
+uptr internal_stat(const char *path, void *buf);
+uptr internal_lstat(const char *path, void *buf);
+uptr internal_fstat(fd_t fd, void *buf);
+uptr internal_dup2(int oldfd, int newfd);
+uptr internal_readlink(const char *path, char *buf, uptr bufsize);
+uptr internal_unlink(const char *path);
+uptr internal_rename(const char *oldpath, const char *newpath);
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
+
+uptr internal_ptrace(int request, int pid, void *addr, void *data);
+uptr internal_waitpid(int pid, int *status, int options);
+
+int internal_fork();
+
+// These functions call appropriate pthread_ functions directly, bypassing
+// the interceptor. They are weak and may not be present in some tools.
+SANITIZER_WEAK_ATTRIBUTE
+int real_pthread_create(void *th, void *attr, void *(*callback)(void *),
+ void *param);
+SANITIZER_WEAK_ATTRIBUTE
+int real_pthread_join(void *th, void **ret);
+
+#define DEFINE_REAL_PTHREAD_FUNCTIONS \
+ namespace __sanitizer { \
+ int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \
+ void *param) { \
+ return REAL(pthread_create)(th, attr, callback, param); \
+ } \
+ int real_pthread_join(void *th, void **ret) { \
+ return REAL(pthread_join(th, ret)); \
+ } \
+ } // namespace __sanitizer
+
+int internal_sigaction(int signum, const void *act, void *oldact);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc
index b4e42c72462..b99cdae9c28 100644
--- a/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc
@@ -13,21 +13,34 @@
#include "sanitizer_platform.h"
#if SANITIZER_POSIX
+
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
#include <errno.h>
+#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/resource.h>
+#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
+#if SANITIZER_FREEBSD
+// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
+// that, it was never implemented. So just define it to zero.
+#undef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
namespace __sanitizer {
u32 GetUid() {
@@ -42,6 +55,18 @@ void FlushUnneededShadowMemory(uptr addr, uptr size) {
madvise((void*)addr, size, MADV_DONTNEED);
}
+void NoHugePagesInRegion(uptr addr, uptr size) {
+#ifdef MADV_NOHUGEPAGE // May not be defined on old systems.
+ madvise((void *)addr, size, MADV_NOHUGEPAGE);
+#endif // MADV_NOHUGEPAGE
+}
+
+void DontDumpShadowMemory(uptr addr, uptr length) {
+#ifdef MADV_DONTDUMP
+ madvise((void *)addr, length, MADV_DONTDUMP);
+#endif
+}
+
static rlim_t getlim(int res) {
rlimit rlim;
CHECK_EQ(0, getrlimit(res, &rlim));
@@ -53,7 +78,7 @@ static void setlim(int res, rlim_t lim) {
volatile struct rlimit rlim;
rlim.rlim_cur = lim;
rlim.rlim_max = lim;
- if (setrlimit(res, (struct rlimit*)&rlim)) {
+ if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) {
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
Die();
}
@@ -105,8 +130,8 @@ int Atexit(void (*function)(void)) {
#endif
}
-int internal_isatty(fd_t fd) {
- return isatty(fd);
+bool SupportsColoredOutput(fd_t fd) {
+ return isatty(fd) != 0;
}
#ifndef SANITIZER_GO
@@ -115,7 +140,7 @@ static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough.
void SetAlternateSignalStack() {
stack_t altstack, oldstack;
- CHECK_EQ(0, sigaltstack(0, &oldstack));
+ CHECK_EQ(0, sigaltstack(nullptr, &oldstack));
// If the alternate stack is already in place, do nothing.
// Android always sets an alternate stack, but it's too small for us.
if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return;
@@ -126,12 +151,12 @@ void SetAlternateSignalStack() {
altstack.ss_sp = (char*) base;
altstack.ss_flags = 0;
altstack.ss_size = kAltStackSize;
- CHECK_EQ(0, sigaltstack(&altstack, 0));
+ CHECK_EQ(0, sigaltstack(&altstack, nullptr));
}
void UnsetAlternateSignalStack() {
stack_t altstack, oldstack;
- altstack.ss_sp = 0;
+ altstack.ss_sp = nullptr;
altstack.ss_flags = SS_DISABLE;
altstack.ss_size = kAltStackSize; // Some sane value required on Darwin.
CHECK_EQ(0, sigaltstack(&altstack, &oldstack));
@@ -150,7 +175,7 @@ static void MaybeInstallSigaction(int signum,
// Clients are responsible for handling this correctly.
sigact.sa_flags = SA_SIGINFO | SA_NODEFER;
if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
- CHECK_EQ(0, internal_sigaction(signum, &sigact, 0));
+ CHECK_EQ(0, internal_sigaction(signum, &sigact, nullptr));
VReport(1, "Installed the sigaction for signal %d\n", signum);
}
@@ -161,6 +186,8 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) {
if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
MaybeInstallSigaction(SIGSEGV, handler);
MaybeInstallSigaction(SIGBUS, handler);
+ MaybeInstallSigaction(SIGABRT, handler);
+ MaybeInstallSigaction(SIGFPE, handler);
}
#endif // SANITIZER_GO
@@ -186,6 +213,67 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
return result;
}
-} // namespace __sanitizer
+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
+}
+
+#if SANITIZER_ANDROID
+int GetNamedMappingFd(const char *name, uptr size) {
+ return -1;
+}
+#else
+int GetNamedMappingFd(const char *name, uptr size) {
+ if (!common_flags()->decorate_proc_maps)
+ return -1;
+ char shmname[200];
+ CHECK(internal_strlen(name) < sizeof(shmname) - 10);
+ internal_snprintf(shmname, sizeof(shmname), "%zu [%s]", internal_getpid(),
+ name);
+ int fd = shm_open(shmname, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
+ CHECK_GE(fd, 0);
+ int res = internal_ftruncate(fd, size);
+ CHECK_EQ(0, res);
+ res = shm_unlink(shmname);
+ CHECK_EQ(0, res);
+ return fd;
+}
+#endif
+
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
+ int fd = name ? GetNamedMappingFd(name, size) : -1;
+ unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
+ if (fd == -1) flags |= MAP_ANON;
+
+ uptr PageSize = GetPageSizeCached();
+ uptr p = internal_mmap((void *)(fixed_addr & ~(PageSize - 1)),
+ RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE,
+ flags, fd, 0);
+ int reserrno;
+ if (internal_iserror(p, &reserrno))
+ Report("ERROR: %s failed to "
+ "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
+ SanitizerToolName, size, size, fixed_addr, reserrno);
+ IncreaseTotalMmap(size);
+ return (void *)p;
+}
+
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
+ int fd = name ? GetNamedMappingFd(name, size) : -1;
+ unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
+ if (fd == -1) flags |= MAP_ANON;
+
+ return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd,
+ 0);
+}
+} // namespace __sanitizer
-#endif // SANITIZER_POSIX
+#endif // SANITIZER_POSIX
diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cc b/libsanitizer/sanitizer_common/sanitizer_printf.cc
index 599f2c5d7c6..6688610bf0f 100644
--- a/libsanitizer/sanitizer_common/sanitizer_printf.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_printf.cc
@@ -12,7 +12,6 @@
// inside it.
//===----------------------------------------------------------------------===//
-
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
@@ -96,7 +95,7 @@ static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,
static int AppendString(char **buff, const char *buff_end, int precision,
const char *s) {
- if (s == 0)
+ if (!s)
s = "<null>";
int result = 0;
for (; *s; s++) {
@@ -249,29 +248,36 @@ static void SharedPrintfCode(bool append_pid, const char *format,
buffer_size = kLen;
}
needed_length = 0;
+ // Check that data fits into the current buffer.
+# define CHECK_NEEDED_LENGTH \
+ if (needed_length >= buffer_size) { \
+ if (!use_mmap) continue; \
+ RAW_CHECK_MSG(needed_length < kLen, \
+ "Buffer in Report is too short!\n"); \
+ }
if (append_pid) {
int pid = internal_getpid();
- needed_length += internal_snprintf(buffer, buffer_size, "==%d==", pid);
- if (needed_length >= buffer_size) {
- // The pid doesn't fit into the current buffer.
- if (!use_mmap)
- continue;
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
+ const char *exe_name = GetProcessName();
+ if (common_flags()->log_exe_name && exe_name) {
+ needed_length += internal_snprintf(buffer, buffer_size,
+ "==%s", exe_name);
+ CHECK_NEEDED_LENGTH
}
+ needed_length += internal_snprintf(buffer + needed_length,
+ buffer_size - needed_length,
+ "==%d==", pid);
+ CHECK_NEEDED_LENGTH
}
needed_length += VSNPrintf(buffer + needed_length,
buffer_size - needed_length, format, args);
- if (needed_length >= buffer_size) {
- // The message doesn't fit into the current buffer.
- if (!use_mmap)
- continue;
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
- }
+ CHECK_NEEDED_LENGTH
// If the message fit into the buffer, print it and exit.
break;
+# undef CHECK_NEEDED_LENGTH
}
RawWrite(buffer);
- AndroidLogWrite(buffer);
+ if (common_flags()->log_to_syslog)
+ WriteToSyslog(buffer);
CallPrintfAndReportCallback(buffer);
// If we had mapped any memory, clean up.
if (buffer != local_buffer)
@@ -320,4 +326,4 @@ void InternalScopedString::append(const char *format, ...) {
CHECK_LT(length_, size());
}
-} // namespace __sanitizer
+} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cc b/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cc
index ca9900fe9ee..1cf3f8510fb 100644
--- a/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cc
@@ -9,7 +9,9 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
+
#if SANITIZER_FREEBSD || SANITIZER_LINUX
+
#include "sanitizer_common.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
@@ -117,7 +119,7 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
string_predicate_t filter) {
Reset();
uptr cur_beg, cur_end, cur_offset, prot;
- InternalScopedBuffer<char> module_name(kMaxPathLength);
+ InternalScopedString 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(),
@@ -128,7 +130,6 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
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
@@ -141,7 +142,8 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
// 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);
+ LoadedModule *cur_module = &modules[n_modules];
+ cur_module->set(cur_name, base_address);
cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
n_modules++;
}
@@ -149,10 +151,11 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
}
void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
- char *smaps = 0;
+ char *smaps = nullptr;
uptr smaps_cap = 0;
- uptr smaps_len = ReadFileToBuffer("/proc/self/smaps",
- &smaps, &smaps_cap, 64<<20);
+ uptr smaps_len = 0;
+ if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len))
+ return;
uptr start = 0;
bool file = false;
const char *pos = smaps;
@@ -171,6 +174,6 @@ void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
UnmapOrDie(smaps, smaps_cap);
}
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_linux.cc b/libsanitizer/sanitizer_common/sanitizer_procmaps_linux.cc
index 43babf89319..e77e33fb2d7 100644
--- a/libsanitizer/sanitizer_common/sanitizer_procmaps_linux.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_linux.cc
@@ -16,8 +16,8 @@
namespace __sanitizer {
void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
- proc_maps->len = ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
- &proc_maps->mmaped_size, 1 << 26);
+ CHECK(ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
+ &proc_maps->mmaped_size, &proc_maps->len));
}
static bool IsOneOf(char c, char c1, char c2) {
diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cc b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cc
index 81874c21b1f..393243b7c28 100644
--- a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cc
@@ -158,7 +158,7 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
string_predicate_t filter) {
Reset();
uptr cur_beg, cur_end, prot;
- InternalScopedBuffer<char> module_name(kMaxPathLength);
+ InternalScopedString module_name(kMaxPathLength);
uptr n_modules = 0;
for (uptr i = 0; n_modules < max_modules &&
Next(&cur_beg, &cur_end, 0, module_name.data(),
@@ -169,13 +169,13 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
continue;
if (filter && !filter(cur_name))
continue;
- LoadedModule *cur_module = 0;
+ LoadedModule *cur_module = nullptr;
if (n_modules > 0 &&
0 == internal_strcmp(cur_name, modules[n_modules - 1].full_name())) {
cur_module = &modules[n_modules - 1];
} else {
- void *mem = &modules[n_modules];
- cur_module = new(mem) LoadedModule(cur_name, cur_beg);
+ cur_module = &modules[n_modules];
+ cur_module->set(cur_name, cur_beg);
n_modules++;
}
cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
diff --git a/libsanitizer/sanitizer_common/sanitizer_quarantine.h b/libsanitizer/sanitizer_common/sanitizer_quarantine.h
index 1e8d056a187..a635871be45 100644
--- a/libsanitizer/sanitizer_common/sanitizer_quarantine.h
+++ b/libsanitizer/sanitizer_common/sanitizer_quarantine.h
@@ -47,11 +47,14 @@ class Quarantine {
}
void Init(uptr size, uptr cache_size) {
- max_size_ = size;
- min_size_ = size / 10 * 9; // 90% of max size.
+ atomic_store(&max_size_, size, memory_order_release);
+ atomic_store(&min_size_, size / 10 * 9,
+ memory_order_release); // 90% of max size.
max_cache_size_ = cache_size;
}
+ uptr GetSize() const { return atomic_load(&max_size_, memory_order_acquire); }
+
void Put(Cache *c, Callback cb, Node *ptr, uptr size) {
c->Enqueue(cb, ptr, size);
if (c->Size() > max_cache_size_)
@@ -63,15 +66,15 @@ class Quarantine {
SpinMutexLock l(&cache_mutex_);
cache_.Transfer(c);
}
- if (cache_.Size() > max_size_ && recycle_mutex_.TryLock())
+ if (cache_.Size() > GetSize() && recycle_mutex_.TryLock())
Recycle(cb);
}
private:
// Read-only data.
char pad0_[kCacheLineSize];
- uptr max_size_;
- uptr min_size_;
+ atomic_uintptr_t max_size_;
+ atomic_uintptr_t min_size_;
uptr max_cache_size_;
char pad1_[kCacheLineSize];
SpinMutex cache_mutex_;
@@ -81,9 +84,10 @@ class Quarantine {
void NOINLINE Recycle(Callback cb) {
Cache tmp;
+ uptr min_size = atomic_load(&min_size_, memory_order_acquire);
{
SpinMutexLock l(&cache_mutex_);
- while (cache_.Size() > min_size_) {
+ while (cache_.Size() > min_size) {
QuarantineBatch *b = cache_.DequeueBatch();
tmp.EnqueueBatch(b);
}
@@ -128,6 +132,7 @@ class QuarantineCache {
size += sizeof(QuarantineBatch); // Count the batch in Quarantine size.
}
QuarantineBatch *b = list_.back();
+ CHECK(b);
b->batch[b->count++] = ptr;
b->size += size;
SizeAdd(size);
@@ -146,7 +151,7 @@ class QuarantineCache {
QuarantineBatch *DequeueBatch() {
if (list_.empty())
- return 0;
+ return nullptr;
QuarantineBatch *b = list_.front();
list_.pop_front();
SizeSub(b->size);
@@ -166,12 +171,13 @@ class QuarantineCache {
NOINLINE QuarantineBatch* AllocBatch(Callback cb) {
QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b));
+ CHECK(b);
b->count = 0;
b->size = 0;
list_.push_back(b);
return b;
}
};
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // #ifndef SANITIZER_QUARANTINE_H
+#endif // SANITIZER_QUARANTINE_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc
index a3c9c0677e2..3c5313c3ae4 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc
@@ -20,7 +20,8 @@ struct StackDepotNode {
StackDepotNode *link;
u32 id;
atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20;
- uptr size;
+ u32 size;
+ u32 tag;
uptr stack[1]; // [size]
static const u32 kTabSizeLog = 20;
@@ -35,7 +36,8 @@ struct StackDepotNode {
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;
+ if ((hash & kHashMask) != hash_bits || args.size != size || args.tag != tag)
+ return false;
uptr i = 0;
for (; i < size; i++) {
if (stack[i] != args.trace[i]) return false;
@@ -70,10 +72,11 @@ struct StackDepotNode {
void store(const args_type &args, u32 hash) {
atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
size = args.size;
+ tag = args.tag;
internal_memcpy(stack, args.trace, size * sizeof(uptr));
}
args_type load() const {
- return args_type(&stack[0], size);
+ return args_type(&stack[0], size, tag);
}
StackDepotHandle get_handle() { return StackDepotHandle(this); }
@@ -147,7 +150,7 @@ StackDepotReverseMap::StackDepotReverseMap()
StackTrace StackDepotReverseMap::Get(u32 id) {
if (!map_.size())
return StackTrace();
- IdDescPair pair = {id, 0};
+ IdDescPair pair = {id, nullptr};
uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair,
IdDescPair::IdComparator);
if (idx > map_.size())
@@ -155,4 +158,4 @@ StackTrace StackDepotReverseMap::Get(u32 id) {
return map_[idx].desc->load();
}
-} // namespace __sanitizer
+} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h
index aad9bfb8d35..dfb5349de9f 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h
+++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h
@@ -8,6 +8,7 @@
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries.
//===----------------------------------------------------------------------===//
+
#ifndef SANITIZER_STACKDEPOT_H
#define SANITIZER_STACKDEPOT_H
@@ -21,7 +22,7 @@ namespace __sanitizer {
struct StackDepotNode;
struct StackDepotHandle {
StackDepotNode *node_;
- StackDepotHandle() : node_(0) {}
+ StackDepotHandle() : node_(nullptr) {}
explicit StackDepotHandle(StackDepotNode *node) : node_(node) {}
bool valid() { return node_; }
u32 id();
@@ -64,6 +65,6 @@ class StackDepotReverseMap {
void operator=(const StackDepotReverseMap&);
};
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_STACKDEPOT_H
+#endif // SANITIZER_STACKDEPOT_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h b/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h
index 05b63092b80..ab4932823a3 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h
+++ b/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h
@@ -8,6 +8,7 @@
// Implementation of a mapping from arbitrary values to unique 32-bit
// identifiers.
//===----------------------------------------------------------------------===//
+
#ifndef SANITIZER_STACKDEPOTBASE_H
#define SANITIZER_STACKDEPOTBASE_H
@@ -24,7 +25,7 @@ class StackDepotBase {
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);
+ handle_type Put(args_type args, bool *inserted = nullptr);
// Retrieves a stored stack trace by the id.
args_type Get(u32 id);
@@ -64,7 +65,7 @@ Node *StackDepotBase<Node, kReservedBits, kTabSizeLog>::find(Node *s,
return s;
}
}
- return 0;
+ return nullptr;
}
template <class Node, int kReservedBits, int kTabSizeLog>
@@ -170,5 +171,6 @@ void StackDepotBase<Node, kReservedBits, kTabSizeLog>::UnlockAll() {
}
}
-} // namespace __sanitizer
-#endif // SANITIZER_STACKDEPOTBASE_H
+} // namespace __sanitizer
+
+#endif // SANITIZER_STACKDEPOTBASE_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc
index a751da2f740..7b0c084b1a2 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc
@@ -15,18 +15,13 @@
namespace __sanitizer {
-uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
-#if defined(__arm__)
- // Cancel Thumb bit.
- pc = pc & (~1);
-#endif
-#if defined(__powerpc__) || defined(__powerpc64__)
- // PCs are always 4 byte aligned.
- return pc - 4;
-#elif defined(__sparc__) || defined(__mips__)
- return pc - 8;
+uptr StackTrace::GetNextInstructionPc(uptr pc) {
+#if defined(__mips__)
+ return pc + 8;
+#elif defined(__powerpc__)
+ return pc + 4;
#else
- return pc - 1;
+ return pc + 1;
#endif
}
@@ -73,7 +68,7 @@ static inline uhwptr *GetCanonicFrame(uptr bp,
}
void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top,
- uptr stack_bottom, uptr max_depth) {
+ uptr stack_bottom, u32 max_depth) {
CHECK_GE(max_depth, 2);
trace_buffer[0] = pc;
size = 1;
@@ -92,7 +87,7 @@ void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top,
// back chain to find the caller frame before extracting it.
uhwptr *caller_frame = (uhwptr*)frame[0];
if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) ||
- !IsAligned((uptr)caller_frame, sizeof(uhwptr)))
+ !IsAligned((uptr)caller_frame, sizeof(uhwptr)))
break;
uhwptr pc1 = caller_frame[2];
#else
@@ -121,7 +116,7 @@ void BufferedStackTrace::PopStackFrames(uptr count) {
uptr BufferedStackTrace::LocatePcInTrace(uptr pc) {
// Use threshold to find PC in stack trace, as PC we want to unwind from may
// slightly differ from return address in the actual unwinded stack trace.
- const int kPcThreshold = 288;
+ const int kPcThreshold = 304;
for (uptr i = 0; i < size; ++i) {
if (MatchPc(pc, trace[i], kPcThreshold))
return i;
diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h
index 68cc2620db7..7f22455ac9e 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h
+++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h
@@ -15,10 +15,9 @@
namespace __sanitizer {
-static const uptr kStackTraceMax = 256;
+static const u32 kStackTraceMax = 256;
-#if SANITIZER_LINUX && (defined(__aarch64__) || defined(__sparc__) || \
- defined(__mips__))
+#if SANITIZER_LINUX && (defined(__sparc__) || defined(__mips__))
# define SANITIZER_CAN_FAST_UNWIND 0
#elif SANITIZER_WINDOWS
# define SANITIZER_CAN_FAST_UNWIND 0
@@ -37,10 +36,18 @@ static const uptr kStackTraceMax = 256;
struct StackTrace {
const uptr *trace;
- uptr size;
+ u32 size;
+ u32 tag;
- StackTrace() : trace(nullptr), size(0) {}
- StackTrace(const uptr *trace, uptr size) : trace(trace), size(size) {}
+ static const int TAG_UNKNOWN = 0;
+ static const int TAG_ALLOC = 1;
+ static const int TAG_DEALLOC = 2;
+ static const int TAG_CUSTOM = 100; // Tool specific tags start here.
+
+ StackTrace() : trace(nullptr), size(0), tag(0) {}
+ StackTrace(const uptr *trace, u32 size) : trace(trace), size(size), tag(0) {}
+ StackTrace(const uptr *trace, u32 size, u32 tag)
+ : trace(trace), size(size), tag(tag) {}
// Prints a symbolized stacktrace, followed by an empty line.
void Print() const;
@@ -54,11 +61,29 @@ struct StackTrace {
}
static uptr GetCurrentPc();
- static uptr GetPreviousInstructionPc(uptr pc);
+ static inline uptr GetPreviousInstructionPc(uptr pc);
+ static uptr GetNextInstructionPc(uptr pc);
typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
int out_size);
};
+// Performance-critical, must be in the header.
+ALWAYS_INLINE
+uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
+#if defined(__arm__)
+ // Cancel Thumb bit.
+ pc = pc & (~1);
+#endif
+#if defined(__powerpc__) || defined(__powerpc64__)
+ // PCs are always 4 byte aligned.
+ return pc - 4;
+#elif defined(__sparc__) || defined(__mips__)
+ return pc - 8;
+#else
+ return pc - 1;
+#endif
+}
+
// StackTrace that owns the buffer used to store the addresses.
struct BufferedStackTrace : public StackTrace {
uptr trace_buffer[kStackTraceMax];
@@ -67,15 +92,15 @@ struct BufferedStackTrace : public StackTrace {
BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {}
void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
- void Unwind(uptr max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
+ void Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
uptr stack_bottom, bool request_fast_unwind);
private:
void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
- uptr max_depth);
- void SlowUnwindStack(uptr pc, uptr max_depth);
+ u32 max_depth);
+ void SlowUnwindStack(uptr pc, u32 max_depth);
void SlowUnwindStackWithContext(uptr pc, void *context,
- uptr max_depth);
+ u32 max_depth);
void PopStackFrames(uptr count);
uptr LocatePcInTrace(uptr pc);
diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc
index 2d55b73f03a..addf44f7327 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc
@@ -22,36 +22,28 @@ void StackTrace::Print() const {
Printf(" <empty stack>\n\n");
return;
}
- const int kMaxAddrFrames = 64;
- InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
- for (uptr i = 0; i < kMaxAddrFrames; i++)
- new(&addr_frames[i]) AddressInfo();
InternalScopedString frame_desc(GetPageSizeCached() * 2);
uptr frame_num = 0;
for (uptr i = 0; i < size && trace[i]; i++) {
// PCs in stack traces are actually the return addresses, that is,
// addresses of the next instructions after the call.
uptr pc = GetPreviousInstructionPc(trace[i]);
- uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
- pc, addr_frames.data(), kMaxAddrFrames);
- if (addr_frames_num == 0) {
- addr_frames[0].address = pc;
- addr_frames_num = 1;
- }
- for (uptr j = 0; j < addr_frames_num; j++) {
- AddressInfo &info = addr_frames[j];
+ SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
+ CHECK(frames);
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
frame_desc.clear();
RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
- info, common_flags()->strip_path_prefix);
+ cur->info, common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
Printf("%s\n", frame_desc.data());
- info.Clear();
}
+ frames->ClearAll();
}
// Always print a trailing empty line after stack trace.
Printf("\n");
}
-void BufferedStackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context,
+void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
uptr stack_top, uptr stack_bottom,
bool request_fast_unwind) {
top_frame_bp = (max_depth > 0) ? bp : 0;
@@ -66,10 +58,14 @@ void BufferedStackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context,
return;
}
if (!WillUseFastUnwind(request_fast_unwind)) {
+#if SANITIZER_CAN_SLOW_UNWIND
if (context)
SlowUnwindStackWithContext(pc, context, max_depth);
else
SlowUnwindStack(pc, max_depth);
+#else
+ UNREACHABLE("slow unwind requested but not available");
+#endif
} else {
FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth);
}
diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc
index 300b4904c54..bcc9de78ec1 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc
@@ -8,13 +8,14 @@
// This file is shared between sanitizers' run-time libraries.
//
//===----------------------------------------------------------------------===//
+
#include "sanitizer_stacktrace_printer.h"
namespace __sanitizer {
static const char *StripFunctionName(const char *function, const char *prefix) {
- if (function == 0) return 0;
- if (prefix == 0) return function;
+ if (!function) return nullptr;
+ if (!prefix) return function;
uptr prefix_len = internal_strlen(prefix);
if (0 == internal_strncmp(function, prefix, prefix_len))
return function + prefix_len;
@@ -24,8 +25,8 @@ static const char *StripFunctionName(const char *function, const char *prefix) {
static const char kDefaultFormat[] = " #%n %p %F %L";
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
- const AddressInfo &info, const char *strip_path_prefix,
- const char *strip_func_prefix) {
+ const AddressInfo &info, bool vs_style,
+ const char *strip_path_prefix, const char *strip_func_prefix) {
if (0 == internal_strcmp(format, "DEFAULT"))
format = kDefaultFormat;
for (const char *p = format; *p != '\0'; p++) {
@@ -80,14 +81,14 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
break;
case 'S':
// File/line information.
- RenderSourceLocation(buffer, info.file, info.line, info.column,
+ RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style,
strip_path_prefix);
break;
case 'L':
// Source location, or module location.
if (info.file) {
RenderSourceLocation(buffer, info.file, info.line, info.column,
- strip_path_prefix);
+ vs_style, strip_path_prefix);
} else if (info.module) {
RenderModuleLocation(buffer, info.module, info.module_offset,
strip_path_prefix);
@@ -97,22 +98,33 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
break;
case 'M':
// Module basename and offset, or PC.
- if (info.module)
+ if (info.address & kExternalPCBit)
+ {} // There PCs are not meaningful.
+ else if (info.module)
buffer->append("(%s+%p)", StripModuleName(info.module),
(void *)info.module_offset);
else
buffer->append("(%p)", (void *)info.address);
break;
default:
- Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n",
- *p, *p);
+ Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
+ *p);
Die();
}
}
}
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
- int line, int column, const char *strip_path_prefix) {
+ int line, int column, bool vs_style,
+ const char *strip_path_prefix) {
+ if (vs_style && line > 0) {
+ buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
+ if (column > 0)
+ buffer->append(",%d", column);
+ buffer->append(")");
+ return;
+ }
+
buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
if (line > 0) {
buffer->append(":%d", line);
@@ -127,4 +139,4 @@ void RenderModuleLocation(InternalScopedString *buffer, const char *module,
offset);
}
-} // namespace __sanitizer
+} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h
index 54e2fb024df..a553568ea6f 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h
+++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h
@@ -46,11 +46,13 @@ namespace __sanitizer {
// module+offset if it is known, or (<unknown module>) string.
// %M - prints module basename and offset, if it is known, or PC.
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
- const AddressInfo &info, const char *strip_path_prefix = "",
+ const AddressInfo &info, bool vs_style,
+ const char *strip_path_prefix = "",
const char *strip_func_prefix = "");
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
- int line, int column, const char *strip_path_prefix);
+ int line, int column, bool vs_style,
+ const char *strip_path_prefix);
void RenderModuleLocation(InternalScopedString *buffer, const char *module,
uptr offset, const char *strip_path_prefix);
diff --git a/libsanitizer/sanitizer_common/sanitizer_stoptheworld.h b/libsanitizer/sanitizer_common/sanitizer_stoptheworld.h
index b1241da1f73..c3245266fb9 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stoptheworld.h
+++ b/libsanitizer/sanitizer_common/sanitizer_stoptheworld.h
@@ -57,7 +57,8 @@ typedef void (*StopTheWorldCallback)(
// Suspend all threads in the current process and run the callback on the list
// of suspended threads. This function will resume the threads before returning.
-// The callback should not call any libc functions.
+// The callback should not call any libc functions. The callback must not call
+// exit() nor _exit() and instead return to the caller.
// This function should NOT be called from multiple threads simultaneously.
void StopTheWorld(StopTheWorldCallback callback, void *argument);
diff --git a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
index 58812023674..c919e4f6e97 100644
--- a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
@@ -10,13 +10,15 @@
//
//===----------------------------------------------------------------------===//
-
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX && defined(__x86_64__)
+
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__))
#include "sanitizer_stoptheworld.h"
#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_atomic.h"
#include <errno.h>
#include <sched.h> // for CLONE_* definitions
@@ -24,9 +26,15 @@
#include <sys/prctl.h> // for PR_* definitions
#include <sys/ptrace.h> // for PTRACE_* definitions
#include <sys/types.h> // for pid_t
+#include <sys/uio.h> // for iovec
+#include <elf.h> // for NT_PRSTATUS
#if SANITIZER_ANDROID && defined(__arm__)
# include <linux/user.h> // for pt_regs
#else
+# ifdef __aarch64__
+// GLIBC 2.20+ sys/user does not include asm/ptrace.h
+# include <asm/ptrace.h>
+# endif
# include <sys/user.h> // for user_regs_struct
#endif
#include <sys/wait.h> // for signal-related stuff
@@ -68,11 +76,25 @@
COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t));
namespace __sanitizer {
+
+// Structure for passing arguments into the tracer thread.
+struct TracerThreadArgument {
+ StopTheWorldCallback callback;
+ void *callback_argument;
+ // The tracer thread waits on this mutex while the parent finishes its
+ // preparations.
+ BlockingMutex mutex;
+ // Tracer thread signals its completion by setting done.
+ atomic_uintptr_t done;
+ uptr parent_pid;
+};
+
// This class handles thread suspending/unsuspending in the tracer thread.
class ThreadSuspender {
public:
- explicit ThreadSuspender(pid_t pid)
- : pid_(pid) {
+ explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg)
+ : arg(arg)
+ , pid_(pid) {
CHECK_GE(pid, 0);
}
bool SuspendAllThreads();
@@ -81,42 +103,58 @@ class ThreadSuspender {
SuspendedThreadsList &suspended_threads_list() {
return suspended_threads_list_;
}
+ TracerThreadArgument *arg;
private:
SuspendedThreadsList suspended_threads_list_;
pid_t pid_;
bool SuspendThread(SuspendedThreadID thread_id);
};
-bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) {
+bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) {
// Are we already attached to this thread?
// Currently this check takes linear time, however the number of threads is
// usually small.
- if (suspended_threads_list_.Contains(thread_id))
+ if (suspended_threads_list_.Contains(tid))
return false;
int pterrno;
- if (internal_iserror(internal_ptrace(PTRACE_ATTACH, thread_id, NULL, NULL),
+ if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr),
&pterrno)) {
// Either the thread is dead, or something prevented us from attaching.
// Log this event and move on.
- VReport(1, "Could not attach to thread %d (errno %d).\n", thread_id,
- pterrno);
+ VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno);
return false;
} else {
- VReport(1, "Attached to thread %d.\n", thread_id);
+ VReport(2, "Attached to thread %d.\n", tid);
// The thread is not guaranteed to stop before ptrace returns, so we must
- // wait on it.
- uptr waitpid_status;
- HANDLE_EINTR(waitpid_status, internal_waitpid(thread_id, NULL, __WALL));
- int wperrno;
- if (internal_iserror(waitpid_status, &wperrno)) {
- // Got a ECHILD error. I don't think this situation is possible, but it
- // doesn't hurt to report it.
- VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n",
- thread_id, wperrno);
- internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL);
- return false;
+ // wait on it. Note: if the thread receives a signal concurrently,
+ // we can get notification about the signal before notification about stop.
+ // In such case we need to forward the signal to the thread, otherwise
+ // the signal will be missed (as we do PTRACE_DETACH with arg=0) and
+ // any logic relying on signals will break. After forwarding we need to
+ // continue to wait for stopping, because the thread is not stopped yet.
+ // We do ignore delivery of SIGSTOP, because we want to make stop-the-world
+ // as invisible as possible.
+ for (;;) {
+ int status;
+ uptr waitpid_status;
+ HANDLE_EINTR(waitpid_status, internal_waitpid(tid, &status, __WALL));
+ int wperrno;
+ if (internal_iserror(waitpid_status, &wperrno)) {
+ // Got a ECHILD error. I don't think this situation is possible, but it
+ // doesn't hurt to report it.
+ VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n",
+ tid, wperrno);
+ internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
+ return false;
+ }
+ if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) {
+ internal_ptrace(PTRACE_CONT, tid, nullptr,
+ (void*)(uptr)WSTOPSIG(status));
+ continue;
+ }
+ break;
}
- suspended_threads_list_.Append(thread_id);
+ suspended_threads_list_.Append(tid);
return true;
}
}
@@ -125,9 +163,9 @@ void ThreadSuspender::ResumeAllThreads() {
for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++) {
pid_t tid = suspended_threads_list_.GetThreadID(i);
int pterrno;
- if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL),
+ if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr),
&pterrno)) {
- VReport(1, "Detached from thread %d.\n", tid);
+ VReport(2, "Detached from thread %d.\n", tid);
} else {
// Either the thread is dead, or we are already detached.
// The latter case is possible, for instance, if this function was called
@@ -140,7 +178,7 @@ void ThreadSuspender::ResumeAllThreads() {
void ThreadSuspender::KillAllThreads() {
for (uptr i = 0; i < suspended_threads_list_.thread_count(); i++)
internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i),
- NULL, NULL);
+ nullptr, nullptr);
}
bool ThreadSuspender::SuspendAllThreads() {
@@ -166,35 +204,11 @@ bool ThreadSuspender::SuspendAllThreads() {
}
// Pointer to the ThreadSuspender instance for use in signal handler.
-static ThreadSuspender *thread_suspender_instance = NULL;
-
-// Signals that should not be blocked (this is used in the parent thread as well
-// as the tracer thread).
-static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV,
- SIGBUS, SIGXCPU, SIGXFSZ };
-
-// Structure for passing arguments into the tracer thread.
-struct TracerThreadArgument {
- StopTheWorldCallback callback;
- void *callback_argument;
- // The tracer thread waits on this mutex while the parent finishes its
- // preparations.
- BlockingMutex mutex;
- uptr parent_pid;
-};
-
-static DieCallbackType old_die_callback;
+static ThreadSuspender *thread_suspender_instance = nullptr;
-// Signal handler to wake up suspended threads when the tracer thread dies.
-void TracerThreadSignalHandler(int signum, void *siginfo, void *) {
- if (thread_suspender_instance != NULL) {
- if (signum == SIGABRT)
- thread_suspender_instance->KillAllThreads();
- else
- thread_suspender_instance->ResumeAllThreads();
- }
- internal__exit((signum == SIGABRT) ? 1 : 2);
-}
+// Synchronous signals that should not be blocked.
+static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,
+ SIGXCPU, SIGXFSZ };
static void TracerThreadDieCallback() {
// Generally a call to Die() in the tracer thread should be fatal to the
@@ -203,10 +217,29 @@ static void TracerThreadDieCallback() {
// point. So we correctly handle calls to Die() from within the callback, but
// not those that happen before or after the callback. Hopefully there aren't
// a lot of opportunities for that to happen...
- if (thread_suspender_instance)
- thread_suspender_instance->KillAllThreads();
- if (old_die_callback)
- old_die_callback();
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst && stoptheworld_tracer_pid == internal_getpid()) {
+ inst->KillAllThreads();
+ thread_suspender_instance = nullptr;
+ }
+}
+
+// Signal handler to wake up suspended threads when the tracer thread dies.
+static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) {
+ SignalContext ctx = SignalContext::Create(siginfo, uctx);
+ VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n",
+ signum, ctx.addr, ctx.pc, ctx.sp);
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst) {
+ if (signum == SIGABRT)
+ inst->KillAllThreads();
+ else
+ inst->ResumeAllThreads();
+ RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+ thread_suspender_instance = nullptr;
+ atomic_store(&inst->arg->done, 1, memory_order_relaxed);
+ }
+ internal__exit((signum == SIGABRT) ? 1 : 2);
}
// Size of alternative stack for signal handlers in the tracer thread.
@@ -226,9 +259,9 @@ static int TracerThread(void* argument) {
tracer_thread_argument->mutex.Lock();
tracer_thread_argument->mutex.Unlock();
- SetDieCallback(TracerThreadDieCallback);
+ RAW_CHECK(AddDieCallback(TracerThreadDieCallback));
- ThreadSuspender thread_suspender(internal_getppid());
+ ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument);
// Global pointer for the signal handler.
thread_suspender_instance = &thread_suspender;
@@ -238,19 +271,16 @@ static int TracerThread(void* argument) {
internal_memset(&handler_stack, 0, sizeof(handler_stack));
handler_stack.ss_sp = handler_stack_memory.data();
handler_stack.ss_size = kHandlerStackSize;
- internal_sigaltstack(&handler_stack, NULL);
-
- // Install our handler for fatal signals. Other signals should be blocked by
- // the mask we inherited from the caller thread.
- for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
- signal_index++) {
- __sanitizer_sigaction new_sigaction;
- internal_memset(&new_sigaction, 0, sizeof(new_sigaction));
- new_sigaction.sigaction = TracerThreadSignalHandler;
- new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO;
- internal_sigfillset(&new_sigaction.sa_mask);
- internal_sigaction_norestorer(kUnblockedSignals[signal_index],
- &new_sigaction, NULL);
+ internal_sigaltstack(&handler_stack, nullptr);
+
+ // Install our handler for synchronous signals. Other signals should be
+ // blocked by the mask we inherited from the parent thread.
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) {
+ __sanitizer_sigaction act;
+ internal_memset(&act, 0, sizeof(act));
+ act.sigaction = TracerThreadSignalHandler;
+ act.sa_flags = SA_ONSTACK | SA_SIGINFO;
+ internal_sigaction_norestorer(kSyncSignals[i], &act, 0);
}
int exit_code = 0;
@@ -263,9 +293,9 @@ static int TracerThread(void* argument) {
thread_suspender.ResumeAllThreads();
exit_code = 0;
}
- thread_suspender_instance = NULL;
- handler_stack.ss_flags = SS_DISABLE;
- internal_sigaltstack(&handler_stack, NULL);
+ RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+ thread_suspender_instance = nullptr;
+ atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed);
return exit_code;
}
@@ -278,7 +308,7 @@ class ScopedStackSpaceWithGuard {
// in the future.
guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_,
"ScopedStackWithGuard");
- CHECK_EQ(guard_start_, (uptr)Mprotect((uptr)guard_start_, guard_size_));
+ CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_));
}
~ScopedStackSpaceWithGuard() {
UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_);
@@ -297,53 +327,21 @@ class ScopedStackSpaceWithGuard {
// into globals.
static __sanitizer_sigset_t blocked_sigset;
static __sanitizer_sigset_t old_sigset;
-static __sanitizer_sigaction old_sigactions
- [ARRAY_SIZE(kUnblockedSignals)];
class StopTheWorldScope {
public:
StopTheWorldScope() {
- // Block all signals that can be blocked safely, and install
- // default handlers for the remaining signals.
- // We cannot allow user-defined handlers to run while the ThreadSuspender
- // thread is active, because they could conceivably call some libc functions
- // which modify errno (which is shared between the two threads).
- internal_sigfillset(&blocked_sigset);
- for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
- signal_index++) {
- // Remove the signal from the set of blocked signals.
- internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]);
- // Install the default handler.
- __sanitizer_sigaction new_sigaction;
- internal_memset(&new_sigaction, 0, sizeof(new_sigaction));
- new_sigaction.handler = SIG_DFL;
- internal_sigfillset(&new_sigaction.sa_mask);
- internal_sigaction_norestorer(kUnblockedSignals[signal_index],
- &new_sigaction, &old_sigactions[signal_index]);
- }
- int sigprocmask_status =
- internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
- CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail
// Make this process dumpable. Processes that are not dumpable cannot be
// attached to.
process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
if (!process_was_dumpable_)
internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
- old_die_callback = GetDieCallback();
}
~StopTheWorldScope() {
- SetDieCallback(old_die_callback);
// Restore the dumpable flag.
if (!process_was_dumpable_)
internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
- // Restore the signal handlers.
- for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
- signal_index++) {
- internal_sigaction_norestorer(kUnblockedSignals[signal_index],
- &old_sigactions[signal_index], NULL);
- }
- internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset);
}
private:
@@ -371,16 +369,42 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) {
tracer_thread_argument.callback = callback;
tracer_thread_argument.callback_argument = argument;
tracer_thread_argument.parent_pid = internal_getpid();
+ atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed);
const uptr kTracerStackSize = 2 * 1024 * 1024;
ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);
// Block the execution of TracerThread until after we have set ptrace
// permissions.
tracer_thread_argument.mutex.Lock();
+ // Signal handling story.
+ // We don't want async signals to be delivered to the tracer thread,
+ // so we block all async signals before creating the thread. An async signal
+ // handler can temporary modify errno, which is shared with this thread.
+ // We ought to use pthread_sigmask here, because sigprocmask has undefined
+ // behavior in multithreaded programs. However, on linux sigprocmask is
+ // equivalent to pthread_sigmask with the exception that pthread_sigmask
+ // does not allow to block some signals used internally in pthread
+ // implementation. We are fine with blocking them here, we are really not
+ // going to pthread_cancel the thread.
+ // The tracer thread should not raise any synchronous signals. But in case it
+ // does, we setup a special handler for sync signals that properly kills the
+ // parent as well. Note: we don't pass CLONE_SIGHAND to clone, so handlers
+ // in the tracer thread won't interfere with user program. Double note: if a
+ // user does something along the lines of 'kill -11 pid', that can kill the
+ // process even if user setup own handler for SEGV.
+ // Thing to watch out for: this code should not change behavior of user code
+ // in any observable way. In particular it should not override user signal
+ // handlers.
+ internal_sigfillset(&blocked_sigset);
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++)
+ internal_sigdelset(&blocked_sigset, kSyncSignals[i]);
+ int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
+ CHECK_EQ(rv, 0);
uptr tracer_pid = internal_clone(
TracerThread, tracer_stack.Bottom(),
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
- &tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0
- /* child_tidptr */);
+ &tracer_thread_argument, nullptr /* parent_tidptr */,
+ nullptr /* newtls */, nullptr /* child_tidptr */);
+ internal_sigprocmask(SIG_SETMASK, &old_sigset, 0);
int local_errno = 0;
if (internal_iserror(tracer_pid, &local_errno)) {
VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);
@@ -394,14 +418,27 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) {
#endif
// Allow the tracer thread to start.
tracer_thread_argument.mutex.Unlock();
- // Since errno is shared between this thread and the tracer thread, we
- // must avoid using errno while the tracer thread is running.
- // At this point, any signal will either be blocked or kill us, so waitpid
- // should never return (and set errno) while the tracer thread is alive.
- uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);
- if (internal_iserror(waitpid_status, &local_errno))
+ // NOTE: errno is shared between this thread and the tracer thread.
+ // internal_waitpid() may call syscall() which can access/spoil errno,
+ // so we can't call it now. Instead we for the tracer thread to finish using
+ // the spin loop below. Man page for sched_yield() says "In the Linux
+ // implementation, sched_yield() always succeeds", so let's hope it does not
+ // spoil errno. Note that this spin loop runs only for brief periods before
+ // the tracer thread has suspended us and when it starts unblocking threads.
+ while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0)
+ sched_yield();
+ // Now the tracer thread is about to exit and does not touch errno,
+ // wait for it.
+ for (;;) {
+ uptr waitpid_status = internal_waitpid(tracer_pid, nullptr, __WALL);
+ if (!internal_iserror(waitpid_status, &local_errno))
+ break;
+ if (local_errno == EINTR)
+ continue;
VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
local_errno);
+ break;
+ }
}
}
@@ -430,6 +467,11 @@ typedef pt_regs regs_struct;
typedef struct user regs_struct;
#define REG_SP regs[EF_REG29]
+#elif defined(__aarch64__)
+typedef struct user_pt_regs regs_struct;
+#define REG_SP sp
+#define ARCH_IOVEC_FOR_GETREGSET
+
#else
#error "Unsupported architecture"
#endif // SANITIZER_ANDROID && defined(__arm__)
@@ -440,8 +482,18 @@ int SuspendedThreadsList::GetRegistersAndSP(uptr index,
pid_t tid = GetThreadID(index);
regs_struct regs;
int pterrno;
- if (internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, NULL, &regs),
- &pterrno)) {
+#ifdef ARCH_IOVEC_FOR_GETREGSET
+ struct iovec regset_io;
+ regset_io.iov_base = &regs;
+ regset_io.iov_len = sizeof(regs_struct);
+ bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid,
+ (void*)NT_PRSTATUS, (void*)&regset_io),
+ &pterrno);
+#else
+ bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, nullptr,
+ &regs), &pterrno);
+#endif
+ if (isErr) {
VReport(1, "Could not get registers from thread %d (errno %d).\n", tid,
pterrno);
return -1;
@@ -455,6 +507,7 @@ int SuspendedThreadsList::GetRegistersAndSP(uptr index,
uptr SuspendedThreadsList::RegisterCount() {
return sizeof(regs_struct) / sizeof(uptr);
}
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_LINUX && defined(__x86_64__)
+#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
+ // || defined(__aarch64__)
diff --git a/libsanitizer/sanitizer_common/sanitizer_suppressions.cc b/libsanitizer/sanitizer_common/sanitizer_suppressions.cc
index ab40598ae8e..2b6a78155fd 100644
--- a/libsanitizer/sanitizer_common/sanitizer_suppressions.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_suppressions.cc
@@ -5,7 +5,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Suppression parsing/matching code shared between TSan and LSan.
+// Suppression parsing/matching code.
//
//===----------------------------------------------------------------------===//
@@ -19,90 +19,73 @@
namespace __sanitizer {
-static const char *const kTypeStrings[SuppressionTypeCount] = {
- "none", "race", "mutex", "thread", "signal",
- "leak", "called_from_lib", "deadlock", "vptr_check"};
+SuppressionContext::SuppressionContext(const char *suppression_types[],
+ int suppression_types_num)
+ : suppression_types_(suppression_types),
+ suppression_types_num_(suppression_types_num), suppressions_(1),
+ can_parse_(true) {
+ CHECK_LE(suppression_types_num_, kMaxSuppressionTypes);
+ internal_memset(has_suppression_type_, 0, suppression_types_num_);
+}
-bool TemplateMatch(char *templ, const char *str) {
- if (str == 0 || str[0] == 0)
- return false;
- bool start = false;
- if (templ && templ[0] == '^') {
- start = true;
- templ++;
+static bool GetPathAssumingFileIsRelativeToExec(const char *file_path,
+ /*out*/char *new_file_path,
+ uptr new_file_path_size) {
+ InternalScopedString exec(kMaxPathLength);
+ if (ReadBinaryNameCached(exec.data(), exec.size())) {
+ const char *file_name_pos = StripModuleName(exec.data());
+ uptr path_to_exec_len = file_name_pos - exec.data();
+ internal_strncat(new_file_path, exec.data(),
+ Min(path_to_exec_len, new_file_path_size - 1));
+ internal_strncat(new_file_path, file_path,
+ new_file_path_size - internal_strlen(new_file_path) - 1);
+ return true;
}
- bool asterisk = false;
- while (templ && templ[0]) {
- if (templ[0] == '*') {
- templ++;
- start = false;
- asterisk = true;
- continue;
- }
- if (templ[0] == '$')
- return str[0] == 0 || asterisk;
- if (str[0] == 0)
- return false;
- char *tpos = (char*)internal_strchr(templ, '*');
- char *tpos1 = (char*)internal_strchr(templ, '$');
- if (tpos == 0 || (tpos1 && tpos1 < tpos))
- tpos = tpos1;
- if (tpos != 0)
- tpos[0] = 0;
- const char *str0 = str;
- const char *spos = internal_strstr(str, templ);
- str = spos + internal_strlen(templ);
- templ = tpos;
- if (tpos)
- tpos[0] = tpos == tpos1 ? '$' : '*';
- if (spos == 0)
- return false;
- if (start && spos != str0)
- return false;
- start = false;
- asterisk = false;
- }
- return true;
+ return false;
}
-ALIGNED(64) static char placeholder[sizeof(SuppressionContext)];
-static SuppressionContext *suppression_ctx = 0;
+void SuppressionContext::ParseFromFile(const char *filename) {
+ if (filename[0] == '\0')
+ return;
-SuppressionContext *SuppressionContext::Get() {
- CHECK(suppression_ctx);
- return suppression_ctx;
-}
+ // If we cannot find the file, check if its location is relative to
+ // the location of the executable.
+ InternalScopedString new_file_path(kMaxPathLength);
+ if (!FileExists(filename) && !IsAbsolutePath(filename) &&
+ GetPathAssumingFileIsRelativeToExec(filename, new_file_path.data(),
+ new_file_path.size())) {
+ filename = new_file_path.data();
+ }
-void SuppressionContext::InitIfNecessary() {
- if (suppression_ctx)
- return;
- suppression_ctx = new(placeholder) SuppressionContext;
- if (common_flags()->suppressions[0] == '\0')
- return;
- char *suppressions_from_file;
+ // Read the file.
+ VPrintf(1, "%s: reading suppressions file at %s\n",
+ SanitizerToolName, filename);
+ char *file_contents;
uptr buffer_size;
- uptr contents_size =
- ReadFileToBuffer(common_flags()->suppressions, &suppressions_from_file,
- &buffer_size, 1 << 26 /* max_len */);
- if (contents_size == 0) {
+ uptr contents_size;
+ if (!ReadFileToBuffer(filename, &file_contents, &buffer_size,
+ &contents_size)) {
Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
- common_flags()->suppressions);
+ filename);
Die();
}
- suppression_ctx->Parse(suppressions_from_file);
+
+ Parse(file_contents);
}
-bool SuppressionContext::Match(const char *str, SuppressionType type,
+bool SuppressionContext::Match(const char *str, const char *type,
Suppression **s) {
can_parse_ = false;
- uptr i;
- for (i = 0; i < suppressions_.size(); i++)
- if (type == suppressions_[i].type &&
- TemplateMatch(suppressions_[i].templ, str))
- break;
- if (i == suppressions_.size()) return false;
- *s = &suppressions_[i];
- return true;
+ if (!HasSuppressionType(type))
+ return false;
+ for (uptr i = 0; i < suppressions_.size(); i++) {
+ Suppression &cur = suppressions_[i];
+ if (0 == internal_strcmp(cur.type, type) && TemplateMatch(cur.templ, str)) {
+ *s = &cur;
+ return true;
+ }
+ }
+ return false;
}
static const char *StripPrefix(const char *str, const char *prefix) {
@@ -127,28 +110,28 @@ void SuppressionContext::Parse(const char *str) {
end = line + internal_strlen(line);
if (line != end && line[0] != '#') {
const char *end2 = end;
- while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t'))
+ while (line != end2 &&
+ (end2[-1] == ' ' || end2[-1] == '\t' || end2[-1] == '\r'))
end2--;
int type;
- for (type = 0; type < SuppressionTypeCount; type++) {
- const char *next_char = StripPrefix(line, kTypeStrings[type]);
+ for (type = 0; type < suppression_types_num_; type++) {
+ const char *next_char = StripPrefix(line, suppression_types_[type]);
if (next_char && *next_char == ':') {
line = ++next_char;
break;
}
}
- if (type == SuppressionTypeCount) {
+ if (type == suppression_types_num_) {
Printf("%s: failed to parse suppressions\n", SanitizerToolName);
Die();
}
- Suppression s;
- s.type = static_cast<SuppressionType>(type);
+ Suppression s = {};
+ s.type = suppression_types_[type];
s.templ = (char*)InternalAlloc(end2 - line + 1);
internal_memcpy(s.templ, line, end2 - line);
s.templ[end2 - line] = 0;
- s.hit_count = 0;
- s.weight = 0;
suppressions_.push_back(s);
+ has_suppression_type_[type] = true;
}
if (end[0] == 0)
break;
@@ -160,6 +143,14 @@ uptr SuppressionContext::SuppressionCount() const {
return suppressions_.size();
}
+bool SuppressionContext::HasSuppressionType(const char *type) const {
+ for (int i = 0; i < suppression_types_num_; i++) {
+ if (0 == internal_strcmp(type, suppression_types_[i]))
+ return has_suppression_type_[i];
+ }
+ return false;
+}
+
const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
CHECK_LT(i, suppressions_.size());
return &suppressions_[i];
@@ -168,13 +159,8 @@ const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
void SuppressionContext::GetMatched(
InternalMmapVector<Suppression *> *matched) {
for (uptr i = 0; i < suppressions_.size(); i++)
- if (suppressions_[i].hit_count)
+ if (atomic_load_relaxed(&suppressions_[i].hit_count))
matched->push_back(&suppressions_[i]);
}
-const char *SuppressionTypeString(SuppressionType t) {
- CHECK(t < SuppressionTypeCount);
- return kTypeStrings[t];
-}
-
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_suppressions.h b/libsanitizer/sanitizer_common/sanitizer_suppressions.h
index 84ee834a9fd..efec926476b 100644
--- a/libsanitizer/sanitizer_common/sanitizer_suppressions.h
+++ b/libsanitizer/sanitizer_common/sanitizer_suppressions.h
@@ -5,64 +5,50 @@
//
//===----------------------------------------------------------------------===//
//
-// Suppression parsing/matching code shared between TSan and LSan.
+// Suppression parsing/matching code.
//
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_SUPPRESSIONS_H
#define SANITIZER_SUPPRESSIONS_H
#include "sanitizer_common.h"
+#include "sanitizer_atomic.h"
#include "sanitizer_internal_defs.h"
namespace __sanitizer {
-enum SuppressionType {
- SuppressionNone,
- SuppressionRace,
- SuppressionMutex,
- SuppressionThread,
- SuppressionSignal,
- SuppressionLeak,
- SuppressionLib,
- SuppressionDeadlock,
- SuppressionVptrCheck,
- SuppressionTypeCount
-};
-
struct Suppression {
- SuppressionType type;
+ const char *type;
char *templ;
- unsigned hit_count;
+ atomic_uint32_t hit_count;
uptr weight;
};
class SuppressionContext {
public:
+ // Create new SuppressionContext capable of parsing given suppression types.
+ SuppressionContext(const char *supprression_types[],
+ int suppression_types_num);
+
+ void ParseFromFile(const char *filename);
void Parse(const char *str);
- bool Match(const char* str, SuppressionType type, Suppression **s);
+
+ bool Match(const char *str, const char *type, Suppression **s);
uptr SuppressionCount() const;
+ bool HasSuppressionType(const char *type) const;
const Suppression *SuppressionAt(uptr i) const;
void GetMatched(InternalMmapVector<Suppression *> *matched);
- // Create a SuppressionContext singleton if it hasn't been created earlier.
- // Not thread safe. Must be called early during initialization (but after
- // runtime flags are parsed).
- static void InitIfNecessary();
- // Returns a SuppressionContext singleton.
- static SuppressionContext *Get();
-
private:
- SuppressionContext() : suppressions_(1), can_parse_(true) {}
+ static const int kMaxSuppressionTypes = 16;
+ const char **const suppression_types_;
+ const int suppression_types_num_;
+
InternalMmapVector<Suppression> suppressions_;
+ bool has_suppression_type_[kMaxSuppressionTypes];
bool can_parse_;
-
- friend class SuppressionContextTest;
};
-const char *SuppressionTypeString(SuppressionType t);
-
-bool TemplateMatch(char *templ, const char *str);
-
} // namespace __sanitizer
#endif // SANITIZER_SUPPRESSIONS_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc
index 6821e5a3262..0e58d5fed6e 100644
--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc
@@ -9,24 +9,63 @@
// run-time libraries.
//===----------------------------------------------------------------------===//
+#include "sanitizer_allocator_internal.h"
#include "sanitizer_platform.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
#include "sanitizer_placement_new.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
namespace __sanitizer {
+AddressInfo::AddressInfo() {
+ internal_memset(this, 0, sizeof(AddressInfo));
+ function_offset = kUnknown;
+}
+
+void AddressInfo::Clear() {
+ InternalFree(module);
+ InternalFree(function);
+ InternalFree(file);
+ internal_memset(this, 0, sizeof(AddressInfo));
+ function_offset = kUnknown;
+}
+
+void AddressInfo::FillModuleInfo(const char *mod_name, uptr mod_offset) {
+ module = internal_strdup(mod_name);
+ module_offset = mod_offset;
+}
+
+SymbolizedStack::SymbolizedStack() : next(nullptr), info() {}
+
+SymbolizedStack *SymbolizedStack::New(uptr addr) {
+ void *mem = InternalAlloc(sizeof(SymbolizedStack));
+ SymbolizedStack *res = new(mem) SymbolizedStack();
+ res->info.address = addr;
+ return res;
+}
+
+void SymbolizedStack::ClearAll() {
+ info.Clear();
+ if (next)
+ next->ClearAll();
+ InternalFree(this);
+}
+
+DataInfo::DataInfo() {
+ internal_memset(this, 0, sizeof(DataInfo));
+}
+
+void DataInfo::Clear() {
+ InternalFree(module);
+ InternalFree(name);
+ internal_memset(this, 0, sizeof(DataInfo));
+}
+
Symbolizer *Symbolizer::symbolizer_;
StaticSpinMutex Symbolizer::init_mu_;
LowLevelAllocator Symbolizer::symbolizer_allocator_;
-Symbolizer *Symbolizer::Disable() {
- CHECK_EQ(0, symbolizer_);
- // Initialize a dummy symbolizer.
- symbolizer_ = new(symbolizer_allocator_) Symbolizer;
- return symbolizer_;
-}
-
void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,
Symbolizer::EndSymbolizationHook end_hook) {
CHECK(start_hook_ == 0 && end_hook_ == 0);
@@ -34,7 +73,29 @@ void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,
end_hook_ = end_hook;
}
-Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {}
+const char *Symbolizer::ModuleNameOwner::GetOwnedCopy(const char *str) {
+ mu_->CheckLocked();
+
+ // 'str' will be the same string multiple times in a row, optimize this case.
+ if (last_match_ && !internal_strcmp(last_match_, str))
+ return last_match_;
+
+ // FIXME: this is linear search.
+ // We should optimize this further if this turns out to be a bottleneck later.
+ for (uptr i = 0; i < storage_.size(); ++i) {
+ if (!internal_strcmp(storage_[i], str)) {
+ last_match_ = storage_[i];
+ return last_match_;
+ }
+ }
+ last_match_ = internal_strdup(str);
+ storage_.push_back(last_match_);
+ return last_match_;
+}
+
+Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools)
+ : module_names_(&mu_), n_modules_(0), modules_fresh_(false), tools_(tools),
+ start_hook_(0), end_hook_(0) {}
Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)
: sym_(sym) {
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h
index 2ff99372da9..0a443a70115 100644
--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h
@@ -17,13 +17,14 @@
#ifndef SANITIZER_SYMBOLIZER_H
#define SANITIZER_SYMBOLIZER_H
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
+#include "sanitizer_common.h"
+#include "sanitizer_mutex.h"
namespace __sanitizer {
struct AddressInfo {
+ // Owns all the string members. Storage for them is
+ // (de)allocated using sanitizer internal allocator.
uptr address;
char *module;
@@ -37,75 +38,68 @@ struct AddressInfo {
int line;
int column;
- AddressInfo() {
- internal_memset(this, 0, sizeof(AddressInfo));
- function_offset = kUnknown;
- }
-
+ AddressInfo();
// Deletes all strings and resets all fields.
- void Clear() {
- InternalFree(module);
- InternalFree(function);
- InternalFree(file);
- internal_memset(this, 0, sizeof(AddressInfo));
- function_offset = kUnknown;
- }
+ void Clear();
+ void FillModuleInfo(const char *mod_name, uptr mod_offset);
+};
- void FillAddressAndModuleInfo(uptr addr, const char *mod_name,
- uptr mod_offset) {
- address = addr;
- module = internal_strdup(mod_name);
- module_offset = mod_offset;
- }
+// Linked list of symbolized frames (each frame is described by AddressInfo).
+struct SymbolizedStack {
+ SymbolizedStack *next;
+ AddressInfo info;
+ static SymbolizedStack *New(uptr addr);
+ // Deletes current, and all subsequent frames in the linked list.
+ // The object cannot be accessed after the call to this function.
+ void ClearAll();
+
+ private:
+ SymbolizedStack();
};
// For now, DataInfo is used to describe global variable.
struct DataInfo {
+ // Owns all the string members. Storage for them is
+ // (de)allocated using sanitizer internal allocator.
char *module;
uptr module_offset;
char *name;
uptr start;
uptr size;
- DataInfo() {
- internal_memset(this, 0, sizeof(DataInfo));
- }
-
- void Clear() {
- InternalFree(module);
- InternalFree(name);
- internal_memset(this, 0, sizeof(DataInfo));
- }
+ DataInfo();
+ void Clear();
};
-class Symbolizer {
+class SymbolizerTool;
+
+class Symbolizer final {
public:
/// Initialize and return platform-specific implementation of symbolizer
/// (if it wasn't already initialized).
static Symbolizer *GetOrInit();
- // Fills at most "max_frames" elements of "frames" with descriptions
- // for a given address (in all inlined functions). Returns the number
- // of descriptions actually filled.
- virtual uptr SymbolizePC(uptr address, AddressInfo *frames, uptr max_frames) {
- return 0;
- }
- virtual bool SymbolizeData(uptr address, DataInfo *info) {
- return false;
- }
- virtual bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
- uptr *module_address) {
- return false;
- }
- virtual bool CanReturnFileLineInfo() {
- return false;
+ // Returns a list of symbolized frames for a given address (containing
+ // all inlined functions, if necessary).
+ SymbolizedStack *SymbolizePC(uptr address);
+ bool SymbolizeData(uptr address, DataInfo *info);
+
+ // The module names Symbolizer returns are stable and unique for every given
+ // module. It is safe to store and compare them as pointers.
+ bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+ uptr *module_address);
+ const char *GetModuleNameForPc(uptr pc) {
+ const char *module_name = nullptr;
+ uptr unused;
+ if (GetModuleNameAndOffsetForPC(pc, &module_name, &unused))
+ return module_name;
+ return nullptr;
}
+
// Release internal caches (if any).
- virtual void Flush() {}
+ void Flush();
// Attempts to demangle the provided C++ mangled name.
- virtual const char *Demangle(const char *name) {
- return name;
- }
- virtual void PrepareForSandboxing() {}
+ const char *Demangle(const char *name);
+ void PrepareForSandboxing();
// Allow user to install hooks that would be called before/after Symbolizer
// does the actual file/line info fetching. Specific sanitizers may need this
@@ -118,16 +112,53 @@ class Symbolizer {
EndSymbolizationHook end_hook);
private:
+ // GetModuleNameAndOffsetForPC has to return a string to the caller.
+ // Since the corresponding module might get unloaded later, we should create
+ // our owned copies of the strings that we can safely return.
+ // ModuleNameOwner does not provide any synchronization, thus calls to
+ // its method should be protected by |mu_|.
+ class ModuleNameOwner {
+ public:
+ explicit ModuleNameOwner(BlockingMutex *synchronized_by)
+ : storage_(kInitialCapacity), last_match_(nullptr),
+ mu_(synchronized_by) {}
+ const char *GetOwnedCopy(const char *str);
+
+ private:
+ static const uptr kInitialCapacity = 1000;
+ InternalMmapVector<const char*> storage_;
+ const char *last_match_;
+
+ BlockingMutex *mu_;
+ } module_names_;
+
/// Platform-specific function for creating a Symbolizer object.
static Symbolizer *PlatformInit();
- /// Initialize the symbolizer in a disabled state. Not thread safe.
- static Symbolizer *Disable();
+
+ bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,
+ uptr *module_offset);
+ LoadedModule *FindModuleForAddress(uptr address);
+ LoadedModule modules_[kMaxNumberOfModules];
+ uptr n_modules_;
+ // If stale, need to reload the modules before looking up addresses.
+ bool modules_fresh_;
+
+ // Platform-specific default demangler, must not return nullptr.
+ const char *PlatformDemangle(const char *name);
+ void PlatformPrepareForSandboxing();
static Symbolizer *symbolizer_;
static StaticSpinMutex init_mu_;
- protected:
- Symbolizer();
+ // Mutex locked from public methods of |Symbolizer|, so that the internals
+ // (including individual symbolizer tools and platform-specific methods) are
+ // always synchronized.
+ BlockingMutex mu_;
+
+ typedef IntrusiveList<SymbolizerTool>::Iterator Iterator;
+ IntrusiveList<SymbolizerTool> tools_;
+
+ explicit Symbolizer(IntrusiveList<SymbolizerTool> tools);
static LowLevelAllocator symbolizer_allocator_;
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_internal.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer_internal.h
new file mode 100644
index 00000000000..a87964b4636
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_internal.h
@@ -0,0 +1,149 @@
+//===-- sanitizer_symbolizer_internal.h -------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Header for internal classes and functions to be used by implementations of
+// symbolizers.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_INTERNAL_H
+#define SANITIZER_SYMBOLIZER_INTERNAL_H
+
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+// Parsing helpers, 'str' is searched for delimiter(s) and a string or uptr
+// is extracted. When extracting a string, a newly allocated (using
+// InternalAlloc) and null-terminataed buffer is returned. They return a pointer
+// to the next characted after the found delimiter.
+const char *ExtractToken(const char *str, const char *delims, char **result);
+const char *ExtractInt(const char *str, const char *delims, int *result);
+const char *ExtractUptr(const char *str, const char *delims, uptr *result);
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+ char **result);
+
+const char *DemangleCXXABI(const char *name);
+
+// SymbolizerTool is an interface that is implemented by individual "tools"
+// that can perform symbolication (external llvm-symbolizer, libbacktrace,
+// Windows DbgHelp symbolizer, etc.).
+class SymbolizerTool {
+ public:
+ // The main |Symbolizer| class implements a "fallback chain" of symbolizer
+ // tools. In a request to symbolize an address, if one tool returns false,
+ // the next tool in the chain will be tried.
+ SymbolizerTool *next;
+
+ SymbolizerTool() : next(nullptr) { }
+
+ // Can't declare pure virtual functions in sanitizer runtimes:
+ // __cxa_pure_virtual might be unavailable.
+
+ // The |stack| parameter is inout. It is pre-filled with the address,
+ // module base and module offset values and is to be used to construct
+ // other stack frames.
+ virtual bool SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ UNIMPLEMENTED();
+ }
+
+ // The |info| parameter is inout. It is pre-filled with the module base
+ // and module offset values.
+ virtual bool SymbolizeData(uptr addr, DataInfo *info) {
+ UNIMPLEMENTED();
+ }
+
+ virtual void Flush() {}
+
+ // Return nullptr to fallback to the default platform-specific demangler.
+ virtual const char *Demangle(const char *name) {
+ return nullptr;
+ }
+};
+
+// SymbolizerProcess encapsulates communication between the tool and
+// external symbolizer program, running in a different subprocess.
+// SymbolizerProcess may not be used from two threads simultaneously.
+class SymbolizerProcess {
+ public:
+ explicit SymbolizerProcess(const char *path, bool use_forkpty = false);
+ const char *SendCommand(const char *command);
+
+ protected:
+ virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {
+ UNIMPLEMENTED();
+ }
+
+ /// The maximum number of arguments required to invoke a tool process.
+ enum { kArgVMax = 6 };
+
+ /// Fill in an argv array to invoke the child process.
+ virtual void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const {
+ UNIMPLEMENTED();
+ }
+
+ virtual bool ReadFromSymbolizer(char *buffer, uptr max_length);
+
+ private:
+ bool Restart();
+ const char *SendCommandImpl(const char *command);
+ bool WriteToSymbolizer(const char *buffer, uptr length);
+ bool StartSymbolizerSubprocess();
+
+ const char *path_;
+ fd_t input_fd_;
+ fd_t output_fd_;
+
+ static const uptr kBufferSize = 16 * 1024;
+ char buffer_[kBufferSize];
+
+ static const uptr kMaxTimesRestarted = 5;
+ static const int kSymbolizerStartupTimeMillis = 10;
+ uptr times_restarted_;
+ bool failed_to_start_;
+ bool reported_invalid_path_;
+ bool use_forkpty_;
+};
+
+class LLVMSymbolizerProcess;
+
+// This tool invokes llvm-symbolizer in a subprocess. It should be as portable
+// as the llvm-symbolizer tool is.
+class LLVMSymbolizer : public SymbolizerTool {
+ public:
+ explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator);
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+ private:
+ const char *SendCommand(bool is_data, const char *module_name,
+ uptr module_offset);
+
+ LLVMSymbolizerProcess *symbolizer_process_;
+ static const uptr kBufferSize = 16 * 1024;
+ char buffer_[kBufferSize];
+};
+
+// Parses one or more two-line strings in the following format:
+// <function_name>
+// <file_name>:<line_number>[:<column_number>]
+// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
+// them use the same output format. Returns true if any useful debug
+// information was found.
+void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res);
+
+// Parses a two-line string in the following format:
+// <symbol_name>
+// <start_address> <size>
+// Used by LLVMSymbolizer and InternalSymbolizer.
+void ParseSymbolizeDataOutput(const char *str, DataInfo *info);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SYMBOLIZER_INTERNAL_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
index 8bae57bfe66..b5bcfdbc089 100644
--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
@@ -31,6 +31,8 @@
namespace __sanitizer {
+static char *DemangleAlloc(const char *name, bool always_alloc);
+
#if SANITIZER_LIBBACKTRACE
namespace {
@@ -81,44 +83,49 @@ char *CplusV3Demangle(const char *name) {
}
# endif // SANITIZER_CP_DEMANGLE
-struct SymbolizeCodeData {
- AddressInfo *frames;
- uptr n_frames;
- uptr max_frames;
- const char *module_name;
- uptr module_offset;
+struct SymbolizeCodeCallbackArg {
+ SymbolizedStack *first;
+ SymbolizedStack *last;
+ uptr frames_symbolized;
+
+ AddressInfo *get_new_frame(uintptr_t addr) {
+ CHECK(last);
+ if (frames_symbolized > 0) {
+ SymbolizedStack *cur = SymbolizedStack::New(addr);
+ AddressInfo *info = &cur->info;
+ info->FillModuleInfo(first->info.module, first->info.module_offset);
+ last->next = cur;
+ last = cur;
+ }
+ CHECK_EQ(addr, first->info.address);
+ CHECK_EQ(addr, last->info.address);
+ return &last->info;
+ }
};
extern "C" {
static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr,
const char *filename, int lineno,
const char *function) {
- SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata;
+ SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
if (function) {
- AddressInfo *info = &cdata->frames[cdata->n_frames++];
- info->Clear();
- info->FillAddressAndModuleInfo(addr, cdata->module_name,
- cdata->module_offset);
- info->function = LibbacktraceSymbolizer::Demangle(function, true);
+ AddressInfo *info = cdata->get_new_frame(addr);
+ info->function = DemangleAlloc(function, /*always_alloc*/ true);
if (filename)
info->file = internal_strdup(filename);
info->line = lineno;
- if (cdata->n_frames == cdata->max_frames)
- return 1;
+ cdata->frames_symbolized++;
}
return 0;
}
static void SymbolizeCodeCallback(void *vdata, uintptr_t addr,
const char *symname, uintptr_t, uintptr_t) {
- SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata;
+ SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
if (symname) {
- AddressInfo *info = &cdata->frames[0];
- info->Clear();
- info->FillAddressAndModuleInfo(addr, cdata->module_name,
- cdata->module_offset);
- info->function = LibbacktraceSymbolizer::Demangle(symname, true);
- cdata->n_frames = 1;
+ AddressInfo *info = cdata->get_new_frame(addr);
+ info->function = DemangleAlloc(symname, /*always_alloc*/ true);
+ cdata->frames_symbolized++;
}
}
@@ -126,7 +133,7 @@ static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname,
uintptr_t symval, uintptr_t symsize) {
DataInfo *info = (DataInfo *)vdata;
if (symname && symval) {
- info->name = LibbacktraceSymbolizer::Demangle(symname, true);
+ info->name = DemangleAlloc(symname, /*always_alloc*/ true);
info->start = symval;
info->size = symsize;
}
@@ -146,23 +153,18 @@ LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
return new(*alloc) LibbacktraceSymbolizer(state);
}
-uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames,
- uptr max_frames,
- const char *module_name,
- uptr module_offset) {
- SymbolizeCodeData data;
- data.frames = frames;
- data.n_frames = 0;
- data.max_frames = max_frames;
- data.module_name = module_name;
- data.module_offset = module_offset;
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ SymbolizeCodeCallbackArg data;
+ data.first = stack;
+ data.last = stack;
+ data.frames_symbolized = 0;
backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback,
ErrorCallback, &data);
- if (data.n_frames)
- return data.n_frames;
+ if (data.frames_symbolized > 0)
+ return true;
backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback,
ErrorCallback, &data);
- return data.n_frames;
+ return (data.frames_symbolized > 0);
}
bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
@@ -177,12 +179,9 @@ LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
return 0;
}
-uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames,
- uptr max_frames,
- const char *module_name,
- uptr module_offset) {
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
(void)state_;
- return 0;
+ return false;
}
bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
@@ -191,7 +190,7 @@ bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
#endif // SANITIZER_LIBBACKTRACE
-char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) {
+static char *DemangleAlloc(const char *name, bool always_alloc) {
#if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE
if (char *demangled = CplusV3Demangle(name))
return demangled;
@@ -201,4 +200,8 @@ char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) {
return 0;
}
+const char *LibbacktraceSymbolizer::Demangle(const char *name) {
+ return DemangleAlloc(name, /*always_alloc*/ false);
+}
+
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
index aa75a7907df..ab1d6f99163 100644
--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
@@ -14,7 +14,8 @@
#include "sanitizer_platform.h"
#include "sanitizer_common.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_symbolizer_internal.h"
#ifndef SANITIZER_LIBBACKTRACE
# define SANITIZER_LIBBACKTRACE 0
@@ -26,17 +27,16 @@
namespace __sanitizer {
-class LibbacktraceSymbolizer {
+class LibbacktraceSymbolizer : public SymbolizerTool {
public:
static LibbacktraceSymbolizer *get(LowLevelAllocator *alloc);
- uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames,
- const char *module_name, uptr module_offset);
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
- bool SymbolizeData(uptr addr, DataInfo *info);
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
// May return NULL if demangling failed.
- static char *Demangle(const char *name, bool always_alloc = false);
+ const char *Demangle(const char *name) override;
private:
explicit LibbacktraceSymbolizer(void *state) : state_(state) {}
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cc
index 9afd805a82c..4264b5e3ddc 100644
--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cc
@@ -9,18 +9,418 @@
// run-time libraries.
//===----------------------------------------------------------------------===//
+#include "sanitizer_allocator_internal.h"
#include "sanitizer_internal_defs.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
namespace __sanitizer {
+const char *ExtractToken(const char *str, const char *delims, char **result) {
+ uptr prefix_len = internal_strcspn(str, delims);
+ *result = (char*)InternalAlloc(prefix_len + 1);
+ internal_memcpy(*result, str, prefix_len);
+ (*result)[prefix_len] = '\0';
+ const char *prefix_end = str + prefix_len;
+ if (*prefix_end != '\0') prefix_end++;
+ return prefix_end;
+}
+
+const char *ExtractInt(const char *str, const char *delims, int *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (int)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+const char *ExtractUptr(const char *str, const char *delims, uptr *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (uptr)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+ char **result) {
+ const char *found_delimiter = internal_strstr(str, delimiter);
+ uptr prefix_len =
+ found_delimiter ? found_delimiter - str : internal_strlen(str);
+ *result = (char *)InternalAlloc(prefix_len + 1);
+ internal_memcpy(*result, str, prefix_len);
+ (*result)[prefix_len] = '\0';
+ const char *prefix_end = str + prefix_len;
+ if (*prefix_end != '\0') prefix_end += internal_strlen(delimiter);
+ return prefix_end;
+}
+
+SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
+ BlockingMutexLock l(&mu_);
+ const char *module_name;
+ uptr module_offset;
+ SymbolizedStack *res = SymbolizedStack::New(addr);
+ if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
+ return res;
+ // Always fill data about module name and offset.
+ res->info.FillModuleInfo(module_name, module_offset);
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ if (tool->SymbolizePC(addr, res)) {
+ return res;
+ }
+ }
+ return res;
+}
+
+bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ BlockingMutexLock l(&mu_);
+ const char *module_name;
+ uptr module_offset;
+ if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
+ return false;
+ info->Clear();
+ info->module = internal_strdup(module_name);
+ info->module_offset = module_offset;
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ if (tool->SymbolizeData(addr, info)) {
+ return true;
+ }
+ }
+ return true;
+}
+
+bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+ uptr *module_address) {
+ BlockingMutexLock l(&mu_);
+ const char *internal_module_name = nullptr;
+ if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name,
+ module_address))
+ return false;
+
+ if (module_name)
+ *module_name = module_names_.GetOwnedCopy(internal_module_name);
+ return true;
+}
+
+void Symbolizer::Flush() {
+ BlockingMutexLock l(&mu_);
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ tool->Flush();
+ }
+}
+
+const char *Symbolizer::Demangle(const char *name) {
+ BlockingMutexLock l(&mu_);
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ if (const char *demangled = tool->Demangle(name))
+ return demangled;
+ }
+ return PlatformDemangle(name);
+}
+
+void Symbolizer::PrepareForSandboxing() {
+ BlockingMutexLock l(&mu_);
+ PlatformPrepareForSandboxing();
+}
+
+bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,
+ const char **module_name,
+ uptr *module_offset) {
+ LoadedModule *module = FindModuleForAddress(address);
+ if (module == 0)
+ return false;
+ *module_name = module->full_name();
+ *module_offset = address - module->base_address();
+ return true;
+}
+
+LoadedModule *Symbolizer::FindModuleForAddress(uptr address) {
+ bool modules_were_reloaded = false;
+ if (!modules_fresh_) {
+ for (uptr i = 0; i < n_modules_; i++)
+ modules_[i].clear();
+ n_modules_ =
+ GetListOfModules(modules_, kMaxNumberOfModules, /* filter */ nullptr);
+ CHECK_GT(n_modules_, 0);
+ CHECK_LT(n_modules_, kMaxNumberOfModules);
+ modules_fresh_ = true;
+ modules_were_reloaded = true;
+ }
+ for (uptr i = 0; i < n_modules_; i++) {
+ if (modules_[i].containsAddress(address)) {
+ return &modules_[i];
+ }
+ }
+ // Reload the modules and look up again, if we haven't tried it yet.
+ if (!modules_were_reloaded) {
+ // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.
+ // It's too aggressive to reload the list of modules each time we fail
+ // to find a module for a given address.
+ modules_fresh_ = false;
+ return FindModuleForAddress(address);
+ }
+ return 0;
+}
+
Symbolizer *Symbolizer::GetOrInit() {
SpinMutexLock l(&init_mu_);
if (symbolizer_)
return symbolizer_;
- if ((symbolizer_ = PlatformInit()))
- return symbolizer_;
- return Disable();
+ symbolizer_ = PlatformInit();
+ CHECK(symbolizer_);
+ return symbolizer_;
+}
+
+// For now we assume the following protocol:
+// For each request of the form
+// <module_name> <module_offset>
+// passed to STDIN, external symbolizer prints to STDOUT response:
+// <function_name>
+// <file_name>:<line_number>:<column_number>
+// <function_name>
+// <file_name>:<line_number>:<column_number>
+// ...
+// <empty line>
+class LLVMSymbolizerProcess : public SymbolizerProcess {
+ public:
+ explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}
+
+ private:
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+ // Empty line marks the end of llvm-symbolizer output.
+ return length >= 2 && buffer[length - 1] == '\n' &&
+ buffer[length - 2] == '\n';
+ }
+
+ void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const override {
+#if defined(__x86_64h__)
+ const char* const kSymbolizerArch = "--default-arch=x86_64h";
+#elif defined(__x86_64__)
+ const char* const kSymbolizerArch = "--default-arch=x86_64";
+#elif defined(__i386__)
+ const char* const kSymbolizerArch = "--default-arch=i386";
+#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__)
+ const char* const kSymbolizerArch = "--default-arch=powerpc64";
+#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
+ const char* const kSymbolizerArch = "--default-arch=powerpc64le";
+#else
+ const char* const kSymbolizerArch = "--default-arch=unknown";
+#endif
+
+ const char *const inline_flag = common_flags()->symbolize_inline_frames
+ ? "--inlining=true"
+ : "--inlining=false";
+ int i = 0;
+ argv[i++] = path_to_binary;
+ argv[i++] = inline_flag;
+ argv[i++] = kSymbolizerArch;
+ argv[i++] = nullptr;
+ }
+};
+
+LLVMSymbolizer::LLVMSymbolizer(const char *path, LowLevelAllocator *allocator)
+ : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {}
+
+// Parse a <file>:<line>[:<column>] buffer. The file path may contain colons on
+// Windows, so extract tokens from the right hand side first. The column info is
+// also optional.
+static const char *ParseFileLineInfo(AddressInfo *info, const char *str) {
+ char *file_line_info = 0;
+ str = ExtractToken(str, "\n", &file_line_info);
+ CHECK(file_line_info);
+ // Parse the last :<int>, which must be there.
+ char *last_colon = internal_strrchr(file_line_info, ':');
+ CHECK(last_colon);
+ int line_or_column = internal_atoll(last_colon + 1);
+ // Truncate the string at the last colon and find the next-to-last colon.
+ *last_colon = '\0';
+ last_colon = internal_strrchr(file_line_info, ':');
+ if (last_colon && IsDigit(last_colon[1])) {
+ // If the second-to-last colon is followed by a digit, it must be the line
+ // number, and the previous parsed number was a column.
+ info->line = internal_atoll(last_colon + 1);
+ info->column = line_or_column;
+ *last_colon = '\0';
+ } else {
+ // Otherwise, we have line info but no column info.
+ info->line = line_or_column;
+ info->column = 0;
+ }
+ ExtractToken(file_line_info, "", &info->file);
+ InternalFree(file_line_info);
+ return str;
+}
+
+// Parses one or more two-line strings in the following format:
+// <function_name>
+// <file_name>:<line_number>[:<column_number>]
+// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
+// them use the same output format.
+void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) {
+ bool top_frame = true;
+ SymbolizedStack *last = res;
+ while (true) {
+ char *function_name = 0;
+ str = ExtractToken(str, "\n", &function_name);
+ CHECK(function_name);
+ if (function_name[0] == '\0') {
+ // There are no more frames.
+ InternalFree(function_name);
+ break;
+ }
+ SymbolizedStack *cur;
+ if (top_frame) {
+ cur = res;
+ top_frame = false;
+ } else {
+ cur = SymbolizedStack::New(res->info.address);
+ cur->info.FillModuleInfo(res->info.module, res->info.module_offset);
+ last->next = cur;
+ last = cur;
+ }
+
+ AddressInfo *info = &cur->info;
+ info->function = function_name;
+ str = ParseFileLineInfo(info, str);
+
+ // Functions and filenames can be "??", in which case we write 0
+ // to address info to mark that names are unknown.
+ if (0 == internal_strcmp(info->function, "??")) {
+ InternalFree(info->function);
+ info->function = 0;
+ }
+ if (0 == internal_strcmp(info->file, "??")) {
+ InternalFree(info->file);
+ info->file = 0;
+ }
+ }
+}
+
+// Parses a two-line string in the following format:
+// <symbol_name>
+// <start_address> <size>
+// Used by LLVMSymbolizer and InternalSymbolizer.
+void ParseSymbolizeDataOutput(const char *str, DataInfo *info) {
+ str = ExtractToken(str, "\n", &info->name);
+ str = ExtractUptr(str, " ", &info->start);
+ str = ExtractUptr(str, "\n", &info->size);
+}
+
+bool LLVMSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module,
+ stack->info.module_offset)) {
+ ParseSymbolizePCOutput(buf, stack);
+ return true;
+ }
+ return false;
+}
+
+bool LLVMSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ if (const char *buf =
+ SendCommand(/*is_data*/ true, info->module, info->module_offset)) {
+ ParseSymbolizeDataOutput(buf, info);
+ info->start += (addr - info->module_offset); // Add the base address.
+ return true;
+ }
+ return false;
+}
+
+const char *LLVMSymbolizer::SendCommand(bool is_data, const char *module_name,
+ uptr module_offset) {
+ CHECK(module_name);
+ internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n",
+ is_data ? "DATA " : "", module_name, module_offset);
+ return symbolizer_process_->SendCommand(buffer_);
+}
+
+SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
+ : path_(path),
+ input_fd_(kInvalidFd),
+ output_fd_(kInvalidFd),
+ times_restarted_(0),
+ failed_to_start_(false),
+ reported_invalid_path_(false),
+ use_forkpty_(use_forkpty) {
+ CHECK(path_);
+ CHECK_NE(path_[0], '\0');
+}
+
+const char *SymbolizerProcess::SendCommand(const char *command) {
+ for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
+ // Start or restart symbolizer if we failed to send command to it.
+ if (const char *res = SendCommandImpl(command))
+ return res;
+ Restart();
+ }
+ if (!failed_to_start_) {
+ Report("WARNING: Failed to use and restart external symbolizer!\n");
+ failed_to_start_ = true;
+ }
+ return 0;
+}
+
+const char *SymbolizerProcess::SendCommandImpl(const char *command) {
+ if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
+ return 0;
+ if (!WriteToSymbolizer(command, internal_strlen(command)))
+ return 0;
+ if (!ReadFromSymbolizer(buffer_, kBufferSize))
+ return 0;
+ return buffer_;
+}
+
+bool SymbolizerProcess::Restart() {
+ if (input_fd_ != kInvalidFd)
+ CloseFile(input_fd_);
+ if (output_fd_ != kInvalidFd)
+ CloseFile(output_fd_);
+ return StartSymbolizerSubprocess();
+}
+
+bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) {
+ if (max_length == 0)
+ return true;
+ uptr read_len = 0;
+ while (true) {
+ uptr just_read = 0;
+ bool success = ReadFromFile(input_fd_, buffer + read_len,
+ max_length - read_len - 1, &just_read);
+ // We can't read 0 bytes, as we don't expect external symbolizer to close
+ // its stdout.
+ if (!success || just_read == 0) {
+ Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
+ return false;
+ }
+ read_len += just_read;
+ if (ReachedEndOfOutput(buffer, read_len))
+ break;
+ }
+ buffer[read_len] = '\0';
+ return true;
+}
+
+bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) {
+ if (length == 0)
+ return true;
+ uptr write_len = 0;
+ bool success = WriteToFile(output_fd_, buffer, length, &write_len);
+ if (!success || write_len != length) {
+ Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
+ return false;
+ }
+ return true;
}
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc
new file mode 100644
index 00000000000..d3495348e75
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.cc
@@ -0,0 +1,151 @@
+//===-- sanitizer_symbolizer_mac.cc ---------------------------------------===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Implementation of Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_mac.h"
+#include "sanitizer_symbolizer_mac.h"
+
+namespace __sanitizer {
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <util.h>
+
+bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ Dl_info info;
+ int result = dladdr((const void *)addr, &info);
+ if (!result) return false;
+ const char *demangled = DemangleCXXABI(info.dli_sname);
+ stack->info.function = internal_strdup(demangled);
+ return true;
+}
+
+bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ return false;
+}
+
+class AtosSymbolizerProcess : public SymbolizerProcess {
+ public:
+ explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
+ : SymbolizerProcess(path, /*use_forkpty*/ true) {
+ // Put the string command line argument in the object so that it outlives
+ // the call to GetArgV.
+ internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
+ }
+
+ private:
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+ return (length >= 1 && buffer[length - 1] == '\n');
+ }
+
+ void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const override {
+ int i = 0;
+ argv[i++] = path_to_binary;
+ argv[i++] = "-p";
+ argv[i++] = &pid_str_[0];
+ if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
+ // On Mavericks atos prints a deprecation warning which we suppress by
+ // passing -d. The warning isn't present on other OSX versions, even the
+ // newer ones.
+ argv[i++] = "-d";
+ }
+ argv[i++] = nullptr;
+ }
+
+ char pid_str_[16];
+};
+
+static const char *kAtosErrorMessages[] = {
+ "atos cannot examine process",
+ "unable to get permission to examine process",
+ "An admin user name and password is required",
+ "could not load inserted library",
+ "architecture mismatch between analysis process",
+};
+
+static bool IsAtosErrorMessage(const char *str) {
+ for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
+ if (internal_strstr(str, kAtosErrorMessages[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
+ // Trim ending newlines.
+ char *trim;
+ ExtractTokenUpToDelimiter(str, "\n", &trim);
+
+ // The line from `atos` is in one of these formats:
+ // myfunction (in library.dylib) (sourcefile.c:17)
+ // myfunction (in library.dylib) + 0x1fe
+ // 0xdeadbeef (in library.dylib) + 0x1fe
+ // 0xdeadbeef (in library.dylib)
+ // 0xdeadbeef
+
+ if (IsAtosErrorMessage(trim)) {
+ Report("atos returned an error: %s\n", trim);
+ InternalFree(trim);
+ return false;
+ }
+
+ const char *rest = trim;
+ char *function_name;
+ rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name);
+ if (internal_strncmp(function_name, "0x", 2) != 0)
+ res->info.function = function_name;
+ else
+ InternalFree(function_name);
+ rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module);
+
+ if (rest[0] == '(') {
+ rest++;
+ rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file);
+ char *extracted_line_number;
+ rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
+ res->info.line = internal_atoll(extracted_line_number);
+ InternalFree(extracted_line_number);
+ }
+
+ InternalFree(trim);
+ return true;
+}
+
+AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
+ : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
+
+bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ if (!process_) return false;
+ char command[32];
+ internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+ const char *buf = process_->SendCommand(command);
+ if (!buf) return false;
+ if (!ParseCommandOutput(buf, stack)) {
+ process_ = nullptr;
+ return false;
+ }
+ return true;
+}
+
+bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; }
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.h
new file mode 100644
index 00000000000..240c538d93d
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_mac.h
@@ -0,0 +1,46 @@
+//===-- sanitizer_symbolizer_mac.h ------------------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Header for Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_SYMBOLIZER_MAC_H
+#define SANITIZER_SYMBOLIZER_MAC_H
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+class DlAddrSymbolizer : public SymbolizerTool {
+ public:
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
+};
+
+class AtosSymbolizerProcess;
+
+class AtosSymbolizer : public SymbolizerTool {
+ public:
+ explicit AtosSymbolizer(const char *path, LowLevelAllocator *allocator);
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+ private:
+ AtosSymbolizerProcess *process_;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
+
+#endif // SANITIZER_SYMBOLIZER_MAC_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
index 5cc21d3f2bf..a67ae36a4d8 100644
--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
@@ -18,15 +18,21 @@
#include "sanitizer_internal_defs.h"
#include "sanitizer_linux.h"
#include "sanitizer_placement_new.h"
+#include "sanitizer_posix.h"
#include "sanitizer_procmaps.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
#include "sanitizer_symbolizer_libbacktrace.h"
+#include "sanitizer_symbolizer_mac.h"
#include <errno.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
+#if SANITIZER_MAC
+#include <util.h> // for forkpty()
+#endif // SANITIZER_MAC
+
// C++ demangling function, as required by Itanium C++ ABI. This is weak,
// because we do not require a C++ ABI library to be linked to a program
// using sanitizers; if it's not present, we'll just use the mangled name.
@@ -39,7 +45,7 @@ namespace __cxxabiv1 {
namespace __sanitizer {
// Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
-static const char *DemangleCXXABI(const char *name) {
+const char *DemangleCXXABI(const char *name) {
// FIXME: __cxa_demangle aggressively insists on allocating memory.
// There's not much we can do about that, short of providing our
// own demangler (libc++abi's implementation could be adapted so that
@@ -53,150 +59,47 @@ static const char *DemangleCXXABI(const char *name) {
return name;
}
-// Extracts the prefix of "str" that consists of any characters not
-// present in "delims" string, and copies this prefix to "result", allocating
-// space for it.
-// Returns a pointer to "str" after skipping extracted prefix and first
-// delimiter char.
-static const char *ExtractToken(const char *str, const char *delims,
- char **result) {
- uptr prefix_len = internal_strcspn(str, delims);
- *result = (char*)InternalAlloc(prefix_len + 1);
- internal_memcpy(*result, str, prefix_len);
- (*result)[prefix_len] = '\0';
- const char *prefix_end = str + prefix_len;
- if (*prefix_end != '\0') prefix_end++;
- return prefix_end;
-}
-
-// Same as ExtractToken, but converts extracted token to integer.
-static const char *ExtractInt(const char *str, const char *delims,
- int *result) {
- char *buff;
- const char *ret = ExtractToken(str, delims, &buff);
- if (buff != 0) {
- *result = (int)internal_atoll(buff);
- }
- InternalFree(buff);
- return ret;
-}
-
-static const char *ExtractUptr(const char *str, const char *delims,
- uptr *result) {
- char *buff;
- const char *ret = ExtractToken(str, delims, &buff);
- if (buff != 0) {
- *result = (uptr)internal_atoll(buff);
- }
- InternalFree(buff);
- return ret;
-}
-
-class ExternalSymbolizerInterface {
- public:
- // Can't declare pure virtual functions in sanitizer runtimes:
- // __cxa_pure_virtual might be unavailable.
- virtual char *SendCommand(bool is_data, const char *module_name,
- uptr module_offset) {
- UNIMPLEMENTED();
- }
-};
-
-// SymbolizerProcess encapsulates communication between the tool and
-// external symbolizer program, running in a different subprocess.
-// SymbolizerProcess may not be used from two threads simultaneously.
-class SymbolizerProcess : public ExternalSymbolizerInterface {
- public:
- explicit SymbolizerProcess(const char *path)
- : path_(path),
- input_fd_(kInvalidFd),
- output_fd_(kInvalidFd),
- times_restarted_(0),
- failed_to_start_(false),
- reported_invalid_path_(false) {
- CHECK(path_);
- CHECK_NE(path_[0], '\0');
- }
-
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
- // Start or restart symbolizer if we failed to send command to it.
- if (char *res = SendCommandImpl(is_data, module_name, module_offset))
- return res;
- Restart();
- }
- if (!failed_to_start_) {
- Report("WARNING: Failed to use and restart external symbolizer!\n");
- failed_to_start_ = true;
- }
- return 0;
- }
-
- private:
- bool Restart() {
- if (input_fd_ != kInvalidFd)
- internal_close(input_fd_);
- if (output_fd_ != kInvalidFd)
- internal_close(output_fd_);
- return StartSymbolizerSubprocess();
- }
-
- char *SendCommandImpl(bool is_data, const char *module_name,
- uptr module_offset) {
- if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
- return 0;
- CHECK(module_name);
- if (!RenderInputCommand(buffer_, kBufferSize, is_data, module_name,
- module_offset))
- return 0;
- if (!writeToSymbolizer(buffer_, internal_strlen(buffer_)))
- return 0;
- if (!readFromSymbolizer(buffer_, kBufferSize))
- return 0;
- return buffer_;
- }
-
- bool readFromSymbolizer(char *buffer, uptr max_length) {
- if (max_length == 0)
- return true;
- uptr read_len = 0;
- while (true) {
- uptr just_read = internal_read(input_fd_, buffer + read_len,
- max_length - read_len - 1);
- // We can't read 0 bytes, as we don't expect external symbolizer to close
- // its stdout.
- if (just_read == 0 || just_read == (uptr)-1) {
- Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
- return false;
- }
- read_len += just_read;
- if (ReachedEndOfOutput(buffer, read_len))
- break;
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+ if (!FileExists(path_)) {
+ if (!reported_invalid_path_) {
+ Report("WARNING: invalid path to external symbolizer!\n");
+ reported_invalid_path_ = true;
}
- buffer[read_len] = '\0';
- return true;
- }
-
- bool writeToSymbolizer(const char *buffer, uptr length) {
- if (length == 0)
- return true;
- uptr write_len = internal_write(output_fd_, buffer, length);
- if (write_len == 0 || write_len == (uptr)-1) {
- Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
- return false;
- }
- return true;
+ return false;
}
- bool StartSymbolizerSubprocess() {
- if (!FileExists(path_)) {
- if (!reported_invalid_path_) {
- Report("WARNING: invalid path to external symbolizer!\n");
- reported_invalid_path_ = true;
- }
+ int pid;
+ if (use_forkpty_) {
+#if SANITIZER_MAC
+ fd_t fd = kInvalidFd;
+ // Use forkpty to disable buffering in the new terminal.
+ pid = forkpty(&fd, 0, 0, 0);
+ if (pid == -1) {
+ // forkpty() failed.
+ Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
+ errno);
return false;
+ } else if (pid == 0) {
+ // Child subprocess.
+ const char *argv[kArgVMax];
+ GetArgV(path_, argv);
+ execv(path_, const_cast<char **>(&argv[0]));
+ internal__exit(1);
}
+ // Continue execution in parent process.
+ input_fd_ = output_fd_ = fd;
+
+ // Disable echo in the new terminal, disable CR.
+ struct termios termflags;
+ tcgetattr(fd, &termflags);
+ termflags.c_oflag &= ~ONLCR;
+ termflags.c_lflag &= ~ECHO;
+ tcsetattr(fd, TCSANOW, &termflags);
+#else // SANITIZER_MAC
+ UNIMPLEMENTED();
+#endif // SANITIZER_MAC
+ } else {
int *infd = NULL;
int *outfd = NULL;
// The client program may close its stdin and/or stdout and/or stderr
@@ -233,7 +136,7 @@ class SymbolizerProcess : public ExternalSymbolizerInterface {
CHECK(outfd);
// Real fork() may call user callbacks registered with pthread_atfork().
- int pid = internal_fork();
+ pid = internal_fork();
if (pid == -1) {
// Fork() failed.
internal_close(infd[0]);
@@ -255,7 +158,9 @@ class SymbolizerProcess : public ExternalSymbolizerInterface {
internal_close(infd[1]);
for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
internal_close(fd);
- ExecuteWithDefaultArgs(path_);
+ const char *argv[kArgVMax];
+ GetArgV(path_, argv);
+ execv(path_, const_cast<char **>(&argv[0]));
internal__exit(1);
}
@@ -264,96 +169,20 @@ class SymbolizerProcess : public ExternalSymbolizerInterface {
internal_close(infd[1]);
input_fd_ = infd[0];
output_fd_ = outfd[1];
-
- // Check that symbolizer subprocess started successfully.
- int pid_status;
- SleepForMillis(kSymbolizerStartupTimeMillis);
- int exited_pid = waitpid(pid, &pid_status, WNOHANG);
- if (exited_pid != 0) {
- // Either waitpid failed, or child has already exited.
- Report("WARNING: external symbolizer didn't start up correctly!\n");
- return false;
- }
-
- return true;
- }
-
- virtual bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
- const char *module_name,
- uptr module_offset) const {
- UNIMPLEMENTED();
- }
-
- virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {
- UNIMPLEMENTED();
}
- virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const {
- UNIMPLEMENTED();
- }
-
- const char *path_;
- int input_fd_;
- int output_fd_;
-
- static const uptr kBufferSize = 16 * 1024;
- char buffer_[kBufferSize];
-
- static const uptr kMaxTimesRestarted = 5;
- static const int kSymbolizerStartupTimeMillis = 10;
- uptr times_restarted_;
- bool failed_to_start_;
- bool reported_invalid_path_;
-};
-
-// For now we assume the following protocol:
-// For each request of the form
-// <module_name> <module_offset>
-// passed to STDIN, external symbolizer prints to STDOUT response:
-// <function_name>
-// <file_name>:<line_number>:<column_number>
-// <function_name>
-// <file_name>:<line_number>:<column_number>
-// ...
-// <empty line>
-class LLVMSymbolizerProcess : public SymbolizerProcess {
- public:
- explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}
-
- private:
- bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
- const char *module_name, uptr module_offset) const {
- internal_snprintf(buffer, max_length, "%s\"%s\" 0x%zx\n",
- is_data ? "DATA " : "", module_name, module_offset);
- return true;
- }
-
- bool ReachedEndOfOutput(const char *buffer, uptr length) const {
- // Empty line marks the end of llvm-symbolizer output.
- return length >= 2 && buffer[length - 1] == '\n' &&
- buffer[length - 2] == '\n';
+ // Check that symbolizer subprocess started successfully.
+ int pid_status;
+ SleepForMillis(kSymbolizerStartupTimeMillis);
+ int exited_pid = waitpid(pid, &pid_status, WNOHANG);
+ if (exited_pid != 0) {
+ // Either waitpid failed, or child has already exited.
+ Report("WARNING: external symbolizer didn't start up correctly!\n");
+ return false;
}
- void ExecuteWithDefaultArgs(const char *path_to_binary) const {
-#if defined(__x86_64__)
- const char* const kSymbolizerArch = "--default-arch=x86_64";
-#elif defined(__i386__)
- const char* const kSymbolizerArch = "--default-arch=i386";
-#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__)
- const char* const kSymbolizerArch = "--default-arch=powerpc64";
-#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
- const char* const kSymbolizerArch = "--default-arch=powerpc64le";
-#else
- const char* const kSymbolizerArch = "--default-arch=unknown";
-#endif
-
- const char *const inline_flag = common_flags()->symbolize_inline_frames
- ? "--inlining=true"
- : "--inlining=false";
- execl(path_to_binary, path_to_binary, inline_flag, kSymbolizerArch,
- (char *)0);
- }
-};
+ return true;
+}
class Addr2LineProcess : public SymbolizerProcess {
public:
@@ -363,44 +192,76 @@ class Addr2LineProcess : public SymbolizerProcess {
const char *module_name() const { return module_name_; }
private:
- bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
- const char *module_name, uptr module_offset) const {
- if (is_data)
- return false;
- CHECK_EQ(0, internal_strcmp(module_name, module_name_));
- internal_snprintf(buffer, max_length, "0x%zx\n", module_offset);
- return true;
+ void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const override {
+ int i = 0;
+ argv[i++] = path_to_binary;
+ argv[i++] = "-iCfe";
+ argv[i++] = module_name_;
+ argv[i++] = nullptr;
}
- bool ReachedEndOfOutput(const char *buffer, uptr length) const {
- // Output should consist of two lines.
- int num_lines = 0;
- for (uptr i = 0; i < length; ++i) {
- if (buffer[i] == '\n')
- num_lines++;
- if (num_lines >= 2)
- return true;
- }
- return false;
- }
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override;
- void ExecuteWithDefaultArgs(const char *path_to_binary) const {
- execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0);
+ bool ReadFromSymbolizer(char *buffer, uptr max_length) override {
+ if (!SymbolizerProcess::ReadFromSymbolizer(buffer, max_length))
+ return false;
+ // We should cut out output_terminator_ at the end of given buffer,
+ // appended by addr2line to mark the end of its meaningful output.
+ // We cannot scan buffer from it's beginning, because it is legal for it
+ // to start with output_terminator_ in case given offset is invalid. So,
+ // scanning from second character.
+ char *garbage = internal_strstr(buffer + 1, output_terminator_);
+ // This should never be NULL since buffer must end up with
+ // output_terminator_.
+ CHECK(garbage);
+ // Trim the buffer.
+ garbage[0] = '\0';
+ return true;
}
const char *module_name_; // Owned, leaked.
+ static const char output_terminator_[];
};
-class Addr2LinePool : public ExternalSymbolizerInterface {
+const char Addr2LineProcess::output_terminator_[] = "??\n??:0\n";
+
+bool Addr2LineProcess::ReachedEndOfOutput(const char *buffer,
+ uptr length) const {
+ const size_t kTerminatorLen = sizeof(output_terminator_) - 1;
+ // Skip, if we read just kTerminatorLen bytes, because Addr2Line output
+ // should consist at least of two pairs of lines:
+ // 1. First one, corresponding to given offset to be symbolized
+ // (may be equal to output_terminator_, if offset is not valid).
+ // 2. Second one for output_terminator_, itself to mark the end of output.
+ if (length <= kTerminatorLen) return false;
+ // Addr2Line output should end up with output_terminator_.
+ return !internal_memcmp(buffer + length - kTerminatorLen,
+ output_terminator_, kTerminatorLen);
+}
+
+class Addr2LinePool : public SymbolizerTool {
public:
explicit Addr2LinePool(const char *addr2line_path,
LowLevelAllocator *allocator)
: addr2line_path_(addr2line_path), allocator_(allocator),
addr2line_pool_(16) {}
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- if (is_data)
- return 0;
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+ if (const char *buf =
+ SendCommand(stack->info.module, stack->info.module_offset)) {
+ ParseSymbolizePCOutput(buf, stack);
+ return true;
+ }
+ return false;
+ }
+
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ return false;
+ }
+
+ private:
+ const char *SendCommand(const char *module_name, uptr module_offset) {
Addr2LineProcess *addr2line = 0;
for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
if (0 ==
@@ -414,13 +275,19 @@ class Addr2LinePool : public ExternalSymbolizerInterface {
new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
addr2line_pool_.push_back(addr2line);
}
- return addr2line->SendCommand(is_data, module_name, module_offset);
+ CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name()));
+ char buffer[kBufferSize];
+ internal_snprintf(buffer, kBufferSize, "0x%zx\n0x%zx\n",
+ module_offset, dummy_address_);
+ return addr2line->SendCommand(buffer);
}
- private:
+ static const uptr kBufferSize = 64;
const char *addr2line_path_;
LowLevelAllocator *allocator_;
InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
+ static const uptr dummy_address_ =
+ FIRST_32_SECOND_64(UINT32_MAX, UINT64_MAX);
};
#if SANITIZER_SUPPORTS_WEAK_HOOKS
@@ -438,10 +305,8 @@ int __sanitizer_symbolize_demangle(const char *Name, char *Buffer,
int MaxLength);
} // extern "C"
-class InternalSymbolizer {
+class InternalSymbolizer : public SymbolizerTool {
public:
- typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int);
-
static InternalSymbolizer *get(LowLevelAllocator *alloc) {
if (__sanitizer_symbolize_code != 0 &&
__sanitizer_symbolize_data != 0) {
@@ -450,20 +315,29 @@ class InternalSymbolizer {
return 0;
}
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data
- : __sanitizer_symbolize_code;
- if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize))
- return buffer_;
- return 0;
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+ bool result = __sanitizer_symbolize_code(
+ stack->info.module, stack->info.module_offset, buffer_, kBufferSize);
+ if (result) ParseSymbolizePCOutput(buffer_, stack);
+ return result;
+ }
+
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ bool result = __sanitizer_symbolize_data(info->module, info->module_offset,
+ buffer_, kBufferSize);
+ if (result) {
+ ParseSymbolizeDataOutput(buffer_, info);
+ info->start += (addr - info->module_offset); // Add the base address.
+ }
+ return result;
}
- void Flush() {
+ void Flush() override {
if (__sanitizer_symbolize_flush)
__sanitizer_symbolize_flush();
}
- const char *Demangle(const char *name) {
+ const char *Demangle(const char *name) override {
if (__sanitizer_symbolize_demangle) {
for (uptr res_length = 1024;
res_length <= InternalSizeClassMap::kMaxSize;) {
@@ -490,272 +364,101 @@ class InternalSymbolizer {
};
#else // SANITIZER_SUPPORTS_WEAK_HOOKS
-class InternalSymbolizer {
+class InternalSymbolizer : public SymbolizerTool {
public:
static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; }
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- return 0;
- }
- void Flush() { }
- const char *Demangle(const char *name) { return name; }
};
#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
-class POSIXSymbolizer : public Symbolizer {
- public:
- POSIXSymbolizer(ExternalSymbolizerInterface *external_symbolizer,
- InternalSymbolizer *internal_symbolizer,
- LibbacktraceSymbolizer *libbacktrace_symbolizer)
- : Symbolizer(),
- external_symbolizer_(external_symbolizer),
- internal_symbolizer_(internal_symbolizer),
- libbacktrace_symbolizer_(libbacktrace_symbolizer) {}
-
- uptr SymbolizePC(uptr addr, AddressInfo *frames, uptr max_frames) {
- BlockingMutexLock l(&mu_);
- if (max_frames == 0)
- return 0;
- const char *module_name;
- uptr module_offset;
- if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
- return 0;
- // First, try to use libbacktrace symbolizer (if it's available).
- if (libbacktrace_symbolizer_ != 0) {
- mu_.CheckLocked();
- uptr res = libbacktrace_symbolizer_->SymbolizeCode(
- addr, frames, max_frames, module_name, module_offset);
- if (res > 0)
- return res;
- }
- const char *str = SendCommand(false, module_name, module_offset);
- if (str == 0) {
- // Symbolizer was not initialized or failed. Fill only data
- // about module name and offset.
- AddressInfo *info = &frames[0];
- info->Clear();
- info->FillAddressAndModuleInfo(addr, module_name, module_offset);
- return 1;
- }
- uptr frame_id = 0;
- for (frame_id = 0; frame_id < max_frames; frame_id++) {
- AddressInfo *info = &frames[frame_id];
- char *function_name = 0;
- str = ExtractToken(str, "\n", &function_name);
- CHECK(function_name);
- if (function_name[0] == '\0') {
- // There are no more frames.
- break;
- }
- info->Clear();
- info->FillAddressAndModuleInfo(addr, module_name, module_offset);
- info->function = function_name;
- // Parse <file>:<line>:<column> buffer.
- char *file_line_info = 0;
- str = ExtractToken(str, "\n", &file_line_info);
- CHECK(file_line_info);
- const char *line_info = ExtractToken(file_line_info, ":", &info->file);
- line_info = ExtractInt(line_info, ":", &info->line);
- line_info = ExtractInt(line_info, "", &info->column);
- InternalFree(file_line_info);
-
- // Functions and filenames can be "??", in which case we write 0
- // to address info to mark that names are unknown.
- if (0 == internal_strcmp(info->function, "??")) {
- InternalFree(info->function);
- info->function = 0;
- }
- if (0 == internal_strcmp(info->file, "??")) {
- InternalFree(info->file);
- info->file = 0;
- }
- }
- if (frame_id == 0) {
- // Make sure we return at least one frame.
- AddressInfo *info = &frames[0];
- info->Clear();
- info->FillAddressAndModuleInfo(addr, module_name, module_offset);
- frame_id = 1;
- }
- return frame_id;
- }
-
- bool SymbolizeData(uptr addr, DataInfo *info) {
- BlockingMutexLock l(&mu_);
- LoadedModule *module = FindModuleForAddress(addr);
- if (module == 0)
- return false;
- const char *module_name = module->full_name();
- uptr module_offset = addr - module->base_address();
- info->Clear();
- info->module = internal_strdup(module_name);
- info->module_offset = module_offset;
- // First, try to use libbacktrace symbolizer (if it's available).
- if (libbacktrace_symbolizer_ != 0) {
- mu_.CheckLocked();
- if (libbacktrace_symbolizer_->SymbolizeData(addr, info))
- return true;
- }
- const char *str = SendCommand(true, module_name, module_offset);
- if (str == 0)
- return true;
- str = ExtractToken(str, "\n", &info->name);
- str = ExtractUptr(str, " ", &info->start);
- str = ExtractUptr(str, "\n", &info->size);
- info->start += module->base_address();
- return true;
- }
-
- bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
- uptr *module_address) {
- BlockingMutexLock l(&mu_);
- return FindModuleNameAndOffsetForAddress(pc, module_name, module_address);
- }
-
- bool CanReturnFileLineInfo() {
- return internal_symbolizer_ != 0 || external_symbolizer_ != 0 ||
- libbacktrace_symbolizer_ != 0;
- }
+const char *Symbolizer::PlatformDemangle(const char *name) {
+ return DemangleCXXABI(name);
+}
- void Flush() {
- BlockingMutexLock l(&mu_);
- if (internal_symbolizer_ != 0) {
- SymbolizerScope sym_scope(this);
- internal_symbolizer_->Flush();
+void Symbolizer::PlatformPrepareForSandboxing() {}
+
+static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
+ const char *path = common_flags()->external_symbolizer_path;
+ const char *binary_name = path ? StripModuleName(path) : "";
+ if (path && path[0] == '\0') {
+ VReport(2, "External symbolizer is explicitly disabled.\n");
+ return nullptr;
+ } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) {
+ VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path);
+ return new(*allocator) LLVMSymbolizer(path, allocator);
+ } else if (!internal_strcmp(binary_name, "atos")) {
+#if SANITIZER_MAC
+ VReport(2, "Using atos at user-specified path: %s\n", path);
+ return new(*allocator) AtosSymbolizer(path, allocator);
+#else // SANITIZER_MAC
+ Report("ERROR: Using `atos` is only supported on Darwin.\n");
+ Die();
+#endif // SANITIZER_MAC
+ } else if (!internal_strcmp(binary_name, "addr2line")) {
+ VReport(2, "Using addr2line at user-specified path: %s\n", path);
+ return new(*allocator) Addr2LinePool(path, allocator);
+ } else if (path) {
+ Report("ERROR: External symbolizer path is set to '%s' which isn't "
+ "a known symbolizer. Please set the path to the llvm-symbolizer "
+ "binary or other known tool.\n", path);
+ Die();
+ }
+
+ // Otherwise symbolizer program is unknown, let's search $PATH
+ CHECK(path == nullptr);
+ if (const char *found_path = FindPathToBinary("llvm-symbolizer")) {
+ VReport(2, "Using llvm-symbolizer found at: %s\n", found_path);
+ return new(*allocator) LLVMSymbolizer(found_path, allocator);
+ }
+#if SANITIZER_MAC
+ if (const char *found_path = FindPathToBinary("atos")) {
+ VReport(2, "Using atos found at: %s\n", found_path);
+ return new(*allocator) AtosSymbolizer(found_path, allocator);
+ }
+#endif // SANITIZER_MAC
+ if (common_flags()->allow_addr2line) {
+ if (const char *found_path = FindPathToBinary("addr2line")) {
+ VReport(2, "Using addr2line found at: %s\n", found_path);
+ return new(*allocator) Addr2LinePool(found_path, allocator);
}
}
+ return nullptr;
+}
- const char *Demangle(const char *name) {
- BlockingMutexLock l(&mu_);
- // Run hooks even if we don't use internal symbolizer, as cxxabi
- // demangle may call system functions.
- SymbolizerScope sym_scope(this);
- // Try to use libbacktrace demangler (if available).
- if (libbacktrace_symbolizer_ != 0) {
- if (const char *demangled = libbacktrace_symbolizer_->Demangle(name))
- return demangled;
- }
- if (internal_symbolizer_ != 0)
- return internal_symbolizer_->Demangle(name);
- return DemangleCXXABI(name);
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+ LowLevelAllocator *allocator) {
+ if (!common_flags()->symbolize) {
+ VReport(2, "Symbolizer is disabled.\n");
+ return;
}
-
- void PrepareForSandboxing() {
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
- BlockingMutexLock l(&mu_);
- // Cache /proc/self/exe on Linux.
- CacheBinaryName();
-#endif
+ if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
+ VReport(2, "Using internal symbolizer.\n");
+ list->push_back(tool);
+ return;
}
-
- private:
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- mu_.CheckLocked();
- // First, try to use internal symbolizer.
- if (internal_symbolizer_) {
- SymbolizerScope sym_scope(this);
- return internal_symbolizer_->SendCommand(is_data, module_name,
- module_offset);
- }
- // Otherwise, fall back to external symbolizer.
- if (external_symbolizer_) {
- SymbolizerScope sym_scope(this);
- return external_symbolizer_->SendCommand(is_data, module_name,
- module_offset);
- }
- return 0;
+ if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
+ VReport(2, "Using libbacktrace symbolizer.\n");
+ list->push_back(tool);
+ return;
}
- LoadedModule *FindModuleForAddress(uptr address) {
- mu_.CheckLocked();
- bool modules_were_reloaded = false;
- if (modules_ == 0 || !modules_fresh_) {
- modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate(
- kMaxNumberOfModuleContexts * sizeof(LoadedModule)));
- CHECK(modules_);
- n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts,
- /* filter */ 0);
- CHECK_GT(n_modules_, 0);
- CHECK_LT(n_modules_, kMaxNumberOfModuleContexts);
- modules_fresh_ = true;
- modules_were_reloaded = true;
- }
- for (uptr i = 0; i < n_modules_; i++) {
- if (modules_[i].containsAddress(address)) {
- return &modules_[i];
- }
- }
- // Reload the modules and look up again, if we haven't tried it yet.
- if (!modules_were_reloaded) {
- // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.
- // It's too aggressive to reload the list of modules each time we fail
- // to find a module for a given address.
- modules_fresh_ = false;
- return FindModuleForAddress(address);
- }
- return 0;
+ if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
+ list->push_back(tool);
+ } else {
+ VReport(2, "No internal or external symbolizer found.\n");
}
- bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,
- uptr *module_offset) {
- mu_.CheckLocked();
- LoadedModule *module = FindModuleForAddress(address);
- if (module == 0)
- return false;
- *module_name = module->full_name();
- *module_offset = address - module->base_address();
- return true;
- }
-
- // 16K loaded modules should be enough for everyone.
- static const uptr kMaxNumberOfModuleContexts = 1 << 14;
- LoadedModule *modules_; // Array of module descriptions is leaked.
- uptr n_modules_;
- // If stale, need to reload the modules before looking up addresses.
- bool modules_fresh_;
- BlockingMutex mu_;
-
- ExternalSymbolizerInterface *external_symbolizer_; // Leaked.
- InternalSymbolizer *const internal_symbolizer_; // Leaked.
- LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked.
-};
+#if SANITIZER_MAC
+ VReport(2, "Using dladdr symbolizer.\n");
+ list->push_back(new(*allocator) DlAddrSymbolizer());
+#endif // SANITIZER_MAC
+}
Symbolizer *Symbolizer::PlatformInit() {
- if (!common_flags()->symbolize) {
- return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0);
- }
- InternalSymbolizer* internal_symbolizer =
- InternalSymbolizer::get(&symbolizer_allocator_);
- ExternalSymbolizerInterface *external_symbolizer = 0;
- LibbacktraceSymbolizer *libbacktrace_symbolizer = 0;
-
- if (!internal_symbolizer) {
- libbacktrace_symbolizer =
- LibbacktraceSymbolizer::get(&symbolizer_allocator_);
- if (!libbacktrace_symbolizer) {
- const char *path_to_external = common_flags()->external_symbolizer_path;
- if (path_to_external && path_to_external[0] == '\0') {
- // External symbolizer is explicitly disabled. Do nothing.
- } else {
- // Find path to llvm-symbolizer if it's not provided.
- if (!path_to_external)
- path_to_external = FindPathToBinary("llvm-symbolizer");
- if (path_to_external) {
- external_symbolizer = new(symbolizer_allocator_)
- LLVMSymbolizerProcess(path_to_external);
- } else if (common_flags()->allow_addr2line) {
- // If llvm-symbolizer is not found, try to use addr2line.
- if (const char *addr2line_path = FindPathToBinary("addr2line")) {
- external_symbolizer = new(symbolizer_allocator_)
- Addr2LinePool(addr2line_path, &symbolizer_allocator_);
- }
- }
- }
- }
- }
-
- return new(symbolizer_allocator_) POSIXSymbolizer(
- external_symbolizer, internal_symbolizer, libbacktrace_symbolizer);
+ IntrusiveList<SymbolizerTool> list;
+ list.clear();
+ ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+ return new(symbolizer_allocator_) Symbolizer(list);
}
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc
index a1ed4e9a7b7..bb59f803285 100644
--- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc
@@ -16,108 +16,264 @@
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
namespace __sanitizer {
-class WinSymbolizer : public Symbolizer {
+namespace {
+
+class WinSymbolizerTool : public SymbolizerTool {
public:
- WinSymbolizer() : initialized_(false) {}
-
- uptr SymbolizePC(uptr addr, AddressInfo *frames, uptr max_frames) {
- if (max_frames == 0)
- return 0;
-
- BlockingMutexLock l(&dbghelp_mu_);
- if (!initialized_) {
- if (!TrySymInitialize()) {
- // OK, maybe the client app has called SymInitialize already.
- // That's a bit unfortunate for us as all the DbgHelp functions are
- // single-threaded and we can't coordinate with the app.
- // FIXME: Can we stop the other threads at this point?
- // Anyways, we have to reconfigure stuff to make sure that SymInitialize
- // has all the appropriate options set.
- // Cross our fingers and reinitialize DbgHelp.
- Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
- Report("*** Most likely this means that the app is already ***\n");
- Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
- Report("*** Due to technical reasons, symbolization might crash ***\n");
- Report("*** or produce wrong results. ***\n");
- SymCleanup(GetCurrentProcess());
- TrySymInitialize();
- }
- initialized_ = true;
- }
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ return false;
+ }
+ const char *Demangle(const char *name) override;
+};
- // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
- char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
- PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
- symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
- symbol->MaxNameLen = MAX_SYM_NAME;
- DWORD64 offset = 0;
- BOOL got_objname = SymFromAddr(GetCurrentProcess(),
- (DWORD64)addr, &offset, symbol);
- if (!got_objname)
- return 0;
-
- DWORD unused;
- IMAGEHLP_LINE64 line_info;
- line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
- BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
- &unused, &line_info);
- AddressInfo *info = &frames[0];
- info->Clear();
- info->function = internal_strdup(symbol->Name);
- info->function_offset = (uptr)offset;
- if (got_fileline) {
- info->file = internal_strdup(line_info.FileName);
- info->line = line_info.LineNumber;
- }
+bool is_dbghelp_initialized = false;
- IMAGEHLP_MODULE64 mod_info;
- internal_memset(&mod_info, 0, sizeof(mod_info));
- mod_info.SizeOfStruct = sizeof(mod_info);
- if (SymGetModuleInfo64(GetCurrentProcess(), addr, &mod_info))
- info->FillAddressAndModuleInfo(addr, mod_info.ImageName,
- addr - (uptr)mod_info.BaseOfImage);
- return 1;
- }
+bool TrySymInitialize() {
+ SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
+ return SymInitialize(GetCurrentProcess(), 0, TRUE);
+ // FIXME: We don't call SymCleanup() on exit yet - should we?
+}
- bool CanReturnFileLineInfo() {
- return true;
+// Initializes DbgHelp library, if it's not yet initialized. Calls to this
+// function should be synchronized with respect to other calls to DbgHelp API
+// (e.g. from WinSymbolizerTool).
+void InitializeDbgHelpIfNeeded() {
+ if (is_dbghelp_initialized)
+ return;
+ if (!TrySymInitialize()) {
+ // OK, maybe the client app has called SymInitialize already.
+ // That's a bit unfortunate for us as all the DbgHelp functions are
+ // single-threaded and we can't coordinate with the app.
+ // FIXME: Can we stop the other threads at this point?
+ // Anyways, we have to reconfigure stuff to make sure that SymInitialize
+ // has all the appropriate options set.
+ // Cross our fingers and reinitialize DbgHelp.
+ Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
+ Report("*** Most likely this means that the app is already ***\n");
+ Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
+ Report("*** Due to technical reasons, symbolization might crash ***\n");
+ Report("*** or produce wrong results. ***\n");
+ SymCleanup(GetCurrentProcess());
+ TrySymInitialize();
}
+ is_dbghelp_initialized = true;
- const char *Demangle(const char *name) {
- CHECK(initialized_);
- static char demangle_buffer[1000];
- if (name[0] == '\01' &&
- UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
- UNDNAME_NAME_ONLY))
- return demangle_buffer;
- else
- return name;
+ // When an executable is run from a location different from the one where it
+ // was originally built, we may not see the nearby PDB files.
+ // To work around this, let's append the directory of the main module
+ // to the symbol search path. All the failures below are not fatal.
+ const size_t kSymPathSize = 2048;
+ static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
+ if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
+ Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
+ return;
+ }
+ size_t sz = wcslen(path_buffer);
+ if (sz) {
+ CHECK_EQ(0, wcscat_s(path_buffer, L";"));
+ sz++;
+ }
+ DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
+ if (res == 0 || res == MAX_PATH) {
+ Report("*** WARNING: Failed to getting the EXE directory ***\n");
+ return;
}
+ // Write the zero character in place of the last backslash to get the
+ // directory of the main module at the end of path_buffer.
+ wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
+ CHECK_NE(last_bslash, 0);
+ *last_bslash = L'\0';
+ if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
+ Report("*** WARNING: Failed to SymSetSearchPathW\n");
+ return;
+ }
+}
- // FIXME: Implement GetModuleNameAndOffsetForPC().
+} // namespace
- private:
- bool TrySymInitialize() {
- SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
- return SymInitialize(GetCurrentProcess(), 0, TRUE);
- // FIXME: We don't call SymCleanup() on exit yet - should we?
+bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
+ InitializeDbgHelpIfNeeded();
+
+ // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
+ char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
+ PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = MAX_SYM_NAME;
+ DWORD64 offset = 0;
+ BOOL got_objname = SymFromAddr(GetCurrentProcess(),
+ (DWORD64)addr, &offset, symbol);
+ if (!got_objname)
+ return false;
+
+ DWORD unused;
+ IMAGEHLP_LINE64 line_info;
+ line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
+ &unused, &line_info);
+ frame->info.function = internal_strdup(symbol->Name);
+ frame->info.function_offset = (uptr)offset;
+ if (got_fileline) {
+ frame->info.file = internal_strdup(line_info.FileName);
+ frame->info.line = line_info.LineNumber;
}
+ // Only consider this a successful symbolization attempt if we got file info.
+ // Otherwise, try llvm-symbolizer.
+ return got_fileline;
+}
+
+const char *WinSymbolizerTool::Demangle(const char *name) {
+ CHECK(is_dbghelp_initialized);
+ static char demangle_buffer[1000];
+ if (name[0] == '\01' &&
+ UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
+ UNDNAME_NAME_ONLY))
+ return demangle_buffer;
+ else
+ return name;
+}
- // All DbgHelp functions are single threaded, so we should use a mutex to
- // serialize accesses.
- BlockingMutex dbghelp_mu_;
- bool initialized_;
+const char *Symbolizer::PlatformDemangle(const char *name) {
+ return name;
+}
+
+void Symbolizer::PlatformPrepareForSandboxing() {
+ // Do nothing.
+}
+
+namespace {
+struct ScopedHandle {
+ ScopedHandle() : h_(nullptr) {}
+ explicit ScopedHandle(HANDLE h) : h_(h) {}
+ ~ScopedHandle() {
+ if (h_)
+ ::CloseHandle(h_);
+ }
+ HANDLE get() { return h_; }
+ HANDLE *receive() { return &h_; }
+ HANDLE release() {
+ HANDLE h = h_;
+ h_ = nullptr;
+ return h;
+ }
+ HANDLE h_;
};
+} // namespace
+
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+ // Create inherited pipes for stdin and stdout.
+ ScopedHandle stdin_read, stdin_write;
+ ScopedHandle stdout_read, stdout_write;
+ SECURITY_ATTRIBUTES attrs;
+ attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ attrs.bInheritHandle = TRUE;
+ attrs.lpSecurityDescriptor = nullptr;
+ if (!::CreatePipe(stdin_read.receive(), stdin_write.receive(), &attrs, 0) ||
+ !::CreatePipe(stdout_read.receive(), stdout_write.receive(), &attrs, 0)) {
+ VReport(2, "WARNING: %s CreatePipe failed (error code: %d)\n",
+ SanitizerToolName, path_, GetLastError());
+ return false;
+ }
+
+ // Don't inherit the writing end of stdin or the reading end of stdout.
+ if (!SetHandleInformation(stdin_write.get(), HANDLE_FLAG_INHERIT, 0) ||
+ !SetHandleInformation(stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) {
+ VReport(2, "WARNING: %s SetHandleInformation failed (error code: %d)\n",
+ SanitizerToolName, path_, GetLastError());
+ return false;
+ }
+
+ // Compute the command line. Wrap double quotes around everything.
+ const char *argv[kArgVMax];
+ GetArgV(path_, argv);
+ InternalScopedString command_line(kMaxPathLength * 3);
+ for (int i = 0; argv[i]; i++) {
+ const char *arg = argv[i];
+ int arglen = internal_strlen(arg);
+ // Check that tool command lines are simple and that complete escaping is
+ // unnecessary.
+ CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported");
+ CHECK(!internal_strstr(arg, "\\\\") &&
+ "double backslashes in args unsupported");
+ CHECK(arglen > 0 && arg[arglen - 1] != '\\' &&
+ "args ending in backslash and empty args unsupported");
+ command_line.append("\"%s\" ", arg);
+ }
+ VReport(3, "Launching symbolizer command: %s\n", command_line.data());
+
+ // Launch llvm-symbolizer with stdin and stdout redirected.
+ STARTUPINFOA si;
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ si.hStdInput = stdin_read.get();
+ si.hStdOutput = stdout_write.get();
+ PROCESS_INFORMATION pi;
+ memset(&pi, 0, sizeof(pi));
+ if (!CreateProcessA(path_, // Executable
+ command_line.data(), // Command line
+ nullptr, // Process handle not inheritable
+ nullptr, // Thread handle not inheritable
+ TRUE, // Set handle inheritance to TRUE
+ 0, // Creation flags
+ nullptr, // Use parent's environment block
+ nullptr, // Use parent's starting directory
+ &si, &pi)) {
+ VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n",
+ SanitizerToolName, path_, GetLastError());
+ return false;
+ }
+
+ // Process creation succeeded, so transfer handle ownership into the fields.
+ input_fd_ = stdout_read.release();
+ output_fd_ = stdin_write.release();
+
+ // The llvm-symbolizer process is responsible for quitting itself when the
+ // stdin pipe is closed, so we don't need these handles. Close them to prevent
+ // leaks. If we ever want to try to kill the symbolizer process from the
+ // parent, we'll want to hang on to these handles.
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return true;
+}
+
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+ LowLevelAllocator *allocator) {
+ if (!common_flags()->symbolize) {
+ VReport(2, "Symbolizer is disabled.\n");
+ return;
+ }
+
+ // Add llvm-symbolizer in case the binary has dwarf.
+ const char *user_path = common_flags()->external_symbolizer_path;
+ const char *path =
+ user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe");
+ if (path) {
+ VReport(2, "Using llvm-symbolizer at %spath: %s\n",
+ user_path ? "user-specified " : "", path);
+ list->push_back(new(*allocator) LLVMSymbolizer(path, allocator));
+ } else {
+ if (user_path && user_path[0] == '\0') {
+ VReport(2, "External symbolizer is explicitly disabled.\n");
+ } else {
+ VReport(2, "External symbolizer is not present.\n");
+ }
+ }
+
+ // Add the dbghelp based symbolizer.
+ list->push_back(new(*allocator) WinSymbolizerTool());
+}
Symbolizer *Symbolizer::PlatformInit() {
- static bool called_once = false;
- CHECK(!called_once && "Shouldn't create more than one symbolizer");
- called_once = true;
- return new(symbolizer_allocator_) WinSymbolizer();
+ IntrusiveList<SymbolizerTool> list;
+ list.clear();
+ ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+
+ return new(symbolizer_allocator_) Symbolizer(list);
}
} // namespace __sanitizer
diff --git a/libsanitizer/sanitizer_common/sanitizer_syscall_generic.inc b/libsanitizer/sanitizer_common/sanitizer_syscall_generic.inc
index 7667b753a8b..3bc50d972a4 100644
--- a/libsanitizer/sanitizer_common/sanitizer_syscall_generic.inc
+++ b/libsanitizer/sanitizer_common/sanitizer_syscall_generic.inc
@@ -9,13 +9,13 @@
//
//===----------------------------------------------------------------------===//
-#if SANITIZER_FREEBSD
+#if SANITIZER_FREEBSD || SANITIZER_MAC
# define SYSCALL(name) SYS_ ## name
#else
# define SYSCALL(name) __NR_ ## name
#endif
-#if SANITIZER_FREEBSD && defined(__x86_64__)
+#if (SANITIZER_FREEBSD || SANITIZER_MAC) && defined(__x86_64__)
# define internal_syscall __syscall
# else
# define internal_syscall syscall
diff --git a/libsanitizer/sanitizer_common/sanitizer_syscall_linux_aarch64.inc b/libsanitizer/sanitizer_common/sanitizer_syscall_linux_aarch64.inc
new file mode 100644
index 00000000000..64d6322651a
--- /dev/null
+++ b/libsanitizer/sanitizer_common/sanitizer_syscall_linux_aarch64.inc
@@ -0,0 +1,136 @@
+//===-- sanitizer_syscall_linux_aarch64.inc --------------------*- C++ -*-===//
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementations of internal_syscall and internal_iserror for Linux/aarch64.
+//
+//===----------------------------------------------------------------------===//
+
+#define SYSCALL(name) __NR_ ## name
+
+static uptr __internal_syscall(u64 nr) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0");
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall0(n) \
+ (__internal_syscall)(n)
+
+static uptr __internal_syscall(u64 nr, u64 arg1) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall1(n, a1) \
+ (__internal_syscall)(n, (u64)(a1))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall2(n, a1, a2) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ register u64 x2 asm("x2") = arg3;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1), "r"(x2)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall3(n, a1, a2, a3) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
+ u64 arg4) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ register u64 x2 asm("x2") = arg3;
+ register u64 x3 asm("x3") = arg4;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall4(n, a1, a2, a3, a4) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
+ u64 arg4, long arg5) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ register u64 x2 asm("x2") = arg3;
+ register u64 x3 asm("x3") = arg4;
+ register u64 x4 asm("x4") = arg5;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall5(n, a1, a2, a3, a4, a5) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
+ (u64)(a5))
+
+static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
+ u64 arg4, long arg5, long arg6) {
+ register u64 x8 asm("x8") = nr;
+ register u64 x0 asm("x0") = arg1;
+ register u64 x1 asm("x1") = arg2;
+ register u64 x2 asm("x2") = arg3;
+ register u64 x3 asm("x3") = arg4;
+ register u64 x4 asm("x4") = arg5;
+ register u64 x5 asm("x5") = arg6;
+ asm volatile("svc 0"
+ : "=r"(x0)
+ : "r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5)
+ : "memory", "cc");
+ return x0;
+}
+#define __internal_syscall6(n, a1, a2, a3, a4, a5, a6) \
+ (__internal_syscall)(n, (u64)(a1), (long)(a2), (long)(a3), (long)(a4), \
+ (u64)(a5), (long)(a6))
+
+#define __SYSCALL_NARGS_X(a1, a2, a3, a4, a5, a6, a7, a8, n, ...) n
+#define __SYSCALL_NARGS(...) \
+ __SYSCALL_NARGS_X(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0, )
+#define __SYSCALL_CONCAT_X(a, b) a##b
+#define __SYSCALL_CONCAT(a, b) __SYSCALL_CONCAT_X(a, b)
+#define __SYSCALL_DISP(b, ...) \
+ __SYSCALL_CONCAT(b, __SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
+
+#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
+
+// Helper function used to avoid cobbler errno.
+bool internal_iserror(uptr retval, int *rverrno) {
+ if (retval >= (uptr)-4095) {
+ if (rverrno)
+ *rverrno = -retval;
+ return true;
+ }
+ return false;
+}
diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_registry.h b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h
index d5a741bbfc4..8d5f1ea3988 100644
--- a/libsanitizer/sanitizer_common/sanitizer_thread_registry.h
+++ b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h
@@ -77,7 +77,8 @@ class ThreadRegistry {
ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
u32 thread_quarantine_size, u32 max_reuse = 0);
- void GetNumberOfThreads(uptr *total = 0, uptr *running = 0, uptr *alive = 0);
+ void GetNumberOfThreads(uptr *total = nullptr, uptr *running = nullptr,
+ uptr *alive = nullptr);
uptr GetMaxAliveThreads();
void Lock() { mtx_.Lock(); }
@@ -140,6 +141,6 @@ class ThreadRegistry {
typedef GenericScopedLock<ThreadRegistry> ThreadRegistryLock;
-} // namespace __sanitizer
+} // namespace __sanitizer
-#endif // SANITIZER_THREAD_REGISTRY_H
+#endif // SANITIZER_THREAD_REGISTRY_H
diff --git a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cc b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cc
index af828045b59..e05c7be0799 100644
--- a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cc
@@ -76,7 +76,8 @@ void DTLS_Destroy() {
DTLS_Deallocate(dtls.dtv, s);
}
-DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res) {
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
+ uptr static_tls_begin, uptr static_tls_end) {
if (!common_flags()->intercept_tls_get_addr) return 0;
TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void);
uptr dso_id = arg->dso_id;
@@ -93,6 +94,11 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res) {
tls_size = dtls.last_memalign_size;
VPrintf(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n",
tls_beg, tls_size);
+ } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
+ // This is the static TLS block which was initialized / unpoisoned at thread
+ // creation.
+ VPrintf(2, "__tls_get_addr: static tls: %p\n", tls_beg);
+ tls_size = 0;
} else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) {
// We may want to check gnu_get_libc_version().
Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1;
diff --git a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h
index 8dc629f630e..e4f8c0c535e 100644
--- a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h
+++ b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h
@@ -48,7 +48,8 @@ struct DTLS {
// Returns pointer and size of a linker-allocated TLS block.
// Each block is returned exactly once.
-DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res);
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, uptr static_tls_begin,
+ uptr static_tls_end);
void DTLS_on_libc_memalign(void *ptr, uptr size);
DTLS *DTLS_Get();
void DTLS_Destroy(); // Make sure to call this before the thread is destroyed.
diff --git a/libsanitizer/sanitizer_common/sanitizer_unwind_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_unwind_linux_libcdep.cc
index b2ca931e9d9..408c21c913b 100644
--- a/libsanitizer/sanitizer_common/sanitizer_unwind_posix_libcdep.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_unwind_linux_libcdep.cc
@@ -1,4 +1,4 @@
-//===-- sanitizer_unwind_posix.cc ----------------------------------------===//
+//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
@@ -6,11 +6,11 @@
//===----------------------------------------------------------------------===//
//
// This file contains the unwind.h-based (aka "slow") stack unwinding routines
-// available to the tools on Linux, Android, FreeBSD and OS X.
+// available to the tools on Linux, Android, and FreeBSD.
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
-#if SANITIZER_POSIX
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_common.h"
#include "sanitizer_stacktrace.h"
@@ -80,7 +80,7 @@ void SanitizerInitializeUnwinder() {
#endif
uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
-#ifdef __arm__
+#if defined(__arm__) && !SANITIZER_MAC
uptr val;
_Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
15 /* r15 = PC */, _UVRSD_UINT32, &val);
@@ -94,7 +94,7 @@ uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
struct UnwindTraceArg {
BufferedStackTrace *stack;
- uptr max_depth;
+ u32 max_depth;
};
_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
@@ -106,14 +106,19 @@ _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
return UNWIND_CONTINUE;
}
-void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
+void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 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_buffer[0] belongs to the current function so we always pop it.
+ // trace_buffer[0] belongs to the current function so we always pop it,
+ // unless there is only 1 frame in the stack trace (1 frame is always better
+ // than 0!).
+ // 1-frame stacks don't normally happen, but this depends on the actual
+ // unwinder implementation (libgcc, libunwind, etc) which is outside of our
+ // control.
if (to_pop == 0 && size > 1)
to_pop = 1;
PopStackFrames(to_pop);
@@ -121,7 +126,7 @@ void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
}
void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
- uptr max_depth) {
+ u32 max_depth) {
CHECK_GE(max_depth, 2);
if (!unwind_backtrace_signal_arch) {
SlowUnwindStack(pc, max_depth);
@@ -148,4 +153,4 @@ void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
} // namespace __sanitizer
-#endif // SANITIZER_POSIX
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_win.cc
index fbccef19cb1..bb84249b243 100644
--- a/libsanitizer/sanitizer_common/sanitizer_win.cc
+++ b/libsanitizer/sanitizer_common/sanitizer_win.cc
@@ -18,6 +18,7 @@
#include <windows.h>
#include <dbghelp.h>
#include <io.h>
+#include <psapi.h>
#include <stdlib.h>
#include "sanitizer_common.h"
@@ -32,7 +33,9 @@ namespace __sanitizer {
// --------------------- sanitizer_common.h
uptr GetPageSize() {
- return 1U << 14; // FIXME: is this configurable?
+ // FIXME: there is an API for getting the system page size (GetSystemInfo or
+ // GetNativeSystemInfo), but if we use it here we get test failures elsewhere.
+ return 1U << 14;
}
uptr GetMmapGranularity() {
@@ -46,7 +49,7 @@ uptr GetMaxVirtualAddress() {
}
bool FileExists(const char *filename) {
- UNIMPLEMENTED();
+ return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES;
}
uptr internal_getpid() {
@@ -80,16 +83,15 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
void *MmapOrDie(uptr size, const char *mem_type) {
void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
- if (rv == 0) {
- Report("ERROR: %s failed to "
- "allocate 0x%zx (%zd) bytes of %s (error code: %d)\n",
- SanitizerToolName, size, size, mem_type, GetLastError());
- CHECK("unable to mmap" && 0);
- }
+ if (rv == 0)
+ ReportMmapFailureAndDie(size, mem_type, GetLastError());
return rv;
}
void UnmapOrDie(void *addr, uptr size) {
+ if (!size || !addr)
+ return;
+
if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
Report("ERROR: %s failed to "
"deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n",
@@ -98,9 +100,10 @@ void UnmapOrDie(void *addr, uptr size) {
}
}
-void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
// FIXME: is this really "NoReserve"? On Win32 this does not matter much,
// but on Win64 it does.
+ (void)name; // unsupported
void *p = VirtualAlloc((LPVOID)fixed_addr, size,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (p == 0)
@@ -119,26 +122,49 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
return MmapOrDie(size, mem_type);
}
-void *Mprotect(uptr fixed_addr, uptr size) {
- return VirtualAlloc((LPVOID)fixed_addr, size,
- MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
+ (void)name; // unsupported
+ void *res = VirtualAlloc((LPVOID)fixed_addr, size,
+ MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
+ if (res == 0)
+ Report("WARNING: %s failed to "
+ "mprotect %p (%zd) bytes at %p (error code: %d)\n",
+ SanitizerToolName, size, size, fixed_addr, GetLastError());
+ return res;
+}
+
+bool MprotectNoAccess(uptr addr, uptr size) {
+ DWORD old_protection;
+ return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection);
}
+
void FlushUnneededShadowMemory(uptr addr, uptr size) {
// This is almost useless on 32-bits.
- // FIXME: add madvice-analog when we move to 64-bits.
+ // FIXME: add madvise-analog when we move to 64-bits.
+}
+
+void NoHugePagesInRegion(uptr addr, uptr size) {
+ // FIXME: probably similar to FlushUnneededShadowMemory.
+}
+
+void DontDumpShadowMemory(uptr addr, uptr length) {
+ // This is almost useless on 32-bits.
+ // FIXME: add madvise-analog when we move to 64-bits.
}
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
- // FIXME: shall we do anything here on Windows?
- return true;
+ MEMORY_BASIC_INFORMATION mbi;
+ CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi)));
+ return mbi.Protect == PAGE_NOACCESS &&
+ (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end;
}
void *MapFileToMemory(const char *file_name, uptr *buff_size) {
UNIMPLEMENTED();
}
-void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) {
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
UNIMPLEMENTED();
}
@@ -185,9 +211,50 @@ u32 GetUid() {
UNIMPLEMENTED();
}
+namespace {
+struct ModuleInfo {
+ const char *filepath;
+ uptr base_address;
+ uptr end_address;
+};
+
+int CompareModulesBase(const void *pl, const void *pr) {
+ const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr;
+ if (l->base_address < r->base_address)
+ return -1;
+ return l->base_address > r->base_address;
+}
+} // namespace
+
+#ifndef SANITIZER_GO
void DumpProcessMap() {
- UNIMPLEMENTED();
+ Report("Dumping process modules:\n");
+ InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules);
+ uptr num_modules =
+ GetListOfModules(modules.data(), kMaxNumberOfModules, nullptr);
+
+ InternalScopedBuffer<ModuleInfo> module_infos(num_modules);
+ for (size_t i = 0; i < num_modules; ++i) {
+ module_infos[i].filepath = modules[i].full_name();
+ module_infos[i].base_address = modules[i].base_address();
+ module_infos[i].end_address = modules[i].ranges().next()->end;
+ }
+ qsort(module_infos.data(), num_modules, sizeof(ModuleInfo),
+ CompareModulesBase);
+
+ for (size_t i = 0; i < num_modules; ++i) {
+ const ModuleInfo &mi = module_infos[i];
+ if (mi.end_address != 0) {
+ Printf("\t%p-%p %s\n", mi.base_address, mi.end_address,
+ mi.filepath[0] ? mi.filepath : "[no name]");
+ } else if (mi.filepath[0]) {
+ Printf("\t??\?-??? %s\n", mi.filepath);
+ } else {
+ Printf("\t???\n");
+ }
+ }
}
+#endif
void DisableCoreDumperIfNecessary() {
// Do nothing.
@@ -198,8 +265,9 @@ void ReExec() {
}
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
- (void)args;
- // Nothing here for now.
+#if !SANITIZER_GO
+ CovPrepareForSandboxing(args);
+#endif
}
bool StackSizeIsUnlimited() {
@@ -218,9 +286,12 @@ void SetAddressSpaceUnlimited() {
UNIMPLEMENTED();
}
-char *FindPathToBinary(const char *name) {
- // Nothing here for now.
- return 0;
+bool IsPathSeparator(const char c) {
+ return c == '\\' || c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+ UNIMPLEMENTED();
}
void SleepForSeconds(int seconds) {
@@ -236,123 +307,234 @@ u64 NanoTime() {
}
void Abort() {
- abort();
- internal__exit(-1); // abort is not NORETURN on Windows.
+ if (::IsDebuggerPresent())
+ __debugbreak();
+ internal__exit(3);
+}
+
+// Read the file to extract the ImageBase field from the PE header. If ASLR is
+// disabled and this virtual address is available, the loader will typically
+// load the image at this address. Therefore, we call it the preferred base. Any
+// addresses in the DWARF typically assume that the object has been loaded at
+// this address.
+static uptr GetPreferredBase(const char *modname) {
+ fd_t fd = OpenFile(modname, RdOnly, nullptr);
+ if (fd == kInvalidFd)
+ return 0;
+ FileCloser closer(fd);
+
+ // Read just the DOS header.
+ IMAGE_DOS_HEADER dos_header;
+ uptr bytes_read;
+ if (!ReadFromFile(fd, &dos_header, sizeof(dos_header), &bytes_read) ||
+ bytes_read != sizeof(dos_header))
+ return 0;
+
+ // The file should start with the right signature.
+ if (dos_header.e_magic != IMAGE_DOS_SIGNATURE)
+ return 0;
+
+ // The layout at e_lfanew is:
+ // "PE\0\0"
+ // IMAGE_FILE_HEADER
+ // IMAGE_OPTIONAL_HEADER
+ // Seek to e_lfanew and read all that data.
+ char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)];
+ if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) ==
+ INVALID_SET_FILE_POINTER)
+ return 0;
+ if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) ||
+ bytes_read != sizeof(buf))
+ return 0;
+
+ // Check for "PE\0\0" before the PE header.
+ char *pe_sig = &buf[0];
+ if (internal_memcmp(pe_sig, "PE\0\0", 4) != 0)
+ return 0;
+
+ // Skip over IMAGE_FILE_HEADER. We could do more validation here if we wanted.
+ IMAGE_OPTIONAL_HEADER *pe_header =
+ (IMAGE_OPTIONAL_HEADER *)(pe_sig + 4 + sizeof(IMAGE_FILE_HEADER));
+
+ // Check for more magic in the PE header.
+ if (pe_header->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+ return 0;
+
+ // Finally, return the ImageBase.
+ return (uptr)pe_header->ImageBase;
}
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter) {
- UNIMPLEMENTED();
+ HANDLE cur_process = GetCurrentProcess();
+
+ // Query the list of modules. Start by assuming there are no more than 256
+ // modules and retry if that's not sufficient.
+ HMODULE *hmodules = 0;
+ uptr modules_buffer_size = sizeof(HMODULE) * 256;
+ DWORD bytes_required;
+ while (!hmodules) {
+ hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__);
+ CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size,
+ &bytes_required));
+ if (bytes_required > modules_buffer_size) {
+ // Either there turned out to be more than 256 hmodules, or new hmodules
+ // could have loaded since the last try. Retry.
+ UnmapOrDie(hmodules, modules_buffer_size);
+ hmodules = 0;
+ modules_buffer_size = bytes_required;
+ }
+ }
+
+ // |num_modules| is the number of modules actually present,
+ // |count| is the number of modules we return.
+ size_t nun_modules = bytes_required / sizeof(HMODULE),
+ count = 0;
+ for (size_t i = 0; i < nun_modules && count < max_modules; ++i) {
+ HMODULE handle = hmodules[i];
+ MODULEINFO mi;
+ if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi)))
+ continue;
+
+ // Get the UTF-16 path and convert to UTF-8.
+ wchar_t modname_utf16[kMaxPathLength];
+ int modname_utf16_len =
+ GetModuleFileNameW(handle, modname_utf16, kMaxPathLength);
+ if (modname_utf16_len == 0)
+ modname_utf16[0] = '\0';
+ char module_name[kMaxPathLength];
+ int module_name_len =
+ ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1,
+ &module_name[0], kMaxPathLength, NULL, NULL);
+ module_name[module_name_len] = '\0';
+
+ if (filter && !filter(module_name))
+ continue;
+
+ uptr base_address = (uptr)mi.lpBaseOfDll;
+ uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage;
+
+ // Adjust the base address of the module so that we get a VA instead of an
+ // RVA when computing the module offset. This helps llvm-symbolizer find the
+ // right DWARF CU. In the common case that the image is loaded at it's
+ // preferred address, we will now print normal virtual addresses.
+ uptr preferred_base = GetPreferredBase(&module_name[0]);
+ uptr adjusted_base = base_address - preferred_base;
+
+ LoadedModule *cur_module = &modules[count];
+ cur_module->set(module_name, adjusted_base);
+ // We add the whole module as one single address range.
+ cur_module->addAddressRange(base_address, end_address, /*executable*/ true);
+ count++;
+ }
+ UnmapOrDie(hmodules, modules_buffer_size);
+
+ return count;
};
#ifndef SANITIZER_GO
+// We can't use atexit() directly at __asan_init time as the CRT is not fully
+// initialized at this point. Place the functions into a vector and use
+// atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers).
+InternalMmapVectorNoCtor<void (*)(void)> atexit_functions;
+
int Atexit(void (*function)(void)) {
- return atexit(function);
+ atexit_functions.push_back(function);
+ return 0;
}
-#endif
-// ------------------ sanitizer_libc.h
-uptr internal_mmap(void *addr, uptr length, int prot, int flags,
- int fd, u64 offset) {
- UNIMPLEMENTED();
+static int RunAtexit() {
+ int ret = 0;
+ for (uptr i = 0; i < atexit_functions.size(); ++i) {
+ ret |= atexit(atexit_functions[i]);
+ }
+ return ret;
}
-uptr internal_munmap(void *addr, uptr length) {
- UNIMPLEMENTED();
-}
+#pragma section(".CRT$XID", long, read) // NOLINT
+__declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit;
+#endif
-uptr internal_close(fd_t fd) {
- UNIMPLEMENTED();
+// ------------------ sanitizer_libc.h
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) {
+ fd_t res;
+ if (mode == RdOnly) {
+ res = CreateFile(filename, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ } else if (mode == WrOnly) {
+ res = CreateFile(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ } else {
+ UNIMPLEMENTED();
+ }
+ CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd);
+ CHECK(res != kStderrFd || kStderrFd == kInvalidFd);
+ if (res == kInvalidFd && last_error)
+ *last_error = GetLastError();
+ return res;
}
-int internal_isatty(fd_t fd) {
- return _isatty(fd);
+void CloseFile(fd_t fd) {
+ CloseHandle(fd);
}
-uptr internal_open(const char *filename, int flags) {
- UNIMPLEMENTED();
-}
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+ error_t *error_p) {
+ CHECK(fd != kInvalidFd);
-uptr internal_open(const char *filename, int flags, u32 mode) {
- UNIMPLEMENTED();
-}
+ // bytes_read can't be passed directly to ReadFile:
+ // uptr is unsigned long long on 64-bit Windows.
+ unsigned long num_read_long;
-uptr OpenFile(const char *filename, bool write) {
- UNIMPLEMENTED();
+ bool success = ::ReadFile(fd, buff, buff_size, &num_read_long, nullptr);
+ if (!success && error_p)
+ *error_p = GetLastError();
+ if (bytes_read)
+ *bytes_read = num_read_long;
+ return success;
}
-uptr internal_read(fd_t fd, void *buf, uptr count) {
- UNIMPLEMENTED();
+bool SupportsColoredOutput(fd_t fd) {
+ // FIXME: support colored output.
+ return false;
}
-uptr internal_write(fd_t fd, const void *buf, uptr count) {
- if (fd != kStderrFd)
- UNIMPLEMENTED();
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+ error_t *error_p) {
+ CHECK(fd != kInvalidFd);
- static HANDLE output_stream = 0;
- // Abort immediately if we know printing is not possible.
- if (output_stream == INVALID_HANDLE_VALUE)
- return 0;
+ // Handle null optional parameters.
+ error_t dummy_error;
+ error_p = error_p ? error_p : &dummy_error;
+ uptr dummy_bytes_written;
+ bytes_written = bytes_written ? bytes_written : &dummy_bytes_written;
- // If called for the first time, try to use stderr to output stuff,
- // falling back to stdout if anything goes wrong.
- bool fallback_to_stdout = false;
- if (output_stream == 0) {
- output_stream = GetStdHandle(STD_ERROR_HANDLE);
- // We don't distinguish "no such handle" from error.
- if (output_stream == 0)
- output_stream = INVALID_HANDLE_VALUE;
-
- if (output_stream == INVALID_HANDLE_VALUE) {
- // Retry with stdout?
- output_stream = GetStdHandle(STD_OUTPUT_HANDLE);
- if (output_stream == 0)
- output_stream = INVALID_HANDLE_VALUE;
- if (output_stream == INVALID_HANDLE_VALUE)
- return 0;
- } else {
- // Successfully got an stderr handle. However, if WriteFile() fails,
- // we can still try to fallback to stdout.
- fallback_to_stdout = true;
+ // Initialize output parameters in case we fail.
+ *error_p = 0;
+ *bytes_written = 0;
+
+ // Map the conventional Unix fds 1 and 2 to Windows handles. They might be
+ // closed, in which case this will fail.
+ if (fd == kStdoutFd || fd == kStderrFd) {
+ fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
+ if (fd == 0) {
+ *error_p = ERROR_INVALID_HANDLE;
+ return false;
}
}
- DWORD ret;
- if (WriteFile(output_stream, buf, count, &ret, 0))
- return ret;
-
- // Re-try with stdout if using a valid stderr handle fails.
- if (fallback_to_stdout) {
- output_stream = GetStdHandle(STD_OUTPUT_HANDLE);
- if (output_stream == 0)
- output_stream = INVALID_HANDLE_VALUE;
- if (output_stream != INVALID_HANDLE_VALUE)
- return internal_write(fd, buf, count);
+ DWORD bytes_written_32;
+ if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) {
+ *error_p = GetLastError();
+ return false;
+ } else {
+ *bytes_written = bytes_written_32;
+ return true;
}
- return 0;
-}
-
-uptr internal_stat(const char *path, void *buf) {
- UNIMPLEMENTED();
-}
-
-uptr internal_lstat(const char *path, void *buf) {
- UNIMPLEMENTED();
}
-uptr internal_fstat(fd_t fd, void *buf) {
- UNIMPLEMENTED();
-}
-
-uptr internal_filesize(fd_t fd) {
- UNIMPLEMENTED();
-}
-
-uptr internal_dup2(int oldfd, int newfd) {
- UNIMPLEMENTED();
-}
-
-uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
UNIMPLEMENTED();
}
@@ -369,10 +551,13 @@ uptr internal_ftruncate(fd_t fd, uptr size) {
UNIMPLEMENTED();
}
-uptr internal_rename(const char *oldpath, const char *newpath) {
- UNIMPLEMENTED();
+uptr GetRSS() {
+ return 0;
}
+void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
+void internal_join_thread(void *th) { }
+
// ---------------------- BlockingMutex ---------------- {{{1
const uptr LOCK_UNINITIALIZED = 0;
const uptr LOCK_READY = (uptr)-1;
@@ -442,7 +627,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
}
#if !SANITIZER_GO
-void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
+void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
CHECK_GE(max_depth, 2);
// FIXME: CaptureStackBackTrace might be too slow for us.
// FIXME: Compare with StackWalk64.
@@ -458,7 +643,7 @@ void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
}
void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
- uptr max_depth) {
+ u32 max_depth) {
CONTEXT ctx = *(CONTEXT *)context;
STACKFRAME64 stack_frame;
memset(&stack_frame, 0, sizeof(stack_frame));
@@ -486,15 +671,10 @@ void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
}
#endif // #if !SANITIZER_GO
-void MaybeOpenReportFile() {
- // Windows doesn't have native fork, and we don't support Cygwin or other
- // environments that try to fake it, so the initial report_fd will always be
- // correct.
-}
-
-void RawWrite(const char *buffer) {
- uptr length = (uptr)internal_strlen(buffer);
- if (length != internal_write(report_fd, buffer, length)) {
+void ReportFile::Write(const char *buffer, uptr length) {
+ SpinMutexLock l(mu);
+ ReopenIfNecessary();
+ if (!WriteToFile(fd, buffer, length)) {
// stderr may be closed, but we may be able to print to the debugger
// instead. This is the case when launching a program from Visual Studio,
// and the following routine should write to its console.
@@ -521,10 +701,62 @@ bool IsDeadlySignal(int signum) {
}
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
- // FIXME: Actually implement this function.
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ uptr page_size = si.dwPageSize;
+ uptr page_mask = ~(page_size - 1);
+
+ for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask;
+ page <= end;) {
+ MEMORY_BASIC_INFORMATION info;
+ if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info))
+ return false;
+
+ if (info.Protect == 0 || info.Protect == PAGE_NOACCESS ||
+ info.Protect == PAGE_EXECUTE)
+ return false;
+
+ if (info.RegionSize == 0)
+ return false;
+
+ page += info.RegionSize;
+ }
+
return true;
}
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+ EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo;
+ CONTEXT *context_record = (CONTEXT*)context;
+
+ uptr pc = (uptr)exception_record->ExceptionAddress;
+#ifdef _WIN64
+ uptr bp = (uptr)context_record->Rbp;
+ uptr sp = (uptr)context_record->Rsp;
+#else
+ uptr bp = (uptr)context_record->Ebp;
+ uptr sp = (uptr)context_record->Esp;
+#endif
+ uptr access_addr = exception_record->ExceptionInformation[1];
+
+ return SignalContext(context, access_addr, pc, sp, bp);
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ // FIXME: Actually implement this function.
+ CHECK_GT(buf_len, 0);
+ buf[0] = 0;
+ return 0;
+}
+
+uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
+ return ReadBinaryName(buf, buf_len);
+}
+
+void CheckVMASize() {
+ // Do nothing.
+}
+
} // namespace __sanitizer
#endif // _WIN32