libsanitizer merge from upstream r253555.

libsanitizer/

2015-11-23  Maxim Ostapenko  <m.ostapenko@partner.samsung.com>

	* All source files: Merge from upstream r253555.
	* configure.tgt: Enable LSan on aarch64-*-linux* targets. Add new
	dependences for TSan for aarch64-*-linux* targets.
	* tsan/Makefile.am: Add new source files.
	* configure: Regenerate.
	* tsan/Makefile.in: Likewise.

From-SVN: r230739
This commit is contained in:
Max Ostapenko 2015-11-23 11:07:18 +02:00 committed by Maxim Ostapenko
parent 096b85f4b4
commit 55aea9f56c
65 changed files with 1710 additions and 779 deletions

View File

@ -1,3 +1,12 @@
2015-11-23 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
* All source files: Merge from upstream r253555.
* configure.tgt: Enable LSan on aarch64-*-linux* targets. Add new
dependences for TSan for aarch64-*-linux* targets.
* tsan/Makefile.am: Add new source files.
* configure: Regenerate.
* tsan/Makefile.in: Likewise.
2015-11-09 Alan Modra <amodra@gmail.com> 2015-11-09 Alan Modra <amodra@gmail.com>
* sanitizer_common/sanitizer_common_interceptors.inc: Update size * sanitizer_common/sanitizer_common_interceptors.inc: Update size

View File

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

View File

@ -73,6 +73,7 @@ ASAN_FLAG(bool, check_malloc_usable_size, true,
"295.*.") "295.*.")
ASAN_FLAG(bool, unmap_shadow_on_exit, false, ASAN_FLAG(bool, unmap_shadow_on_exit, false,
"If set, explicitly unmaps the (huge) shadow at exit.") "If set, explicitly unmaps the (huge) shadow at exit.")
ASAN_FLAG(bool, protect_shadow_gap, true, "If set, mprotect the shadow gap")
ASAN_FLAG(bool, print_stats, false, ASAN_FLAG(bool, print_stats, false,
"Print various statistics after printing an error message or if " "Print various statistics after printing an error message or if "
"atexit=1.") "atexit=1.")
@ -132,3 +133,6 @@ ASAN_FLAG(int, detect_odr_violation, 2,
ASAN_FLAG(bool, dump_instruction_bytes, false, ASAN_FLAG(bool, dump_instruction_bytes, false,
"If true, dump 16 bytes starting at the instruction that caused SEGV") "If true, dump 16 bytes starting at the instruction that caused SEGV")
ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.") ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
ASAN_FLAG(bool, halt_on_error, true,
"Crash the program after printing the first error report "
"(WARNING: USE AT YOUR OWN RISK!)")

View File

@ -73,7 +73,7 @@ struct AsanInterceptorContext {
} \ } \
if (!suppressed) { \ if (!suppressed) { \
GET_CURRENT_PC_BP_SP; \ GET_CURRENT_PC_BP_SP; \
__asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0); \ ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false);\
} \ } \
} \ } \
} while (0) } while (0)

View File

@ -165,6 +165,19 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN(uptr p, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN(uptr p, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN(uptr p, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN(uptr p, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load8_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load16_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store1_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store2_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store4_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store8_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store16_noabort(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN_noabort(uptr p, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN_noabort(uptr p, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load1(uptr p, u32 exp); SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load1(uptr p, u32 exp);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp); SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load4(uptr p, u32 exp); SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load4(uptr p, u32 exp);

View File

@ -31,17 +31,17 @@ extern "C" {
#endif #endif
#include <dlfcn.h> // for dladdr() #include <dlfcn.h> // for dladdr()
#include <fcntl.h>
#include <libkern/OSAtomic.h>
#include <mach-o/dyld.h> #include <mach-o/dyld.h>
#include <mach-o/loader.h> #include <mach-o/loader.h>
#include <pthread.h>
#include <stdlib.h> // for free()
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/ucontext.h> #include <sys/ucontext.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h> // for free()
#include <unistd.h> #include <unistd.h>
#include <libkern/OSAtomic.h>
namespace __asan { namespace __asan {

View File

@ -13,348 +13,51 @@
#include "sanitizer_common/sanitizer_platform.h" #include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_MAC #if SANITIZER_MAC
#include <AvailabilityMacros.h>
#include <CoreFoundation/CFBase.h>
#include <dlfcn.h>
#include <malloc/malloc.h>
#include <sys/mman.h>
#include "asan_allocator.h"
#include "asan_interceptors.h" #include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_report.h" #include "asan_report.h"
#include "asan_stack.h" #include "asan_stack.h"
#include "asan_stats.h" #include "asan_stats.h"
#include "sanitizer_common/sanitizer_mac.h"
// Similar code is used in Google Perftools, using namespace __asan;
// http://code.google.com/p/google-perftools. #define COMMON_MALLOC_ZONE_NAME "asan"
#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED()
// ---------------------- Replacement functions ---------------- {{{1 #define COMMON_MALLOC_SANITIZER_INITIALIZED asan_inited
using namespace __asan; // NOLINT #define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock()
#define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock()
// TODO(glider): do we need both zones? #define COMMON_MALLOC_MEMALIGN(alignment, size) \
static malloc_zone_t *system_malloc_zone = 0; GET_STACK_TRACE_MALLOC; \
static malloc_zone_t asan_zone; void *p = asan_memalign(alignment, size, &stack, FROM_MALLOC)
#define COMMON_MALLOC_MALLOC(size) \
INTERCEPTOR(malloc_zone_t *, malloc_create_zone, GET_STACK_TRACE_MALLOC; \
vm_size_t start_size, unsigned zone_flags) { void *p = asan_malloc(size, &stack)
ENSURE_ASAN_INITED(); #define COMMON_MALLOC_REALLOC(ptr, size) \
GET_STACK_TRACE_MALLOC; GET_STACK_TRACE_MALLOC; \
uptr page_size = GetPageSizeCached(); void *p = asan_realloc(ptr, size, &stack);
uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size); #define COMMON_MALLOC_CALLOC(count, size) \
malloc_zone_t *new_zone = GET_STACK_TRACE_MALLOC; \
(malloc_zone_t*)asan_memalign(page_size, allocated_size, void *p = asan_calloc(count, size, &stack);
&stack, FROM_MALLOC); #define COMMON_MALLOC_VALLOC(size) \
internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone)); GET_STACK_TRACE_MALLOC; \
new_zone->zone_name = NULL; // The name will be changed anyway. void *p = asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
if (GetMacosVersion() >= MACOS_VERSION_LION) { #define COMMON_MALLOC_FREE(ptr) \
// Prevent the client app from overwriting the zone contents. GET_STACK_TRACE_FREE; \
// Library functions that need to modify the zone will set PROT_WRITE on it.
// This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
mprotect(new_zone, allocated_size, PROT_READ);
}
return new_zone;
}
INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
ENSURE_ASAN_INITED();
return &asan_zone;
}
INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
// FIXME: ASan should support purgeable allocations.
// https://code.google.com/p/address-sanitizer/issues/detail?id=139
ENSURE_ASAN_INITED();
return &asan_zone;
}
INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
// for now.
ENSURE_ASAN_INITED();
}
INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
// for now.
ENSURE_ASAN_INITED();
// Must return 0 if the contents were not purged since the last call to
// malloc_make_purgeable().
return 0;
}
INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
ENSURE_ASAN_INITED();
// Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes.
size_t buflen = 6 + (name ? internal_strlen(name) : 0);
InternalScopedString new_name(buflen);
if (name && zone->introspect == asan_zone.introspect) {
new_name.append("asan-%s", name);
name = new_name.data();
}
// Call the system malloc's implementation for both external and our zones,
// since that appropriately changes VM region protections on the zone.
REAL(malloc_set_zone_name)(zone, name);
}
INTERCEPTOR(void *, malloc, size_t size) {
ENSURE_ASAN_INITED();
GET_STACK_TRACE_MALLOC;
void *res = asan_malloc(size, &stack);
return res;
}
INTERCEPTOR(void, free, void *ptr) {
ENSURE_ASAN_INITED();
if (!ptr) return;
GET_STACK_TRACE_FREE;
asan_free(ptr, &stack, FROM_MALLOC); asan_free(ptr, &stack, FROM_MALLOC);
} #define COMMON_MALLOC_SIZE(ptr) \
uptr size = asan_mz_size(ptr);
INTERCEPTOR(void *, realloc, void *ptr, size_t size) { #define COMMON_MALLOC_FILL_STATS(zone, stats) \
ENSURE_ASAN_INITED(); AsanMallocStats malloc_stats; \
GET_STACK_TRACE_MALLOC; FillMallocStatistics(&malloc_stats); \
return asan_realloc(ptr, size, &stack); CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); \
}
INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
ENSURE_ASAN_INITED();
GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
INTERCEPTOR(void *, valloc, size_t size) {
ENSURE_ASAN_INITED();
GET_STACK_TRACE_MALLOC;
return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
}
INTERCEPTOR(size_t, malloc_good_size, size_t size) {
ENSURE_ASAN_INITED();
return asan_zone.introspect->good_size(&asan_zone, size);
}
INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
ENSURE_ASAN_INITED();
CHECK(memptr);
GET_STACK_TRACE_MALLOC;
void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC);
if (result) {
*memptr = result;
return 0;
}
return -1;
}
namespace {
// TODO(glider): the __asan_mz_* functions should be united with the Linux
// wrappers, as they are basically copied from there.
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
size_t __asan_mz_size(malloc_zone_t* zone, const void* ptr) {
return asan_mz_size(ptr);
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__asan_mz_malloc(malloc_zone_t *zone, uptr size) {
if (UNLIKELY(!asan_inited)) {
CHECK(system_malloc_zone);
return malloc_zone_malloc(system_malloc_zone, size);
}
GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__asan_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
if (UNLIKELY(!asan_inited)) {
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
const size_t kCallocPoolSize = 1024;
static uptr calloc_memory_for_dlsym[kCallocPoolSize];
static size_t allocated;
size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
void *mem = (void*)&calloc_memory_for_dlsym[allocated];
allocated += size_in_words;
CHECK(allocated < kCallocPoolSize);
return mem;
}
GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__asan_mz_valloc(malloc_zone_t *zone, size_t size) {
if (UNLIKELY(!asan_inited)) {
CHECK(system_malloc_zone);
return malloc_zone_valloc(system_malloc_zone, size);
}
GET_STACK_TRACE_MALLOC;
return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
}
#define GET_ZONE_FOR_PTR(ptr) \
malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
void ALWAYS_INLINE free_common(void *context, void *ptr) {
if (!ptr) return;
GET_STACK_TRACE_FREE;
// FIXME: need to retire this flag.
if (!flags()->mac_ignore_invalid_free) {
asan_free(ptr, &stack, FROM_MALLOC);
} else {
GET_ZONE_FOR_PTR(ptr);
WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
return;
}
}
// TODO(glider): the allocation callbacks need to be refactored.
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_mz_free(malloc_zone_t *zone, void *ptr) {
free_common(zone, ptr);
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__asan_mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
if (!ptr) {
GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
} else {
if (asan_mz_size(ptr)) {
GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
} else {
// We can't recover from reallocating an unknown address, because
// this would require reading at most |size| bytes from
// potentially unaccessible memory.
GET_STACK_TRACE_FREE;
GET_ZONE_FOR_PTR(ptr);
ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
}
}
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_mz_destroy(malloc_zone_t* zone) {
// A no-op -- we will not be destroyed!
Report("__asan_mz_destroy() called -- ignoring\n");
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__asan_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
if (UNLIKELY(!asan_inited)) {
CHECK(system_malloc_zone);
return malloc_zone_memalign(system_malloc_zone, align, size);
}
GET_STACK_TRACE_MALLOC;
return asan_memalign(align, size, &stack, FROM_MALLOC);
}
// This function is currently unused, and we build with -Werror.
#if 0
void __asan_mz_free_definite_size(
malloc_zone_t* zone, void *ptr, size_t size) {
// TODO(glider): check that |size| is valid.
UNIMPLEMENTED();
}
#endif
kern_return_t mi_enumerator(task_t task, void *,
unsigned type_mask, vm_address_t zone_address,
memory_reader_t reader,
vm_range_recorder_t recorder) {
// Should enumerate all the pointers we have. Seems like a lot of work.
return KERN_FAILURE;
}
size_t mi_good_size(malloc_zone_t *zone, size_t size) {
// I think it's always safe to return size, but we maybe could do better.
return size;
}
boolean_t mi_check(malloc_zone_t *zone) {
UNIMPLEMENTED();
}
void mi_print(malloc_zone_t *zone, boolean_t verbose) {
UNIMPLEMENTED();
}
void mi_log(malloc_zone_t *zone, void *address) {
// I don't think we support anything like this
}
void mi_force_lock(malloc_zone_t *zone) {
asan_mz_force_lock();
}
void mi_force_unlock(malloc_zone_t *zone) {
asan_mz_force_unlock();
}
void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
AsanMallocStats malloc_stats;
FillMallocStatistics(&malloc_stats);
CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats));
internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t));
} #define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
GET_STACK_TRACE_FREE; \
ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
#define COMMON_MALLOC_IGNORE_INVALID_FREE flags()->mac_ignore_invalid_free
#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \
GET_STACK_TRACE_FREE; \
WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
#define COMMON_MALLOC_NAMESPACE __asan
boolean_t mi_zone_locked(malloc_zone_t *zone) { #include "sanitizer_common/sanitizer_malloc_mac.inc"
// UNIMPLEMENTED();
return false;
}
} // unnamed namespace #endif
namespace __asan {
void ReplaceSystemMalloc() {
static malloc_introspection_t asan_introspection;
// Ok to use internal_memset, these places are not performance-critical.
internal_memset(&asan_introspection, 0, sizeof(asan_introspection));
asan_introspection.enumerator = &mi_enumerator;
asan_introspection.good_size = &mi_good_size;
asan_introspection.check = &mi_check;
asan_introspection.print = &mi_print;
asan_introspection.log = &mi_log;
asan_introspection.force_lock = &mi_force_lock;
asan_introspection.force_unlock = &mi_force_unlock;
asan_introspection.statistics = &mi_statistics;
asan_introspection.zone_locked = &mi_zone_locked;
internal_memset(&asan_zone, 0, sizeof(malloc_zone_t));
// Use version 6 for OSX >= 10.6.
asan_zone.version = 6;
asan_zone.zone_name = "asan";
asan_zone.size = &__asan_mz_size;
asan_zone.malloc = &__asan_mz_malloc;
asan_zone.calloc = &__asan_mz_calloc;
asan_zone.valloc = &__asan_mz_valloc;
asan_zone.free = &__asan_mz_free;
asan_zone.realloc = &__asan_mz_realloc;
asan_zone.destroy = &__asan_mz_destroy;
asan_zone.batch_malloc = 0;
asan_zone.batch_free = 0;
asan_zone.free_definite_size = 0;
asan_zone.memalign = &__asan_mz_memalign;
asan_zone.introspect = &asan_introspection;
// Register the ASan zone.
malloc_zone_register(&asan_zone);
}
} // namespace __asan
#endif // SANITIZER_MAC

View File

@ -116,11 +116,7 @@ static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000
static const u64 kIosShadowOffset64 = 0x130000000; static const u64 kIosShadowOffset64 = 0x130000000;
static const u64 kIosSimShadowOffset32 = 1ULL << 30; static const u64 kIosSimShadowOffset32 = 1ULL << 30;
static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64; static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64;
#if SANITIZER_AARCH64_VMA == 39
static const u64 kAArch64_ShadowOffset64 = 1ULL << 36; static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
#elif SANITIZER_AARCH64_VMA == 42
static const u64 kAArch64_ShadowOffset64 = 1ULL << 39;
#endif
static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000; static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37; static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37;
static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; static const u64 kPPC64_ShadowOffset64 = 1ULL << 41;

View File

@ -373,10 +373,10 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p,
} }
} }
int __sanitizer_verify_contiguous_container(const void *beg_p, const void *__sanitizer_contiguous_container_find_bad_address(
const void *mid_p, const void *beg_p, const void *mid_p, const void *end_p) {
const void *end_p) { if (!flags()->detect_container_overflow)
if (!flags()->detect_container_overflow) return 1; return nullptr;
uptr beg = reinterpret_cast<uptr>(beg_p); uptr beg = reinterpret_cast<uptr>(beg_p);
uptr end = reinterpret_cast<uptr>(end_p); uptr end = reinterpret_cast<uptr>(end_p);
uptr mid = reinterpret_cast<uptr>(mid_p); uptr mid = reinterpret_cast<uptr>(mid_p);
@ -393,17 +393,24 @@ int __sanitizer_verify_contiguous_container(const void *beg_p,
uptr r3_end = end; uptr r3_end = end;
for (uptr i = r1_beg; i < r1_end; i++) for (uptr i = r1_beg; i < r1_end; i++)
if (AddressIsPoisoned(i)) if (AddressIsPoisoned(i))
return 0; return reinterpret_cast<const void *>(i);
for (uptr i = r2_beg; i < mid; i++) for (uptr i = r2_beg; i < mid; i++)
if (AddressIsPoisoned(i)) if (AddressIsPoisoned(i))
return 0; return reinterpret_cast<const void *>(i);
for (uptr i = mid; i < r2_end; i++) for (uptr i = mid; i < r2_end; i++)
if (!AddressIsPoisoned(i)) if (!AddressIsPoisoned(i))
return 0; return reinterpret_cast<const void *>(i);
for (uptr i = r3_beg; i < r3_end; i++) for (uptr i = r3_beg; i < r3_end; i++)
if (!AddressIsPoisoned(i)) if (!AddressIsPoisoned(i))
return 0; return reinterpret_cast<const void *>(i);
return 1; return nullptr;
}
int __sanitizer_verify_contiguous_container(const void *beg_p,
const void *mid_p,
const void *end_p) {
return __sanitizer_contiguous_container_find_bad_address(beg_p, mid_p,
end_p) == nullptr;
} }
extern "C" SANITIZER_INTERFACE_ATTRIBUTE extern "C" SANITIZER_INTERFACE_ATTRIBUTE

View File

@ -620,41 +620,57 @@ void DescribeThread(AsanThreadContext *context) {
// immediately after printing error report. // immediately after printing error report.
class ScopedInErrorReport { class ScopedInErrorReport {
public: public:
explicit ScopedInErrorReport(ReportData *report = nullptr) { explicit ScopedInErrorReport(ReportData *report = nullptr,
static atomic_uint32_t num_calls; bool fatal = false) {
static u32 reporting_thread_tid; halt_on_error_ = fatal || flags()->halt_on_error;
if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
if (lock_.TryLock()) {
StartReporting(report);
return;
}
// ASan found two bugs in different threads simultaneously.
u32 current_tid = GetCurrentTidOrInvalid();
if (reporting_thread_tid_ == current_tid ||
reporting_thread_tid_ == kInvalidTid) {
// This is either asynch signal or nested error during error reporting.
// Fail simple to avoid deadlocks in Report().
// Can't use Report() here because of potential deadlocks
// in nested signal handlers.
const char msg[] = "AddressSanitizer: nested bug in the same thread, "
"aborting.\n";
WriteToFile(kStderrFd, msg, sizeof(msg));
internal__exit(common_flags()->exitcode);
}
if (halt_on_error_) {
// Do not print more than one report, otherwise they will mix up. // Do not print more than one report, otherwise they will mix up.
// Error reporting functions shouldn't return at this situation, as // Error reporting functions shouldn't return at this situation, as
// they are defined as no-return. // they are effectively no-returns.
Report("AddressSanitizer: while reporting a bug found another one. " Report("AddressSanitizer: while reporting a bug found another one. "
"Ignoring.\n"); "Ignoring.\n");
u32 current_tid = GetCurrentTidOrInvalid();
if (current_tid != reporting_thread_tid) { // Sleep long enough to make sure that the thread which started
// ASan found two bugs in different threads simultaneously. Sleep // to print an error report will finish doing it.
// long enough to make sure that the thread which started to print SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
// an error report will finish doing it.
SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
}
// If we're still not dead for some reason, use raw _exit() instead of // If we're still not dead for some reason, use raw _exit() instead of
// Die() to bypass any additional checks. // Die() to bypass any additional checks.
internal__exit(common_flags()->exitcode); internal__exit(common_flags()->exitcode);
} else {
// The other thread will eventually finish reporting
// so it's safe to wait
lock_.Lock();
} }
if (report) report_data = *report;
report_happened = true; StartReporting(report);
ASAN_ON_ERROR();
// Make sure the registry and sanitizer report mutexes are locked while
// we're printing an error report.
// We can lock them only here to avoid self-deadlock in case of
// recursive reports.
asanThreadRegistry().Lock();
CommonSanitizerReportMutex.Lock();
reporting_thread_tid = GetCurrentTidOrInvalid();
Printf("===================================================="
"=============\n");
} }
// Destructor is NORETURN, as functions that report errors are.
NORETURN ~ScopedInErrorReport() { ~ScopedInErrorReport() {
// Make sure the current thread is announced. // Make sure the current thread is announced.
DescribeThread(GetCurrentThread()); DescribeThread(GetCurrentThread());
// We may want to grab this lock again when printing stats. // We may want to grab this lock again when printing stats.
@ -665,11 +681,39 @@ class ScopedInErrorReport {
if (error_report_callback) { if (error_report_callback) {
error_report_callback(error_message_buffer); error_report_callback(error_message_buffer);
} }
Report("ABORTING\n"); CommonSanitizerReportMutex.Unlock();
Die(); reporting_thread_tid_ = kInvalidTid;
lock_.Unlock();
if (halt_on_error_) {
Report("ABORTING\n");
Die();
}
} }
private:
void StartReporting(ReportData *report) {
if (report) report_data = *report;
report_happened = true;
ASAN_ON_ERROR();
// Make sure the registry and sanitizer report mutexes are locked while
// we're printing an error report.
// We can lock them only here to avoid self-deadlock in case of
// recursive reports.
asanThreadRegistry().Lock();
CommonSanitizerReportMutex.Lock();
reporting_thread_tid_ = GetCurrentTidOrInvalid();
Printf("===================================================="
"=============\n");
}
static StaticSpinMutex lock_;
static u32 reporting_thread_tid_;
bool halt_on_error_;
}; };
StaticSpinMutex ScopedInErrorReport::lock_;
u32 ScopedInErrorReport::reporting_thread_tid_;
void ReportStackOverflow(const SignalContext &sig) { void ReportStackOverflow(const SignalContext &sig) {
ScopedInErrorReport in_report; ScopedInErrorReport in_report;
Decorator d; Decorator d;
@ -686,7 +730,7 @@ void ReportStackOverflow(const SignalContext &sig) {
} }
void ReportDeadlySignal(const char *description, const SignalContext &sig) { void ReportDeadlySignal(const char *description, const SignalContext &sig) {
ScopedInErrorReport in_report; ScopedInErrorReport in_report(/*report*/nullptr, /*fatal*/true);
Decorator d; Decorator d;
Printf("%s", d.Warning()); Printf("%s", d.Warning());
Report( Report(
@ -743,7 +787,7 @@ void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
stack.Print(); stack.Print();
DescribeHeapAddress(addr, 1); DescribeHeapAddress(addr, 1);
ReportErrorSummary("new-delete-type-mismatch", &stack); ReportErrorSummary("new-delete-type-mismatch", &stack);
Report("HINT: if you don't care about these warnings you may set " Report("HINT: if you don't care about these errors you may set "
"ASAN_OPTIONS=new_delete_type_mismatch=0\n"); "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
} }
@ -783,7 +827,7 @@ void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
stack.Print(); stack.Print();
DescribeHeapAddress(addr, 1); DescribeHeapAddress(addr, 1);
ReportErrorSummary("alloc-dealloc-mismatch", &stack); ReportErrorSummary("alloc-dealloc-mismatch", &stack);
Report("HINT: if you don't care about these warnings you may set " Report("HINT: if you don't care about these errors you may set "
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
} }
@ -885,7 +929,7 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
Printf(" [2]:\n"); Printf(" [2]:\n");
StackDepotGet(stack_id2).Print(); StackDepotGet(stack_id2).Print();
} }
Report("HINT: if you don't care about these warnings you may set " Report("HINT: if you don't care about these errors you may set "
"ASAN_OPTIONS=detect_odr_violation=0\n"); "ASAN_OPTIONS=detect_odr_violation=0\n");
InternalScopedString error_msg(256); InternalScopedString error_msg(256);
error_msg.append("odr-violation: global '%s' at %s", error_msg.append("odr-violation: global '%s' at %s",
@ -957,13 +1001,8 @@ void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
DescribeHeapAddress(addr, 1); DescribeHeapAddress(addr, 1);
} }
} // namespace __asan void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
uptr access_size, u32 exp, bool fatal) {
// --------------------------- Interface --------------------- {{{1
using namespace __asan; // NOLINT
void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
uptr access_size, u32 exp) {
ENABLE_FRAME_POINTER; ENABLE_FRAME_POINTER;
// Optimization experiments. // Optimization experiments.
@ -1032,7 +1071,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size, ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
bug_descr }; bug_descr };
ScopedInErrorReport in_report(&report); ScopedInErrorReport in_report(&report, fatal);
Decorator d; Decorator d;
Printf("%s", d.Warning()); Printf("%s", d.Warning());
@ -1058,6 +1097,18 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
PrintShadowMemoryForAddress(addr); PrintShadowMemoryForAddress(addr);
} }
} // namespace __asan
// --------------------------- Interface --------------------- {{{1
using namespace __asan; // NOLINT
void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
uptr access_size, u32 exp) {
ENABLE_FRAME_POINTER;
bool fatal = flags()->halt_on_error;
ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal);
}
void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
error_report_callback = callback; error_report_callback = callback;
if (callback) { if (callback) {

View File

@ -47,45 +47,41 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size);
void DescribeThread(AsanThreadContext *context); void DescribeThread(AsanThreadContext *context);
// Different kinds of error reports. // Different kinds of error reports.
void NORETURN ReportStackOverflow(const SignalContext &sig); void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
void NORETURN ReportDeadlySignal(const char* description, uptr access_size, u32 exp, bool fatal);
const SignalContext &sig); void ReportStackOverflow(const SignalContext &sig);
void NORETURN ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size, void ReportDeadlySignal(const char *description, const SignalContext &sig);
BufferedStackTrace *free_stack); void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
void NORETURN ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack); BufferedStackTrace *free_stack);
void NORETURN ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack); void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
void NORETURN ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack, void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
AllocType alloc_type, void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
AllocType dealloc_type); AllocType alloc_type,
void NORETURN AllocType dealloc_type);
ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack); void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack);
void NORETURN void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, BufferedStackTrace *stack);
BufferedStackTrace *stack); void ReportStringFunctionMemoryRangesOverlap(const char *function,
void NORETURN const char *offset1, uptr length1,
ReportStringFunctionMemoryRangesOverlap(const char *function, const char *offset2, uptr length2,
const char *offset1, uptr length1, BufferedStackTrace *stack);
const char *offset2, uptr length2, void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
BufferedStackTrace *stack); BufferedStackTrace *stack);
void NORETURN ReportStringFunctionSizeOverflow(uptr offset, uptr size, void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
BufferedStackTrace *stack); uptr old_mid, uptr new_mid,
void NORETURN BufferedStackTrace *stack);
ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
uptr old_mid, uptr new_mid,
BufferedStackTrace *stack);
void NORETURN void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
ReportODRViolation(const __asan_global *g1, u32 stack_id1, const __asan_global *g2, u32 stack_id2);
const __asan_global *g2, u32 stack_id2);
// Mac-specific errors and warnings. // Mac-specific errors and warnings.
void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name, void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
BufferedStackTrace *stack); BufferedStackTrace *stack);
void NORETURN ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
const char *zone_name, const char *zone_name,
BufferedStackTrace *stack); BufferedStackTrace *stack);
void NORETURN ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
const char *zone_name, const char *zone_name,
BufferedStackTrace *stack); BufferedStackTrace *stack);
} // namespace __asan } // namespace __asan

View File

@ -111,13 +111,18 @@ static void OnLowLevelAllocate(uptr ptr, uptr size) {
extern "C" NOINLINE INTERFACE_ATTRIBUTE \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
void __asan_report_ ## type ## size(uptr addr) { \ void __asan_report_ ## type ## size(uptr addr) { \
GET_CALLER_PC_BP_SP; \ GET_CALLER_PC_BP_SP; \
__asan_report_error(pc, bp, sp, addr, is_write, size, 0); \ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \
} \ } \
extern "C" NOINLINE INTERFACE_ATTRIBUTE \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \ void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \
GET_CALLER_PC_BP_SP; \ GET_CALLER_PC_BP_SP; \
__asan_report_error(pc, bp, sp, addr, is_write, size, exp); \ ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \
} } \
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
void __asan_report_ ## type ## size ## _noabort(uptr addr) { \
GET_CALLER_PC_BP_SP; \
ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \
} \
ASAN_REPORT_ERROR(load, false, 1) ASAN_REPORT_ERROR(load, false, 1)
ASAN_REPORT_ERROR(load, false, 2) ASAN_REPORT_ERROR(load, false, 2)
@ -130,22 +135,27 @@ ASAN_REPORT_ERROR(store, true, 4)
ASAN_REPORT_ERROR(store, true, 8) ASAN_REPORT_ERROR(store, true, 8)
ASAN_REPORT_ERROR(store, true, 16) ASAN_REPORT_ERROR(store, true, 16)
#define ASAN_REPORT_ERROR_N(type, is_write) \ #define ASAN_REPORT_ERROR_N(type, is_write) \
extern "C" NOINLINE INTERFACE_ATTRIBUTE \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ void __asan_report_ ## type ## _n(uptr addr, uptr size) { \
GET_CALLER_PC_BP_SP; \ GET_CALLER_PC_BP_SP; \
__asan_report_error(pc, bp, sp, addr, is_write, size, 0); \ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \
} \ } \
extern "C" NOINLINE INTERFACE_ATTRIBUTE \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \ void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \
GET_CALLER_PC_BP_SP; \ GET_CALLER_PC_BP_SP; \
__asan_report_error(pc, bp, sp, addr, is_write, size, exp); \ ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \
} } \
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) { \
GET_CALLER_PC_BP_SP; \
ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \
} \
ASAN_REPORT_ERROR_N(load, false) ASAN_REPORT_ERROR_N(load, false)
ASAN_REPORT_ERROR_N(store, true) ASAN_REPORT_ERROR_N(store, true)
#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg) \ #define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \
uptr sp = MEM_TO_SHADOW(addr); \ uptr sp = MEM_TO_SHADOW(addr); \
uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \ uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \
: *reinterpret_cast<u16 *>(sp); \ : *reinterpret_cast<u16 *>(sp); \
@ -157,7 +167,8 @@ ASAN_REPORT_ERROR_N(store, true)
*__asan_test_only_reported_buggy_pointer = addr; \ *__asan_test_only_reported_buggy_pointer = addr; \
} else { \ } else { \
GET_CALLER_PC_BP_SP; \ GET_CALLER_PC_BP_SP; \
__asan_report_error(pc, bp, sp, addr, is_write, size, exp_arg); \ ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \
fatal); \
} \ } \
} \ } \
} }
@ -165,12 +176,16 @@ ASAN_REPORT_ERROR_N(store, true)
#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \ #define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \
extern "C" NOINLINE INTERFACE_ATTRIBUTE \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
void __asan_##type##size(uptr addr) { \ void __asan_##type##size(uptr addr) { \
ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0) \ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, true) \
} \ } \
extern "C" NOINLINE INTERFACE_ATTRIBUTE \ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
void __asan_exp_##type##size(uptr addr, u32 exp) { \ void __asan_exp_##type##size(uptr addr, u32 exp) { \
ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp) \ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true) \
} } \
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
void __asan_##type##size ## _noabort(uptr addr) { \
ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false) \
} \
ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1) ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1)
ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2) ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2)
@ -188,7 +203,7 @@ NOINLINE INTERFACE_ATTRIBUTE
void __asan_loadN(uptr addr, uptr size) { void __asan_loadN(uptr addr, uptr size) {
if (__asan_region_is_poisoned(addr, size)) { if (__asan_region_is_poisoned(addr, size)) {
GET_CALLER_PC_BP_SP; GET_CALLER_PC_BP_SP;
__asan_report_error(pc, bp, sp, addr, false, size, 0); ReportGenericError(pc, bp, sp, addr, false, size, 0, true);
} }
} }
@ -197,7 +212,16 @@ NOINLINE INTERFACE_ATTRIBUTE
void __asan_exp_loadN(uptr addr, uptr size, u32 exp) { void __asan_exp_loadN(uptr addr, uptr size, u32 exp) {
if (__asan_region_is_poisoned(addr, size)) { if (__asan_region_is_poisoned(addr, size)) {
GET_CALLER_PC_BP_SP; GET_CALLER_PC_BP_SP;
__asan_report_error(pc, bp, sp, addr, false, size, exp); ReportGenericError(pc, bp, sp, addr, false, size, exp, true);
}
}
extern "C"
NOINLINE INTERFACE_ATTRIBUTE
void __asan_loadN_noabort(uptr addr, uptr size) {
if (__asan_region_is_poisoned(addr, size)) {
GET_CALLER_PC_BP_SP;
ReportGenericError(pc, bp, sp, addr, false, size, 0, false);
} }
} }
@ -206,7 +230,7 @@ NOINLINE INTERFACE_ATTRIBUTE
void __asan_storeN(uptr addr, uptr size) { void __asan_storeN(uptr addr, uptr size) {
if (__asan_region_is_poisoned(addr, size)) { if (__asan_region_is_poisoned(addr, size)) {
GET_CALLER_PC_BP_SP; GET_CALLER_PC_BP_SP;
__asan_report_error(pc, bp, sp, addr, true, size, 0); ReportGenericError(pc, bp, sp, addr, true, size, 0, true);
} }
} }
@ -215,7 +239,16 @@ NOINLINE INTERFACE_ATTRIBUTE
void __asan_exp_storeN(uptr addr, uptr size, u32 exp) { void __asan_exp_storeN(uptr addr, uptr size, u32 exp) {
if (__asan_region_is_poisoned(addr, size)) { if (__asan_region_is_poisoned(addr, size)) {
GET_CALLER_PC_BP_SP; GET_CALLER_PC_BP_SP;
__asan_report_error(pc, bp, sp, addr, true, size, exp); ReportGenericError(pc, bp, sp, addr, true, size, exp, true);
}
}
extern "C"
NOINLINE INTERFACE_ATTRIBUTE
void __asan_storeN_noabort(uptr addr, uptr size) {
if (__asan_region_is_poisoned(addr, size)) {
GET_CALLER_PC_BP_SP;
ReportGenericError(pc, bp, sp, addr, true, size, 0, false);
} }
} }
@ -291,6 +324,8 @@ static void InitializeHighMemEnd() {
} }
static void ProtectGap(uptr addr, uptr size) { static void ProtectGap(uptr addr, uptr size) {
if (!flags()->protect_shadow_gap)
return;
void *res = MmapNoAccess(addr, size, "shadow gap"); void *res = MmapNoAccess(addr, size, "shadow gap");
if (addr == (uptr)res) if (addr == (uptr)res)
return; return;
@ -376,8 +411,6 @@ static void AsanInitInternal() {
// initialization steps look at flags(). // initialization steps look at flags().
InitializeFlags(); InitializeFlags();
CheckVMASize();
AsanCheckIncompatibleRT(); AsanCheckIncompatibleRT();
AsanCheckDynamicRTPrereqs(); AsanCheckDynamicRTPrereqs();

View File

@ -12,6 +12,7 @@
#include "sanitizer_common/sanitizer_platform.h" #include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_WINDOWS #if SANITIZER_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include <stdlib.h> #include <stdlib.h>

View File

@ -255,6 +255,9 @@ INTERFACE_FUNCTION(__asan_memcpy);
INTERFACE_FUNCTION(__asan_memset); INTERFACE_FUNCTION(__asan_memset);
INTERFACE_FUNCTION(__asan_memmove); INTERFACE_FUNCTION(__asan_memmove);
INTERFACE_FUNCTION(__asan_alloca_poison);
INTERFACE_FUNCTION(__asan_allocas_unpoison);
INTERFACE_FUNCTION(__asan_register_globals) INTERFACE_FUNCTION(__asan_register_globals)
INTERFACE_FUNCTION(__asan_unregister_globals) INTERFACE_FUNCTION(__asan_unregister_globals)
@ -298,6 +301,7 @@ INTERFACE_FUNCTION(__asan_stack_free_10)
// FIXME: we might want to have a sanitizer_win_dll_thunk? // FIXME: we might want to have a sanitizer_win_dll_thunk?
INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container) INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
INTERFACE_FUNCTION(__sanitizer_cov) INTERFACE_FUNCTION(__sanitizer_cov)
INTERFACE_FUNCTION(__sanitizer_cov_dump) INTERFACE_FUNCTION(__sanitizer_cov_dump)
INTERFACE_FUNCTION(__sanitizer_cov_indir_call16) INTERFACE_FUNCTION(__sanitizer_cov_indir_call16)
@ -315,6 +319,7 @@ INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size)
INTERFACE_FUNCTION(__sanitizer_get_free_bytes) INTERFACE_FUNCTION(__sanitizer_get_free_bytes)
INTERFACE_FUNCTION(__sanitizer_get_heap_size) INTERFACE_FUNCTION(__sanitizer_get_heap_size)
INTERFACE_FUNCTION(__sanitizer_get_ownership) INTERFACE_FUNCTION(__sanitizer_get_ownership)
INTERFACE_FUNCTION(__sanitizer_get_total_unique_caller_callee_pairs)
INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage) INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes) INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file) INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)

View File

@ -22,6 +22,7 @@
// Using #ifdef rather than relying on Makefiles etc. // Using #ifdef rather than relying on Makefiles etc.
// simplifies the build procedure. // simplifies the build procedure.
#ifdef ASAN_DYNAMIC_RUNTIME_THUNK #ifdef ASAN_DYNAMIC_RUNTIME_THUNK
#define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
// First, declare CRT sections we'll be using in this file // First, declare CRT sections we'll be using in this file

View File

@ -37,6 +37,8 @@ case "${target}" in
aarch64*-*-linux*) aarch64*-*-linux*)
if test x$ac_cv_sizeof_void_p = x8; then if test x$ac_cv_sizeof_void_p = x8; then
TSAN_SUPPORTED=yes TSAN_SUPPORTED=yes
LSAN_SUPPORTED=yes
TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_aarch64.lo
fi fi
;; ;;
x86_64-*-darwin[1]* | i?86-*-darwin[1]*) x86_64-*-darwin[1]* | i?86-*-darwin[1]*)

View File

@ -103,6 +103,12 @@ extern "C" {
int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
const void *end); const void *end);
// Similar to __sanitizer_verify_contiguous_container but returns the address
// of the first improperly poisoned byte otherwise. Returns null if the area
// is poisoned properly.
const void *__sanitizer_contiguous_container_find_bad_address(
const void *beg, const void *mid, const void *end);
// Print the stack trace leading to this call. Useful for debugging user code. // Print the stack trace leading to this call. Useful for debugging user code.
void __sanitizer_print_stack_trace(); void __sanitizer_print_stack_trace();

View File

@ -25,9 +25,11 @@ extern "C" {
// descriptor. Returns -1 on failure, or if coverage dumping is disabled. // descriptor. Returns -1 on failure, or if coverage dumping is disabled.
// This is intended for use by sandboxing code. // This is intended for use by sandboxing code.
intptr_t __sanitizer_maybe_open_cov_file(const char *name); intptr_t __sanitizer_maybe_open_cov_file(const char *name);
// Get the number of total unique covered entities (blocks, edges, calls). // Get the number of unique covered blocks (or edges).
// This can be useful for coverage-directed in-process fuzzers. // This can be useful for coverage-directed in-process fuzzers.
uintptr_t __sanitizer_get_total_unique_coverage(); uintptr_t __sanitizer_get_total_unique_coverage();
// Get the number of unique indirect caller-callee pairs.
uintptr_t __sanitizer_get_total_unique_caller_callee_pairs();
// Reset the basic-block (edge) coverage to the initial state. // Reset the basic-block (edge) coverage to the initial state.
// Useful for in-process fuzzing to start collecting coverage from scratch. // Useful for in-process fuzzing to start collecting coverage from scratch.

View File

@ -13,6 +13,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include "interception.h" #include "interception.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
namespace __interception { namespace __interception {

View File

@ -30,7 +30,7 @@ struct ChunkMetadata {
u32 stack_trace_id; u32 stack_trace_id;
}; };
#if defined(__mips64) #if defined(__mips64) || defined(__aarch64__)
static const uptr kMaxAllowedMallocSize = 4UL << 30; static const uptr kMaxAllowedMallocSize = 4UL << 30;
static const uptr kRegionSizeLog = 20; static const uptr kRegionSizeLog = 20;
static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;

View File

@ -117,6 +117,10 @@ static inline bool CanBeAHeapPointer(uptr p) {
return ((p >> 47) == 0); return ((p >> 47) == 0);
#elif defined(__mips64) #elif defined(__mips64)
return ((p >> 40) == 0); return ((p >> 40) == 0);
#elif defined(__aarch64__)
unsigned runtimeVMA =
(MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
return ((p >> runtimeVMA) == 0);
#else #else
return true; return true;
#endif #endif

View File

@ -20,8 +20,8 @@
#include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_stoptheworld.h"
#include "sanitizer_common/sanitizer_symbolizer.h" #include "sanitizer_common/sanitizer_symbolizer.h"
#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips64)) \ #if (SANITIZER_LINUX && !SANITIZER_ANDROID) && (SANITIZER_WORDSIZE == 64) \
&& (SANITIZER_WORDSIZE == 64) && (defined(__x86_64__) || defined(__mips64) || defined(__aarch64__))
#define CAN_SANITIZE_LEAKS 1 #define CAN_SANITIZE_LEAKS 1
#else #else
#define CAN_SANITIZE_LEAKS 0 #define CAN_SANITIZE_LEAKS 0

View File

@ -21,8 +21,11 @@
# define CFI_STARTPROC .cfi_startproc # define CFI_STARTPROC .cfi_startproc
# define CFI_ENDPROC .cfi_endproc # define CFI_ENDPROC .cfi_endproc
# define CFI_ADJUST_CFA_OFFSET(n) .cfi_adjust_cfa_offset n # define CFI_ADJUST_CFA_OFFSET(n) .cfi_adjust_cfa_offset n
# define CFI_DEF_CFA_OFFSET(n) .cfi_def_cfa_offset n
# define CFI_REL_OFFSET(reg, n) .cfi_rel_offset reg, n # define CFI_REL_OFFSET(reg, n) .cfi_rel_offset reg, n
# define CFI_OFFSET(reg, n) .cfi_offset reg, n
# define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg # define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg
# define CFI_DEF_CFA(reg, n) .cfi_def_cfa reg, n
# define CFI_RESTORE(reg) .cfi_restore reg # define CFI_RESTORE(reg) .cfi_restore reg
#else // No CFI #else // No CFI
@ -30,7 +33,10 @@
# define CFI_STARTPROC # define CFI_STARTPROC
# define CFI_ENDPROC # define CFI_ENDPROC
# define CFI_ADJUST_CFA_OFFSET(n) # define CFI_ADJUST_CFA_OFFSET(n)
# define CFI_DEF_CFA_OFFSET(n)
# define CFI_REL_OFFSET(reg, n) # define CFI_REL_OFFSET(reg, n)
# define CFI_OFFSET(reg, n)
# define CFI_DEF_CFA_REGISTER(reg) # define CFI_DEF_CFA_REGISTER(reg)
# define CFI_DEF_CFA(reg, n)
# define CFI_RESTORE(reg) # define CFI_RESTORE(reg)
#endif #endif

View File

@ -162,7 +162,7 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
} }
void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type, void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
error_t err) { const char *mmap_type, error_t err) {
static int recursion_count; static int recursion_count;
if (recursion_count) { if (recursion_count) {
// The Report() and CHECK calls below may call mmap recursively and fail. // The Report() and CHECK calls below may call mmap recursively and fail.
@ -172,9 +172,11 @@ void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
} }
recursion_count++; recursion_count++;
Report("ERROR: %s failed to " Report("ERROR: %s failed to "
"allocate 0x%zx (%zd) bytes of %s (error code: %d)\n", "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
SanitizerToolName, size, size, mem_type, err); SanitizerToolName, mmap_type, size, size, mem_type, err);
#ifndef SANITIZER_GO
DumpProcessMap(); DumpProcessMap();
#endif
UNREACHABLE("unable to mmap"); UNREACHABLE("unable to mmap");
} }

View File

@ -307,7 +307,7 @@ void NORETURN Die();
void NORETURN void NORETURN
CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2);
void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type, void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
error_t err); const char *mmap_type, error_t err);
// Set the name of the current thread to 'name', return true on succees. // Set the name of the current thread to 'name', return true on succees.
// The name may be truncated to a system-dependent limit. // The name may be truncated to a system-dependent limit.

View File

@ -439,6 +439,8 @@ INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
#if SANITIZER_INTERCEPT_MEMCHR #if SANITIZER_INTERCEPT_MEMCHR
INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) { INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) {
if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
return internal_memchr(s, c, n);
void *ctx; void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n); COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n);
void *res = REAL(memchr)(s, c, n); void *res = REAL(memchr)(s, c, n);
@ -2444,6 +2446,7 @@ INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry,
INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
void *ctx; void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data); COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data);
__sanitizer_iovec local_iovec;
if (data) { if (data) {
if (request == ptrace_setregs) if (request == ptrace_setregs)
@ -2452,11 +2455,19 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz); COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz);
else if (request == ptrace_setfpxregs) else if (request == ptrace_setfpxregs)
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz); COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
else if (request == ptrace_setvfpregs)
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
else if (request == ptrace_setsiginfo) else if (request == ptrace_setsiginfo)
COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz); COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz);
else if (request == ptrace_setregset) { // Some kernel might zero the iovec::iov_base in case of invalid
__sanitizer_iovec *iov = (__sanitizer_iovec *)data; // write access. In this case copy the invalid address for further
COMMON_INTERCEPTOR_READ_RANGE(ctx, iov->iov_base, iov->iov_len); // inspection.
else if (request == ptrace_setregset || request == ptrace_getregset) {
__sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec));
local_iovec = *iovec;
if (request == ptrace_setregset)
COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec->iov_base, iovec->iov_len);
} }
} }
@ -2474,13 +2485,17 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz);
else if (request == ptrace_getfpxregs) else if (request == ptrace_getfpxregs)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz);
else if (request == ptrace_getvfpregs)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_vfpregs_struct_sz);
else if (request == ptrace_getsiginfo) else if (request == ptrace_getsiginfo)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz);
else if (request == ptrace_geteventmsg) else if (request == ptrace_geteventmsg)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long));
else if (request == ptrace_getregset) { else if (request == ptrace_getregset) {
__sanitizer_iovec *iov = (__sanitizer_iovec *)data; __sanitizer_iovec *iovec = (__sanitizer_iovec*)data;
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iov->iov_base, iov->iov_len); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec, sizeof(*iovec));
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, local_iovec.iov_base,
local_iovec.iov_len);
} }
} }
return res; return res;
@ -4874,9 +4889,8 @@ static void initialize_obstack(__sanitizer_obstack *obstack) {
sizeof(*obstack->chunk)); sizeof(*obstack->chunk));
} }
INTERCEPTOR(int, _obstack_begin_1, __sanitizer_obstack *obstack, INTERCEPTOR(int, _obstack_begin_1, __sanitizer_obstack *obstack, int sz,
_OBSTACK_SIZE_T sz, _OBSTACK_SIZE_T align, int align, void *(*alloc_fn)(uptr arg, uptr sz),
void *(*alloc_fn)(uptr arg, SIZE_T sz),
void (*free_fn)(uptr arg, void *p)) { void (*free_fn)(uptr arg, void *p)) {
void *ctx; void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin_1, obstack, sz, align, alloc_fn, COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin_1, obstack, sz, align, alloc_fn,
@ -4885,10 +4899,8 @@ INTERCEPTOR(int, _obstack_begin_1, __sanitizer_obstack *obstack,
if (res) initialize_obstack(obstack); if (res) initialize_obstack(obstack);
return res; return res;
} }
INTERCEPTOR(int, _obstack_begin, __sanitizer_obstack *obstack, INTERCEPTOR(int, _obstack_begin, __sanitizer_obstack *obstack, int sz,
_OBSTACK_SIZE_T sz, _OBSTACK_SIZE_T align, int align, void *(*alloc_fn)(uptr sz), void (*free_fn)(void *p)) {
void *(*alloc_fn)(SIZE_T sz),
void (*free_fn)(void *p)) {
void *ctx; void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin, obstack, sz, align, alloc_fn, COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin, obstack, sz, align, alloc_fn,
free_fn); free_fn);
@ -4896,8 +4908,7 @@ INTERCEPTOR(int, _obstack_begin, __sanitizer_obstack *obstack,
if (res) initialize_obstack(obstack); if (res) initialize_obstack(obstack);
return res; return res;
} }
INTERCEPTOR(void, _obstack_newchunk, __sanitizer_obstack *obstack, INTERCEPTOR(void, _obstack_newchunk, __sanitizer_obstack *obstack, int length) {
_OBSTACK_SIZE_T length) {
void *ctx; void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, _obstack_newchunk, obstack, length); COMMON_INTERCEPTOR_ENTER(ctx, _obstack_newchunk, obstack, length);
REAL(_obstack_newchunk)(obstack, length); REAL(_obstack_newchunk)(obstack, length);
@ -5238,6 +5249,39 @@ INTERCEPTOR(int, mincore, void *addr, uptr length, unsigned char *vec) {
#define INIT_MINCORE #define INIT_MINCORE
#endif #endif
#if SANITIZER_INTERCEPT_PROCESS_VM_READV
INTERCEPTOR(SSIZE_T, process_vm_readv, int pid, __sanitizer_iovec *local_iov,
uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt,
uptr flags) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, process_vm_readv, pid, local_iov, liovcnt,
remote_iov, riovcnt, flags);
SSIZE_T res = REAL(process_vm_readv)(pid, local_iov, liovcnt, remote_iov,
riovcnt, flags);
if (res > 0)
write_iovec(ctx, local_iov, liovcnt, res);
return res;
}
INTERCEPTOR(SSIZE_T, process_vm_writev, int pid, __sanitizer_iovec *local_iov,
uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt,
uptr flags) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, process_vm_writev, pid, local_iov, liovcnt,
remote_iov, riovcnt, flags);
SSIZE_T res = REAL(process_vm_writev)(pid, local_iov, liovcnt, remote_iov,
riovcnt, flags);
if (res > 0)
read_iovec(ctx, local_iov, liovcnt, res);
return res;
}
#define INIT_PROCESS_VM_READV \
COMMON_INTERCEPT_FUNCTION(process_vm_readv); \
COMMON_INTERCEPT_FUNCTION(process_vm_writev);
#else
#define INIT_PROCESS_VM_READV
#endif
static void InitializeCommonInterceptors() { static void InitializeCommonInterceptors() {
static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
@ -5411,4 +5455,5 @@ static void InitializeCommonInterceptors() {
INIT_SEM; INIT_SEM;
INIT_PTHREAD_SETCANCEL; INIT_PTHREAD_SETCANCEL;
INIT_MINCORE; INIT_MINCORE;
INIT_PROCESS_VM_READV;
} }

View File

@ -51,6 +51,12 @@ static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL;
static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once. static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
static atomic_uintptr_t coverage_counter; static atomic_uintptr_t coverage_counter;
static atomic_uintptr_t caller_callee_counter;
static void ResetGlobalCounters() {
return atomic_store(&coverage_counter, 0, memory_order_relaxed);
return atomic_store(&caller_callee_counter, 0, memory_order_relaxed);
}
// pc_array is the array containing the covered PCs. // 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. // To make the pc_array thread- and async-signal-safe it has to be large enough.
@ -223,7 +229,8 @@ void CoverageData::InitializeGuardArray(s32 *guards) {
Enable(); // Make sure coverage is enabled at this point. Enable(); // Make sure coverage is enabled at this point.
s32 n = guards[0]; s32 n = guards[0];
for (s32 j = 1; j <= n; j++) { for (s32 j = 1; j <= n; j++) {
uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); uptr idx = atomic_load_relaxed(&pc_array_index);
atomic_store_relaxed(&pc_array_index, idx + 1);
guards[j] = -static_cast<s32>(idx + 1); guards[j] = -static_cast<s32>(idx + 1);
} }
} }
@ -433,7 +440,7 @@ void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[],
uptr was = 0; uptr was = 0;
if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee, 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); atomic_fetch_add(&caller_callee_counter, 1, memory_order_relaxed);
return; return;
} }
if (was == callee) // Already have this callee. if (was == callee) // Already have this callee.
@ -905,6 +912,11 @@ uptr __sanitizer_get_total_unique_coverage() {
return atomic_load(&coverage_counter, memory_order_relaxed); return atomic_load(&coverage_counter, memory_order_relaxed);
} }
SANITIZER_INTERFACE_ATTRIBUTE
uptr __sanitizer_get_total_unique_caller_callee_pairs() {
return atomic_load(&caller_callee_counter, memory_order_relaxed);
}
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_cov_trace_func_enter(s32 *id) { void __sanitizer_cov_trace_func_enter(s32 *id) {
coverage_data.TraceBasicBlock(id); coverage_data.TraceBasicBlock(id);
@ -915,6 +927,7 @@ void __sanitizer_cov_trace_basic_block(s32 *id) {
} }
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_reset_coverage() { void __sanitizer_reset_coverage() {
ResetGlobalCounters();
coverage_data.ReinitializeGuards(); coverage_data.ReinitializeGuards();
internal_bzero_aligned16( internal_bzero_aligned16(
coverage_data.data(), coverage_data.data(),

View File

@ -51,6 +51,9 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
int __sanitizer_verify_contiguous_container(const void *beg, const void *mid, int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
const void *end); const void *end);
} // extern "C" SANITIZER_INTERFACE_ATTRIBUTE
const void *__sanitizer_contiguous_container_find_bad_address(
const void *beg, const void *mid, const void *end);
} // extern "C"
#endif // SANITIZER_INTERFACE_INTERNAL_H #endif // SANITIZER_INTERFACE_INTERNAL_H

View File

@ -7,7 +7,7 @@
#include "sanitizer_platform.h" #include "sanitizer_platform.h"
#if SANITIZER_FREEBSD || SANITIZER_LINUX #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC
#include "sanitizer_libignore.h" #include "sanitizer_libignore.h"
#include "sanitizer_flags.h" #include "sanitizer_flags.h"

View File

@ -495,7 +495,7 @@ void BlockingMutex::CheckLocked() {
// Note that getdents64 uses a different structure format. We only provide the // Note that getdents64 uses a different structure format. We only provide the
// 32-bit syscall here. // 32-bit syscall here.
struct linux_dirent { struct linux_dirent {
#if SANITIZER_X32 #if SANITIZER_X32 || defined(__aarch64__)
u64 d_ino; u64 d_ino;
u64 d_off; u64 d_off;
#else #else
@ -503,6 +503,9 @@ struct linux_dirent {
unsigned long d_off; unsigned long d_off;
#endif #endif
unsigned short d_reclen; unsigned short d_reclen;
#ifdef __aarch64__
unsigned char d_type;
#endif
char d_name[256]; char d_name[256];
}; };

View File

@ -60,20 +60,6 @@
namespace __sanitizer { namespace __sanitizer {
// This function is defined elsewhere if we intercepted pthread_attr_getstack.
extern "C" {
SANITIZER_WEAK_ATTRIBUTE int
real_pthread_attr_getstack(void *attr, void **addr, size_t *size);
} // extern "C"
static int my_pthread_attr_getstack(void *attr, void **addr, size_t *size) {
#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);
}
SANITIZER_WEAK_ATTRIBUTE int SANITIZER_WEAK_ATTRIBUTE int
real_sigaction(int signum, const void *act, void *oldact); real_sigaction(int signum, const void *act, void *oldact);
@ -126,7 +112,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
uptr stacksize = 0; uptr stacksize = 0;
void *stackaddr = nullptr; void *stackaddr = nullptr;
my_pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); my_pthread_attr_getstack(&attr, &stackaddr, &stacksize);
pthread_attr_destroy(&attr); pthread_attr_destroy(&attr);
CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check. CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check.
@ -178,11 +164,15 @@ static uptr g_tls_size;
# define DL_INTERNAL_FUNCTION # define DL_INTERNAL_FUNCTION
#endif #endif
#if defined(__mips__) #if defined(__mips__) || defined(__powerpc64__)
// TlsPreTcbSize includes size of struct pthread_descr and size of tcb // TlsPreTcbSize includes size of struct pthread_descr and size of tcb
// head structure. It lies before the static tls blocks. // head structure. It lies before the static tls blocks.
static uptr TlsPreTcbSize() { static uptr TlsPreTcbSize() {
const uptr kTcbHead = 16; # if defined(__mips__)
const uptr kTcbHead = 16; // sizeof (tcbhead_t)
# elif defined(__powerpc64__)
const uptr kTcbHead = 88; // sizeof (tcbhead_t)
# endif
const uptr kTlsAlign = 16; const uptr kTlsAlign = 16;
const uptr kTlsPreTcbSize = const uptr kTlsPreTcbSize =
(ThreadDescriptorSize() + kTcbHead + kTlsAlign - 1) & ~(kTlsAlign - 1); (ThreadDescriptorSize() + kTcbHead + kTlsAlign - 1) & ~(kTlsAlign - 1);
@ -213,9 +203,9 @@ void InitTlsSize() {
} }
#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) \ #if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) \
|| defined(__aarch64__)) \ || defined(__aarch64__) || defined(__powerpc64__)) \
&& SANITIZER_LINUX && !SANITIZER_ANDROID && SANITIZER_LINUX && !SANITIZER_ANDROID
// sizeof(struct thread) from glibc. // sizeof(struct pthread) from glibc.
static atomic_uintptr_t kThreadDescriptorSize; static atomic_uintptr_t kThreadDescriptorSize;
uptr ThreadDescriptorSize() { uptr ThreadDescriptorSize() {
@ -230,7 +220,7 @@ uptr ThreadDescriptorSize() {
char *end; char *end;
int minor = internal_simple_strtoll(buf + 8, &end, 10); int minor = internal_simple_strtoll(buf + 8, &end, 10);
if (end != buf + 8 && (*end == '\0' || *end == '.')) { if (end != buf + 8 && (*end == '\0' || *end == '.')) {
/* sizeof(struct thread) values from various glibc versions. */ /* sizeof(struct pthread) values from various glibc versions. */
if (SANITIZER_X32) if (SANITIZER_X32)
val = 1728; // Assume only one particular version for x32. val = 1728; // Assume only one particular version for x32.
else if (minor <= 3) else if (minor <= 3)
@ -266,6 +256,10 @@ uptr ThreadDescriptorSize() {
val = 1776; val = 1776;
atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed); atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
return val; return val;
#elif defined(__powerpc64__)
val = 1776; // from glibc.ppc64le 2.20-8.fc21
atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
return val;
#endif #endif
return 0; return 0;
} }
@ -297,6 +291,15 @@ uptr ThreadSelf() {
descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize(); descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
# elif defined(__aarch64__) # elif defined(__aarch64__)
descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer()); descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer());
# elif defined(__powerpc64__)
// PPC64LE uses TLS variant I. The thread pointer (in GPR 13)
// 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("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset));
descr_addr = thread_pointer - TlsPreTcbSize();
# else # else
# error "unsupported CPU arch" # error "unsupported CPU arch"
# endif # endif
@ -332,7 +335,7 @@ static void GetTls(uptr *addr, uptr *size) {
*size = GetTlsSize(); *size = GetTlsSize();
*addr -= *size; *addr -= *size;
*addr += ThreadDescriptorSize(); *addr += ThreadDescriptorSize();
# elif defined(__mips__) || defined(__aarch64__) # elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__)
*addr = ThreadSelf(); *addr = ThreadSelf();
*size = GetTlsSize(); *size = GetTlsSize();
# else # else
@ -398,33 +401,6 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
#endif #endif
} }
#if !SANITIZER_GO
void AdjustStackSize(void *attr_) {
pthread_attr_t *attr = (pthread_attr_t *)attr_;
uptr stackaddr = 0;
size_t stacksize = 0;
my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize);
// GLibC will return (0 - stacksize) as the stack address in the case when
// stacksize is set, but stackaddr is not.
bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0);
// We place a lot of tool data into TLS, account for that.
const uptr minstacksize = GetTlsSize() + 128*1024;
if (stacksize < minstacksize) {
if (!stack_set) {
if (stacksize != 0) {
VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize,
minstacksize);
pthread_attr_setstacksize(attr, minstacksize);
}
} else {
Printf("Sanitizer: pre-allocated stack size is insufficient: "
"%zu < %zu\n", stacksize, minstacksize);
Printf("Sanitizer: pthread_create is likely to fail.\n");
}
}
}
#endif // !SANITIZER_GO
# if !SANITIZER_FREEBSD # if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr; typedef ElfW(Phdr) Elf_Phdr;
# elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2 # elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2

View File

@ -39,6 +39,7 @@ extern char **environ;
#include <libkern/OSAtomic.h> #include <libkern/OSAtomic.h>
#include <mach-o/dyld.h> #include <mach-o/dyld.h>
#include <mach/mach.h> #include <mach/mach.h>
#include <mach/vm_statistics.h>
#include <pthread.h> #include <pthread.h>
#include <sched.h> #include <sched.h>
#include <signal.h> #include <signal.h>
@ -57,6 +58,7 @@ namespace __sanitizer {
// ---------------------- sanitizer_libc.h // ---------------------- sanitizer_libc.h
uptr internal_mmap(void *addr, size_t length, int prot, int flags, uptr internal_mmap(void *addr, size_t length, int prot, int flags,
int fd, u64 offset) { int fd, u64 offset) {
if (fd == -1) fd = VM_MAKE_TAG(VM_MEMORY_ANALYSIS_TOOL);
return (uptr)mmap(addr, length, prot, flags, fd, offset); return (uptr)mmap(addr, length, prot, flags, fd, offset);
} }
@ -367,8 +369,18 @@ uptr GetRSS() {
return info.resident_size; return info.resident_size;
} }
void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } void *internal_start_thread(void(*func)(void *arg), void *arg) {
void internal_join_thread(void *th) { } // Start the thread with signals blocked, otherwise it can steal user signals.
__sanitizer_sigset_t set, old;
internal_sigfillset(&set);
internal_sigprocmask(SIG_SETMASK, &set, &old);
pthread_t th;
pthread_create(&th, 0, (void*(*)(void *arg))func, arg);
internal_sigprocmask(SIG_SETMASK, &old, 0);
return th;
}
void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); }
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
ucontext_t *ucontext = (ucontext_t*)context; ucontext_t *ucontext = (ucontext_t*)context;

View File

@ -0,0 +1,337 @@
//===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains Mac-specific malloc interceptors and a custom zone
// implementation, which together replace the system allocator.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
#if !SANITIZER_MAC
#error "This file should only be compiled on Darwin."
#endif
#include <AvailabilityMacros.h>
#include <CoreFoundation/CFBase.h>
#include <dlfcn.h>
#include <malloc/malloc.h>
#include <sys/mman.h>
#include "interception/interception.h"
#include "sanitizer_common/sanitizer_mac.h"
// Similar code is used in Google Perftools,
// http://code.google.com/p/google-perftools.
static malloc_zone_t sanitizer_zone;
INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
vm_size_t start_size, unsigned zone_flags) {
COMMON_MALLOC_ENTER();
uptr page_size = GetPageSizeCached();
uptr allocated_size = RoundUpTo(sizeof(sanitizer_zone), page_size);
COMMON_MALLOC_MEMALIGN(page_size, allocated_size);
malloc_zone_t *new_zone = (malloc_zone_t *)p;
internal_memcpy(new_zone, &sanitizer_zone, sizeof(sanitizer_zone));
new_zone->zone_name = NULL; // The name will be changed anyway.
if (GetMacosVersion() >= MACOS_VERSION_LION) {
// Prevent the client app from overwriting the zone contents.
// Library functions that need to modify the zone will set PROT_WRITE on it.
// This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
mprotect(new_zone, allocated_size, PROT_READ);
}
return new_zone;
}
INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
COMMON_MALLOC_ENTER();
return &sanitizer_zone;
}
INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
// FIXME: ASan should support purgeable allocations.
// https://code.google.com/p/address-sanitizer/issues/detail?id=139
COMMON_MALLOC_ENTER();
return &sanitizer_zone;
}
INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
// for now.
COMMON_MALLOC_ENTER();
}
INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
// for now.
COMMON_MALLOC_ENTER();
// Must return 0 if the contents were not purged since the last call to
// malloc_make_purgeable().
return 0;
}
INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
COMMON_MALLOC_ENTER();
// Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)|
// bytes.
size_t buflen =
sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0);
InternalScopedString new_name(buflen);
if (name && zone->introspect == sanitizer_zone.introspect) {
new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name);
name = new_name.data();
}
// Call the system malloc's implementation for both external and our zones,
// since that appropriately changes VM region protections on the zone.
REAL(malloc_set_zone_name)(zone, name);
}
INTERCEPTOR(void *, malloc, size_t size) {
COMMON_MALLOC_ENTER();
COMMON_MALLOC_MALLOC(size);
return p;
}
INTERCEPTOR(void, free, void *ptr) {
COMMON_MALLOC_ENTER();
if (!ptr) return;
COMMON_MALLOC_FREE(ptr);
}
INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
COMMON_MALLOC_ENTER();
COMMON_MALLOC_REALLOC(ptr, size);
return p;
}
INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
COMMON_MALLOC_ENTER();
COMMON_MALLOC_CALLOC(nmemb, size);
return p;
}
INTERCEPTOR(void *, valloc, size_t size) {
COMMON_MALLOC_ENTER();
COMMON_MALLOC_VALLOC(size);
return p;
}
INTERCEPTOR(size_t, malloc_good_size, size_t size) {
COMMON_MALLOC_ENTER();
return sanitizer_zone.introspect->good_size(&sanitizer_zone, size);
}
INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
COMMON_MALLOC_ENTER();
CHECK(memptr);
COMMON_MALLOC_MEMALIGN(alignment, size);
if (p) {
*memptr = p;
return 0;
}
return -1;
}
namespace {
// TODO(glider): the __sanitizer_mz_* functions should be united with the Linux
// wrappers, as they are basically copied from there.
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
size_t __sanitizer_mz_size(malloc_zone_t* zone, const void* ptr) {
COMMON_MALLOC_SIZE(ptr);
return size;
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__sanitizer_mz_malloc(malloc_zone_t *zone, uptr size) {
COMMON_MALLOC_ENTER();
COMMON_MALLOC_MALLOC(size);
return p;
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__sanitizer_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
if (UNLIKELY(!COMMON_MALLOC_SANITIZER_INITIALIZED)) {
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
const size_t kCallocPoolSize = 1024;
static uptr calloc_memory_for_dlsym[kCallocPoolSize];
static size_t allocated;
size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
void *mem = (void*)&calloc_memory_for_dlsym[allocated];
allocated += size_in_words;
CHECK(allocated < kCallocPoolSize);
return mem;
}
COMMON_MALLOC_CALLOC(nmemb, size);
return p;
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__sanitizer_mz_valloc(malloc_zone_t *zone, size_t size) {
COMMON_MALLOC_ENTER();
COMMON_MALLOC_VALLOC(size);
return p;
}
#define GET_ZONE_FOR_PTR(ptr) \
malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
void ALWAYS_INLINE free_common(void *context, void *ptr) {
if (!ptr) return;
// FIXME: need to retire this flag.
if (!COMMON_MALLOC_IGNORE_INVALID_FREE) {
COMMON_MALLOC_FREE(ptr);
} else {
GET_ZONE_FOR_PTR(ptr);
COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name);
}
}
// TODO(glider): the allocation callbacks need to be refactored.
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_mz_free(malloc_zone_t *zone, void *ptr) {
free_common(zone, ptr);
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__sanitizer_mz_realloc(malloc_zone_t *zone, void *ptr, size_t new_size) {
if (!ptr) {
COMMON_MALLOC_MALLOC(new_size);
return p;
} else {
COMMON_MALLOC_SIZE(ptr);
if (size) {
COMMON_MALLOC_REALLOC(ptr, new_size);
return p;
} else {
// We can't recover from reallocating an unknown address, because
// this would require reading at most |new_size| bytes from
// potentially unaccessible memory.
GET_ZONE_FOR_PTR(ptr);
COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name);
return nullptr;
}
}
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_mz_destroy(malloc_zone_t* zone) {
// A no-op -- we will not be destroyed!
Report("__sanitizer_mz_destroy() called -- ignoring\n");
}
extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
COMMON_MALLOC_ENTER();
COMMON_MALLOC_MEMALIGN(align, size);
return p;
}
// This function is currently unused, and we build with -Werror.
#if 0
void __sanitizer_mz_free_definite_size(
malloc_zone_t* zone, void *ptr, size_t size) {
// TODO(glider): check that |size| is valid.
UNIMPLEMENTED();
}
#endif
kern_return_t mi_enumerator(task_t task, void *,
unsigned type_mask, vm_address_t zone_address,
memory_reader_t reader,
vm_range_recorder_t recorder) {
// Should enumerate all the pointers we have. Seems like a lot of work.
return KERN_FAILURE;
}
size_t mi_good_size(malloc_zone_t *zone, size_t size) {
// I think it's always safe to return size, but we maybe could do better.
return size;
}
boolean_t mi_check(malloc_zone_t *zone) {
UNIMPLEMENTED();
}
void mi_print(malloc_zone_t *zone, boolean_t verbose) {
UNIMPLEMENTED();
}
void mi_log(malloc_zone_t *zone, void *address) {
// I don't think we support anything like this
}
void mi_force_lock(malloc_zone_t *zone) {
COMMON_MALLOC_FORCE_LOCK();
}
void mi_force_unlock(malloc_zone_t *zone) {
COMMON_MALLOC_FORCE_UNLOCK();
}
void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
COMMON_MALLOC_FILL_STATS(zone, stats);
}
boolean_t mi_zone_locked(malloc_zone_t *zone) {
// UNIMPLEMENTED();
return false;
}
} // unnamed namespace
namespace COMMON_MALLOC_NAMESPACE {
void ReplaceSystemMalloc() {
static malloc_introspection_t sanitizer_zone_introspection;
// Ok to use internal_memset, these places are not performance-critical.
internal_memset(&sanitizer_zone_introspection, 0,
sizeof(sanitizer_zone_introspection));
sanitizer_zone_introspection.enumerator = &mi_enumerator;
sanitizer_zone_introspection.good_size = &mi_good_size;
sanitizer_zone_introspection.check = &mi_check;
sanitizer_zone_introspection.print = &mi_print;
sanitizer_zone_introspection.log = &mi_log;
sanitizer_zone_introspection.force_lock = &mi_force_lock;
sanitizer_zone_introspection.force_unlock = &mi_force_unlock;
sanitizer_zone_introspection.statistics = &mi_statistics;
sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
// Use version 6 for OSX >= 10.6.
sanitizer_zone.version = 6;
sanitizer_zone.zone_name = COMMON_MALLOC_ZONE_NAME;
sanitizer_zone.size = &__sanitizer_mz_size;
sanitizer_zone.malloc = &__sanitizer_mz_malloc;
sanitizer_zone.calloc = &__sanitizer_mz_calloc;
sanitizer_zone.valloc = &__sanitizer_mz_valloc;
sanitizer_zone.free = &__sanitizer_mz_free;
sanitizer_zone.realloc = &__sanitizer_mz_realloc;
sanitizer_zone.destroy = &__sanitizer_mz_destroy;
sanitizer_zone.batch_malloc = 0;
sanitizer_zone.batch_free = 0;
sanitizer_zone.free_definite_size = 0;
sanitizer_zone.memalign = &__sanitizer_mz_memalign;
sanitizer_zone.introspect = &sanitizer_zone_introspection;
// Register the zone.
malloc_zone_register(&sanitizer_zone);
}
} // namespace COMMON_MALLOC_NAMESPACE

View File

@ -95,7 +95,7 @@
// For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or // For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
// change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here. // change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
#ifndef SANITIZER_CAN_USE_ALLOCATOR64 #ifndef SANITIZER_CAN_USE_ALLOCATOR64
# if defined(__mips64) || (defined(__aarch64__) && SANITIZER_AARCH64_VMA == 39) # if defined(__mips64) || defined(__aarch64__)
# define SANITIZER_CAN_USE_ALLOCATOR64 0 # define SANITIZER_CAN_USE_ALLOCATOR64 0
# else # else
# define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64) # define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64)
@ -103,16 +103,9 @@
#endif #endif
// The range of addresses which can be returned my mmap. // The range of addresses which can be returned my mmap.
// FIXME: this value should be different on different platforms, // FIXME: this value should be different on different platforms. Larger values
// e.g. on AArch64 it is most likely (1ULL << 39). Larger values will still work // will still work but will consume more memory for TwoLevelByteMap.
// but will consume more memory for TwoLevelByteMap. #if defined(__mips__)
#if defined(__aarch64__)
# 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) # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
#else #else
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)

View File

@ -131,7 +131,7 @@
#define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \ #define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
defined(__powerpc64__) || defined(__aarch64__)) defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__))
#define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID
@ -218,7 +218,7 @@
// FIXME: getline seems to be available on OSX 10.7 // FIXME: getline seems to be available on OSX 10.7
#define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD #define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD || SI_MAC
#define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \ #define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \
@ -257,6 +257,7 @@
#define SANITIZER_INTERCEPT_SEM SI_LINUX || SI_FREEBSD #define SANITIZER_INTERCEPT_SEM SI_LINUX || SI_FREEBSD
#define SANITIZER_INTERCEPT_PTHREAD_SETCANCEL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_SETCANCEL SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_MINCORE SI_LINUX #define SANITIZER_INTERCEPT_MINCORE SI_LINUX
#define SANITIZER_INTERCEPT_PROCESS_VM_READV SI_LINUX
#define SANITIZER_INTERCEPTOR_HOOKS SI_LINUX #define SANITIZER_INTERCEPTOR_HOOKS SI_LINUX

View File

@ -117,8 +117,11 @@
#if SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX || SANITIZER_FREEBSD
# include <utime.h> # include <utime.h>
# include <sys/ptrace.h> # include <sys/ptrace.h>
# if defined(__mips64) || defined(__aarch64__) # if defined(__mips64) || defined(__aarch64__) || defined(__arm__)
# include <asm/ptrace.h> # include <asm/ptrace.h>
# ifdef __arm__
typedef struct user_fpregs elf_fpregset_t;
# endif
# endif # endif
# include <semaphore.h> # include <semaphore.h>
#endif #endif
@ -302,8 +305,8 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ #if SANITIZER_LINUX && !SANITIZER_ANDROID && \
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
defined(__powerpc64__) || defined(__aarch64__)) defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__))
#if defined(__mips64) || defined(__powerpc64__) #if defined(__mips64) || defined(__powerpc64__) || defined(__arm__)
unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs); unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs);
unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t); unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t);
#elif defined(__aarch64__) #elif defined(__aarch64__)
@ -314,36 +317,51 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct);
#endif // __mips64 || __powerpc64__ || __aarch64__ #endif // __mips64 || __powerpc64__ || __aarch64__
#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \ #if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
defined(__aarch64__) defined(__aarch64__) || defined(__arm__)
unsigned struct_user_fpxregs_struct_sz = 0; unsigned struct_user_fpxregs_struct_sz = 0;
#else #else
unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
#endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ #endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ || __arm__
#ifdef __arm__
unsigned struct_user_vfpregs_struct_sz = ARM_VFPREGS_SIZE;
#else
unsigned struct_user_vfpregs_struct_sz = 0;
#endif
int ptrace_peektext = PTRACE_PEEKTEXT; int ptrace_peektext = PTRACE_PEEKTEXT;
int ptrace_peekdata = PTRACE_PEEKDATA; int ptrace_peekdata = PTRACE_PEEKDATA;
int ptrace_peekuser = PTRACE_PEEKUSER; int ptrace_peekuser = PTRACE_PEEKUSER;
#if defined(PT_GETREGS) && defined(PT_SETREGS) #if (defined(PTRACE_GETREGS) && defined(PTRACE_SETREGS)) || \
(defined(PT_GETREGS) && defined(PT_SETREGS))
int ptrace_getregs = PTRACE_GETREGS; int ptrace_getregs = PTRACE_GETREGS;
int ptrace_setregs = PTRACE_SETREGS; int ptrace_setregs = PTRACE_SETREGS;
#else #else
int ptrace_getregs = -1; int ptrace_getregs = -1;
int ptrace_setregs = -1; int ptrace_setregs = -1;
#endif #endif
#if defined(PT_GETFPREGS) && defined(PT_SETFPREGS) #if (defined(PTRACE_GETFPREGS) && defined(PTRACE_SETFPREGS)) || \
(defined(PT_GETFPREGS) && defined(PT_SETFPREGS))
int ptrace_getfpregs = PTRACE_GETFPREGS; int ptrace_getfpregs = PTRACE_GETFPREGS;
int ptrace_setfpregs = PTRACE_SETFPREGS; int ptrace_setfpregs = PTRACE_SETFPREGS;
#else #else
int ptrace_getfpregs = -1; int ptrace_getfpregs = -1;
int ptrace_setfpregs = -1; int ptrace_setfpregs = -1;
#endif #endif
#if defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS) #if (defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS)) || \
(defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS))
int ptrace_getfpxregs = PTRACE_GETFPXREGS; int ptrace_getfpxregs = PTRACE_GETFPXREGS;
int ptrace_setfpxregs = PTRACE_SETFPXREGS; int ptrace_setfpxregs = PTRACE_SETFPXREGS;
#else #else
int ptrace_getfpxregs = -1; int ptrace_getfpxregs = -1;
int ptrace_setfpxregs = -1; int ptrace_setfpxregs = -1;
#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS #endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS
#if defined(PTRACE_GETVFPREGS) && defined(PTRACE_SETVFPREGS)
int ptrace_getvfpregs = PTRACE_GETVFPREGS;
int ptrace_setvfpregs = PTRACE_SETVFPREGS;
#else
int ptrace_getvfpregs = -1;
int ptrace_setvfpregs = -1;
#endif
int ptrace_geteventmsg = PTRACE_GETEVENTMSG; int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \ #if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
(defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO)) (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))

View File

@ -779,10 +779,11 @@ namespace __sanitizer {
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \ #if SANITIZER_LINUX && !SANITIZER_ANDROID && \
(defined(__i386) || defined(__x86_64) || defined(__mips64) || \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
defined(__powerpc64__) || defined(__aarch64__)) defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__))
extern unsigned struct_user_regs_struct_sz; extern unsigned struct_user_regs_struct_sz;
extern unsigned struct_user_fpregs_struct_sz; extern unsigned struct_user_fpregs_struct_sz;
extern unsigned struct_user_fpxregs_struct_sz; extern unsigned struct_user_fpxregs_struct_sz;
extern unsigned struct_user_vfpregs_struct_sz;
extern int ptrace_peektext; extern int ptrace_peektext;
extern int ptrace_peekdata; extern int ptrace_peekdata;
@ -793,6 +794,8 @@ namespace __sanitizer {
extern int ptrace_setfpregs; extern int ptrace_setfpregs;
extern int ptrace_getfpxregs; extern int ptrace_getfpxregs;
extern int ptrace_setfpxregs; extern int ptrace_setfpxregs;
extern int ptrace_getvfpregs;
extern int ptrace_setvfpregs;
extern int ptrace_getsiginfo; extern int ptrace_getsiginfo;
extern int ptrace_setsiginfo; extern int ptrace_setsiginfo;
extern int ptrace_getregset; extern int ptrace_getregset;

View File

@ -117,7 +117,7 @@ void *MmapOrDie(uptr size, const char *mem_type) {
MAP_PRIVATE | MAP_ANON, -1, 0); MAP_PRIVATE | MAP_ANON, -1, 0);
int reserrno; int reserrno;
if (internal_iserror(res, &reserrno)) if (internal_iserror(res, &reserrno))
ReportMmapFailureAndDie(size, mem_type, reserrno); ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
IncreaseTotalMmap(size); IncreaseTotalMmap(size);
return (void *)res; return (void *)res;
} }
@ -141,12 +141,8 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
-1, 0); -1, 0);
int reserrno; int reserrno;
if (internal_iserror(p, &reserrno)) { if (internal_iserror(p, &reserrno))
Report("ERROR: %s failed to " ReportMmapFailureAndDie(size, mem_type, "allocate noreserve", reserrno);
"allocate noreserve 0x%zx (%zd) bytes for '%s' (errno: %d)\n",
SanitizerToolName, size, size, mem_type, reserrno);
CHECK("unable to mmap" && 0);
}
IncreaseTotalMmap(size); IncreaseTotalMmap(size);
return (void *)p; return (void *)p;
} }
@ -160,10 +156,10 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
-1, 0); -1, 0);
int reserrno; int reserrno;
if (internal_iserror(p, &reserrno)) { if (internal_iserror(p, &reserrno)) {
Report("ERROR: %s failed to " char mem_type[30];
"allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n", internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
SanitizerToolName, size, size, fixed_addr, reserrno); fixed_addr);
CHECK("unable to mmap" && 0); ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
} }
IncreaseTotalMmap(size); IncreaseTotalMmap(size);
return (void *)p; return (void *)p;

View File

@ -72,6 +72,8 @@ int real_pthread_join(void *th, void **ret);
} \ } \
} // namespace __sanitizer } // namespace __sanitizer
int my_pthread_attr_getstack(void *attr, void **addr, uptr *size);
int internal_sigaction(int signum, const void *act, void *oldact); int internal_sigaction(int signum, const void *act, void *oldact);
} // namespace __sanitizer } // namespace __sanitizer

View File

@ -226,7 +226,7 @@ void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
#endif #endif
} }
#if SANITIZER_ANDROID #if SANITIZER_ANDROID || SANITIZER_GO
int GetNamedMappingFd(const char *name, uptr size) { int GetNamedMappingFd(const char *name, uptr size) {
return -1; return -1;
} }
@ -274,6 +274,49 @@ void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd, return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd,
0); 0);
} }
// This function is defined elsewhere if we intercepted pthread_attr_getstack.
extern "C" {
SANITIZER_WEAK_ATTRIBUTE int
real_pthread_attr_getstack(void *attr, void **addr, size_t *size);
} // extern "C"
int my_pthread_attr_getstack(void *attr, void **addr, uptr *size) {
#if !SANITIZER_GO && !SANITIZER_MAC
if (&real_pthread_attr_getstack)
return real_pthread_attr_getstack((pthread_attr_t *)attr, addr,
(size_t *)size);
#endif
return pthread_attr_getstack((pthread_attr_t *)attr, addr, (size_t *)size);
}
#if !SANITIZER_GO
void AdjustStackSize(void *attr_) {
pthread_attr_t *attr = (pthread_attr_t *)attr_;
uptr stackaddr = 0;
uptr stacksize = 0;
my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize);
// GLibC will return (0 - stacksize) as the stack address in the case when
// stacksize is set, but stackaddr is not.
bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0);
// We place a lot of tool data into TLS, account for that.
const uptr minstacksize = GetTlsSize() + 128*1024;
if (stacksize < minstacksize) {
if (!stack_set) {
if (stacksize != 0) {
VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize,
minstacksize);
pthread_attr_setstacksize(attr, minstacksize);
}
} else {
Printf("Sanitizer: pre-allocated stack size is insufficient: "
"%zu < %zu\n", stacksize, minstacksize);
Printf("Sanitizer: pthread_create is likely to fail.\n");
}
}
}
#endif // !SANITIZER_GO
} // namespace __sanitizer } // namespace __sanitizer
#endif // SANITIZER_POSIX #endif // SANITIZER_POSIX

View File

@ -35,8 +35,14 @@ bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
return true; return true;
} }
bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
return false; Dl_info info;
int result = dladdr((const void *)addr, &info);
if (!result) return false;
const char *demangled = DemangleCXXABI(info.dli_sname);
datainfo->name = internal_strdup(demangled);
datainfo->start = (uptr)info.dli_saddr;
return true;
} }
class AtosSymbolizerProcess : public SymbolizerProcess { class AtosSymbolizerProcess : public SymbolizerProcess {
@ -88,7 +94,9 @@ static bool IsAtosErrorMessage(const char *str) {
return false; return false;
} }
static bool ParseCommandOutput(const char *str, SymbolizedStack *res) { static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
char **out_module, char **out_file, uptr *line,
uptr *start_address) {
// Trim ending newlines. // Trim ending newlines.
char *trim; char *trim;
ExtractTokenUpToDelimiter(str, "\n", &trim); ExtractTokenUpToDelimiter(str, "\n", &trim);
@ -96,7 +104,9 @@ static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
// The line from `atos` is in one of these formats: // The line from `atos` is in one of these formats:
// myfunction (in library.dylib) (sourcefile.c:17) // myfunction (in library.dylib) (sourcefile.c:17)
// myfunction (in library.dylib) + 0x1fe // myfunction (in library.dylib) + 0x1fe
// myfunction (in library.dylib) + 15
// 0xdeadbeef (in library.dylib) + 0x1fe // 0xdeadbeef (in library.dylib) + 0x1fe
// 0xdeadbeef (in library.dylib) + 15
// 0xdeadbeef (in library.dylib) // 0xdeadbeef (in library.dylib)
// 0xdeadbeef // 0xdeadbeef
@ -107,21 +117,27 @@ static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
} }
const char *rest = trim; const char *rest = trim;
char *function_name; char *symbol_name;
rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name); rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
if (internal_strncmp(function_name, "0x", 2) != 0) if (internal_strncmp(symbol_name, "0x", 2) != 0)
res->info.function = function_name; *out_name = symbol_name;
else else
InternalFree(function_name); InternalFree(symbol_name);
rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module); rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
if (rest[0] == '(') { if (rest[0] == '(') {
rest++; if (out_file) {
rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file); rest++;
char *extracted_line_number; rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number); char *extracted_line_number;
res->info.line = internal_atoll(extracted_line_number); rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
InternalFree(extracted_line_number); if (line) *line = (uptr)internal_atoll(extracted_line_number);
InternalFree(extracted_line_number);
}
} else if (rest[0] == '+') {
rest += 2;
uptr offset = internal_atoll(rest);
if (start_address) *start_address = addr - offset;
} }
InternalFree(trim); InternalFree(trim);
@ -137,15 +153,30 @@ bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
internal_snprintf(command, sizeof(command), "0x%zx\n", addr); internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
const char *buf = process_->SendCommand(command); const char *buf = process_->SendCommand(command);
if (!buf) return false; if (!buf) return false;
if (!ParseCommandOutput(buf, stack)) { uptr line;
if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
&stack->info.file, &line, nullptr)) {
process_ = nullptr;
return false;
}
stack->info.line = (int)line;
return true;
}
bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
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, addr, &info->name, &info->module, nullptr,
nullptr, &info->start)) {
process_ = nullptr; process_ = nullptr;
return false; return false;
} }
return true; return true;
} }
bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; }
} // namespace __sanitizer } // namespace __sanitizer
#endif // SANITIZER_MAC #endif // SANITIZER_MAC

View File

@ -444,14 +444,16 @@ static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) { if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
list->push_back(tool); list->push_back(tool);
} else {
VReport(2, "No internal or external symbolizer found.\n");
} }
#if SANITIZER_MAC #if SANITIZER_MAC
VReport(2, "Using dladdr symbolizer.\n"); VReport(2, "Using dladdr symbolizer.\n");
list->push_back(new(*allocator) DlAddrSymbolizer()); list->push_back(new(*allocator) DlAddrSymbolizer());
#endif // SANITIZER_MAC #endif // SANITIZER_MAC
if (list->size() == 0) {
Report("WARNING: no internal or external symbolizer found.\n");
}
} }
Symbolizer *Symbolizer::PlatformInit() { Symbolizer *Symbolizer::PlatformInit() {

View File

@ -12,6 +12,7 @@
#include "sanitizer_platform.h" #include "sanitizer_platform.h"
#if SANITIZER_WINDOWS #if SANITIZER_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include <dbghelp.h> #include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib") #pragma comment(lib, "dbghelp.lib")

View File

@ -76,6 +76,15 @@ void DTLS_Destroy() {
DTLS_Deallocate(dtls.dtv, s); DTLS_Deallocate(dtls.dtv, s);
} }
#if defined(__powerpc64__)
// This is glibc's TLS_DTV_OFFSET:
// "Dynamic thread vector pointers point 0x8000 past the start of each
// TLS block."
static const uptr kDtvOffset = 0x8000;
#else
static const uptr kDtvOffset = 0;
#endif
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) { uptr static_tls_begin, uptr static_tls_end) {
if (!common_flags()->intercept_tls_get_addr) return 0; if (!common_flags()->intercept_tls_get_addr) return 0;
@ -85,7 +94,7 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
DTLS_Resize(dso_id + 1); DTLS_Resize(dso_id + 1);
if (dtls.dtv[dso_id].beg) return 0; if (dtls.dtv[dso_id].beg) return 0;
uptr tls_size = 0; uptr tls_size = 0;
uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset; uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset;
VPrintf(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p " VPrintf(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p "
"num_live_dtls %zd\n", "num_live_dtls %zd\n",
arg, arg->dso_id, arg->offset, res, tls_beg, &tls_beg, arg, arg->dso_id, arg->offset, res, tls_beg, &tls_beg,

View File

@ -84,7 +84,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
void *MmapOrDie(uptr size, const char *mem_type) { void *MmapOrDie(uptr size, const char *mem_type) {
void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (rv == 0) if (rv == 0)
ReportMmapFailureAndDie(size, mem_type, GetLastError()); ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError());
return rv; return rv;
} }
@ -218,12 +218,14 @@ struct ModuleInfo {
uptr end_address; uptr end_address;
}; };
#ifndef SANITIZER_GO
int CompareModulesBase(const void *pl, const void *pr) { int CompareModulesBase(const void *pl, const void *pr) {
const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr; const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr;
if (l->base_address < r->base_address) if (l->base_address < r->base_address)
return -1; return -1;
return l->base_address > r->base_address; return l->base_address > r->base_address;
} }
#endif
} // namespace } // namespace
#ifndef SANITIZER_GO #ifndef SANITIZER_GO
@ -364,6 +366,7 @@ static uptr GetPreferredBase(const char *modname) {
return (uptr)pe_header->ImageBase; return (uptr)pe_header->ImageBase;
} }
#ifndef SANITIZER_GO
uptr GetListOfModules(LoadedModule *modules, uptr max_modules, uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter) { string_predicate_t filter) {
HANDLE cur_process = GetCurrentProcess(); HANDLE cur_process = GetCurrentProcess();
@ -432,7 +435,6 @@ uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
return count; return count;
}; };
#ifndef SANITIZER_GO
// We can't use atexit() directly at __asan_init time as the CRT is not fully // 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 // 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). // atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers).

View File

@ -21,6 +21,8 @@ tsan_files = \
tsan_interface_atomic.cc \ tsan_interface_atomic.cc \
tsan_interface.cc \ tsan_interface.cc \
tsan_interface_java.cc \ tsan_interface_java.cc \
tsan_libdispatch_mac.cc \
tsan_malloc_mac.cc \
tsan_md5.cc \ tsan_md5.cc \
tsan_mman.cc \ tsan_mman.cc \
tsan_mutex.cc \ tsan_mutex.cc \
@ -28,6 +30,7 @@ tsan_files = \
tsan_new_delete.cc \ tsan_new_delete.cc \
tsan_platform_linux.cc \ tsan_platform_linux.cc \
tsan_platform_mac.cc \ tsan_platform_mac.cc \
tsan_platform_posix.cc \
tsan_platform_windows.cc \ tsan_platform_windows.cc \
tsan_report.cc \ tsan_report.cc \
tsan_rtl.cc \ tsan_rtl.cc \
@ -41,7 +44,7 @@ tsan_files = \
tsan_sync.cc tsan_sync.cc
libtsan_la_SOURCES = $(tsan_files) libtsan_la_SOURCES = $(tsan_files)
EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S tsan_rtl_aarch64.S
libtsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(TSAN_TARGET_DEPENDENT_OBJECTS) libtsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(TSAN_TARGET_DEPENDENT_OBJECTS)
libtsan_la_DEPENDENCIES = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(TSAN_TARGET_DEPENDENT_OBJECTS) libtsan_la_DEPENDENCIES = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/interception/libinterception.la $(TSAN_TARGET_DEPENDENT_OBJECTS)
if LIBBACKTRACE_SUPPORTED if LIBBACKTRACE_SUPPORTED

View File

@ -107,12 +107,14 @@ am__DEPENDENCIES_1 =
am__objects_1 = tsan_clock.lo tsan_fd.lo tsan_flags.lo \ am__objects_1 = tsan_clock.lo tsan_fd.lo tsan_flags.lo \
tsan_ignoreset.lo tsan_interceptors.lo tsan_interface_ann.lo \ tsan_ignoreset.lo tsan_interceptors.lo tsan_interface_ann.lo \
tsan_interface_atomic.lo tsan_interface.lo \ tsan_interface_atomic.lo tsan_interface.lo \
tsan_interface_java.lo tsan_md5.lo tsan_mman.lo tsan_mutex.lo \ tsan_interface_java.lo tsan_libdispatch_mac.lo \
tsan_malloc_mac.lo tsan_md5.lo tsan_mman.lo tsan_mutex.lo \
tsan_mutexset.lo tsan_new_delete.lo tsan_platform_linux.lo \ tsan_mutexset.lo tsan_new_delete.lo tsan_platform_linux.lo \
tsan_platform_mac.lo tsan_platform_windows.lo tsan_report.lo \ tsan_platform_mac.lo tsan_platform_posix.lo \
tsan_rtl.lo tsan_rtl_mutex.lo tsan_rtl_report.lo \ tsan_platform_windows.lo tsan_report.lo tsan_rtl.lo \
tsan_rtl_thread.lo tsan_stack_trace.lo tsan_stat.lo \ tsan_rtl_mutex.lo tsan_rtl_report.lo tsan_rtl_thread.lo \
tsan_suppressions.lo tsan_symbolize.lo tsan_sync.lo tsan_stack_trace.lo tsan_stat.lo tsan_suppressions.lo \
tsan_symbolize.lo tsan_sync.lo
am_libtsan_la_OBJECTS = $(am__objects_1) am_libtsan_la_OBJECTS = $(am__objects_1)
libtsan_la_OBJECTS = $(am_libtsan_la_OBJECTS) libtsan_la_OBJECTS = $(am_libtsan_la_OBJECTS)
libtsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ libtsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
@ -318,6 +320,8 @@ tsan_files = \
tsan_interface_atomic.cc \ tsan_interface_atomic.cc \
tsan_interface.cc \ tsan_interface.cc \
tsan_interface_java.cc \ tsan_interface_java.cc \
tsan_libdispatch_mac.cc \
tsan_malloc_mac.cc \
tsan_md5.cc \ tsan_md5.cc \
tsan_mman.cc \ tsan_mman.cc \
tsan_mutex.cc \ tsan_mutex.cc \
@ -325,6 +329,7 @@ tsan_files = \
tsan_new_delete.cc \ tsan_new_delete.cc \
tsan_platform_linux.cc \ tsan_platform_linux.cc \
tsan_platform_mac.cc \ tsan_platform_mac.cc \
tsan_platform_posix.cc \
tsan_platform_windows.cc \ tsan_platform_windows.cc \
tsan_report.cc \ tsan_report.cc \
tsan_rtl.cc \ tsan_rtl.cc \
@ -338,7 +343,7 @@ tsan_files = \
tsan_sync.cc tsan_sync.cc
libtsan_la_SOURCES = $(tsan_files) libtsan_la_SOURCES = $(tsan_files)
EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S tsan_rtl_aarch64.S
libtsan_la_LIBADD = \ libtsan_la_LIBADD = \
$(top_builddir)/sanitizer_common/libsanitizer_common.la \ $(top_builddir)/sanitizer_common/libsanitizer_common.la \
$(top_builddir)/interception/libinterception.la \ $(top_builddir)/interception/libinterception.la \
@ -473,6 +478,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_atomic.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_atomic.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_java.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_java.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_libdispatch_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_malloc_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_md5.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_md5.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mman.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mman.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutex.Plo@am__quote@
@ -480,9 +487,11 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_new_delete.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_new_delete.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_linux.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_mac.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_mac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_windows.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_windows.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_report.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_report.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_aarch64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_amd64.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_amd64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_mutex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_mutex.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_report.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_report.Plo@am__quote@

View File

@ -88,8 +88,6 @@
namespace __tsan { namespace __tsan {
const unsigned kInvalidTid = (unsigned)-1;
ThreadClock::ThreadClock(unsigned tid, unsigned reused) ThreadClock::ThreadClock(unsigned tid, unsigned reused)
: tid_(tid) : tid_(tid)
, reused_(reused + 1) { // 0 has special meaning , reused_(reused + 1) { // 0 has special meaning

View File

@ -81,6 +81,8 @@ const bool kCollectHistory = false;
const bool kCollectHistory = true; const bool kCollectHistory = true;
#endif #endif
const unsigned kInvalidTid = (unsigned)-1;
// The following "build consistency" machinery ensures that all source files // The following "build consistency" machinery ensures that all source files
// are built in the same configuration. Inconsistent builds lead to // are built in the same configuration. Inconsistent builds lead to
// hard to debug crashes. // hard to debug crashes.

View File

@ -59,9 +59,13 @@ void InitializeFlags(Flags *f, const char *env) {
CommonFlags cf; CommonFlags cf;
cf.CopyFrom(*common_flags()); cf.CopyFrom(*common_flags());
cf.allow_addr2line = true; cf.allow_addr2line = true;
#ifndef SANITIZER_GO if (kGoMode) {
cf.detect_deadlocks = true; // Does not work as expected for Go: runtime handles SIGABRT and crashes.
#endif cf.abort_on_error = false;
// Go does not have mutexes.
} else {
cf.detect_deadlocks = true;
}
cf.print_suppressions = false; cf.print_suppressions = false;
cf.stack_trace_format = " #%n %f %S %M"; cf.stack_trace_format = " #%n %f %S %M";
cf.exitcode = 66; cf.exitcode = 66;

View File

@ -26,16 +26,28 @@
#include "tsan_mman.h" #include "tsan_mman.h"
#include "tsan_fd.h" #include "tsan_fd.h"
#if SANITIZER_POSIX
#include "sanitizer_common/sanitizer_posix.h"
#endif
using namespace __tsan; // NOLINT using namespace __tsan; // NOLINT
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD || SANITIZER_MAC
#define __errno_location __error #define __errno_location __error
#define __libc_realloc __realloc
#define __libc_calloc __calloc
#define stdout __stdoutp #define stdout __stdoutp
#define stderr __stderrp #define stderr __stderrp
#endif #endif
#if SANITIZER_FREEBSD
#define __libc_realloc __realloc
#define __libc_calloc __calloc
#elif SANITIZER_MAC
#define __libc_malloc REAL(malloc)
#define __libc_realloc REAL(realloc)
#define __libc_calloc REAL(calloc)
#define __libc_free REAL(free)
#endif
#if SANITIZER_LINUX || SANITIZER_FREEBSD #if SANITIZER_LINUX || SANITIZER_FREEBSD
#define PTHREAD_CREATE_DETACHED 1 #define PTHREAD_CREATE_DETACHED 1
#elif SANITIZER_MAC #elif SANITIZER_MAC
@ -78,12 +90,13 @@ extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize);
extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v));
extern "C" int pthread_setspecific(unsigned key, const void *v); extern "C" int pthread_setspecific(unsigned key, const void *v);
DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *) DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *)
extern "C" int pthread_yield();
extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set, extern "C" int pthread_sigmask(int how, const __sanitizer_sigset_t *set,
__sanitizer_sigset_t *oldset); __sanitizer_sigset_t *oldset);
// REAL(sigfillset) defined in common interceptors. // REAL(sigfillset) defined in common interceptors.
DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set) DECLARE_REAL(int, sigfillset, __sanitizer_sigset_t *set)
DECLARE_REAL(int, fflush, __sanitizer_FILE *fp) DECLARE_REAL(int, fflush, __sanitizer_FILE *fp)
DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size)
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
extern "C" void *pthread_self(); extern "C" void *pthread_self();
extern "C" void _exit(int status); extern "C" void _exit(int status);
extern "C" int *__errno_location(); extern "C" int *__errno_location();
@ -100,7 +113,9 @@ const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
const int EINVAL = 22; const int EINVAL = 22;
const int EBUSY = 16; const int EBUSY = 16;
const int EOWNERDEAD = 130; const int EOWNERDEAD = 130;
#if !SANITIZER_MAC
const int EPOLL_CTL_ADD = 1; const int EPOLL_CTL_ADD = 1;
#endif
const int SIGILL = 4; const int SIGILL = 4;
const int SIGABRT = 6; const int SIGABRT = 6;
const int SIGFPE = 8; const int SIGFPE = 8;
@ -115,7 +130,9 @@ const int SIGBUS = 7;
const int SIGSYS = 31; const int SIGSYS = 31;
#endif #endif
void *const MAP_FAILED = (void*)-1; void *const MAP_FAILED = (void*)-1;
#if !SANITIZER_MAC
const int PTHREAD_BARRIER_SERIAL_THREAD = -1; const int PTHREAD_BARRIER_SERIAL_THREAD = -1;
#endif
const int MAP_FIXED = 0x10; const int MAP_FIXED = 0x10;
typedef long long_t; // NOLINT typedef long long_t; // NOLINT
@ -245,17 +262,6 @@ ScopedInterceptor::~ScopedInterceptor() {
} }
} }
#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
if (REAL(func) == 0) { \
Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
Die(); \
} \
if (thr->ignore_interceptors || thr->in_ignored_lib) \
return REAL(func)(__VA_ARGS__); \
/**/
#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func) #define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func) # define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
@ -370,6 +376,7 @@ static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
return res; return res;
} }
#if !SANITIZER_MAC
static void on_exit_wrapper(int status, void *arg) { static void on_exit_wrapper(int status, void *arg) {
ThreadState *thr = cur_thread(); ThreadState *thr = cur_thread();
uptr pc = 0; uptr pc = 0;
@ -394,6 +401,7 @@ TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
ThreadIgnoreEnd(thr, pc); ThreadIgnoreEnd(thr, pc);
return res; return res;
} }
#endif
// Cleanup old bufs. // Cleanup old bufs.
static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) { static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) {
@ -430,8 +438,12 @@ static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) {
static void LongJmp(ThreadState *thr, uptr *env) { static void LongJmp(ThreadState *thr, uptr *env) {
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD
uptr mangled_sp = env[2]; uptr mangled_sp = env[2];
#else #elif defined(SANITIZER_LINUX)
# ifdef __aarch64__
uptr mangled_sp = env[13];
# else
uptr mangled_sp = env[6]; uptr mangled_sp = env[6];
# endif
#endif // SANITIZER_FREEBSD #endif // SANITIZER_FREEBSD
// Find the saved buf by mangled_sp. // Find the saved buf by mangled_sp.
for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) { for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
@ -517,6 +529,7 @@ TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) {
REAL(siglongjmp)(env, val); REAL(siglongjmp)(env, val);
} }
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(void*, malloc, uptr size) { TSAN_INTERCEPTOR(void*, malloc, uptr size) {
if (cur_thread()->in_symbolizer) if (cur_thread()->in_symbolizer)
return __libc_malloc(size); return __libc_malloc(size);
@ -583,6 +596,7 @@ TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) {
SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p); SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p);
return user_alloc_usable_size(p); return user_alloc_usable_size(p);
} }
#endif
TSAN_INTERCEPTOR(uptr, strlen, const char *s) { TSAN_INTERCEPTOR(uptr, strlen, const char *s) {
SCOPED_TSAN_INTERCEPTOR(strlen, s); SCOPED_TSAN_INTERCEPTOR(strlen, s);
@ -607,13 +621,18 @@ TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) {
MemoryAccessRange(thr, pc, (uptr)dst, size, true); MemoryAccessRange(thr, pc, (uptr)dst, size, true);
MemoryAccessRange(thr, pc, (uptr)src, size, false); MemoryAccessRange(thr, pc, (uptr)src, size, false);
} }
return internal_memcpy(dst, src, size); // On OS X, calling internal_memcpy here will cause memory corruptions,
// because memcpy and memmove are actually aliases of the same implementation.
// We need to use internal_memmove here.
return internal_memmove(dst, src, size);
} }
TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) { TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) {
SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n); if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) {
MemoryAccessRange(thr, pc, (uptr)dst, n, true); SCOPED_TSAN_INTERCEPTOR(memmove, dst, src, n);
MemoryAccessRange(thr, pc, (uptr)src, n, false); MemoryAccessRange(thr, pc, (uptr)dst, n, true);
MemoryAccessRange(thr, pc, (uptr)src, n, false);
}
return REAL(memmove)(dst, src, n); return REAL(memmove)(dst, src, n);
} }
@ -626,6 +645,7 @@ TSAN_INTERCEPTOR(char*, strchr, char *s, int c) {
return res; return res;
} }
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) { TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c); SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c);
char *res = REAL(strchrnul)(s, c); char *res = REAL(strchrnul)(s, c);
@ -633,6 +653,7 @@ TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) {
READ_STRING(thr, pc, s, len); READ_STRING(thr, pc, s, len);
return res; return res;
} }
#endif
TSAN_INTERCEPTOR(char*, strrchr, char *s, int c) { TSAN_INTERCEPTOR(char*, strrchr, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strrchr, s, c); SCOPED_TSAN_INTERCEPTOR(strrchr, s, c);
@ -676,8 +697,8 @@ static bool fix_mmap_addr(void **addr, long_t sz, int flags) {
return true; return true;
} }
TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot, TSAN_INTERCEPTOR(void *, mmap, void *addr, SIZE_T sz, int prot, int flags,
int flags, int fd, unsigned off) { int fd, OFF_T off) {
SCOPED_TSAN_INTERCEPTOR(mmap, addr, sz, prot, flags, fd, off); SCOPED_TSAN_INTERCEPTOR(mmap, addr, sz, prot, flags, fd, off);
if (!fix_mmap_addr(&addr, sz, flags)) if (!fix_mmap_addr(&addr, sz, flags))
return MAP_FAILED; return MAP_FAILED;
@ -690,9 +711,9 @@ TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot,
return res; return res;
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot, TSAN_INTERCEPTOR(void *, mmap64, void *addr, SIZE_T sz, int prot, int flags,
int flags, int fd, u64 off) { int fd, OFF64_T off) {
SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off); SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off);
if (!fix_mmap_addr(&addr, sz, flags)) if (!fix_mmap_addr(&addr, sz, flags))
return MAP_FAILED; return MAP_FAILED;
@ -720,7 +741,7 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
return res; return res;
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(memalign, align, sz); SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
return user_alloc(thr, pc, sz, align); return user_alloc(thr, pc, sz, align);
@ -730,6 +751,7 @@ TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
#define TSAN_MAYBE_INTERCEPT_MEMALIGN #define TSAN_MAYBE_INTERCEPT_MEMALIGN
#endif #endif
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) { TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(memalign, align, sz); SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
return user_alloc(thr, pc, sz, align); return user_alloc(thr, pc, sz, align);
@ -739,8 +761,9 @@ TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
SCOPED_INTERCEPTOR_RAW(valloc, sz); SCOPED_INTERCEPTOR_RAW(valloc, sz);
return user_alloc(thr, pc, sz, GetPageSizeCached()); return user_alloc(thr, pc, sz, GetPageSizeCached());
} }
#endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
SCOPED_INTERCEPTOR_RAW(pvalloc, sz); SCOPED_INTERCEPTOR_RAW(pvalloc, sz);
sz = RoundUp(sz, GetPageSizeCached()); sz = RoundUp(sz, GetPageSizeCached());
@ -751,11 +774,13 @@ TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
#define TSAN_MAYBE_INTERCEPT_PVALLOC #define TSAN_MAYBE_INTERCEPT_PVALLOC
#endif #endif
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) { TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz);
*memptr = user_alloc(thr, pc, sz, align); *memptr = user_alloc(thr, pc, sz, align);
return 0; return 0;
} }
#endif
// Used in thread-safe function static initialization. // Used in thread-safe function static initialization.
extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) { extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) {
@ -785,6 +810,19 @@ extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_abort(atomic_uint32_t *g) {
atomic_store(g, 0, memory_order_relaxed); atomic_store(g, 0, memory_order_relaxed);
} }
namespace __tsan {
void DestroyThreadState() {
ThreadState *thr = cur_thread();
ThreadFinish(thr);
ThreadSignalContext *sctx = thr->signal_ctx;
if (sctx) {
thr->signal_ctx = 0;
UnmapOrDie(sctx, sizeof(*sctx));
}
cur_thread_finalize();
}
} // namespace __tsan
static void thread_finalize(void *v) { static void thread_finalize(void *v) {
uptr iter = (uptr)v; uptr iter = (uptr)v;
if (iter > 1) { if (iter > 1) {
@ -794,15 +832,7 @@ static void thread_finalize(void *v) {
} }
return; return;
} }
{ DestroyThreadState();
ThreadState *thr = cur_thread();
ThreadFinish(thr);
ThreadSignalContext *sctx = thr->signal_ctx;
if (sctx) {
thr->signal_ctx = 0;
UnmapOrDie(sctx, sizeof(*sctx));
}
}
} }
@ -829,7 +859,7 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
} }
ThreadIgnoreEnd(thr, 0); ThreadIgnoreEnd(thr, 0);
while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
pthread_yield(); internal_sched_yield();
ThreadStart(thr, tid, GetTid()); ThreadStart(thr, tid, GetTid());
atomic_store(&p->tid, 0, memory_order_release); atomic_store(&p->tid, 0, memory_order_release);
} }
@ -889,7 +919,7 @@ TSAN_INTERCEPTOR(int, pthread_create,
// before the new thread got a chance to acquire from it in ThreadStart. // before the new thread got a chance to acquire from it in ThreadStart.
atomic_store(&p.tid, tid, memory_order_release); atomic_store(&p.tid, tid, memory_order_release);
while (atomic_load(&p.tid, memory_order_acquire) != 0) while (atomic_load(&p.tid, memory_order_acquire) != 0)
pthread_yield(); internal_sched_yield();
} }
if (attr == &myattr) if (attr == &myattr)
pthread_attr_destroy(&myattr); pthread_attr_destroy(&myattr);
@ -1092,6 +1122,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
return res; return res;
} }
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime); SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime);
int res = REAL(pthread_mutex_timedlock)(m, abstime); int res = REAL(pthread_mutex_timedlock)(m, abstime);
@ -1100,7 +1131,9 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
} }
return res; return res;
} }
#endif
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) { TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared); SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared);
int res = REAL(pthread_spin_init)(m, pshared); int res = REAL(pthread_spin_init)(m, pshared);
@ -1143,6 +1176,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) {
int res = REAL(pthread_spin_unlock)(m); int res = REAL(pthread_spin_unlock)(m);
return res; return res;
} }
#endif
TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) { TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a); SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a);
@ -1180,6 +1214,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) {
return res; return res;
} }
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) { TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime); SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime);
int res = REAL(pthread_rwlock_timedrdlock)(m, abstime); int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
@ -1188,6 +1223,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
} }
return res; return res;
} }
#endif
TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) { TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m); SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
@ -1207,6 +1243,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) {
return res; return res;
} }
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) { TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime); SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime);
int res = REAL(pthread_rwlock_timedwrlock)(m, abstime); int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
@ -1215,6 +1252,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
} }
return res; return res;
} }
#endif
TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m); SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m);
@ -1223,6 +1261,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
return res; return res;
} }
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) { TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) {
SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count); SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count);
MemoryWrite(thr, pc, (uptr)b, kSizeLog1); MemoryWrite(thr, pc, (uptr)b, kSizeLog1);
@ -1248,12 +1287,17 @@ TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) {
} }
return res; return res;
} }
#endif
TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
SCOPED_INTERCEPTOR_RAW(pthread_once, o, f); SCOPED_INTERCEPTOR_RAW(pthread_once, o, f);
if (o == 0 || f == 0) if (o == 0 || f == 0)
return EINVAL; return EINVAL;
atomic_uint32_t *a = static_cast<atomic_uint32_t*>(o); atomic_uint32_t *a;
if (!SANITIZER_MAC)
a = static_cast<atomic_uint32_t*>(o);
else // On OS X, pthread_once_t has a header with a long-sized signature.
a = static_cast<atomic_uint32_t*>((void *)((char *)o + sizeof(long_t)));
u32 v = atomic_load(a, memory_order_acquire); u32 v = atomic_load(a, memory_order_acquire);
if (v == 0 && atomic_compare_exchange_strong(a, &v, 1, if (v == 0 && atomic_compare_exchange_strong(a, &v, 1,
memory_order_relaxed)) { memory_order_relaxed)) {
@ -1263,7 +1307,7 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
atomic_store(a, 2, memory_order_release); atomic_store(a, 2, memory_order_release);
} else { } else {
while (v != 2) { while (v != 2) {
pthread_yield(); internal_sched_yield();
v = atomic_load(a, memory_order_acquire); v = atomic_load(a, memory_order_acquire);
} }
if (!thr->in_ignored_lib) if (!thr->in_ignored_lib)
@ -1272,7 +1316,7 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
return 0; return 0;
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf); SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf);
READ_STRING(thr, pc, path, 0); READ_STRING(thr, pc, path, 0);
@ -1284,7 +1328,7 @@ TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
#endif #endif
TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD || SANITIZER_MAC
SCOPED_TSAN_INTERCEPTOR(stat, path, buf); SCOPED_TSAN_INTERCEPTOR(stat, path, buf);
READ_STRING(thr, pc, path, 0); READ_STRING(thr, pc, path, 0);
return REAL(stat)(path, buf); return REAL(stat)(path, buf);
@ -1295,7 +1339,7 @@ TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
#endif #endif
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf); SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf);
READ_STRING(thr, pc, path, 0); READ_STRING(thr, pc, path, 0);
@ -1306,7 +1350,7 @@ TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
#define TSAN_MAYBE_INTERCEPT___XSTAT64 #define TSAN_MAYBE_INTERCEPT___XSTAT64
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf); SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf);
READ_STRING(thr, pc, path, 0); READ_STRING(thr, pc, path, 0);
@ -1317,7 +1361,7 @@ TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
#define TSAN_MAYBE_INTERCEPT_STAT64 #define TSAN_MAYBE_INTERCEPT_STAT64
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf); SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf);
READ_STRING(thr, pc, path, 0); READ_STRING(thr, pc, path, 0);
@ -1329,7 +1373,7 @@ TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
#endif #endif
TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD || SANITIZER_MAC
SCOPED_TSAN_INTERCEPTOR(lstat, path, buf); SCOPED_TSAN_INTERCEPTOR(lstat, path, buf);
READ_STRING(thr, pc, path, 0); READ_STRING(thr, pc, path, 0);
return REAL(lstat)(path, buf); return REAL(lstat)(path, buf);
@ -1340,7 +1384,7 @@ TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
#endif #endif
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf); SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf);
READ_STRING(thr, pc, path, 0); READ_STRING(thr, pc, path, 0);
@ -1351,7 +1395,7 @@ TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
#define TSAN_MAYBE_INTERCEPT___LXSTAT64 #define TSAN_MAYBE_INTERCEPT___LXSTAT64
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) { TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf); SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf);
READ_STRING(thr, pc, path, 0); READ_STRING(thr, pc, path, 0);
@ -1362,7 +1406,7 @@ TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
#define TSAN_MAYBE_INTERCEPT_LSTAT64 #define TSAN_MAYBE_INTERCEPT_LSTAT64
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) { TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf); SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf);
if (fd > 0) if (fd > 0)
@ -1375,7 +1419,7 @@ TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
#endif #endif
TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) { TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD || SANITIZER_MAC
SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf); SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf);
if (fd > 0) if (fd > 0)
FdAccess(thr, pc, fd); FdAccess(thr, pc, fd);
@ -1388,7 +1432,7 @@ TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
#endif #endif
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) { TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf); SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
if (fd > 0) if (fd > 0)
@ -1400,7 +1444,7 @@ TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
#define TSAN_MAYBE_INTERCEPT___FXSTAT64 #define TSAN_MAYBE_INTERCEPT___FXSTAT64
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) { TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf); SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf);
if (fd > 0) if (fd > 0)
@ -1421,7 +1465,7 @@ TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
return fd; return fd;
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) { TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode); SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
READ_STRING(thr, pc, name, 0); READ_STRING(thr, pc, name, 0);
@ -1444,7 +1488,7 @@ TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
return fd; return fd;
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) { TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat64, name, mode); SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
READ_STRING(thr, pc, name, 0); READ_STRING(thr, pc, name, 0);
@ -1474,6 +1518,7 @@ TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) {
return newfd2; return newfd2;
} }
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) { TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags); SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags);
int newfd2 = REAL(dup3)(oldfd, newfd, flags); int newfd2 = REAL(dup3)(oldfd, newfd, flags);
@ -1481,8 +1526,9 @@ TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
FdDup(thr, pc, oldfd, newfd2, false); FdDup(thr, pc, oldfd, newfd2, false);
return newfd2; return newfd2;
} }
#endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) { TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags); SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags);
int fd = REAL(eventfd)(initval, flags); int fd = REAL(eventfd)(initval, flags);
@ -1495,7 +1541,7 @@ TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
#define TSAN_MAYBE_INTERCEPT_EVENTFD #define TSAN_MAYBE_INTERCEPT_EVENTFD
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) { TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags); SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags);
if (fd >= 0) if (fd >= 0)
@ -1510,7 +1556,7 @@ TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
#define TSAN_MAYBE_INTERCEPT_SIGNALFD #define TSAN_MAYBE_INTERCEPT_SIGNALFD
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, inotify_init, int fake) { TSAN_INTERCEPTOR(int, inotify_init, int fake) {
SCOPED_TSAN_INTERCEPTOR(inotify_init, fake); SCOPED_TSAN_INTERCEPTOR(inotify_init, fake);
int fd = REAL(inotify_init)(fake); int fd = REAL(inotify_init)(fake);
@ -1523,7 +1569,7 @@ TSAN_INTERCEPTOR(int, inotify_init, int fake) {
#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT #define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, inotify_init1, int flags) { TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags); SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags);
int fd = REAL(inotify_init1)(flags); int fd = REAL(inotify_init1)(flags);
@ -1577,7 +1623,7 @@ TSAN_INTERCEPTOR(int, listen, int fd, int backlog) {
return res; return res;
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_create, int size) { TSAN_INTERCEPTOR(int, epoll_create, int size) {
SCOPED_TSAN_INTERCEPTOR(epoll_create, size); SCOPED_TSAN_INTERCEPTOR(epoll_create, size);
int fd = REAL(epoll_create)(size); int fd = REAL(epoll_create)(size);
@ -1590,7 +1636,7 @@ TSAN_INTERCEPTOR(int, epoll_create, int size) {
#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE #define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_create1, int flags) { TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags); SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags);
int fd = REAL(epoll_create1)(flags); int fd = REAL(epoll_create1)(flags);
@ -1610,7 +1656,7 @@ TSAN_INTERCEPTOR(int, close, int fd) {
return REAL(close)(fd); return REAL(close)(fd);
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, __close, int fd) { TSAN_INTERCEPTOR(int, __close, int fd) {
SCOPED_TSAN_INTERCEPTOR(__close, fd); SCOPED_TSAN_INTERCEPTOR(__close, fd);
if (fd >= 0) if (fd >= 0)
@ -1623,7 +1669,7 @@ TSAN_INTERCEPTOR(int, __close, int fd) {
#endif #endif
// glibc guts // glibc guts
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) { TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) {
SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr); SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr);
int fds[64]; int fds[64];
@ -1647,6 +1693,7 @@ TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
return res; return res;
} }
#if !SANITIZER_MAC
TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags); SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags);
int res = REAL(pipe2)(pipefd, flags); int res = REAL(pipe2)(pipefd, flags);
@ -1654,6 +1701,7 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
FdPipeCreate(thr, pc, pipefd[0], pipefd[1]); FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
return res; return res;
} }
#endif
TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) { TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) {
SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags); SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags);
@ -1704,7 +1752,7 @@ TSAN_INTERCEPTOR(void*, tmpfile, int fake) {
return res; return res;
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(void*, tmpfile64, int fake) { TSAN_INTERCEPTOR(void*, tmpfile64, int fake) {
SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake); SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake);
void *res = REAL(tmpfile64)(fake); void *res = REAL(tmpfile64)(fake);
@ -1771,7 +1819,7 @@ TSAN_INTERCEPTOR(int, closedir, void *dirp) {
return REAL(closedir)(dirp); return REAL(closedir)(dirp);
} }
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) { TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev); SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
if (epfd >= 0) if (epfd >= 0)
@ -1788,7 +1836,7 @@ TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
#define TSAN_MAYBE_INTERCEPT_EPOLL_CTL #define TSAN_MAYBE_INTERCEPT_EPOLL_CTL
#endif #endif
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) { TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout); SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
if (epfd >= 0) if (epfd >= 0)
@ -2087,6 +2135,7 @@ TSAN_INTERCEPTOR(int, vfork, int fake) {
return WRAP(fork)(fake); return WRAP(fork)(fake);
} }
#if !SANITIZER_MAC
typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size, typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size,
void *data); void *data);
struct dl_iterate_phdr_data { struct dl_iterate_phdr_data {
@ -2130,6 +2179,7 @@ TSAN_INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb_t cb, void *data) {
int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata); int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata);
return res; return res;
} }
#endif
static int OnExit(ThreadState *thr) { static int OnExit(ThreadState *thr) {
int status = Finalize(thr); int status = Finalize(thr);
@ -2143,6 +2193,7 @@ struct TsanInterceptorContext {
const uptr pc; const uptr pc;
}; };
#if !SANITIZER_MAC
static void HandleRecvmsg(ThreadState *thr, uptr pc, static void HandleRecvmsg(ThreadState *thr, uptr pc,
__sanitizer_msghdr *msg) { __sanitizer_msghdr *msg) {
int fds[64]; int fds[64];
@ -2150,6 +2201,7 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
for (int i = 0; i < cnt; i++) for (int i = 0; i < cnt; i++)
FdEventCreate(thr, pc, fds[i]); FdEventCreate(thr, pc, fds[i]);
} }
#endif
#include "sanitizer_common/sanitizer_platform_interceptors.h" #include "sanitizer_common/sanitizer_platform_interceptors.h"
// Causes interceptor recursion (getaddrinfo() and fopen()) // Causes interceptor recursion (getaddrinfo() and fopen())
@ -2264,9 +2316,11 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
MutexRepair(((TsanInterceptorContext *)ctx)->thr, \ MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, (uptr)m) ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
#if !SANITIZER_MAC
#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \ #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \ HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, msg) ((TsanInterceptorContext *)ctx)->pc, msg)
#endif
#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
if (TsanThread *t = GetCurrentThread()) { \ if (TsanThread *t = GetCurrentThread()) { \
@ -2298,6 +2352,7 @@ struct ScopedSyscall {
} }
}; };
#if !SANITIZER_MAC
static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) { static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) {
TSAN_SYSCALL(); TSAN_SYSCALL();
MemoryAccessRange(thr, pc, p, s, write); MemoryAccessRange(thr, pc, p, s, write);
@ -2351,6 +2406,7 @@ static void syscall_post_fork(uptr pc, int pid) {
ForkParentAfter(thr, pc); ForkParentAfter(thr, pc);
} }
} }
#endif
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \ #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \
syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false) syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false)
@ -2401,24 +2457,29 @@ static void finalize(void *arg) {
Die(); Die();
} }
#if !SANITIZER_MAC
static void unreachable() { static void unreachable() {
Report("FATAL: ThreadSanitizer: unreachable called\n"); Report("FATAL: ThreadSanitizer: unreachable called\n");
Die(); Die();
} }
#endif
void InitializeInterceptors() { void InitializeInterceptors() {
#if !SANITIZER_MAC
// We need to setup it early, because functions like dlsym() can call it. // We need to setup it early, because functions like dlsym() can call it.
REAL(memset) = internal_memset; REAL(memset) = internal_memset;
REAL(memcpy) = internal_memcpy; REAL(memcpy) = internal_memcpy;
#endif
// Instruct libc malloc to consume less memory. // Instruct libc malloc to consume less memory.
#if !SANITIZER_FREEBSD #if SANITIZER_LINUX
mallopt(1, 0); // M_MXFAST mallopt(1, 0); // M_MXFAST
mallopt(-3, 32*1024); // M_MMAP_THRESHOLD mallopt(-3, 32*1024); // M_MMAP_THRESHOLD
#endif #endif
InitializeCommonInterceptors(); InitializeCommonInterceptors();
#if !SANITIZER_MAC
// We can not use TSAN_INTERCEPT to get setjmp addr, // We can not use TSAN_INTERCEPT to get setjmp addr,
// because it does &setjmp and setjmp is not present in some versions of libc. // because it does &setjmp and setjmp is not present in some versions of libc.
using __interception::GetRealFunctionAddress; using __interception::GetRealFunctionAddress;
@ -2426,6 +2487,7 @@ void InitializeInterceptors() {
GetRealFunctionAddress("_setjmp", (uptr*)&REAL(_setjmp), 0, 0); GetRealFunctionAddress("_setjmp", (uptr*)&REAL(_setjmp), 0, 0);
GetRealFunctionAddress("sigsetjmp", (uptr*)&REAL(sigsetjmp), 0, 0); GetRealFunctionAddress("sigsetjmp", (uptr*)&REAL(sigsetjmp), 0, 0);
GetRealFunctionAddress("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0); GetRealFunctionAddress("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0);
#endif
TSAN_INTERCEPT(longjmp); TSAN_INTERCEPT(longjmp);
TSAN_INTERCEPT(siglongjmp); TSAN_INTERCEPT(siglongjmp);
@ -2565,9 +2627,12 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(__cxa_atexit); TSAN_INTERCEPT(__cxa_atexit);
TSAN_INTERCEPT(_exit); TSAN_INTERCEPT(_exit);
#if !SANITIZER_MAC
// Need to setup it, because interceptors check that the function is resolved. // Need to setup it, because interceptors check that the function is resolved.
// But atexit is emitted directly into the module, so can't be resolved. // But atexit is emitted directly into the module, so can't be resolved.
REAL(atexit) = (int(*)(void(*)()))unreachable; REAL(atexit) = (int(*)(void(*)()))unreachable;
#endif
if (REAL(__cxa_atexit)(&finalize, 0, 0)) { if (REAL(__cxa_atexit)(&finalize, 0, 0)) {
Printf("ThreadSanitizer: failed to setup atexit callback\n"); Printf("ThreadSanitizer: failed to setup atexit callback\n");
Die(); Die();

View File

@ -24,6 +24,18 @@ class ScopedInterceptor {
(void)pc; \ (void)pc; \
/**/ /**/
#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
if (REAL(func) == 0) { \
Report("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
Die(); \
} \
if (thr->ignore_interceptors || thr->in_ignored_lib) \
return REAL(func)(__VA_ARGS__); \
/**/
#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#if SANITIZER_FREEBSD #if SANITIZER_FREEBSD
#define __libc_free __free #define __libc_free __free
#define __libc_malloc __malloc #define __libc_malloc __malloc

View File

@ -0,0 +1,70 @@
//===-- tsan_libdispatch_mac.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 (TSan), a race detector.
//
// Mac-specific libdispatch (GCD) support.
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_MAC
#include "sanitizer_common/sanitizer_common.h"
#include "interception/interception.h"
#include "tsan_interceptors.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include <dispatch/dispatch.h>
#include <pthread.h>
namespace __tsan {
// GCD's dispatch_once implementation has a fast path that contains a racy read
// and it's inlined into user's code. Furthermore, this fast path doesn't
// establish a proper happens-before relations between the initialization and
// code following the call to dispatch_once. We could deal with this in
// instrumented code, but there's not much we can do about it in system
// libraries. Let's disable the fast path (by never storing the value ~0 to
// predicate), so the interceptor is always called, and let's add proper release
// and acquire semantics. Since TSan does not see its own atomic stores, the
// race on predicate won't be reported - the only accesses to it that TSan sees
// are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
// both a macro and a real function, we want to intercept the function, so we
// need to undefine the macro.
#undef dispatch_once
TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
dispatch_block_t block) {
SCOPED_TSAN_INTERCEPTOR(dispatch_once, predicate, block);
atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
u32 v = atomic_load(a, memory_order_acquire);
if (v == 0 &&
atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
block();
Release(thr, pc, (uptr)a);
atomic_store(a, 2, memory_order_release);
} else {
while (v != 2) {
internal_sched_yield();
v = atomic_load(a, memory_order_acquire);
}
Acquire(thr, pc, (uptr)a);
}
}
#undef dispatch_once_f
TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
void *context, dispatch_function_t function) {
SCOPED_TSAN_INTERCEPTOR(dispatch_once_f, predicate, context, function);
WRAP(dispatch_once)(predicate, ^(void) {
function(context);
});
}
} // namespace __tsan
#endif // SANITIZER_MAC

View File

@ -0,0 +1,67 @@
//===-- tsan_malloc_mac.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 (TSan), a race detector.
//
// Mac-specific malloc interception.
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_MAC
#include "tsan_interceptors.h"
#include "tsan_stack_trace.h"
using namespace __tsan;
#define COMMON_MALLOC_ZONE_NAME "tsan"
#define COMMON_MALLOC_ENTER()
#define COMMON_MALLOC_SANITIZER_INITIALIZED (cur_thread()->is_inited)
#define COMMON_MALLOC_FORCE_LOCK()
#define COMMON_MALLOC_FORCE_UNLOCK()
#define COMMON_MALLOC_MEMALIGN(alignment, size) \
void *p = \
user_alloc(cur_thread(), StackTrace::GetCurrentPc(), size, alignment)
#define COMMON_MALLOC_MALLOC(size) \
if (cur_thread()->in_symbolizer) \
return REAL(malloc)(size); \
SCOPED_INTERCEPTOR_RAW(malloc, size); \
void *p = user_alloc(thr, pc, size)
#define COMMON_MALLOC_REALLOC(ptr, size) \
if (cur_thread()->in_symbolizer) \
return REAL(realloc)(ptr, size); \
SCOPED_INTERCEPTOR_RAW(realloc, ptr, size); \
void *p = user_realloc(thr, pc, ptr, size)
#define COMMON_MALLOC_CALLOC(count, size) \
if (cur_thread()->in_symbolizer) \
return REAL(calloc)(count, size); \
SCOPED_INTERCEPTOR_RAW(calloc, size, count); \
void *p = user_calloc(thr, pc, size, count)
#define COMMON_MALLOC_VALLOC(size) \
if (cur_thread()->in_symbolizer) \
return REAL(valloc)(size); \
SCOPED_INTERCEPTOR_RAW(valloc, size); \
void *p = user_alloc(thr, pc, size, GetPageSizeCached())
#define COMMON_MALLOC_FREE(ptr) \
if (cur_thread()->in_symbolizer) \
return REAL(free)(ptr); \
SCOPED_INTERCEPTOR_RAW(free, ptr); \
user_free(thr, pc, ptr)
#define COMMON_MALLOC_SIZE(ptr) \
uptr size = user_alloc_usable_size(ptr);
#define COMMON_MALLOC_FILL_STATS(zone, stats)
#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
(void)zone_name; \
Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr);
#define COMMON_MALLOC_IGNORE_INVALID_FREE false
#define COMMON_MALLOC_REPORT_FREE_UNALLOCATED(ptr, zone_ptr, zone_name) \
(void)zone_name; \
Report("free_common(%p) -- attempting to free unallocated memory.\n", ptr);
#define COMMON_MALLOC_NAMESPACE __tsan
#include "sanitizer_common/sanitizer_malloc_mac.inc"
#endif

View File

@ -18,6 +18,7 @@ namespace __tsan {
const uptr kDefaultAlignment = 16; const uptr kDefaultAlignment = 16;
void InitializeAllocator(); void InitializeAllocator();
void ReplaceSystemMalloc();
void AllocatorThreadStart(ThreadState *thr); void AllocatorThreadStart(ThreadState *thr);
void AllocatorThreadFinish(ThreadState *thr); void AllocatorThreadFinish(ThreadState *thr);
void AllocatorPrintStats(); void AllocatorPrintStats();

View File

@ -9,6 +9,7 @@
// //
// Interceptors for operators new and delete. // Interceptors for operators new and delete.
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "interception/interception.h"
#include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_internal_defs.h"
#include "tsan_interceptors.h" #include "tsan_interceptors.h"
@ -18,6 +19,13 @@ namespace std {
struct nothrow_t {}; struct nothrow_t {};
} // namespace std } // namespace std
DECLARE_REAL(void *, malloc, uptr size)
DECLARE_REAL(void, free, void *ptr)
#if SANITIZER_MAC
#define __libc_malloc REAL(malloc)
#define __libc_free REAL(free)
#endif
#define OPERATOR_NEW_BODY(mangled_name) \ #define OPERATOR_NEW_BODY(mangled_name) \
if (cur_thread()->in_symbolizer) \ if (cur_thread()->in_symbolizer) \
return __libc_malloc(size); \ return __libc_malloc(size); \

View File

@ -338,6 +338,8 @@ uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) {
} }
void InitializePlatform(); void InitializePlatform();
void CheckAndProtect();
void InitializeShadowMemoryPlatform();
void FlushShadowMemory(); void FlushShadowMemory();
void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive); void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive);
@ -351,6 +353,8 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
void *abstime), void *c, void *m, void *abstime, void *abstime), void *c, void *m, void *abstime,
void(*cleanup)(void *arg), void *arg); void(*cleanup)(void *arg), void *arg);
void DestroyThreadState();
} // namespace __tsan } // namespace __tsan
#endif // TSAN_PLATFORM_H #endif // TSAN_PLATFORM_H

View File

@ -130,17 +130,6 @@ void FlushShadowMemory() {
} }
#ifndef SANITIZER_GO #ifndef SANITIZER_GO
static void ProtectRange(uptr beg, uptr end) {
CHECK_LE(beg, end);
if (beg == end)
return;
if (beg != (uptr)MmapNoAccess(beg, end - beg)) {
Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
Printf("FATAL: Make sure you are not using unlimited stack\n");
Die();
}
}
// Mark shadow for .rodata sections with the special kShadowRodata marker. // Mark shadow for .rodata sections with the special kShadowRodata marker.
// Accesses to .rodata can't race, so this saves time, memory and trace space. // Accesses to .rodata can't race, so this saves time, memory and trace space.
static void MapRodata() { static void MapRodata() {
@ -198,58 +187,7 @@ static void MapRodata() {
internal_close(fd); internal_close(fd);
} }
void InitializeShadowMemory() { void InitializeShadowMemoryPlatform() {
// Map memory shadow.
uptr shadow =
(uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow");
if (shadow != kShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie (%p, %p).\n", shadow, kShadowBeg);
Die();
}
// This memory range is used for thread stacks and large user mmaps.
// Frequently a thread uses only a small part of stack and similarly
// a program uses a small part of large mmap. On some programs
// we see 20% memory usage reduction without huge pages for this range.
// FIXME: don't use constants here.
#if defined(__x86_64__)
const uptr kMadviseRangeBeg = 0x7f0000000000ull;
const uptr kMadviseRangeSize = 0x010000000000ull;
#elif defined(__mips64)
const uptr kMadviseRangeBeg = 0xff00000000ull;
const uptr kMadviseRangeSize = 0x0100000000ull;
#elif defined(__aarch64__)
const uptr kMadviseRangeBeg = 0x7e00000000ull;
const uptr kMadviseRangeSize = 0x0100000000ull;
#endif
NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg),
kMadviseRangeSize * kShadowMultiplier);
// Meta shadow is compressing and we don't flush it,
// so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory.
// On one program it reduces memory consumption from 5GB to 2.5GB.
NoHugePagesInRegion(kMetaShadowBeg, kMetaShadowEnd - kMetaShadowBeg);
if (common_flags()->use_madv_dontdump)
DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
kShadowBeg, kShadowEnd,
(kShadowEnd - kShadowBeg) >> 30);
// Map meta shadow.
uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
uptr meta =
(uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow");
if (meta != kMetaShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie (%p, %p).\n", meta, kMetaShadowBeg);
Die();
}
if (common_flags()->use_madv_dontdump)
DontDumpShadowMemory(meta, meta_size);
DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
meta, meta + meta_size, meta_size >> 30);
MapRodata(); MapRodata();
} }
@ -293,31 +231,6 @@ static void InitDataSeg() {
CHECK_LT((uptr)&g_data_start, g_data_end); CHECK_LT((uptr)&g_data_start, g_data_end);
} }
static void CheckAndProtect() {
// Ensure that the binary is indeed compiled with -pie.
MemoryMappingLayout proc_maps(true);
uptr p, end;
while (proc_maps.Next(&p, &end, 0, 0, 0, 0)) {
if (IsAppMem(p))
continue;
if (p >= kHeapMemEnd &&
p < HeapEnd())
continue;
if (p >= kVdsoBeg) // vdso
break;
Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end);
Die();
}
ProtectRange(kLoAppMemEnd, kShadowBeg);
ProtectRange(kShadowEnd, kMetaShadowBeg);
ProtectRange(kMetaShadowEnd, kTraceMemBeg);
// Memory for traces is mapped lazily in MapThreadTrace.
// Protect the whole range for now, so that user does not map something here.
ProtectRange(kTraceMemBeg, kTraceMemEnd);
ProtectRange(kTraceMemEnd, kHeapMemBeg);
ProtectRange(HeapEnd(), kHiAppMemBeg);
}
#endif // #ifndef SANITIZER_GO #endif // #ifndef SANITIZER_GO
void InitializePlatform() { void InitializePlatform() {
@ -416,6 +329,10 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
} }
#endif #endif
#ifndef SANITIZER_GO
void ReplaceSystemMalloc() { }
#endif
} // namespace __tsan } // namespace __tsan
#endif // SANITIZER_LINUX || SANITIZER_FREEBSD #endif // SANITIZER_LINUX || SANITIZER_FREEBSD

View File

@ -13,8 +13,10 @@
#include "sanitizer_common/sanitizer_platform.h" #include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_MAC #if SANITIZER_MAC
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_posix.h"
#include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_procmaps.h"
#include "tsan_platform.h" #include "tsan_platform.h"
#include "tsan_rtl.h" #include "tsan_rtl.h"
@ -38,6 +40,62 @@
namespace __tsan { namespace __tsan {
#ifndef SANITIZER_GO
static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) {
atomic_uintptr_t *a = (atomic_uintptr_t *)dst;
void *val = (void *)atomic_load_relaxed(a);
atomic_signal_fence(memory_order_acquire); // Turns the previous load into
// acquire wrt signals.
if (UNLIKELY(val == nullptr)) {
val = (void *)internal_mmap(nullptr, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
CHECK(val);
void *cmp = nullptr;
if (!atomic_compare_exchange_strong(a, (uintptr_t *)&cmp, (uintptr_t)val,
memory_order_acq_rel)) {
internal_munmap(val, size);
val = cmp;
}
}
return val;
}
// On OS X, accessing TLVs via __thread or manually by using pthread_key_* is
// problematic, because there are several places where interceptors are called
// when TLVs are not accessible (early process startup, thread cleanup, ...).
// The following provides a "poor man's TLV" implementation, where we use the
// shadow memory of the pointer returned by pthread_self() to store a pointer to
// the ThreadState object. The main thread's ThreadState pointer is stored
// separately in a static variable, because we need to access it even before the
// shadow memory is set up.
static uptr main_thread_identity = 0;
static ThreadState *main_thread_state = nullptr;
ThreadState *cur_thread() {
ThreadState **fake_tls;
uptr thread_identity = (uptr)pthread_self();
if (thread_identity == main_thread_identity || main_thread_identity == 0) {
fake_tls = &main_thread_state;
} else {
fake_tls = (ThreadState **)MemToShadow(thread_identity);
}
ThreadState *thr = (ThreadState *)SignalSafeGetOrAllocate(
(uptr *)fake_tls, sizeof(ThreadState));
return thr;
}
// TODO(kuba.brecka): This is not async-signal-safe. In particular, we call
// munmap first and then clear `fake_tls`; if we receive a signal in between,
// handler will try to access the unmapped ThreadState.
void cur_thread_finalize() {
uptr thread_identity = (uptr)pthread_self();
CHECK_NE(thread_identity, main_thread_identity);
ThreadState **fake_tls = (ThreadState **)MemToShadow(thread_identity);
internal_munmap(*fake_tls, sizeof(ThreadState));
*fake_tls = nullptr;
}
#endif
uptr GetShadowMemoryConsumption() { uptr GetShadowMemoryConsumption() {
return 0; return 0;
} }
@ -49,28 +107,57 @@ void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
} }
#ifndef SANITIZER_GO #ifndef SANITIZER_GO
void InitializeShadowMemory() { void InitializeShadowMemoryPlatform() { }
uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg,
kShadowEnd - kShadowBeg); // On OS X, GCD worker threads are created without a call to pthread_create. We
if (shadow != kShadowBeg) { // need to properly register these threads with ThreadCreate and ThreadStart.
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); // These threads don't have a parent thread, as they are created "spuriously".
Printf("FATAL: Make sure to compile with -fPIE and " // We're using a libpthread API that notifies us about a newly created thread.
"to link with -pie.\n"); // The `thread == pthread_self()` check indicates this is actually a worker
Die(); // thread. If it's just a regular thread, this hook is called on the parent
// thread.
typedef void (*pthread_introspection_hook_t)(unsigned int event,
pthread_t thread, void *addr,
size_t size);
extern "C" pthread_introspection_hook_t pthread_introspection_hook_install(
pthread_introspection_hook_t hook);
static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1;
static const uptr PTHREAD_INTROSPECTION_THREAD_DESTROY = 4;
static pthread_introspection_hook_t prev_pthread_introspection_hook;
static void my_pthread_introspection_hook(unsigned int event, pthread_t thread,
void *addr, size_t size) {
if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) {
if (thread == pthread_self()) {
// The current thread is a newly created GCD worker thread.
ThreadState *parent_thread_state = nullptr; // No parent.
int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true);
CHECK_NE(tid, 0);
ThreadState *thr = cur_thread();
ThreadStart(thr, tid, GetTid());
}
} else if (event == PTHREAD_INTROSPECTION_THREAD_DESTROY) {
ThreadState *thr = cur_thread();
if (thr->tctx && thr->tctx->parent_tid == kInvalidTid) {
DestroyThreadState();
}
} }
if (common_flags()->use_madv_dontdump)
DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); if (prev_pthread_introspection_hook != nullptr)
DPrintf("kShadow %zx-%zx (%zuGB)\n", prev_pthread_introspection_hook(event, thread, addr, size);
kShadowBeg, kShadowEnd,
(kShadowEnd - kShadowBeg) >> 30);
DPrintf("kAppMem %zx-%zx (%zuGB)\n",
kAppMemBeg, kAppMemEnd,
(kAppMemEnd - kAppMemBeg) >> 30);
} }
#endif #endif
void InitializePlatform() { void InitializePlatform() {
DisableCoreDumperIfNecessary(); DisableCoreDumperIfNecessary();
#ifndef SANITIZER_GO
CheckAndProtect();
CHECK_EQ(main_thread_identity, 0);
main_thread_identity = (uptr)pthread_self();
prev_pthread_introspection_hook =
pthread_introspection_hook_install(&my_pthread_introspection_hook);
#endif
} }
#ifndef SANITIZER_GO #ifndef SANITIZER_GO
@ -89,6 +176,10 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
} }
#endif #endif
bool IsGlobalVar(uptr addr) {
return false;
}
} // namespace __tsan } // namespace __tsan
#endif // SANITIZER_MAC #endif // SANITIZER_MAC

View File

@ -0,0 +1,122 @@
//===-- tsan_platform_posix.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 (TSan), a race detector.
//
// POSIX-specific code.
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_POSIX
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
namespace __tsan {
#ifndef SANITIZER_GO
void InitializeShadowMemory() {
// Map memory shadow.
uptr shadow =
(uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow");
if (shadow != kShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie (%p, %p).\n", shadow, kShadowBeg);
Die();
}
// This memory range is used for thread stacks and large user mmaps.
// Frequently a thread uses only a small part of stack and similarly
// a program uses a small part of large mmap. On some programs
// we see 20% memory usage reduction without huge pages for this range.
// FIXME: don't use constants here.
#if defined(__x86_64__)
const uptr kMadviseRangeBeg = 0x7f0000000000ull;
const uptr kMadviseRangeSize = 0x010000000000ull;
#elif defined(__mips64)
const uptr kMadviseRangeBeg = 0xff00000000ull;
const uptr kMadviseRangeSize = 0x0100000000ull;
#elif defined(__aarch64__)
const uptr kMadviseRangeBeg = 0x7e00000000ull;
const uptr kMadviseRangeSize = 0x0100000000ull;
#endif
NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg),
kMadviseRangeSize * kShadowMultiplier);
// Meta shadow is compressing and we don't flush it,
// so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory.
// On one program it reduces memory consumption from 5GB to 2.5GB.
NoHugePagesInRegion(kMetaShadowBeg, kMetaShadowEnd - kMetaShadowBeg);
if (common_flags()->use_madv_dontdump)
DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
kShadowBeg, kShadowEnd,
(kShadowEnd - kShadowBeg) >> 30);
// Map meta shadow.
uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
uptr meta =
(uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow");
if (meta != kMetaShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie (%p, %p).\n", meta, kMetaShadowBeg);
Die();
}
if (common_flags()->use_madv_dontdump)
DontDumpShadowMemory(meta, meta_size);
DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
meta, meta + meta_size, meta_size >> 30);
InitializeShadowMemoryPlatform();
}
static void ProtectRange(uptr beg, uptr end) {
CHECK_LE(beg, end);
if (beg == end)
return;
if (beg != (uptr)MmapNoAccess(beg, end - beg)) {
Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
Printf("FATAL: Make sure you are not using unlimited stack\n");
Die();
}
}
void CheckAndProtect() {
// Ensure that the binary is indeed compiled with -pie.
MemoryMappingLayout proc_maps(true);
uptr p, end, prot;
while (proc_maps.Next(&p, &end, 0, 0, 0, &prot)) {
if (IsAppMem(p))
continue;
if (p >= kHeapMemEnd &&
p < HeapEnd())
continue;
if (prot == 0) // Zero page or mprotected.
continue;
if (p >= kVdsoBeg) // vdso
break;
Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end);
Die();
}
ProtectRange(kLoAppMemEnd, kShadowBeg);
ProtectRange(kShadowEnd, kMetaShadowBeg);
ProtectRange(kMetaShadowEnd, kTraceMemBeg);
// Memory for traces is mapped lazily in MapThreadTrace.
// Protect the whole range for now, so that user does not map something here.
ProtectRange(kTraceMemBeg, kTraceMemEnd);
ProtectRange(kTraceMemEnd, kHeapMemBeg);
ProtectRange(HeapEnd(), kHiAppMemBeg);
}
#endif
} // namespace __tsan
#endif // SANITIZER_POSIX

View File

@ -109,6 +109,12 @@ static const char *ReportTypeString(ReportType typ) {
return ""; return "";
} }
#if SANITIZER_MAC
static const char *const kInterposedFunctionPrefix = "wrap_";
#else
static const char *const kInterposedFunctionPrefix = "__interceptor_";
#endif
void PrintStack(const ReportStack *ent) { void PrintStack(const ReportStack *ent) {
if (ent == 0 || ent->frames == 0) { if (ent == 0 || ent->frames == 0) {
Printf(" [failed to restore the stack]\n\n"); Printf(" [failed to restore the stack]\n\n");
@ -119,7 +125,7 @@ void PrintStack(const ReportStack *ent) {
InternalScopedString res(2 * GetPageSizeCached()); InternalScopedString res(2 * GetPageSizeCached());
RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info, RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
common_flags()->symbolize_vs_style, common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix, "__interceptor_"); common_flags()->strip_path_prefix, kInterposedFunctionPrefix);
Printf("%s\n", res.data()); Printf("%s\n", res.data());
} }
Printf("\n"); Printf("\n");
@ -163,9 +169,14 @@ static void PrintLocation(const ReportLocation *loc) {
Printf("%s", d.Location()); Printf("%s", d.Location());
if (loc->type == ReportLocationGlobal) { if (loc->type == ReportLocationGlobal) {
const DataInfo &global = loc->global; const DataInfo &global = loc->global;
Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n", if (global.size != 0)
global.name, global.size, global.start, Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
StripModuleName(global.module), global.module_offset); global.name, global.size, global.start,
StripModuleName(global.module), global.module_offset);
else
Printf(" Location is global '%s' at %p (%s+%p)\n\n", global.name,
global.start, StripModuleName(global.module),
global.module_offset);
} else if (loc->type == ReportLocationHeap) { } else if (loc->type == ReportLocationHeap) {
char thrbuf[kThreadBufSize]; char thrbuf[kThreadBufSize];
Printf(" Location is heap block of size %zu at %p allocated by %s:\n", Printf(" Location is heap block of size %zu at %p allocated by %s:\n",

View File

@ -42,7 +42,7 @@ extern "C" void __tsan_resume() {
namespace __tsan { namespace __tsan {
#ifndef SANITIZER_GO #if !defined(SANITIZER_GO) && !SANITIZER_MAC
THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64);
#endif #endif
static char ctx_placeholder[sizeof(Context)] ALIGNED(64); static char ctx_placeholder[sizeof(Context)] ALIGNED(64);
@ -323,6 +323,7 @@ void Initialize(ThreadState *thr) {
CheckVMASize(); CheckVMASize();
#ifndef SANITIZER_GO #ifndef SANITIZER_GO
InitializeAllocator(); InitializeAllocator();
ReplaceSystemMalloc();
#endif #endif
InitializeInterceptors(); InitializeInterceptors();
CheckShadowMapping(); CheckShadowMapping();

View File

@ -408,12 +408,18 @@ struct ThreadState {
}; };
#ifndef SANITIZER_GO #ifndef SANITIZER_GO
#if SANITIZER_MAC
ThreadState *cur_thread();
void cur_thread_finalize();
#else
__attribute__((tls_model("initial-exec"))) __attribute__((tls_model("initial-exec")))
extern THREADLOCAL char cur_thread_placeholder[]; extern THREADLOCAL char cur_thread_placeholder[];
INLINE ThreadState *cur_thread() { INLINE ThreadState *cur_thread() {
return reinterpret_cast<ThreadState *>(&cur_thread_placeholder); return reinterpret_cast<ThreadState *>(&cur_thread_placeholder);
} }
#endif INLINE void cur_thread_finalize() { }
#endif // SANITIZER_MAC
#endif // SANITIZER_GO
class ThreadContext : public ThreadContextBase { class ThreadContext : public ThreadContextBase {
public: public:
@ -707,7 +713,7 @@ void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c);
// The trick is that the call preserves all registers and the compiler // The trick is that the call preserves all registers and the compiler
// does not treat it as a call. // does not treat it as a call.
// If it does not work for you, use normal call. // If it does not work for you, use normal call.
#if !SANITIZER_DEBUG && defined(__x86_64__) #if !SANITIZER_DEBUG && defined(__x86_64__) && !SANITIZER_MAC
// The caller may not create the stack frame for itself at all, // The caller may not create the stack frame for itself at all,
// so we create a reserve stack frame for it (1024b must be enough). // so we create a reserve stack frame for it (1024b must be enough).
#define HACKY_CALL(f) \ #define HACKY_CALL(f) \

View File

@ -0,0 +1,204 @@
#include "sanitizer_common/sanitizer_asm.h"
.hidden __tsan_setjmp
.comm _ZN14__interception11real_setjmpE,8,8
.type setjmp, @function
setjmp:
CFI_STARTPROC
// save env parameters for function call
stp x29, x30, [sp, -32]!
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (29, -32)
CFI_OFFSET (30, -24)
// Adjust the SP for previous frame
add x29, sp, 0
CFI_DEF_CFA_REGISTER (29)
// Save jmp_buf
str x19, [sp, 16]
CFI_OFFSET (19, -16)
mov x19, x0
// SP pointer mangling (see glibc setjmp)
adrp x2, :got:__pointer_chk_guard
ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
add x0, x29, 32
ldr x2, [x2]
eor x1, x2, x0
// call tsan interceptor
bl __tsan_setjmp
// restore env parameter
mov x0, x19
ldr x19, [sp, 16]
ldp x29, x30, [sp], 32
CFI_RESTORE (30)
CFI_RESTORE (19)
CFI_DEF_CFA (31, 0)
// tail jump to libc setjmp
adrp x1, :got:_ZN14__interception11real_setjmpE
ldr x1, [x1, #:got_lo12:_ZN14__interception11real_setjmpE]
ldr x1, [x1]
br x1
CFI_ENDPROC
.size setjmp, .-setjmp
.comm _ZN14__interception12real__setjmpE,8,8
.globl _setjmp
.type _setjmp, @function
_setjmp:
CFI_STARTPROC
// save env parameters for function call
stp x29, x30, [sp, -32]!
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (29, -32)
CFI_OFFSET (30, -24)
// Adjust the SP for previous frame
add x29, sp, 0
CFI_DEF_CFA_REGISTER (29)
// Save jmp_buf
str x19, [sp, 16]
CFI_OFFSET (19, -16)
mov x19, x0
// SP pointer mangling (see glibc setjmp)
adrp x2, :got:__pointer_chk_guard
ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
add x0, x29, 32
ldr x2, [x2]
eor x1, x2, x0
// call tsan interceptor
bl __tsan_setjmp
// Restore jmp_buf parameter
mov x0, x19
ldr x19, [sp, 16]
ldp x29, x30, [sp], 32
CFI_RESTORE (30)
CFI_RESTORE (19)
CFI_DEF_CFA (31, 0)
// tail jump to libc setjmp
adrp x1, :got:_ZN14__interception12real__setjmpE
ldr x1, [x1, #:got_lo12:_ZN14__interception12real__setjmpE]
ldr x1, [x1]
br x1
CFI_ENDPROC
.size _setjmp, .-_setjmp
.comm _ZN14__interception14real_sigsetjmpE,8,8
.globl sigsetjmp
.type sigsetjmp, @function
sigsetjmp:
CFI_STARTPROC
// save env parameters for function call
stp x29, x30, [sp, -32]!
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (29, -32)
CFI_OFFSET (30, -24)
// Adjust the SP for previous frame
add x29, sp, 0
CFI_DEF_CFA_REGISTER (29)
// Save jmp_buf and savesigs
stp x19, x20, [sp, 16]
CFI_OFFSET (19, -16)
CFI_OFFSET (20, -8)
mov w20, w1
mov x19, x0
// SP pointer mangling (see glibc setjmp)
adrp x2, :got:__pointer_chk_guard
ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
add x0, x29, 32
ldr x2, [x2]
eor x1, x2, x0
// call tsan interceptor
bl __tsan_setjmp
// restore env parameter
mov w1, w20
mov x0, x19
ldp x19, x20, [sp, 16]
ldp x29, x30, [sp], 32
CFI_RESTORE (30)
CFI_RESTORE (29)
CFI_RESTORE (19)
CFI_RESTORE (20)
CFI_DEF_CFA (31, 0)
// tail jump to libc sigsetjmp
adrp x2, :got:_ZN14__interception14real_sigsetjmpE
ldr x2, [x2, #:got_lo12:_ZN14__interception14real_sigsetjmpE]
ldr x2, [x2]
br x2
CFI_ENDPROC
.size sigsetjmp, .-sigsetjmp
.comm _ZN14__interception16real___sigsetjmpE,8,8
.globl __sigsetjmp
.type __sigsetjmp, @function
__sigsetjmp:
CFI_STARTPROC
// save env parameters for function call
stp x29, x30, [sp, -32]!
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (29, -32)
CFI_OFFSET (30, -24)
// Adjust the SP for previous frame
add x29, sp, 0
CFI_DEF_CFA_REGISTER (29)
// Save jmp_buf and savesigs
stp x19, x20, [sp, 16]
CFI_OFFSET (19, -16)
CFI_OFFSET (20, -8)
mov w20, w1
mov x19, x0
// SP pointer mangling (see glibc setjmp)
adrp x2, :got:__pointer_chk_guard
ldr x2, [x2, #:got_lo12:__pointer_chk_guard]
add x0, x29, 32
ldr x2, [x2]
eor x1, x2, x0
// call tsan interceptor
bl __tsan_setjmp
mov w1, w20
mov x0, x19
ldp x19, x20, [sp, 16]
ldp x29, x30, [sp], 32
CFI_RESTORE (30)
CFI_RESTORE (29)
CFI_RESTORE (19)
CFI_RESTORE (20)
CFI_DEF_CFA (31, 0)
// tail jump to libc __sigsetjmp
adrp x2, :got:_ZN14__interception16real___sigsetjmpE
ldr x2, [x2, #:got_lo12:_ZN14__interception16real___sigsetjmpE]
ldr x2, [x2]
br x2
CFI_ENDPROC
.size __sigsetjmp, .-__sigsetjmp
#if defined(__linux__)
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif

View File

@ -53,6 +53,8 @@ void ThreadContext::OnCreated(void *arg) {
if (tid == 0) if (tid == 0)
return; return;
OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg); OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg);
if (!args->thr) // GCD workers don't have a parent thread.
return;
args->thr->fast_state.IncrementEpoch(); args->thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well. // Can't increment epoch w/o writing to the trace as well.
TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0); TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0);
@ -229,8 +231,10 @@ int ThreadCount(ThreadState *thr) {
int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) { int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
StatInc(thr, StatThreadCreate); StatInc(thr, StatThreadCreate);
OnCreatedArgs args = { thr, pc }; OnCreatedArgs args = { thr, pc };
int tid = ctx->thread_registry->CreateThread(uid, detached, thr->tid, &args); u32 parent_tid = thr ? thr->tid : kInvalidTid; // No parent for GCD workers.
DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", thr->tid, tid, uid); int tid =
ctx->thread_registry->CreateThread(uid, detached, parent_tid, &args);
DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent_tid, tid, uid);
StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads()); StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads());
return tid; return tid;
} }