1 //=-- lsan_common_fuchsia.cpp --------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===---------------------------------------------------------------------===//
9 // This file is a part of LeakSanitizer.
10 // Implementation of common leak checking functionality. Fuchsia-specific code.
12 //===---------------------------------------------------------------------===//
14 #include "lsan_common.h"
15 #include "sanitizer_common/sanitizer_platform.h"
17 #if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA
18 #include <zircon/sanitizer.h>
20 #include "lsan_allocator.h"
21 #include "sanitizer_common/sanitizer_flags.h"
22 #include "sanitizer_common/sanitizer_thread_registry.h"
24 // Ensure that the Zircon system ABI is linked in.
25 #pragma comment(lib, "zircon")
29 void InitializePlatformSpecificModules() {}
31 LoadedModule
*GetLinker() { return nullptr; }
33 __attribute__((tls_model("initial-exec"))) THREADLOCAL
int disable_counter
;
34 bool DisabledInThisThread() { return disable_counter
> 0; }
35 void DisableInThisThread() { disable_counter
++; }
36 void EnableInThisThread() {
37 if (disable_counter
== 0) {
38 DisableCounterUnderflow();
43 // There is nothing left to do after the globals callbacks.
44 void ProcessGlobalRegions(Frontier
*frontier
) {}
46 // Nothing to do here.
47 void ProcessPlatformSpecificAllocations(Frontier
*frontier
) {}
49 // On Fuchsia, we can intercept _Exit gracefully, and return a failing exit
50 // code if required at that point. Calling Die() here is undefined
51 // behavior and causes rare race conditions.
54 int ExitHook(int status
) {
55 return status
== 0 && HasReportedLeaks() ? common_flags()->exitcode
: status
;
58 void LockStuffAndStopTheWorld(StopTheWorldCallback callback
,
59 CheckForLeaksParam
*argument
) {
64 InternalMmapVector
<uptr
> allocator_caches
;
65 StopTheWorldCallback callback
;
66 CheckForLeaksParam
*argument
;
67 } params
= {{}, callback
, argument
};
69 // Callback from libc for globals (data/bss modulo relro), when enabled.
70 auto globals
= +[](void *chunk
, size_t size
, void *data
) {
71 auto params
= static_cast<const Params
*>(data
);
72 uptr begin
= reinterpret_cast<uptr
>(chunk
);
73 uptr end
= begin
+ size
;
74 ScanGlobalRange(begin
, end
, ¶ms
->argument
->frontier
);
77 // Callback from libc for thread stacks.
78 auto stacks
= +[](void *chunk
, size_t size
, void *data
) {
79 auto params
= static_cast<const Params
*>(data
);
80 uptr begin
= reinterpret_cast<uptr
>(chunk
);
81 uptr end
= begin
+ size
;
82 ScanRangeForPointers(begin
, end
, ¶ms
->argument
->frontier
, "STACK",
86 // Callback from libc for thread registers.
87 auto registers
= +[](void *chunk
, size_t size
, void *data
) {
88 auto params
= static_cast<const Params
*>(data
);
89 uptr begin
= reinterpret_cast<uptr
>(chunk
);
90 uptr end
= begin
+ size
;
91 ScanRangeForPointers(begin
, end
, ¶ms
->argument
->frontier
, "REGISTERS",
95 if (flags()->use_tls
) {
96 // Collect the allocator cache range from each thread so these
97 // can all be excluded from the reported TLS ranges.
98 GetAllThreadAllocatorCachesLocked(¶ms
.allocator_caches
);
99 __sanitizer::Sort(params
.allocator_caches
.data(),
100 params
.allocator_caches
.size());
103 // Callback from libc for TLS regions. This includes thread_local
104 // variables as well as C11 tss_set and POSIX pthread_setspecific.
105 auto tls
= +[](void *chunk
, size_t size
, void *data
) {
106 auto params
= static_cast<const Params
*>(data
);
107 uptr begin
= reinterpret_cast<uptr
>(chunk
);
108 uptr end
= begin
+ size
;
109 auto i
= __sanitizer::InternalLowerBound(params
->allocator_caches
, 0,
110 params
->allocator_caches
.size(),
111 begin
, CompareLess
<uptr
>());
112 if (i
< params
->allocator_caches
.size() &&
113 params
->allocator_caches
[i
] >= begin
&&
114 end
- params
->allocator_caches
[i
] <= sizeof(AllocatorCache
)) {
115 // Split the range in two and omit the allocator cache within.
116 ScanRangeForPointers(begin
, params
->allocator_caches
[i
],
117 ¶ms
->argument
->frontier
, "TLS", kReachable
);
118 uptr begin2
= params
->allocator_caches
[i
] + sizeof(AllocatorCache
);
119 ScanRangeForPointers(begin2
, end
, ¶ms
->argument
->frontier
, "TLS",
122 ScanRangeForPointers(begin
, end
, ¶ms
->argument
->frontier
, "TLS",
127 // This stops the world and then makes callbacks for various memory regions.
128 // The final callback is the last thing before the world starts up again.
129 __sanitizer_memory_snapshot(
130 flags()->use_globals
? globals
: nullptr,
131 flags()->use_stacks
? stacks
: nullptr,
132 flags()->use_registers
? registers
: nullptr,
133 flags()->use_tls
? tls
: nullptr,
134 [](zx_status_t
, void *data
) {
135 auto params
= static_cast<const Params
*>(data
);
137 // We don't use the thread registry at all for enumerating the threads
138 // and their stacks, registers, and TLS regions. So use it separately
139 // just for the allocator cache, and to call ForEachExtraStackRange,
141 if (flags()->use_stacks
) {
142 GetThreadRegistryLocked()->RunCallbackForEachThreadLocked(
143 [](ThreadContextBase
*tctx
, void *arg
) {
144 ForEachExtraStackRange(tctx
->os_id
, ForEachExtraStackRangeCb
,
147 ¶ms
->argument
->frontier
);
150 params
->callback({}, params
->argument
);
155 UnlockThreadRegistry();
158 } // namespace __lsan
160 // This is declared (in extern "C") by <zircon/sanitizer.h>.
161 // _Exit calls this directly to intercept and change the status value.
162 int __sanitizer_process_exit_hook(int status
) {
163 return __lsan::ExitHook(status
);