1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
//=-- 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_stoptheworld_fuchsia.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() {}
// This is defined differently in asan_fuchsia.cpp and lsan_fuchsia.cpp.
bool UseExitcodeOnLeak();
int ExitHook(int status) {
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
if (UseExitcodeOnLeak())
DoLeakCheck();
else
DoRecoverableLeakCheckVoid();
}
return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status;
}
void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
CheckForLeaksParam *argument) {
ScopedStopTheWorldLock lock;
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, begin);
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(SuspendedThreadsListFuchsia(), params->argument);
},
¶ms);
}
} // 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
|