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
64extern "C" void *_DYNAMIC;
65# else
66# include <link.h>
67# include <sys/ucontext.h>
68# endif
69
70typedef 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?
77extern "C" {
78SANITIZER_INTERFACE_ATTRIBUTE
79asan_rt_version_t __asan_rt_version;
80}
81
82namespace __asan {
83
84void InitializePlatformInterceptors() {}
85void InitializePlatformExceptionHandlers() {}
86bool IsSystemHeapAddress(uptr addr) { return false; }
87
88# if ASAN_PREMAP_SHADOW
89uptr 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
100uptr 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
112void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
113 UNIMPLEMENTED();
114}
115
116void 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
122void 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?
157void AsanCheckDynamicRTPrereqs() {}
158void AsanCheckIncompatibleRT() {}
159# else
160static 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
197static 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
202static void ReportIncompatibleRT() {
203 Report(format: "Your application is linked against incompatible ASan runtimes.\n");
204 Die();
205}
206
207void 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
223void 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
253constexpr u32 kAsanContextStackFlagsMagic = 0x51260eea;
254
255static 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
262void SignContextStack(void *context) {
263 ucontext_t *ucp = reinterpret_cast<ucontext_t *>(context);
264 ucp->uc_stack.ss_flags = HashContextStack(ucp: *ucp);
265}
266
267void 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
279void *AsanDlSymNext(const char *sym) { return dlsym(RTLD_NEXT, name: sym); }
280
281bool 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