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