1 | //===-- asan_linux.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 | // Linux-specific details. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "sanitizer_common/sanitizer_platform.h" |
15 | #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ |
16 | SANITIZER_SOLARIS || SANITIZER_HAIKU |
17 | |
18 | # if SANITIZER_HAIKU |
19 | # define _DEFAULT_SOURCE |
20 | # endif |
21 | |
22 | # include <dlfcn.h> |
23 | # include <fcntl.h> |
24 | # include <limits.h> |
25 | # include <pthread.h> |
26 | # include <stdio.h> |
27 | # include <sys/mman.h> |
28 | # include <sys/resource.h> |
29 | # if !SANITIZER_HAIKU |
30 | # include <sys/syscall.h> |
31 | # endif |
32 | # include <sys/time.h> |
33 | # include <sys/types.h> |
34 | # include <unistd.h> |
35 | # include <unwind.h> |
36 | |
37 | # include "asan_interceptors.h" |
38 | # include "asan_internal.h" |
39 | # include "asan_premap_shadow.h" |
40 | # include "asan_thread.h" |
41 | # include "sanitizer_common/sanitizer_flags.h" |
42 | # include "sanitizer_common/sanitizer_hash.h" |
43 | # include "sanitizer_common/sanitizer_libc.h" |
44 | # include "sanitizer_common/sanitizer_procmaps.h" |
45 | |
46 | # if SANITIZER_FREEBSD || SANITIZER_HAIKU |
47 | # include <sys/link_elf.h> |
48 | # endif |
49 | |
50 | # if SANITIZER_LINUX |
51 | # include <sys/personality.h> |
52 | # endif |
53 | |
54 | # if SANITIZER_SOLARIS |
55 | # include <link.h> |
56 | # endif |
57 | |
58 | # if SANITIZER_ANDROID || SANITIZER_FREEBSD || SANITIZER_SOLARIS |
59 | # include <ucontext.h> |
60 | # elif SANITIZER_NETBSD |
61 | # include <link_elf.h> |
62 | # include <ucontext.h> |
63 | # elif SANITIZER_HAIKU |
64 | extern "C" void *_DYNAMIC; |
65 | # else |
66 | # include <link.h> |
67 | # include <sys/ucontext.h> |
68 | # endif |
69 | |
70 | typedef enum { |
71 | ASAN_RT_VERSION_UNDEFINED = 0, |
72 | ASAN_RT_VERSION_DYNAMIC, |
73 | ASAN_RT_VERSION_STATIC, |
74 | } asan_rt_version_t; |
75 | |
76 | // FIXME: perhaps also store abi version here? |
77 | extern "C" { |
78 | SANITIZER_INTERFACE_ATTRIBUTE |
79 | asan_rt_version_t __asan_rt_version; |
80 | } |
81 | |
82 | namespace __asan { |
83 | |
84 | void InitializePlatformInterceptors() {} |
85 | void InitializePlatformExceptionHandlers() {} |
86 | bool IsSystemHeapAddress(uptr addr) { return false; } |
87 | |
88 | # if ASAN_PREMAP_SHADOW |
89 | uptr FindPremappedShadowStart(uptr shadow_size_bytes) { |
90 | uptr granularity = GetMmapGranularity(); |
91 | uptr shadow_start = reinterpret_cast<uptr>(&__asan_shadow); |
92 | uptr premap_shadow_size = PremapShadowSize(); |
93 | uptr shadow_size = RoundUpTo(shadow_size_bytes, granularity); |
94 | // We may have mapped too much. Release extra memory. |
95 | UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size); |
96 | return shadow_start; |
97 | } |
98 | # endif |
99 | |
100 | uptr FindDynamicShadowStart() { |
101 | uptr shadow_size_bytes = MemToShadowSize(size: kHighMemEnd); |
102 | # if ASAN_PREMAP_SHADOW |
103 | if (!PremapShadowFailed()) |
104 | return FindPremappedShadowStart(shadow_size_bytes); |
105 | # endif |
106 | |
107 | return MapDynamicShadow(shadow_size_bytes, ASAN_SHADOW_SCALE, |
108 | /*min_shadow_base_alignment*/ 0, high_mem_end&: kHighMemEnd, |
109 | granularity: GetMmapGranularity()); |
110 | } |
111 | |
112 | void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { |
113 | UNIMPLEMENTED(); |
114 | } |
115 | |
116 | void FlushUnneededASanShadowMemory(uptr p, uptr size) { |
117 | // Since asan's mapping is compacting, the shadow chunk may be |
118 | // not page-aligned, so we only flush the page-aligned portion. |
119 | ReleaseMemoryPagesToOS(beg: MemToShadow(p), end: MemToShadow(p: p + size)); |
120 | } |
121 | |
122 | void TryReExecWithoutASLR() { |
123 | # if SANITIZER_LINUX |
124 | // ASLR personality check. |
125 | // Caution: 'personality' is sometimes forbidden by sandboxes, so only call |
126 | // this function as a last resort (when the memory mapping is incompatible |
127 | // and ASan would fail anyway). |
128 | int old_personality = personality(persona: 0xffffffff); |
129 | if (old_personality == -1) { |
130 | VReport(1, "WARNING: unable to run personality check.\n" ); |
131 | return; |
132 | } |
133 | |
134 | bool aslr_on = (old_personality & ADDR_NO_RANDOMIZE) == 0; |
135 | |
136 | if (aslr_on) { |
137 | // Disable ASLR if the memory layout was incompatible. |
138 | // Alternatively, we could just keep re-execing until we get lucky |
139 | // with a compatible randomized layout, but the risk is that if it's |
140 | // not an ASLR-related issue, we will be stuck in an infinite loop of |
141 | // re-execing (unless we change ReExec to pass a parameter of the |
142 | // number of retries allowed.) |
143 | VReport(1, |
144 | "WARNING: AddressSanitizer: memory layout is incompatible, " |
145 | "possibly due to high-entropy ASLR.\n" |
146 | "Re-execing with fixed virtual address space.\n" |
147 | "N.B. reducing ASLR entropy is preferable.\n" ); |
148 | CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1); |
149 | |
150 | ReExec(); |
151 | } |
152 | # endif |
153 | } |
154 | |
155 | # if SANITIZER_ANDROID |
156 | // FIXME: should we do anything for Android? |
157 | void AsanCheckDynamicRTPrereqs() {} |
158 | void AsanCheckIncompatibleRT() {} |
159 | # else |
160 | static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size, |
161 | void *data) { |
162 | VReport(2, "info->dlpi_name = %s\tinfo->dlpi_addr = %p\n" , info->dlpi_name, |
163 | (void *)info->dlpi_addr); |
164 | |
165 | const char **name = (const char **)data; |
166 | |
167 | // Ignore first entry (the main program) |
168 | if (!*name) { |
169 | *name = "" ; |
170 | return 0; |
171 | } |
172 | |
173 | # if SANITIZER_HAIKU |
174 | if (!info->dlpi_name[0] || |
175 | internal_strncmp(info->dlpi_name, "/boot/system/runtime_loader" , |
176 | sizeof("/boot/system/runtime_loader" ) - 1) == 0) |
177 | return 0; |
178 | # endif |
179 | # if SANITIZER_LINUX |
180 | // Ignore vDSO. glibc versions earlier than 2.15 (and some patched |
181 | // by distributors) return an empty name for the vDSO entry, so |
182 | // detect this as well. |
183 | if (!info->dlpi_name[0] || |
184 | internal_strncmp(s1: info->dlpi_name, s2: "linux-" , n: sizeof("linux-" ) - 1) == 0) |
185 | return 0; |
186 | # endif |
187 | # if SANITIZER_FREEBSD |
188 | // Ignore vDSO. |
189 | if (internal_strcmp(info->dlpi_name, "[vdso]" ) == 0) |
190 | return 0; |
191 | # endif |
192 | |
193 | *name = info->dlpi_name; |
194 | return 1; |
195 | } |
196 | |
197 | static bool IsDynamicRTName(const char *libname) { |
198 | return internal_strstr(haystack: libname, needle: "libclang_rt.asan" ) || |
199 | internal_strstr(haystack: libname, needle: "libasan.so" ); |
200 | } |
201 | |
202 | static void ReportIncompatibleRT() { |
203 | Report(format: "Your application is linked against incompatible ASan runtimes.\n" ); |
204 | Die(); |
205 | } |
206 | |
207 | void AsanCheckDynamicRTPrereqs() { |
208 | if (!ASAN_DYNAMIC || !flags()->verify_asan_link_order) |
209 | return; |
210 | |
211 | // Ensure that dynamic RT is the first DSO in the list |
212 | const char *first_dso_name = nullptr; |
213 | dl_iterate_phdr(callback: FindFirstDSOCallback, data: &first_dso_name); |
214 | if (first_dso_name && first_dso_name[0] && !IsDynamicRTName(libname: first_dso_name)) { |
215 | Report( |
216 | format: "ASan runtime does not come first in initial library list; " |
217 | "you should either link runtime to your application or " |
218 | "manually preload it with LD_PRELOAD.\n" ); |
219 | Die(); |
220 | } |
221 | } |
222 | |
223 | void AsanCheckIncompatibleRT() { |
224 | if (ASAN_DYNAMIC) { |
225 | if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { |
226 | __asan_rt_version = ASAN_RT_VERSION_DYNAMIC; |
227 | } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) { |
228 | ReportIncompatibleRT(); |
229 | } |
230 | } else { |
231 | if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { |
232 | // Ensure that dynamic runtime is not present. We should detect it |
233 | // as early as possible, otherwise ASan interceptors could bind to |
234 | // the functions in dynamic ASan runtime instead of the functions in |
235 | // system libraries, causing crashes later in ASan initialization. |
236 | MemoryMappingLayout proc_maps(/*cache_enabled*/ true); |
237 | char filename[PATH_MAX]; |
238 | MemoryMappedSegment segment(filename, sizeof(filename)); |
239 | while (proc_maps.Next(segment: &segment)) { |
240 | if (IsDynamicRTName(libname: segment.filename)) { |
241 | ReportIncompatibleRT(); |
242 | } |
243 | } |
244 | __asan_rt_version = ASAN_RT_VERSION_STATIC; |
245 | } else if (__asan_rt_version != ASAN_RT_VERSION_STATIC) { |
246 | ReportIncompatibleRT(); |
247 | } |
248 | } |
249 | } |
250 | # endif // SANITIZER_ANDROID |
251 | |
252 | # if ASAN_INTERCEPT_SWAPCONTEXT |
253 | constexpr u32 kAsanContextStackFlagsMagic = 0x51260eea; |
254 | |
255 | static int HashContextStack(const ucontext_t &ucp) { |
256 | MurMur2Hash64Builder hash(kAsanContextStackFlagsMagic); |
257 | hash.add(k: reinterpret_cast<uptr>(ucp.uc_stack.ss_sp)); |
258 | hash.add(k: ucp.uc_stack.ss_size); |
259 | return static_cast<int>(hash.get()); |
260 | } |
261 | |
262 | void SignContextStack(void *context) { |
263 | ucontext_t *ucp = reinterpret_cast<ucontext_t *>(context); |
264 | ucp->uc_stack.ss_flags = HashContextStack(ucp: *ucp); |
265 | } |
266 | |
267 | void ReadContextStack(void *context, uptr *stack, uptr *ssize) { |
268 | const ucontext_t *ucp = reinterpret_cast<const ucontext_t *>(context); |
269 | if (HashContextStack(ucp: *ucp) == ucp->uc_stack.ss_flags) { |
270 | *stack = reinterpret_cast<uptr>(ucp->uc_stack.ss_sp); |
271 | *ssize = ucp->uc_stack.ss_size; |
272 | return; |
273 | } |
274 | *stack = 0; |
275 | *ssize = 0; |
276 | } |
277 | # endif // ASAN_INTERCEPT_SWAPCONTEXT |
278 | |
279 | void *AsanDlSymNext(const char *sym) { return dlsym(RTLD_NEXT, name: sym); } |
280 | |
281 | bool HandleDlopenInit() { |
282 | // Not supported on this platform. |
283 | static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN, |
284 | "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false" ); |
285 | return false; |
286 | } |
287 | |
288 | } // namespace __asan |
289 | |
290 | #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || |
291 | // SANITIZER_SOLARIS || SANITIZER_HAIKU |
292 | |