forked from OSchip/llvm-project
[lsan] Support LeakSanitizer runtime on Fuchsia
Support LeakSanitizer runtime on Fuchsia. Patch By: mcgrathr Differential Revision: https://reviews.llvm.org/D72887
This commit is contained in:
parent
b276dec5b6
commit
d59e3429f3
|
@ -640,7 +640,7 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_SUPPORTED_ARCH AND
|
if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_SUPPORTED_ARCH AND
|
||||||
OS_NAME MATCHES "Darwin|Linux|NetBSD")
|
OS_NAME MATCHES "Darwin|Linux|NetBSD|Fuchsia")
|
||||||
set(COMPILER_RT_HAS_LSAN TRUE)
|
set(COMPILER_RT_HAS_LSAN TRUE)
|
||||||
else()
|
else()
|
||||||
set(COMPILER_RT_HAS_LSAN FALSE)
|
set(COMPILER_RT_HAS_LSAN FALSE)
|
||||||
|
|
|
@ -480,6 +480,8 @@ bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches) {}
|
||||||
|
|
||||||
void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
|
void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
|
||||||
void *arg) {
|
void *arg) {
|
||||||
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
|
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
|
||||||
|
|
|
@ -5,6 +5,7 @@ append_rtti_flag(OFF LSAN_CFLAGS)
|
||||||
|
|
||||||
set(LSAN_COMMON_SOURCES
|
set(LSAN_COMMON_SOURCES
|
||||||
lsan_common.cpp
|
lsan_common.cpp
|
||||||
|
lsan_common_fuchsia.cpp
|
||||||
lsan_common_linux.cpp
|
lsan_common_linux.cpp
|
||||||
lsan_common_mac.cpp
|
lsan_common_mac.cpp
|
||||||
)
|
)
|
||||||
|
@ -12,6 +13,7 @@ set(LSAN_COMMON_SOURCES
|
||||||
set(LSAN_SOURCES
|
set(LSAN_SOURCES
|
||||||
lsan.cpp
|
lsan.cpp
|
||||||
lsan_allocator.cpp
|
lsan_allocator.cpp
|
||||||
|
lsan_fuchsia.cpp
|
||||||
lsan_interceptors.cpp
|
lsan_interceptors.cpp
|
||||||
lsan_linux.cpp
|
lsan_linux.cpp
|
||||||
lsan_mac.cpp
|
lsan_mac.cpp
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
#include "sanitizer_common/sanitizer_flags.h"
|
#include "sanitizer_common/sanitizer_flags.h"
|
||||||
#include "sanitizer_common/sanitizer_flag_parser.h"
|
#include "sanitizer_common/sanitizer_flag_parser.h"
|
||||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
|
||||||
#include "lsan_allocator.h"
|
#include "lsan_allocator.h"
|
||||||
#include "lsan_common.h"
|
#include "lsan_common.h"
|
||||||
#include "lsan_thread.h"
|
#include "lsan_thread.h"
|
||||||
|
@ -87,17 +86,6 @@ static void InitializeFlags() {
|
||||||
__sanitizer_set_report_path(common_flags()->log_path);
|
__sanitizer_set_report_path(common_flags()->log_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnStackUnwind(const SignalContext &sig, const void *,
|
|
||||||
BufferedStackTrace *stack) {
|
|
||||||
stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context,
|
|
||||||
common_flags()->fast_unwind_on_fatal);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void LsanOnDeadlySignal(int signo, void *siginfo, void *context) {
|
|
||||||
HandleDeadlySignal(siginfo, context, GetCurrentThread(), &OnStackUnwind,
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" void __lsan_init() {
|
extern "C" void __lsan_init() {
|
||||||
CHECK(!lsan_init_is_running);
|
CHECK(!lsan_init_is_running);
|
||||||
if (lsan_inited)
|
if (lsan_inited)
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "lsan_thread.h"
|
#include "lsan_thread.h"
|
||||||
#if SANITIZER_POSIX
|
#if SANITIZER_POSIX
|
||||||
#include "lsan_posix.h"
|
#include "lsan_posix.h"
|
||||||
|
#elif SANITIZER_FUCHSIA
|
||||||
|
#include "lsan_fuchsia.h"
|
||||||
#endif
|
#endif
|
||||||
#include "sanitizer_common/sanitizer_flags.h"
|
#include "sanitizer_common/sanitizer_flags.h"
|
||||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||||
|
@ -36,6 +38,7 @@ namespace __lsan {
|
||||||
|
|
||||||
void InitializeInterceptors();
|
void InitializeInterceptors();
|
||||||
void ReplaceSystemMalloc();
|
void ReplaceSystemMalloc();
|
||||||
|
void LsanOnDeadlySignal(int signo, void *siginfo, void *context);
|
||||||
|
|
||||||
#define ENSURE_LSAN_INITED do { \
|
#define ENSURE_LSAN_INITED do { \
|
||||||
CHECK(!lsan_init_is_running); \
|
CHECK(!lsan_init_is_running); \
|
||||||
|
|
|
@ -66,7 +66,10 @@ template <typename AddressSpaceView>
|
||||||
using PrimaryAllocatorASVT = SizeClassAllocator32<AP32<AddressSpaceView>>;
|
using PrimaryAllocatorASVT = SizeClassAllocator32<AP32<AddressSpaceView>>;
|
||||||
using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
|
using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
|
||||||
#elif defined(__x86_64__) || defined(__powerpc64__)
|
#elif defined(__x86_64__) || defined(__powerpc64__)
|
||||||
# if defined(__powerpc64__)
|
# if SANITIZER_FUCHSIA
|
||||||
|
const uptr kAllocatorSpace = ~(uptr)0;
|
||||||
|
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
|
||||||
|
# elif defined(__powerpc64__)
|
||||||
const uptr kAllocatorSpace = 0xa0000000000ULL;
|
const uptr kAllocatorSpace = 0xa0000000000ULL;
|
||||||
const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
|
const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
|
||||||
# else
|
# else
|
||||||
|
|
|
@ -211,6 +211,13 @@ void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) {
|
||||||
ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable);
|
ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SANITIZER_FUCHSIA
|
||||||
|
|
||||||
|
// Fuchsia handles all threads together with its own callback.
|
||||||
|
static void ProcessThreads(SuspendedThreadsList const &, Frontier *) {}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
// Scans thread data (stacks and TLS) for heap pointers.
|
// Scans thread data (stacks and TLS) for heap pointers.
|
||||||
static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
|
static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
|
||||||
Frontier *frontier) {
|
Frontier *frontier) {
|
||||||
|
@ -308,6 +315,8 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // SANITIZER_FUCHSIA
|
||||||
|
|
||||||
void ScanRootRegion(Frontier *frontier, const RootRegion &root_region,
|
void ScanRootRegion(Frontier *frontier, const RootRegion &root_region,
|
||||||
uptr region_begin, uptr region_end, bool is_readable) {
|
uptr region_begin, uptr region_end, bool is_readable) {
|
||||||
uptr intersection_begin = Max(root_region.begin, region_begin);
|
uptr intersection_begin = Max(root_region.begin, region_begin);
|
||||||
|
@ -531,6 +540,14 @@ static void ReportIfNotSuspended(ThreadContextBase *tctx, void *arg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if SANITIZER_FUCHSIA
|
||||||
|
|
||||||
|
// Fuchsia provides a libc interface that guarantees all threads are
|
||||||
|
// covered, and SuspendedThreadList is never really used.
|
||||||
|
static void ReportUnsuspendedThreads(const SuspendedThreadsList &) {}
|
||||||
|
|
||||||
|
#else // !SANITIZER_FUCHSIA
|
||||||
|
|
||||||
static void ReportUnsuspendedThreads(
|
static void ReportUnsuspendedThreads(
|
||||||
const SuspendedThreadsList &suspended_threads) {
|
const SuspendedThreadsList &suspended_threads) {
|
||||||
InternalMmapVector<tid_t> threads(suspended_threads.ThreadCount());
|
InternalMmapVector<tid_t> threads(suspended_threads.ThreadCount());
|
||||||
|
@ -543,6 +560,8 @@ static void ReportUnsuspendedThreads(
|
||||||
&ReportIfNotSuspended, &threads);
|
&ReportIfNotSuspended, &threads);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // !SANITIZER_FUCHSIA
|
||||||
|
|
||||||
static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
|
static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
|
||||||
void *arg) {
|
void *arg) {
|
||||||
CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
|
CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
#elif defined(__arm__) && \
|
#elif defined(__arm__) && \
|
||||||
SANITIZER_LINUX && !SANITIZER_ANDROID
|
SANITIZER_LINUX && !SANITIZER_ANDROID
|
||||||
#define CAN_SANITIZE_LEAKS 1
|
#define CAN_SANITIZE_LEAKS 1
|
||||||
#elif SANITIZER_NETBSD
|
#elif SANITIZER_NETBSD || SANITIZER_FUCHSIA
|
||||||
#define CAN_SANITIZE_LEAKS 1
|
#define CAN_SANITIZE_LEAKS 1
|
||||||
#else
|
#else
|
||||||
#define CAN_SANITIZE_LEAKS 0
|
#define CAN_SANITIZE_LEAKS 0
|
||||||
|
@ -223,6 +223,7 @@ ThreadRegistry *GetThreadRegistryLocked();
|
||||||
bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
|
bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
|
||||||
uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
|
uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
|
||||||
uptr *cache_end, DTLS **dtls);
|
uptr *cache_end, DTLS **dtls);
|
||||||
|
void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches);
|
||||||
void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
|
void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
|
||||||
void *arg);
|
void *arg);
|
||||||
// If called from the main thread, updates the main thread's TID in the thread
|
// If called from the main thread, updates the main thread's TID in the thread
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
//=-- lsan_common_fuchsia.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 LeakSanitizer.
|
||||||
|
// Implementation of common leak checking functionality. Fuchsia-specific code.
|
||||||
|
//
|
||||||
|
//===---------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "lsan_common.h"
|
||||||
|
#include "sanitizer_common/sanitizer_platform.h"
|
||||||
|
|
||||||
|
#if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA
|
||||||
|
#include <zircon/sanitizer.h>
|
||||||
|
|
||||||
|
#include "lsan_allocator.h"
|
||||||
|
#include "sanitizer_common/sanitizer_flags.h"
|
||||||
|
#include "sanitizer_common/sanitizer_thread_registry.h"
|
||||||
|
|
||||||
|
// Ensure that the Zircon system ABI is linked in.
|
||||||
|
#pragma comment(lib, "zircon")
|
||||||
|
|
||||||
|
namespace __lsan {
|
||||||
|
|
||||||
|
void InitializePlatformSpecificModules() {}
|
||||||
|
|
||||||
|
LoadedModule *GetLinker() { return nullptr; }
|
||||||
|
|
||||||
|
__attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter;
|
||||||
|
bool DisabledInThisThread() { return disable_counter > 0; }
|
||||||
|
void DisableInThisThread() { disable_counter++; }
|
||||||
|
void EnableInThisThread() {
|
||||||
|
if (disable_counter == 0) {
|
||||||
|
DisableCounterUnderflow();
|
||||||
|
}
|
||||||
|
disable_counter--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is nothing left to do after the globals callbacks.
|
||||||
|
void ProcessGlobalRegions(Frontier *frontier) {}
|
||||||
|
|
||||||
|
// Nothing to do here.
|
||||||
|
void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
|
||||||
|
|
||||||
|
// On Fuchsia, we can intercept _Exit gracefully, and return a failing exit
|
||||||
|
// code if required at that point. Calling Die() here is undefined
|
||||||
|
// behavior and causes rare race conditions.
|
||||||
|
void HandleLeaks() {}
|
||||||
|
|
||||||
|
int ExitHook(int status) {
|
||||||
|
return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
|
||||||
|
CheckForLeaksParam *argument) {
|
||||||
|
LockThreadRegistry();
|
||||||
|
LockAllocator();
|
||||||
|
|
||||||
|
struct Params {
|
||||||
|
InternalMmapVector<uptr> allocator_caches;
|
||||||
|
StopTheWorldCallback callback;
|
||||||
|
CheckForLeaksParam *argument;
|
||||||
|
} params = {{}, callback, argument};
|
||||||
|
|
||||||
|
// Callback from libc for globals (data/bss modulo relro), when enabled.
|
||||||
|
auto globals = +[](void *chunk, size_t size, void *data) {
|
||||||
|
auto params = static_cast<const Params *>(data);
|
||||||
|
uptr begin = reinterpret_cast<uptr>(chunk);
|
||||||
|
uptr end = begin + size;
|
||||||
|
ScanGlobalRange(begin, end, ¶ms->argument->frontier);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Callback from libc for thread stacks.
|
||||||
|
auto stacks = +[](void *chunk, size_t size, void *data) {
|
||||||
|
auto params = static_cast<const Params *>(data);
|
||||||
|
uptr begin = reinterpret_cast<uptr>(chunk);
|
||||||
|
uptr end = begin + size;
|
||||||
|
ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "STACK",
|
||||||
|
kReachable);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Callback from libc for thread registers.
|
||||||
|
auto registers = +[](void *chunk, size_t size, void *data) {
|
||||||
|
auto params = static_cast<const Params *>(data);
|
||||||
|
uptr begin = reinterpret_cast<uptr>(chunk);
|
||||||
|
uptr end = begin + size;
|
||||||
|
ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "REGISTERS",
|
||||||
|
kReachable);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (flags()->use_tls) {
|
||||||
|
// Collect the allocator cache range from each thread so these
|
||||||
|
// can all be excluded from the reported TLS ranges.
|
||||||
|
GetAllThreadAllocatorCachesLocked(¶ms.allocator_caches);
|
||||||
|
__sanitizer::Sort(params.allocator_caches.data(),
|
||||||
|
params.allocator_caches.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback from libc for TLS regions. This includes thread_local
|
||||||
|
// variables as well as C11 tss_set and POSIX pthread_setspecific.
|
||||||
|
auto tls = +[](void *chunk, size_t size, void *data) {
|
||||||
|
auto params = static_cast<const Params *>(data);
|
||||||
|
uptr begin = reinterpret_cast<uptr>(chunk);
|
||||||
|
uptr end = begin + size;
|
||||||
|
auto i = __sanitizer::InternalLowerBound(params->allocator_caches, 0,
|
||||||
|
params->allocator_caches.size(),
|
||||||
|
begin, CompareLess<uptr>());
|
||||||
|
if (i < params->allocator_caches.size() &&
|
||||||
|
params->allocator_caches[i] >= begin &&
|
||||||
|
end - params->allocator_caches[i] <= sizeof(AllocatorCache)) {
|
||||||
|
// Split the range in two and omit the allocator cache within.
|
||||||
|
ScanRangeForPointers(begin, params->allocator_caches[i],
|
||||||
|
¶ms->argument->frontier, "TLS", kReachable);
|
||||||
|
uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache);
|
||||||
|
ScanRangeForPointers(begin2, end, ¶ms->argument->frontier, "TLS",
|
||||||
|
kReachable);
|
||||||
|
} else {
|
||||||
|
ScanRangeForPointers(begin, end, ¶ms->argument->frontier, "TLS",
|
||||||
|
kReachable);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This stops the world and then makes callbacks for various memory regions.
|
||||||
|
// The final callback is the last thing before the world starts up again.
|
||||||
|
__sanitizer_memory_snapshot(
|
||||||
|
flags()->use_globals ? globals : nullptr,
|
||||||
|
flags()->use_stacks ? stacks : nullptr,
|
||||||
|
flags()->use_registers ? registers : nullptr,
|
||||||
|
flags()->use_tls ? tls : nullptr,
|
||||||
|
[](zx_status_t, void *data) {
|
||||||
|
auto params = static_cast<const Params *>(data);
|
||||||
|
|
||||||
|
// We don't use the thread registry at all for enumerating the threads
|
||||||
|
// and their stacks, registers, and TLS regions. So use it separately
|
||||||
|
// just for the allocator cache, and to call ForEachExtraStackRange,
|
||||||
|
// which ASan needs.
|
||||||
|
if (flags()->use_stacks) {
|
||||||
|
GetThreadRegistryLocked()->RunCallbackForEachThreadLocked(
|
||||||
|
[](ThreadContextBase *tctx, void *arg) {
|
||||||
|
ForEachExtraStackRange(tctx->os_id, ForEachExtraStackRangeCb,
|
||||||
|
arg);
|
||||||
|
},
|
||||||
|
¶ms->argument->frontier);
|
||||||
|
}
|
||||||
|
|
||||||
|
params->callback({}, params->argument);
|
||||||
|
},
|
||||||
|
¶ms);
|
||||||
|
|
||||||
|
UnlockAllocator();
|
||||||
|
UnlockThreadRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __lsan
|
||||||
|
|
||||||
|
// This is declared (in extern "C") by <zircon/sanitizer.h>.
|
||||||
|
// _Exit calls this directly to intercept and change the status value.
|
||||||
|
int __sanitizer_process_exit_hook(int status) {
|
||||||
|
return __lsan::ExitHook(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,123 @@
|
||||||
|
//=-- lsan_fuchsia.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 LeakSanitizer.
|
||||||
|
// Standalone LSan RTL code specific to Fuchsia.
|
||||||
|
//
|
||||||
|
//===---------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "sanitizer_common/sanitizer_platform.h"
|
||||||
|
|
||||||
|
#if SANITIZER_FUCHSIA
|
||||||
|
#include <zircon/sanitizer.h>
|
||||||
|
|
||||||
|
#include "lsan.h"
|
||||||
|
#include "lsan_allocator.h"
|
||||||
|
|
||||||
|
using namespace __lsan;
|
||||||
|
|
||||||
|
namespace __lsan {
|
||||||
|
|
||||||
|
void LsanOnDeadlySignal(int signo, void *siginfo, void *context) {}
|
||||||
|
|
||||||
|
ThreadContext::ThreadContext(int tid) : ThreadContextLsanBase(tid) {}
|
||||||
|
|
||||||
|
struct OnCreatedArgs {
|
||||||
|
uptr stack_begin, stack_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
// On Fuchsia, the stack bounds of a new thread are available before
|
||||||
|
// the thread itself has started running.
|
||||||
|
void ThreadContext::OnCreated(void *arg) {
|
||||||
|
// Stack bounds passed through from __sanitizer_before_thread_create_hook
|
||||||
|
// or InitializeMainThread.
|
||||||
|
auto args = reinterpret_cast<const OnCreatedArgs *>(arg);
|
||||||
|
stack_begin_ = args->stack_begin;
|
||||||
|
stack_end_ = args->stack_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OnStartedArgs {
|
||||||
|
uptr cache_begin, cache_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ThreadContext::OnStarted(void *arg) {
|
||||||
|
auto args = reinterpret_cast<const OnStartedArgs *>(arg);
|
||||||
|
cache_begin_ = args->cache_begin;
|
||||||
|
cache_end_ = args->cache_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadStart(u32 tid) {
|
||||||
|
OnStartedArgs args;
|
||||||
|
GetAllocatorCacheRange(&args.cache_begin, &args.cache_end);
|
||||||
|
CHECK_EQ(args.cache_end - args.cache_begin, sizeof(AllocatorCache));
|
||||||
|
ThreadContextLsanBase::ThreadStart(tid, GetTid(), ThreadType::Regular, &args);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeMainThread() {
|
||||||
|
OnCreatedArgs args;
|
||||||
|
__sanitizer::GetThreadStackTopAndBottom(true, &args.stack_end,
|
||||||
|
&args.stack_begin);
|
||||||
|
u32 tid = ThreadCreate(0, GetThreadSelf(), true, &args);
|
||||||
|
CHECK_EQ(tid, 0);
|
||||||
|
ThreadStart(tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetAllThreadAllocatorCachesLocked(InternalMmapVector<uptr> *caches) {
|
||||||
|
GetThreadRegistryLocked()->RunCallbackForEachThreadLocked(
|
||||||
|
[](ThreadContextBase *tctx, void *arg) {
|
||||||
|
auto ctx = static_cast<ThreadContext *>(tctx);
|
||||||
|
static_cast<decltype(caches)>(arg)->push_back(ctx->cache_begin());
|
||||||
|
},
|
||||||
|
caches);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __lsan
|
||||||
|
|
||||||
|
// These are declared (in extern "C") by <zircon/sanitizer.h>.
|
||||||
|
// The system runtime will call our definitions directly.
|
||||||
|
|
||||||
|
// This is called before each thread creation is attempted. So, in
|
||||||
|
// its first call, the calling thread is the initial and sole thread.
|
||||||
|
void *__sanitizer_before_thread_create_hook(thrd_t thread, bool detached,
|
||||||
|
const char *name, void *stack_base,
|
||||||
|
size_t stack_size) {
|
||||||
|
uptr user_id = reinterpret_cast<uptr>(thread);
|
||||||
|
ENSURE_LSAN_INITED;
|
||||||
|
EnsureMainThreadIDIsCorrect();
|
||||||
|
OnCreatedArgs args;
|
||||||
|
args.stack_begin = reinterpret_cast<uptr>(stack_base);
|
||||||
|
args.stack_end = args.stack_begin + stack_size;
|
||||||
|
u32 parent_tid = GetCurrentThread();
|
||||||
|
u32 tid = ThreadCreate(parent_tid, user_id, detached, &args);
|
||||||
|
return reinterpret_cast<void *>(static_cast<uptr>(tid));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called after creating a new thread (in the creating thread),
|
||||||
|
// with the pointer returned by __sanitizer_before_thread_create_hook (above).
|
||||||
|
void __sanitizer_thread_create_hook(void *hook, thrd_t thread, int error) {
|
||||||
|
u32 tid = static_cast<u32>(reinterpret_cast<uptr>(hook));
|
||||||
|
// On success, there is nothing to do here.
|
||||||
|
if (error != thrd_success) {
|
||||||
|
// Clean up the thread registry for the thread creation that didn't happen.
|
||||||
|
GetThreadRegistryLocked()->FinishThread(tid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called in the newly-created thread before it runs anything else,
|
||||||
|
// with the pointer returned by __sanitizer_before_thread_create_hook (above).
|
||||||
|
void __sanitizer_thread_start_hook(void *hook, thrd_t self) {
|
||||||
|
u32 tid = static_cast<u32>(reinterpret_cast<uptr>(hook));
|
||||||
|
ThreadStart(tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each thread runs this just before it exits,
|
||||||
|
// with the pointer returned by BeforeThreadCreateHook (above).
|
||||||
|
// All per-thread destructors have already been called.
|
||||||
|
void __sanitizer_thread_exit_hook(void *hook, thrd_t self) { ThreadFinish(); }
|
||||||
|
|
||||||
|
#endif // SANITIZER_FUCHSIA
|
|
@ -0,0 +1,35 @@
|
||||||
|
//=-- lsan_fuchsia.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 a part of LeakSanitizer.
|
||||||
|
// Standalone LSan RTL code specific to Fuchsia.
|
||||||
|
//
|
||||||
|
//===---------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LSAN_FUCHSIA_H
|
||||||
|
#define LSAN_FUCHSIA_H
|
||||||
|
|
||||||
|
#include "lsan_thread.h"
|
||||||
|
#include "sanitizer_common/sanitizer_platform.h"
|
||||||
|
|
||||||
|
#if !SANITIZER_FUCHSIA
|
||||||
|
#error "lsan_fuchsia.h is used only on Fuchsia systems (SANITIZER_FUCHSIA)"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace __lsan {
|
||||||
|
|
||||||
|
class ThreadContext : public ThreadContextLsanBase {
|
||||||
|
public:
|
||||||
|
explicit ThreadContext(int tid);
|
||||||
|
void OnCreated(void *arg) override;
|
||||||
|
void OnStarted(void *arg) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace __lsan
|
||||||
|
|
||||||
|
#endif // LSAN_FUCHSIA_H
|
|
@ -63,6 +63,9 @@ INTERCEPTOR(void, free, void *p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
|
INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
|
||||||
|
// This hack is not required for Fuchsia because there are no dlsym calls
|
||||||
|
// involved in setting up interceptors.
|
||||||
|
#if !SANITIZER_FUCHSIA
|
||||||
if (lsan_init_is_running) {
|
if (lsan_init_is_running) {
|
||||||
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
||||||
const uptr kCallocPoolSize = 1024;
|
const uptr kCallocPoolSize = 1024;
|
||||||
|
@ -74,6 +77,7 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
|
||||||
CHECK(allocated < kCallocPoolSize);
|
CHECK(allocated < kCallocPoolSize);
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
#endif // !SANITIZER_FUCHSIA
|
||||||
ENSURE_LSAN_INITED;
|
ENSURE_LSAN_INITED;
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return lsan_calloc(nmemb, size, stack);
|
return lsan_calloc(nmemb, size, stack);
|
||||||
|
@ -102,7 +106,7 @@ INTERCEPTOR(void*, valloc, uptr size) {
|
||||||
GET_STACK_TRACE_MALLOC;
|
GET_STACK_TRACE_MALLOC;
|
||||||
return lsan_valloc(size, stack);
|
return lsan_valloc(size, stack);
|
||||||
}
|
}
|
||||||
#endif
|
#endif // !SANITIZER_MAC
|
||||||
|
|
||||||
#if SANITIZER_INTERCEPT_MEMALIGN
|
#if SANITIZER_INTERCEPT_MEMALIGN
|
||||||
INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
|
INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
|
||||||
|
@ -309,7 +313,7 @@ INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&)
|
||||||
|
|
||||||
///// Thread initialization and finalization. /////
|
///// Thread initialization and finalization. /////
|
||||||
|
|
||||||
#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD
|
#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD && !SANITIZER_FUCHSIA
|
||||||
static unsigned g_thread_finalize_key;
|
static unsigned g_thread_finalize_key;
|
||||||
|
|
||||||
static void thread_finalize(void *v) {
|
static void thread_finalize(void *v) {
|
||||||
|
@ -396,6 +400,8 @@ INTERCEPTOR(char *, strerror, int errnum) {
|
||||||
#define LSAN_MAYBE_INTERCEPT_STRERROR
|
#define LSAN_MAYBE_INTERCEPT_STRERROR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if SANITIZER_POSIX
|
||||||
|
|
||||||
struct ThreadParam {
|
struct ThreadParam {
|
||||||
void *(*callback)(void *arg);
|
void *(*callback)(void *arg);
|
||||||
void *param;
|
void *param;
|
||||||
|
@ -478,9 +484,13 @@ INTERCEPTOR(void, _exit, int status) {
|
||||||
#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
|
#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
|
||||||
#include "sanitizer_common/sanitizer_signal_interceptors.inc"
|
#include "sanitizer_common/sanitizer_signal_interceptors.inc"
|
||||||
|
|
||||||
|
#endif // SANITIZER_POSIX
|
||||||
|
|
||||||
namespace __lsan {
|
namespace __lsan {
|
||||||
|
|
||||||
void InitializeInterceptors() {
|
void InitializeInterceptors() {
|
||||||
|
// Fuchsia doesn't use interceptors that require any setup.
|
||||||
|
#if !SANITIZER_FUCHSIA
|
||||||
InitializeSignalInterceptors();
|
InitializeSignalInterceptors();
|
||||||
|
|
||||||
INTERCEPT_FUNCTION(malloc);
|
INTERCEPT_FUNCTION(malloc);
|
||||||
|
@ -516,6 +526,8 @@ void InitializeInterceptors() {
|
||||||
Die();
|
Die();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif // !SANITIZER_FUCHSIA
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace __lsan
|
} // namespace __lsan
|
||||||
|
|
|
@ -6,13 +6,13 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This file is a part of LeakSanitizer. Linux/NetBSD-specific code.
|
// This file is a part of LeakSanitizer. Linux/NetBSD/Fuchsia-specific code.
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "sanitizer_common/sanitizer_platform.h"
|
#include "sanitizer_common/sanitizer_platform.h"
|
||||||
|
|
||||||
#if SANITIZER_LINUX || SANITIZER_NETBSD
|
#if SANITIZER_LINUX || SANITIZER_NETBSD || SANITIZER_FUCHSIA
|
||||||
|
|
||||||
#include "lsan_allocator.h"
|
#include "lsan_allocator.h"
|
||||||
|
|
||||||
|
@ -29,4 +29,4 @@ void ReplaceSystemMalloc() {}
|
||||||
|
|
||||||
} // namespace __lsan
|
} // namespace __lsan
|
||||||
|
|
||||||
#endif // SANITIZER_LINUX || SANITIZER_NETBSD
|
#endif // SANITIZER_LINUX || SANITIZER_NETBSD || SANITIZER_FUCHSIA
|
||||||
|
|
|
@ -35,6 +35,7 @@ set(SANITIZER_SOURCES_NOTERMINATION
|
||||||
sanitizer_procmaps_solaris.cpp
|
sanitizer_procmaps_solaris.cpp
|
||||||
sanitizer_rtems.cpp
|
sanitizer_rtems.cpp
|
||||||
sanitizer_solaris.cpp
|
sanitizer_solaris.cpp
|
||||||
|
sanitizer_stoptheworld_fuchsia.cpp
|
||||||
sanitizer_stoptheworld_mac.cpp
|
sanitizer_stoptheworld_mac.cpp
|
||||||
sanitizer_suppressions.cpp
|
sanitizer_suppressions.cpp
|
||||||
sanitizer_tls_get_addr.cpp
|
sanitizer_tls_get_addr.cpp
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
//===-- sanitizer_stoptheworld_fuchsia.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
|
||||||
|
//
|
||||||
|
//===---------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// See sanitizer_stoptheworld.h for details.
|
||||||
|
//
|
||||||
|
//===---------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "sanitizer_platform.h"
|
||||||
|
|
||||||
|
#if SANITIZER_FUCHSIA
|
||||||
|
|
||||||
|
#include <zircon/sanitizer.h>
|
||||||
|
|
||||||
|
#include "sanitizer_stoptheworld.h"
|
||||||
|
|
||||||
|
namespace __sanitizer {
|
||||||
|
|
||||||
|
// The Fuchsia implementation stops the world but doesn't offer a real
|
||||||
|
// SuspendedThreadsList argument. This is enough for ASan's use case,
|
||||||
|
// and LSan does not use this API on Fuchsia.
|
||||||
|
void StopTheWorld(StopTheWorldCallback callback, void *argument) {
|
||||||
|
struct Params {
|
||||||
|
StopTheWorldCallback callback;
|
||||||
|
void *argument;
|
||||||
|
} params = {callback, argument};
|
||||||
|
__sanitizer_memory_snapshot(
|
||||||
|
nullptr, nullptr, nullptr, nullptr,
|
||||||
|
[](zx_status_t, void *data) {
|
||||||
|
auto params = reinterpret_cast<Params *>(data);
|
||||||
|
params->callback({}, params->argument);
|
||||||
|
},
|
||||||
|
¶ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace __sanitizer
|
||||||
|
|
||||||
|
#endif // SANITIZER_FUCHSIA
|
Loading…
Reference in New Issue