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