libsanitizer: Merge with upstream
Merged revision: 7704fedfff6ef5676adb6415f3be0ac927d1a746
This commit is contained in:
parent
8bf5b49ebd
commit
90e46074e6
@ -1,4 +1,4 @@
|
||||
f58e0513dd95944b81ce7a6e7b49ba656de7d75f
|
||||
7704fedfff6ef5676adb6415f3be0ac927d1a746
|
||||
|
||||
The first line of this file holds the git revision number of the
|
||||
last merge done from the master library sources.
|
||||
|
@ -38,7 +38,6 @@ asan_files = \
|
||||
asan_posix.cpp \
|
||||
asan_premap_shadow.cpp \
|
||||
asan_report.cpp \
|
||||
asan_rtems.cpp \
|
||||
asan_rtl.cpp \
|
||||
asan_shadow_setup.cpp \
|
||||
asan_stack.cpp \
|
||||
|
@ -156,9 +156,9 @@ am__objects_1 = asan_activation.lo asan_allocator.lo asan_debugging.lo \
|
||||
asan_interceptors_memintrinsics.lo asan_linux.lo asan_mac.lo \
|
||||
asan_malloc_linux.lo asan_malloc_mac.lo asan_malloc_win.lo \
|
||||
asan_memory_profile.lo asan_new_delete.lo asan_poisoning.lo \
|
||||
asan_posix.lo asan_premap_shadow.lo asan_report.lo \
|
||||
asan_rtems.lo asan_rtl.lo asan_shadow_setup.lo asan_stack.lo \
|
||||
asan_stats.lo asan_suppressions.lo asan_thread.lo asan_win.lo \
|
||||
asan_posix.lo asan_premap_shadow.lo asan_report.lo asan_rtl.lo \
|
||||
asan_shadow_setup.lo asan_stack.lo asan_stats.lo \
|
||||
asan_suppressions.lo asan_thread.lo asan_win.lo \
|
||||
asan_win_dll_thunk.lo asan_win_dynamic_runtime_thunk.lo \
|
||||
asan_interceptors_vfork.lo
|
||||
am_libasan_la_OBJECTS = $(am__objects_1)
|
||||
@ -446,7 +446,6 @@ asan_files = \
|
||||
asan_posix.cpp \
|
||||
asan_premap_shadow.cpp \
|
||||
asan_report.cpp \
|
||||
asan_rtems.cpp \
|
||||
asan_rtl.cpp \
|
||||
asan_shadow_setup.cpp \
|
||||
asan_stack.cpp \
|
||||
@ -604,7 +603,6 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_posix.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_premap_shadow.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_report.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtems.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtl.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_shadow_setup.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stack.Plo@am__quote@
|
||||
|
@ -852,12 +852,12 @@ struct Allocator {
|
||||
quarantine.PrintStats();
|
||||
}
|
||||
|
||||
void ForceLock() {
|
||||
void ForceLock() ACQUIRE(fallback_mutex) {
|
||||
allocator.ForceLock();
|
||||
fallback_mutex.Lock();
|
||||
}
|
||||
|
||||
void ForceUnlock() {
|
||||
void ForceUnlock() RELEASE(fallback_mutex) {
|
||||
fallback_mutex.Unlock();
|
||||
allocator.ForceUnlock();
|
||||
}
|
||||
@ -1081,11 +1081,9 @@ uptr asan_mz_size(const void *ptr) {
|
||||
return instance.AllocationSize(reinterpret_cast<uptr>(ptr));
|
||||
}
|
||||
|
||||
void asan_mz_force_lock() {
|
||||
instance.ForceLock();
|
||||
}
|
||||
void asan_mz_force_lock() NO_THREAD_SAFETY_ANALYSIS { instance.ForceLock(); }
|
||||
|
||||
void asan_mz_force_unlock() {
|
||||
void asan_mz_force_unlock() NO_THREAD_SAFETY_ANALYSIS {
|
||||
instance.ForceUnlock();
|
||||
}
|
||||
|
||||
|
@ -533,7 +533,6 @@ static void PrintLegend(InternalScopedString *str) {
|
||||
PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
|
||||
PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic);
|
||||
PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic);
|
||||
PrintShadowByte(str, " Shadow gap: ", kAsanShadowGap);
|
||||
}
|
||||
|
||||
static void PrintShadowBytes(InternalScopedString *str, const char *before,
|
||||
|
@ -187,7 +187,7 @@ void SetTLSFakeStack(FakeStack *fs) { }
|
||||
static FakeStack *GetFakeStack() {
|
||||
AsanThread *t = GetCurrentThread();
|
||||
if (!t) return nullptr;
|
||||
return t->fake_stack();
|
||||
return t->get_or_create_fake_stack();
|
||||
}
|
||||
|
||||
static FakeStack *GetFakeStackFast() {
|
||||
@ -198,7 +198,13 @@ static FakeStack *GetFakeStackFast() {
|
||||
return GetFakeStack();
|
||||
}
|
||||
|
||||
ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) {
|
||||
static FakeStack *GetFakeStackFastAlways() {
|
||||
if (FakeStack *fs = GetTLSFakeStack())
|
||||
return fs;
|
||||
return GetFakeStack();
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) {
|
||||
FakeStack *fs = GetFakeStackFast();
|
||||
if (!fs) return 0;
|
||||
uptr local_stack;
|
||||
@ -210,7 +216,21 @@ ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) {
|
||||
static ALWAYS_INLINE uptr OnMallocAlways(uptr class_id, uptr size) {
|
||||
FakeStack *fs = GetFakeStackFastAlways();
|
||||
if (!fs)
|
||||
return 0;
|
||||
uptr local_stack;
|
||||
uptr real_stack = reinterpret_cast<uptr>(&local_stack);
|
||||
FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack);
|
||||
if (!ff)
|
||||
return 0; // Out of fake stack.
|
||||
uptr ptr = reinterpret_cast<uptr>(ff);
|
||||
SetShadow(ptr, size, class_id, 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) {
|
||||
FakeStack::Deallocate(ptr, class_id);
|
||||
SetShadow(ptr, size, class_id, kMagic8);
|
||||
}
|
||||
@ -219,14 +239,18 @@ ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) {
|
||||
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
using namespace __asan;
|
||||
#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \
|
||||
__asan_stack_malloc_##class_id(uptr size) { \
|
||||
return OnMalloc(class_id, size); \
|
||||
} \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \
|
||||
uptr ptr, uptr size) { \
|
||||
OnFree(ptr, class_id, size); \
|
||||
#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \
|
||||
__asan_stack_malloc_##class_id(uptr size) { \
|
||||
return OnMalloc(class_id, size); \
|
||||
} \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \
|
||||
__asan_stack_malloc_always_##class_id(uptr size) { \
|
||||
return OnMallocAlways(class_id, size); \
|
||||
} \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \
|
||||
uptr ptr, uptr size) { \
|
||||
OnFree(ptr, class_id, size); \
|
||||
}
|
||||
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0)
|
||||
@ -240,7 +264,11 @@ DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9)
|
||||
DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10)
|
||||
|
||||
extern "C" {
|
||||
// TODO: remove this method and fix tests that use it by setting
|
||||
// -asan-use-after-return=never, after modal UAR flag lands
|
||||
// (https://github.com/google/sanitizers/issues/1394)
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__asan_get_current_fake_stack() { return GetFakeStackFast(); }
|
||||
|
||||
|
@ -155,10 +155,6 @@ void InitializeFlags() {
|
||||
CHECK_LE(f->max_redzone, 2048);
|
||||
CHECK(IsPowerOfTwo(f->redzone));
|
||||
CHECK(IsPowerOfTwo(f->max_redzone));
|
||||
if (SANITIZER_RTEMS) {
|
||||
CHECK(!f->unmap_shadow_on_exit);
|
||||
CHECK(!f->protect_shadow_gap);
|
||||
}
|
||||
|
||||
// quarantine_size is deprecated but we still honor it.
|
||||
// quarantine_size can not be used together with quarantine_size_mb.
|
||||
|
@ -87,8 +87,7 @@ ASAN_FLAG(bool, check_malloc_usable_size, true,
|
||||
"295.*.")
|
||||
ASAN_FLAG(bool, unmap_shadow_on_exit, false,
|
||||
"If set, explicitly unmaps the (huge) shadow at exit.")
|
||||
ASAN_FLAG(bool, protect_shadow_gap, !SANITIZER_RTEMS,
|
||||
"If set, mprotect the shadow gap")
|
||||
ASAN_FLAG(bool, protect_shadow_gap, true, "If set, mprotect the shadow gap")
|
||||
ASAN_FLAG(bool, print_stats, false,
|
||||
"Print various statistics after printing an error message or if "
|
||||
"atexit=1.")
|
||||
|
@ -154,6 +154,23 @@ static void CheckODRViolationViaIndicator(const Global *g) {
|
||||
}
|
||||
}
|
||||
|
||||
// Check ODR violation for given global G by checking if it's already poisoned.
|
||||
// We use this method in case compiler doesn't use private aliases for global
|
||||
// variables.
|
||||
static void CheckODRViolationViaPoisoning(const Global *g) {
|
||||
if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) {
|
||||
// This check may not be enough: if the first global is much larger
|
||||
// the entire redzone of the second global may be within the first global.
|
||||
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
|
||||
if (g->beg == l->g->beg &&
|
||||
(flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
|
||||
!IsODRViolationSuppressed(g->name))
|
||||
ReportODRViolation(g, FindRegistrationSite(g),
|
||||
l->g, FindRegistrationSite(l->g));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clang provides two different ways for global variables protection:
|
||||
// it can poison the global itself or its private alias. In former
|
||||
// case we may poison same symbol multiple times, that can help us to
|
||||
@ -199,6 +216,8 @@ static void RegisterGlobal(const Global *g) {
|
||||
// where two globals with the same name are defined in different modules.
|
||||
if (UseODRIndicator(g))
|
||||
CheckODRViolationViaIndicator(g);
|
||||
else
|
||||
CheckODRViolationViaPoisoning(g);
|
||||
}
|
||||
if (CanPoisonMemory())
|
||||
PoisonRedZones(*g);
|
||||
|
@ -23,25 +23,25 @@
|
||||
#include "lsan/lsan_common.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
// There is no general interception at all on Fuchsia and RTEMS.
|
||||
// There is no general interception at all on Fuchsia.
|
||||
// Only the functions in asan_interceptors_memintrinsics.cpp are
|
||||
// really defined to replace libc functions.
|
||||
#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
#if !SANITIZER_FUCHSIA
|
||||
|
||||
#if SANITIZER_POSIX
|
||||
#include "sanitizer_common/sanitizer_posix.h"
|
||||
#endif
|
||||
# if SANITIZER_POSIX
|
||||
# include "sanitizer_common/sanitizer_posix.h"
|
||||
# endif
|
||||
|
||||
#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION || \
|
||||
ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION
|
||||
#include <unwind.h>
|
||||
#endif
|
||||
# if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION || \
|
||||
ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION
|
||||
# include <unwind.h>
|
||||
# endif
|
||||
|
||||
#if defined(__i386) && SANITIZER_LINUX
|
||||
#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.1"
|
||||
#elif defined(__mips__) && SANITIZER_LINUX
|
||||
#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2"
|
||||
#endif
|
||||
# if defined(__i386) && SANITIZER_LINUX
|
||||
# define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.1"
|
||||
# elif defined(__mips__) && SANITIZER_LINUX
|
||||
# define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2"
|
||||
# endif
|
||||
|
||||
namespace __asan {
|
||||
|
||||
|
@ -34,10 +34,10 @@ void InitializePlatformInterceptors();
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
// There is no general interception at all on Fuchsia and RTEMS.
|
||||
// There is no general interception at all on Fuchsia.
|
||||
// Only the functions in asan_interceptors_memintrinsics.h are
|
||||
// really defined to replace libc functions.
|
||||
#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
#if !SANITIZER_FUCHSIA
|
||||
|
||||
// Use macro to describe if specific function should be
|
||||
// intercepted on a given platform.
|
||||
@ -81,12 +81,7 @@ void InitializePlatformInterceptors();
|
||||
#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS && !SANITIZER_SOLARIS && \
|
||||
!SANITIZER_NETBSD
|
||||
# define ASAN_INTERCEPT___CXA_THROW 1
|
||||
# if ! defined(ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION) \
|
||||
|| ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION
|
||||
# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1
|
||||
# else
|
||||
# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 0
|
||||
# endif
|
||||
# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1
|
||||
# if defined(_GLIBCXX_SJLJ_EXCEPTIONS) || (SANITIZER_IOS && defined(__arm__))
|
||||
# define ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION 1
|
||||
# else
|
||||
|
@ -30,9 +30,9 @@ void *__asan_memmove(void *to, const void *from, uptr size) {
|
||||
ASAN_MEMMOVE_IMPL(nullptr, to, from, size);
|
||||
}
|
||||
|
||||
#if SANITIZER_FUCHSIA || SANITIZER_RTEMS
|
||||
#if SANITIZER_FUCHSIA
|
||||
|
||||
// Fuchsia and RTEMS don't use sanitizer_common_interceptors.inc, but
|
||||
// Fuchsia doesn't use sanitizer_common_interceptors.inc, but
|
||||
// the only things there it wants are these three. Just define them
|
||||
// as aliases here rather than repeating the contents.
|
||||
|
||||
@ -40,4 +40,4 @@ extern "C" decltype(__asan_memcpy) memcpy[[gnu::alias("__asan_memcpy")]];
|
||||
extern "C" decltype(__asan_memmove) memmove[[gnu::alias("__asan_memmove")]];
|
||||
extern "C" decltype(__asan_memset) memset[[gnu::alias("__asan_memset")]];
|
||||
|
||||
#endif // SANITIZER_FUCHSIA || SANITIZER_RTEMS
|
||||
#endif // SANITIZER_FUCHSIA
|
||||
|
@ -134,6 +134,17 @@ INTERFACE_FUNCTION(__asan_stack_malloc_7)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_8)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_9)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_10)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_0)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_1)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_2)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_3)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_4)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_5)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_6)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_7)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_8)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_9)
|
||||
INTERFACE_FUNCTION(__asan_stack_malloc_always_10)
|
||||
INTERFACE_FUNCTION(__asan_store1)
|
||||
INTERFACE_FUNCTION(__asan_store2)
|
||||
INTERFACE_FUNCTION(__asan_store4)
|
||||
|
@ -35,11 +35,11 @@
|
||||
// If set, values like allocator chunk size, as well as defaults for some flags
|
||||
// will be changed towards less memory overhead.
|
||||
#ifndef ASAN_LOW_MEMORY
|
||||
# if SANITIZER_IOS || SANITIZER_ANDROID || SANITIZER_RTEMS
|
||||
# define ASAN_LOW_MEMORY 1
|
||||
# else
|
||||
# define ASAN_LOW_MEMORY 0
|
||||
# endif
|
||||
# if SANITIZER_IOS || SANITIZER_ANDROID
|
||||
# define ASAN_LOW_MEMORY 1
|
||||
# else
|
||||
# define ASAN_LOW_MEMORY 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ASAN_DYNAMIC
|
||||
@ -77,7 +77,7 @@ void InitializeShadowMemory();
|
||||
// asan_malloc_linux.cpp / asan_malloc_mac.cpp
|
||||
void ReplaceSystemMalloc();
|
||||
|
||||
// asan_linux.cpp / asan_mac.cpp / asan_rtems.cpp / asan_win.cpp
|
||||
// asan_linux.cpp / asan_mac.cpp / asan_win.cpp
|
||||
uptr FindDynamicShadowStart();
|
||||
void *AsanDoesNotSupportStaticLinkage();
|
||||
void AsanCheckDynamicRTPrereqs();
|
||||
@ -159,9 +159,6 @@ const int kAsanArrayCookieMagic = 0xac;
|
||||
const int kAsanIntraObjectRedzone = 0xbb;
|
||||
const int kAsanAllocaLeftMagic = 0xca;
|
||||
const int kAsanAllocaRightMagic = 0xcb;
|
||||
// Used to populate the shadow gap for systems without memory
|
||||
// protection there (i.e. Myriad).
|
||||
const int kAsanShadowGap = 0xcc;
|
||||
|
||||
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
|
||||
static const uptr kRetiredStackFrameMagic = 0x45E0360E;
|
||||
|
@ -15,23 +15,22 @@
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \
|
||||
SANITIZER_NETBSD || SANITIZER_RTEMS || SANITIZER_SOLARIS
|
||||
SANITIZER_NETBSD || SANITIZER_SOLARIS
|
||||
|
||||
#include "sanitizer_common/sanitizer_allocator_checks.h"
|
||||
#include "sanitizer_common/sanitizer_errno.h"
|
||||
#include "sanitizer_common/sanitizer_tls_get_addr.h"
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_malloc_local.h"
|
||||
#include "asan_stack.h"
|
||||
# include "asan_allocator.h"
|
||||
# include "asan_interceptors.h"
|
||||
# include "asan_internal.h"
|
||||
# include "asan_stack.h"
|
||||
# include "sanitizer_common/sanitizer_allocator_checks.h"
|
||||
# include "sanitizer_common/sanitizer_errno.h"
|
||||
# include "sanitizer_common/sanitizer_tls_get_addr.h"
|
||||
|
||||
// ---------------------- Replacement functions ---------------- {{{1
|
||||
using namespace __asan;
|
||||
|
||||
static uptr allocated_for_dlsym;
|
||||
static uptr last_dlsym_alloc_size_in_words;
|
||||
static const uptr kDlsymAllocPoolSize = SANITIZER_RTEMS ? 4096 : 1024;
|
||||
static const uptr kDlsymAllocPoolSize = 1024;
|
||||
static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
|
||||
|
||||
static inline bool IsInDlsymAllocPool(const void *ptr) {
|
||||
@ -82,27 +81,12 @@ static int PosixMemalignFromLocalPool(void **memptr, uptr alignment,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if SANITIZER_RTEMS
|
||||
void* MemalignFromLocalPool(uptr alignment, uptr size) {
|
||||
void *ptr = nullptr;
|
||||
alignment = Max(alignment, kWordSize);
|
||||
PosixMemalignFromLocalPool(&ptr, alignment, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool IsFromLocalPool(const void *ptr) {
|
||||
return IsInDlsymAllocPool(ptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool MaybeInDlsym() {
|
||||
// Fuchsia doesn't use dlsym-based interceptors.
|
||||
return !SANITIZER_FUCHSIA && asan_init_is_running;
|
||||
}
|
||||
|
||||
static inline bool UseLocalPool() {
|
||||
return EarlyMalloc() || MaybeInDlsym();
|
||||
}
|
||||
static inline bool UseLocalPool() { return MaybeInDlsym(); }
|
||||
|
||||
static void *ReallocFromLocalPool(void *ptr, uptr size) {
|
||||
const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
|
||||
|
@ -1,52 +0,0 @@
|
||||
//===-- asan_malloc_local.h -------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Provide interfaces to check for and handle local pool memory allocation.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ASAN_MALLOC_LOCAL_H
|
||||
#define ASAN_MALLOC_LOCAL_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#include "asan_internal.h"
|
||||
|
||||
static inline bool EarlyMalloc() {
|
||||
return SANITIZER_RTEMS &&
|
||||
(!__asan::asan_inited || __asan::asan_init_is_running);
|
||||
}
|
||||
|
||||
#if SANITIZER_RTEMS
|
||||
|
||||
bool IsFromLocalPool(const void *ptr);
|
||||
void *MemalignFromLocalPool(uptr alignment, uptr size);
|
||||
|
||||
// On RTEMS, we use the local pool to handle memory allocation when the ASan
|
||||
// run-time is not up. This macro is expanded in the context of the operator new
|
||||
// implementation.
|
||||
#define MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow) \
|
||||
do { \
|
||||
if (UNLIKELY(EarlyMalloc())) { \
|
||||
void *res = MemalignFromLocalPool(SHADOW_GRANULARITY, size); \
|
||||
if (!nothrow) \
|
||||
CHECK(res); \
|
||||
return res; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define IS_FROM_LOCAL_POOL(ptr) UNLIKELY(IsFromLocalPool(ptr))
|
||||
|
||||
#else // SANITIZER_RTEMS
|
||||
|
||||
#define MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow)
|
||||
#define IS_FROM_LOCAL_POOL(ptr) 0
|
||||
|
||||
#endif // SANITIZER_RTEMS
|
||||
|
||||
#endif // ASAN_MALLOC_LOCAL_H
|
@ -150,17 +150,11 @@
|
||||
// || `[0x36000000, 0x39ffffff]` || ShadowGap ||
|
||||
// || `[0x30000000, 0x35ffffff]` || LowShadow ||
|
||||
// || `[0x00000000, 0x2fffffff]` || LowMem ||
|
||||
//
|
||||
// Shadow mapping on Myriad2 (for shadow scale 5):
|
||||
// || `[0x9ff80000, 0x9fffffff]` || ShadowGap ||
|
||||
// || `[0x9f000000, 0x9ff7ffff]` || LowShadow ||
|
||||
// || `[0x80000000, 0x9effffff]` || LowMem ||
|
||||
// || `[0x00000000, 0x7fffffff]` || Ignored ||
|
||||
|
||||
#if defined(ASAN_SHADOW_SCALE)
|
||||
static const u64 kDefaultShadowScale = ASAN_SHADOW_SCALE;
|
||||
#else
|
||||
static const u64 kDefaultShadowScale = SANITIZER_MYRIAD2 ? 5 : 3;
|
||||
static const u64 kDefaultShadowScale = 3;
|
||||
#endif
|
||||
static const u64 kDefaultShadowSentinel = ~(uptr)0;
|
||||
static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000
|
||||
@ -171,7 +165,7 @@ static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
|
||||
static const u64 kRiscv64_ShadowOffset64 = 0xd55550000;
|
||||
static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
|
||||
static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37;
|
||||
static const u64 kPPC64_ShadowOffset64 = 1ULL << 41;
|
||||
static const u64 kPPC64_ShadowOffset64 = 1ULL << 44;
|
||||
static const u64 kSystemZ_ShadowOffset64 = 1ULL << 52;
|
||||
static const u64 kSPARC64_ShadowOffset64 = 1ULL << 43; // 0x80000000000
|
||||
static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000
|
||||
@ -180,15 +174,6 @@ static const u64 kNetBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000
|
||||
static const u64 kNetBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000
|
||||
static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
|
||||
|
||||
static const u64 kMyriadMemoryOffset32 = 0x80000000ULL;
|
||||
static const u64 kMyriadMemorySize32 = 0x20000000ULL;
|
||||
static const u64 kMyriadMemoryEnd32 =
|
||||
kMyriadMemoryOffset32 + kMyriadMemorySize32 - 1;
|
||||
static const u64 kMyriadShadowOffset32 =
|
||||
(kMyriadMemoryOffset32 + kMyriadMemorySize32 -
|
||||
(kMyriadMemorySize32 >> kDefaultShadowScale));
|
||||
static const u64 kMyriadCacheBitMask32 = 0x40000000ULL;
|
||||
|
||||
#define SHADOW_SCALE kDefaultShadowScale
|
||||
|
||||
#if SANITIZER_FUCHSIA
|
||||
@ -206,8 +191,6 @@ static const u64 kMyriadCacheBitMask32 = 0x40000000ULL;
|
||||
# define SHADOW_OFFSET kWindowsShadowOffset32
|
||||
# elif SANITIZER_IOS
|
||||
# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
|
||||
# elif SANITIZER_MYRIAD2
|
||||
# define SHADOW_OFFSET kMyriadShadowOffset32
|
||||
# else
|
||||
# define SHADOW_OFFSET kDefaultShadowOffset32
|
||||
# endif
|
||||
@ -278,10 +261,8 @@ extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init.
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#if SANITIZER_MYRIAD2
|
||||
#include "asan_mapping_myriad.h"
|
||||
#elif defined(__sparc__) && SANITIZER_WORDSIZE == 64
|
||||
#include "asan_mapping_sparc64.h"
|
||||
#if defined(__sparc__) && SANITIZER_WORDSIZE == 64
|
||||
# include "asan_mapping_sparc64.h"
|
||||
#else
|
||||
#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) + (SHADOW_OFFSET))
|
||||
|
||||
@ -363,7 +344,7 @@ static inline bool AddrIsInShadowGap(uptr a) {
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // SANITIZER_MYRIAD2
|
||||
#endif
|
||||
|
||||
namespace __asan {
|
||||
|
||||
@ -393,8 +374,6 @@ static inline bool AddrIsAlignedByGranularity(uptr a) {
|
||||
|
||||
static inline bool AddressIsPoisoned(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
if (SANITIZER_MYRIAD2 && !AddrIsInMem(a) && !AddrIsInShadow(a))
|
||||
return false;
|
||||
const uptr kAccessSize = 1;
|
||||
u8 *shadow_address = (u8*)MEM_TO_SHADOW(a);
|
||||
s8 shadow_value = *shadow_address;
|
||||
|
@ -1,85 +0,0 @@
|
||||
//===-- asan_mapping_myriad.h -----------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Myriad-specific definitions for ASan memory mapping.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef ASAN_MAPPING_MYRIAD_H
|
||||
#define ASAN_MAPPING_MYRIAD_H
|
||||
|
||||
#define RAW_ADDR(mem) ((mem) & ~kMyriadCacheBitMask32)
|
||||
#define MEM_TO_SHADOW(mem) \
|
||||
(((RAW_ADDR(mem) - kLowMemBeg) >> SHADOW_SCALE) + (SHADOW_OFFSET))
|
||||
|
||||
#define kLowMemBeg kMyriadMemoryOffset32
|
||||
#define kLowMemEnd (SHADOW_OFFSET - 1)
|
||||
|
||||
#define kLowShadowBeg SHADOW_OFFSET
|
||||
#define kLowShadowEnd MEM_TO_SHADOW(kLowMemEnd)
|
||||
|
||||
#define kHighMemBeg 0
|
||||
|
||||
#define kHighShadowBeg 0
|
||||
#define kHighShadowEnd 0
|
||||
|
||||
#define kMidShadowBeg 0
|
||||
#define kMidShadowEnd 0
|
||||
|
||||
#define kShadowGapBeg (kLowShadowEnd + 1)
|
||||
#define kShadowGapEnd kMyriadMemoryEnd32
|
||||
|
||||
#define kShadowGap2Beg 0
|
||||
#define kShadowGap2End 0
|
||||
|
||||
#define kShadowGap3Beg 0
|
||||
#define kShadowGap3End 0
|
||||
|
||||
namespace __asan {
|
||||
|
||||
static inline bool AddrIsInLowMem(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
a = RAW_ADDR(a);
|
||||
return a >= kLowMemBeg && a <= kLowMemEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInLowShadow(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
a = RAW_ADDR(a);
|
||||
return a >= kLowShadowBeg && a <= kLowShadowEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInMidMem(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInMidShadow(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInHighMem(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInHighShadow(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInShadowGap(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
a = RAW_ADDR(a);
|
||||
return a >= kShadowGapBeg && a <= kShadowGapEnd;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_MAPPING_MYRIAD_H
|
@ -11,16 +11,14 @@
|
||||
// Interceptors for operators new and delete.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_malloc_local.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
|
||||
#include "interception/interception.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// C++ operators can't have dllexport attributes on Windows. We export them
|
||||
// anyway by passing extra -export flags to the linker, which is exactly that
|
||||
// dllexport would normally do. We need to export them in order to make the
|
||||
@ -72,14 +70,12 @@ enum class align_val_t: size_t {};
|
||||
// For local pool allocation, align to SHADOW_GRANULARITY to match asan
|
||||
// allocator behavior.
|
||||
#define OPERATOR_NEW_BODY(type, nothrow) \
|
||||
MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow); \
|
||||
GET_STACK_TRACE_MALLOC; \
|
||||
void *res = asan_memalign(0, size, &stack, type); \
|
||||
if (!nothrow && UNLIKELY(!res)) \
|
||||
ReportOutOfMemory(size, &stack); \
|
||||
return res;
|
||||
#define OPERATOR_NEW_BODY_ALIGN(type, nothrow) \
|
||||
MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow); \
|
||||
GET_STACK_TRACE_MALLOC; \
|
||||
void *res = asan_memalign((uptr)align, size, &stack, type); \
|
||||
if (!nothrow && UNLIKELY(!res)) \
|
||||
@ -135,23 +131,19 @@ INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
|
||||
#endif // !SANITIZER_MAC
|
||||
|
||||
#define OPERATOR_DELETE_BODY(type) \
|
||||
if (IS_FROM_LOCAL_POOL(ptr)) return;\
|
||||
GET_STACK_TRACE_FREE;\
|
||||
GET_STACK_TRACE_FREE; \
|
||||
asan_delete(ptr, 0, 0, &stack, type);
|
||||
|
||||
#define OPERATOR_DELETE_BODY_SIZE(type) \
|
||||
if (IS_FROM_LOCAL_POOL(ptr)) return;\
|
||||
GET_STACK_TRACE_FREE;\
|
||||
GET_STACK_TRACE_FREE; \
|
||||
asan_delete(ptr, size, 0, &stack, type);
|
||||
|
||||
#define OPERATOR_DELETE_BODY_ALIGN(type) \
|
||||
if (IS_FROM_LOCAL_POOL(ptr)) return;\
|
||||
GET_STACK_TRACE_FREE;\
|
||||
GET_STACK_TRACE_FREE; \
|
||||
asan_delete(ptr, 0, static_cast<uptr>(align), &stack, type);
|
||||
|
||||
#define OPERATOR_DELETE_BODY_SIZE_ALIGN(type) \
|
||||
if (IS_FROM_LOCAL_POOL(ptr)) return;\
|
||||
GET_STACK_TRACE_FREE;\
|
||||
GET_STACK_TRACE_FREE; \
|
||||
asan_delete(ptr, size, static_cast<uptr>(align), &stack, type);
|
||||
|
||||
#if !SANITIZER_MAC
|
||||
|
@ -173,17 +173,13 @@ int __asan_address_is_poisoned(void const volatile *addr) {
|
||||
}
|
||||
|
||||
uptr __asan_region_is_poisoned(uptr beg, uptr size) {
|
||||
if (!size) return 0;
|
||||
if (!size)
|
||||
return 0;
|
||||
uptr end = beg + size;
|
||||
if (SANITIZER_MYRIAD2) {
|
||||
// On Myriad, address not in DRAM range need to be treated as
|
||||
// unpoisoned.
|
||||
if (!AddrIsInMem(beg) && !AddrIsInShadow(beg)) return 0;
|
||||
if (!AddrIsInMem(end) && !AddrIsInShadow(end)) return 0;
|
||||
} else {
|
||||
if (!AddrIsInMem(beg)) return beg;
|
||||
if (!AddrIsInMem(end)) return end;
|
||||
}
|
||||
if (!AddrIsInMem(beg))
|
||||
return beg;
|
||||
if (!AddrIsInMem(end))
|
||||
return end;
|
||||
CHECK_LT(beg, end);
|
||||
uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY);
|
||||
uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY);
|
||||
@ -192,8 +188,7 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) {
|
||||
// First check the first and the last application bytes,
|
||||
// then check the SHADOW_GRANULARITY-aligned region by calling
|
||||
// mem_is_zero on the corresponding shadow.
|
||||
if (!__asan::AddressIsPoisoned(beg) &&
|
||||
!__asan::AddressIsPoisoned(end - 1) &&
|
||||
if (!__asan::AddressIsPoisoned(beg) && !__asan::AddressIsPoisoned(end - 1) &&
|
||||
(shadow_end <= shadow_beg ||
|
||||
__sanitizer::mem_is_zero((const char *)shadow_beg,
|
||||
shadow_end - shadow_beg)))
|
||||
|
@ -51,9 +51,6 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
|
||||
// probably provide higher-level interface for these operations.
|
||||
// For now, just memset on Windows.
|
||||
if (value || SANITIZER_WINDOWS == 1 ||
|
||||
// RTEMS doesn't have have pages, let alone a fast way to zero
|
||||
// them, so default to memset.
|
||||
SANITIZER_RTEMS == 1 ||
|
||||
shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
|
||||
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
|
||||
} else {
|
||||
|
@ -1,266 +0,0 @@
|
||||
//===-- asan_rtems.cpp ----------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// RTEMS-specific details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common/sanitizer_rtems.h"
|
||||
#if SANITIZER_RTEMS
|
||||
|
||||
#include "asan_internal.h"
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_poisoning.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace __asan {
|
||||
|
||||
static void ResetShadowMemory() {
|
||||
uptr shadow_start = SHADOW_OFFSET;
|
||||
uptr shadow_end = MEM_TO_SHADOW(kMyriadMemoryEnd32);
|
||||
uptr gap_start = MEM_TO_SHADOW(shadow_start);
|
||||
uptr gap_end = MEM_TO_SHADOW(shadow_end);
|
||||
|
||||
REAL(memset)((void *)shadow_start, 0, shadow_end - shadow_start);
|
||||
REAL(memset)((void *)gap_start, kAsanShadowGap, gap_end - gap_start);
|
||||
}
|
||||
|
||||
void InitializeShadowMemory() {
|
||||
kHighMemEnd = 0;
|
||||
kMidMemBeg = 0;
|
||||
kMidMemEnd = 0;
|
||||
|
||||
ResetShadowMemory();
|
||||
}
|
||||
|
||||
void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void FlushUnneededASanShadowMemory(uptr p, uptr size) {
|
||||
// Since asan's mapping is compacting, the shadow chunk may be
|
||||
// not page-aligned, so we only flush the page-aligned portion.
|
||||
ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));
|
||||
}
|
||||
|
||||
void AsanCheckDynamicRTPrereqs() {}
|
||||
void AsanCheckIncompatibleRT() {}
|
||||
void InitializeAsanInterceptors() {}
|
||||
void InitializePlatformInterceptors() {}
|
||||
void InitializePlatformExceptionHandlers() {}
|
||||
|
||||
// RTEMS only support static linking; it sufficies to return with no
|
||||
// error.
|
||||
void *AsanDoesNotSupportStaticLinkage() { return nullptr; }
|
||||
|
||||
void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
bool PlatformUnpoisonStacks() { return false; }
|
||||
|
||||
void EarlyInit() {
|
||||
// Provide early initialization of shadow memory so that
|
||||
// instrumented code running before full initialzation will not
|
||||
// report spurious errors.
|
||||
ResetShadowMemory();
|
||||
}
|
||||
|
||||
// We can use a plain thread_local variable for TSD.
|
||||
static thread_local void *per_thread;
|
||||
|
||||
void *AsanTSDGet() { return per_thread; }
|
||||
|
||||
void AsanTSDSet(void *tsd) { per_thread = tsd; }
|
||||
|
||||
// There's no initialization needed, and the passed-in destructor
|
||||
// will never be called. Instead, our own thread destruction hook
|
||||
// (below) will call AsanThread::TSDDtor directly.
|
||||
void AsanTSDInit(void (*destructor)(void *tsd)) {
|
||||
DCHECK(destructor == &PlatformTSDDtor);
|
||||
}
|
||||
|
||||
void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); }
|
||||
|
||||
//
|
||||
// Thread registration. We provide an API similar to the Fushia port.
|
||||
//
|
||||
|
||||
struct AsanThread::InitOptions {
|
||||
uptr stack_bottom, stack_size, tls_bottom, tls_size;
|
||||
};
|
||||
|
||||
// Shared setup between thread creation and startup for the initial thread.
|
||||
static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid,
|
||||
uptr user_id, bool detached,
|
||||
uptr stack_bottom, uptr stack_size,
|
||||
uptr tls_bottom, uptr tls_size) {
|
||||
// In lieu of AsanThread::Create.
|
||||
AsanThread *thread = (AsanThread *)MmapOrDie(sizeof(AsanThread), __func__);
|
||||
AsanThreadContext::CreateThreadContextArgs args = {thread, stack};
|
||||
asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args);
|
||||
|
||||
// On other systems, AsanThread::Init() is called from the new
|
||||
// thread itself. But on RTEMS we already know the stack address
|
||||
// range beforehand, so we can do most of the setup right now.
|
||||
const AsanThread::InitOptions options = {stack_bottom, stack_size,
|
||||
tls_bottom, tls_size};
|
||||
thread->Init(&options);
|
||||
return thread;
|
||||
}
|
||||
|
||||
// This gets the same arguments passed to Init by CreateAsanThread, above.
|
||||
// We're in the creator thread before the new thread is actually started, but
|
||||
// its stack and tls address range are already known.
|
||||
void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) {
|
||||
DCHECK_NE(GetCurrentThread(), this);
|
||||
DCHECK_NE(GetCurrentThread(), nullptr);
|
||||
CHECK_NE(options->stack_bottom, 0);
|
||||
CHECK_NE(options->stack_size, 0);
|
||||
stack_bottom_ = options->stack_bottom;
|
||||
stack_top_ = options->stack_bottom + options->stack_size;
|
||||
tls_begin_ = options->tls_bottom;
|
||||
tls_end_ = options->tls_bottom + options->tls_size;
|
||||
}
|
||||
|
||||
// Called by __asan::AsanInitInternal (asan_rtl.c). Unlike other ports, the
|
||||
// main thread on RTEMS does not require special treatment; its AsanThread is
|
||||
// already created by the provided hooks. This function simply looks up and
|
||||
// returns the created thread.
|
||||
AsanThread *CreateMainThread() {
|
||||
return GetThreadContextByTidLocked(0)->thread;
|
||||
}
|
||||
|
||||
// This is called before each thread creation is attempted. So, in
|
||||
// its first call, the calling thread is the initial and sole thread.
|
||||
static void *BeforeThreadCreateHook(uptr user_id, bool detached,
|
||||
uptr stack_bottom, uptr stack_size,
|
||||
uptr tls_bottom, uptr tls_size) {
|
||||
EnsureMainThreadIDIsCorrect();
|
||||
// Strict init-order checking is thread-hostile.
|
||||
if (flags()->strict_init_order) StopInitOrderChecking();
|
||||
|
||||
GET_STACK_TRACE_THREAD;
|
||||
u32 parent_tid = GetCurrentTidOrInvalid();
|
||||
|
||||
return CreateAsanThread(&stack, parent_tid, user_id, detached,
|
||||
stack_bottom, stack_size, tls_bottom, tls_size);
|
||||
}
|
||||
|
||||
// This is called after creating a new thread (in the creating thread),
|
||||
// with the pointer returned by BeforeThreadCreateHook (above).
|
||||
static void ThreadCreateHook(void *hook, bool aborted) {
|
||||
AsanThread *thread = static_cast<AsanThread *>(hook);
|
||||
if (!aborted) {
|
||||
// The thread was created successfully.
|
||||
// ThreadStartHook is already running in the new thread.
|
||||
} else {
|
||||
// The thread wasn't created after all.
|
||||
// Clean up everything we set up in BeforeThreadCreateHook.
|
||||
asanThreadRegistry().FinishThread(thread->tid());
|
||||
UnmapOrDie(thread, sizeof(AsanThread));
|
||||
}
|
||||
}
|
||||
|
||||
// This is called (1) in the newly-created thread before it runs anything else,
|
||||
// with the pointer returned by BeforeThreadCreateHook (above). (2) before a
|
||||
// thread restart.
|
||||
static void ThreadStartHook(void *hook, uptr os_id) {
|
||||
if (!hook)
|
||||
return;
|
||||
|
||||
AsanThread *thread = static_cast<AsanThread *>(hook);
|
||||
SetCurrentThread(thread);
|
||||
|
||||
ThreadStatus status =
|
||||
asanThreadRegistry().GetThreadLocked(thread->tid())->status;
|
||||
DCHECK(status == ThreadStatusCreated || status == ThreadStatusRunning);
|
||||
// Determine whether we are starting or restarting the thread.
|
||||
if (status == ThreadStatusCreated) {
|
||||
// In lieu of AsanThread::ThreadStart.
|
||||
asanThreadRegistry().StartThread(thread->tid(), os_id, ThreadType::Regular,
|
||||
nullptr);
|
||||
} else {
|
||||
// In a thread restart, a thread may resume execution at an
|
||||
// arbitrary function entry point, with its stack and TLS state
|
||||
// reset. We unpoison the stack in that case.
|
||||
PoisonShadow(thread->stack_bottom(), thread->stack_size(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Each thread runs this just before it exits,
|
||||
// with the pointer returned by BeforeThreadCreateHook (above).
|
||||
// All per-thread destructors have already been called.
|
||||
static void ThreadExitHook(void *hook, uptr os_id) {
|
||||
AsanThread *thread = static_cast<AsanThread *>(hook);
|
||||
if (thread)
|
||||
AsanThread::TSDDtor(thread->context());
|
||||
}
|
||||
|
||||
static void HandleExit() {
|
||||
// Disable ASan by setting it to uninitialized. Also reset the
|
||||
// shadow memory to avoid reporting errors after the run-time has
|
||||
// been desroyed.
|
||||
if (asan_inited) {
|
||||
asan_inited = false;
|
||||
ResetShadowMemory();
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleDlopenInit() {
|
||||
// Not supported on this platform.
|
||||
static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
|
||||
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
|
||||
return false;
|
||||
}
|
||||
} // namespace __asan
|
||||
|
||||
// These are declared (in extern "C") by <some_path/sanitizer.h>.
|
||||
// The system runtime will call our definitions directly.
|
||||
|
||||
extern "C" {
|
||||
void __sanitizer_early_init() {
|
||||
__asan::EarlyInit();
|
||||
}
|
||||
|
||||
void *__sanitizer_before_thread_create_hook(uptr thread, bool detached,
|
||||
const char *name,
|
||||
void *stack_base, size_t stack_size,
|
||||
void *tls_base, size_t tls_size) {
|
||||
return __asan::BeforeThreadCreateHook(
|
||||
thread, detached,
|
||||
reinterpret_cast<uptr>(stack_base), stack_size,
|
||||
reinterpret_cast<uptr>(tls_base), tls_size);
|
||||
}
|
||||
|
||||
void __sanitizer_thread_create_hook(void *handle, uptr thread, int status) {
|
||||
__asan::ThreadCreateHook(handle, status != 0);
|
||||
}
|
||||
|
||||
void __sanitizer_thread_start_hook(void *handle, uptr self) {
|
||||
__asan::ThreadStartHook(handle, self);
|
||||
}
|
||||
|
||||
void __sanitizer_thread_exit_hook(void *handle, uptr self) {
|
||||
__asan::ThreadExitHook(handle, self);
|
||||
}
|
||||
|
||||
void __sanitizer_exit() {
|
||||
__asan::HandleExit();
|
||||
}
|
||||
} // "C"
|
||||
|
||||
#endif // SANITIZER_RTEMS
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "asan_activation.h"
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_fake_stack.h"
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_interface_internal.h"
|
||||
#include "asan_internal.h"
|
||||
@ -23,11 +24,11 @@
|
||||
#include "asan_stats.h"
|
||||
#include "asan_suppressions.h"
|
||||
#include "asan_thread.h"
|
||||
#include "lsan/lsan_common.h"
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_flags.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_symbolizer.h"
|
||||
#include "lsan/lsan_common.h"
|
||||
#include "ubsan/ubsan_init.h"
|
||||
#include "ubsan/ubsan_platform.h"
|
||||
|
||||
@ -137,24 +138,21 @@ ASAN_REPORT_ERROR_N(load, false)
|
||||
ASAN_REPORT_ERROR_N(store, true)
|
||||
|
||||
#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \
|
||||
if (SANITIZER_MYRIAD2 && !AddrIsInMem(addr) && !AddrIsInShadow(addr)) \
|
||||
return; \
|
||||
uptr sp = MEM_TO_SHADOW(addr); \
|
||||
uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \
|
||||
: *reinterpret_cast<u16 *>(sp); \
|
||||
if (UNLIKELY(s)) { \
|
||||
if (UNLIKELY(size >= SHADOW_GRANULARITY || \
|
||||
((s8)((addr & (SHADOW_GRANULARITY - 1)) + size - 1)) >= \
|
||||
(s8)s)) { \
|
||||
if (__asan_test_only_reported_buggy_pointer) { \
|
||||
*__asan_test_only_reported_buggy_pointer = addr; \
|
||||
} else { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \
|
||||
fatal); \
|
||||
} \
|
||||
uptr sp = MEM_TO_SHADOW(addr); \
|
||||
uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \
|
||||
: *reinterpret_cast<u16 *>(sp); \
|
||||
if (UNLIKELY(s)) { \
|
||||
if (UNLIKELY(size >= SHADOW_GRANULARITY || \
|
||||
((s8)((addr & (SHADOW_GRANULARITY - 1)) + size - 1)) >= \
|
||||
(s8)s)) { \
|
||||
if (__asan_test_only_reported_buggy_pointer) { \
|
||||
*__asan_test_only_reported_buggy_pointer = addr; \
|
||||
} else { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, fatal); \
|
||||
} \
|
||||
}
|
||||
} \
|
||||
}
|
||||
|
||||
#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
@ -305,7 +303,6 @@ static void asan_atexit() {
|
||||
}
|
||||
|
||||
static void InitializeHighMemEnd() {
|
||||
#if !SANITIZER_MYRIAD2
|
||||
#if !ASAN_FIXED_MAPPING
|
||||
kHighMemEnd = GetMaxUserVirtualAddress();
|
||||
// Increase kHighMemEnd to make sure it's properly
|
||||
@ -313,7 +310,6 @@ static void InitializeHighMemEnd() {
|
||||
kHighMemEnd |= (GetMmapGranularity() << SHADOW_SCALE) - 1;
|
||||
#endif // !ASAN_FIXED_MAPPING
|
||||
CHECK_EQ((kHighMemBeg % GetMmapGranularity()), 0);
|
||||
#endif // !SANITIZER_MYRIAD2
|
||||
}
|
||||
|
||||
void PrintAddressSpaceLayout() {
|
||||
@ -569,9 +565,6 @@ static void UnpoisonDefaultStack() {
|
||||
const uptr page_size = GetPageSizeCached();
|
||||
top = curr_thread->stack_top();
|
||||
bottom = ((uptr)&local_stack - page_size) & ~(page_size - 1);
|
||||
} else if (SANITIZER_RTEMS) {
|
||||
// Give up On RTEMS.
|
||||
return;
|
||||
} else {
|
||||
CHECK(!SANITIZER_FUCHSIA);
|
||||
// If we haven't seen this thread, try asking the OS for stack bounds.
|
||||
@ -586,8 +579,12 @@ static void UnpoisonDefaultStack() {
|
||||
|
||||
static void UnpoisonFakeStack() {
|
||||
AsanThread *curr_thread = GetCurrentThread();
|
||||
if (curr_thread && curr_thread->has_fake_stack())
|
||||
curr_thread->fake_stack()->HandleNoReturn();
|
||||
if (!curr_thread)
|
||||
return;
|
||||
FakeStack *stack = curr_thread->get_fake_stack();
|
||||
if (!stack)
|
||||
return;
|
||||
stack->HandleNoReturn();
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
|
@ -13,12 +13,11 @@
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
|
||||
// asan_fuchsia.cpp and asan_rtems.cpp have their own
|
||||
// InitializeShadowMemory implementation.
|
||||
#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
// asan_fuchsia.cpp has their own InitializeShadowMemory implementation.
|
||||
#if !SANITIZER_FUCHSIA
|
||||
|
||||
#include "asan_internal.h"
|
||||
#include "asan_mapping.h"
|
||||
# include "asan_internal.h"
|
||||
# include "asan_mapping.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
@ -123,4 +122,4 @@ void InitializeShadowMemory() {
|
||||
|
||||
} // namespace __asan
|
||||
|
||||
#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
#endif // !SANITIZER_FUCHSIA
|
||||
|
@ -74,7 +74,8 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(
|
||||
if (SANITIZER_MIPS && t &&
|
||||
!IsValidFrame(bp, t->stack_top(), t->stack_bottom()))
|
||||
return;
|
||||
Unwind(max_depth, pc, bp, context, 0, 0, false);
|
||||
Unwind(max_depth, pc, bp, context, t ? t->stack_top() : 0,
|
||||
t ? t->stack_bottom() : 0, false);
|
||||
}
|
||||
|
||||
// ------------------ Interface -------------- {{{1
|
||||
|
@ -60,8 +60,8 @@ ThreadRegistry &asanThreadRegistry() {
|
||||
// in TSD and can't reliably tell when no more TSD destructors will
|
||||
// be called. It would be wrong to reuse AsanThreadContext for another
|
||||
// thread before all TSD destructors will be called for it.
|
||||
asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry(
|
||||
GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads);
|
||||
asan_thread_registry =
|
||||
new (thread_registry_placeholder) ThreadRegistry(GetAsanThreadContext);
|
||||
initialized = true;
|
||||
}
|
||||
return *asan_thread_registry;
|
||||
@ -257,10 +257,9 @@ void AsanThread::Init(const InitOptions *options) {
|
||||
&local);
|
||||
}
|
||||
|
||||
// Fuchsia and RTEMS don't use ThreadStart.
|
||||
// asan_fuchsia.c/asan_rtems.c define CreateMainThread and
|
||||
// SetThreadStackAndTls.
|
||||
#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
// Fuchsia doesn't use ThreadStart.
|
||||
// asan_fuchsia.c definies CreateMainThread and SetThreadStackAndTls.
|
||||
#if !SANITIZER_FUCHSIA
|
||||
|
||||
thread_return_t AsanThread::ThreadStart(tid_t os_id) {
|
||||
Init();
|
||||
@ -317,7 +316,7 @@ void AsanThread::SetThreadStackAndTls(const InitOptions *options) {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
#endif // !SANITIZER_FUCHSIA
|
||||
|
||||
void AsanThread::ClearShadowForThreadStackAndTLS() {
|
||||
if (stack_top_ != stack_bottom_)
|
||||
@ -339,8 +338,8 @@ bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
|
||||
uptr bottom = 0;
|
||||
if (AddrIsInStack(addr)) {
|
||||
bottom = stack_bottom();
|
||||
} else if (has_fake_stack()) {
|
||||
bottom = fake_stack()->AddrIsInFakeStack(addr);
|
||||
} else if (FakeStack *fake_stack = get_fake_stack()) {
|
||||
bottom = fake_stack->AddrIsInFakeStack(addr);
|
||||
CHECK(bottom);
|
||||
access->offset = addr - bottom;
|
||||
access->frame_pc = ((uptr*)bottom)[2];
|
||||
@ -380,8 +379,8 @@ uptr AsanThread::GetStackVariableShadowStart(uptr addr) {
|
||||
uptr bottom = 0;
|
||||
if (AddrIsInStack(addr)) {
|
||||
bottom = stack_bottom();
|
||||
} else if (has_fake_stack()) {
|
||||
bottom = fake_stack()->AddrIsInFakeStack(addr);
|
||||
} else if (FakeStack *fake_stack = get_fake_stack()) {
|
||||
bottom = fake_stack->AddrIsInFakeStack(addr);
|
||||
if (bottom == 0) {
|
||||
return 0;
|
||||
}
|
||||
@ -409,19 +408,19 @@ bool AsanThread::AddrIsInStack(uptr addr) {
|
||||
|
||||
static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
|
||||
void *addr) {
|
||||
AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
|
||||
AsanThreadContext *tctx = static_cast<AsanThreadContext *>(tctx_base);
|
||||
AsanThread *t = tctx->thread;
|
||||
if (!t) return false;
|
||||
if (t->AddrIsInStack((uptr)addr)) return true;
|
||||
if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr))
|
||||
if (!t)
|
||||
return false;
|
||||
if (t->AddrIsInStack((uptr)addr))
|
||||
return true;
|
||||
return false;
|
||||
FakeStack *fake_stack = t->get_fake_stack();
|
||||
if (!fake_stack)
|
||||
return false;
|
||||
return fake_stack->AddrIsInFakeStack((uptr)addr);
|
||||
}
|
||||
|
||||
AsanThread *GetCurrentThread() {
|
||||
if (SANITIZER_RTEMS && !asan_inited)
|
||||
return nullptr;
|
||||
|
||||
AsanThreadContext *context =
|
||||
reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
|
||||
if (!context) {
|
||||
@ -503,8 +502,12 @@ void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches) {}
|
||||
void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
|
||||
void *arg) {
|
||||
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
|
||||
if (t && t->has_fake_stack())
|
||||
t->fake_stack()->ForEachFakeFrame(callback, arg);
|
||||
if (!t)
|
||||
return;
|
||||
__asan::FakeStack *fake_stack = t->get_fake_stack();
|
||||
if (!fake_stack)
|
||||
return;
|
||||
fake_stack->ForEachFakeFrame(callback, arg);
|
||||
}
|
||||
|
||||
void LockThreadRegistry() {
|
||||
|
@ -28,8 +28,6 @@ struct DTLS;
|
||||
|
||||
namespace __asan {
|
||||
|
||||
const u32 kMaxNumberOfThreads = (1 << 22); // 4M
|
||||
|
||||
class AsanThread;
|
||||
|
||||
// These objects are created for every thread and are never deleted,
|
||||
@ -104,17 +102,18 @@ class AsanThread {
|
||||
void FinishSwitchFiber(FakeStack *fake_stack_save, uptr *bottom_old,
|
||||
uptr *size_old);
|
||||
|
||||
bool has_fake_stack() {
|
||||
return !atomic_load(&stack_switching_, memory_order_relaxed) &&
|
||||
(reinterpret_cast<uptr>(fake_stack_) > 1);
|
||||
}
|
||||
|
||||
FakeStack *fake_stack() {
|
||||
if (!__asan_option_detect_stack_use_after_return)
|
||||
return nullptr;
|
||||
FakeStack *get_fake_stack() {
|
||||
if (atomic_load(&stack_switching_, memory_order_relaxed))
|
||||
return nullptr;
|
||||
if (!has_fake_stack())
|
||||
if (reinterpret_cast<uptr>(fake_stack_) <= 1)
|
||||
return nullptr;
|
||||
return fake_stack_;
|
||||
}
|
||||
|
||||
FakeStack *get_or_create_fake_stack() {
|
||||
if (atomic_load(&stack_switching_, memory_order_relaxed))
|
||||
return nullptr;
|
||||
if (reinterpret_cast<uptr>(fake_stack_) <= 1)
|
||||
return AsyncSignalSafeLazyInitFakeStack();
|
||||
return fake_stack_;
|
||||
}
|
||||
|
@ -13,11 +13,13 @@ ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
|
||||
toolexeclib_LTLIBRARIES = libhwasan.la
|
||||
|
||||
hwasan_files = \
|
||||
hwasan_allocation_functions.cpp \
|
||||
hwasan_allocator.cpp \
|
||||
hwasan.cpp \
|
||||
hwasan_dynamic_shadow.cpp \
|
||||
hwasan_exceptions.cpp \
|
||||
hwasan_flags.inc \
|
||||
hwasan_fuchsia.cpp \
|
||||
hwasan_globals.cpp \
|
||||
hwasan_interceptors.cpp \
|
||||
hwasan_interceptors_vfork.S \
|
||||
|
@ -146,8 +146,9 @@ am__DEPENDENCIES_1 =
|
||||
libhwasan_la_DEPENDENCIES = \
|
||||
$(top_builddir)/sanitizer_common/libsanitizer_common.la \
|
||||
$(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1)
|
||||
am__objects_1 = hwasan_allocator.lo hwasan.lo hwasan_dynamic_shadow.lo \
|
||||
hwasan_exceptions.lo hwasan_globals.lo hwasan_interceptors.lo \
|
||||
am__objects_1 = hwasan_allocation_functions.lo hwasan_allocator.lo \
|
||||
hwasan.lo hwasan_dynamic_shadow.lo hwasan_exceptions.lo \
|
||||
hwasan_fuchsia.lo hwasan_globals.lo hwasan_interceptors.lo \
|
||||
hwasan_interceptors_vfork.lo hwasan_linux.lo \
|
||||
hwasan_memintrinsics.lo hwasan_new_delete.lo \
|
||||
hwasan_poisoning.lo hwasan_report.lo hwasan_setjmp.lo \
|
||||
@ -411,11 +412,13 @@ AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
|
||||
ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
|
||||
toolexeclib_LTLIBRARIES = libhwasan.la
|
||||
hwasan_files = \
|
||||
hwasan_allocation_functions.cpp \
|
||||
hwasan_allocator.cpp \
|
||||
hwasan.cpp \
|
||||
hwasan_dynamic_shadow.cpp \
|
||||
hwasan_exceptions.cpp \
|
||||
hwasan_flags.inc \
|
||||
hwasan_fuchsia.cpp \
|
||||
hwasan_globals.cpp \
|
||||
hwasan_interceptors.cpp \
|
||||
hwasan_interceptors_vfork.S \
|
||||
@ -554,9 +557,11 @@ distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_allocation_functions.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_allocator.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_dynamic_shadow.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_exceptions.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_fuchsia.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_globals.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hwasan_interceptors_vfork.Plo@am__quote@
|
||||
|
@ -50,6 +50,11 @@ bool hwasan_init_is_running;
|
||||
|
||||
int hwasan_report_count = 0;
|
||||
|
||||
uptr kLowShadowStart;
|
||||
uptr kLowShadowEnd;
|
||||
uptr kHighShadowStart;
|
||||
uptr kHighShadowEnd;
|
||||
|
||||
void Flags::SetDefaults() {
|
||||
#define HWASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
|
||||
#include "hwasan_flags.inc"
|
||||
@ -177,6 +182,65 @@ void UpdateMemoryUsage() {
|
||||
void UpdateMemoryUsage() {}
|
||||
#endif
|
||||
|
||||
void HwasanAtExit() {
|
||||
if (common_flags()->print_module_map)
|
||||
DumpProcessMap();
|
||||
if (flags()->print_stats && (flags()->atexit || hwasan_report_count > 0))
|
||||
ReportStats();
|
||||
if (hwasan_report_count > 0) {
|
||||
// ReportAtExitStatistics();
|
||||
if (common_flags()->exitcode)
|
||||
internal__exit(common_flags()->exitcode);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleTagMismatch(AccessInfo ai, uptr pc, uptr frame, void *uc,
|
||||
uptr *registers_frame) {
|
||||
InternalMmapVector<BufferedStackTrace> stack_buffer(1);
|
||||
BufferedStackTrace *stack = stack_buffer.data();
|
||||
stack->Reset();
|
||||
stack->Unwind(pc, frame, uc, common_flags()->fast_unwind_on_fatal);
|
||||
|
||||
// The second stack frame contains the failure __hwasan_check function, as
|
||||
// we have a stack frame for the registers saved in __hwasan_tag_mismatch that
|
||||
// we wish to ignore. This (currently) only occurs on AArch64, as x64
|
||||
// implementations use SIGTRAP to implement the failure, and thus do not go
|
||||
// through the stack saver.
|
||||
if (registers_frame && stack->trace && stack->size > 0) {
|
||||
stack->trace++;
|
||||
stack->size--;
|
||||
}
|
||||
|
||||
bool fatal = flags()->halt_on_error || !ai.recover;
|
||||
ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store, fatal,
|
||||
registers_frame);
|
||||
}
|
||||
|
||||
void HwasanTagMismatch(uptr addr, uptr access_info, uptr *registers_frame,
|
||||
size_t outsize) {
|
||||
__hwasan::AccessInfo ai;
|
||||
ai.is_store = access_info & 0x10;
|
||||
ai.is_load = !ai.is_store;
|
||||
ai.recover = access_info & 0x20;
|
||||
ai.addr = addr;
|
||||
if ((access_info & 0xf) == 0xf)
|
||||
ai.size = outsize;
|
||||
else
|
||||
ai.size = 1 << (access_info & 0xf);
|
||||
|
||||
HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
|
||||
(uptr)__builtin_frame_address(0), nullptr, registers_frame);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Thread *GetCurrentThread() {
|
||||
uptr *ThreadLongPtr = GetCurrentThreadLongPtr();
|
||||
if (UNLIKELY(*ThreadLongPtr == 0))
|
||||
return nullptr;
|
||||
auto *R = (StackAllocationsRingBuffer *)ThreadLongPtr;
|
||||
return hwasanThreadList().GetThreadByBufferAddress((uptr)R->Next());
|
||||
}
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
using namespace __hwasan;
|
||||
@ -216,7 +280,7 @@ static void InitLoadedGlobals() {
|
||||
static void InitInstrumentation() {
|
||||
if (hwasan_instrumentation_inited) return;
|
||||
|
||||
InitPrctl();
|
||||
InitializeOsSupport();
|
||||
|
||||
if (!InitShadow()) {
|
||||
Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
|
||||
@ -225,7 +289,6 @@ static void InitInstrumentation() {
|
||||
}
|
||||
|
||||
InitThreads();
|
||||
hwasanThreadList().CreateCurrentThread();
|
||||
|
||||
hwasan_instrumentation_inited = 1;
|
||||
}
|
||||
@ -495,7 +558,7 @@ void __hwasan_print_memory_usage() {
|
||||
Printf("%s\n", s.data());
|
||||
}
|
||||
|
||||
static const u8 kFallbackTag = 0xBB;
|
||||
static const u8 kFallbackTag = 0xBB & kTagMask;
|
||||
|
||||
u8 __hwasan_generate_tag() {
|
||||
Thread *t = GetCurrentThread();
|
||||
@ -516,4 +579,12 @@ void __sanitizer_print_stack_trace() {
|
||||
GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME());
|
||||
stack.Print();
|
||||
}
|
||||
|
||||
// Entry point for interoperability between __hwasan_tag_mismatch (ASM) and the
|
||||
// rest of the mismatch handling code (C++).
|
||||
void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
|
||||
size_t outsize) {
|
||||
__hwasan::HwasanTagMismatch(addr, access_info, registers_frame, outsize);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
@ -36,7 +36,10 @@
|
||||
|
||||
typedef u8 tag_t;
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#if defined(HWASAN_ALIASING_MODE)
|
||||
# if !defined(__x86_64__)
|
||||
# error Aliasing mode is only supported on x86_64
|
||||
# endif
|
||||
// Tags are done in middle bits using userspace aliasing.
|
||||
constexpr unsigned kAddressTagShift = 39;
|
||||
constexpr unsigned kTagBits = 3;
|
||||
@ -49,12 +52,16 @@ constexpr unsigned kTagBits = 3;
|
||||
// simpler/faster shadow calculation.
|
||||
constexpr unsigned kTaggableRegionCheckShift =
|
||||
__sanitizer::Max(kAddressTagShift + kTagBits + 1U, 44U);
|
||||
#elif defined(__x86_64__)
|
||||
// Tags are done in upper bits using Intel LAM.
|
||||
constexpr unsigned kAddressTagShift = 57;
|
||||
constexpr unsigned kTagBits = 6;
|
||||
#else
|
||||
// TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address
|
||||
// translation and can be used to store a tag.
|
||||
constexpr unsigned kAddressTagShift = 56;
|
||||
constexpr unsigned kTagBits = 8;
|
||||
#endif // defined(__x86_64__)
|
||||
#endif // defined(HWASAN_ALIASING_MODE)
|
||||
|
||||
// Mask for extracting tag bits from the lower 8 bits.
|
||||
constexpr uptr kTagMask = (1UL << kTagBits) - 1;
|
||||
@ -95,7 +102,7 @@ extern bool hwasan_init_is_running;
|
||||
extern int hwasan_report_count;
|
||||
|
||||
bool InitShadow();
|
||||
void InitPrctl();
|
||||
void InitializeOsSupport();
|
||||
void InitThreads();
|
||||
void InitializeInterceptors();
|
||||
|
||||
@ -129,6 +136,7 @@ void InstallAtExitHandler();
|
||||
|
||||
void HwasanTSDInit();
|
||||
void HwasanTSDThreadInit();
|
||||
void HwasanAtExit();
|
||||
|
||||
void HwasanOnDeadlySignal(int signo, void *info, void *context);
|
||||
|
||||
@ -138,6 +146,26 @@ void AppendToErrorMessageBuffer(const char *buffer);
|
||||
|
||||
void AndroidTestTlsSlot();
|
||||
|
||||
// This is a compiler-generated struct that can be shared between hwasan
|
||||
// implementations.
|
||||
struct AccessInfo {
|
||||
uptr addr;
|
||||
uptr size;
|
||||
bool is_store;
|
||||
bool is_load;
|
||||
bool recover;
|
||||
};
|
||||
|
||||
// Given access info and frame information, unwind the stack and report the tag
|
||||
// mismatch.
|
||||
void HandleTagMismatch(AccessInfo ai, uptr pc, uptr frame, void *uc,
|
||||
uptr *registers_frame = nullptr);
|
||||
|
||||
// This dispatches to HandleTagMismatch but sets up the AccessInfo, program
|
||||
// counter, and frame pointer.
|
||||
void HwasanTagMismatch(uptr addr, uptr access_info, uptr *registers_frame,
|
||||
size_t outsize);
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
#define HWASAN_MALLOC_HOOK(ptr, size) \
|
||||
@ -175,4 +203,12 @@ typedef struct __hw_jmp_buf_struct __hw_jmp_buf[1];
|
||||
typedef struct __hw_jmp_buf_struct __hw_sigjmp_buf[1];
|
||||
#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__
|
||||
|
||||
#define ENSURE_HWASAN_INITED() \
|
||||
do { \
|
||||
CHECK(!hwasan_init_is_running); \
|
||||
if (!hwasan_inited) { \
|
||||
__hwasan_init(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // HWASAN_H
|
||||
|
172
libsanitizer/hwasan/hwasan_allocation_functions.cpp
Normal file
172
libsanitizer/hwasan/hwasan_allocation_functions.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
//===-- hwasan_allocation_functions.cpp -----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of HWAddressSanitizer.
|
||||
//
|
||||
// Definitions for __sanitizer allocation functions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "hwasan.h"
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_interface.h"
|
||||
#include "sanitizer_common/sanitizer_tls_get_addr.h"
|
||||
|
||||
using namespace __hwasan;
|
||||
|
||||
static uptr allocated_for_dlsym;
|
||||
static const uptr kDlsymAllocPoolSize = 1024;
|
||||
static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
|
||||
|
||||
static bool IsInDlsymAllocPool(const void *ptr) {
|
||||
uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
|
||||
return off < sizeof(alloc_memory_for_dlsym);
|
||||
}
|
||||
|
||||
static void *AllocateFromLocalPool(uptr size_in_bytes) {
|
||||
uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
|
||||
void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym];
|
||||
allocated_for_dlsym += size_in_words;
|
||||
CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
|
||||
return mem;
|
||||
}
|
||||
|
||||
int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
CHECK_NE(memptr, 0);
|
||||
int res = hwasan_posix_memalign(memptr, alignment, size, &stack);
|
||||
return res;
|
||||
}
|
||||
|
||||
void *__sanitizer_memalign(uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_memalign(alignment, size, &stack);
|
||||
}
|
||||
|
||||
void *__sanitizer_aligned_alloc(uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_aligned_alloc(alignment, size, &stack);
|
||||
}
|
||||
|
||||
void *__sanitizer___libc_memalign(uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
void *ptr = hwasan_memalign(alignment, size, &stack);
|
||||
if (ptr)
|
||||
DTLS_on_libc_memalign(ptr, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *__sanitizer_valloc(uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_valloc(size, &stack);
|
||||
}
|
||||
|
||||
void *__sanitizer_pvalloc(uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_pvalloc(size, &stack);
|
||||
}
|
||||
|
||||
void __sanitizer_free(void *ptr) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr)))
|
||||
return;
|
||||
hwasan_free(ptr, &stack);
|
||||
}
|
||||
|
||||
void __sanitizer_cfree(void *ptr) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr)))
|
||||
return;
|
||||
hwasan_free(ptr, &stack);
|
||||
}
|
||||
|
||||
uptr __sanitizer_malloc_usable_size(const void *ptr) {
|
||||
return __sanitizer_get_allocated_size(ptr);
|
||||
}
|
||||
|
||||
struct __sanitizer_struct_mallinfo __sanitizer_mallinfo() {
|
||||
__sanitizer_struct_mallinfo sret;
|
||||
internal_memset(&sret, 0, sizeof(sret));
|
||||
return sret;
|
||||
}
|
||||
|
||||
int __sanitizer_mallopt(int cmd, int value) { return 0; }
|
||||
|
||||
void __sanitizer_malloc_stats(void) {
|
||||
// FIXME: implement, but don't call REAL(malloc_stats)!
|
||||
}
|
||||
|
||||
void *__sanitizer_calloc(uptr nmemb, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (UNLIKELY(!hwasan_inited))
|
||||
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
||||
return AllocateFromLocalPool(nmemb * size);
|
||||
return hwasan_calloc(nmemb, size, &stack);
|
||||
}
|
||||
|
||||
void *__sanitizer_realloc(void *ptr, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
|
||||
uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
|
||||
uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
|
||||
void *new_ptr;
|
||||
if (UNLIKELY(!hwasan_inited)) {
|
||||
new_ptr = AllocateFromLocalPool(copy_size);
|
||||
} else {
|
||||
copy_size = size;
|
||||
new_ptr = hwasan_malloc(copy_size, &stack);
|
||||
}
|
||||
internal_memcpy(new_ptr, ptr, copy_size);
|
||||
return new_ptr;
|
||||
}
|
||||
return hwasan_realloc(ptr, size, &stack);
|
||||
}
|
||||
|
||||
void *__sanitizer_reallocarray(void *ptr, uptr nmemb, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_reallocarray(ptr, nmemb, size, &stack);
|
||||
}
|
||||
|
||||
void *__sanitizer_malloc(uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (UNLIKELY(!hwasan_init_is_running))
|
||||
ENSURE_HWASAN_INITED();
|
||||
if (UNLIKELY(!hwasan_inited))
|
||||
// Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
|
||||
return AllocateFromLocalPool(size);
|
||||
return hwasan_malloc(size, &stack);
|
||||
}
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
# define INTERCEPTOR_ALIAS(RET, FN, ARGS...) \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE RET WRAP(FN)(ARGS) \
|
||||
ALIAS("__sanitizer_" #FN); \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE RET FN( \
|
||||
ARGS) ALIAS("__sanitizer_" #FN)
|
||||
|
||||
INTERCEPTOR_ALIAS(int, posix_memalign, void **memptr, SIZE_T alignment,
|
||||
SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, aligned_alloc, SIZE_T alignment, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, __libc_memalign, SIZE_T alignment, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, valloc, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void, free, void *ptr);
|
||||
INTERCEPTOR_ALIAS(uptr, malloc_usable_size, const void *ptr);
|
||||
INTERCEPTOR_ALIAS(void *, calloc, SIZE_T nmemb, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, realloc, void *ptr, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, reallocarray, void *ptr, SIZE_T nmemb, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, malloc, SIZE_T size);
|
||||
|
||||
# if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
|
||||
INTERCEPTOR_ALIAS(void *, memalign, SIZE_T alignment, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, pvalloc, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void, cfree, void *ptr);
|
||||
INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo);
|
||||
INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value);
|
||||
INTERCEPTOR_ALIAS(void, malloc_stats, void);
|
||||
# endif
|
||||
#endif // #if HWASAN_WITH_INTERCEPTORS
|
@ -80,12 +80,29 @@ void GetAllocatorStats(AllocatorStatCounters s) {
|
||||
allocator.GetStats(s);
|
||||
}
|
||||
|
||||
uptr GetAliasRegionStart() {
|
||||
#if defined(HWASAN_ALIASING_MODE)
|
||||
constexpr uptr kAliasRegionOffset = 1ULL << (kTaggableRegionCheckShift - 1);
|
||||
uptr AliasRegionStart =
|
||||
__hwasan_shadow_memory_dynamic_address + kAliasRegionOffset;
|
||||
|
||||
CHECK_EQ(AliasRegionStart >> kTaggableRegionCheckShift,
|
||||
__hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift);
|
||||
CHECK_EQ(
|
||||
(AliasRegionStart + kAliasRegionOffset - 1) >> kTaggableRegionCheckShift,
|
||||
__hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift);
|
||||
return AliasRegionStart;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void HwasanAllocatorInit() {
|
||||
atomic_store_relaxed(&hwasan_allocator_tagging_enabled,
|
||||
!flags()->disable_allocator_tagging);
|
||||
SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
|
||||
allocator.Init(common_flags()->allocator_release_to_os_interval_ms,
|
||||
kAliasRegionStart);
|
||||
GetAliasRegionStart());
|
||||
for (uptr i = 0; i < sizeof(tail_magic); i++)
|
||||
tail_magic[i] = GetCurrentThread()->GenerateRandomTag();
|
||||
}
|
||||
@ -196,6 +213,7 @@ static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
|
||||
: tagged_ptr;
|
||||
void *aligned_ptr = reinterpret_cast<void *>(
|
||||
RoundDownTo(reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment));
|
||||
tag_t pointer_tag = GetTagFromPointer(reinterpret_cast<uptr>(tagged_ptr));
|
||||
Metadata *meta =
|
||||
reinterpret_cast<Metadata *>(allocator.GetMetaData(aligned_ptr));
|
||||
uptr orig_size = meta->get_requested_size();
|
||||
@ -229,7 +247,20 @@ static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
|
||||
flags()->tag_in_free && malloc_bisect(stack, 0) &&
|
||||
atomic_load_relaxed(&hwasan_allocator_tagging_enabled)) {
|
||||
// Always store full 8-bit tags on free to maximize UAF detection.
|
||||
tag_t tag = t ? t->GenerateRandomTag(/*num_bits=*/8) : kFallbackFreeTag;
|
||||
tag_t tag;
|
||||
if (t) {
|
||||
// Make sure we are not using a short granule tag as a poison tag. This
|
||||
// would make us attempt to read the memory on a UaF.
|
||||
// The tag can be zero if tagging is disabled on this thread.
|
||||
do {
|
||||
tag = t->GenerateRandomTag(/*num_bits=*/8);
|
||||
} while (
|
||||
UNLIKELY((tag < kShadowAlignment || tag == pointer_tag) && tag != 0));
|
||||
} else {
|
||||
static_assert(kFallbackFreeTag >= kShadowAlignment,
|
||||
"fallback tag must not be a short granule tag.");
|
||||
tag = kFallbackFreeTag;
|
||||
}
|
||||
TagMemoryAligned(reinterpret_cast<uptr>(aligned_ptr), TaggedSize(orig_size),
|
||||
tag);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "hwasan.h"
|
||||
#include "hwasan_interface_internal.h"
|
||||
#include "hwasan_mapping.h"
|
||||
#include "hwasan_poisoning.h"
|
||||
#include "sanitizer_common/sanitizer_allocator.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_checks.h"
|
||||
@ -58,7 +59,7 @@ static const uptr kMaxAllowedMallocSize = 1UL << 40; // 1T
|
||||
struct AP64 {
|
||||
static const uptr kSpaceBeg = ~0ULL;
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#if defined(HWASAN_ALIASING_MODE)
|
||||
static const uptr kSpaceSize = 1ULL << kAddressTagShift;
|
||||
#else
|
||||
static const uptr kSpaceSize = 0x2000000000ULL;
|
||||
@ -110,11 +111,11 @@ typedef RingBuffer<HeapAllocationRecord> HeapAllocationsRingBuffer;
|
||||
void GetAllocatorStats(AllocatorStatCounters s);
|
||||
|
||||
inline bool InTaggableRegion(uptr addr) {
|
||||
#if defined(__x86_64__)
|
||||
#if defined(HWASAN_ALIASING_MODE)
|
||||
// Aliases are mapped next to shadow so that the upper bits match the shadow
|
||||
// base.
|
||||
return (addr >> kTaggableRegionCheckShift) ==
|
||||
(__hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift);
|
||||
(GetShadowOffset() >> kTaggableRegionCheckShift);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
@ -119,12 +119,12 @@ namespace __hwasan {
|
||||
void InitShadowGOT() {}
|
||||
|
||||
uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
|
||||
#if defined(__x86_64__)
|
||||
# if defined(HWASAN_ALIASING_MODE)
|
||||
constexpr uptr kAliasSize = 1ULL << kAddressTagShift;
|
||||
constexpr uptr kNumAliases = 1ULL << kTagBits;
|
||||
return MapDynamicShadowAndAliases(shadow_size_bytes, kAliasSize, kNumAliases,
|
||||
RingBufferSize());
|
||||
#endif
|
||||
# endif
|
||||
return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
|
||||
kHighMemEnd);
|
||||
}
|
||||
|
192
libsanitizer/hwasan/hwasan_fuchsia.cpp
Normal file
192
libsanitizer/hwasan/hwasan_fuchsia.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
//===-- hwasan_fuchsia.cpp --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file is a part of HWAddressSanitizer and contains Fuchsia-specific
|
||||
/// code.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_common/sanitizer_fuchsia.h"
|
||||
#if SANITIZER_FUCHSIA
|
||||
|
||||
#include "hwasan.h"
|
||||
#include "hwasan_interface_internal.h"
|
||||
#include "hwasan_report.h"
|
||||
#include "hwasan_thread.h"
|
||||
#include "hwasan_thread_list.h"
|
||||
|
||||
// This TLS variable contains the location of the stack ring buffer and can be
|
||||
// used to always find the hwasan thread object associated with the current
|
||||
// running thread.
|
||||
[[gnu::tls_model("initial-exec")]]
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
THREADLOCAL uptr __hwasan_tls;
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
bool InitShadow() {
|
||||
__sanitizer::InitShadowBounds();
|
||||
CHECK_NE(__sanitizer::ShadowBounds.shadow_limit, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemIsApp(uptr p) {
|
||||
CHECK(GetTagFromPointer(p) == 0);
|
||||
return __sanitizer::ShadowBounds.shadow_limit <= p &&
|
||||
p <= (__sanitizer::ShadowBounds.memory_limit - 1);
|
||||
}
|
||||
|
||||
// These are known parameters passed to the hwasan runtime on thread creation.
|
||||
struct Thread::InitState {
|
||||
uptr stack_bottom, stack_top;
|
||||
};
|
||||
|
||||
static void FinishThreadInitialization(Thread *thread);
|
||||
|
||||
void InitThreads() {
|
||||
// This is the minimal alignment needed for the storage where hwasan threads
|
||||
// and their stack ring buffers are placed. This alignment is necessary so the
|
||||
// stack ring buffer can perform a simple calculation to get the next element
|
||||
// in the RB. The instructions for this calculation are emitted by the
|
||||
// compiler. (Full explanation in hwasan_thread_list.h.)
|
||||
uptr alloc_size = UINT64_C(1) << kShadowBaseAlignment;
|
||||
uptr thread_start = reinterpret_cast<uptr>(
|
||||
MmapAlignedOrDieOnFatalError(alloc_size, alloc_size, __func__));
|
||||
|
||||
InitThreadList(thread_start, alloc_size);
|
||||
|
||||
// Create the hwasan thread object for the current (main) thread. Stack info
|
||||
// for this thread is known from information passed via
|
||||
// __sanitizer_startup_hook.
|
||||
const Thread::InitState state = {
|
||||
.stack_bottom = __sanitizer::MainThreadStackBase,
|
||||
.stack_top =
|
||||
__sanitizer::MainThreadStackBase + __sanitizer::MainThreadStackSize,
|
||||
};
|
||||
FinishThreadInitialization(hwasanThreadList().CreateCurrentThread(&state));
|
||||
}
|
||||
|
||||
uptr *GetCurrentThreadLongPtr() { return &__hwasan_tls; }
|
||||
|
||||
// This is called from the parent thread before the new thread is created. Here
|
||||
// we can propagate known info like the stack bounds to Thread::Init before
|
||||
// jumping into the thread. We cannot initialize the stack ring buffer yet since
|
||||
// we have not entered the new thread.
|
||||
static void *BeforeThreadCreateHook(uptr user_id, bool detached,
|
||||
const char *name, uptr stack_bottom,
|
||||
uptr stack_size) {
|
||||
const Thread::InitState state = {
|
||||
.stack_bottom = stack_bottom,
|
||||
.stack_top = stack_bottom + stack_size,
|
||||
};
|
||||
return hwasanThreadList().CreateCurrentThread(&state);
|
||||
}
|
||||
|
||||
// This sets the stack top and bottom according to the InitState passed to
|
||||
// CreateCurrentThread above.
|
||||
void Thread::InitStackAndTls(const InitState *state) {
|
||||
CHECK_NE(state->stack_bottom, 0);
|
||||
CHECK_NE(state->stack_top, 0);
|
||||
stack_bottom_ = state->stack_bottom;
|
||||
stack_top_ = state->stack_top;
|
||||
tls_end_ = tls_begin_ = 0;
|
||||
}
|
||||
|
||||
// This is called after creating a new thread with the pointer returned by
|
||||
// BeforeThreadCreateHook. We are still in the creating thread and should check
|
||||
// if it was actually created correctly.
|
||||
static void ThreadCreateHook(void *hook, bool aborted) {
|
||||
Thread *thread = static_cast<Thread *>(hook);
|
||||
if (!aborted) {
|
||||
// The thread was created successfully.
|
||||
// ThreadStartHook can already be running in the new thread.
|
||||
} else {
|
||||
// The thread wasn't created after all.
|
||||
// Clean up everything we set up in BeforeThreadCreateHook.
|
||||
atomic_signal_fence(memory_order_seq_cst);
|
||||
hwasanThreadList().ReleaseThread(thread);
|
||||
}
|
||||
}
|
||||
|
||||
// This is called in the newly-created thread before it runs anything else,
|
||||
// with the pointer returned by BeforeThreadCreateHook (above). Here we can
|
||||
// setup the stack ring buffer.
|
||||
static void ThreadStartHook(void *hook, thrd_t self) {
|
||||
Thread *thread = static_cast<Thread *>(hook);
|
||||
FinishThreadInitialization(thread);
|
||||
thread->InitRandomState();
|
||||
}
|
||||
|
||||
// This is the function that sets up the stack ring buffer and enables us to use
|
||||
// GetCurrentThread. This function should only be called while IN the thread
|
||||
// that we want to create the hwasan thread object for so __hwasan_tls can be
|
||||
// properly referenced.
|
||||
static void FinishThreadInitialization(Thread *thread) {
|
||||
CHECK_NE(thread, nullptr);
|
||||
|
||||
// The ring buffer is located immediately before the thread object.
|
||||
uptr stack_buffer_size = hwasanThreadList().GetRingBufferSize();
|
||||
uptr stack_buffer_start = reinterpret_cast<uptr>(thread) - stack_buffer_size;
|
||||
thread->InitStackRingBuffer(stack_buffer_start, stack_buffer_size);
|
||||
}
|
||||
|
||||
static void ThreadExitHook(void *hook, thrd_t self) {
|
||||
Thread *thread = static_cast<Thread *>(hook);
|
||||
atomic_signal_fence(memory_order_seq_cst);
|
||||
hwasanThreadList().ReleaseThread(thread);
|
||||
}
|
||||
|
||||
// Not implemented because Fuchsia does not use signal handlers.
|
||||
void HwasanOnDeadlySignal(int signo, void *info, void *context) {}
|
||||
|
||||
// Not implemented because Fuchsia does not use interceptors.
|
||||
void InitializeInterceptors() {}
|
||||
|
||||
// Not implemented because this is only relevant for Android.
|
||||
void AndroidTestTlsSlot() {}
|
||||
|
||||
// TSD was normally used on linux as a means of calling the hwasan thread exit
|
||||
// handler passed to pthread_key_create. This is not needed on Fuchsia because
|
||||
// we will be using __sanitizer_thread_exit_hook.
|
||||
void HwasanTSDInit() {}
|
||||
void HwasanTSDThreadInit() {}
|
||||
|
||||
// On linux, this just would call `atexit(HwasanAtExit)`. The functions in
|
||||
// HwasanAtExit are unimplemented for Fuchsia and effectively no-ops, so this
|
||||
// function is unneeded.
|
||||
void InstallAtExitHandler() {}
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
extern "C" {
|
||||
|
||||
void *__sanitizer_before_thread_create_hook(thrd_t thread, bool detached,
|
||||
const char *name, void *stack_base,
|
||||
size_t stack_size) {
|
||||
return __hwasan::BeforeThreadCreateHook(
|
||||
reinterpret_cast<uptr>(thread), detached, name,
|
||||
reinterpret_cast<uptr>(stack_base), stack_size);
|
||||
}
|
||||
|
||||
void __sanitizer_thread_create_hook(void *hook, thrd_t thread, int error) {
|
||||
__hwasan::ThreadCreateHook(hook, error != thrd_success);
|
||||
}
|
||||
|
||||
void __sanitizer_thread_start_hook(void *hook, thrd_t self) {
|
||||
__hwasan::ThreadStartHook(hook, reinterpret_cast<uptr>(self));
|
||||
}
|
||||
|
||||
void __sanitizer_thread_exit_hook(void *hook, thrd_t self) {
|
||||
__hwasan::ThreadExitHook(hook, self);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // SANITIZER_FUCHSIA
|
@ -16,192 +16,14 @@
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "hwasan.h"
|
||||
#include "hwasan_allocator.h"
|
||||
#include "hwasan_mapping.h"
|
||||
#include "hwasan_thread.h"
|
||||
#include "hwasan_poisoning.h"
|
||||
#include "hwasan_report.h"
|
||||
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
|
||||
#include "sanitizer_common/sanitizer_allocator.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_interface.h"
|
||||
#include "sanitizer_common/sanitizer_allocator_internal.h"
|
||||
#include "sanitizer_common/sanitizer_atomic.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
#include "sanitizer_common/sanitizer_errno.h"
|
||||
#include "sanitizer_common/sanitizer_stackdepot.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_linux.h"
|
||||
#include "sanitizer_common/sanitizer_tls_get_addr.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
// ACHTUNG! No other system header includes in this file.
|
||||
// Ideally, we should get rid of stdarg.h as well.
|
||||
#if !SANITIZER_FUCHSIA
|
||||
|
||||
using namespace __hwasan;
|
||||
|
||||
using __sanitizer::memory_order;
|
||||
using __sanitizer::atomic_load;
|
||||
using __sanitizer::atomic_store;
|
||||
using __sanitizer::atomic_uintptr_t;
|
||||
|
||||
static uptr allocated_for_dlsym;
|
||||
static const uptr kDlsymAllocPoolSize = 1024;
|
||||
static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
|
||||
|
||||
static bool IsInDlsymAllocPool(const void *ptr) {
|
||||
uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
|
||||
return off < sizeof(alloc_memory_for_dlsym);
|
||||
}
|
||||
|
||||
static void *AllocateFromLocalPool(uptr size_in_bytes) {
|
||||
uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
|
||||
void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym];
|
||||
allocated_for_dlsym += size_in_words;
|
||||
CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
|
||||
return mem;
|
||||
}
|
||||
|
||||
#define ENSURE_HWASAN_INITED() do { \
|
||||
CHECK(!hwasan_init_is_running); \
|
||||
if (!hwasan_inited) { \
|
||||
__hwasan_init(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
CHECK_NE(memptr, 0);
|
||||
int res = hwasan_posix_memalign(memptr, alignment, size, &stack);
|
||||
return res;
|
||||
}
|
||||
|
||||
void * __sanitizer_memalign(uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_memalign(alignment, size, &stack);
|
||||
}
|
||||
|
||||
void * __sanitizer_aligned_alloc(uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_aligned_alloc(alignment, size, &stack);
|
||||
}
|
||||
|
||||
void * __sanitizer___libc_memalign(uptr alignment, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
void *ptr = hwasan_memalign(alignment, size, &stack);
|
||||
if (ptr)
|
||||
DTLS_on_libc_memalign(ptr, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void * __sanitizer_valloc(uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_valloc(size, &stack);
|
||||
}
|
||||
|
||||
void * __sanitizer_pvalloc(uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_pvalloc(size, &stack);
|
||||
}
|
||||
|
||||
void __sanitizer_free(void *ptr) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
|
||||
hwasan_free(ptr, &stack);
|
||||
}
|
||||
|
||||
void __sanitizer_cfree(void *ptr) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
|
||||
hwasan_free(ptr, &stack);
|
||||
}
|
||||
|
||||
uptr __sanitizer_malloc_usable_size(const void *ptr) {
|
||||
return __sanitizer_get_allocated_size(ptr);
|
||||
}
|
||||
|
||||
struct __sanitizer_struct_mallinfo __sanitizer_mallinfo() {
|
||||
__sanitizer_struct_mallinfo sret;
|
||||
internal_memset(&sret, 0, sizeof(sret));
|
||||
return sret;
|
||||
}
|
||||
|
||||
int __sanitizer_mallopt(int cmd, int value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __sanitizer_malloc_stats(void) {
|
||||
// FIXME: implement, but don't call REAL(malloc_stats)!
|
||||
}
|
||||
|
||||
void * __sanitizer_calloc(uptr nmemb, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (UNLIKELY(!hwasan_inited))
|
||||
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
||||
return AllocateFromLocalPool(nmemb * size);
|
||||
return hwasan_calloc(nmemb, size, &stack);
|
||||
}
|
||||
|
||||
void * __sanitizer_realloc(void *ptr, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
|
||||
uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
|
||||
uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
|
||||
void *new_ptr;
|
||||
if (UNLIKELY(!hwasan_inited)) {
|
||||
new_ptr = AllocateFromLocalPool(copy_size);
|
||||
} else {
|
||||
copy_size = size;
|
||||
new_ptr = hwasan_malloc(copy_size, &stack);
|
||||
}
|
||||
internal_memcpy(new_ptr, ptr, copy_size);
|
||||
return new_ptr;
|
||||
}
|
||||
return hwasan_realloc(ptr, size, &stack);
|
||||
}
|
||||
|
||||
void * __sanitizer_reallocarray(void *ptr, uptr nmemb, uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
return hwasan_reallocarray(ptr, nmemb, size, &stack);
|
||||
}
|
||||
|
||||
void * __sanitizer_malloc(uptr size) {
|
||||
GET_MALLOC_STACK_TRACE;
|
||||
if (UNLIKELY(!hwasan_init_is_running))
|
||||
ENSURE_HWASAN_INITED();
|
||||
if (UNLIKELY(!hwasan_inited))
|
||||
// Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
|
||||
return AllocateFromLocalPool(size);
|
||||
return hwasan_malloc(size, &stack);
|
||||
}
|
||||
|
||||
#if HWASAN_WITH_INTERCEPTORS
|
||||
#define INTERCEPTOR_ALIAS(RET, FN, ARGS...) \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE RET WRAP(FN)(ARGS) \
|
||||
ALIAS("__sanitizer_" #FN); \
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE RET FN( \
|
||||
ARGS) ALIAS("__sanitizer_" #FN)
|
||||
|
||||
INTERCEPTOR_ALIAS(int, posix_memalign, void **memptr, SIZE_T alignment,
|
||||
SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, aligned_alloc, SIZE_T alignment, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, __libc_memalign, SIZE_T alignment, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, valloc, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void, free, void *ptr);
|
||||
INTERCEPTOR_ALIAS(uptr, malloc_usable_size, const void *ptr);
|
||||
INTERCEPTOR_ALIAS(void *, calloc, SIZE_T nmemb, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, realloc, void *ptr, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, reallocarray, void *ptr, SIZE_T nmemb, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, malloc, SIZE_T size);
|
||||
|
||||
#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
|
||||
INTERCEPTOR_ALIAS(void *, memalign, SIZE_T alignment, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void *, pvalloc, SIZE_T size);
|
||||
INTERCEPTOR_ALIAS(void, cfree, void *ptr);
|
||||
INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo);
|
||||
INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value);
|
||||
INTERCEPTOR_ALIAS(void, malloc_stats, void);
|
||||
#endif
|
||||
|
||||
struct ThreadStartArg {
|
||||
thread_callback_t callback;
|
||||
@ -346,3 +168,5 @@ void InitializeInterceptors() {
|
||||
inited = 1;
|
||||
}
|
||||
} // namespace __hwasan
|
||||
|
||||
#endif // #if !SANITIZER_FUCHSIA
|
||||
|
@ -69,15 +69,9 @@ static void ProtectGap(uptr addr, uptr size) {
|
||||
|
||||
uptr kLowMemStart;
|
||||
uptr kLowMemEnd;
|
||||
uptr kLowShadowEnd;
|
||||
uptr kLowShadowStart;
|
||||
uptr kHighShadowStart;
|
||||
uptr kHighShadowEnd;
|
||||
uptr kHighMemStart;
|
||||
uptr kHighMemEnd;
|
||||
|
||||
uptr kAliasRegionStart; // Always 0 on non-x86.
|
||||
|
||||
static void PrintRange(uptr start, uptr end, const char *name) {
|
||||
Printf("|| [%p, %p] || %.*s ||\n", (void *)start, (void *)end, 10, name);
|
||||
}
|
||||
@ -116,7 +110,7 @@ static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
|
||||
FindDynamicShadowStart(shadow_size_bytes);
|
||||
}
|
||||
|
||||
void InitPrctl() {
|
||||
void InitializeOsSupport() {
|
||||
#define PR_SET_TAGGED_ADDR_CTRL 55
|
||||
#define PR_GET_TAGGED_ADDR_CTRL 56
|
||||
#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
|
||||
@ -125,33 +119,50 @@ void InitPrctl() {
|
||||
if (internal_iserror(internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
|
||||
&local_errno) &&
|
||||
local_errno == EINVAL) {
|
||||
#if SANITIZER_ANDROID || defined(__x86_64__)
|
||||
# if SANITIZER_ANDROID || defined(HWASAN_ALIASING_MODE)
|
||||
// Some older Android kernels have the tagged pointer ABI on
|
||||
// unconditionally, and hence don't have the tagged-addr prctl while still
|
||||
// allow the ABI.
|
||||
// If targeting Android and the prctl is not around we assume this is the
|
||||
// case.
|
||||
return;
|
||||
#else
|
||||
# else
|
||||
if (flags()->fail_without_syscall_abi) {
|
||||
Printf(
|
||||
"FATAL: "
|
||||
"HWAddressSanitizer requires a kernel with tagged address ABI.\n");
|
||||
Die();
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
|
||||
// Turn on the tagged address ABI.
|
||||
if ((internal_iserror(internal_prctl(PR_SET_TAGGED_ADDR_CTRL,
|
||||
PR_TAGGED_ADDR_ENABLE, 0, 0, 0)) ||
|
||||
!internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0)) &&
|
||||
flags()->fail_without_syscall_abi) {
|
||||
Printf(
|
||||
"FATAL: HWAddressSanitizer failed to enable tagged address syscall "
|
||||
"ABI.\nSuggest check `sysctl abi.tagged_addr_disabled` "
|
||||
"configuration.\n");
|
||||
Die();
|
||||
!internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0))) {
|
||||
# if defined(__x86_64__) && !defined(HWASAN_ALIASING_MODE)
|
||||
// Try the new prctl API for Intel LAM. The API is based on a currently
|
||||
// unsubmitted patch to the Linux kernel (as of May 2021) and is thus
|
||||
// subject to change. Patch is here:
|
||||
// https://lore.kernel.org/linux-mm/20210205151631.43511-12-kirill.shutemov@linux.intel.com/
|
||||
int tag_bits = kTagBits;
|
||||
int tag_shift = kAddressTagShift;
|
||||
if (!internal_iserror(
|
||||
internal_prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE,
|
||||
reinterpret_cast<unsigned long>(&tag_bits),
|
||||
reinterpret_cast<unsigned long>(&tag_shift), 0))) {
|
||||
CHECK_EQ(tag_bits, kTagBits);
|
||||
CHECK_EQ(tag_shift, kAddressTagShift);
|
||||
return;
|
||||
}
|
||||
# endif // defined(__x86_64__) && !defined(HWASAN_ALIASING_MODE)
|
||||
if (flags()->fail_without_syscall_abi) {
|
||||
Printf(
|
||||
"FATAL: HWAddressSanitizer failed to enable tagged address syscall "
|
||||
"ABI.\nSuggest check `sysctl abi.tagged_addr_disabled` "
|
||||
"configuration.\n");
|
||||
Die();
|
||||
}
|
||||
}
|
||||
#undef PR_SET_TAGGED_ADDR_CTRL
|
||||
#undef PR_GET_TAGGED_ADDR_CTRL
|
||||
@ -181,18 +192,6 @@ bool InitShadow() {
|
||||
// High memory starts where allocated shadow allows.
|
||||
kHighMemStart = ShadowToMem(kHighShadowStart);
|
||||
|
||||
#if defined(__x86_64__)
|
||||
constexpr uptr kAliasRegionOffset = 1ULL << (kTaggableRegionCheckShift - 1);
|
||||
kAliasRegionStart =
|
||||
__hwasan_shadow_memory_dynamic_address + kAliasRegionOffset;
|
||||
|
||||
CHECK_EQ(kAliasRegionStart >> kTaggableRegionCheckShift,
|
||||
__hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift);
|
||||
CHECK_EQ(
|
||||
(kAliasRegionStart + kAliasRegionOffset - 1) >> kTaggableRegionCheckShift,
|
||||
__hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift);
|
||||
#endif
|
||||
|
||||
// Check the sanity of the defined memory ranges (there might be gaps).
|
||||
CHECK_EQ(kHighMemStart % GetMmapGranularity(), 0);
|
||||
CHECK_GT(kHighMemStart, kHighShadowEnd);
|
||||
@ -233,25 +232,16 @@ void InitThreads() {
|
||||
ProtectGap(thread_space_end,
|
||||
__hwasan_shadow_memory_dynamic_address - thread_space_end);
|
||||
InitThreadList(thread_space_start, thread_space_end - thread_space_start);
|
||||
hwasanThreadList().CreateCurrentThread();
|
||||
}
|
||||
|
||||
bool MemIsApp(uptr p) {
|
||||
#if !defined(__x86_64__) // Memory outside the alias range has non-zero tags.
|
||||
// Memory outside the alias range has non-zero tags.
|
||||
# if !defined(HWASAN_ALIASING_MODE)
|
||||
CHECK(GetTagFromPointer(p) == 0);
|
||||
#endif
|
||||
return p >= kHighMemStart || (p >= kLowMemStart && p <= kLowMemEnd);
|
||||
}
|
||||
# endif
|
||||
|
||||
static void HwasanAtExit(void) {
|
||||
if (common_flags()->print_module_map)
|
||||
DumpProcessMap();
|
||||
if (flags()->print_stats && (flags()->atexit || hwasan_report_count > 0))
|
||||
ReportStats();
|
||||
if (hwasan_report_count > 0) {
|
||||
// ReportAtExitStatistics();
|
||||
if (common_flags()->exitcode)
|
||||
internal__exit(common_flags()->exitcode);
|
||||
}
|
||||
return p >= kHighMemStart || (p >= kLowMemStart && p <= kLowMemEnd);
|
||||
}
|
||||
|
||||
void InstallAtExitHandler() {
|
||||
@ -330,22 +320,6 @@ void AndroidTestTlsSlot() {
|
||||
void AndroidTestTlsSlot() {}
|
||||
#endif
|
||||
|
||||
Thread *GetCurrentThread() {
|
||||
uptr *ThreadLongPtr = GetCurrentThreadLongPtr();
|
||||
if (UNLIKELY(*ThreadLongPtr == 0))
|
||||
return nullptr;
|
||||
auto *R = (StackAllocationsRingBuffer *)ThreadLongPtr;
|
||||
return hwasanThreadList().GetThreadByBufferAddress((uptr)R->Next());
|
||||
}
|
||||
|
||||
struct AccessInfo {
|
||||
uptr addr;
|
||||
uptr size;
|
||||
bool is_store;
|
||||
bool is_load;
|
||||
bool recover;
|
||||
};
|
||||
|
||||
static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) {
|
||||
// Access type is passed in a platform dependent way (see below) and encoded
|
||||
// as 0xXY, where X&1 is 1 for store, 0 for load, and X&2 is 1 if the error is
|
||||
@ -396,28 +370,6 @@ static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) {
|
||||
return AccessInfo{addr, size, is_store, !is_store, recover};
|
||||
}
|
||||
|
||||
static void HandleTagMismatch(AccessInfo ai, uptr pc, uptr frame,
|
||||
ucontext_t *uc, uptr *registers_frame = nullptr) {
|
||||
InternalMmapVector<BufferedStackTrace> stack_buffer(1);
|
||||
BufferedStackTrace *stack = stack_buffer.data();
|
||||
stack->Reset();
|
||||
stack->Unwind(pc, frame, uc, common_flags()->fast_unwind_on_fatal);
|
||||
|
||||
// The second stack frame contains the failure __hwasan_check function, as
|
||||
// we have a stack frame for the registers saved in __hwasan_tag_mismatch that
|
||||
// we wish to ignore. This (currently) only occurs on AArch64, as x64
|
||||
// implementations use SIGTRAP to implement the failure, and thus do not go
|
||||
// through the stack saver.
|
||||
if (registers_frame && stack->trace && stack->size > 0) {
|
||||
stack->trace++;
|
||||
stack->size--;
|
||||
}
|
||||
|
||||
bool fatal = flags()->halt_on_error || !ai.recover;
|
||||
ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store, fatal,
|
||||
registers_frame);
|
||||
}
|
||||
|
||||
static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
|
||||
AccessInfo ai = GetAccessInfo(info, uc);
|
||||
if (!ai.is_store && !ai.is_load)
|
||||
@ -450,27 +402,39 @@ void HwasanOnDeadlySignal(int signo, void *info, void *context) {
|
||||
HandleDeadlySignal(info, context, GetTid(), &OnStackUnwind, nullptr);
|
||||
}
|
||||
|
||||
void Thread::InitStackAndTls(const InitState *) {
|
||||
uptr tls_size;
|
||||
uptr stack_size;
|
||||
GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_,
|
||||
&tls_size);
|
||||
stack_top_ = stack_bottom_ + stack_size;
|
||||
tls_end_ = tls_begin_ + tls_size;
|
||||
}
|
||||
|
||||
uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) {
|
||||
CHECK(IsAligned(p, kShadowAlignment));
|
||||
CHECK(IsAligned(size, kShadowAlignment));
|
||||
uptr shadow_start = MemToShadow(p);
|
||||
uptr shadow_size = MemToShadowSize(size);
|
||||
|
||||
uptr page_size = GetPageSizeCached();
|
||||
uptr page_start = RoundUpTo(shadow_start, page_size);
|
||||
uptr page_end = RoundDownTo(shadow_start + shadow_size, page_size);
|
||||
uptr threshold = common_flags()->clear_shadow_mmap_threshold;
|
||||
if (SANITIZER_LINUX &&
|
||||
UNLIKELY(page_end >= page_start + threshold && tag == 0)) {
|
||||
internal_memset((void *)shadow_start, tag, page_start - shadow_start);
|
||||
internal_memset((void *)page_end, tag,
|
||||
shadow_start + shadow_size - page_end);
|
||||
// For an anonymous private mapping MADV_DONTNEED will return a zero page on
|
||||
// Linux.
|
||||
ReleaseMemoryPagesToOSAndZeroFill(page_start, page_end);
|
||||
} else {
|
||||
internal_memset((void *)shadow_start, tag, shadow_size);
|
||||
}
|
||||
return AddTagToPointer(p, tag);
|
||||
}
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
// Entry point for interoperability between __hwasan_tag_mismatch (ASM) and the
|
||||
// rest of the mismatch handling code (C++).
|
||||
void __hwasan_tag_mismatch4(uptr addr, uptr access_info, uptr *registers_frame,
|
||||
size_t outsize) {
|
||||
__hwasan::AccessInfo ai;
|
||||
ai.is_store = access_info & 0x10;
|
||||
ai.is_load = !ai.is_store;
|
||||
ai.recover = access_info & 0x20;
|
||||
ai.addr = addr;
|
||||
if ((access_info & 0xf) == 0xf)
|
||||
ai.size = outsize;
|
||||
else
|
||||
ai.size = 1 << (access_info & 0xf);
|
||||
|
||||
__hwasan::HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
|
||||
(uptr)__builtin_frame_address(0), nullptr,
|
||||
registers_frame);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
|
||||
|
@ -48,14 +48,14 @@ extern uptr kHighShadowEnd;
|
||||
extern uptr kHighMemStart;
|
||||
extern uptr kHighMemEnd;
|
||||
|
||||
extern uptr kAliasRegionStart;
|
||||
|
||||
inline uptr GetShadowOffset() {
|
||||
return SANITIZER_FUCHSIA ? 0 : __hwasan_shadow_memory_dynamic_address;
|
||||
}
|
||||
inline uptr MemToShadow(uptr untagged_addr) {
|
||||
return (untagged_addr >> kShadowScale) +
|
||||
__hwasan_shadow_memory_dynamic_address;
|
||||
return (untagged_addr >> kShadowScale) + GetShadowOffset();
|
||||
}
|
||||
inline uptr ShadowToMem(uptr shadow_addr) {
|
||||
return (shadow_addr - __hwasan_shadow_memory_dynamic_address) << kShadowScale;
|
||||
return (shadow_addr - GetShadowOffset()) << kShadowScale;
|
||||
}
|
||||
inline uptr MemToShadowSize(uptr size) {
|
||||
return size >> kShadowScale;
|
||||
@ -63,6 +63,13 @@ inline uptr MemToShadowSize(uptr size) {
|
||||
|
||||
bool MemIsApp(uptr p);
|
||||
|
||||
inline bool MemIsShadow(uptr p) {
|
||||
return (kLowShadowStart <= p && p <= kLowShadowEnd) ||
|
||||
(kHighShadowStart <= p && p <= kHighShadowEnd);
|
||||
}
|
||||
|
||||
uptr GetAliasRegionStart();
|
||||
|
||||
} // namespace __hwasan
|
||||
|
||||
#endif // HWASAN_MAPPING_H
|
||||
|
@ -56,7 +56,6 @@ using namespace __hwasan;
|
||||
// Fake std::nothrow_t to avoid including <new>.
|
||||
namespace std {
|
||||
struct nothrow_t {};
|
||||
enum class align_val_t : size_t {};
|
||||
} // namespace std
|
||||
|
||||
|
||||
@ -73,6 +72,32 @@ INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void *operator new[](size_t size, std::nothrow_t const&) {
|
||||
OPERATOR_NEW_BODY(true /*nothrow*/);
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete(void *ptr)
|
||||
NOEXCEPT {
|
||||
OPERATOR_DELETE_BODY;
|
||||
}
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete[](
|
||||
void *ptr) NOEXCEPT {
|
||||
OPERATOR_DELETE_BODY;
|
||||
}
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete(
|
||||
void *ptr, std::nothrow_t const &) {
|
||||
OPERATOR_DELETE_BODY;
|
||||
}
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete[](
|
||||
void *ptr, std::nothrow_t const &) {
|
||||
OPERATOR_DELETE_BODY;
|
||||
}
|
||||
|
||||
#endif // OPERATOR_NEW_BODY
|
||||
|
||||
#ifdef OPERATOR_NEW_ALIGN_BODY
|
||||
|
||||
namespace std {
|
||||
enum class align_val_t : size_t {};
|
||||
} // namespace std
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void *operator new(
|
||||
size_t size, std::align_val_t align) {
|
||||
OPERATOR_NEW_ALIGN_BODY(false /*nothrow*/);
|
||||
@ -90,16 +115,6 @@ INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void *operator new[](
|
||||
OPERATOR_NEW_ALIGN_BODY(true /*nothrow*/);
|
||||
}
|
||||
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; }
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void operator delete[](void *ptr, std::nothrow_t const&) {
|
||||
OPERATOR_DELETE_BODY;
|
||||
}
|
||||
INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete(
|
||||
void *ptr, std::align_val_t align) NOEXCEPT {
|
||||
OPERATOR_DELETE_BODY;
|
||||
@ -117,4 +132,4 @@ INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete[](
|
||||
OPERATOR_DELETE_BODY;
|
||||
}
|
||||
|
||||
#endif // OPERATOR_NEW_BODY
|
||||
#endif // OPERATOR_NEW_ALIGN_BODY
|
||||
|
@ -19,30 +19,6 @@
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) {
|
||||
CHECK(IsAligned(p, kShadowAlignment));
|
||||
CHECK(IsAligned(size, kShadowAlignment));
|
||||
uptr shadow_start = MemToShadow(p);
|
||||
uptr shadow_size = MemToShadowSize(size);
|
||||
|
||||
uptr page_size = GetPageSizeCached();
|
||||
uptr page_start = RoundUpTo(shadow_start, page_size);
|
||||
uptr page_end = RoundDownTo(shadow_start + shadow_size, page_size);
|
||||
uptr threshold = common_flags()->clear_shadow_mmap_threshold;
|
||||
if (SANITIZER_LINUX &&
|
||||
UNLIKELY(page_end >= page_start + threshold && tag == 0)) {
|
||||
internal_memset((void *)shadow_start, tag, page_start - shadow_start);
|
||||
internal_memset((void *)page_end, tag,
|
||||
shadow_start + shadow_size - page_end);
|
||||
// For an anonymous private mapping MADV_DONTNEED will return a zero page on
|
||||
// Linux.
|
||||
ReleaseMemoryPagesToOSAndZeroFill(page_start, page_end);
|
||||
} else {
|
||||
internal_memset((void *)shadow_start, tag, shadow_size);
|
||||
}
|
||||
return AddTagToPointer(p, tag);
|
||||
}
|
||||
|
||||
uptr TagMemory(uptr p, uptr size, tag_t tag) {
|
||||
uptr start = RoundDownTo(p, kShadowAlignment);
|
||||
uptr end = RoundUpTo(p + size, kShadowAlignment);
|
||||
|
@ -236,12 +236,12 @@ static void PrintStackAllocations(StackAllocationsRingBuffer *sa,
|
||||
frame_desc.append(" record_addr:0x%zx record:0x%zx",
|
||||
reinterpret_cast<uptr>(record_addr), record);
|
||||
if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
|
||||
RenderFrame(&frame_desc, " %F %L\n", 0, frame->info.address, &frame->info,
|
||||
RenderFrame(&frame_desc, " %F %L", 0, frame->info.address, &frame->info,
|
||||
common_flags()->symbolize_vs_style,
|
||||
common_flags()->strip_path_prefix);
|
||||
frame->ClearAll();
|
||||
}
|
||||
Printf("%s", frame_desc.data());
|
||||
Printf("%s\n", frame_desc.data());
|
||||
frame_desc.clear();
|
||||
}
|
||||
}
|
||||
@ -296,6 +296,75 @@ static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ShowHeapOrGlobalCandidate(uptr untagged_addr, tag_t *candidate,
|
||||
tag_t *left, tag_t *right) {
|
||||
Decorator d;
|
||||
uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
|
||||
HwasanChunkView chunk = FindHeapChunkByAddress(mem);
|
||||
if (chunk.IsAllocated()) {
|
||||
uptr offset;
|
||||
const char *whence;
|
||||
if (untagged_addr < chunk.End() && untagged_addr >= chunk.Beg()) {
|
||||
offset = untagged_addr - chunk.Beg();
|
||||
whence = "inside";
|
||||
} else if (candidate == left) {
|
||||
offset = untagged_addr - chunk.End();
|
||||
whence = "to the right of";
|
||||
} else {
|
||||
offset = chunk.Beg() - untagged_addr;
|
||||
whence = "to the left of";
|
||||
}
|
||||
Printf("%s", d.Error());
|
||||
Printf("\nCause: heap-buffer-overflow\n");
|
||||
Printf("%s", d.Default());
|
||||
Printf("%s", d.Location());
|
||||
Printf("%p is located %zd bytes %s %zd-byte region [%p,%p)\n",
|
||||
untagged_addr, offset, whence, chunk.UsedSize(), chunk.Beg(),
|
||||
chunk.End());
|
||||
Printf("%s", d.Allocation());
|
||||
Printf("allocated here:\n");
|
||||
Printf("%s", d.Default());
|
||||
GetStackTraceFromId(chunk.GetAllocStackId()).Print();
|
||||
return;
|
||||
}
|
||||
// Check whether the address points into a loaded library. If so, this is
|
||||
// most likely a global variable.
|
||||
const char *module_name;
|
||||
uptr module_address;
|
||||
Symbolizer *sym = Symbolizer::GetOrInit();
|
||||
if (sym->GetModuleNameAndOffsetForPC(mem, &module_name, &module_address)) {
|
||||
Printf("%s", d.Error());
|
||||
Printf("\nCause: global-overflow\n");
|
||||
Printf("%s", d.Default());
|
||||
DataInfo info;
|
||||
Printf("%s", d.Location());
|
||||
if (sym->SymbolizeData(mem, &info) && info.start) {
|
||||
Printf(
|
||||
"%p is located %zd bytes to the %s of %zd-byte global variable "
|
||||
"%s [%p,%p) in %s\n",
|
||||
untagged_addr,
|
||||
candidate == left ? untagged_addr - (info.start + info.size)
|
||||
: info.start - untagged_addr,
|
||||
candidate == left ? "right" : "left", info.size, info.name,
|
||||
info.start, info.start + info.size, module_name);
|
||||
} else {
|
||||
uptr size = GetGlobalSizeFromDescriptor(mem);
|
||||
if (size == 0)
|
||||
// We couldn't find the size of the global from the descriptors.
|
||||
Printf("%p is located to the %s of a global variable in (%s+0x%x)\n",
|
||||
untagged_addr, candidate == left ? "right" : "left", module_name,
|
||||
module_address);
|
||||
else
|
||||
Printf(
|
||||
"%p is located to the %s of a %zd-byte global variable in "
|
||||
"(%s+0x%x)\n",
|
||||
untagged_addr, candidate == left ? "right" : "left", size,
|
||||
module_name, module_address);
|
||||
}
|
||||
Printf("%s", d.Default());
|
||||
}
|
||||
}
|
||||
|
||||
void PrintAddressDescription(
|
||||
uptr tagged_addr, uptr access_size,
|
||||
StackAllocationsRingBuffer *current_stack_allocations) {
|
||||
@ -317,78 +386,59 @@ void PrintAddressDescription(
|
||||
d.Default());
|
||||
}
|
||||
|
||||
tag_t addr_tag = GetTagFromPointer(tagged_addr);
|
||||
|
||||
bool on_stack = false;
|
||||
// Check stack first. If the address is on the stack of a live thread, we
|
||||
// know it cannot be a heap / global overflow.
|
||||
hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
|
||||
if (t->AddrIsInStack(untagged_addr)) {
|
||||
on_stack = true;
|
||||
// TODO(fmayer): figure out how to distinguish use-after-return and
|
||||
// stack-buffer-overflow.
|
||||
Printf("%s", d.Error());
|
||||
Printf("\nCause: stack tag-mismatch\n");
|
||||
Printf("%s", d.Location());
|
||||
Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
|
||||
t->unique_id());
|
||||
Printf("%s", d.Default());
|
||||
t->Announce();
|
||||
|
||||
auto *sa = (t == GetCurrentThread() && current_stack_allocations)
|
||||
? current_stack_allocations
|
||||
: t->stack_allocations();
|
||||
PrintStackAllocations(sa, addr_tag, untagged_addr);
|
||||
num_descriptions_printed++;
|
||||
}
|
||||
});
|
||||
|
||||
// Check if this looks like a heap buffer overflow by scanning
|
||||
// the shadow left and right and looking for the first adjacent
|
||||
// object with a different memory tag. If that tag matches addr_tag,
|
||||
// check the allocator if it has a live chunk there.
|
||||
tag_t addr_tag = GetTagFromPointer(tagged_addr);
|
||||
tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
|
||||
tag_t *candidate = nullptr, *left = tag_ptr, *right = tag_ptr;
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
if (TagsEqual(addr_tag, left)) {
|
||||
uptr candidate_distance = 0;
|
||||
for (; candidate_distance < 1000; candidate_distance++) {
|
||||
if (MemIsShadow(reinterpret_cast<uptr>(left)) &&
|
||||
TagsEqual(addr_tag, left)) {
|
||||
candidate = left;
|
||||
break;
|
||||
}
|
||||
--left;
|
||||
if (TagsEqual(addr_tag, right)) {
|
||||
if (MemIsShadow(reinterpret_cast<uptr>(right)) &&
|
||||
TagsEqual(addr_tag, right)) {
|
||||
candidate = right;
|
||||
break;
|
||||
}
|
||||
++right;
|
||||
}
|
||||
|
||||
if (candidate) {
|
||||
uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
|
||||
HwasanChunkView chunk = FindHeapChunkByAddress(mem);
|
||||
if (chunk.IsAllocated()) {
|
||||
Printf("%s", d.Location());
|
||||
Printf("%p is located %zd bytes to the %s of %zd-byte region [%p,%p)\n",
|
||||
untagged_addr,
|
||||
candidate == left ? untagged_addr - chunk.End()
|
||||
: chunk.Beg() - untagged_addr,
|
||||
candidate == left ? "right" : "left", chunk.UsedSize(),
|
||||
chunk.Beg(), chunk.End());
|
||||
Printf("%s", d.Allocation());
|
||||
Printf("allocated here:\n");
|
||||
Printf("%s", d.Default());
|
||||
GetStackTraceFromId(chunk.GetAllocStackId()).Print();
|
||||
num_descriptions_printed++;
|
||||
} else {
|
||||
// Check whether the address points into a loaded library. If so, this is
|
||||
// most likely a global variable.
|
||||
const char *module_name;
|
||||
uptr module_address;
|
||||
Symbolizer *sym = Symbolizer::GetOrInit();
|
||||
if (sym->GetModuleNameAndOffsetForPC(mem, &module_name,
|
||||
&module_address)) {
|
||||
DataInfo info;
|
||||
if (sym->SymbolizeData(mem, &info) && info.start) {
|
||||
Printf(
|
||||
"%p is located %zd bytes to the %s of %zd-byte global variable "
|
||||
"%s [%p,%p) in %s\n",
|
||||
untagged_addr,
|
||||
candidate == left ? untagged_addr - (info.start + info.size)
|
||||
: info.start - untagged_addr,
|
||||
candidate == left ? "right" : "left", info.size, info.name,
|
||||
info.start, info.start + info.size, module_name);
|
||||
} else {
|
||||
uptr size = GetGlobalSizeFromDescriptor(mem);
|
||||
if (size == 0)
|
||||
// We couldn't find the size of the global from the descriptors.
|
||||
Printf(
|
||||
"%p is located to the %s of a global variable in (%s+0x%x)\n",
|
||||
untagged_addr, candidate == left ? "right" : "left",
|
||||
module_name, module_address);
|
||||
else
|
||||
Printf(
|
||||
"%p is located to the %s of a %zd-byte global variable in "
|
||||
"(%s+0x%x)\n",
|
||||
untagged_addr, candidate == left ? "right" : "left", size,
|
||||
module_name, module_address);
|
||||
}
|
||||
num_descriptions_printed++;
|
||||
}
|
||||
}
|
||||
constexpr auto kCloseCandidateDistance = 1;
|
||||
|
||||
if (!on_stack && candidate && candidate_distance <= kCloseCandidateDistance) {
|
||||
ShowHeapOrGlobalCandidate(untagged_addr, candidate, left, right);
|
||||
num_descriptions_printed++;
|
||||
}
|
||||
|
||||
hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
|
||||
@ -398,6 +448,8 @@ void PrintAddressDescription(
|
||||
if (FindHeapAllocation(t->heap_allocations(), tagged_addr, &har,
|
||||
&ring_index, &num_matching_addrs,
|
||||
&num_matching_addrs_4b)) {
|
||||
Printf("%s", d.Error());
|
||||
Printf("\nCause: use-after-free\n");
|
||||
Printf("%s", d.Location());
|
||||
Printf("%p is located %zd bytes inside of %zd-byte region [%p,%p)\n",
|
||||
untagged_addr, untagged_addr - UntagAddr(har.tagged_addr),
|
||||
@ -424,29 +476,25 @@ void PrintAddressDescription(
|
||||
t->Announce();
|
||||
num_descriptions_printed++;
|
||||
}
|
||||
|
||||
// Very basic check for stack memory.
|
||||
if (t->AddrIsInStack(untagged_addr)) {
|
||||
Printf("%s", d.Location());
|
||||
Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
|
||||
t->unique_id());
|
||||
Printf("%s", d.Default());
|
||||
t->Announce();
|
||||
|
||||
auto *sa = (t == GetCurrentThread() && current_stack_allocations)
|
||||
? current_stack_allocations
|
||||
: t->stack_allocations();
|
||||
PrintStackAllocations(sa, addr_tag, untagged_addr);
|
||||
num_descriptions_printed++;
|
||||
}
|
||||
});
|
||||
|
||||
if (candidate && num_descriptions_printed == 0) {
|
||||
ShowHeapOrGlobalCandidate(untagged_addr, candidate, left, right);
|
||||
num_descriptions_printed++;
|
||||
}
|
||||
|
||||
// Print the remaining threads, as an extra information, 1 line per thread.
|
||||
hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });
|
||||
|
||||
if (!num_descriptions_printed)
|
||||
// We exhausted our possibilities. Bail out.
|
||||
Printf("HWAddressSanitizer can not describe address in more detail.\n");
|
||||
if (num_descriptions_printed > 1) {
|
||||
Printf(
|
||||
"There are %d potential causes, printed above in order "
|
||||
"of likeliness.\n",
|
||||
num_descriptions_printed);
|
||||
}
|
||||
}
|
||||
|
||||
void ReportStats() {}
|
||||
@ -538,6 +586,12 @@ void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
|
||||
Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName,
|
||||
bug_type, untagged_addr, untagged_addr + orig_size, orig_size);
|
||||
Printf("\n%s", d.Default());
|
||||
Printf(
|
||||
"Stack of invalid access unknown. Issue detected at deallocation "
|
||||
"time.\n");
|
||||
Printf("%s", d.Allocation());
|
||||
Printf("deallocated here:\n");
|
||||
Printf("%s", d.Default());
|
||||
stack->Print();
|
||||
HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
|
||||
if (chunk.Beg()) {
|
||||
@ -657,8 +711,10 @@ void ReportRegisters(uptr *frame, uptr pc) {
|
||||
frame[20], frame[21], frame[22], frame[23]);
|
||||
Printf(" x24 %016llx x25 %016llx x26 %016llx x27 %016llx\n",
|
||||
frame[24], frame[25], frame[26], frame[27]);
|
||||
Printf(" x28 %016llx x29 %016llx x30 %016llx\n",
|
||||
frame[28], frame[29], frame[30]);
|
||||
// hwasan_check* reduces the stack pointer by 256, then __hwasan_tag_mismatch
|
||||
// passes it to this function.
|
||||
Printf(" x28 %016llx x29 %016llx x30 %016llx sp %016llx\n", frame[28],
|
||||
frame[29], frame[30], reinterpret_cast<u8 *>(frame) + 256);
|
||||
}
|
||||
|
||||
} // namespace __hwasan
|
||||
|
@ -34,7 +34,8 @@ void Thread::InitRandomState() {
|
||||
stack_allocations_->push(0);
|
||||
}
|
||||
|
||||
void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size) {
|
||||
void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size,
|
||||
const InitState *state) {
|
||||
CHECK_EQ(0, unique_id_); // try to catch bad stack reuse
|
||||
CHECK_EQ(0, stack_top_);
|
||||
CHECK_EQ(0, stack_bottom_);
|
||||
@ -44,6 +45,17 @@ void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size) {
|
||||
if (auto sz = flags()->heap_history_size)
|
||||
heap_allocations_ = HeapAllocationsRingBuffer::New(sz);
|
||||
|
||||
InitStackAndTls(state);
|
||||
#if !SANITIZER_FUCHSIA
|
||||
// Do not initialize the stack ring buffer just yet on Fuchsia. Threads will
|
||||
// be initialized before we enter the thread itself, so we will instead call
|
||||
// this later.
|
||||
InitStackRingBuffer(stack_buffer_start, stack_buffer_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Thread::InitStackRingBuffer(uptr stack_buffer_start,
|
||||
uptr stack_buffer_size) {
|
||||
HwasanTSDThreadInit(); // Only needed with interceptors.
|
||||
uptr *ThreadLong = GetCurrentThreadLongPtr();
|
||||
// The following implicitly sets (this) as the current thread.
|
||||
@ -55,13 +67,6 @@ void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size) {
|
||||
// ScopedTaggingDisable needs GetCurrentThread to be set up.
|
||||
ScopedTaggingDisabler disabler;
|
||||
|
||||
uptr tls_size;
|
||||
uptr stack_size;
|
||||
GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_,
|
||||
&tls_size);
|
||||
stack_top_ = stack_bottom_ + stack_size;
|
||||
tls_end_ = tls_begin_ + tls_size;
|
||||
|
||||
if (stack_bottom_) {
|
||||
int local;
|
||||
CHECK(AddrIsInStack((uptr)&local));
|
||||
|
@ -23,8 +23,17 @@ typedef __sanitizer::CompactRingBuffer<uptr> StackAllocationsRingBuffer;
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
void Init(uptr stack_buffer_start, uptr stack_buffer_size); // Must be called from the thread itself.
|
||||
// These are optional parameters that can be passed to Init.
|
||||
struct InitState;
|
||||
|
||||
void Init(uptr stack_buffer_start, uptr stack_buffer_size,
|
||||
const InitState *state = nullptr);
|
||||
void InitRandomState();
|
||||
void InitStackAndTls(const InitState *state = nullptr);
|
||||
|
||||
// Must be called from the thread itself.
|
||||
void InitStackRingBuffer(uptr stack_buffer_start, uptr stack_buffer_size);
|
||||
|
||||
void Destroy();
|
||||
|
||||
uptr stack_top() { return stack_top_; }
|
||||
|
@ -12,4 +12,4 @@ void InitThreadList(uptr storage, uptr size) {
|
||||
new (thread_list_placeholder) HwasanThreadList(storage, size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace __hwasan
|
||||
|
@ -85,7 +85,7 @@ class HwasanThreadList {
|
||||
RoundUpTo(ring_buffer_size_ + sizeof(Thread), ring_buffer_size_ * 2);
|
||||
}
|
||||
|
||||
Thread *CreateCurrentThread() {
|
||||
Thread *CreateCurrentThread(const Thread::InitState *state = nullptr) {
|
||||
Thread *t = nullptr;
|
||||
{
|
||||
SpinMutexLock l(&free_list_mutex_);
|
||||
@ -104,7 +104,7 @@ class HwasanThreadList {
|
||||
SpinMutexLock l(&live_list_mutex_);
|
||||
live_list_.push_back(t);
|
||||
}
|
||||
t->Init((uptr)t - ring_buffer_size_, ring_buffer_size_);
|
||||
t->Init((uptr)t - ring_buffer_size_, ring_buffer_size_, state);
|
||||
AddThreadStats(t);
|
||||
return t;
|
||||
}
|
||||
@ -171,6 +171,8 @@ class HwasanThreadList {
|
||||
return stats_;
|
||||
}
|
||||
|
||||
uptr GetRingBufferSize() const { return ring_buffer_size_; }
|
||||
|
||||
private:
|
||||
Thread *AllocThread() {
|
||||
SpinMutexLock l(&free_space_mutex_);
|
||||
@ -200,4 +202,4 @@ class HwasanThreadList {
|
||||
void InitThreadList(uptr storage, uptr size);
|
||||
HwasanThreadList &hwasanThreadList();
|
||||
|
||||
} // namespace
|
||||
} // namespace __hwasan
|
||||
|
@ -21,34 +21,15 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef uint16_t dfsan_label;
|
||||
typedef uint8_t dfsan_label;
|
||||
typedef uint32_t dfsan_origin;
|
||||
|
||||
/// Stores information associated with a specific label identifier. A label
|
||||
/// may be a base label created using dfsan_create_label, with associated
|
||||
/// text description and user data, or an automatically created union label,
|
||||
/// which represents the union of two label identifiers (which may themselves
|
||||
/// be base or union labels).
|
||||
struct dfsan_label_info {
|
||||
// Fields for union labels, set to 0 for base labels.
|
||||
dfsan_label l1;
|
||||
dfsan_label l2;
|
||||
|
||||
// Fields for base labels.
|
||||
const char *desc;
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
/// Signature of the callback argument to dfsan_set_write_callback().
|
||||
typedef void (*dfsan_write_callback_t)(int fd, const void *buf, size_t count);
|
||||
|
||||
/// Computes the union of \c l1 and \c l2, possibly creating a union label in
|
||||
/// the process.
|
||||
/// Computes the union of \c l1 and \c l2, resulting in a union label.
|
||||
dfsan_label dfsan_union(dfsan_label l1, dfsan_label l2);
|
||||
|
||||
/// Creates and returns a base label with the given description and user data.
|
||||
dfsan_label dfsan_create_label(const char *desc, void *userdata);
|
||||
|
||||
/// Sets the label for each address in [addr,addr+size) to \c label.
|
||||
void dfsan_set_label(dfsan_label label, void *addr, size_t size);
|
||||
|
||||
@ -73,19 +54,9 @@ dfsan_origin dfsan_get_origin(long data);
|
||||
/// Retrieves the label associated with the data at the given address.
|
||||
dfsan_label dfsan_read_label(const void *addr, size_t size);
|
||||
|
||||
/// Retrieves a pointer to the dfsan_label_info struct for the given label.
|
||||
const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label);
|
||||
|
||||
/// Returns whether the given label label contains the label elem.
|
||||
int dfsan_has_label(dfsan_label label, dfsan_label elem);
|
||||
|
||||
/// If the given label label contains a label with the description desc, returns
|
||||
/// that label, else returns 0.
|
||||
dfsan_label dfsan_has_label_with_desc(dfsan_label label, const char *desc);
|
||||
|
||||
/// Returns the number of labels allocated.
|
||||
size_t dfsan_get_label_count(void);
|
||||
|
||||
/// Flushes the DFSan shadow, i.e. forgets about all labels currently associated
|
||||
/// with the application memory. Use this call to start over the taint tracking
|
||||
/// within the same process.
|
||||
@ -99,12 +70,6 @@ void dfsan_flush(void);
|
||||
/// callback executes. Pass in NULL to remove any callback.
|
||||
void dfsan_set_write_callback(dfsan_write_callback_t labeled_write_callback);
|
||||
|
||||
/// Writes the labels currently used by the program to the given file
|
||||
/// descriptor. The lines of the output have the following format:
|
||||
///
|
||||
/// <label> <parent label 1> <parent label 2> <label description if any>
|
||||
void dfsan_dump_labels(int fd);
|
||||
|
||||
/// Interceptor hooks.
|
||||
/// Whenever a dfsan's custom function is called the corresponding
|
||||
/// hook is called it non-zero. The hooks should be defined by the user.
|
||||
@ -123,9 +88,65 @@ void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2,
|
||||
/// on, or the address is not labeled, it prints nothing.
|
||||
void dfsan_print_origin_trace(const void *addr, const char *description);
|
||||
|
||||
/// Prints the origin trace of the label at the address \p addr to a
|
||||
/// pre-allocated output buffer. If origin tracking is not on, or the address is
|
||||
/// not labeled, it prints nothing.
|
||||
///
|
||||
/// Typical usage:
|
||||
/// \code
|
||||
/// char kDescription[] = "...";
|
||||
/// char buf[1024];
|
||||
/// dfsan_sprint_origin_trace(&tainted_var, kDescription, buf, sizeof(buf));
|
||||
/// \endcode
|
||||
///
|
||||
/// Typical usage that handles truncation:
|
||||
/// \code
|
||||
/// char buf[1024];
|
||||
/// int len = dfsan_sprint_origin_trace(&var, nullptr, buf, sizeof(buf));
|
||||
///
|
||||
/// if (len < sizeof(buf)) {
|
||||
/// ProcessOriginTrace(buf);
|
||||
/// } else {
|
||||
/// char *tmpbuf = new char[len + 1];
|
||||
/// dfsan_sprint_origin_trace(&var, nullptr, tmpbuf, len + 1);
|
||||
/// ProcessOriginTrace(tmpbuf);
|
||||
/// delete[] tmpbuf;
|
||||
/// }
|
||||
/// \endcode
|
||||
///
|
||||
/// \param addr The tainted memory address whose origin we are printing.
|
||||
/// \param description A description printed at the beginning of the trace.
|
||||
/// \param [out] out_buf The output buffer to write the results to.
|
||||
/// \param out_buf_size The size of \p out_buf.
|
||||
///
|
||||
/// \returns The number of symbols that should have been written to \p out_buf
|
||||
/// (not including trailing null byte '\0'). Thus, the string is truncated iff
|
||||
/// return value is not less than \p out_buf_size.
|
||||
size_t dfsan_sprint_origin_trace(const void *addr, const char *description,
|
||||
char *out_buf, size_t out_buf_size);
|
||||
|
||||
/// Prints the stack trace leading to this call to a pre-allocated output
|
||||
/// buffer.
|
||||
///
|
||||
/// For usage examples, see dfsan_sprint_origin_trace.
|
||||
///
|
||||
/// \param [out] out_buf The output buffer to write the results to.
|
||||
/// \param out_buf_size The size of \p out_buf.
|
||||
///
|
||||
/// \returns The number of symbols that should have been written to \p out_buf
|
||||
/// (not including trailing null byte '\0'). Thus, the string is truncated iff
|
||||
/// return value is not less than \p out_buf_size.
|
||||
size_t dfsan_sprint_stack_trace(char *out_buf, size_t out_buf_size);
|
||||
|
||||
/// Retrieves the very first origin associated with the data at the given
|
||||
/// address.
|
||||
dfsan_origin dfsan_get_init_origin(const void *addr);
|
||||
|
||||
/// Returns the value of -dfsan-track-origins.
|
||||
/// * 0: do not track origins.
|
||||
/// * 1: track origins at memory store operations.
|
||||
/// * 2: track origins at memory load and store operations.
|
||||
int dfsan_get_track_origins(void);
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
|
||||
#if !SANITIZER_LINUX && !SANITIZER_FREEBSD && !SANITIZER_MAC && \
|
||||
!SANITIZER_NETBSD && !SANITIZER_WINDOWS && \
|
||||
!SANITIZER_FUCHSIA && !SANITIZER_RTEMS && !SANITIZER_SOLARIS
|
||||
# error "Interception doesn't work on this operating system."
|
||||
#if !SANITIZER_LINUX && !SANITIZER_FREEBSD && !SANITIZER_MAC && \
|
||||
!SANITIZER_NETBSD && !SANITIZER_WINDOWS && !SANITIZER_FUCHSIA && \
|
||||
!SANITIZER_SOLARIS
|
||||
# error "Interception doesn't work on this operating system."
|
||||
#endif
|
||||
|
||||
// These typedefs should be used only in the interceptor definitions to replace
|
||||
@ -130,11 +130,6 @@ const interpose_substitution substitution_##func_name[] \
|
||||
extern "C" ret_type func(__VA_ARGS__);
|
||||
# define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \
|
||||
extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__);
|
||||
#elif SANITIZER_RTEMS
|
||||
# define WRAP(x) x
|
||||
# define WRAPPER_NAME(x) #x
|
||||
# define INTERCEPTOR_ATTRIBUTE
|
||||
# define DECLARE_WRAPPER(ret_type, func, ...)
|
||||
#elif SANITIZER_FREEBSD || SANITIZER_NETBSD
|
||||
# define WRAP(x) __interceptor_ ## x
|
||||
# define WRAPPER_NAME(x) "__interceptor_" #x
|
||||
@ -162,10 +157,6 @@ const interpose_substitution substitution_##func_name[] \
|
||||
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
|
||||
# define REAL(x) __unsanitized_##x
|
||||
# define DECLARE_REAL(ret_type, func, ...)
|
||||
#elif SANITIZER_RTEMS
|
||||
# define REAL(x) __real_ ## x
|
||||
# define DECLARE_REAL(ret_type, func, ...) \
|
||||
extern "C" ret_type REAL(func)(__VA_ARGS__);
|
||||
#elif !SANITIZER_MAC
|
||||
# define PTR_TO_REAL(x) real_##x
|
||||
# define REAL(x) __interception::PTR_TO_REAL(x)
|
||||
@ -184,10 +175,10 @@ const interpose_substitution substitution_##func_name[] \
|
||||
# define ASSIGN_REAL(x, y)
|
||||
#endif // SANITIZER_MAC
|
||||
|
||||
#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \
|
||||
DECLARE_REAL(ret_type, func, __VA_ARGS__) \
|
||||
extern "C" ret_type WRAP(func)(__VA_ARGS__);
|
||||
#if !SANITIZER_FUCHSIA
|
||||
# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \
|
||||
DECLARE_REAL(ret_type, func, __VA_ARGS__) \
|
||||
extern "C" ret_type WRAP(func)(__VA_ARGS__);
|
||||
// Declare an interceptor and its wrapper defined in a different translation
|
||||
// unit (ex. asm).
|
||||
# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...) \
|
||||
@ -202,11 +193,11 @@ const interpose_substitution substitution_##func_name[] \
|
||||
// macros does its job. In exceptional cases you may need to call REAL(foo)
|
||||
// without defining INTERCEPTOR(..., foo, ...). For example, if you override
|
||||
// foo with an interceptor for other function.
|
||||
#if !SANITIZER_MAC && !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
# define DEFINE_REAL(ret_type, func, ...) \
|
||||
#if !SANITIZER_MAC && !SANITIZER_FUCHSIA
|
||||
# define DEFINE_REAL(ret_type, func, ...) \
|
||||
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
|
||||
namespace __interception { \
|
||||
FUNC_TYPE(func) PTR_TO_REAL(func); \
|
||||
namespace __interception { \
|
||||
FUNC_TYPE(func) PTR_TO_REAL(func); \
|
||||
}
|
||||
#else
|
||||
# define DEFINE_REAL(ret_type, func, ...)
|
||||
|
@ -35,18 +35,14 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(
|
||||
uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
|
||||
using namespace __lsan;
|
||||
uptr stack_top = 0, stack_bottom = 0;
|
||||
ThreadContext *t;
|
||||
if (StackTrace::WillUseFastUnwind(request_fast) &&
|
||||
(t = CurrentThreadContext())) {
|
||||
if (ThreadContext *t = CurrentThreadContext()) {
|
||||
stack_top = t->stack_end();
|
||||
stack_bottom = t->stack_begin();
|
||||
}
|
||||
if (!SANITIZER_MIPS || IsValidFrame(bp, stack_top, stack_bottom)) {
|
||||
if (StackTrace::WillUseFastUnwind(request_fast))
|
||||
Unwind(max_depth, pc, bp, nullptr, stack_top, stack_bottom, true);
|
||||
else
|
||||
Unwind(max_depth, pc, 0, context, 0, 0, false);
|
||||
}
|
||||
if (SANITIZER_MIPS && !IsValidFrame(bp, stack_top, stack_bottom))
|
||||
return;
|
||||
bool fast = StackTrace::WillUseFastUnwind(request_fast);
|
||||
Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, fast);
|
||||
}
|
||||
|
||||
using namespace __lsan;
|
||||
|
@ -221,8 +221,8 @@ void UnlockAllocator();
|
||||
// Returns true if [addr, addr + sizeof(void *)) is poisoned.
|
||||
bool WordIsPoisoned(uptr addr);
|
||||
// Wrappers for ThreadRegistry access.
|
||||
void LockThreadRegistry();
|
||||
void UnlockThreadRegistry();
|
||||
void LockThreadRegistry() NO_THREAD_SAFETY_ANALYSIS;
|
||||
void UnlockThreadRegistry() NO_THREAD_SAFETY_ANALYSIS;
|
||||
ThreadRegistry *GetThreadRegistryLocked();
|
||||
bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
|
||||
uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
|
||||
|
@ -30,13 +30,10 @@ static ThreadContextBase *CreateThreadContext(u32 tid) {
|
||||
return new (mem) ThreadContext(tid);
|
||||
}
|
||||
|
||||
static const uptr kMaxThreads = 1 << 13;
|
||||
static const uptr kThreadQuarantineSize = 64;
|
||||
|
||||
void InitializeThreadRegistry() {
|
||||
static ALIGNED(64) char thread_registry_placeholder[sizeof(ThreadRegistry)];
|
||||
thread_registry = new (thread_registry_placeholder)
|
||||
ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize);
|
||||
thread_registry =
|
||||
new (thread_registry_placeholder) ThreadRegistry(CreateThreadContext);
|
||||
}
|
||||
|
||||
ThreadContextLsanBase::ThreadContextLsanBase(int tid)
|
||||
|
@ -41,6 +41,7 @@ sanitizer_common_files = \
|
||||
sanitizer_linux_s390.cpp \
|
||||
sanitizer_mac.cpp \
|
||||
sanitizer_mac_libcdep.cpp \
|
||||
sanitizer_mutex.cpp \
|
||||
sanitizer_netbsd.cpp \
|
||||
sanitizer_openbsd.cpp \
|
||||
sanitizer_persistent_allocator.cpp \
|
||||
@ -57,7 +58,6 @@ sanitizer_common_files = \
|
||||
sanitizer_procmaps_linux.cpp \
|
||||
sanitizer_procmaps_mac.cpp \
|
||||
sanitizer_procmaps_solaris.cpp \
|
||||
sanitizer_rtems.cpp \
|
||||
sanitizer_solaris.cpp \
|
||||
sanitizer_stackdepot.cpp \
|
||||
sanitizer_stacktrace.cpp \
|
||||
|
@ -128,8 +128,9 @@ am__objects_1 = sancov_flags.lo sanitizer_allocator.lo \
|
||||
sanitizer_file.lo sanitizer_flags.lo sanitizer_flag_parser.lo \
|
||||
sanitizer_libc.lo sanitizer_libignore.lo sanitizer_linux.lo \
|
||||
sanitizer_linux_libcdep.lo sanitizer_linux_s390.lo \
|
||||
sanitizer_mac.lo sanitizer_mac_libcdep.lo sanitizer_netbsd.lo \
|
||||
sanitizer_openbsd.lo sanitizer_persistent_allocator.lo \
|
||||
sanitizer_mac.lo sanitizer_mac_libcdep.lo sanitizer_mutex.lo \
|
||||
sanitizer_netbsd.lo sanitizer_openbsd.lo \
|
||||
sanitizer_persistent_allocator.lo \
|
||||
sanitizer_platform_limits_freebsd.lo \
|
||||
sanitizer_platform_limits_linux.lo \
|
||||
sanitizer_platform_limits_openbsd.lo \
|
||||
@ -138,11 +139,11 @@ am__objects_1 = sancov_flags.lo sanitizer_allocator.lo \
|
||||
sanitizer_posix_libcdep.lo sanitizer_printf.lo \
|
||||
sanitizer_procmaps_bsd.lo sanitizer_procmaps_common.lo \
|
||||
sanitizer_procmaps_linux.lo sanitizer_procmaps_mac.lo \
|
||||
sanitizer_procmaps_solaris.lo sanitizer_rtems.lo \
|
||||
sanitizer_solaris.lo sanitizer_stackdepot.lo \
|
||||
sanitizer_stacktrace.lo sanitizer_stacktrace_libcdep.lo \
|
||||
sanitizer_stacktrace_sparc.lo sanitizer_symbolizer_mac.lo \
|
||||
sanitizer_symbolizer_report.lo sanitizer_stacktrace_printer.lo \
|
||||
sanitizer_procmaps_solaris.lo sanitizer_solaris.lo \
|
||||
sanitizer_stackdepot.lo sanitizer_stacktrace.lo \
|
||||
sanitizer_stacktrace_libcdep.lo sanitizer_stacktrace_sparc.lo \
|
||||
sanitizer_symbolizer_mac.lo sanitizer_symbolizer_report.lo \
|
||||
sanitizer_stacktrace_printer.lo \
|
||||
sanitizer_stoptheworld_linux_libcdep.lo \
|
||||
sanitizer_stoptheworld_mac.lo sanitizer_suppressions.lo \
|
||||
sanitizer_symbolizer.lo sanitizer_symbolizer_libbacktrace.lo \
|
||||
@ -400,6 +401,7 @@ sanitizer_common_files = \
|
||||
sanitizer_linux_s390.cpp \
|
||||
sanitizer_mac.cpp \
|
||||
sanitizer_mac_libcdep.cpp \
|
||||
sanitizer_mutex.cpp \
|
||||
sanitizer_netbsd.cpp \
|
||||
sanitizer_openbsd.cpp \
|
||||
sanitizer_persistent_allocator.cpp \
|
||||
@ -416,7 +418,6 @@ sanitizer_common_files = \
|
||||
sanitizer_procmaps_linux.cpp \
|
||||
sanitizer_procmaps_mac.cpp \
|
||||
sanitizer_procmaps_solaris.cpp \
|
||||
sanitizer_rtems.cpp \
|
||||
sanitizer_solaris.cpp \
|
||||
sanitizer_stackdepot.cpp \
|
||||
sanitizer_stacktrace.cpp \
|
||||
@ -557,6 +558,7 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_s390.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac_libcdep.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mutex.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_netbsd.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_openbsd.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_persistent_allocator.Plo@am__quote@
|
||||
@ -573,7 +575,6 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_linux.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_mac.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_solaris.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_rtems.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_solaris.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace.Plo@am__quote@
|
||||
|
@ -162,8 +162,8 @@ AddrHashMap<T, kSize>::AddrHashMap() {
|
||||
table_ = (Bucket*)MmapOrDie(kSize * sizeof(table_[0]), "AddrHashMap");
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
void AddrHashMap<T, kSize>::acquire(Handle *h) {
|
||||
template <typename T, uptr kSize>
|
||||
void AddrHashMap<T, kSize>::acquire(Handle *h) NO_THREAD_SAFETY_ANALYSIS {
|
||||
uptr addr = h->addr_;
|
||||
uptr hash = calcHash(addr);
|
||||
Bucket *b = &table_[hash];
|
||||
@ -289,57 +289,57 @@ void AddrHashMap<T, kSize>::acquire(Handle *h) {
|
||||
CHECK_EQ(atomic_load(&c->addr, memory_order_relaxed), 0);
|
||||
h->addidx_ = i;
|
||||
h->cell_ = c;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
void AddrHashMap<T, kSize>::release(Handle *h) {
|
||||
if (!h->cell_)
|
||||
return;
|
||||
Bucket *b = h->bucket_;
|
||||
Cell *c = h->cell_;
|
||||
uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
|
||||
if (h->created_) {
|
||||
// Denote completion of insertion.
|
||||
CHECK_EQ(addr1, 0);
|
||||
// After the following store, the element becomes available
|
||||
// for lock-free reads.
|
||||
atomic_store(&c->addr, h->addr_, memory_order_release);
|
||||
b->mtx.Unlock();
|
||||
} else if (h->remove_) {
|
||||
// Denote that the cell is empty now.
|
||||
CHECK_EQ(addr1, h->addr_);
|
||||
atomic_store(&c->addr, 0, memory_order_release);
|
||||
// See if we need to compact the bucket.
|
||||
AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
|
||||
if (h->addidx_ == -1U) {
|
||||
// Removed from embed array, move an add element into the freed cell.
|
||||
if (add && add->size != 0) {
|
||||
uptr last = --add->size;
|
||||
Cell *c1 = &add->cells[last];
|
||||
c->val = c1->val;
|
||||
uptr addr1 = atomic_load(&c1->addr, memory_order_relaxed);
|
||||
atomic_store(&c->addr, addr1, memory_order_release);
|
||||
atomic_store(&c1->addr, 0, memory_order_release);
|
||||
}
|
||||
} else {
|
||||
// Removed from add array, compact it.
|
||||
uptr last = --add->size;
|
||||
Cell *c1 = &add->cells[last];
|
||||
if (c != c1) {
|
||||
*c = *c1;
|
||||
atomic_store(&c1->addr, 0, memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
if (add && add->size == 0) {
|
||||
// FIXME(dvyukov): free add?
|
||||
}
|
||||
b->mtx.Unlock();
|
||||
} else {
|
||||
CHECK_EQ(addr1, h->addr_);
|
||||
if (h->addidx_ != -1U)
|
||||
b->mtx.ReadUnlock();
|
||||
}
|
||||
}
|
||||
template <typename T, uptr kSize>
|
||||
void AddrHashMap<T, kSize>::release(Handle *h) NO_THREAD_SAFETY_ANALYSIS {
|
||||
if (!h->cell_)
|
||||
return;
|
||||
Bucket *b = h->bucket_;
|
||||
Cell *c = h->cell_;
|
||||
uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
|
||||
if (h->created_) {
|
||||
// Denote completion of insertion.
|
||||
CHECK_EQ(addr1, 0);
|
||||
// After the following store, the element becomes available
|
||||
// for lock-free reads.
|
||||
atomic_store(&c->addr, h->addr_, memory_order_release);
|
||||
b->mtx.Unlock();
|
||||
} else if (h->remove_) {
|
||||
// Denote that the cell is empty now.
|
||||
CHECK_EQ(addr1, h->addr_);
|
||||
atomic_store(&c->addr, 0, memory_order_release);
|
||||
// See if we need to compact the bucket.
|
||||
AddBucket *add = (AddBucket *)atomic_load(&b->add, memory_order_relaxed);
|
||||
if (h->addidx_ == -1U) {
|
||||
// Removed from embed array, move an add element into the freed cell.
|
||||
if (add && add->size != 0) {
|
||||
uptr last = --add->size;
|
||||
Cell *c1 = &add->cells[last];
|
||||
c->val = c1->val;
|
||||
uptr addr1 = atomic_load(&c1->addr, memory_order_relaxed);
|
||||
atomic_store(&c->addr, addr1, memory_order_release);
|
||||
atomic_store(&c1->addr, 0, memory_order_release);
|
||||
}
|
||||
} else {
|
||||
// Removed from add array, compact it.
|
||||
uptr last = --add->size;
|
||||
Cell *c1 = &add->cells[last];
|
||||
if (c != c1) {
|
||||
*c = *c1;
|
||||
atomic_store(&c1->addr, 0, memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
if (add && add->size == 0) {
|
||||
// FIXME(dvyukov): free add?
|
||||
}
|
||||
b->mtx.Unlock();
|
||||
} else {
|
||||
CHECK_EQ(addr1, h->addr_);
|
||||
if (h->addidx_ != -1U)
|
||||
b->mtx.ReadUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, uptr kSize>
|
||||
uptr AddrHashMap<T, kSize>::calcHash(uptr addr) {
|
||||
|
@ -137,14 +137,6 @@ static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
|
||||
|
||||
#endif // SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
|
||||
|
||||
namespace {
|
||||
const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull;
|
||||
|
||||
struct BlockHeader {
|
||||
u64 magic;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static void NORETURN ReportInternalAllocatorOutOfMemory(uptr requested_size) {
|
||||
SetAllocatorOutOfMemory();
|
||||
Report("FATAL: %s: internal allocator is out of memory trying to allocate "
|
||||
@ -153,28 +145,17 @@ static void NORETURN ReportInternalAllocatorOutOfMemory(uptr requested_size) {
|
||||
}
|
||||
|
||||
void *InternalAlloc(uptr size, InternalAllocatorCache *cache, uptr alignment) {
|
||||
uptr s = size + sizeof(BlockHeader);
|
||||
if (s < size)
|
||||
return nullptr;
|
||||
BlockHeader *p = (BlockHeader *)RawInternalAlloc(s, cache, alignment);
|
||||
void *p = RawInternalAlloc(size, cache, alignment);
|
||||
if (UNLIKELY(!p))
|
||||
ReportInternalAllocatorOutOfMemory(s);
|
||||
p->magic = kBlockMagic;
|
||||
return p + 1;
|
||||
ReportInternalAllocatorOutOfMemory(size);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *InternalRealloc(void *addr, uptr size, InternalAllocatorCache *cache) {
|
||||
if (!addr)
|
||||
return InternalAlloc(size, cache);
|
||||
uptr s = size + sizeof(BlockHeader);
|
||||
if (s < size)
|
||||
return nullptr;
|
||||
BlockHeader *p = (BlockHeader *)addr - 1;
|
||||
CHECK_EQ(kBlockMagic, p->magic);
|
||||
p = (BlockHeader *)RawInternalRealloc(p, s, cache);
|
||||
void *p = RawInternalRealloc(addr, size, cache);
|
||||
if (UNLIKELY(!p))
|
||||
ReportInternalAllocatorOutOfMemory(s);
|
||||
return p + 1;
|
||||
ReportInternalAllocatorOutOfMemory(size);
|
||||
return p;
|
||||
}
|
||||
|
||||
void *InternalReallocArray(void *addr, uptr count, uptr size,
|
||||
@ -203,12 +184,7 @@ void *InternalCalloc(uptr count, uptr size, InternalAllocatorCache *cache) {
|
||||
}
|
||||
|
||||
void InternalFree(void *addr, InternalAllocatorCache *cache) {
|
||||
if (!addr)
|
||||
return;
|
||||
BlockHeader *p = (BlockHeader *)addr - 1;
|
||||
CHECK_EQ(kBlockMagic, p->magic);
|
||||
p->magic = 0;
|
||||
RawInternalFree(p, cache);
|
||||
RawInternalFree(addr, cache);
|
||||
}
|
||||
|
||||
// LowLevelAllocator
|
||||
|
@ -177,12 +177,12 @@ class CombinedAllocator {
|
||||
|
||||
// ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
|
||||
// introspection API.
|
||||
void ForceLock() {
|
||||
void ForceLock() NO_THREAD_SAFETY_ANALYSIS {
|
||||
primary_.ForceLock();
|
||||
secondary_.ForceLock();
|
||||
}
|
||||
|
||||
void ForceUnlock() {
|
||||
void ForceUnlock() NO_THREAD_SAFETY_ANALYSIS {
|
||||
secondary_.ForceUnlock();
|
||||
primary_.ForceUnlock();
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
template <class SizeClassAllocator>
|
||||
struct SizeClassAllocator64LocalCache {
|
||||
typedef SizeClassAllocator Allocator;
|
||||
typedef MemoryMapper<Allocator> MemoryMapperT;
|
||||
|
||||
void Init(AllocatorGlobalStats *s) {
|
||||
stats_.Init();
|
||||
@ -53,7 +54,7 @@ struct SizeClassAllocator64LocalCache {
|
||||
PerClass *c = &per_class_[class_id];
|
||||
InitCache(c);
|
||||
if (UNLIKELY(c->count == c->max_count))
|
||||
Drain(c, allocator, class_id, c->max_count / 2);
|
||||
DrainHalfMax(c, allocator, class_id);
|
||||
CompactPtrT chunk = allocator->PointerToCompactPtr(
|
||||
allocator->GetRegionBeginBySizeClass(class_id),
|
||||
reinterpret_cast<uptr>(p));
|
||||
@ -62,10 +63,10 @@ struct SizeClassAllocator64LocalCache {
|
||||
}
|
||||
|
||||
void Drain(SizeClassAllocator *allocator) {
|
||||
MemoryMapperT memory_mapper(*allocator);
|
||||
for (uptr i = 1; i < kNumClasses; i++) {
|
||||
PerClass *c = &per_class_[i];
|
||||
while (c->count > 0)
|
||||
Drain(c, allocator, i, c->count);
|
||||
while (c->count > 0) Drain(&memory_mapper, c, allocator, i, c->count);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,12 +107,18 @@ struct SizeClassAllocator64LocalCache {
|
||||
return true;
|
||||
}
|
||||
|
||||
NOINLINE void Drain(PerClass *c, SizeClassAllocator *allocator, uptr class_id,
|
||||
uptr count) {
|
||||
NOINLINE void DrainHalfMax(PerClass *c, SizeClassAllocator *allocator,
|
||||
uptr class_id) {
|
||||
MemoryMapperT memory_mapper(*allocator);
|
||||
Drain(&memory_mapper, c, allocator, class_id, c->max_count / 2);
|
||||
}
|
||||
|
||||
void Drain(MemoryMapperT *memory_mapper, PerClass *c,
|
||||
SizeClassAllocator *allocator, uptr class_id, uptr count) {
|
||||
CHECK_GE(c->count, count);
|
||||
const uptr first_idx_to_drain = c->count - count;
|
||||
c->count -= count;
|
||||
allocator->ReturnToAllocator(&stats_, class_id,
|
||||
allocator->ReturnToAllocator(memory_mapper, &stats_, class_id,
|
||||
&c->chunks[first_idx_to_drain], count);
|
||||
}
|
||||
};
|
||||
|
@ -237,13 +237,13 @@ class SizeClassAllocator32 {
|
||||
|
||||
// ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
|
||||
// introspection API.
|
||||
void ForceLock() {
|
||||
void ForceLock() NO_THREAD_SAFETY_ANALYSIS {
|
||||
for (uptr i = 0; i < kNumClasses; i++) {
|
||||
GetSizeClassInfo(i)->mutex.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
void ForceUnlock() {
|
||||
void ForceUnlock() NO_THREAD_SAFETY_ANALYSIS {
|
||||
for (int i = kNumClasses - 1; i >= 0; i--) {
|
||||
GetSizeClassInfo(i)->mutex.Unlock();
|
||||
}
|
||||
|
@ -42,6 +42,44 @@ struct SizeClassAllocator64FlagMasks { // Bit masks.
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Allocator>
|
||||
class MemoryMapper {
|
||||
public:
|
||||
typedef typename Allocator::CompactPtrT CompactPtrT;
|
||||
|
||||
explicit MemoryMapper(const Allocator &allocator) : allocator_(allocator) {}
|
||||
|
||||
bool GetAndResetStats(uptr &ranges, uptr &bytes) {
|
||||
ranges = released_ranges_count_;
|
||||
released_ranges_count_ = 0;
|
||||
bytes = released_bytes_;
|
||||
released_bytes_ = 0;
|
||||
return ranges != 0;
|
||||
}
|
||||
|
||||
u64 *MapPackedCounterArrayBuffer(uptr count) {
|
||||
buffer_.clear();
|
||||
buffer_.resize(count);
|
||||
return buffer_.data();
|
||||
}
|
||||
|
||||
// Releases [from, to) range of pages back to OS.
|
||||
void ReleasePageRangeToOS(uptr class_id, CompactPtrT from, CompactPtrT to) {
|
||||
const uptr region_base = allocator_.GetRegionBeginBySizeClass(class_id);
|
||||
const uptr from_page = allocator_.CompactPtrToPointer(region_base, from);
|
||||
const uptr to_page = allocator_.CompactPtrToPointer(region_base, to);
|
||||
ReleaseMemoryPagesToOS(from_page, to_page);
|
||||
released_ranges_count_++;
|
||||
released_bytes_ += to_page - from_page;
|
||||
}
|
||||
|
||||
private:
|
||||
const Allocator &allocator_;
|
||||
uptr released_ranges_count_ = 0;
|
||||
uptr released_bytes_ = 0;
|
||||
InternalMmapVector<u64> buffer_;
|
||||
};
|
||||
|
||||
template <class Params>
|
||||
class SizeClassAllocator64 {
|
||||
public:
|
||||
@ -57,6 +95,7 @@ class SizeClassAllocator64 {
|
||||
|
||||
typedef SizeClassAllocator64<Params> ThisT;
|
||||
typedef SizeClassAllocator64LocalCache<ThisT> AllocatorCache;
|
||||
typedef MemoryMapper<ThisT> MemoryMapperT;
|
||||
|
||||
// When we know the size class (the region base) we can represent a pointer
|
||||
// as a 4-byte integer (offset from the region start shifted right by 4).
|
||||
@ -120,9 +159,10 @@ class SizeClassAllocator64 {
|
||||
}
|
||||
|
||||
void ForceReleaseToOS() {
|
||||
MemoryMapperT memory_mapper(*this);
|
||||
for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
|
||||
BlockingMutexLock l(&GetRegionInfo(class_id)->mutex);
|
||||
MaybeReleaseToOS(class_id, true /*force*/);
|
||||
MaybeReleaseToOS(&memory_mapper, class_id, true /*force*/);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +171,8 @@ class SizeClassAllocator64 {
|
||||
alignment <= SizeClassMap::kMaxSize;
|
||||
}
|
||||
|
||||
NOINLINE void ReturnToAllocator(AllocatorStats *stat, uptr class_id,
|
||||
NOINLINE void ReturnToAllocator(MemoryMapperT *memory_mapper,
|
||||
AllocatorStats *stat, uptr class_id,
|
||||
const CompactPtrT *chunks, uptr n_chunks) {
|
||||
RegionInfo *region = GetRegionInfo(class_id);
|
||||
uptr region_beg = GetRegionBeginBySizeClass(class_id);
|
||||
@ -154,7 +195,7 @@ class SizeClassAllocator64 {
|
||||
region->num_freed_chunks = new_num_freed_chunks;
|
||||
region->stats.n_freed += n_chunks;
|
||||
|
||||
MaybeReleaseToOS(class_id, false /*force*/);
|
||||
MaybeReleaseToOS(memory_mapper, class_id, false /*force*/);
|
||||
}
|
||||
|
||||
NOINLINE bool GetFromAllocator(AllocatorStats *stat, uptr class_id,
|
||||
@ -312,13 +353,13 @@ class SizeClassAllocator64 {
|
||||
|
||||
// ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
|
||||
// introspection API.
|
||||
void ForceLock() {
|
||||
void ForceLock() NO_THREAD_SAFETY_ANALYSIS {
|
||||
for (uptr i = 0; i < kNumClasses; i++) {
|
||||
GetRegionInfo(i)->mutex.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
void ForceUnlock() {
|
||||
void ForceUnlock() NO_THREAD_SAFETY_ANALYSIS {
|
||||
for (int i = (int)kNumClasses - 1; i >= 0; i--) {
|
||||
GetRegionInfo(i)->mutex.Unlock();
|
||||
}
|
||||
@ -362,11 +403,11 @@ class SizeClassAllocator64 {
|
||||
// For the performance sake, none of the accessors check the validity of the
|
||||
// arguments, it is assumed that index is always in [0, n) range and the value
|
||||
// is not incremented past max_value.
|
||||
template<class MemoryMapperT>
|
||||
class PackedCounterArray {
|
||||
public:
|
||||
PackedCounterArray(u64 num_counters, u64 max_value, MemoryMapperT *mapper)
|
||||
: n(num_counters), memory_mapper(mapper) {
|
||||
template <typename MemoryMapper>
|
||||
PackedCounterArray(u64 num_counters, u64 max_value, MemoryMapper *mapper)
|
||||
: n(num_counters) {
|
||||
CHECK_GT(num_counters, 0);
|
||||
CHECK_GT(max_value, 0);
|
||||
constexpr u64 kMaxCounterBits = sizeof(*buffer) * 8ULL;
|
||||
@ -383,16 +424,8 @@ class SizeClassAllocator64 {
|
||||
packing_ratio_log = Log2(packing_ratio);
|
||||
bit_offset_mask = packing_ratio - 1;
|
||||
|
||||
buffer_size =
|
||||
(RoundUpTo(n, 1ULL << packing_ratio_log) >> packing_ratio_log) *
|
||||
sizeof(*buffer);
|
||||
buffer = reinterpret_cast<u64*>(
|
||||
memory_mapper->MapPackedCounterArrayBuffer(buffer_size));
|
||||
}
|
||||
~PackedCounterArray() {
|
||||
if (buffer) {
|
||||
memory_mapper->UnmapPackedCounterArrayBuffer(buffer, buffer_size);
|
||||
}
|
||||
buffer = mapper->MapPackedCounterArrayBuffer(
|
||||
RoundUpTo(n, 1ULL << packing_ratio_log) >> packing_ratio_log);
|
||||
}
|
||||
|
||||
bool IsAllocated() const {
|
||||
@ -429,19 +462,16 @@ class SizeClassAllocator64 {
|
||||
u64 counter_mask;
|
||||
u64 packing_ratio_log;
|
||||
u64 bit_offset_mask;
|
||||
|
||||
MemoryMapperT* const memory_mapper;
|
||||
u64 buffer_size;
|
||||
u64* buffer;
|
||||
};
|
||||
|
||||
template<class MemoryMapperT>
|
||||
template <class MemoryMapperT>
|
||||
class FreePagesRangeTracker {
|
||||
public:
|
||||
explicit FreePagesRangeTracker(MemoryMapperT* mapper)
|
||||
FreePagesRangeTracker(MemoryMapperT *mapper, uptr class_id)
|
||||
: memory_mapper(mapper),
|
||||
page_size_scaled_log(Log2(GetPageSizeCached() >> kCompactPtrScale)),
|
||||
in_the_range(false), current_page(0), current_range_start_page(0) {}
|
||||
class_id(class_id),
|
||||
page_size_scaled_log(Log2(GetPageSizeCached() >> kCompactPtrScale)) {}
|
||||
|
||||
void NextPage(bool freed) {
|
||||
if (freed) {
|
||||
@ -463,28 +493,30 @@ class SizeClassAllocator64 {
|
||||
void CloseOpenedRange() {
|
||||
if (in_the_range) {
|
||||
memory_mapper->ReleasePageRangeToOS(
|
||||
current_range_start_page << page_size_scaled_log,
|
||||
class_id, current_range_start_page << page_size_scaled_log,
|
||||
current_page << page_size_scaled_log);
|
||||
in_the_range = false;
|
||||
}
|
||||
}
|
||||
|
||||
MemoryMapperT* const memory_mapper;
|
||||
const uptr page_size_scaled_log;
|
||||
bool in_the_range;
|
||||
uptr current_page;
|
||||
uptr current_range_start_page;
|
||||
MemoryMapperT *const memory_mapper = nullptr;
|
||||
const uptr class_id = 0;
|
||||
const uptr page_size_scaled_log = 0;
|
||||
bool in_the_range = false;
|
||||
uptr current_page = 0;
|
||||
uptr current_range_start_page = 0;
|
||||
};
|
||||
|
||||
// Iterates over the free_array to identify memory pages containing freed
|
||||
// chunks only and returns these pages back to OS.
|
||||
// allocated_pages_count is the total number of pages allocated for the
|
||||
// current bucket.
|
||||
template<class MemoryMapperT>
|
||||
template <typename MemoryMapper>
|
||||
static void ReleaseFreeMemoryToOS(CompactPtrT *free_array,
|
||||
uptr free_array_count, uptr chunk_size,
|
||||
uptr allocated_pages_count,
|
||||
MemoryMapperT *memory_mapper) {
|
||||
MemoryMapper *memory_mapper,
|
||||
uptr class_id) {
|
||||
const uptr page_size = GetPageSizeCached();
|
||||
|
||||
// Figure out the number of chunks per page and whether we can take a fast
|
||||
@ -520,9 +552,8 @@ class SizeClassAllocator64 {
|
||||
UNREACHABLE("All chunk_size/page_size ratios must be handled.");
|
||||
}
|
||||
|
||||
PackedCounterArray<MemoryMapperT> counters(allocated_pages_count,
|
||||
full_pages_chunk_count_max,
|
||||
memory_mapper);
|
||||
PackedCounterArray counters(allocated_pages_count,
|
||||
full_pages_chunk_count_max, memory_mapper);
|
||||
if (!counters.IsAllocated())
|
||||
return;
|
||||
|
||||
@ -547,7 +578,7 @@ class SizeClassAllocator64 {
|
||||
|
||||
// Iterate over pages detecting ranges of pages with chunk counters equal
|
||||
// to the expected number of chunks for the particular page.
|
||||
FreePagesRangeTracker<MemoryMapperT> range_tracker(memory_mapper);
|
||||
FreePagesRangeTracker<MemoryMapper> range_tracker(memory_mapper, class_id);
|
||||
if (same_chunk_count_per_page) {
|
||||
// Fast path, every page has the same number of chunks affecting it.
|
||||
for (uptr i = 0; i < counters.GetCount(); i++)
|
||||
@ -586,7 +617,7 @@ class SizeClassAllocator64 {
|
||||
}
|
||||
|
||||
private:
|
||||
friend class MemoryMapper;
|
||||
friend class MemoryMapper<ThisT>;
|
||||
|
||||
ReservedAddressRange address_range;
|
||||
|
||||
@ -820,57 +851,13 @@ class SizeClassAllocator64 {
|
||||
return true;
|
||||
}
|
||||
|
||||
class MemoryMapper {
|
||||
public:
|
||||
MemoryMapper(const ThisT& base_allocator, uptr class_id)
|
||||
: allocator(base_allocator),
|
||||
region_base(base_allocator.GetRegionBeginBySizeClass(class_id)),
|
||||
released_ranges_count(0),
|
||||
released_bytes(0) {
|
||||
}
|
||||
|
||||
uptr GetReleasedRangesCount() const {
|
||||
return released_ranges_count;
|
||||
}
|
||||
|
||||
uptr GetReleasedBytes() const {
|
||||
return released_bytes;
|
||||
}
|
||||
|
||||
void *MapPackedCounterArrayBuffer(uptr buffer_size) {
|
||||
// TODO(alekseyshl): The idea to explore is to check if we have enough
|
||||
// space between num_freed_chunks*sizeof(CompactPtrT) and
|
||||
// mapped_free_array to fit buffer_size bytes and use that space instead
|
||||
// of mapping a temporary one.
|
||||
return MmapOrDieOnFatalError(buffer_size, "ReleaseToOSPageCounters");
|
||||
}
|
||||
|
||||
void UnmapPackedCounterArrayBuffer(void *buffer, uptr buffer_size) {
|
||||
UnmapOrDie(buffer, buffer_size);
|
||||
}
|
||||
|
||||
// Releases [from, to) range of pages back to OS.
|
||||
void ReleasePageRangeToOS(CompactPtrT from, CompactPtrT to) {
|
||||
const uptr from_page = allocator.CompactPtrToPointer(region_base, from);
|
||||
const uptr to_page = allocator.CompactPtrToPointer(region_base, to);
|
||||
ReleaseMemoryPagesToOS(from_page, to_page);
|
||||
released_ranges_count++;
|
||||
released_bytes += to_page - from_page;
|
||||
}
|
||||
|
||||
private:
|
||||
const ThisT& allocator;
|
||||
const uptr region_base;
|
||||
uptr released_ranges_count;
|
||||
uptr released_bytes;
|
||||
};
|
||||
|
||||
// Attempts to release RAM occupied by freed chunks back to OS. The region is
|
||||
// expected to be locked.
|
||||
//
|
||||
// TODO(morehouse): Support a callback on memory release so HWASan can release
|
||||
// aliases as well.
|
||||
void MaybeReleaseToOS(uptr class_id, bool force) {
|
||||
void MaybeReleaseToOS(MemoryMapperT *memory_mapper, uptr class_id,
|
||||
bool force) {
|
||||
RegionInfo *region = GetRegionInfo(class_id);
|
||||
const uptr chunk_size = ClassIdToSize(class_id);
|
||||
const uptr page_size = GetPageSizeCached();
|
||||
@ -894,17 +881,16 @@ class SizeClassAllocator64 {
|
||||
}
|
||||
}
|
||||
|
||||
MemoryMapper memory_mapper(*this, class_id);
|
||||
|
||||
ReleaseFreeMemoryToOS<MemoryMapper>(
|
||||
ReleaseFreeMemoryToOS(
|
||||
GetFreeArray(GetRegionBeginBySizeClass(class_id)), n, chunk_size,
|
||||
RoundUpTo(region->allocated_user, page_size) / page_size,
|
||||
&memory_mapper);
|
||||
RoundUpTo(region->allocated_user, page_size) / page_size, memory_mapper,
|
||||
class_id);
|
||||
|
||||
if (memory_mapper.GetReleasedRangesCount() > 0) {
|
||||
uptr ranges, bytes;
|
||||
if (memory_mapper->GetAndResetStats(ranges, bytes)) {
|
||||
region->rtoi.n_freed_at_last_release = region->stats.n_freed;
|
||||
region->rtoi.num_releases += memory_mapper.GetReleasedRangesCount();
|
||||
region->rtoi.last_released_bytes = memory_mapper.GetReleasedBytes();
|
||||
region->rtoi.num_releases += ranges;
|
||||
region->rtoi.last_released_bytes = bytes;
|
||||
}
|
||||
region->rtoi.last_release_at_ns = MonotonicNanoTime();
|
||||
}
|
||||
|
@ -267,13 +267,9 @@ class LargeMmapAllocator {
|
||||
|
||||
// ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
|
||||
// introspection API.
|
||||
void ForceLock() {
|
||||
mutex_.Lock();
|
||||
}
|
||||
void ForceLock() ACQUIRE(mutex_) { mutex_.Lock(); }
|
||||
|
||||
void ForceUnlock() {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
void ForceUnlock() RELEASE(mutex_) { mutex_.Unlock(); }
|
||||
|
||||
// Iterate over all existing chunks.
|
||||
// The allocator must be locked when calling this function.
|
||||
|
@ -37,10 +37,9 @@ void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
|
||||
const char *mmap_type, error_t err,
|
||||
bool raw_report) {
|
||||
static int recursion_count;
|
||||
if (SANITIZER_RTEMS || raw_report || recursion_count) {
|
||||
// If we are on RTEMS or raw report is requested or we went into recursion,
|
||||
// just die. The Report() and CHECK calls below may call mmap recursively
|
||||
// and fail.
|
||||
if (raw_report || recursion_count) {
|
||||
// If raw report is requested or we went into recursion just die. The
|
||||
// Report() and CHECK calls below may call mmap recursively and fail.
|
||||
RawWrite("ERROR: Failed to mmap\n");
|
||||
Die();
|
||||
}
|
||||
@ -331,6 +330,14 @@ static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr),
|
||||
return 0;
|
||||
}
|
||||
|
||||
void internal_sleep(unsigned seconds) {
|
||||
internal_usleep((u64)seconds * 1000 * 1000);
|
||||
}
|
||||
void SleepForSeconds(unsigned seconds) {
|
||||
internal_usleep((u64)seconds * 1000 * 1000);
|
||||
}
|
||||
void SleepForMillis(unsigned millis) { internal_usleep((u64)millis * 1000); }
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
using namespace __sanitizer;
|
||||
|
@ -237,10 +237,16 @@ void SetPrintfAndReportCallback(void (*callback)(const char *));
|
||||
// Lock sanitizer error reporting and protects against nested errors.
|
||||
class ScopedErrorReportLock {
|
||||
public:
|
||||
ScopedErrorReportLock();
|
||||
~ScopedErrorReportLock();
|
||||
ScopedErrorReportLock() ACQUIRE(mutex_) { Lock(); }
|
||||
~ScopedErrorReportLock() RELEASE(mutex_) { Unlock(); }
|
||||
|
||||
static void CheckLocked();
|
||||
static void Lock() ACQUIRE(mutex_);
|
||||
static void Unlock() RELEASE(mutex_);
|
||||
static void CheckLocked() CHECK_LOCKED(mutex_);
|
||||
|
||||
private:
|
||||
static atomic_uintptr_t reporting_thread_;
|
||||
static StaticSpinMutex mutex_;
|
||||
};
|
||||
|
||||
extern uptr stoptheworld_tracer_pid;
|
||||
@ -288,8 +294,8 @@ void InitTlsSize();
|
||||
uptr GetTlsSize();
|
||||
|
||||
// Other
|
||||
void SleepForSeconds(int seconds);
|
||||
void SleepForMillis(int millis);
|
||||
void SleepForSeconds(unsigned seconds);
|
||||
void SleepForMillis(unsigned millis);
|
||||
u64 NanoTime();
|
||||
u64 MonotonicNanoTime();
|
||||
int Atexit(void (*function)(void));
|
||||
@ -1057,6 +1063,13 @@ class ArrayRef {
|
||||
T *end_ = nullptr;
|
||||
};
|
||||
|
||||
#define PRINTF_128(v) \
|
||||
(*((u8 *)&v + 0)), (*((u8 *)&v + 1)), (*((u8 *)&v + 2)), (*((u8 *)&v + 3)), \
|
||||
(*((u8 *)&v + 4)), (*((u8 *)&v + 5)), (*((u8 *)&v + 6)), \
|
||||
(*((u8 *)&v + 7)), (*((u8 *)&v + 8)), (*((u8 *)&v + 9)), \
|
||||
(*((u8 *)&v + 10)), (*((u8 *)&v + 11)), (*((u8 *)&v + 12)), \
|
||||
(*((u8 *)&v + 13)), (*((u8 *)&v + 14)), (*((u8 *)&v + 15))
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
inline void *operator new(__sanitizer::operator_new_size_type size,
|
||||
|
@ -134,11 +134,11 @@ extern const short *_tolower_tab_;
|
||||
|
||||
// Platform-specific options.
|
||||
#if SANITIZER_MAC
|
||||
#define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE false
|
||||
#define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 0
|
||||
#elif SANITIZER_WINDOWS64
|
||||
#define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE false
|
||||
#define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 0
|
||||
#else
|
||||
#define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
|
||||
#define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE 1
|
||||
#endif // SANITIZER_MAC
|
||||
|
||||
#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE
|
||||
@ -823,11 +823,11 @@ INTERCEPTOR(void *, memcpy, void *dst, const void *src, uptr size) {
|
||||
// N.B.: If we switch this to internal_ we'll have to use internal_memmove
|
||||
// due to memcpy being an alias of memmove on OS X.
|
||||
void *ctx;
|
||||
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
|
||||
#if PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE
|
||||
COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, dst, src, size);
|
||||
} else {
|
||||
#else
|
||||
COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define INIT_MEMCPY \
|
||||
@ -957,6 +957,7 @@ INTERCEPTOR(double, frexp, double x, int *exp) {
|
||||
// Assuming frexp() always writes to |exp|.
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
|
||||
double res = REAL(frexp)(x, exp);
|
||||
COMMON_INTERCEPTOR_INITIALIZE_RANGE(exp, sizeof(*exp));
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -969,22 +970,18 @@ INTERCEPTOR(double, frexp, double x, int *exp) {
|
||||
INTERCEPTOR(float, frexpf, float x, int *exp) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp);
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
float res = REAL(frexpf)(x, exp);
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
|
||||
float res = REAL(frexpf)(x, exp);
|
||||
COMMON_INTERCEPTOR_INITIALIZE_RANGE(exp, sizeof(*exp));
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(long double, frexpl, long double x, int *exp) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, frexpl, x, exp);
|
||||
// FIXME: under ASan the call below may write to freed memory and corrupt
|
||||
// its metadata. See
|
||||
// https://github.com/google/sanitizers/issues/321.
|
||||
long double res = REAL(frexpl)(x, exp);
|
||||
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp));
|
||||
long double res = REAL(frexpl)(x, exp);
|
||||
COMMON_INTERCEPTOR_INITIALIZE_RANGE(exp, sizeof(*exp));
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -5303,6 +5300,12 @@ INTERCEPTOR(__sanitizer_clock_t, times, void *tms) {
|
||||
#define INIT_TIMES
|
||||
#endif
|
||||
|
||||
#if SANITIZER_S390 && \
|
||||
(SANITIZER_INTERCEPT_TLS_GET_ADDR || SANITIZER_INTERCEPT_TLS_GET_OFFSET)
|
||||
extern "C" uptr __tls_get_offset_wrapper(void *arg, uptr (*fn)(void *arg));
|
||||
DEFINE_REAL(uptr, __tls_get_offset, void *arg)
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_TLS_GET_ADDR
|
||||
#if !SANITIZER_S390
|
||||
#define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_addr)
|
||||
@ -5342,11 +5345,7 @@ void *__tls_get_addr_opt(void *arg);
|
||||
// descriptor offset as an argument instead of a pointer. GOT address
|
||||
// is passed in r12, so it's necessary to write it in assembly. This is
|
||||
// the function used by the compiler.
|
||||
extern "C" uptr __tls_get_offset_wrapper(void *arg, uptr (*fn)(void *arg));
|
||||
#define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_offset)
|
||||
DEFINE_REAL(uptr, __tls_get_offset, void *arg)
|
||||
extern "C" uptr __tls_get_offset(void *arg);
|
||||
extern "C" uptr __interceptor___tls_get_offset(void *arg);
|
||||
INTERCEPTOR(uptr, __tls_get_addr_internal, void *arg) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr_internal, arg);
|
||||
@ -5362,6 +5361,15 @@ INTERCEPTOR(uptr, __tls_get_addr_internal, void *arg) {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif // SANITIZER_S390
|
||||
#else
|
||||
#define INIT_TLS_GET_ADDR
|
||||
#endif
|
||||
|
||||
#if SANITIZER_S390 && \
|
||||
(SANITIZER_INTERCEPT_TLS_GET_ADDR || SANITIZER_INTERCEPT_TLS_GET_OFFSET)
|
||||
extern "C" uptr __tls_get_offset(void *arg);
|
||||
extern "C" uptr __interceptor___tls_get_offset(void *arg);
|
||||
// We need a hidden symbol aliasing the above, so that we can jump
|
||||
// directly to it from the assembly below.
|
||||
extern "C" __attribute__((alias("__interceptor___tls_get_addr_internal"),
|
||||
@ -5400,9 +5408,6 @@ asm(
|
||||
"br %r3\n"
|
||||
".size __tls_get_offset_wrapper, .-__tls_get_offset_wrapper\n"
|
||||
);
|
||||
#endif // SANITIZER_S390
|
||||
#else
|
||||
#define INIT_TLS_GET_ADDR
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_LISTXATTR
|
||||
@ -6099,6 +6104,40 @@ INTERCEPTOR(__sanitizer_FILE *, freopen, const char *path, const char *mode,
|
||||
#define INIT_FOPEN
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_FLOPEN
|
||||
INTERCEPTOR(int, flopen, const char *path, int flags, ...) {
|
||||
void *ctx;
|
||||
va_list ap;
|
||||
va_start(ap, flags);
|
||||
u16 mode = static_cast<u16>(va_arg(ap, u32));
|
||||
va_end(ap);
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, flopen, path, flags, mode);
|
||||
if (path) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
|
||||
}
|
||||
return REAL(flopen)(path, flags, mode);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, flopenat, int dirfd, const char *path, int flags, ...) {
|
||||
void *ctx;
|
||||
va_list ap;
|
||||
va_start(ap, flags);
|
||||
u16 mode = static_cast<u16>(va_arg(ap, u32));
|
||||
va_end(ap);
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, flopen, path, flags, mode);
|
||||
if (path) {
|
||||
COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
|
||||
}
|
||||
return REAL(flopenat)(dirfd, path, flags, mode);
|
||||
}
|
||||
|
||||
#define INIT_FLOPEN \
|
||||
COMMON_INTERCEPT_FUNCTION(flopen); \
|
||||
COMMON_INTERCEPT_FUNCTION(flopenat);
|
||||
#else
|
||||
#define INIT_FLOPEN
|
||||
#endif
|
||||
|
||||
#if SANITIZER_INTERCEPT_FOPEN64
|
||||
INTERCEPTOR(__sanitizer_FILE *, fopen64, const char *path, const char *mode) {
|
||||
void *ctx;
|
||||
@ -6463,7 +6502,7 @@ INTERCEPTOR(int, sem_wait, __sanitizer_sem_t *s) {
|
||||
INTERCEPTOR(int, sem_trywait, __sanitizer_sem_t *s) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER(ctx, sem_trywait, s);
|
||||
int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_trywait)(s);
|
||||
int res = REAL(sem_trywait)(s);
|
||||
if (res == 0) {
|
||||
COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s);
|
||||
}
|
||||
@ -10264,6 +10303,7 @@ static void InitializeCommonInterceptors() {
|
||||
INIT_LIBIO_INTERNALS;
|
||||
INIT_FOPEN;
|
||||
INIT_FOPEN64;
|
||||
INIT_FLOPEN;
|
||||
INIT_OPEN_MEMSTREAM;
|
||||
INIT_OBSTACK;
|
||||
INIT_FFLUSH;
|
||||
|
@ -138,7 +138,7 @@ uptr ReservedAddressRange::InitAligned(uptr size, uptr align,
|
||||
return start;
|
||||
}
|
||||
|
||||
#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
#if !SANITIZER_FUCHSIA
|
||||
|
||||
// Reserve memory range [beg, end].
|
||||
// We need to use inclusive range because end+1 may not be representable.
|
||||
@ -189,7 +189,7 @@ void ProtectGap(uptr addr, uptr size, uptr zero_base_shadow_start,
|
||||
Die();
|
||||
}
|
||||
|
||||
#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
|
||||
#endif // !SANITIZER_FUCHSIA
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
|
@ -25,7 +25,6 @@ void LogMessageOnPrintf(const char *str) {}
|
||||
#endif
|
||||
void WriteToSyslog(const char *buffer) {}
|
||||
void Abort() { internal__exit(1); }
|
||||
void SleepForSeconds(int seconds) { internal_sleep(seconds); }
|
||||
#endif // !SANITIZER_WINDOWS
|
||||
|
||||
#if !SANITIZER_WINDOWS && !SANITIZER_MAC
|
||||
|
@ -136,7 +136,7 @@ void DD::ReportDeadlock(DDCallback *cb, DDMutex *m) {
|
||||
DDMutex *m0 = (DDMutex*)dd.getData(from);
|
||||
DDMutex *m1 = (DDMutex*)dd.getData(to);
|
||||
|
||||
u32 stk_from = -1U, stk_to = -1U;
|
||||
u32 stk_from = 0, stk_to = 0;
|
||||
int unique_tid = 0;
|
||||
dd.findEdge(from, to, &stk_from, &stk_to, &unique_tid);
|
||||
// Printf("Edge: %zd=>%zd: %u/%u T%d\n", from, to, stk_from, stk_to,
|
||||
|
@ -73,7 +73,7 @@ struct DDLogicalThread {
|
||||
int nlocked;
|
||||
};
|
||||
|
||||
struct Mutex {
|
||||
struct MutexState {
|
||||
StaticSpinMutex mtx;
|
||||
u32 seq;
|
||||
int nlink;
|
||||
@ -101,12 +101,12 @@ struct DD final : public DDetector {
|
||||
void CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *mtx);
|
||||
void Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath);
|
||||
u32 allocateId(DDCallback *cb);
|
||||
Mutex *getMutex(u32 id);
|
||||
u32 getMutexId(Mutex *m);
|
||||
MutexState *getMutex(u32 id);
|
||||
u32 getMutexId(MutexState *m);
|
||||
|
||||
DDFlags flags;
|
||||
|
||||
Mutex* mutex[kL1Size];
|
||||
MutexState *mutex[kL1Size];
|
||||
|
||||
SpinMutex mtx;
|
||||
InternalMmapVector<u32> free_id;
|
||||
@ -152,13 +152,11 @@ void DD::MutexInit(DDCallback *cb, DDMutex *m) {
|
||||
atomic_store(&m->owner, 0, memory_order_relaxed);
|
||||
}
|
||||
|
||||
Mutex *DD::getMutex(u32 id) {
|
||||
return &mutex[id / kL2Size][id % kL2Size];
|
||||
}
|
||||
MutexState *DD::getMutex(u32 id) { return &mutex[id / kL2Size][id % kL2Size]; }
|
||||
|
||||
u32 DD::getMutexId(Mutex *m) {
|
||||
u32 DD::getMutexId(MutexState *m) {
|
||||
for (int i = 0; i < kL1Size; i++) {
|
||||
Mutex *tab = mutex[i];
|
||||
MutexState *tab = mutex[i];
|
||||
if (tab == 0)
|
||||
break;
|
||||
if (m >= tab && m < tab + kL2Size)
|
||||
@ -176,8 +174,8 @@ u32 DD::allocateId(DDCallback *cb) {
|
||||
} else {
|
||||
CHECK_LT(id_gen, kMaxMutex);
|
||||
if ((id_gen % kL2Size) == 0) {
|
||||
mutex[id_gen / kL2Size] = (Mutex*)MmapOrDie(kL2Size * sizeof(Mutex),
|
||||
"deadlock detector (mutex table)");
|
||||
mutex[id_gen / kL2Size] = (MutexState *)MmapOrDie(
|
||||
kL2Size * sizeof(MutexState), "deadlock detector (mutex table)");
|
||||
}
|
||||
id = id_gen++;
|
||||
}
|
||||
@ -216,11 +214,11 @@ void DD::MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {
|
||||
}
|
||||
|
||||
bool added = false;
|
||||
Mutex *mtx = getMutex(m->id);
|
||||
MutexState *mtx = getMutex(m->id);
|
||||
for (int i = 0; i < lt->nlocked - 1; i++) {
|
||||
u32 id1 = lt->locked[i].id;
|
||||
u32 stk1 = lt->locked[i].stk;
|
||||
Mutex *mtx1 = getMutex(id1);
|
||||
MutexState *mtx1 = getMutex(id1);
|
||||
SpinMutexLock l(&mtx1->mtx);
|
||||
if (mtx1->nlink == kMaxLink) {
|
||||
// FIXME(dvyukov): check stale links
|
||||
@ -342,7 +340,7 @@ void DD::MutexDestroy(DDCallback *cb, DDMutex *m) {
|
||||
|
||||
// Clear and invalidate the mutex descriptor.
|
||||
{
|
||||
Mutex *mtx = getMutex(m->id);
|
||||
MutexState *mtx = getMutex(m->id);
|
||||
SpinMutexLock l(&mtx->mtx);
|
||||
mtx->seq++;
|
||||
mtx->nlink = 0;
|
||||
@ -361,7 +359,7 @@ void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
|
||||
int npath = 0;
|
||||
int npending = 0;
|
||||
{
|
||||
Mutex *mtx = getMutex(m->id);
|
||||
MutexState *mtx = getMutex(m->id);
|
||||
SpinMutexLock l(&mtx->mtx);
|
||||
for (int li = 0; li < mtx->nlink; li++)
|
||||
pt->pending[npending++] = mtx->link[li];
|
||||
@ -374,7 +372,7 @@ void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
|
||||
}
|
||||
if (pt->visited[link.id])
|
||||
continue;
|
||||
Mutex *mtx1 = getMutex(link.id);
|
||||
MutexState *mtx1 = getMutex(link.id);
|
||||
SpinMutexLock l(&mtx1->mtx);
|
||||
if (mtx1->seq != link.seq)
|
||||
continue;
|
||||
@ -387,7 +385,7 @@ void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
|
||||
return Report(pt, lt, npath); // Bingo!
|
||||
for (int li = 0; li < mtx1->nlink; li++) {
|
||||
Link *link1 = &mtx1->link[li];
|
||||
// Mutex *mtx2 = getMutex(link->id);
|
||||
// MutexState *mtx2 = getMutex(link->id);
|
||||
// FIXME(dvyukov): fast seq check
|
||||
// FIXME(dvyukov): fast nlink != 0 check
|
||||
// FIXME(dvyukov): fast pending check?
|
||||
|
@ -23,8 +23,7 @@
|
||||
|
||||
#if SANITIZER_FREEBSD || SANITIZER_MAC
|
||||
# define __errno_location __error
|
||||
#elif SANITIZER_ANDROID || SANITIZER_NETBSD || \
|
||||
SANITIZER_RTEMS
|
||||
#elif SANITIZER_ANDROID || SANITIZER_NETBSD
|
||||
# define __errno_location __errno
|
||||
#elif SANITIZER_SOLARIS
|
||||
# define __errno_location ___errno
|
||||
|
@ -36,16 +36,11 @@ uptr internal_sched_yield() {
|
||||
return 0; // Why doesn't this return void?
|
||||
}
|
||||
|
||||
static void internal_nanosleep(zx_time_t ns) {
|
||||
zx_status_t status = _zx_nanosleep(_zx_deadline_after(ns));
|
||||
void internal_usleep(u64 useconds) {
|
||||
zx_status_t status = _zx_nanosleep(_zx_deadline_after(ZX_USEC(useconds)));
|
||||
CHECK_EQ(status, ZX_OK);
|
||||
}
|
||||
|
||||
unsigned int internal_sleep(unsigned int seconds) {
|
||||
internal_nanosleep(ZX_SEC(seconds));
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 NanoTime() {
|
||||
zx_handle_t utc_clock = _zx_utc_reference_get();
|
||||
CHECK_NE(utc_clock, ZX_HANDLE_INVALID);
|
||||
@ -78,10 +73,6 @@ void Abort() { abort(); }
|
||||
|
||||
int Atexit(void (*function)(void)) { return atexit(function); }
|
||||
|
||||
void SleepForSeconds(int seconds) { internal_sleep(seconds); }
|
||||
|
||||
void SleepForMillis(int millis) { internal_nanosleep(ZX_MSEC(millis)); }
|
||||
|
||||
void GetThreadStackTopAndBottom(bool, uptr *stack_top, uptr *stack_bottom) {
|
||||
pthread_attr_t attr;
|
||||
CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
|
||||
@ -109,6 +100,18 @@ bool SignalContext::IsStackOverflow() const { return false; }
|
||||
void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }
|
||||
const char *SignalContext::Describe() const { UNIMPLEMENTED(); }
|
||||
|
||||
void FutexWait(atomic_uint32_t *p, u32 cmp) {
|
||||
zx_status_t status = _zx_futex_wait(reinterpret_cast<zx_futex_t *>(p), cmp,
|
||||
ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
|
||||
if (status != ZX_ERR_BAD_STATE) // Normal race.
|
||||
CHECK_EQ(status, ZX_OK);
|
||||
}
|
||||
|
||||
void FutexWake(atomic_uint32_t *p, u32 count) {
|
||||
zx_status_t status = _zx_futex_wake(reinterpret_cast<zx_futex_t *>(p), count);
|
||||
CHECK_EQ(status, ZX_OK);
|
||||
}
|
||||
|
||||
enum MutexState : int { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 };
|
||||
|
||||
BlockingMutex::BlockingMutex() {
|
||||
@ -145,8 +148,8 @@ void BlockingMutex::Unlock() {
|
||||
}
|
||||
}
|
||||
|
||||
void BlockingMutex::CheckLocked() {
|
||||
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
|
||||
void BlockingMutex::CheckLocked() const {
|
||||
auto m = reinterpret_cast<atomic_uint32_t const *>(&opaque_storage_);
|
||||
CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
|
||||
}
|
||||
|
||||
@ -156,8 +159,10 @@ uptr GetMmapGranularity() { return _zx_system_get_page_size(); }
|
||||
|
||||
sanitizer_shadow_bounds_t ShadowBounds;
|
||||
|
||||
void InitShadowBounds() { ShadowBounds = __sanitizer_shadow_bounds(); }
|
||||
|
||||
uptr GetMaxUserVirtualAddress() {
|
||||
ShadowBounds = __sanitizer_shadow_bounds();
|
||||
InitShadowBounds();
|
||||
return ShadowBounds.memory_limit - 1;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,8 @@ struct MemoryMappingLayoutData {
|
||||
size_t current; // Current index into the vector.
|
||||
};
|
||||
|
||||
void InitShadowBounds();
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_FUCHSIA
|
||||
|
@ -67,7 +67,8 @@ uptr internal_ftruncate(fd_t fd, uptr size);
|
||||
|
||||
// OS
|
||||
void NORETURN internal__exit(int exitcode);
|
||||
unsigned int internal_sleep(unsigned int seconds);
|
||||
void internal_sleep(unsigned seconds);
|
||||
void internal_usleep(u64 useconds);
|
||||
|
||||
uptr internal_getpid();
|
||||
uptr internal_getppid();
|
||||
|
@ -84,6 +84,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
|
||||
ignored_code_ranges_[idx].begin = range.beg;
|
||||
ignored_code_ranges_[idx].end = range.end;
|
||||
atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release);
|
||||
atomic_store(&enabled_, 1, memory_order_release);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -114,6 +115,7 @@ void LibIgnore::OnLibraryLoaded(const char *name) {
|
||||
instrumented_code_ranges_[idx].end = range.end;
|
||||
atomic_store(&instrumented_ranges_count_, idx + 1,
|
||||
memory_order_release);
|
||||
atomic_store(&enabled_, 1, memory_order_release);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,6 +125,29 @@ void LibIgnore::OnLibraryUnloaded() {
|
||||
OnLibraryLoaded(nullptr);
|
||||
}
|
||||
|
||||
bool LibIgnore::IsIgnoredSlow(uptr pc, bool *pc_in_ignored_lib) const {
|
||||
const uptr n = atomic_load(&ignored_ranges_count_, memory_order_acquire);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (IsInRange(pc, ignored_code_ranges_[i])) {
|
||||
*pc_in_ignored_lib = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*pc_in_ignored_lib = false;
|
||||
if (track_instrumented_libs_ && !IsPcInstrumented(pc))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LibIgnore::IsPcInstrumented(uptr pc) const {
|
||||
const uptr n = atomic_load(&instrumented_ranges_count_, memory_order_acquire);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (IsInRange(pc, instrumented_code_ranges_[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC ||
|
||||
|
@ -45,9 +45,6 @@ class LibIgnore {
|
||||
// "pc_in_ignored_lib" if the PC is in an ignored library, false otherwise.
|
||||
bool IsIgnored(uptr pc, bool *pc_in_ignored_lib) const;
|
||||
|
||||
// Checks whether the provided PC belongs to an instrumented module.
|
||||
bool IsPcInstrumented(uptr pc) const;
|
||||
|
||||
private:
|
||||
struct Lib {
|
||||
char *templ;
|
||||
@ -61,6 +58,10 @@ class LibIgnore {
|
||||
uptr end;
|
||||
};
|
||||
|
||||
// Checks whether the provided PC belongs to an instrumented module.
|
||||
bool IsPcInstrumented(uptr pc) const;
|
||||
bool IsIgnoredSlow(uptr pc, bool *pc_in_ignored_lib) const;
|
||||
|
||||
inline bool IsInRange(uptr pc, const LibCodeRange &range) const {
|
||||
return (pc >= range.begin && pc < range.end);
|
||||
}
|
||||
@ -70,6 +71,8 @@ class LibIgnore {
|
||||
static const uptr kMaxLibs = 1024;
|
||||
|
||||
// Hot part:
|
||||
atomic_uintptr_t enabled_;
|
||||
|
||||
atomic_uintptr_t ignored_ranges_count_;
|
||||
LibCodeRange ignored_code_ranges_[kMaxIgnoredRanges];
|
||||
|
||||
@ -87,27 +90,11 @@ class LibIgnore {
|
||||
void operator = (const LibIgnore&); // not implemented
|
||||
};
|
||||
|
||||
inline bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
|
||||
const uptr n = atomic_load(&ignored_ranges_count_, memory_order_acquire);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (IsInRange(pc, ignored_code_ranges_[i])) {
|
||||
*pc_in_ignored_lib = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*pc_in_ignored_lib = false;
|
||||
if (track_instrumented_libs_ && !IsPcInstrumented(pc))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool LibIgnore::IsPcInstrumented(uptr pc) const {
|
||||
const uptr n = atomic_load(&instrumented_ranges_count_, memory_order_acquire);
|
||||
for (uptr i = 0; i < n; i++) {
|
||||
if (IsInRange(pc, instrumented_code_ranges_[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
ALWAYS_INLINE
|
||||
bool LibIgnore::IsIgnored(uptr pc, bool *pc_in_ignored_lib) const {
|
||||
if (LIKELY(atomic_load(&enabled_, memory_order_acquire) == 0))
|
||||
return false;
|
||||
return IsIgnoredSlow(pc, pc_in_ignored_lib);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
@ -430,13 +430,11 @@ uptr internal_sched_yield() {
|
||||
return internal_syscall(SYSCALL(sched_yield));
|
||||
}
|
||||
|
||||
unsigned int internal_sleep(unsigned int seconds) {
|
||||
void internal_usleep(u64 useconds) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = seconds;
|
||||
ts.tv_nsec = 0;
|
||||
int res = internal_syscall(SYSCALL(nanosleep), &ts, &ts);
|
||||
if (res) return ts.tv_sec;
|
||||
return 0;
|
||||
ts.tv_sec = useconds / 1000000;
|
||||
ts.tv_nsec = (useconds % 1000000) * 1000;
|
||||
internal_syscall(SYSCALL(nanosleep), &ts, &ts);
|
||||
}
|
||||
|
||||
uptr internal_execve(const char *filename, char *const argv[],
|
||||
@ -641,11 +639,27 @@ char **GetEnviron() {
|
||||
}
|
||||
|
||||
#if !SANITIZER_SOLARIS
|
||||
enum MutexState {
|
||||
MtxUnlocked = 0,
|
||||
MtxLocked = 1,
|
||||
MtxSleeping = 2
|
||||
};
|
||||
void FutexWait(atomic_uint32_t *p, u32 cmp) {
|
||||
# if SANITIZER_FREEBSD
|
||||
_umtx_op(p, UMTX_OP_WAIT_UINT, cmp, 0, 0);
|
||||
# elif SANITIZER_NETBSD
|
||||
sched_yield(); /* No userspace futex-like synchronization */
|
||||
# else
|
||||
internal_syscall(SYSCALL(futex), (uptr)p, FUTEX_WAIT_PRIVATE, cmp, 0, 0, 0);
|
||||
# endif
|
||||
}
|
||||
|
||||
void FutexWake(atomic_uint32_t *p, u32 count) {
|
||||
# if SANITIZER_FREEBSD
|
||||
_umtx_op(p, UMTX_OP_WAKE, count, 0, 0);
|
||||
# elif SANITIZER_NETBSD
|
||||
/* No userspace futex-like synchronization */
|
||||
# else
|
||||
internal_syscall(SYSCALL(futex), (uptr)p, FUTEX_WAKE_PRIVATE, count, 0, 0, 0);
|
||||
# endif
|
||||
}
|
||||
|
||||
enum { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 };
|
||||
|
||||
BlockingMutex::BlockingMutex() {
|
||||
internal_memset(this, 0, sizeof(*this));
|
||||
@ -683,11 +697,11 @@ void BlockingMutex::Unlock() {
|
||||
}
|
||||
}
|
||||
|
||||
void BlockingMutex::CheckLocked() {
|
||||
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
|
||||
void BlockingMutex::CheckLocked() const {
|
||||
auto m = reinterpret_cast<atomic_uint32_t const *>(&opaque_storage_);
|
||||
CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
|
||||
}
|
||||
#endif // !SANITIZER_SOLARIS
|
||||
# endif // !SANITIZER_SOLARIS
|
||||
|
||||
// ----------------- sanitizer_linux.h
|
||||
// The actual size of this structure is specified by d_reclen.
|
||||
@ -884,7 +898,7 @@ void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
|
||||
__sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
|
||||
const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);
|
||||
const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
|
||||
k_set->sig[idx] &= ~(1 << bit);
|
||||
k_set->sig[idx] &= ~((uptr)1 << bit);
|
||||
}
|
||||
|
||||
bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
|
||||
@ -894,7 +908,7 @@ bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
|
||||
__sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
|
||||
const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);
|
||||
const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
|
||||
return k_set->sig[idx] & (1 << bit);
|
||||
return k_set->sig[idx] & ((uptr)1 << bit);
|
||||
}
|
||||
#elif SANITIZER_FREEBSD
|
||||
void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
|
||||
|
@ -203,7 +203,7 @@ void InitTlsSize() {
|
||||
g_use_dlpi_tls_data =
|
||||
GetLibcVersion(&major, &minor, &patch) && major == 2 && minor >= 25;
|
||||
|
||||
#if defined(__x86_64__) || defined(__powerpc64__)
|
||||
#if defined(__aarch64__) || defined(__x86_64__) || defined(__powerpc64__)
|
||||
void *get_tls_static_info = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
|
||||
size_t tls_align;
|
||||
((void (*)(size_t *, size_t *))get_tls_static_info)(&g_tls_size, &tls_align);
|
||||
@ -317,21 +317,44 @@ struct TlsBlock {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
#ifdef __s390__
|
||||
extern "C" uptr __tls_get_offset(void *arg);
|
||||
|
||||
static uptr TlsGetOffset(uptr ti_module, uptr ti_offset) {
|
||||
// The __tls_get_offset ABI requires %r12 to point to GOT and %r2 to be an
|
||||
// offset of a struct tls_index inside GOT. We don't possess either of the
|
||||
// two, so violate the letter of the "ELF Handling For Thread-Local
|
||||
// Storage" document and assume that the implementation just dereferences
|
||||
// %r2 + %r12.
|
||||
uptr tls_index[2] = {ti_module, ti_offset};
|
||||
register uptr r2 asm("2") = 0;
|
||||
register void *r12 asm("12") = tls_index;
|
||||
asm("basr %%r14, %[__tls_get_offset]"
|
||||
: "+r"(r2)
|
||||
: [__tls_get_offset] "r"(__tls_get_offset), "r"(r12)
|
||||
: "memory", "cc", "0", "1", "3", "4", "5", "14");
|
||||
return r2;
|
||||
}
|
||||
#else
|
||||
extern "C" void *__tls_get_addr(size_t *);
|
||||
#endif
|
||||
|
||||
static int CollectStaticTlsBlocks(struct dl_phdr_info *info, size_t size,
|
||||
void *data) {
|
||||
if (!info->dlpi_tls_modid)
|
||||
return 0;
|
||||
uptr begin = (uptr)info->dlpi_tls_data;
|
||||
#ifndef __s390__
|
||||
if (!g_use_dlpi_tls_data) {
|
||||
// Call __tls_get_addr as a fallback. This forces TLS allocation on glibc
|
||||
// and FreeBSD.
|
||||
#ifdef __s390__
|
||||
begin = (uptr)__builtin_thread_pointer() +
|
||||
TlsGetOffset(info->dlpi_tls_modid, 0);
|
||||
#else
|
||||
size_t mod_and_off[2] = {info->dlpi_tls_modid, 0};
|
||||
begin = (uptr)__tls_get_addr(mod_and_off);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
for (unsigned i = 0; i != info->dlpi_phnum; ++i)
|
||||
if (info->dlpi_phdr[i].p_type == PT_TLS) {
|
||||
static_cast<InternalMmapVector<TlsBlock> *>(data)->push_back(
|
||||
@ -427,12 +450,16 @@ static void GetTls(uptr *addr, uptr *size) {
|
||||
*size = 0;
|
||||
}
|
||||
#elif SANITIZER_GLIBC && defined(__x86_64__)
|
||||
// For x86-64, use an O(1) approach which requires precise
|
||||
// ThreadDescriptorSize. g_tls_size was initialized in InitTlsSize.
|
||||
// For aarch64 and x86-64, use an O(1) approach which requires relatively
|
||||
// precise ThreadDescriptorSize. g_tls_size was initialized in InitTlsSize.
|
||||
asm("mov %%fs:16,%0" : "=r"(*addr));
|
||||
*size = g_tls_size;
|
||||
*addr -= *size;
|
||||
*addr += ThreadDescriptorSize();
|
||||
#elif SANITIZER_GLIBC && defined(__aarch64__)
|
||||
*addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) -
|
||||
ThreadDescriptorSize();
|
||||
*size = g_tls_size + ThreadDescriptorSize();
|
||||
#elif SANITIZER_GLIBC && defined(__powerpc64__)
|
||||
// Workaround for glibc<2.25(?). 2.27 is known to not need this.
|
||||
uptr tp;
|
||||
@ -732,13 +759,9 @@ u32 GetNumberOfCPUs() {
|
||||
#elif SANITIZER_SOLARIS
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#else
|
||||
#if defined(CPU_COUNT)
|
||||
cpu_set_t CPUs;
|
||||
CHECK_EQ(sched_getaffinity(0, sizeof(cpu_set_t), &CPUs), 0);
|
||||
return CPU_COUNT(&CPUs);
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
#if defined(__has_include) && __has_include(<os/trace.h>) && defined(__BLOCKS__)
|
||||
#if defined(__has_include) && __has_include(<os/trace.h>)
|
||||
#define SANITIZER_OS_TRACE 1
|
||||
#include <os/trace.h>
|
||||
#else
|
||||
@ -70,15 +70,7 @@ extern "C" {
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach/vm_statistics.h>
|
||||
#include <malloc/malloc.h>
|
||||
#if defined(__has_builtin) && __has_builtin(__builtin_os_log_format)
|
||||
# include <os/log.h>
|
||||
#else
|
||||
/* Without support for __builtin_os_log_format, fall back to the older
|
||||
method. */
|
||||
# define OS_LOG_DEFAULT 0
|
||||
# define os_log_error(A,B,C) \
|
||||
asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", (C));
|
||||
#endif
|
||||
#include <os/log.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
@ -227,9 +219,7 @@ void internal__exit(int exitcode) {
|
||||
_exit(exitcode);
|
||||
}
|
||||
|
||||
unsigned int internal_sleep(unsigned int seconds) {
|
||||
return sleep(seconds);
|
||||
}
|
||||
void internal_usleep(u64 useconds) { usleep(useconds); }
|
||||
|
||||
uptr internal_getpid() {
|
||||
return getpid();
|
||||
@ -519,6 +509,13 @@ void MprotectMallocZones(void *addr, int prot) {
|
||||
}
|
||||
}
|
||||
|
||||
void FutexWait(atomic_uint32_t *p, u32 cmp) {
|
||||
// FIXME: implement actual blocking.
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
void FutexWake(atomic_uint32_t *p, u32 count) {}
|
||||
|
||||
BlockingMutex::BlockingMutex() {
|
||||
internal_memset(this, 0, sizeof(*this));
|
||||
}
|
||||
@ -534,7 +531,7 @@ void BlockingMutex::Unlock() {
|
||||
OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
|
||||
}
|
||||
|
||||
void BlockingMutex::CheckLocked() {
|
||||
void BlockingMutex::CheckLocked() const {
|
||||
CHECK_NE(*(OSSpinLock*)&opaque_storage_, 0);
|
||||
}
|
||||
|
||||
|
@ -14,26 +14,6 @@
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
/* TARGET_OS_OSX is not present in SDKs before Darwin16 (macOS 10.12) use
|
||||
TARGET_OS_MAC (we have no support for iOS in any form for these versions,
|
||||
so there's no ambiguity). */
|
||||
#if !defined(TARGET_OS_OSX) && TARGET_OS_MAC
|
||||
# define TARGET_OS_OSX 1
|
||||
#endif
|
||||
|
||||
/* Other TARGET_OS_xxx are not present on earlier versions, define them to
|
||||
0 (we have no support for them; they are not valid targets anyway). */
|
||||
#ifndef TARGET_OS_IOS
|
||||
#define TARGET_OS_IOS 0
|
||||
#endif
|
||||
#ifndef TARGET_OS_TV
|
||||
#define TARGET_OS_TV 0
|
||||
#endif
|
||||
#ifndef TARGET_OS_WATCH
|
||||
#define TARGET_OS_WATCH 0
|
||||
#endif
|
||||
|
||||
#if SANITIZER_MAC
|
||||
#include "sanitizer_posix.h"
|
||||
|
||||
|
39
libsanitizer/sanitizer_common/sanitizer_mutex.cpp
Normal file
39
libsanitizer/sanitizer_common/sanitizer_mutex.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
//===-- sanitizer_mutex.cpp -----------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between AddressSanitizer and ThreadSanitizer
|
||||
// run-time libraries.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_mutex.h"
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
void Semaphore::Wait() {
|
||||
u32 count = atomic_load(&state_, memory_order_relaxed);
|
||||
for (;;) {
|
||||
if (count == 0) {
|
||||
FutexWait(&state_, 0);
|
||||
count = atomic_load(&state_, memory_order_relaxed);
|
||||
continue;
|
||||
}
|
||||
if (atomic_compare_exchange_weak(&state_, &count, count - 1,
|
||||
memory_order_acquire))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Semaphore::Post(u32 count) {
|
||||
CHECK_NE(count, 0);
|
||||
atomic_fetch_add(&state_, count, memory_order_release);
|
||||
FutexWake(&state_, count);
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
@ -16,30 +16,29 @@
|
||||
#include "sanitizer_atomic.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_libc.h"
|
||||
#include "sanitizer_thread_safety.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
class StaticSpinMutex {
|
||||
class MUTEX StaticSpinMutex {
|
||||
public:
|
||||
void Init() {
|
||||
atomic_store(&state_, 0, memory_order_relaxed);
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
void Lock() ACQUIRE() {
|
||||
if (TryLock())
|
||||
return;
|
||||
LockSlow();
|
||||
}
|
||||
|
||||
bool TryLock() {
|
||||
bool TryLock() TRY_ACQUIRE(true) {
|
||||
return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
atomic_store(&state_, 0, memory_order_release);
|
||||
}
|
||||
void Unlock() RELEASE() { atomic_store(&state_, 0, memory_order_release); }
|
||||
|
||||
void CheckLocked() {
|
||||
void CheckLocked() const CHECK_LOCKED() {
|
||||
CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1);
|
||||
}
|
||||
|
||||
@ -59,24 +58,223 @@ class StaticSpinMutex {
|
||||
}
|
||||
};
|
||||
|
||||
class SpinMutex : public StaticSpinMutex {
|
||||
class MUTEX SpinMutex : public StaticSpinMutex {
|
||||
public:
|
||||
SpinMutex() {
|
||||
Init();
|
||||
}
|
||||
|
||||
private:
|
||||
SpinMutex(const SpinMutex&);
|
||||
void operator=(const SpinMutex&);
|
||||
SpinMutex(const SpinMutex &) = delete;
|
||||
void operator=(const SpinMutex &) = delete;
|
||||
};
|
||||
|
||||
class BlockingMutex {
|
||||
// Semaphore provides an OS-dependent way to park/unpark threads.
|
||||
// The last thread returned from Wait can destroy the object
|
||||
// (destruction-safety).
|
||||
class Semaphore {
|
||||
public:
|
||||
constexpr Semaphore() {}
|
||||
Semaphore(const Semaphore &) = delete;
|
||||
void operator=(const Semaphore &) = delete;
|
||||
|
||||
void Wait();
|
||||
void Post(u32 count = 1);
|
||||
|
||||
private:
|
||||
atomic_uint32_t state_ = {0};
|
||||
};
|
||||
|
||||
// Reader-writer mutex.
|
||||
class MUTEX Mutex2 {
|
||||
public:
|
||||
constexpr Mutex2() {}
|
||||
|
||||
void Lock() ACQUIRE() {
|
||||
u64 reset_mask = ~0ull;
|
||||
u64 state = atomic_load_relaxed(&state_);
|
||||
const uptr kMaxSpinIters = 1500;
|
||||
for (uptr spin_iters = 0;; spin_iters++) {
|
||||
u64 new_state;
|
||||
bool locked = (state & (kWriterLock | kReaderLockMask)) != 0;
|
||||
if (LIKELY(!locked)) {
|
||||
// The mutex is not read-/write-locked, try to lock.
|
||||
new_state = (state | kWriterLock) & reset_mask;
|
||||
} else if (spin_iters > kMaxSpinIters) {
|
||||
// We've spun enough, increment waiting writers count and block.
|
||||
// The counter will be decremented by whoever wakes us.
|
||||
new_state = (state + kWaitingWriterInc) & reset_mask;
|
||||
} else if ((state & kWriterSpinWait) == 0) {
|
||||
// Active spinning, but denote our presence so that unlocking
|
||||
// thread does not wake up other threads.
|
||||
new_state = state | kWriterSpinWait;
|
||||
} else {
|
||||
// Active spinning.
|
||||
state = atomic_load(&state_, memory_order_relaxed);
|
||||
continue;
|
||||
}
|
||||
if (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
|
||||
memory_order_acquire)))
|
||||
continue;
|
||||
if (LIKELY(!locked))
|
||||
return; // We've locked the mutex.
|
||||
if (spin_iters > kMaxSpinIters) {
|
||||
// We've incremented waiting writers, so now block.
|
||||
writers_.Wait();
|
||||
spin_iters = 0;
|
||||
state = atomic_load(&state_, memory_order_relaxed);
|
||||
DCHECK_NE(state & kWriterSpinWait, 0);
|
||||
} else {
|
||||
// We've set kWriterSpinWait, but we are still in active spinning.
|
||||
}
|
||||
// We either blocked and were unblocked,
|
||||
// or we just spun but set kWriterSpinWait.
|
||||
// Either way we need to reset kWriterSpinWait
|
||||
// next time we take the lock or block again.
|
||||
reset_mask = ~kWriterSpinWait;
|
||||
}
|
||||
}
|
||||
|
||||
void Unlock() RELEASE() {
|
||||
bool wake_writer;
|
||||
u64 wake_readers;
|
||||
u64 new_state;
|
||||
u64 state = atomic_load_relaxed(&state_);
|
||||
do {
|
||||
DCHECK_NE(state & kWriterLock, 0);
|
||||
DCHECK_EQ(state & kReaderLockMask, 0);
|
||||
new_state = state & ~kWriterLock;
|
||||
wake_writer =
|
||||
(state & kWriterSpinWait) == 0 && (state & kWaitingWriterMask) != 0;
|
||||
if (wake_writer)
|
||||
new_state = (new_state - kWaitingWriterInc) | kWriterSpinWait;
|
||||
wake_readers =
|
||||
(state & (kWriterSpinWait | kWaitingWriterMask)) != 0
|
||||
? 0
|
||||
: ((state & kWaitingReaderMask) >> kWaitingReaderShift);
|
||||
if (wake_readers)
|
||||
new_state = (new_state & ~kWaitingReaderMask) +
|
||||
(wake_readers << kReaderLockShift);
|
||||
} while (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
|
||||
memory_order_release)));
|
||||
if (UNLIKELY(wake_writer))
|
||||
writers_.Post();
|
||||
else if (UNLIKELY(wake_readers))
|
||||
readers_.Post(wake_readers);
|
||||
}
|
||||
|
||||
void ReadLock() ACQUIRE_SHARED() {
|
||||
bool locked;
|
||||
u64 new_state;
|
||||
u64 state = atomic_load_relaxed(&state_);
|
||||
do {
|
||||
locked =
|
||||
(state & kReaderLockMask) == 0 &&
|
||||
(state & (kWriterLock | kWriterSpinWait | kWaitingWriterMask)) != 0;
|
||||
if (LIKELY(!locked))
|
||||
new_state = state + kReaderLockInc;
|
||||
else
|
||||
new_state = state + kWaitingReaderInc;
|
||||
} while (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
|
||||
memory_order_acquire)));
|
||||
if (UNLIKELY(locked))
|
||||
readers_.Wait();
|
||||
DCHECK_EQ(atomic_load_relaxed(&state_) & kWriterLock, 0);
|
||||
DCHECK_NE(atomic_load_relaxed(&state_) & kReaderLockMask, 0);
|
||||
}
|
||||
|
||||
void ReadUnlock() RELEASE_SHARED() {
|
||||
bool wake;
|
||||
u64 new_state;
|
||||
u64 state = atomic_load_relaxed(&state_);
|
||||
do {
|
||||
DCHECK_NE(state & kReaderLockMask, 0);
|
||||
DCHECK_EQ(state & (kWaitingReaderMask | kWriterLock), 0);
|
||||
new_state = state - kReaderLockInc;
|
||||
wake = (new_state & (kReaderLockMask | kWriterSpinWait)) == 0 &&
|
||||
(new_state & kWaitingWriterMask) != 0;
|
||||
if (wake)
|
||||
new_state = (new_state - kWaitingWriterInc) | kWriterSpinWait;
|
||||
} while (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
|
||||
memory_order_release)));
|
||||
if (UNLIKELY(wake))
|
||||
writers_.Post();
|
||||
}
|
||||
|
||||
// This function does not guarantee an explicit check that the calling thread
|
||||
// is the thread which owns the mutex. This behavior, while more strictly
|
||||
// correct, causes problems in cases like StopTheWorld, where a parent thread
|
||||
// owns the mutex but a child checks that it is locked. Rather than
|
||||
// maintaining complex state to work around those situations, the check only
|
||||
// checks that the mutex is owned.
|
||||
void CheckWriteLocked() const CHECK_LOCKED() {
|
||||
CHECK(atomic_load(&state_, memory_order_relaxed) & kWriterLock);
|
||||
}
|
||||
|
||||
void CheckLocked() const CHECK_LOCKED() { CheckWriteLocked(); }
|
||||
|
||||
void CheckReadLocked() const CHECK_LOCKED() {
|
||||
CHECK(atomic_load(&state_, memory_order_relaxed) & kReaderLockMask);
|
||||
}
|
||||
|
||||
private:
|
||||
atomic_uint64_t state_ = {0};
|
||||
Semaphore writers_;
|
||||
Semaphore readers_;
|
||||
|
||||
// The state has 3 counters:
|
||||
// - number of readers holding the lock,
|
||||
// if non zero, the mutex is read-locked
|
||||
// - number of waiting readers,
|
||||
// if not zero, the mutex is write-locked
|
||||
// - number of waiting writers,
|
||||
// if non zero, the mutex is read- or write-locked
|
||||
// And 2 flags:
|
||||
// - writer lock
|
||||
// if set, the mutex is write-locked
|
||||
// - a writer is awake and spin-waiting
|
||||
// the flag is used to prevent thundering herd problem
|
||||
// (new writers are not woken if this flag is set)
|
||||
//
|
||||
// Writer support active spinning, readers does not.
|
||||
// But readers are more aggressive and always take the mutex
|
||||
// if there are any other readers.
|
||||
// Writers hand off the mutex to readers: after wake up readers
|
||||
// already assume ownership of the mutex (don't need to do any
|
||||
// state updates). But the mutex is not handed off to writers,
|
||||
// after wake up writers compete to lock the mutex again.
|
||||
// This is needed to allow repeated write locks even in presence
|
||||
// of other blocked writers.
|
||||
static constexpr u64 kCounterWidth = 20;
|
||||
static constexpr u64 kReaderLockShift = 0;
|
||||
static constexpr u64 kReaderLockInc = 1ull << kReaderLockShift;
|
||||
static constexpr u64 kReaderLockMask = ((1ull << kCounterWidth) - 1)
|
||||
<< kReaderLockShift;
|
||||
static constexpr u64 kWaitingReaderShift = kCounterWidth;
|
||||
static constexpr u64 kWaitingReaderInc = 1ull << kWaitingReaderShift;
|
||||
static constexpr u64 kWaitingReaderMask = ((1ull << kCounterWidth) - 1)
|
||||
<< kWaitingReaderShift;
|
||||
static constexpr u64 kWaitingWriterShift = 2 * kCounterWidth;
|
||||
static constexpr u64 kWaitingWriterInc = 1ull << kWaitingWriterShift;
|
||||
static constexpr u64 kWaitingWriterMask = ((1ull << kCounterWidth) - 1)
|
||||
<< kWaitingWriterShift;
|
||||
static constexpr u64 kWriterLock = 1ull << (3 * kCounterWidth);
|
||||
static constexpr u64 kWriterSpinWait = 1ull << (3 * kCounterWidth + 1);
|
||||
|
||||
Mutex2(const Mutex2 &) = delete;
|
||||
void operator=(const Mutex2 &) = delete;
|
||||
};
|
||||
|
||||
void FutexWait(atomic_uint32_t *p, u32 cmp);
|
||||
void FutexWake(atomic_uint32_t *p, u32 count);
|
||||
|
||||
class MUTEX BlockingMutex {
|
||||
public:
|
||||
explicit constexpr BlockingMutex(LinkerInitialized)
|
||||
: opaque_storage_ {0, }, owner_ {0} {}
|
||||
BlockingMutex();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
void Lock() ACQUIRE();
|
||||
void Unlock() RELEASE();
|
||||
|
||||
// This function does not guarantee an explicit check that the calling thread
|
||||
// is the thread which owns the mutex. This behavior, while more strictly
|
||||
@ -85,7 +283,7 @@ class BlockingMutex {
|
||||
// maintaining complex state to work around those situations, the check only
|
||||
// checks that the mutex is owned, and assumes callers to be generally
|
||||
// well-behaved.
|
||||
void CheckLocked();
|
||||
void CheckLocked() const CHECK_LOCKED();
|
||||
|
||||
private:
|
||||
// Solaris mutex_t has a member that requires 64-bit alignment.
|
||||
@ -94,7 +292,7 @@ class BlockingMutex {
|
||||
};
|
||||
|
||||
// Reader-writer spin mutex.
|
||||
class RWMutex {
|
||||
class MUTEX RWMutex {
|
||||
public:
|
||||
RWMutex() {
|
||||
atomic_store(&state_, kUnlocked, memory_order_relaxed);
|
||||
@ -104,7 +302,7 @@ class RWMutex {
|
||||
CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
void Lock() ACQUIRE() {
|
||||
u32 cmp = kUnlocked;
|
||||
if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
|
||||
memory_order_acquire))
|
||||
@ -112,27 +310,27 @@ class RWMutex {
|
||||
LockSlow();
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
void Unlock() RELEASE() {
|
||||
u32 prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
|
||||
DCHECK_NE(prev & kWriteLock, 0);
|
||||
(void)prev;
|
||||
}
|
||||
|
||||
void ReadLock() {
|
||||
void ReadLock() ACQUIRE_SHARED() {
|
||||
u32 prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
|
||||
if ((prev & kWriteLock) == 0)
|
||||
return;
|
||||
ReadLockSlow();
|
||||
}
|
||||
|
||||
void ReadUnlock() {
|
||||
void ReadUnlock() RELEASE_SHARED() {
|
||||
u32 prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
|
||||
DCHECK_EQ(prev & kWriteLock, 0);
|
||||
DCHECK_GT(prev & ~kWriteLock, 0);
|
||||
(void)prev;
|
||||
}
|
||||
|
||||
void CheckLocked() {
|
||||
void CheckLocked() const CHECK_LOCKED() {
|
||||
CHECK_NE(atomic_load(&state_, memory_order_relaxed), kUnlocked);
|
||||
}
|
||||
|
||||
@ -171,46 +369,40 @@ class RWMutex {
|
||||
}
|
||||
}
|
||||
|
||||
RWMutex(const RWMutex&);
|
||||
void operator = (const RWMutex&);
|
||||
RWMutex(const RWMutex &) = delete;
|
||||
void operator=(const RWMutex &) = delete;
|
||||
};
|
||||
|
||||
template<typename MutexType>
|
||||
class GenericScopedLock {
|
||||
template <typename MutexType>
|
||||
class SCOPED_LOCK GenericScopedLock {
|
||||
public:
|
||||
explicit GenericScopedLock(MutexType *mu)
|
||||
: mu_(mu) {
|
||||
explicit GenericScopedLock(MutexType *mu) ACQUIRE(mu) : mu_(mu) {
|
||||
mu_->Lock();
|
||||
}
|
||||
|
||||
~GenericScopedLock() {
|
||||
mu_->Unlock();
|
||||
}
|
||||
~GenericScopedLock() RELEASE() { mu_->Unlock(); }
|
||||
|
||||
private:
|
||||
MutexType *mu_;
|
||||
|
||||
GenericScopedLock(const GenericScopedLock&);
|
||||
void operator=(const GenericScopedLock&);
|
||||
GenericScopedLock(const GenericScopedLock &) = delete;
|
||||
void operator=(const GenericScopedLock &) = delete;
|
||||
};
|
||||
|
||||
template<typename MutexType>
|
||||
class GenericScopedReadLock {
|
||||
template <typename MutexType>
|
||||
class SCOPED_LOCK GenericScopedReadLock {
|
||||
public:
|
||||
explicit GenericScopedReadLock(MutexType *mu)
|
||||
: mu_(mu) {
|
||||
explicit GenericScopedReadLock(MutexType *mu) ACQUIRE(mu) : mu_(mu) {
|
||||
mu_->ReadLock();
|
||||
}
|
||||
|
||||
~GenericScopedReadLock() {
|
||||
mu_->ReadUnlock();
|
||||
}
|
||||
~GenericScopedReadLock() RELEASE() { mu_->ReadUnlock(); }
|
||||
|
||||
private:
|
||||
MutexType *mu_;
|
||||
|
||||
GenericScopedReadLock(const GenericScopedReadLock&);
|
||||
void operator=(const GenericScopedReadLock&);
|
||||
GenericScopedReadLock(const GenericScopedReadLock &) = delete;
|
||||
void operator=(const GenericScopedReadLock &) = delete;
|
||||
};
|
||||
|
||||
typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
|
||||
|
@ -215,15 +215,12 @@ void internal__exit(int exitcode) {
|
||||
Die(); // Unreachable.
|
||||
}
|
||||
|
||||
unsigned int internal_sleep(unsigned int seconds) {
|
||||
void internal_usleep(u64 useconds) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = seconds;
|
||||
ts.tv_nsec = 0;
|
||||
ts.tv_sec = useconds / 1000000;
|
||||
ts.tv_nsec = (useconds % 1000000) * 1000;
|
||||
CHECK(&_sys___nanosleep50);
|
||||
int res = _sys___nanosleep50(&ts, &ts);
|
||||
if (res)
|
||||
return ts.tv_sec;
|
||||
return 0;
|
||||
_sys___nanosleep50(&ts, &ts);
|
||||
}
|
||||
|
||||
uptr internal_execve(const char *filename, char *const argv[],
|
||||
|
@ -13,10 +13,9 @@
|
||||
#define SANITIZER_PLATFORM_H
|
||||
|
||||
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
|
||||
!defined(__APPLE__) && !defined(_WIN32) && \
|
||||
!defined(__Fuchsia__) && !defined(__rtems__) && \
|
||||
!(defined(__sun__) && defined(__svr4__))
|
||||
# error "This operating system is not supported"
|
||||
!defined(__APPLE__) && !defined(_WIN32) && !defined(__Fuchsia__) && \
|
||||
!(defined(__sun__) && defined(__svr4__))
|
||||
# error "This operating system is not supported"
|
||||
#endif
|
||||
|
||||
// Get __GLIBC__ on a glibc platform. Exclude Android: features.h includes C
|
||||
@ -117,12 +116,6 @@
|
||||
# define SANITIZER_FUCHSIA 0
|
||||
#endif
|
||||
|
||||
#if defined(__rtems__)
|
||||
# define SANITIZER_RTEMS 1
|
||||
#else
|
||||
# define SANITIZER_RTEMS 0
|
||||
#endif
|
||||
|
||||
#define SANITIZER_POSIX \
|
||||
(SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC || \
|
||||
SANITIZER_NETBSD || SANITIZER_SOLARIS)
|
||||
@ -226,12 +219,6 @@
|
||||
# define SANITIZER_SOLARIS32 0
|
||||
#endif
|
||||
|
||||
#if defined(__myriad2__)
|
||||
# define SANITIZER_MYRIAD2 1
|
||||
#else
|
||||
# define SANITIZER_MYRIAD2 0
|
||||
#endif
|
||||
|
||||
#if defined(__riscv) && (__riscv_xlen == 64)
|
||||
#define SANITIZER_RISCV64 1
|
||||
#else
|
||||
@ -374,9 +361,9 @@
|
||||
# define SANITIZER_CACHE_LINE_SIZE 64
|
||||
#endif
|
||||
|
||||
// Enable offline markup symbolizer for Fuchsia and RTEMS.
|
||||
#if SANITIZER_FUCHSIA || SANITIZER_RTEMS
|
||||
#define SANITIZER_SYMBOLIZER_MARKUP 1
|
||||
// Enable offline markup symbolizer for Fuchsia.
|
||||
#if SANITIZER_FUCHSIA
|
||||
# define SANITIZER_SYMBOLIZER_MARKUP 1
|
||||
#else
|
||||
#define SANITIZER_SYMBOLIZER_MARKUP 0
|
||||
#endif
|
||||
|
@ -114,12 +114,6 @@
|
||||
#define SI_NOT_FUCHSIA 1
|
||||
#endif
|
||||
|
||||
#if SANITIZER_RTEMS
|
||||
#define SI_NOT_RTEMS 0
|
||||
#else
|
||||
#define SI_NOT_RTEMS 1
|
||||
#endif
|
||||
|
||||
#if SANITIZER_SOLARIS
|
||||
#define SI_SOLARIS 1
|
||||
#else
|
||||
@ -482,13 +476,12 @@
|
||||
#define SANITIZER_INTERCEPT_MMAP SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_MMAP64 SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO (SI_GLIBC || SI_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_MEMALIGN \
|
||||
(!SI_FREEBSD && !SI_MAC && !SI_NETBSD && SI_NOT_RTEMS)
|
||||
#define SANITIZER_INTERCEPT_MEMALIGN (!SI_FREEBSD && !SI_MAC && !SI_NETBSD)
|
||||
#define SANITIZER_INTERCEPT___LIBC_MEMALIGN SI_GLIBC
|
||||
#define SANITIZER_INTERCEPT_PVALLOC (SI_GLIBC || SI_ANDROID)
|
||||
#define SANITIZER_INTERCEPT_CFREE (SI_GLIBC && !SANITIZER_RISCV64)
|
||||
#define SANITIZER_INTERCEPT_REALLOCARRAY SI_POSIX
|
||||
#define SANITIZER_INTERCEPT_ALIGNED_ALLOC (!SI_MAC && SI_NOT_RTEMS)
|
||||
#define SANITIZER_INTERCEPT_ALIGNED_ALLOC (!SI_MAC)
|
||||
#define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC && !SI_NETBSD)
|
||||
#define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID
|
||||
#define SANITIZER_INTERCEPT_WCSCAT SI_POSIX
|
||||
@ -584,6 +577,7 @@
|
||||
(SI_POSIX && !(SANITIZER_MAC && SANITIZER_I386))
|
||||
#define SANITIZER_INTERCEPT_UNAME (SI_POSIX && !SI_FREEBSD)
|
||||
#define SANITIZER_INTERCEPT___XUNAME SI_FREEBSD
|
||||
#define SANITIZER_INTERCEPT_FLOPEN SI_FREEBSD
|
||||
|
||||
// This macro gives a way for downstream users to override the above
|
||||
// interceptor macros irrespective of the platform they are on. They have
|
||||
|
@ -26,12 +26,9 @@
|
||||
|
||||
// With old kernels (and even new kernels on powerpc) asm/stat.h uses types that
|
||||
// are not defined anywhere in userspace headers. Fake them. This seems to work
|
||||
// fine with newer headers, too. Beware that with <sys/stat.h>, struct stat
|
||||
// takes the form of struct stat64 on 32-bit platforms if _FILE_OFFSET_BITS=64.
|
||||
// Also, for some platforms (e.g. mips) there are additional members in the
|
||||
// <sys/stat.h> struct stat:s.
|
||||
// fine with newer headers, too.
|
||||
#include <linux/posix_types.h>
|
||||
#if defined(__x86_64__)
|
||||
#if defined(__x86_64__) || defined(__mips__)
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#define ino_t __kernel_ino_t
|
||||
|
@ -83,7 +83,7 @@ const unsigned struct_kernel_stat64_sz = 104;
|
||||
#elif defined(__mips__)
|
||||
const unsigned struct_kernel_stat_sz = SANITIZER_ANDROID
|
||||
? FIRST_32_SECOND_64(104, 128)
|
||||
: FIRST_32_SECOND_64(144, 216);
|
||||
: FIRST_32_SECOND_64(160, 216);
|
||||
const unsigned struct_kernel_stat64_sz = 104;
|
||||
#elif defined(__s390__) && !defined(__s390x__)
|
||||
const unsigned struct_kernel_stat_sz = 64;
|
||||
@ -650,15 +650,15 @@ struct __sanitizer_sigaction {
|
||||
#endif // !SANITIZER_ANDROID
|
||||
|
||||
#if defined(__mips__)
|
||||
struct __sanitizer_kernel_sigset_t {
|
||||
uptr sig[2];
|
||||
};
|
||||
#define __SANITIZER_KERNEL_NSIG 128
|
||||
#else
|
||||
struct __sanitizer_kernel_sigset_t {
|
||||
u8 sig[8];
|
||||
};
|
||||
#define __SANITIZER_KERNEL_NSIG 64
|
||||
#endif
|
||||
|
||||
struct __sanitizer_kernel_sigset_t {
|
||||
uptr sig[__SANITIZER_KERNEL_NSIG / (sizeof(uptr) * 8)];
|
||||
};
|
||||
|
||||
// Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
|
||||
#if SANITIZER_MIPS
|
||||
struct __sanitizer_kernel_sigaction_t {
|
||||
|
@ -128,14 +128,6 @@ void SetAddressSpaceUnlimited() {
|
||||
CHECK(AddressSpaceIsUnlimited());
|
||||
}
|
||||
|
||||
void SleepForSeconds(int seconds) {
|
||||
sleep(seconds);
|
||||
}
|
||||
|
||||
void SleepForMillis(int millis) {
|
||||
usleep(millis * 1000);
|
||||
}
|
||||
|
||||
void Abort() {
|
||||
#if !SANITIZER_GO
|
||||
// If we are handling SIGABRT, unhandle it first.
|
||||
@ -166,9 +158,10 @@ bool SupportsColoredOutput(fd_t fd) {
|
||||
#if !SANITIZER_GO
|
||||
// TODO(glider): different tools may require different altstack size.
|
||||
static uptr GetAltStackSize() {
|
||||
// SIGSTKSZ is not enough.
|
||||
static const uptr kAltStackSize = SIGSTKSZ * 4;
|
||||
return kAltStackSize;
|
||||
// Note: since GLIBC_2.31, SIGSTKSZ may be a function call, so this may be
|
||||
// more costly that you think. However GetAltStackSize is only call 2-3 times
|
||||
// per thread so don't cache the evaluation.
|
||||
return SIGSTKSZ * 4;
|
||||
}
|
||||
|
||||
void SetAlternateSignalStack() {
|
||||
|
@ -20,6 +20,10 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if defined(__x86_64__)
|
||||
# include <emmintrin.h>
|
||||
#endif
|
||||
|
||||
#if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \
|
||||
!defined(va_copy)
|
||||
# define va_copy(dst, src) ((dst) = (src))
|
||||
@ -128,7 +132,7 @@ static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
|
||||
int VSNPrintf(char *buff, int buff_length,
|
||||
const char *format, va_list args) {
|
||||
static const char *kPrintfFormatsHelp =
|
||||
"Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
|
||||
"Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x,X,V}; %p; "
|
||||
"%[-]([0-9]*)?(\\.\\*)?s; %c\n";
|
||||
RAW_CHECK(format);
|
||||
RAW_CHECK(buff_length > 0);
|
||||
@ -162,17 +166,15 @@ int VSNPrintf(char *buff, int buff_length,
|
||||
cur += have_z;
|
||||
bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l');
|
||||
cur += have_ll * 2;
|
||||
s64 dval;
|
||||
u64 uval;
|
||||
const bool have_length = have_z || have_ll;
|
||||
const bool have_flags = have_width || have_length;
|
||||
// At the moment only %s supports precision and left-justification.
|
||||
CHECK(!((precision >= 0 || left_justified) && *cur != 's'));
|
||||
switch (*cur) {
|
||||
case 'd': {
|
||||
dval = have_ll ? va_arg(args, s64)
|
||||
: have_z ? va_arg(args, sptr)
|
||||
: va_arg(args, int);
|
||||
s64 dval = have_ll ? va_arg(args, s64)
|
||||
: have_z ? va_arg(args, sptr)
|
||||
: va_arg(args, int);
|
||||
result += AppendSignedDecimal(&buff, buff_end, dval, width,
|
||||
pad_with_zero);
|
||||
break;
|
||||
@ -180,14 +182,21 @@ int VSNPrintf(char *buff, int buff_length,
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X': {
|
||||
uval = have_ll ? va_arg(args, u64)
|
||||
: have_z ? va_arg(args, uptr)
|
||||
: va_arg(args, unsigned);
|
||||
u64 uval = have_ll ? va_arg(args, u64)
|
||||
: have_z ? va_arg(args, uptr)
|
||||
: va_arg(args, unsigned);
|
||||
bool uppercase = (*cur == 'X');
|
||||
result += AppendUnsigned(&buff, buff_end, uval, (*cur == 'u') ? 10 : 16,
|
||||
width, pad_with_zero, uppercase);
|
||||
break;
|
||||
}
|
||||
case 'V': {
|
||||
for (uptr i = 0; i < 16; i++) {
|
||||
unsigned x = va_arg(args, unsigned);
|
||||
result += AppendUnsigned(&buff, buff_end, x, 16, 2, true, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
|
||||
result += AppendPointer(&buff, buff_end, va_arg(args, uptr));
|
||||
|
@ -149,7 +149,8 @@ class Quarantine {
|
||||
Cache cache_;
|
||||
char pad2_[kCacheLineSize];
|
||||
|
||||
void NOINLINE Recycle(uptr min_size, Callback cb) {
|
||||
void NOINLINE Recycle(uptr min_size, Callback cb) REQUIRES(recycle_mutex_)
|
||||
RELEASE(recycle_mutex_) {
|
||||
Cache tmp;
|
||||
{
|
||||
SpinMutexLock l(&cache_mutex_);
|
||||
|
@ -1,281 +0,0 @@
|
||||
//===-- sanitizer_rtems.cpp -----------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between various sanitizers' runtime libraries and
|
||||
// implements RTEMS-specific functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_rtems.h"
|
||||
#if SANITIZER_RTEMS
|
||||
|
||||
#define posix_memalign __real_posix_memalign
|
||||
#define free __real_free
|
||||
#define memset __real_memset
|
||||
|
||||
#include "sanitizer_file.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// There is no mmap on RTEMS. Use memalign, etc.
|
||||
#define __mmap_alloc_aligned posix_memalign
|
||||
#define __mmap_free free
|
||||
#define __mmap_memset memset
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
#include "sanitizer_syscall_generic.inc"
|
||||
|
||||
void NORETURN internal__exit(int exitcode) {
|
||||
_exit(exitcode);
|
||||
}
|
||||
|
||||
uptr internal_sched_yield() {
|
||||
return sched_yield();
|
||||
}
|
||||
|
||||
uptr internal_getpid() {
|
||||
return getpid();
|
||||
}
|
||||
|
||||
int internal_dlinfo(void *handle, int request, void *p) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
bool FileExists(const char *filename) {
|
||||
struct stat st;
|
||||
if (stat(filename, &st))
|
||||
return false;
|
||||
// Sanity check: filename is a regular file.
|
||||
return S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
uptr GetThreadSelf() { return static_cast<uptr>(pthread_self()); }
|
||||
|
||||
tid_t GetTid() { return GetThreadSelf(); }
|
||||
|
||||
void Abort() { abort(); }
|
||||
|
||||
int Atexit(void (*function)(void)) { return atexit(function); }
|
||||
|
||||
void SleepForSeconds(int seconds) { sleep(seconds); }
|
||||
|
||||
void SleepForMillis(int millis) { usleep(millis * 1000); }
|
||||
|
||||
bool SupportsColoredOutput(fd_t fd) { return false; }
|
||||
|
||||
void GetThreadStackTopAndBottom(bool at_initialization,
|
||||
uptr *stack_top, uptr *stack_bottom) {
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
|
||||
void *base = nullptr;
|
||||
size_t size = 0;
|
||||
CHECK_EQ(pthread_attr_getstack(&attr, &base, &size), 0);
|
||||
CHECK_EQ(pthread_attr_destroy(&attr), 0);
|
||||
|
||||
*stack_bottom = reinterpret_cast<uptr>(base);
|
||||
*stack_top = *stack_bottom + size;
|
||||
}
|
||||
|
||||
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
||||
uptr *tls_addr, uptr *tls_size) {
|
||||
uptr stack_top, stack_bottom;
|
||||
GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
|
||||
*stk_addr = stack_bottom;
|
||||
*stk_size = stack_top - stack_bottom;
|
||||
*tls_addr = *tls_size = 0;
|
||||
}
|
||||
|
||||
void InitializePlatformEarly() {}
|
||||
void MaybeReexec() {}
|
||||
void CheckASLR() {}
|
||||
void CheckMPROTECT() {}
|
||||
void DisableCoreDumperIfNecessary() {}
|
||||
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
|
||||
void SetAlternateSignalStack() {}
|
||||
void UnsetAlternateSignalStack() {}
|
||||
void InitTlsSize() {}
|
||||
|
||||
void SignalContext::DumpAllRegisters(void *context) {}
|
||||
const char *DescribeSignalOrException(int signo) { UNIMPLEMENTED(); }
|
||||
|
||||
enum MutexState { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 };
|
||||
|
||||
BlockingMutex::BlockingMutex() {
|
||||
internal_memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
void BlockingMutex::Lock() {
|
||||
CHECK_EQ(owner_, 0);
|
||||
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
|
||||
if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
|
||||
return;
|
||||
while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
|
||||
internal_sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void BlockingMutex::Unlock() {
|
||||
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
|
||||
u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release);
|
||||
CHECK_NE(v, MtxUnlocked);
|
||||
}
|
||||
|
||||
void BlockingMutex::CheckLocked() {
|
||||
atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
|
||||
CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
|
||||
}
|
||||
|
||||
uptr GetPageSize() { return getpagesize(); }
|
||||
|
||||
uptr GetMmapGranularity() { return GetPageSize(); }
|
||||
|
||||
uptr GetMaxVirtualAddress() {
|
||||
return (1ULL << 32) - 1; // 0xffffffff
|
||||
}
|
||||
|
||||
void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
|
||||
void* ptr = 0;
|
||||
int res = __mmap_alloc_aligned(&ptr, GetPageSize(), size);
|
||||
if (UNLIKELY(res))
|
||||
ReportMmapFailureAndDie(size, mem_type, "allocate", res, raw_report);
|
||||
__mmap_memset(ptr, 0, size);
|
||||
IncreaseTotalMmap(size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
|
||||
void* ptr = 0;
|
||||
int res = __mmap_alloc_aligned(&ptr, GetPageSize(), size);
|
||||
if (UNLIKELY(res)) {
|
||||
if (res == ENOMEM)
|
||||
return nullptr;
|
||||
ReportMmapFailureAndDie(size, mem_type, "allocate", false);
|
||||
}
|
||||
__mmap_memset(ptr, 0, size);
|
||||
IncreaseTotalMmap(size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
|
||||
const char *mem_type) {
|
||||
CHECK(IsPowerOfTwo(size));
|
||||
CHECK(IsPowerOfTwo(alignment));
|
||||
void* ptr = 0;
|
||||
int res = __mmap_alloc_aligned(&ptr, alignment, size);
|
||||
if (res)
|
||||
ReportMmapFailureAndDie(size, mem_type, "align allocate", res, false);
|
||||
__mmap_memset(ptr, 0, size);
|
||||
IncreaseTotalMmap(size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
|
||||
return MmapOrDie(size, mem_type, false);
|
||||
}
|
||||
|
||||
void UnmapOrDie(void *addr, uptr size) {
|
||||
if (!addr || !size) return;
|
||||
__mmap_free(addr);
|
||||
DecreaseTotalMmap(size);
|
||||
}
|
||||
|
||||
fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
|
||||
int flags;
|
||||
switch (mode) {
|
||||
case RdOnly: flags = O_RDONLY; break;
|
||||
case WrOnly: flags = O_WRONLY | O_CREAT | O_TRUNC; break;
|
||||
case RdWr: flags = O_RDWR | O_CREAT; break;
|
||||
}
|
||||
fd_t res = open(filename, flags, 0660);
|
||||
if (internal_iserror(res, errno_p))
|
||||
return kInvalidFd;
|
||||
return res;
|
||||
}
|
||||
|
||||
void CloseFile(fd_t fd) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
|
||||
error_t *error_p) {
|
||||
uptr res = read(fd, buff, buff_size);
|
||||
if (internal_iserror(res, error_p))
|
||||
return false;
|
||||
if (bytes_read)
|
||||
*bytes_read = res;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
|
||||
error_t *error_p) {
|
||||
uptr res = write(fd, buff, buff_size);
|
||||
if (internal_iserror(res, error_p))
|
||||
return false;
|
||||
if (bytes_written)
|
||||
*bytes_written = res;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReleaseMemoryPagesToOS(uptr beg, uptr end) {}
|
||||
void DumpProcessMap() {}
|
||||
|
||||
// There is no page protection so everything is "accessible."
|
||||
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char **GetArgv() { return nullptr; }
|
||||
char **GetEnviron() { return nullptr; }
|
||||
|
||||
const char *GetEnv(const char *name) {
|
||||
return getenv(name);
|
||||
}
|
||||
|
||||
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
|
||||
internal_strncpy(buf, "StubBinaryName", buf_len);
|
||||
return internal_strlen(buf);
|
||||
}
|
||||
|
||||
uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
|
||||
internal_strncpy(buf, "StubProcessName", buf_len);
|
||||
return internal_strlen(buf);
|
||||
}
|
||||
|
||||
bool IsPathSeparator(const char c) {
|
||||
return c == '/';
|
||||
}
|
||||
|
||||
bool IsAbsolutePath(const char *path) {
|
||||
return path != nullptr && IsPathSeparator(path[0]);
|
||||
}
|
||||
|
||||
void ReportFile::Write(const char *buffer, uptr length) {
|
||||
SpinMutexLock l(mu);
|
||||
static const char *kWriteError =
|
||||
"ReportFile::Write() can't output requested buffer!\n";
|
||||
ReopenIfNecessary();
|
||||
if (length != write(fd, buffer, length)) {
|
||||
write(fd, kWriteError, internal_strlen(kWriteError));
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
uptr MainThreadStackBase, MainThreadStackSize;
|
||||
uptr MainThreadTlsBase, MainThreadTlsSize;
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_RTEMS
|
@ -1,20 +0,0 @@
|
||||
//===-- sanitizer_rtems.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between various sanitizers' runtime libraries and
|
||||
// provides definitions for RTEMS-specific functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_RTEMS_H
|
||||
#define SANITIZER_RTEMS_H
|
||||
|
||||
#include "sanitizer_platform.h"
|
||||
#if SANITIZER_RTEMS
|
||||
#include "sanitizer_common.h"
|
||||
|
||||
#endif // SANITIZER_RTEMS
|
||||
#endif // SANITIZER_RTEMS_H
|
@ -160,6 +160,13 @@ DECLARE__REAL_AND_INTERNAL(uptr, sched_yield, void) {
|
||||
return sched_yield();
|
||||
}
|
||||
|
||||
DECLARE__REAL_AND_INTERNAL(void, usleep, u64 useconds) {
|
||||
struct timespec ts;
|
||||
ts.tv_sec = useconds / 1000000;
|
||||
ts.tv_nsec = (useconds % 1000000) * 1000;
|
||||
nanosleep(&ts, nullptr);
|
||||
}
|
||||
|
||||
DECLARE__REAL_AND_INTERNAL(uptr, execve, const char *filename,
|
||||
char *const argv[], char *const envp[]) {
|
||||
return _REAL(execve)(filename, argv, envp);
|
||||
@ -211,6 +218,13 @@ uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
|
||||
}
|
||||
|
||||
// ----------------- sanitizer_common.h
|
||||
void FutexWait(atomic_uint32_t *p, u32 cmp) {
|
||||
// FIXME: implement actual blocking.
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
void FutexWake(atomic_uint32_t *p, u32 count) {}
|
||||
|
||||
BlockingMutex::BlockingMutex() {
|
||||
CHECK(sizeof(mutex_t) <= sizeof(opaque_storage_));
|
||||
internal_memset(this, 0, sizeof(*this));
|
||||
@ -231,9 +245,7 @@ void BlockingMutex::Unlock() {
|
||||
CHECK_EQ(mutex_unlock((mutex_t *)&opaque_storage_), 0);
|
||||
}
|
||||
|
||||
void BlockingMutex::CheckLocked() {
|
||||
CHECK_EQ((uptr)thr_self(), owner_);
|
||||
}
|
||||
void BlockingMutex::CheckLocked() const { CHECK_EQ((uptr)thr_self(), owner_); }
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
|
@ -85,8 +85,8 @@ static inline uhwptr *GetCanonicFrame(uptr bp,
|
||||
// Nope, this does not look right either. This means the frame after next does
|
||||
// not have a valid frame pointer, but we can still extract the caller PC.
|
||||
// Unfortunately, there is no way to decide between GCC and LLVM frame
|
||||
// layouts. Assume GCC.
|
||||
return bp_prev - 1;
|
||||
// layouts. Assume LLVM.
|
||||
return bp_prev;
|
||||
#else
|
||||
return (uhwptr*)bp;
|
||||
#endif
|
||||
@ -109,21 +109,14 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top,
|
||||
IsAligned((uptr)frame, sizeof(*frame)) &&
|
||||
size < max_depth) {
|
||||
#ifdef __powerpc__
|
||||
// PowerPC ABIs specify that the return address is saved on the
|
||||
// *caller's* stack frame. Thus we must dereference the back chain
|
||||
// to find the caller frame before extracting it.
|
||||
// PowerPC ABIs specify that the return address is saved at offset
|
||||
// 16 of the *caller's* stack frame. Thus we must dereference the
|
||||
// back chain to find the caller frame before extracting it.
|
||||
uhwptr *caller_frame = (uhwptr*)frame[0];
|
||||
if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) ||
|
||||
!IsAligned((uptr)caller_frame, sizeof(uhwptr)))
|
||||
break;
|
||||
// For most ABIs the offset where the return address is saved is two
|
||||
// register sizes. The exception is the SVR4 ABI, which uses an
|
||||
// offset of only one register size.
|
||||
#ifdef _CALL_SYSV
|
||||
uhwptr pc1 = caller_frame[1];
|
||||
#else
|
||||
uhwptr pc1 = caller_frame[2];
|
||||
#endif
|
||||
#elif defined(__s390__)
|
||||
uhwptr pc1 = frame[14];
|
||||
#elif defined(__riscv)
|
||||
|
@ -12,6 +12,7 @@
|
||||
#ifndef SANITIZER_STACKTRACE_H
|
||||
#define SANITIZER_STACKTRACE_H
|
||||
|
||||
#include "sanitizer_common.h"
|
||||
#include "sanitizer_internal_defs.h"
|
||||
#include "sanitizer_platform.h"
|
||||
|
||||
@ -32,8 +33,8 @@ static const u32 kStackTraceMax = 256;
|
||||
// Fast unwind is the only option on Mac for now; we will need to
|
||||
// revisit this macro when slow unwind works on Mac, see
|
||||
// https://github.com/google/sanitizers/issues/137
|
||||
#if SANITIZER_MAC || SANITIZER_RTEMS
|
||||
# define SANITIZER_CAN_SLOW_UNWIND 0
|
||||
#if SANITIZER_MAC
|
||||
# define SANITIZER_CAN_SLOW_UNWIND 0
|
||||
#else
|
||||
# define SANITIZER_CAN_SLOW_UNWIND 1
|
||||
#endif
|
||||
@ -56,6 +57,16 @@ struct StackTrace {
|
||||
// Prints a symbolized stacktrace, followed by an empty line.
|
||||
void Print() const;
|
||||
|
||||
// Prints a symbolized stacktrace to the output string, followed by an empty
|
||||
// line.
|
||||
void PrintTo(InternalScopedString *output) const;
|
||||
|
||||
// Prints a symbolized stacktrace to the output buffer, followed by an empty
|
||||
// line. Returns the number of symbols that should have been written to buffer
|
||||
// (not including trailing '\0'). Thus, the string is truncated iff return
|
||||
// value is not less than "out_buf_size".
|
||||
uptr PrintTo(char *out_buf, uptr out_buf_size) const;
|
||||
|
||||
static bool WillUseFastUnwind(bool request_fast_unwind) {
|
||||
if (!SANITIZER_CAN_FAST_UNWIND)
|
||||
return false;
|
||||
@ -185,5 +196,26 @@ static inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) {
|
||||
uptr local_stack; \
|
||||
uptr sp = (uptr)&local_stack
|
||||
|
||||
// GET_CURRENT_PC() is equivalent to StackTrace::GetCurrentPc().
|
||||
// Optimized x86 version is faster than GetCurrentPc because
|
||||
// it does not involve a function call, instead it reads RIP register.
|
||||
// Reads of RIP by an instruction return RIP pointing to the next
|
||||
// instruction, which is exactly what we want here, thus 0 offset.
|
||||
// It needs to be a macro because otherwise we will get the name
|
||||
// of this function on the top of most stacks. Attribute artificial
|
||||
// does not do what it claims to do, unfortunatley. And attribute
|
||||
// __nodebug__ is clang-only. If we would have an attribute that
|
||||
// would remove this function from debug info, we could simply make
|
||||
// StackTrace::GetCurrentPc() faster.
|
||||
#if defined(__x86_64__)
|
||||
# define GET_CURRENT_PC() \
|
||||
({ \
|
||||
uptr pc; \
|
||||
asm("lea 0(%%rip), %0" : "=r"(pc)); \
|
||||
pc; \
|
||||
})
|
||||
#else
|
||||
# define GET_CURRENT_PC() StackTrace::GetCurrentPc()
|
||||
#endif
|
||||
|
||||
#endif // SANITIZER_STACKTRACE_H
|
||||
|
@ -18,46 +18,119 @@
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
void StackTrace::Print() const {
|
||||
namespace {
|
||||
|
||||
class StackTraceTextPrinter {
|
||||
public:
|
||||
StackTraceTextPrinter(const char *stack_trace_fmt, char frame_delimiter,
|
||||
InternalScopedString *output,
|
||||
InternalScopedString *dedup_token)
|
||||
: stack_trace_fmt_(stack_trace_fmt),
|
||||
frame_delimiter_(frame_delimiter),
|
||||
output_(output),
|
||||
dedup_token_(dedup_token),
|
||||
symbolize_(RenderNeedsSymbolization(stack_trace_fmt)) {}
|
||||
|
||||
bool ProcessAddressFrames(uptr pc) {
|
||||
SymbolizedStack *frames = symbolize_
|
||||
? Symbolizer::GetOrInit()->SymbolizePC(pc)
|
||||
: SymbolizedStack::New(pc);
|
||||
if (!frames)
|
||||
return false;
|
||||
|
||||
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
uptr prev_len = output_->length();
|
||||
RenderFrame(output_, stack_trace_fmt_, frame_num_++, cur->info.address,
|
||||
symbolize_ ? &cur->info : nullptr,
|
||||
common_flags()->symbolize_vs_style,
|
||||
common_flags()->strip_path_prefix);
|
||||
|
||||
if (prev_len != output_->length())
|
||||
output_->append("%c", frame_delimiter_);
|
||||
|
||||
ExtendDedupToken(cur);
|
||||
}
|
||||
frames->ClearAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Extend the dedup token by appending a new frame.
|
||||
void ExtendDedupToken(SymbolizedStack *stack) {
|
||||
if (!dedup_token_)
|
||||
return;
|
||||
|
||||
if (dedup_frames_-- > 0) {
|
||||
if (dedup_token_->length())
|
||||
dedup_token_->append("--");
|
||||
if (stack->info.function != nullptr)
|
||||
dedup_token_->append(stack->info.function);
|
||||
}
|
||||
}
|
||||
|
||||
const char *stack_trace_fmt_;
|
||||
const char frame_delimiter_;
|
||||
int dedup_frames_ = common_flags()->dedup_token_length;
|
||||
uptr frame_num_ = 0;
|
||||
InternalScopedString *output_;
|
||||
InternalScopedString *dedup_token_;
|
||||
const bool symbolize_ = false;
|
||||
};
|
||||
|
||||
static void CopyStringToBuffer(const InternalScopedString &str, char *out_buf,
|
||||
uptr out_buf_size) {
|
||||
if (!out_buf_size)
|
||||
return;
|
||||
|
||||
CHECK_GT(out_buf_size, 0);
|
||||
uptr copy_size = Min(str.length(), out_buf_size - 1);
|
||||
internal_memcpy(out_buf, str.data(), copy_size);
|
||||
out_buf[copy_size] = '\0';
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void StackTrace::PrintTo(InternalScopedString *output) const {
|
||||
CHECK(output);
|
||||
|
||||
InternalScopedString dedup_token;
|
||||
StackTraceTextPrinter printer(common_flags()->stack_trace_format, '\n',
|
||||
output, &dedup_token);
|
||||
|
||||
if (trace == nullptr || size == 0) {
|
||||
Printf(" <empty stack>\n\n");
|
||||
output->append(" <empty stack>\n\n");
|
||||
return;
|
||||
}
|
||||
InternalScopedString frame_desc;
|
||||
InternalScopedString dedup_token;
|
||||
int dedup_frames = common_flags()->dedup_token_length;
|
||||
bool symbolize = RenderNeedsSymbolization(common_flags()->stack_trace_format);
|
||||
uptr frame_num = 0;
|
||||
|
||||
for (uptr i = 0; i < size && trace[i]; i++) {
|
||||
// PCs in stack traces are actually the return addresses, that is,
|
||||
// addresses of the next instructions after the call.
|
||||
uptr pc = GetPreviousInstructionPc(trace[i]);
|
||||
SymbolizedStack *frames;
|
||||
if (symbolize)
|
||||
frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
|
||||
else
|
||||
frames = SymbolizedStack::New(pc);
|
||||
CHECK(frames);
|
||||
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
|
||||
frame_desc.clear();
|
||||
RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
|
||||
cur->info.address, symbolize ? &cur->info : nullptr,
|
||||
common_flags()->symbolize_vs_style,
|
||||
common_flags()->strip_path_prefix);
|
||||
Printf("%s\n", frame_desc.data());
|
||||
if (dedup_frames-- > 0) {
|
||||
if (dedup_token.length())
|
||||
dedup_token.append("--");
|
||||
if (cur->info.function != nullptr)
|
||||
dedup_token.append(cur->info.function);
|
||||
}
|
||||
}
|
||||
frames->ClearAll();
|
||||
CHECK(printer.ProcessAddressFrames(pc));
|
||||
}
|
||||
// Always print a trailing empty line after stack trace.
|
||||
Printf("\n");
|
||||
|
||||
// Always add a trailing empty line after stack trace.
|
||||
output->append("\n");
|
||||
|
||||
// Append deduplication token, if non-empty.
|
||||
if (dedup_token.length())
|
||||
Printf("DEDUP_TOKEN: %s\n", dedup_token.data());
|
||||
output->append("DEDUP_TOKEN: %s\n", dedup_token.data());
|
||||
}
|
||||
|
||||
uptr StackTrace::PrintTo(char *out_buf, uptr out_buf_size) const {
|
||||
CHECK(out_buf);
|
||||
|
||||
InternalScopedString output;
|
||||
PrintTo(&output);
|
||||
CopyStringToBuffer(output, out_buf, out_buf_size);
|
||||
|
||||
return output.length();
|
||||
}
|
||||
|
||||
void StackTrace::Print() const {
|
||||
InternalScopedString output;
|
||||
PrintTo(&output);
|
||||
Printf("%s", output.data());
|
||||
}
|
||||
|
||||
void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
|
||||
@ -82,12 +155,15 @@ void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
|
||||
UnwindSlow(pc, context, max_depth);
|
||||
else
|
||||
UnwindSlow(pc, max_depth);
|
||||
// If there are too few frames, the program may be built with
|
||||
// -fno-asynchronous-unwind-tables. Fall back to fast unwinder below.
|
||||
if (size > 2 || size >= max_depth)
|
||||
return;
|
||||
#else
|
||||
UNREACHABLE("slow unwind requested but not available");
|
||||
#endif
|
||||
} else {
|
||||
UnwindFast(pc, bp, stack_top, stack_bottom, max_depth);
|
||||
}
|
||||
UnwindFast(pc, bp, stack_top, stack_bottom, max_depth);
|
||||
}
|
||||
|
||||
static int GetModuleAndOffsetForPc(uptr pc, char *module_name,
|
||||
@ -112,41 +188,18 @@ extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
|
||||
uptr out_buf_size) {
|
||||
if (!out_buf_size) return;
|
||||
pc = StackTrace::GetPreviousInstructionPc(pc);
|
||||
SymbolizedStack *frame;
|
||||
bool symbolize = RenderNeedsSymbolization(fmt);
|
||||
if (symbolize)
|
||||
frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
|
||||
else
|
||||
frame = SymbolizedStack::New(pc);
|
||||
if (!frame) {
|
||||
internal_strncpy(out_buf, "<can't symbolize>", out_buf_size);
|
||||
out_buf[out_buf_size - 1] = 0;
|
||||
if (!out_buf_size)
|
||||
return;
|
||||
|
||||
pc = StackTrace::GetPreviousInstructionPc(pc);
|
||||
|
||||
InternalScopedString output;
|
||||
StackTraceTextPrinter printer(fmt, '\0', &output, nullptr);
|
||||
if (!printer.ProcessAddressFrames(pc)) {
|
||||
output.clear();
|
||||
output.append("<can't symbolize>");
|
||||
}
|
||||
InternalScopedString frame_desc;
|
||||
uptr frame_num = 0;
|
||||
// Reserve one byte for the final 0.
|
||||
char *out_end = out_buf + out_buf_size - 1;
|
||||
for (SymbolizedStack *cur = frame; cur && out_buf < out_end;
|
||||
cur = cur->next) {
|
||||
frame_desc.clear();
|
||||
RenderFrame(&frame_desc, fmt, frame_num++, cur->info.address,
|
||||
symbolize ? &cur->info : nullptr,
|
||||
common_flags()->symbolize_vs_style,
|
||||
common_flags()->strip_path_prefix);
|
||||
if (!frame_desc.length())
|
||||
continue;
|
||||
// Reserve one byte for the terminating 0.
|
||||
uptr n = out_end - out_buf - 1;
|
||||
internal_strncpy(out_buf, frame_desc.data(), n);
|
||||
out_buf += __sanitizer::Min<uptr>(n, frame_desc.length());
|
||||
*out_buf++ = 0;
|
||||
}
|
||||
CHECK(out_buf <= out_end);
|
||||
*out_buf = 0;
|
||||
frame->ClearAll();
|
||||
CopyStringToBuffer(output, out_buf, out_buf_size);
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
|
@ -16,14 +16,13 @@
|
||||
|
||||
#if SANITIZER_FUCHSIA
|
||||
#include "sanitizer_symbolizer_fuchsia.h"
|
||||
#elif SANITIZER_RTEMS
|
||||
#include "sanitizer_symbolizer_rtems.h"
|
||||
#endif
|
||||
#include "sanitizer_stacktrace.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
# endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <unwind.h>
|
||||
# include <limits.h>
|
||||
# include <unwind.h>
|
||||
|
||||
# include "sanitizer_stacktrace.h"
|
||||
# include "sanitizer_symbolizer.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
|
@ -120,7 +120,7 @@ void ReportMmapWriteExec(int prot) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS && !SANITIZER_GO
|
||||
#if !SANITIZER_FUCHSIA && !SANITIZER_GO
|
||||
void StartReportDeadlySignal() {
|
||||
// Write the first message using fd=2, just in case.
|
||||
// It may actually fail to write in case stderr is closed.
|
||||
@ -250,17 +250,17 @@ void HandleDeadlySignal(void *siginfo, void *context, u32 tid,
|
||||
|
||||
#endif // !SANITIZER_FUCHSIA && !SANITIZER_GO
|
||||
|
||||
static atomic_uintptr_t reporting_thread = {0};
|
||||
static StaticSpinMutex CommonSanitizerReportMutex;
|
||||
atomic_uintptr_t ScopedErrorReportLock::reporting_thread_ = {0};
|
||||
StaticSpinMutex ScopedErrorReportLock::mutex_;
|
||||
|
||||
ScopedErrorReportLock::ScopedErrorReportLock() {
|
||||
void ScopedErrorReportLock::Lock() {
|
||||
uptr current = GetThreadSelf();
|
||||
for (;;) {
|
||||
uptr expected = 0;
|
||||
if (atomic_compare_exchange_strong(&reporting_thread, &expected, current,
|
||||
if (atomic_compare_exchange_strong(&reporting_thread_, &expected, current,
|
||||
memory_order_relaxed)) {
|
||||
// We've claimed reporting_thread so proceed.
|
||||
CommonSanitizerReportMutex.Lock();
|
||||
mutex_.Lock();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -282,13 +282,11 @@ ScopedErrorReportLock::ScopedErrorReportLock() {
|
||||
}
|
||||
}
|
||||
|
||||
ScopedErrorReportLock::~ScopedErrorReportLock() {
|
||||
CommonSanitizerReportMutex.Unlock();
|
||||
atomic_store_relaxed(&reporting_thread, 0);
|
||||
void ScopedErrorReportLock::Unlock() {
|
||||
mutex_.Unlock();
|
||||
atomic_store_relaxed(&reporting_thread_, 0);
|
||||
}
|
||||
|
||||
void ScopedErrorReportLock::CheckLocked() {
|
||||
CommonSanitizerReportMutex.CheckLocked();
|
||||
}
|
||||
void ScopedErrorReportLock::CheckLocked() { mutex_.CheckLocked(); }
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
@ -1,40 +0,0 @@
|
||||
//===-- sanitizer_symbolizer_rtems.h -----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between various sanitizers' runtime libraries.
|
||||
//
|
||||
// Define RTEMS's string formats and limits for the markup symbolizer.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef SANITIZER_SYMBOLIZER_RTEMS_H
|
||||
#define SANITIZER_SYMBOLIZER_RTEMS_H
|
||||
|
||||
#include "sanitizer_internal_defs.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// The Myriad RTEMS symbolizer currently only parses backtrace lines,
|
||||
// so use a format that the symbolizer understands. For other
|
||||
// markups, keep them the same as the Fuchsia's.
|
||||
|
||||
// This is used by UBSan for type names, and by ASan for global variable names.
|
||||
constexpr const char *kFormatDemangle = "{{{symbol:%s}}}";
|
||||
constexpr uptr kFormatDemangleMax = 1024; // Arbitrary.
|
||||
|
||||
// Function name or equivalent from PC location.
|
||||
constexpr const char *kFormatFunction = "{{{pc:%p}}}";
|
||||
constexpr uptr kFormatFunctionMax = 64; // More than big enough for 64-bit hex.
|
||||
|
||||
// Global variable name or equivalent from data memory address.
|
||||
constexpr const char *kFormatData = "{{{data:%p}}}";
|
||||
|
||||
// One frame in a backtrace (printed on a line by itself).
|
||||
constexpr const char *kFormatFrame = " [%u] IP: %p";
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_SYMBOLIZER_RTEMS_H
|
@ -99,6 +99,9 @@ void ThreadContextBase::Reset() {
|
||||
|
||||
// ThreadRegistry implementation.
|
||||
|
||||
ThreadRegistry::ThreadRegistry(ThreadContextFactory factory)
|
||||
: ThreadRegistry(factory, UINT32_MAX, UINT32_MAX, 0) {}
|
||||
|
||||
ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
|
||||
u32 thread_quarantine_size, u32 max_reuse)
|
||||
: context_factory_(factory),
|
||||
@ -106,13 +109,10 @@ ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
|
||||
thread_quarantine_size_(thread_quarantine_size),
|
||||
max_reuse_(max_reuse),
|
||||
mtx_(),
|
||||
n_contexts_(0),
|
||||
total_threads_(0),
|
||||
alive_threads_(0),
|
||||
max_alive_threads_(0),
|
||||
running_threads_(0) {
|
||||
threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]),
|
||||
"ThreadRegistry");
|
||||
dead_threads_.clear();
|
||||
invalid_threads_.clear();
|
||||
}
|
||||
@ -120,7 +120,8 @@ ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
|
||||
void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,
|
||||
uptr *alive) {
|
||||
BlockingMutexLock l(&mtx_);
|
||||
if (total) *total = n_contexts_;
|
||||
if (total)
|
||||
*total = threads_.size();
|
||||
if (running) *running = running_threads_;
|
||||
if (alive) *alive = alive_threads_;
|
||||
}
|
||||
@ -137,11 +138,11 @@ u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
|
||||
ThreadContextBase *tctx = QuarantinePop();
|
||||
if (tctx) {
|
||||
tid = tctx->tid;
|
||||
} else if (n_contexts_ < max_threads_) {
|
||||
} else if (threads_.size() < max_threads_) {
|
||||
// Allocate new thread context and tid.
|
||||
tid = n_contexts_++;
|
||||
tid = threads_.size();
|
||||
tctx = context_factory_(tid);
|
||||
threads_[tid] = tctx;
|
||||
threads_.push_back(tctx);
|
||||
} else {
|
||||
#if !SANITIZER_GO
|
||||
Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
|
||||
@ -169,7 +170,7 @@ u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
|
||||
void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,
|
||||
void *arg) {
|
||||
CheckLocked();
|
||||
for (u32 tid = 0; tid < n_contexts_; tid++) {
|
||||
for (u32 tid = 0; tid < threads_.size(); tid++) {
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
if (tctx == 0)
|
||||
continue;
|
||||
@ -179,7 +180,7 @@ void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,
|
||||
|
||||
u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
|
||||
BlockingMutexLock l(&mtx_);
|
||||
for (u32 tid = 0; tid < n_contexts_; tid++) {
|
||||
for (u32 tid = 0; tid < threads_.size(); tid++) {
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
if (tctx != 0 && cb(tctx, arg))
|
||||
return tctx->tid;
|
||||
@ -190,7 +191,7 @@ u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
|
||||
ThreadContextBase *
|
||||
ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
|
||||
CheckLocked();
|
||||
for (u32 tid = 0; tid < n_contexts_; tid++) {
|
||||
for (u32 tid = 0; tid < threads_.size(); tid++) {
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
if (tctx != 0 && cb(tctx, arg))
|
||||
return tctx;
|
||||
@ -211,7 +212,6 @@ ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) {
|
||||
|
||||
void ThreadRegistry::SetThreadName(u32 tid, const char *name) {
|
||||
BlockingMutexLock l(&mtx_);
|
||||
CHECK_LT(tid, n_contexts_);
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
CHECK_NE(tctx, 0);
|
||||
CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning,
|
||||
@ -221,7 +221,7 @@ void ThreadRegistry::SetThreadName(u32 tid, const char *name) {
|
||||
|
||||
void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {
|
||||
BlockingMutexLock l(&mtx_);
|
||||
for (u32 tid = 0; tid < n_contexts_; tid++) {
|
||||
for (u32 tid = 0; tid < threads_.size(); tid++) {
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
if (tctx != 0 && tctx->user_id == user_id &&
|
||||
tctx->status != ThreadStatusInvalid) {
|
||||
@ -233,7 +233,6 @@ void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {
|
||||
|
||||
void ThreadRegistry::DetachThread(u32 tid, void *arg) {
|
||||
BlockingMutexLock l(&mtx_);
|
||||
CHECK_LT(tid, n_contexts_);
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
CHECK_NE(tctx, 0);
|
||||
if (tctx->status == ThreadStatusInvalid) {
|
||||
@ -254,7 +253,6 @@ void ThreadRegistry::JoinThread(u32 tid, void *arg) {
|
||||
do {
|
||||
{
|
||||
BlockingMutexLock l(&mtx_);
|
||||
CHECK_LT(tid, n_contexts_);
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
CHECK_NE(tctx, 0);
|
||||
if (tctx->status == ThreadStatusInvalid) {
|
||||
@ -280,7 +278,6 @@ ThreadStatus ThreadRegistry::FinishThread(u32 tid) {
|
||||
BlockingMutexLock l(&mtx_);
|
||||
CHECK_GT(alive_threads_, 0);
|
||||
alive_threads_--;
|
||||
CHECK_LT(tid, n_contexts_);
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
CHECK_NE(tctx, 0);
|
||||
bool dead = tctx->detached;
|
||||
@ -306,7 +303,6 @@ void ThreadRegistry::StartThread(u32 tid, tid_t os_id, ThreadType thread_type,
|
||||
void *arg) {
|
||||
BlockingMutexLock l(&mtx_);
|
||||
running_threads_++;
|
||||
CHECK_LT(tid, n_contexts_);
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
CHECK_NE(tctx, 0);
|
||||
CHECK_EQ(ThreadStatusCreated, tctx->status);
|
||||
@ -339,7 +335,6 @@ ThreadContextBase *ThreadRegistry::QuarantinePop() {
|
||||
|
||||
void ThreadRegistry::SetThreadUserId(u32 tid, uptr user_id) {
|
||||
BlockingMutexLock l(&mtx_);
|
||||
CHECK_LT(tid, n_contexts_);
|
||||
ThreadContextBase *tctx = threads_[tid];
|
||||
CHECK_NE(tctx, 0);
|
||||
CHECK_NE(tctx->status, ThreadStatusInvalid);
|
||||
|
@ -85,22 +85,22 @@ class ThreadContextBase {
|
||||
|
||||
typedef ThreadContextBase* (*ThreadContextFactory)(u32 tid);
|
||||
|
||||
class ThreadRegistry {
|
||||
class MUTEX ThreadRegistry {
|
||||
public:
|
||||
ThreadRegistry(ThreadContextFactory factory);
|
||||
ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
|
||||
u32 thread_quarantine_size, u32 max_reuse = 0);
|
||||
u32 thread_quarantine_size, u32 max_reuse);
|
||||
void GetNumberOfThreads(uptr *total = nullptr, uptr *running = nullptr,
|
||||
uptr *alive = nullptr);
|
||||
uptr GetMaxAliveThreads();
|
||||
|
||||
void Lock() { mtx_.Lock(); }
|
||||
void CheckLocked() { mtx_.CheckLocked(); }
|
||||
void Unlock() { mtx_.Unlock(); }
|
||||
void Lock() ACQUIRE() { mtx_.Lock(); }
|
||||
void CheckLocked() const CHECK_LOCKED() { mtx_.CheckLocked(); }
|
||||
void Unlock() RELEASE() { mtx_.Unlock(); }
|
||||
|
||||
// Should be guarded by ThreadRegistryLock.
|
||||
ThreadContextBase *GetThreadLocked(u32 tid) {
|
||||
DCHECK_LT(tid, n_contexts_);
|
||||
return threads_[tid];
|
||||
return threads_.empty() ? nullptr : threads_[tid];
|
||||
}
|
||||
|
||||
u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg);
|
||||
@ -137,15 +137,13 @@ class ThreadRegistry {
|
||||
|
||||
BlockingMutex mtx_;
|
||||
|
||||
u32 n_contexts_; // Number of created thread contexts,
|
||||
// at most max_threads_.
|
||||
u64 total_threads_; // Total number of created threads. May be greater than
|
||||
// max_threads_ if contexts were reused.
|
||||
uptr alive_threads_; // Created or running.
|
||||
uptr max_alive_threads_;
|
||||
uptr running_threads_;
|
||||
|
||||
ThreadContextBase **threads_; // Array of thread contexts is leaked.
|
||||
InternalMmapVector<ThreadContextBase *> threads_;
|
||||
IntrusiveList<ThreadContextBase> dead_threads_;
|
||||
IntrusiveList<ThreadContextBase> invalid_threads_;
|
||||
|
||||
|
42
libsanitizer/sanitizer_common/sanitizer_thread_safety.h
Normal file
42
libsanitizer/sanitizer_common/sanitizer_thread_safety.h
Normal file
@ -0,0 +1,42 @@
|
||||
//===-- sanitizer_thread_safety.h -------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is shared between sanitizer tools.
|
||||
//
|
||||
// Wrappers around thread safety annotations.
|
||||
// https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_THREAD_SAFETY_H
|
||||
#define SANITIZER_THREAD_SAFETY_H
|
||||
|
||||
#if defined(__clang__)
|
||||
# define THREAD_ANNOTATION(x) __attribute__((x))
|
||||
#else
|
||||
# define THREAD_ANNOTATION(x)
|
||||
#endif
|
||||
|
||||
#define MUTEX THREAD_ANNOTATION(capability("mutex"))
|
||||
#define SCOPED_LOCK THREAD_ANNOTATION(scoped_lockable)
|
||||
#define GUARDED_BY(x) THREAD_ANNOTATION(guarded_by(x))
|
||||
#define PT_GUARDED_BY(x) THREAD_ANNOTATION(pt_guarded_by(x))
|
||||
#define REQUIRES(...) THREAD_ANNOTATION(requires_capability(__VA_ARGS__))
|
||||
#define REQUIRES_SHARED(...) \
|
||||
THREAD_ANNOTATION(requires_shared_capability(__VA_ARGS__))
|
||||
#define ACQUIRE(...) THREAD_ANNOTATION(acquire_capability(__VA_ARGS__))
|
||||
#define ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION(acquire_shared_capability(__VA_ARGS__))
|
||||
#define TRY_ACQUIRE(...) THREAD_ANNOTATION(try_acquire_capability(__VA_ARGS__))
|
||||
#define RELEASE(...) THREAD_ANNOTATION(release_capability(__VA_ARGS__))
|
||||
#define RELEASE_SHARED(...) \
|
||||
THREAD_ANNOTATION(release_shared_capability(__VA_ARGS__))
|
||||
#define EXCLUDES(...) THREAD_ANNOTATION(locks_excluded(__VA_ARGS__))
|
||||
#define CHECK_LOCKED(...) THREAD_ANNOTATION(assert_capability(__VA_ARGS__))
|
||||
#define NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION(no_thread_safety_analysis)
|
||||
|
||||
#endif
|
@ -44,6 +44,9 @@ TRACELOGGING_DEFINE_PROVIDER(g_asan_provider, "AddressSanitizerLoggingProvider",
|
||||
#define TraceLoggingUnregister(x)
|
||||
#endif
|
||||
|
||||
// For WaitOnAddress
|
||||
# pragma comment(lib, "synchronization.lib")
|
||||
|
||||
// A macro to tell the compiler that this part of the code cannot be reached,
|
||||
// if the compiler supports this feature. Since we're using this in
|
||||
// code that is called when terminating the process, the expansion of the
|
||||
@ -541,13 +544,7 @@ bool IsAbsolutePath(const char *path) {
|
||||
IsPathSeparator(path[2]);
|
||||
}
|
||||
|
||||
void SleepForSeconds(int seconds) {
|
||||
Sleep(seconds * 1000);
|
||||
}
|
||||
|
||||
void SleepForMillis(int millis) {
|
||||
Sleep(millis);
|
||||
}
|
||||
void internal_usleep(u64 useconds) { Sleep(useconds / 1000); }
|
||||
|
||||
u64 NanoTime() {
|
||||
static LARGE_INTEGER frequency = {};
|
||||
@ -819,6 +816,17 @@ uptr GetRSS() {
|
||||
void *internal_start_thread(void *(*func)(void *arg), void *arg) { return 0; }
|
||||
void internal_join_thread(void *th) { }
|
||||
|
||||
void FutexWait(atomic_uint32_t *p, u32 cmp) {
|
||||
WaitOnAddress(p, &cmp, sizeof(cmp), INFINITE);
|
||||
}
|
||||
|
||||
void FutexWake(atomic_uint32_t *p, u32 count) {
|
||||
if (count == 1)
|
||||
WakeByAddressSingle(p);
|
||||
else
|
||||
WakeByAddressAll(p);
|
||||
}
|
||||
|
||||
// ---------------------- BlockingMutex ---------------- {{{1
|
||||
|
||||
BlockingMutex::BlockingMutex() {
|
||||
@ -838,9 +846,7 @@ void BlockingMutex::Unlock() {
|
||||
ReleaseSRWLockExclusive((PSRWLOCK)opaque_storage_);
|
||||
}
|
||||
|
||||
void BlockingMutex::CheckLocked() {
|
||||
CHECK_EQ(owner_, GetThreadSelf());
|
||||
}
|
||||
void BlockingMutex::CheckLocked() const { CHECK_EQ(owner_, GetThreadSelf()); }
|
||||
|
||||
uptr GetTlsSize() {
|
||||
return 0;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user