]>
Commit | Line | Data |
---|---|---|
b667dd70 | 1 | //===-- sanitizer_mac.cpp -------------------------------------------------===// |
f35db108 | 2 | // |
b667dd70 ML |
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 | |
f35db108 WM |
6 | // |
7 | //===----------------------------------------------------------------------===// | |
8 | // | |
dee5ea7a KS |
9 | // This file is shared between various sanitizers' runtime libraries and |
10 | // implements OSX-specific functions. | |
f35db108 WM |
11 | //===----------------------------------------------------------------------===// |
12 | ||
ef1b3fda KS |
13 | #include "sanitizer_platform.h" |
14 | #if SANITIZER_MAC | |
10189819 | 15 | #include "sanitizer_mac.h" |
3ca75cd5 | 16 | #include "interception/interception.h" |
ef1b3fda | 17 | |
b4ab7d34 KS |
18 | // Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so |
19 | // the clients will most certainly use 64-bit ones as well. | |
20 | #ifndef _DARWIN_USE_64_BIT_INODE | |
21 | #define _DARWIN_USE_64_BIT_INODE 1 | |
22 | #endif | |
23 | #include <stdio.h> | |
f35db108 WM |
24 | |
25 | #include "sanitizer_common.h" | |
5d3805fc | 26 | #include "sanitizer_file.h" |
dee5ea7a | 27 | #include "sanitizer_flags.h" |
f35db108 WM |
28 | #include "sanitizer_internal_defs.h" |
29 | #include "sanitizer_libc.h" | |
ef1b3fda | 30 | #include "sanitizer_placement_new.h" |
696d846a | 31 | #include "sanitizer_platform_limits_posix.h" |
f35db108 | 32 | #include "sanitizer_procmaps.h" |
3c6331c2 | 33 | #include "sanitizer_ptrauth.h" |
f35db108 | 34 | |
696d846a | 35 | #if !SANITIZER_IOS |
f35db108 | 36 | #include <crt_externs.h> // for _NSGetEnviron |
696d846a MO |
37 | #else |
38 | extern char **environ; | |
39 | #endif | |
40 | ||
f18ab180 | 41 | #if defined(__has_include) && __has_include(<os/trace.h>) && defined(__BLOCKS__) |
10189819 MO |
42 | #define SANITIZER_OS_TRACE 1 |
43 | #include <os/trace.h> | |
44 | #else | |
45 | #define SANITIZER_OS_TRACE 0 | |
46 | #endif | |
47 | ||
48 | #if !SANITIZER_IOS | |
49 | #include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron | |
50 | #else | |
51 | extern "C" { | |
52 | extern char ***_NSGetArgv(void); | |
53 | } | |
54 | #endif | |
55 | ||
56 | #include <asl.h> | |
57 | #include <dlfcn.h> // for dladdr() | |
696d846a | 58 | #include <errno.h> |
f35db108 | 59 | #include <fcntl.h> |
696d846a MO |
60 | #include <libkern/OSAtomic.h> |
61 | #include <mach-o/dyld.h> | |
62 | #include <mach/mach.h> | |
eac97531 | 63 | #include <mach/mach_time.h> |
55aea9f5 | 64 | #include <mach/vm_statistics.h> |
eac97531 | 65 | #include <malloc/malloc.h> |
f35db108 WM |
66 | #include <pthread.h> |
67 | #include <sched.h> | |
dee5ea7a | 68 | #include <signal.h> |
3ca75cd5 | 69 | #include <spawn.h> |
696d846a | 70 | #include <stdlib.h> |
3ca75cd5 | 71 | #include <sys/ioctl.h> |
f35db108 WM |
72 | #include <sys/mman.h> |
73 | #include <sys/resource.h> | |
74 | #include <sys/stat.h> | |
dee5ea7a | 75 | #include <sys/sysctl.h> |
f35db108 | 76 | #include <sys/types.h> |
10189819 | 77 | #include <sys/wait.h> |
f35db108 | 78 | #include <unistd.h> |
10189819 MO |
79 | #include <util.h> |
80 | ||
81 | // From <crt_externs.h>, but we don't have that file on iOS. | |
82 | extern "C" { | |
83 | extern char ***_NSGetArgv(void); | |
84 | extern char ***_NSGetEnviron(void); | |
85 | } | |
86 | ||
87 | // From <mach/mach_vm.h>, but we don't have that file on iOS. | |
88 | extern "C" { | |
89 | extern kern_return_t mach_vm_region_recurse( | |
90 | vm_map_t target_task, | |
91 | mach_vm_address_t *address, | |
92 | mach_vm_size_t *size, | |
93 | natural_t *nesting_depth, | |
94 | vm_region_recurse_info_t info, | |
95 | mach_msg_type_number_t *infoCnt); | |
96 | } | |
f35db108 WM |
97 | |
98 | namespace __sanitizer { | |
99 | ||
ef1b3fda KS |
100 | #include "sanitizer_syscall_generic.inc" |
101 | ||
778e0ac3 | 102 | // Direct syscalls, don't call libmalloc hooks (but not available on 10.6). |
10189819 | 103 | extern "C" void *__mmap(void *addr, size_t len, int prot, int flags, int fildes, |
778e0ac3 MO |
104 | off_t off) SANITIZER_WEAK_ATTRIBUTE; |
105 | extern "C" int __munmap(void *, size_t) SANITIZER_WEAK_ATTRIBUTE; | |
10189819 | 106 | |
f35db108 | 107 | // ---------------------- sanitizer_libc.h |
eac97531 ML |
108 | |
109 | // From <mach/vm_statistics.h>, but not on older OSs. | |
110 | #ifndef VM_MEMORY_SANITIZER | |
111 | #define VM_MEMORY_SANITIZER 99 | |
112 | #endif | |
113 | ||
b667dd70 ML |
114 | // XNU on Darwin provides a mmap flag that optimizes allocation/deallocation of |
115 | // giant memory regions (i.e. shadow memory regions). | |
116 | #define kXnuFastMmapFd 0x4 | |
117 | static size_t kXnuFastMmapThreshold = 2 << 30; // 2 GB | |
118 | static bool use_xnu_fast_mmap = false; | |
119 | ||
ef1b3fda KS |
120 | uptr internal_mmap(void *addr, size_t length, int prot, int flags, |
121 | int fd, u64 offset) { | |
b667dd70 ML |
122 | if (fd == -1) { |
123 | fd = VM_MAKE_TAG(VM_MEMORY_SANITIZER); | |
124 | if (length >= kXnuFastMmapThreshold) { | |
125 | if (use_xnu_fast_mmap) fd |= kXnuFastMmapFd; | |
126 | } | |
127 | } | |
5d3805fc | 128 | if (&__mmap) return (uptr)__mmap(addr, length, prot, flags, fd, offset); |
778e0ac3 | 129 | return (uptr)mmap(addr, length, prot, flags, fd, offset); |
f35db108 WM |
130 | } |
131 | ||
ef1b3fda | 132 | uptr internal_munmap(void *addr, uptr length) { |
5d3805fc | 133 | if (&__munmap) return __munmap(addr, length); |
778e0ac3 | 134 | return munmap(addr, length); |
f35db108 WM |
135 | } |
136 | ||
696d846a MO |
137 | int internal_mprotect(void *addr, uptr length, int prot) { |
138 | return mprotect(addr, length, prot); | |
139 | } | |
140 | ||
ef1b3fda | 141 | uptr internal_close(fd_t fd) { |
f35db108 WM |
142 | return close(fd); |
143 | } | |
144 | ||
ef1b3fda | 145 | uptr internal_open(const char *filename, int flags) { |
b4ab7d34 KS |
146 | return open(filename, flags); |
147 | } | |
148 | ||
ef1b3fda | 149 | uptr internal_open(const char *filename, int flags, u32 mode) { |
b4ab7d34 KS |
150 | return open(filename, flags, mode); |
151 | } | |
152 | ||
f35db108 WM |
153 | uptr internal_read(fd_t fd, void *buf, uptr count) { |
154 | return read(fd, buf, count); | |
155 | } | |
156 | ||
157 | uptr internal_write(fd_t fd, const void *buf, uptr count) { | |
158 | return write(fd, buf, count); | |
159 | } | |
160 | ||
ef1b3fda | 161 | uptr internal_stat(const char *path, void *buf) { |
b4ab7d34 KS |
162 | return stat(path, (struct stat *)buf); |
163 | } | |
164 | ||
ef1b3fda | 165 | uptr internal_lstat(const char *path, void *buf) { |
b4ab7d34 KS |
166 | return lstat(path, (struct stat *)buf); |
167 | } | |
168 | ||
ef1b3fda | 169 | uptr internal_fstat(fd_t fd, void *buf) { |
b4ab7d34 KS |
170 | return fstat(fd, (struct stat *)buf); |
171 | } | |
172 | ||
f35db108 WM |
173 | uptr internal_filesize(fd_t fd) { |
174 | struct stat st; | |
b4ab7d34 | 175 | if (internal_fstat(fd, &st)) |
f35db108 WM |
176 | return -1; |
177 | return (uptr)st.st_size; | |
178 | } | |
179 | ||
b667dd70 ML |
180 | uptr internal_dup(int oldfd) { |
181 | return dup(oldfd); | |
182 | } | |
183 | ||
ef1b3fda | 184 | uptr internal_dup2(int oldfd, int newfd) { |
f35db108 WM |
185 | return dup2(oldfd, newfd); |
186 | } | |
187 | ||
188 | uptr internal_readlink(const char *path, char *buf, uptr bufsize) { | |
189 | return readlink(path, buf, bufsize); | |
190 | } | |
191 | ||
696d846a MO |
192 | uptr internal_unlink(const char *path) { |
193 | return unlink(path); | |
194 | } | |
195 | ||
ef1b3fda | 196 | uptr internal_sched_yield() { |
f35db108 WM |
197 | return sched_yield(); |
198 | } | |
199 | ||
7df59255 KS |
200 | void internal__exit(int exitcode) { |
201 | _exit(exitcode); | |
202 | } | |
203 | ||
10189819 MO |
204 | unsigned int internal_sleep(unsigned int seconds) { |
205 | return sleep(seconds); | |
206 | } | |
207 | ||
ef1b3fda KS |
208 | uptr internal_getpid() { |
209 | return getpid(); | |
210 | } | |
211 | ||
3c6331c2 ML |
212 | int internal_dlinfo(void *handle, int request, void *p) { |
213 | UNIMPLEMENTED(); | |
214 | } | |
215 | ||
dee5ea7a KS |
216 | int internal_sigaction(int signum, const void *act, void *oldact) { |
217 | return sigaction(signum, | |
eac97531 | 218 | (const struct sigaction *)act, (struct sigaction *)oldact); |
dee5ea7a KS |
219 | } |
220 | ||
696d846a MO |
221 | void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); } |
222 | ||
223 | uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, | |
224 | __sanitizer_sigset_t *oldset) { | |
5d3805fc JJ |
225 | // Don't use sigprocmask here, because it affects all threads. |
226 | return pthread_sigmask(how, set, oldset); | |
696d846a MO |
227 | } |
228 | ||
778e0ac3 MO |
229 | // Doesn't call pthread_atfork() handlers (but not available on 10.6). |
230 | extern "C" pid_t __fork(void) SANITIZER_WEAK_ATTRIBUTE; | |
10189819 | 231 | |
dee5ea7a | 232 | int internal_fork() { |
5d3805fc | 233 | if (&__fork) |
778e0ac3 MO |
234 | return __fork(); |
235 | return fork(); | |
10189819 MO |
236 | } |
237 | ||
eac97531 ML |
238 | int internal_sysctl(const int *name, unsigned int namelen, void *oldp, |
239 | uptr *oldlenp, const void *newp, uptr newlen) { | |
240 | return sysctl(const_cast<int *>(name), namelen, oldp, (size_t *)oldlenp, | |
241 | const_cast<void *>(newp), (size_t)newlen); | |
242 | } | |
243 | ||
244 | int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp, | |
245 | const void *newp, uptr newlen) { | |
246 | return sysctlbyname(sname, oldp, (size_t *)oldlenp, const_cast<void *>(newp), | |
247 | (size_t)newlen); | |
248 | } | |
249 | ||
3c6331c2 ML |
250 | static fd_t internal_spawn_impl(const char *argv[], const char *envp[], |
251 | pid_t *pid) { | |
3ca75cd5 ML |
252 | fd_t master_fd = kInvalidFd; |
253 | fd_t slave_fd = kInvalidFd; | |
254 | ||
255 | auto fd_closer = at_scope_exit([&] { | |
256 | internal_close(master_fd); | |
257 | internal_close(slave_fd); | |
258 | }); | |
259 | ||
260 | // We need a new pseudoterminal to avoid buffering problems. The 'atos' tool | |
261 | // in particular detects when it's talking to a pipe and forgets to flush the | |
262 | // output stream after sending a response. | |
263 | master_fd = posix_openpt(O_RDWR); | |
264 | if (master_fd == kInvalidFd) return kInvalidFd; | |
265 | ||
266 | int res = grantpt(master_fd) || unlockpt(master_fd); | |
267 | if (res != 0) return kInvalidFd; | |
268 | ||
269 | // Use TIOCPTYGNAME instead of ptsname() to avoid threading problems. | |
270 | char slave_pty_name[128]; | |
271 | res = ioctl(master_fd, TIOCPTYGNAME, slave_pty_name); | |
272 | if (res == -1) return kInvalidFd; | |
273 | ||
274 | slave_fd = internal_open(slave_pty_name, O_RDWR); | |
275 | if (slave_fd == kInvalidFd) return kInvalidFd; | |
276 | ||
277 | // File descriptor actions | |
278 | posix_spawn_file_actions_t acts; | |
279 | res = posix_spawn_file_actions_init(&acts); | |
280 | if (res != 0) return kInvalidFd; | |
281 | ||
282 | auto acts_cleanup = at_scope_exit([&] { | |
283 | posix_spawn_file_actions_destroy(&acts); | |
284 | }); | |
285 | ||
286 | res = posix_spawn_file_actions_adddup2(&acts, slave_fd, STDIN_FILENO) || | |
287 | posix_spawn_file_actions_adddup2(&acts, slave_fd, STDOUT_FILENO) || | |
288 | posix_spawn_file_actions_addclose(&acts, slave_fd); | |
289 | if (res != 0) return kInvalidFd; | |
290 | ||
291 | // Spawn attributes | |
292 | posix_spawnattr_t attrs; | |
293 | res = posix_spawnattr_init(&attrs); | |
294 | if (res != 0) return kInvalidFd; | |
295 | ||
296 | auto attrs_cleanup = at_scope_exit([&] { | |
297 | posix_spawnattr_destroy(&attrs); | |
298 | }); | |
299 | ||
300 | // In the spawned process, close all file descriptors that are not explicitly | |
301 | // described by the file actions object. This is Darwin-specific extension. | |
302 | res = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_CLOEXEC_DEFAULT); | |
303 | if (res != 0) return kInvalidFd; | |
304 | ||
305 | // posix_spawn | |
306 | char **argv_casted = const_cast<char **>(argv); | |
3c6331c2 ML |
307 | char **envp_casted = const_cast<char **>(envp); |
308 | res = posix_spawn(pid, argv[0], &acts, &attrs, argv_casted, envp_casted); | |
3ca75cd5 ML |
309 | if (res != 0) return kInvalidFd; |
310 | ||
311 | // Disable echo in the new terminal, disable CR. | |
312 | struct termios termflags; | |
313 | tcgetattr(master_fd, &termflags); | |
314 | termflags.c_oflag &= ~ONLCR; | |
315 | termflags.c_lflag &= ~ECHO; | |
316 | tcsetattr(master_fd, TCSANOW, &termflags); | |
317 | ||
318 | // On success, do not close master_fd on scope exit. | |
319 | fd_t fd = master_fd; | |
320 | master_fd = kInvalidFd; | |
321 | ||
322 | return fd; | |
323 | } | |
324 | ||
3c6331c2 | 325 | fd_t internal_spawn(const char *argv[], const char *envp[], pid_t *pid) { |
3ca75cd5 ML |
326 | // The client program may close its stdin and/or stdout and/or stderr thus |
327 | // allowing open/posix_openpt to reuse file descriptors 0, 1 or 2. In this | |
328 | // case the communication is broken if either the parent or the child tries to | |
329 | // close or duplicate these descriptors. We temporarily reserve these | |
330 | // descriptors here to prevent this. | |
331 | fd_t low_fds[3]; | |
332 | size_t count = 0; | |
333 | ||
334 | for (; count < 3; count++) { | |
335 | low_fds[count] = posix_openpt(O_RDWR); | |
336 | if (low_fds[count] >= STDERR_FILENO) | |
337 | break; | |
10189819 | 338 | } |
3ca75cd5 | 339 | |
3c6331c2 | 340 | fd_t fd = internal_spawn_impl(argv, envp, pid); |
3ca75cd5 ML |
341 | |
342 | for (; count > 0; count--) { | |
343 | internal_close(low_fds[count]); | |
10189819 | 344 | } |
3ca75cd5 ML |
345 | |
346 | return fd; | |
dee5ea7a KS |
347 | } |
348 | ||
866e32ad KS |
349 | uptr internal_rename(const char *oldpath, const char *newpath) { |
350 | return rename(oldpath, newpath); | |
351 | } | |
352 | ||
353 | uptr internal_ftruncate(fd_t fd, uptr size) { | |
354 | return ftruncate(fd, size); | |
355 | } | |
356 | ||
10189819 MO |
357 | uptr internal_execve(const char *filename, char *const argv[], |
358 | char *const envp[]) { | |
359 | return execve(filename, argv, envp); | |
360 | } | |
361 | ||
362 | uptr internal_waitpid(int pid, int *status, int options) { | |
363 | return waitpid(pid, status, options); | |
364 | } | |
365 | ||
f35db108 | 366 | // ----------------- sanitizer_common.h |
e297eb60 | 367 | bool FileExists(const char *filename) { |
b667dd70 ML |
368 | if (ShouldMockFailureToOpen(filename)) |
369 | return false; | |
e297eb60 KS |
370 | struct stat st; |
371 | if (stat(filename, &st)) | |
372 | return false; | |
373 | // Sanity check: filename is a regular file. | |
374 | return S_ISREG(st.st_mode); | |
375 | } | |
376 | ||
5d3805fc JJ |
377 | tid_t GetTid() { |
378 | tid_t tid; | |
10189819 MO |
379 | pthread_threadid_np(nullptr, &tid); |
380 | return tid; | |
f35db108 WM |
381 | } |
382 | ||
383 | void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, | |
384 | uptr *stack_bottom) { | |
385 | CHECK(stack_top); | |
386 | CHECK(stack_bottom); | |
387 | uptr stacksize = pthread_get_stacksize_np(pthread_self()); | |
dee5ea7a KS |
388 | // pthread_get_stacksize_np() returns an incorrect stack size for the main |
389 | // thread on Mavericks. See | |
10189819 | 390 | // https://github.com/google/sanitizers/issues/261 |
c5be964a | 391 | if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization && |
dee5ea7a KS |
392 | stacksize == (1 << 19)) { |
393 | struct rlimit rl; | |
394 | CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0); | |
395 | // Most often rl.rlim_cur will be the desired 8M. | |
396 | if (rl.rlim_cur < kMaxThreadStackSize) { | |
397 | stacksize = rl.rlim_cur; | |
398 | } else { | |
399 | stacksize = kMaxThreadStackSize; | |
400 | } | |
401 | } | |
f35db108 WM |
402 | void *stackaddr = pthread_get_stackaddr_np(pthread_self()); |
403 | *stack_top = (uptr)stackaddr; | |
404 | *stack_bottom = *stack_top - stacksize; | |
405 | } | |
406 | ||
696d846a MO |
407 | char **GetEnviron() { |
408 | #if !SANITIZER_IOS | |
f35db108 | 409 | char ***env_ptr = _NSGetEnviron(); |
df77f0e4 KS |
410 | if (!env_ptr) { |
411 | Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is " | |
412 | "called after libSystem_initializer().\n"); | |
413 | CHECK(env_ptr); | |
414 | } | |
f35db108 | 415 | char **environ = *env_ptr; |
696d846a | 416 | #endif |
f35db108 | 417 | CHECK(environ); |
696d846a MO |
418 | return environ; |
419 | } | |
420 | ||
421 | const char *GetEnv(const char *name) { | |
422 | char **env = GetEnviron(); | |
f35db108 | 423 | uptr name_len = internal_strlen(name); |
696d846a MO |
424 | while (*env != 0) { |
425 | uptr len = internal_strlen(*env); | |
f35db108 | 426 | if (len > name_len) { |
696d846a | 427 | const char *p = *env; |
f35db108 WM |
428 | if (!internal_memcmp(p, name, name_len) && |
429 | p[name_len] == '=') { // Match. | |
696d846a | 430 | return *env + name_len + 1; // String starting after =. |
f35db108 WM |
431 | } |
432 | } | |
696d846a | 433 | env++; |
f35db108 WM |
434 | } |
435 | return 0; | |
436 | } | |
437 | ||
696d846a MO |
438 | uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { |
439 | CHECK_LE(kMaxPathLength, buf_len); | |
440 | ||
441 | // On OS X the executable path is saved to the stack by dyld. Reading it | |
442 | // from there is much faster than calling dladdr, especially for large | |
443 | // binaries with symbols. | |
444 | InternalScopedString exe_path(kMaxPathLength); | |
445 | uint32_t size = exe_path.size(); | |
446 | if (_NSGetExecutablePath(exe_path.data(), &size) == 0 && | |
447 | realpath(exe_path.data(), buf) != 0) { | |
448 | return internal_strlen(buf); | |
449 | } | |
450 | return 0; | |
f35db108 WM |
451 | } |
452 | ||
696d846a MO |
453 | uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) { |
454 | return ReadBinaryName(buf, buf_len); | |
e9772e16 KS |
455 | } |
456 | ||
696d846a MO |
457 | void ReExec() { |
458 | UNIMPLEMENTED(); | |
ef1b3fda KS |
459 | } |
460 | ||
eac97531 ML |
461 | void CheckASLR() { |
462 | // Do nothing | |
463 | } | |
464 | ||
b667dd70 ML |
465 | void CheckMPROTECT() { |
466 | // Do nothing | |
467 | } | |
468 | ||
696d846a MO |
469 | uptr GetPageSize() { |
470 | return sysconf(_SC_PAGESIZE); | |
2660d12d KS |
471 | } |
472 | ||
eac97531 ML |
473 | extern "C" unsigned malloc_num_zones; |
474 | extern "C" malloc_zone_t **malloc_zones; | |
475 | malloc_zone_t sanitizer_zone; | |
476 | ||
477 | // We need to make sure that sanitizer_zone is registered as malloc_zones[0]. If | |
478 | // libmalloc tries to set up a different zone as malloc_zones[0], it will call | |
479 | // mprotect(malloc_zones, ..., PROT_READ). This interceptor will catch that and | |
480 | // make sure we are still the first (default) zone. | |
481 | void MprotectMallocZones(void *addr, int prot) { | |
482 | if (addr == malloc_zones && prot == PROT_READ) { | |
483 | if (malloc_num_zones > 1 && malloc_zones[0] != &sanitizer_zone) { | |
484 | for (unsigned i = 1; i < malloc_num_zones; i++) { | |
485 | if (malloc_zones[i] == &sanitizer_zone) { | |
486 | // Swap malloc_zones[0] and malloc_zones[i]. | |
487 | malloc_zones[i] = malloc_zones[0]; | |
488 | malloc_zones[0] = &sanitizer_zone; | |
489 | break; | |
490 | } | |
491 | } | |
492 | } | |
493 | } | |
494 | } | |
495 | ||
ef1b3fda KS |
496 | BlockingMutex::BlockingMutex() { |
497 | internal_memset(this, 0, sizeof(*this)); | |
498 | } | |
499 | ||
2660d12d KS |
500 | void BlockingMutex::Lock() { |
501 | CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_)); | |
ef1b3fda | 502 | CHECK_EQ(OS_SPINLOCK_INIT, 0); |
5d3805fc | 503 | CHECK_EQ(owner_, 0); |
2660d12d | 504 | OSSpinLockLock((OSSpinLock*)&opaque_storage_); |
2660d12d KS |
505 | } |
506 | ||
507 | void BlockingMutex::Unlock() { | |
2660d12d KS |
508 | OSSpinLockUnlock((OSSpinLock*)&opaque_storage_); |
509 | } | |
510 | ||
ef1b3fda | 511 | void BlockingMutex::CheckLocked() { |
5d3805fc | 512 | CHECK_NE(*(OSSpinLock*)&opaque_storage_, 0); |
ef1b3fda KS |
513 | } |
514 | ||
515 | u64 NanoTime() { | |
eac97531 ML |
516 | timeval tv; |
517 | internal_memset(&tv, 0, sizeof(tv)); | |
518 | gettimeofday(&tv, 0); | |
519 | return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000; | |
520 | } | |
521 | ||
522 | // This needs to be called during initialization to avoid being racy. | |
523 | u64 MonotonicNanoTime() { | |
524 | static mach_timebase_info_data_t timebase_info; | |
525 | if (timebase_info.denom == 0) mach_timebase_info(&timebase_info); | |
526 | return (mach_absolute_time() * timebase_info.numer) / timebase_info.denom; | |
ef1b3fda KS |
527 | } |
528 | ||
529 | uptr GetTlsSize() { | |
530 | return 0; | |
531 | } | |
532 | ||
533 | void InitTlsSize() { | |
534 | } | |
535 | ||
5d3805fc JJ |
536 | uptr TlsBaseAddr() { |
537 | uptr segbase = 0; | |
538 | #if defined(__x86_64__) | |
539 | asm("movq %%gs:0,%0" : "=r"(segbase)); | |
540 | #elif defined(__i386__) | |
541 | asm("movl %%gs:0,%0" : "=r"(segbase)); | |
542 | #endif | |
543 | return segbase; | |
544 | } | |
545 | ||
546 | // The size of the tls on darwin does not appear to be well documented, | |
547 | // however the vm memory map suggests that it is 1024 uptrs in size, | |
548 | // with a size of 0x2000 bytes on x86_64 and 0x1000 bytes on i386. | |
549 | uptr TlsSize() { | |
550 | #if defined(__x86_64__) || defined(__i386__) | |
551 | return 1024 * sizeof(uptr); | |
552 | #else | |
553 | return 0; | |
554 | #endif | |
555 | } | |
556 | ||
ef1b3fda KS |
557 | void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, |
558 | uptr *tls_addr, uptr *tls_size) { | |
10189819 | 559 | #if !SANITIZER_GO |
ef1b3fda KS |
560 | uptr stack_top, stack_bottom; |
561 | GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); | |
562 | *stk_addr = stack_bottom; | |
563 | *stk_size = stack_top - stack_bottom; | |
5d3805fc JJ |
564 | *tls_addr = TlsBaseAddr(); |
565 | *tls_size = TlsSize(); | |
ef1b3fda KS |
566 | #else |
567 | *stk_addr = 0; | |
568 | *stk_size = 0; | |
569 | *tls_addr = 0; | |
570 | *tls_size = 0; | |
571 | #endif | |
572 | } | |
573 | ||
10189819 | 574 | void ListOfModules::init() { |
5d3805fc | 575 | clearOrInit(); |
ef1b3fda | 576 | MemoryMappingLayout memory_mapping(false); |
10189819 | 577 | memory_mapping.DumpListOfModules(&modules_); |
dee5ea7a KS |
578 | } |
579 | ||
5d3805fc JJ |
580 | void ListOfModules::fallbackInit() { clear(); } |
581 | ||
582 | static HandleSignalMode GetHandleSignalModeImpl(int signum) { | |
583 | switch (signum) { | |
584 | case SIGABRT: | |
585 | return common_flags()->handle_abort; | |
586 | case SIGILL: | |
587 | return common_flags()->handle_sigill; | |
eac97531 ML |
588 | case SIGTRAP: |
589 | return common_flags()->handle_sigtrap; | |
5d3805fc JJ |
590 | case SIGFPE: |
591 | return common_flags()->handle_sigfpe; | |
592 | case SIGSEGV: | |
593 | return common_flags()->handle_segv; | |
594 | case SIGBUS: | |
595 | return common_flags()->handle_sigbus; | |
596 | } | |
597 | return kHandleSignalNo; | |
598 | } | |
599 | ||
600 | HandleSignalMode GetHandleSignalMode(int signum) { | |
601 | // Handling fatal signals on watchOS and tvOS devices is disallowed. | |
10189819 | 602 | if ((SANITIZER_WATCHOS || SANITIZER_TVOS) && !(SANITIZER_IOSSIM)) |
5d3805fc JJ |
603 | return kHandleSignalNo; |
604 | HandleSignalMode result = GetHandleSignalModeImpl(signum); | |
605 | if (result == kHandleSignalYes && !common_flags()->allow_user_segv_handler) | |
606 | return kHandleSignalExclusive; | |
607 | return result; | |
dee5ea7a KS |
608 | } |
609 | ||
610 | MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED; | |
611 | ||
612 | MacosVersion GetMacosVersionInternal() { | |
613 | int mib[2] = { CTL_KERN, KERN_OSRELEASE }; | |
614 | char version[100]; | |
615 | uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]); | |
616 | for (uptr i = 0; i < maxlen; i++) version[i] = '\0'; | |
617 | // Get the version length. | |
eac97531 | 618 | CHECK_NE(internal_sysctl(mib, 2, 0, &len, 0, 0), -1); |
dee5ea7a | 619 | CHECK_LT(len, maxlen); |
eac97531 | 620 | CHECK_NE(internal_sysctl(mib, 2, version, &len, 0, 0), -1); |
b667dd70 ML |
621 | |
622 | // Expect <major>.<minor>(.<patch>) | |
623 | CHECK_GE(len, 3); | |
624 | const char *p = version; | |
625 | int major = internal_simple_strtoll(p, &p, /*base=*/10); | |
626 | if (*p != '.') return MACOS_VERSION_UNKNOWN; | |
627 | p += 1; | |
628 | int minor = internal_simple_strtoll(p, &p, /*base=*/10); | |
629 | if (*p != '.') return MACOS_VERSION_UNKNOWN; | |
630 | ||
631 | switch (major) { | |
b667dd70 ML |
632 | case 11: return MACOS_VERSION_LION; |
633 | case 12: return MACOS_VERSION_MOUNTAIN_LION; | |
634 | case 13: return MACOS_VERSION_MAVERICKS; | |
635 | case 14: return MACOS_VERSION_YOSEMITE; | |
636 | case 15: return MACOS_VERSION_EL_CAPITAN; | |
637 | case 16: return MACOS_VERSION_SIERRA; | |
3c6331c2 | 638 | case 17: return MACOS_VERSION_HIGH_SIERRA; |
b667dd70 ML |
639 | case 18: return MACOS_VERSION_MOJAVE; |
640 | case 19: return MACOS_VERSION_CATALINA; | |
641 | default: | |
642 | if (major < 9) return MACOS_VERSION_UNKNOWN; | |
643 | return MACOS_VERSION_UNKNOWN_NEWER; | |
dee5ea7a KS |
644 | } |
645 | } | |
646 | ||
647 | MacosVersion GetMacosVersion() { | |
648 | atomic_uint32_t *cache = | |
649 | reinterpret_cast<atomic_uint32_t*>(&cached_macos_version); | |
650 | MacosVersion result = | |
651 | static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire)); | |
652 | if (result == MACOS_VERSION_UNINITIALIZED) { | |
653 | result = GetMacosVersionInternal(); | |
654 | atomic_store(cache, result, memory_order_release); | |
ef1b3fda | 655 | } |
dee5ea7a | 656 | return result; |
ef1b3fda KS |
657 | } |
658 | ||
3c6331c2 ML |
659 | DarwinKernelVersion GetDarwinKernelVersion() { |
660 | char buf[100]; | |
661 | size_t len = sizeof(buf); | |
662 | int res = internal_sysctlbyname("kern.osrelease", buf, &len, nullptr, 0); | |
663 | CHECK_EQ(res, 0); | |
664 | ||
665 | // Format: <major>.<minor>.<patch>\0 | |
666 | CHECK_GE(len, 6); | |
667 | const char *p = buf; | |
668 | u16 major = internal_simple_strtoll(p, &p, /*base=*/10); | |
669 | CHECK_EQ(*p, '.'); | |
670 | p += 1; | |
671 | u16 minor = internal_simple_strtoll(p, &p, /*base=*/10); | |
672 | ||
673 | return DarwinKernelVersion(major, minor); | |
5d3805fc JJ |
674 | } |
675 | ||
696d846a MO |
676 | uptr GetRSS() { |
677 | struct task_basic_info info; | |
678 | unsigned count = TASK_BASIC_INFO_COUNT; | |
679 | kern_return_t result = | |
680 | task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count); | |
681 | if (UNLIKELY(result != KERN_SUCCESS)) { | |
682 | Report("Cannot get task info. Error: %d\n", result); | |
683 | Die(); | |
684 | } | |
685 | return info.resident_size; | |
686 | } | |
687 | ||
3c6331c2 | 688 | void *internal_start_thread(void *(*func)(void *arg), void *arg) { |
55aea9f5 MO |
689 | // Start the thread with signals blocked, otherwise it can steal user signals. |
690 | __sanitizer_sigset_t set, old; | |
691 | internal_sigfillset(&set); | |
692 | internal_sigprocmask(SIG_SETMASK, &set, &old); | |
693 | pthread_t th; | |
3c6331c2 | 694 | pthread_create(&th, 0, func, arg); |
55aea9f5 MO |
695 | internal_sigprocmask(SIG_SETMASK, &old, 0); |
696 | return th; | |
697 | } | |
698 | ||
699 | void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); } | |
696d846a | 700 | |
10189819 MO |
701 | #if !SANITIZER_GO |
702 | static BlockingMutex syslog_lock(LINKER_INITIALIZED); | |
703 | #endif | |
704 | ||
705 | void WriteOneLineToSyslog(const char *s) { | |
706 | #if !SANITIZER_GO | |
707 | syslog_lock.CheckLocked(); | |
708 | asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s); | |
709 | #endif | |
710 | } | |
711 | ||
712 | void LogMessageOnPrintf(const char *str) { | |
713 | // Log all printf output to CrashLog. | |
714 | if (common_flags()->abort_on_error) | |
715 | CRAppendCrashLogMessage(str); | |
716 | } | |
717 | ||
718 | void LogFullErrorReport(const char *buffer) { | |
719 | #if !SANITIZER_GO | |
720 | // Log with os_trace. This will make it into the crash log. | |
721 | #if SANITIZER_OS_TRACE | |
722 | if (GetMacosVersion() >= MACOS_VERSION_YOSEMITE) { | |
723 | // os_trace requires the message (format parameter) to be a string literal. | |
724 | if (internal_strncmp(SanitizerToolName, "AddressSanitizer", | |
725 | sizeof("AddressSanitizer") - 1) == 0) | |
726 | os_trace("Address Sanitizer reported a failure."); | |
727 | else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer", | |
728 | sizeof("UndefinedBehaviorSanitizer") - 1) == 0) | |
729 | os_trace("Undefined Behavior Sanitizer reported a failure."); | |
730 | else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer", | |
731 | sizeof("ThreadSanitizer") - 1) == 0) | |
732 | os_trace("Thread Sanitizer reported a failure."); | |
733 | else | |
734 | os_trace("Sanitizer tool reported a failure."); | |
735 | ||
736 | if (common_flags()->log_to_syslog) | |
737 | os_trace("Consult syslog for more information."); | |
738 | } | |
739 | #endif | |
740 | ||
741 | // Log to syslog. | |
742 | // The logging on OS X may call pthread_create so we need the threading | |
743 | // environment to be fully initialized. Also, this should never be called when | |
744 | // holding the thread registry lock since that may result in a deadlock. If | |
745 | // the reporting thread holds the thread registry mutex, and asl_log waits | |
746 | // for GCD to dispatch a new thread, the process will deadlock, because the | |
747 | // pthread_create wrapper needs to acquire the lock as well. | |
748 | BlockingMutexLock l(&syslog_lock); | |
749 | if (common_flags()->log_to_syslog) | |
750 | WriteToSyslog(buffer); | |
751 | ||
752 | // The report is added to CrashLog as part of logging all of Printf output. | |
753 | #endif | |
754 | } | |
755 | ||
5d3805fc | 756 | SignalContext::WriteFlag SignalContext::GetWriteFlag() const { |
10189819 MO |
757 | #if defined(__x86_64__) || defined(__i386__) |
758 | ucontext_t *ucontext = static_cast<ucontext_t*>(context); | |
759 | return ucontext->uc_mcontext->__es.__err & 2 /*T_PF_WRITE*/ ? WRITE : READ; | |
760 | #else | |
761 | return UNKNOWN; | |
762 | #endif | |
763 | } | |
764 | ||
3ca75cd5 ML |
765 | bool SignalContext::IsTrueFaultingAddress() const { |
766 | auto si = static_cast<const siginfo_t *>(siginfo); | |
767 | // "Real" SIGSEGV codes (e.g., SEGV_MAPERR, SEGV_MAPERR) are non-zero. | |
768 | return si->si_signo == SIGSEGV && si->si_code != 0; | |
769 | } | |
770 | ||
3c6331c2 ML |
771 | #if defined(__aarch64__) && defined(arm_thread_state64_get_sp) |
772 | #define AARCH64_GET_REG(r) \ | |
773 | (uptr)ptrauth_strip( \ | |
774 | (void *)arm_thread_state64_get_##r(ucontext->uc_mcontext->__ss), 0) | |
775 | #else | |
776 | #define AARCH64_GET_REG(r) ucontext->uc_mcontext->__ss.__##r | |
777 | #endif | |
778 | ||
5d3805fc | 779 | static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { |
696d846a MO |
780 | ucontext_t *ucontext = (ucontext_t*)context; |
781 | # if defined(__aarch64__) | |
3c6331c2 | 782 | *pc = AARCH64_GET_REG(pc); |
696d846a | 783 | # if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 |
3c6331c2 | 784 | *bp = AARCH64_GET_REG(fp); |
696d846a | 785 | # else |
3c6331c2 | 786 | *bp = AARCH64_GET_REG(lr); |
696d846a | 787 | # endif |
3c6331c2 | 788 | *sp = AARCH64_GET_REG(sp); |
696d846a MO |
789 | # elif defined(__x86_64__) |
790 | *pc = ucontext->uc_mcontext->__ss.__rip; | |
791 | *bp = ucontext->uc_mcontext->__ss.__rbp; | |
792 | *sp = ucontext->uc_mcontext->__ss.__rsp; | |
793 | # elif defined(__arm__) | |
794 | *pc = ucontext->uc_mcontext->__ss.__pc; | |
795 | *bp = ucontext->uc_mcontext->__ss.__r[7]; | |
796 | *sp = ucontext->uc_mcontext->__ss.__sp; | |
797 | # elif defined(__i386__) | |
798 | *pc = ucontext->uc_mcontext->__ss.__eip; | |
799 | *bp = ucontext->uc_mcontext->__ss.__ebp; | |
800 | *sp = ucontext->uc_mcontext->__ss.__esp; | |
801 | # else | |
802 | # error "Unknown architecture" | |
803 | # endif | |
804 | } | |
805 | ||
3c6331c2 ML |
806 | void SignalContext::InitPcSpBp() { |
807 | addr = (uptr)ptrauth_strip((void *)addr, 0); | |
808 | GetPcSpBp(context, &pc, &sp, &bp); | |
809 | } | |
5d3805fc | 810 | |
b667dd70 | 811 | void InitializePlatformEarly() { |
3c6331c2 | 812 | // Only use xnu_fast_mmap when on x86_64 and the kernel supports it. |
b667dd70 ML |
813 | use_xnu_fast_mmap = |
814 | #if defined(__x86_64__) | |
3c6331c2 | 815 | GetDarwinKernelVersion() >= DarwinKernelVersion(17, 5); |
b667dd70 ML |
816 | #else |
817 | false; | |
818 | #endif | |
819 | } | |
820 | ||
10189819 MO |
821 | #if !SANITIZER_GO |
822 | static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES"; | |
823 | LowLevelAllocator allocator_for_env; | |
824 | ||
825 | // Change the value of the env var |name|, leaking the original value. | |
826 | // If |name_value| is NULL, the variable is deleted from the environment, | |
827 | // otherwise the corresponding "NAME=value" string is replaced with | |
828 | // |name_value|. | |
829 | void LeakyResetEnv(const char *name, const char *name_value) { | |
830 | char **env = GetEnviron(); | |
831 | uptr name_len = internal_strlen(name); | |
832 | while (*env != 0) { | |
833 | uptr len = internal_strlen(*env); | |
834 | if (len > name_len) { | |
835 | const char *p = *env; | |
836 | if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { | |
837 | // Match. | |
838 | if (name_value) { | |
839 | // Replace the old value with the new one. | |
840 | *env = const_cast<char*>(name_value); | |
841 | } else { | |
842 | // Shift the subsequent pointers back. | |
843 | char **del = env; | |
844 | do { | |
845 | del[0] = del[1]; | |
846 | } while (*del++); | |
847 | } | |
848 | } | |
849 | } | |
850 | env++; | |
851 | } | |
852 | } | |
853 | ||
854 | SANITIZER_WEAK_CXX_DEFAULT_IMPL | |
855 | bool ReexecDisabled() { | |
856 | return false; | |
857 | } | |
858 | ||
859 | extern "C" SANITIZER_WEAK_ATTRIBUTE double dyldVersionNumber; | |
860 | static const double kMinDyldVersionWithAutoInterposition = 360.0; | |
861 | ||
862 | bool DyldNeedsEnvVariable() { | |
863 | // Although sanitizer support was added to LLVM on OS X 10.7+, GCC users | |
864 | // still may want use them on older systems. On older Darwin platforms, dyld | |
865 | // doesn't export dyldVersionNumber symbol and we simply return true. | |
866 | if (!&dyldVersionNumber) return true; | |
867 | // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if | |
868 | // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via | |
869 | // GetMacosVersion() doesn't work for the simulator. Let's instead check | |
870 | // `dyldVersionNumber`, which is exported by dyld, against a known version | |
871 | // number from the first OS release where this appeared. | |
872 | return dyldVersionNumber < kMinDyldVersionWithAutoInterposition; | |
873 | } | |
874 | ||
875 | void MaybeReexec() { | |
eac97531 ML |
876 | // FIXME: This should really live in some "InitializePlatform" method. |
877 | MonotonicNanoTime(); | |
878 | ||
10189819 MO |
879 | if (ReexecDisabled()) return; |
880 | ||
881 | // Make sure the dynamic runtime library is preloaded so that the | |
882 | // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec | |
883 | // ourselves. | |
884 | Dl_info info; | |
885 | RAW_CHECK(dladdr((void*)((uptr)&__sanitizer_report_error_summary), &info)); | |
886 | char *dyld_insert_libraries = | |
887 | const_cast<char*>(GetEnv(kDyldInsertLibraries)); | |
888 | uptr old_env_len = dyld_insert_libraries ? | |
889 | internal_strlen(dyld_insert_libraries) : 0; | |
890 | uptr fname_len = internal_strlen(info.dli_fname); | |
891 | const char *dylib_name = StripModuleName(info.dli_fname); | |
892 | uptr dylib_name_len = internal_strlen(dylib_name); | |
893 | ||
894 | bool lib_is_in_env = dyld_insert_libraries && | |
895 | internal_strstr(dyld_insert_libraries, dylib_name); | |
896 | if (DyldNeedsEnvVariable() && !lib_is_in_env) { | |
897 | // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime | |
898 | // library. | |
899 | InternalScopedString program_name(1024); | |
900 | uint32_t buf_size = program_name.size(); | |
901 | _NSGetExecutablePath(program_name.data(), &buf_size); | |
902 | char *new_env = const_cast<char*>(info.dli_fname); | |
903 | if (dyld_insert_libraries) { | |
904 | // Append the runtime dylib name to the existing value of | |
905 | // DYLD_INSERT_LIBRARIES. | |
906 | new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2); | |
907 | internal_strncpy(new_env, dyld_insert_libraries, old_env_len); | |
908 | new_env[old_env_len] = ':'; | |
909 | // Copy fname_len and add a trailing zero. | |
910 | internal_strncpy(new_env + old_env_len + 1, info.dli_fname, | |
911 | fname_len + 1); | |
912 | // Ok to use setenv() since the wrappers don't depend on the value of | |
913 | // asan_inited. | |
914 | setenv(kDyldInsertLibraries, new_env, /*overwrite*/1); | |
915 | } else { | |
916 | // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. | |
917 | setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); | |
918 | } | |
919 | VReport(1, "exec()-ing the program with\n"); | |
920 | VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env); | |
921 | VReport(1, "to enable wrappers.\n"); | |
922 | execv(program_name.data(), *_NSGetArgv()); | |
923 | ||
924 | // We get here only if execv() failed. | |
925 | Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, " | |
926 | "which is required for the sanitizer to work. We tried to set the " | |
927 | "environment variable and re-execute itself, but execv() failed, " | |
928 | "possibly because of sandbox restrictions. Make sure to launch the " | |
929 | "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env); | |
930 | RAW_CHECK("execv failed" && 0); | |
931 | } | |
932 | ||
933 | // Verify that interceptors really work. We'll use dlsym to locate | |
934 | // "pthread_create", if interceptors are working, it should really point to | |
935 | // "wrap_pthread_create" within our own dylib. | |
936 | Dl_info info_pthread_create; | |
937 | void *dlopen_addr = dlsym(RTLD_DEFAULT, "pthread_create"); | |
938 | RAW_CHECK(dladdr(dlopen_addr, &info_pthread_create)); | |
939 | if (internal_strcmp(info.dli_fname, info_pthread_create.dli_fname) != 0) { | |
940 | Report( | |
941 | "ERROR: Interceptors are not working. This may be because %s is " | |
942 | "loaded too late (e.g. via dlopen). Please launch the executable " | |
943 | "with:\n%s=%s\n", | |
944 | SanitizerToolName, kDyldInsertLibraries, info.dli_fname); | |
945 | RAW_CHECK("interceptors not installed" && 0); | |
946 | } | |
947 | ||
948 | if (!lib_is_in_env) | |
949 | return; | |
950 | ||
eac97531 ML |
951 | if (!common_flags()->strip_env) |
952 | return; | |
953 | ||
10189819 MO |
954 | // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove |
955 | // the dylib from the environment variable, because interceptors are installed | |
956 | // and we don't want our children to inherit the variable. | |
957 | ||
958 | uptr env_name_len = internal_strlen(kDyldInsertLibraries); | |
959 | // Allocate memory to hold the previous env var name, its value, the '=' | |
960 | // sign and the '\0' char. | |
961 | char *new_env = (char*)allocator_for_env.Allocate( | |
962 | old_env_len + 2 + env_name_len); | |
963 | RAW_CHECK(new_env); | |
964 | internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); | |
965 | internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); | |
966 | new_env[env_name_len] = '='; | |
967 | char *new_env_pos = new_env + env_name_len + 1; | |
968 | ||
969 | // Iterate over colon-separated pieces of |dyld_insert_libraries|. | |
970 | char *piece_start = dyld_insert_libraries; | |
971 | char *piece_end = NULL; | |
972 | char *old_env_end = dyld_insert_libraries + old_env_len; | |
973 | do { | |
974 | if (piece_start[0] == ':') piece_start++; | |
975 | piece_end = internal_strchr(piece_start, ':'); | |
976 | if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; | |
977 | if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; | |
978 | uptr piece_len = piece_end - piece_start; | |
979 | ||
980 | char *filename_start = | |
981 | (char *)internal_memrchr(piece_start, '/', piece_len); | |
982 | uptr filename_len = piece_len; | |
983 | if (filename_start) { | |
984 | filename_start += 1; | |
985 | filename_len = piece_len - (filename_start - piece_start); | |
986 | } else { | |
987 | filename_start = piece_start; | |
988 | } | |
989 | ||
990 | // If the current piece isn't the runtime library name, | |
991 | // append it to new_env. | |
992 | if ((dylib_name_len != filename_len) || | |
993 | (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) { | |
994 | if (new_env_pos != new_env + env_name_len + 1) { | |
995 | new_env_pos[0] = ':'; | |
996 | new_env_pos++; | |
997 | } | |
998 | internal_strncpy(new_env_pos, piece_start, piece_len); | |
999 | new_env_pos += piece_len; | |
1000 | } | |
1001 | // Move on to the next piece. | |
1002 | piece_start = piece_end; | |
1003 | } while (piece_start < old_env_end); | |
1004 | ||
1005 | // Can't use setenv() here, because it requires the allocator to be | |
1006 | // initialized. | |
1007 | // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in | |
1008 | // a separate function called after InitializeAllocator(). | |
1009 | if (new_env_pos == new_env + env_name_len + 1) new_env = NULL; | |
1010 | LeakyResetEnv(kDyldInsertLibraries, new_env); | |
1011 | } | |
1012 | #endif // SANITIZER_GO | |
1013 | ||
1014 | char **GetArgv() { | |
1015 | return *_NSGetArgv(); | |
1016 | } | |
1017 | ||
b667dd70 | 1018 | #if SANITIZER_IOS |
5d3805fc JJ |
1019 | // The task_vm_info struct is normally provided by the macOS SDK, but we need |
1020 | // fields only available in 10.12+. Declare the struct manually to be able to | |
1021 | // build against older SDKs. | |
1022 | struct __sanitizer_task_vm_info { | |
1023 | mach_vm_size_t virtual_size; | |
1024 | integer_t region_count; | |
1025 | integer_t page_size; | |
1026 | mach_vm_size_t resident_size; | |
1027 | mach_vm_size_t resident_size_peak; | |
1028 | mach_vm_size_t device; | |
1029 | mach_vm_size_t device_peak; | |
1030 | mach_vm_size_t internal; | |
1031 | mach_vm_size_t internal_peak; | |
1032 | mach_vm_size_t external; | |
1033 | mach_vm_size_t external_peak; | |
1034 | mach_vm_size_t reusable; | |
1035 | mach_vm_size_t reusable_peak; | |
1036 | mach_vm_size_t purgeable_volatile_pmap; | |
1037 | mach_vm_size_t purgeable_volatile_resident; | |
1038 | mach_vm_size_t purgeable_volatile_virtual; | |
1039 | mach_vm_size_t compressed; | |
1040 | mach_vm_size_t compressed_peak; | |
1041 | mach_vm_size_t compressed_lifetime; | |
1042 | mach_vm_size_t phys_footprint; | |
1043 | mach_vm_address_t min_address; | |
1044 | mach_vm_address_t max_address; | |
1045 | }; | |
1046 | #define __SANITIZER_TASK_VM_INFO_COUNT ((mach_msg_type_number_t) \ | |
1047 | (sizeof(__sanitizer_task_vm_info) / sizeof(natural_t))) | |
1048 | ||
b667dd70 | 1049 | static uptr GetTaskInfoMaxAddress() { |
eac97531 | 1050 | __sanitizer_task_vm_info vm_info = {} /* zero initialize */; |
5d3805fc JJ |
1051 | mach_msg_type_number_t count = __SANITIZER_TASK_VM_INFO_COUNT; |
1052 | int err = task_info(mach_task_self(), TASK_VM_INFO, (int *)&vm_info, &count); | |
b667dd70 | 1053 | return err ? 0 : vm_info.max_address; |
5d3805fc | 1054 | } |
5d3805fc | 1055 | |
eac97531 | 1056 | uptr GetMaxUserVirtualAddress() { |
5d3805fc | 1057 | static uptr max_vm = GetTaskInfoMaxAddress(); |
b667dd70 ML |
1058 | if (max_vm != 0) |
1059 | return max_vm - 1; | |
1060 | ||
1061 | // xnu cannot provide vm address limit | |
1062 | # if SANITIZER_WORDSIZE == 32 | |
1063 | return 0xffe00000 - 1; | |
5d3805fc | 1064 | # else |
b667dd70 | 1065 | return 0x200000000 - 1; |
5d3805fc | 1066 | # endif |
b667dd70 ML |
1067 | } |
1068 | ||
1069 | #else // !SANITIZER_IOS | |
1070 | ||
1071 | uptr GetMaxUserVirtualAddress() { | |
1072 | # if SANITIZER_WORDSIZE == 64 | |
1073 | return (1ULL << 47) - 1; // 0x00007fffffffffffUL; | |
1074 | # else // SANITIZER_WORDSIZE == 32 | |
1075 | static_assert(SANITIZER_WORDSIZE == 32, "Wrong wordsize"); | |
5d3805fc | 1076 | return (1ULL << 32) - 1; // 0xffffffff; |
b667dd70 | 1077 | # endif |
5d3805fc | 1078 | } |
b667dd70 | 1079 | #endif |
5d3805fc | 1080 | |
eac97531 ML |
1081 | uptr GetMaxVirtualAddress() { |
1082 | return GetMaxUserVirtualAddress(); | |
1083 | } | |
1084 | ||
1085 | uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding, | |
1086 | uptr *largest_gap_found, | |
1087 | uptr *max_occupied_addr) { | |
10189819 MO |
1088 | typedef vm_region_submap_short_info_data_64_t RegionInfo; |
1089 | enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 }; | |
1090 | // Start searching for available memory region past PAGEZERO, which is | |
1091 | // 4KB on 32-bit and 4GB on 64-bit. | |
1092 | mach_vm_address_t start_address = | |
1093 | (SANITIZER_WORDSIZE == 32) ? 0x000000001000 : 0x000100000000; | |
1094 | ||
1095 | mach_vm_address_t address = start_address; | |
1096 | mach_vm_address_t free_begin = start_address; | |
1097 | kern_return_t kr = KERN_SUCCESS; | |
5d3805fc | 1098 | if (largest_gap_found) *largest_gap_found = 0; |
eac97531 | 1099 | if (max_occupied_addr) *max_occupied_addr = 0; |
10189819 MO |
1100 | while (kr == KERN_SUCCESS) { |
1101 | mach_vm_size_t vmsize = 0; | |
1102 | natural_t depth = 0; | |
1103 | RegionInfo vminfo; | |
1104 | mach_msg_type_number_t count = kRegionInfoSize; | |
1105 | kr = mach_vm_region_recurse(mach_task_self(), &address, &vmsize, &depth, | |
1106 | (vm_region_info_t)&vminfo, &count); | |
eac97531 ML |
1107 | if (kr == KERN_INVALID_ADDRESS) { |
1108 | // No more regions beyond "address", consider the gap at the end of VM. | |
1109 | address = GetMaxVirtualAddress() + 1; | |
1110 | vmsize = 0; | |
1111 | } else { | |
1112 | if (max_occupied_addr) *max_occupied_addr = address + vmsize; | |
1113 | } | |
10189819 MO |
1114 | if (free_begin != address) { |
1115 | // We found a free region [free_begin..address-1]. | |
5d3805fc JJ |
1116 | uptr gap_start = RoundUpTo((uptr)free_begin + left_padding, alignment); |
1117 | uptr gap_end = RoundDownTo((uptr)address, alignment); | |
1118 | uptr gap_size = gap_end > gap_start ? gap_end - gap_start : 0; | |
eac97531 | 1119 | if (size < gap_size) { |
5d3805fc JJ |
1120 | return gap_start; |
1121 | } | |
1122 | ||
1123 | if (largest_gap_found && *largest_gap_found < gap_size) { | |
1124 | *largest_gap_found = gap_size; | |
10189819 MO |
1125 | } |
1126 | } | |
1127 | // Move to the next region. | |
1128 | address += vmsize; | |
1129 | free_begin = address; | |
1130 | } | |
1131 | ||
1132 | // We looked at all free regions and could not find one large enough. | |
1133 | return 0; | |
1134 | } | |
1135 | ||
1136 | // FIXME implement on this platform. | |
1137 | void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { } | |
1138 | ||
5d3805fc JJ |
1139 | void SignalContext::DumpAllRegisters(void *context) { |
1140 | Report("Register values:\n"); | |
1141 | ||
1142 | ucontext_t *ucontext = (ucontext_t*)context; | |
1143 | # define DUMPREG64(r) \ | |
1144 | Printf("%s = 0x%016llx ", #r, ucontext->uc_mcontext->__ss.__ ## r); | |
3c6331c2 ML |
1145 | # define DUMPREGA64(r) \ |
1146 | Printf(" %s = 0x%016llx ", #r, AARCH64_GET_REG(r)); | |
5d3805fc JJ |
1147 | # define DUMPREG32(r) \ |
1148 | Printf("%s = 0x%08x ", #r, ucontext->uc_mcontext->__ss.__ ## r); | |
1149 | # define DUMPREG_(r) Printf(" "); DUMPREG(r); | |
1150 | # define DUMPREG__(r) Printf(" "); DUMPREG(r); | |
1151 | # define DUMPREG___(r) Printf(" "); DUMPREG(r); | |
1152 | ||
1153 | # if defined(__x86_64__) | |
1154 | # define DUMPREG(r) DUMPREG64(r) | |
1155 | DUMPREG(rax); DUMPREG(rbx); DUMPREG(rcx); DUMPREG(rdx); Printf("\n"); | |
1156 | DUMPREG(rdi); DUMPREG(rsi); DUMPREG(rbp); DUMPREG(rsp); Printf("\n"); | |
1157 | DUMPREG_(r8); DUMPREG_(r9); DUMPREG(r10); DUMPREG(r11); Printf("\n"); | |
1158 | DUMPREG(r12); DUMPREG(r13); DUMPREG(r14); DUMPREG(r15); Printf("\n"); | |
1159 | # elif defined(__i386__) | |
1160 | # define DUMPREG(r) DUMPREG32(r) | |
1161 | DUMPREG(eax); DUMPREG(ebx); DUMPREG(ecx); DUMPREG(edx); Printf("\n"); | |
1162 | DUMPREG(edi); DUMPREG(esi); DUMPREG(ebp); DUMPREG(esp); Printf("\n"); | |
1163 | # elif defined(__aarch64__) | |
1164 | # define DUMPREG(r) DUMPREG64(r) | |
1165 | DUMPREG_(x[0]); DUMPREG_(x[1]); DUMPREG_(x[2]); DUMPREG_(x[3]); Printf("\n"); | |
1166 | DUMPREG_(x[4]); DUMPREG_(x[5]); DUMPREG_(x[6]); DUMPREG_(x[7]); Printf("\n"); | |
1167 | DUMPREG_(x[8]); DUMPREG_(x[9]); DUMPREG(x[10]); DUMPREG(x[11]); Printf("\n"); | |
1168 | DUMPREG(x[12]); DUMPREG(x[13]); DUMPREG(x[14]); DUMPREG(x[15]); Printf("\n"); | |
1169 | DUMPREG(x[16]); DUMPREG(x[17]); DUMPREG(x[18]); DUMPREG(x[19]); Printf("\n"); | |
1170 | DUMPREG(x[20]); DUMPREG(x[21]); DUMPREG(x[22]); DUMPREG(x[23]); Printf("\n"); | |
1171 | DUMPREG(x[24]); DUMPREG(x[25]); DUMPREG(x[26]); DUMPREG(x[27]); Printf("\n"); | |
3c6331c2 | 1172 | DUMPREG(x[28]); DUMPREGA64(fp); DUMPREGA64(lr); DUMPREGA64(sp); Printf("\n"); |
5d3805fc JJ |
1173 | # elif defined(__arm__) |
1174 | # define DUMPREG(r) DUMPREG32(r) | |
1175 | DUMPREG_(r[0]); DUMPREG_(r[1]); DUMPREG_(r[2]); DUMPREG_(r[3]); Printf("\n"); | |
1176 | DUMPREG_(r[4]); DUMPREG_(r[5]); DUMPREG_(r[6]); DUMPREG_(r[7]); Printf("\n"); | |
1177 | DUMPREG_(r[8]); DUMPREG_(r[9]); DUMPREG(r[10]); DUMPREG(r[11]); Printf("\n"); | |
1178 | DUMPREG(r[12]); DUMPREG___(sp); DUMPREG___(lr); DUMPREG___(pc); Printf("\n"); | |
1179 | # else | |
1180 | # error "Unknown architecture" | |
1181 | # endif | |
1182 | ||
1183 | # undef DUMPREG64 | |
1184 | # undef DUMPREG32 | |
1185 | # undef DUMPREG_ | |
1186 | # undef DUMPREG__ | |
1187 | # undef DUMPREG___ | |
1188 | # undef DUMPREG | |
1189 | } | |
1190 | ||
1191 | static inline bool CompareBaseAddress(const LoadedModule &a, | |
1192 | const LoadedModule &b) { | |
1193 | return a.base_address() < b.base_address(); | |
1194 | } | |
1195 | ||
1196 | void FormatUUID(char *out, uptr size, const u8 *uuid) { | |
1197 | internal_snprintf(out, size, | |
1198 | "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-" | |
1199 | "%02X%02X%02X%02X%02X%02X>", | |
1200 | uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], | |
1201 | uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], | |
1202 | uuid[12], uuid[13], uuid[14], uuid[15]); | |
1203 | } | |
1204 | ||
1205 | void PrintModuleMap() { | |
1206 | Printf("Process module map:\n"); | |
1207 | MemoryMappingLayout memory_mapping(false); | |
eac97531 ML |
1208 | InternalMmapVector<LoadedModule> modules; |
1209 | modules.reserve(128); | |
5d3805fc | 1210 | memory_mapping.DumpListOfModules(&modules); |
eac97531 | 1211 | Sort(modules.data(), modules.size(), CompareBaseAddress); |
5d3805fc JJ |
1212 | for (uptr i = 0; i < modules.size(); ++i) { |
1213 | char uuid_str[128]; | |
1214 | FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid()); | |
1215 | Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(), | |
1216 | modules[i].max_executable_address(), modules[i].full_name(), | |
1217 | ModuleArchToString(modules[i].arch()), uuid_str); | |
1218 | } | |
1219 | Printf("End of module map.\n"); | |
1220 | } | |
1221 | ||
1222 | void CheckNoDeepBind(const char *filename, int flag) { | |
1223 | // Do nothing. | |
1224 | } | |
1225 | ||
5d3805fc | 1226 | bool GetRandom(void *buffer, uptr length, bool blocking) { |
eac97531 ML |
1227 | if (!buffer || !length || length > 256) |
1228 | return false; | |
1229 | // arc4random never fails. | |
3ca75cd5 | 1230 | REAL(arc4random_buf)(buffer, length); |
eac97531 ML |
1231 | return true; |
1232 | } | |
1233 | ||
1234 | u32 GetNumberOfCPUs() { | |
1235 | return (u32)sysconf(_SC_NPROCESSORS_ONLN); | |
5d3805fc JJ |
1236 | } |
1237 | ||
f35db108 WM |
1238 | } // namespace __sanitizer |
1239 | ||
ef1b3fda | 1240 | #endif // SANITIZER_MAC |