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 | granularity: GetMmapGranularity()); |
98 | } |
99 | |
100 | void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { |
101 | UNIMPLEMENTED(); |
102 | } |
103 | |
104 | void FlushUnneededASanShadowMemory(uptr p, uptr size) { |
105 | // Since asan's mapping is compacting, the shadow chunk may be |
106 | // not page-aligned, so we only flush the page-aligned portion. |
107 | ReleaseMemoryPagesToOS(beg: MemToShadow(p), end: MemToShadow(p: p + size)); |
108 | } |
109 | |
110 | # if SANITIZER_ANDROID |
111 | // FIXME: should we do anything for Android? |
112 | void AsanCheckDynamicRTPrereqs() {} |
113 | void AsanCheckIncompatibleRT() {} |
114 | # else |
115 | static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size, |
116 | void *data) { |
117 | VReport(2, "info->dlpi_name = %s\tinfo->dlpi_addr = %p\n" , info->dlpi_name, |
118 | (void *)info->dlpi_addr); |
119 | |
120 | const char **name = (const char **)data; |
121 | |
122 | // Ignore first entry (the main program) |
123 | if (!*name) { |
124 | *name = "" ; |
125 | return 0; |
126 | } |
127 | |
128 | # if SANITIZER_LINUX |
129 | // Ignore vDSO. glibc versions earlier than 2.15 (and some patched |
130 | // by distributors) return an empty name for the vDSO entry, so |
131 | // detect this as well. |
132 | if (!info->dlpi_name[0] || |
133 | internal_strncmp(s1: info->dlpi_name, s2: "linux-" , n: sizeof("linux-" ) - 1) == 0) |
134 | return 0; |
135 | # endif |
136 | # if SANITIZER_FREEBSD |
137 | // Ignore vDSO. |
138 | if (internal_strcmp(info->dlpi_name, "[vdso]" ) == 0) |
139 | return 0; |
140 | # endif |
141 | |
142 | *name = info->dlpi_name; |
143 | return 1; |
144 | } |
145 | |
146 | static bool IsDynamicRTName(const char *libname) { |
147 | return internal_strstr(haystack: libname, needle: "libclang_rt.asan" ) || |
148 | internal_strstr(haystack: libname, needle: "libasan.so" ); |
149 | } |
150 | |
151 | static void ReportIncompatibleRT() { |
152 | Report(format: "Your application is linked against incompatible ASan runtimes.\n" ); |
153 | Die(); |
154 | } |
155 | |
156 | void AsanCheckDynamicRTPrereqs() { |
157 | if (!ASAN_DYNAMIC || !flags()->verify_asan_link_order) |
158 | return; |
159 | |
160 | // Ensure that dynamic RT is the first DSO in the list |
161 | const char *first_dso_name = nullptr; |
162 | dl_iterate_phdr(callback: FindFirstDSOCallback, data: &first_dso_name); |
163 | if (first_dso_name && first_dso_name[0] && !IsDynamicRTName(libname: first_dso_name)) { |
164 | Report( |
165 | format: "ASan runtime does not come first in initial library list; " |
166 | "you should either link runtime to your application or " |
167 | "manually preload it with LD_PRELOAD.\n" ); |
168 | Die(); |
169 | } |
170 | } |
171 | |
172 | void AsanCheckIncompatibleRT() { |
173 | if (ASAN_DYNAMIC) { |
174 | if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { |
175 | __asan_rt_version = ASAN_RT_VERSION_DYNAMIC; |
176 | } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) { |
177 | ReportIncompatibleRT(); |
178 | } |
179 | } else { |
180 | if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) { |
181 | // Ensure that dynamic runtime is not present. We should detect it |
182 | // as early as possible, otherwise ASan interceptors could bind to |
183 | // the functions in dynamic ASan runtime instead of the functions in |
184 | // system libraries, causing crashes later in ASan initialization. |
185 | MemoryMappingLayout proc_maps(/*cache_enabled*/ true); |
186 | char filename[PATH_MAX]; |
187 | MemoryMappedSegment segment(filename, sizeof(filename)); |
188 | while (proc_maps.Next(segment: &segment)) { |
189 | if (IsDynamicRTName(libname: segment.filename)) { |
190 | Report( |
191 | format: "Your application is linked against " |
192 | "incompatible ASan runtimes.\n" ); |
193 | Die(); |
194 | } |
195 | } |
196 | __asan_rt_version = ASAN_RT_VERSION_STATIC; |
197 | } else if (__asan_rt_version != ASAN_RT_VERSION_STATIC) { |
198 | ReportIncompatibleRT(); |
199 | } |
200 | } |
201 | } |
202 | # endif // SANITIZER_ANDROID |
203 | |
204 | # if ASAN_INTERCEPT_SWAPCONTEXT |
205 | constexpr u32 kAsanContextStackFlagsMagic = 0x51260eea; |
206 | |
207 | static int HashContextStack(const ucontext_t &ucp) { |
208 | MurMur2Hash64Builder hash(kAsanContextStackFlagsMagic); |
209 | hash.add(k: reinterpret_cast<uptr>(ucp.uc_stack.ss_sp)); |
210 | hash.add(k: ucp.uc_stack.ss_size); |
211 | return static_cast<int>(hash.get()); |
212 | } |
213 | |
214 | void SignContextStack(void *context) { |
215 | ucontext_t *ucp = reinterpret_cast<ucontext_t *>(context); |
216 | ucp->uc_stack.ss_flags = HashContextStack(ucp: *ucp); |
217 | } |
218 | |
219 | void ReadContextStack(void *context, uptr *stack, uptr *ssize) { |
220 | const ucontext_t *ucp = reinterpret_cast<const ucontext_t *>(context); |
221 | if (HashContextStack(ucp: *ucp) == ucp->uc_stack.ss_flags) { |
222 | *stack = reinterpret_cast<uptr>(ucp->uc_stack.ss_sp); |
223 | *ssize = ucp->uc_stack.ss_size; |
224 | return; |
225 | } |
226 | *stack = 0; |
227 | *ssize = 0; |
228 | } |
229 | # endif // ASAN_INTERCEPT_SWAPCONTEXT |
230 | |
231 | void *AsanDlSymNext(const char *sym) { return dlsym(RTLD_NEXT, name: sym); } |
232 | |
233 | bool HandleDlopenInit() { |
234 | // Not supported on this platform. |
235 | static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN, |
236 | "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false" ); |
237 | return false; |
238 | } |
239 | |
240 | } // namespace __asan |
241 | |
242 | #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || |
243 | // SANITIZER_SOLARIS |
244 | |