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