1 | //===-- asan_posix.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 AddressSanitizer, an address sanity checker. |
10 | // |
11 | // Posix-specific details. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "sanitizer_common/sanitizer_platform.h" |
15 | #if SANITIZER_POSIX |
16 | |
17 | # include <pthread.h> |
18 | # include <signal.h> |
19 | # include <stdlib.h> |
20 | # include <sys/resource.h> |
21 | # include <sys/time.h> |
22 | # include <unistd.h> |
23 | |
24 | # include "asan_interceptors.h" |
25 | # include "asan_internal.h" |
26 | # include "asan_mapping.h" |
27 | # include "asan_poisoning.h" |
28 | # include "asan_report.h" |
29 | # include "asan_stack.h" |
30 | # include "lsan/lsan_common.h" |
31 | # include "sanitizer_common/sanitizer_libc.h" |
32 | # include "sanitizer_common/sanitizer_posix.h" |
33 | # include "sanitizer_common/sanitizer_procmaps.h" |
34 | |
35 | namespace __asan { |
36 | |
37 | void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { |
38 | StartReportDeadlySignal(); |
39 | SignalContext sig(siginfo, context); |
40 | ReportDeadlySignal(sig); |
41 | } |
42 | |
43 | bool PlatformUnpoisonStacks() { |
44 | stack_t signal_stack; |
45 | CHECK_EQ(0, sigaltstack(nullptr, &signal_stack)); |
46 | uptr sigalt_bottom = (uptr)signal_stack.ss_sp; |
47 | uptr sigalt_top = (uptr)((char *)signal_stack.ss_sp + signal_stack.ss_size); |
48 | // If we're executing on the signal alternate stack AND the Linux flag |
49 | // SS_AUTODISARM was used, then we cannot get the signal alternate stack |
50 | // bounds from sigaltstack -- sigaltstack's output looks just as if no |
51 | // alternate stack has ever been set up. |
52 | // We're always unpoisoning the signal alternate stack to support jumping |
53 | // between the default stack and signal alternate stack. |
54 | if (signal_stack.ss_flags != SS_DISABLE) |
55 | UnpoisonStack(bottom: sigalt_bottom, top: sigalt_top, type: "sigalt" ); |
56 | |
57 | if (signal_stack.ss_flags != SS_ONSTACK) |
58 | return false; |
59 | |
60 | // Since we're on the signal alternate stack, we cannot find the DEFAULT |
61 | // stack bottom using a local variable. |
62 | uptr default_bottom, tls_addr, tls_size, stack_size; |
63 | GetThreadStackAndTls(/*main=*/false, stk_addr: &default_bottom, stk_size: &stack_size, tls_addr: &tls_addr, |
64 | tls_size: &tls_size); |
65 | UnpoisonStack(bottom: default_bottom, top: default_bottom + stack_size, type: "default" ); |
66 | return true; |
67 | } |
68 | |
69 | // ---------------------- TSD ---------------- {{{1 |
70 | |
71 | #if SANITIZER_NETBSD && !ASAN_DYNAMIC |
72 | // Thread Static Data cannot be used in early static ASan init on NetBSD. |
73 | // Reuse the Asan TSD API for compatibility with existing code |
74 | // with an alternative implementation. |
75 | |
76 | static void (*tsd_destructor)(void *tsd) = nullptr; |
77 | |
78 | struct tsd_key { |
79 | tsd_key() : key(nullptr) {} |
80 | ~tsd_key() { |
81 | CHECK(tsd_destructor); |
82 | if (key) |
83 | (*tsd_destructor)(key); |
84 | } |
85 | void *key; |
86 | }; |
87 | |
88 | static thread_local struct tsd_key key; |
89 | |
90 | void AsanTSDInit(void (*destructor)(void *tsd)) { |
91 | CHECK(!tsd_destructor); |
92 | tsd_destructor = destructor; |
93 | } |
94 | |
95 | void *AsanTSDGet() { |
96 | CHECK(tsd_destructor); |
97 | return key.key; |
98 | } |
99 | |
100 | void AsanTSDSet(void *tsd) { |
101 | CHECK(tsd_destructor); |
102 | CHECK(tsd); |
103 | CHECK(!key.key); |
104 | key.key = tsd; |
105 | } |
106 | |
107 | void PlatformTSDDtor(void *tsd) { |
108 | CHECK(tsd_destructor); |
109 | CHECK_EQ(key.key, tsd); |
110 | key.key = nullptr; |
111 | // Make sure that signal handler can not see a stale current thread pointer. |
112 | atomic_signal_fence(memory_order_seq_cst); |
113 | AsanThread::TSDDtor(tsd); |
114 | } |
115 | #else |
116 | static pthread_key_t tsd_key; |
117 | static bool tsd_key_inited = false; |
118 | void AsanTSDInit(void (*destructor)(void *tsd)) { |
119 | CHECK(!tsd_key_inited); |
120 | tsd_key_inited = true; |
121 | CHECK_EQ(0, pthread_key_create(&tsd_key, destructor)); |
122 | } |
123 | |
124 | void *AsanTSDGet() { |
125 | CHECK(tsd_key_inited); |
126 | return pthread_getspecific(key: tsd_key); |
127 | } |
128 | |
129 | void AsanTSDSet(void *tsd) { |
130 | CHECK(tsd_key_inited); |
131 | pthread_setspecific(key: tsd_key, pointer: tsd); |
132 | } |
133 | |
134 | void PlatformTSDDtor(void *tsd) { |
135 | AsanThreadContext *context = (AsanThreadContext *)tsd; |
136 | if (context->destructor_iterations > 1) { |
137 | context->destructor_iterations--; |
138 | CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); |
139 | return; |
140 | } |
141 | # if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ |
142 | SANITIZER_SOLARIS |
143 | // After this point it's unsafe to execute signal handlers which may be |
144 | // instrumented. It's probably not just a Linux issue. |
145 | BlockSignals(); |
146 | # endif |
147 | AsanThread::TSDDtor(tsd); |
148 | } |
149 | # endif |
150 | |
151 | static void BeforeFork() { |
152 | if (CAN_SANITIZE_LEAKS) { |
153 | __lsan::LockGlobal(); |
154 | } |
155 | // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and lock the |
156 | // stuff we need. |
157 | __lsan::LockThreads(); |
158 | __lsan::LockAllocator(); |
159 | StackDepotLockBeforeFork(); |
160 | } |
161 | |
162 | static void AfterFork(bool fork_child) { |
163 | StackDepotUnlockAfterFork(fork_child); |
164 | // `_lsan` functions defined regardless of `CAN_SANITIZE_LEAKS` and unlock |
165 | // the stuff we need. |
166 | __lsan::UnlockAllocator(); |
167 | __lsan::UnlockThreads(); |
168 | if (CAN_SANITIZE_LEAKS) { |
169 | __lsan::UnlockGlobal(); |
170 | } |
171 | } |
172 | |
173 | void InstallAtForkHandler() { |
174 | # if SANITIZER_SOLARIS || SANITIZER_NETBSD || SANITIZER_APPLE |
175 | return; // FIXME: Implement FutexWait. |
176 | # endif |
177 | pthread_atfork( |
178 | prepare: &BeforeFork, parent: []() { AfterFork(/* fork_child= */ false); }, |
179 | child: []() { AfterFork(/* fork_child= */ true); }); |
180 | } |
181 | |
182 | void InstallAtExitCheckLeaks() { |
183 | if (CAN_SANITIZE_LEAKS) { |
184 | if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) { |
185 | if (flags()->halt_on_error) |
186 | Atexit(function: __lsan::DoLeakCheck); |
187 | else |
188 | Atexit(function: __lsan::DoRecoverableLeakCheckVoid); |
189 | } |
190 | } |
191 | } |
192 | |
193 | } // namespace __asan |
194 | |
195 | #endif // SANITIZER_POSIX |
196 | |