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