1//===-- sanitizer_mac.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 shared between various sanitizers' runtime libraries and
10// implements OSX-specific functions.
11//===----------------------------------------------------------------------===//
12
13#include "sanitizer_platform.h"
14#if SANITIZER_APPLE
15# include "interception/interception.h"
16# include "sanitizer_mac.h"
17
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>
24
25// Start searching for available memory region past PAGEZERO, which is
26// 4KB on 32-bit and 4GB on 64-bit.
27# define GAP_SEARCH_START_ADDRESS \
28 ((SANITIZER_WORDSIZE == 32) ? 0x000000001000 : 0x000100000000)
29
30# include "sanitizer_allocator_internal.h"
31# include "sanitizer_common.h"
32# include "sanitizer_file.h"
33# include "sanitizer_flags.h"
34# include "sanitizer_interface_internal.h"
35# include "sanitizer_internal_defs.h"
36# include "sanitizer_libc.h"
37# include "sanitizer_platform_limits_posix.h"
38# include "sanitizer_procmaps.h"
39# include "sanitizer_ptrauth.h"
40
41# if !SANITIZER_IOS
42# include <crt_externs.h> // for _NSGetEnviron
43# else
44extern char **environ;
45# endif
46
47// Integrate with CrashReporter library if available
48# if defined(__has_include) && __has_include(<CrashReporterClient.h>)
49# define HAVE_CRASHREPORTERCLIENT_H 1
50# include <CrashReporterClient.h>
51# else
52# define HAVE_CRASHREPORTERCLIENT_H 0
53# endif
54
55# if !SANITIZER_IOS
56# include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
57# else
58extern "C" {
59extern char ***_NSGetArgv(void);
60}
61# endif
62
63# include <asl.h>
64# include <dlfcn.h> // for dladdr()
65# include <errno.h>
66# include <fcntl.h>
67# include <inttypes.h>
68# include <libkern/OSAtomic.h>
69# include <mach-o/dyld.h>
70# include <mach/mach.h>
71# include <mach/mach_error.h>
72# include <mach/mach_time.h>
73# include <mach/vm_statistics.h>
74# include <malloc/malloc.h>
75# include <os/log.h>
76# include <pthread.h>
77# include <pthread/introspection.h>
78# include <sched.h>
79# include <signal.h>
80# include <spawn.h>
81# include <stdlib.h>
82# include <sys/ioctl.h>
83# include <sys/mman.h>
84# include <sys/resource.h>
85# include <sys/stat.h>
86# include <sys/sysctl.h>
87# include <sys/types.h>
88# include <sys/wait.h>
89# include <unistd.h>
90# include <util.h>
91
92// From <crt_externs.h>, but we don't have that file on iOS.
93extern "C" {
94 extern char ***_NSGetArgv(void);
95 extern char ***_NSGetEnviron(void);
96}
97
98// From <mach/mach_vm.h>, but we don't have that file on iOS.
99extern "C" {
100extern kern_return_t mach_vm_region_recurse(vm_map_t target_task,
101 mach_vm_address_t* address,
102 mach_vm_size_t* size,
103 natural_t* nesting_depth,
104 vm_region_recurse_info_t info,
105 mach_msg_type_number_t* infoCnt);
106
107extern const void* _dyld_get_shared_cache_range(size_t* length);
108}
109
110# if !SANITIZER_GO
111// Weak symbol no-op when TSan is not linked
112SANITIZER_WEAK_ATTRIBUTE extern void __tsan_set_in_internal_write_call(
113 bool value) {}
114# endif
115
116namespace __sanitizer {
117
118#include "sanitizer_syscall_generic.inc"
119
120// Direct syscalls, don't call libmalloc hooks (but not available on 10.6).
121extern "C" void *__mmap(void *addr, size_t len, int prot, int flags, int fildes,
122 off_t off) SANITIZER_WEAK_ATTRIBUTE;
123extern "C" int __munmap(void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
124
125// ---------------------- sanitizer_libc.h
126
127// From <mach/vm_statistics.h>, but not on older OSs.
128#ifndef VM_MEMORY_SANITIZER
129#define VM_MEMORY_SANITIZER 99
130#endif
131# ifndef VM_MEMORY_DEBUG
132# define VM_MEMORY_DEBUG 108
133# endif
134
135// XNU on Darwin provides a mmap flag that optimizes allocation/deallocation of
136// giant memory regions (i.e. shadow memory regions).
137#define kXnuFastMmapFd 0x4
138static size_t kXnuFastMmapThreshold = 2 << 30; // 2 GB
139static bool use_xnu_fast_mmap = false;
140bool debug_region_activated = false;
141
142uptr internal_mmap(void* addr_hint, size_t length, int prot, int flags, int fd,
143 u64 offset) {
144 bool use_debug_vm = false;
145 if (fd == -1) {
146 use_debug_vm = (debug_region_activated &&
147 (uptr)addr_hint >= DARWIN_DEBUG_MEMORY_START);
148 fd = VM_MAKE_TAG(use_debug_vm ? VM_MEMORY_DEBUG : VM_MEMORY_SANITIZER);
149 if (length >= kXnuFastMmapThreshold) {
150 if (use_xnu_fast_mmap) fd |= kXnuFastMmapFd;
151 }
152 }
153
154 auto mmap_fn = (&__mmap ? __mmap : mmap);
155 uptr addr = (uptr)mmap_fn(addr_hint, length, prot, flags, fd, offset);
156
157 if (addr == (uptr)MAP_FAILED)
158 return (uptr)MAP_FAILED;
159
160 // If mmap succeeded, check that the return value is within
161 // the expected range.
162 if (use_debug_vm) {
163 // Allocations from the debug region should be >= DARWIN_DEBUG_MEMORY_START
164 if (addr < DARWIN_DEBUG_MEMORY_START) {
165 Report(
166 "FATAL: Unsupported VM layout. mmap returned %p, Expected debug "
167 "memory >= %p\n",
168 addr, DARWIN_DEBUG_MEMORY_START);
169 Die();
170 }
171 } else {
172 // Allocations NOT from the debug region should be inside the range given by
173 // SANITIZER_MMAP_BEGIN and SANITIZER_MMAP_RANGE_SIZE
174 if (addr < SANITIZER_MMAP_BEGIN ||
175 addr + length > SANITIZER_MMAP_BEGIN + SANITIZER_MMAP_RANGE_SIZE) {
176 Report(
177 "FATAL: Unsupported VM layout. mmap returned %p, expected [%p, %p]\n",
178 addr, SANITIZER_MMAP_BEGIN,
179 SANITIZER_MMAP_BEGIN + SANITIZER_MMAP_RANGE_SIZE);
180 Die();
181 }
182 }
183
184 return addr;
185}
186
187uptr internal_munmap(void *addr, uptr length) {
188 if (&__munmap) return __munmap(addr, length);
189 return munmap(addr, length);
190}
191
192uptr internal_mremap(void *old_address, uptr old_size, uptr new_size, int flags,
193 void *new_address) {
194 CHECK(false && "internal_mremap is unimplemented on Mac");
195 return 0;
196}
197
198int internal_mprotect(void *addr, uptr length, int prot) {
199 return mprotect(addr, length, prot);
200}
201
202int internal_madvise(uptr addr, uptr length, int advice) {
203 return madvise((void *)addr, length, advice);
204}
205
206uptr internal_close(fd_t fd) {
207 return close(fd);
208}
209
210uptr internal_close_range(fd_t lowfd, fd_t highfd, int flags) {
211 return -1; // Not supported.
212}
213
214uptr internal_open(const char *filename, int flags) {
215 return open(filename, flags);
216}
217
218uptr internal_open(const char *filename, int flags, u32 mode) {
219 return open(filename, flags, mode);
220}
221
222uptr internal_read(fd_t fd, void *buf, uptr count) {
223 return read(fd, buf, count);
224}
225
226uptr internal_write(fd_t fd, const void *buf, uptr count) {
227# if SANITIZER_GO
228 return write(fd, buf, count);
229# else
230 // We need to disable interceptors when writing in TSan
231 __tsan_set_in_internal_write_call(true);
232 uptr res = write(fd, buf, count);
233 __tsan_set_in_internal_write_call(false);
234 return res;
235# endif
236}
237
238uptr internal_stat(const char *path, void *buf) {
239 return stat(path, (struct stat *)buf);
240}
241
242uptr internal_lstat(const char *path, void *buf) {
243 return lstat(path, (struct stat *)buf);
244}
245
246uptr internal_fstat(fd_t fd, void *buf) {
247 return fstat(fd, (struct stat *)buf);
248}
249
250uptr internal_filesize(fd_t fd) {
251 struct stat st;
252 if (internal_fstat(fd, &st))
253 return -1;
254 return (uptr)st.st_size;
255}
256
257uptr internal_dup(int oldfd) {
258 return dup(oldfd);
259}
260
261uptr internal_dup2(int oldfd, int newfd) {
262 return dup2(oldfd, newfd);
263}
264
265uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
266 return readlink(path, buf, bufsize);
267}
268
269uptr internal_unlink(const char *path) {
270 return unlink(path);
271}
272
273uptr internal_sched_yield() {
274 return sched_yield();
275}
276
277void internal__exit(int exitcode) {
278 _exit(exitcode);
279}
280
281void internal_usleep(u64 useconds) { usleep(useconds); }
282
283uptr internal_getpid() {
284 return getpid();
285}
286
287int internal_dlinfo(void *handle, int request, void *p) {
288 UNIMPLEMENTED();
289}
290
291int internal_sigaction(int signum, const void *act, void *oldact) {
292 return sigaction(signum,
293 (const struct sigaction *)act, (struct sigaction *)oldact);
294}
295
296void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); }
297
298uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
299 __sanitizer_sigset_t *oldset) {
300 // Don't use sigprocmask here, because it affects all threads.
301 return pthread_sigmask(how, set, oldset);
302}
303
304// Doesn't call pthread_atfork() handlers (but not available on 10.6).
305extern "C" pid_t __fork(void) SANITIZER_WEAK_ATTRIBUTE;
306
307int internal_fork() {
308 if (&__fork)
309 return __fork();
310 return fork();
311}
312
313int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
314 uptr *oldlenp, const void *newp, uptr newlen) {
315 return sysctl(const_cast<int *>(name), namelen, oldp, (size_t *)oldlenp,
316 const_cast<void *>(newp), (size_t)newlen);
317}
318
319int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
320 const void *newp, uptr newlen) {
321 return sysctlbyname(sname, oldp, (size_t *)oldlenp, const_cast<void *>(newp),
322 (size_t)newlen);
323}
324
325bool internal_spawn(const char* argv[], const char* envp[], pid_t* pid,
326 fd_t fd_stdin, fd_t fd_stdout) {
327 // NOTE: Caller ensures that fd_stdin and fd_stdout are not 0, 1, or 2, since
328 // this can break communication.
329 //
330 // NOTE: Caller is responsible for closing fd_stdin after the process has
331 // died.
332
333 int res;
334 auto fd_closer = at_scope_exit([&] {
335 // NOTE: We intentionally do not close fd_stdin since this can
336 // cause us to receive a fatal SIGPIPE if the process dies.
337 internal_close(fd_stdout);
338 });
339
340 // File descriptor actions
341 posix_spawn_file_actions_t acts;
342 res = posix_spawn_file_actions_init(&acts);
343 if (res != 0)
344 return false;
345
346 auto acts_cleanup = at_scope_exit([&] {
347 posix_spawn_file_actions_destroy(&acts);
348 });
349
350 res = posix_spawn_file_actions_adddup2(&acts, fd_stdin, STDIN_FILENO) ||
351 posix_spawn_file_actions_adddup2(&acts, fd_stdout, STDOUT_FILENO) ||
352 posix_spawn_file_actions_addclose(&acts, fd_stdin) ||
353 posix_spawn_file_actions_addclose(&acts, fd_stdout);
354 if (res != 0)
355 return false;
356
357 // Spawn attributes
358 posix_spawnattr_t attrs;
359 res = posix_spawnattr_init(&attrs);
360 if (res != 0)
361 return false;
362
363 auto attrs_cleanup = at_scope_exit([&] {
364 posix_spawnattr_destroy(&attrs);
365 });
366
367 // In the spawned process, close all file descriptors that are not explicitly
368 // described by the file actions object. This is Darwin-specific extension.
369 res = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_CLOEXEC_DEFAULT);
370 if (res != 0)
371 return false;
372
373 // posix_spawn
374 char **argv_casted = const_cast<char **>(argv);
375 char **envp_casted = const_cast<char **>(envp);
376 res = posix_spawn(pid, argv[0], &acts, &attrs, argv_casted, envp_casted);
377 if (res != 0)
378 return false;
379
380 return true;
381}
382
383uptr internal_rename(const char *oldpath, const char *newpath) {
384 return rename(oldpath, newpath);
385}
386
387uptr internal_ftruncate(fd_t fd, uptr size) {
388 return ftruncate(fd, size);
389}
390
391uptr internal_execve(const char *filename, char *const argv[],
392 char *const envp[]) {
393 return execve(filename, argv, envp);
394}
395
396uptr internal_waitpid(int pid, int *status, int options) {
397 return waitpid(pid, status, options);
398}
399
400kern_return_t internal_mach_vm_region_recurse(vm_map_t target_task,
401 mach_vm_address_t* address,
402 mach_vm_size_t* size,
403 natural_t* nesting_depth,
404 vm_region_recurse_info_t info,
405 mach_msg_type_number_t* infoCnt) {
406 kern_return_t kr = mach_vm_region_recurse(target_task, address, size,
407 nesting_depth, info, infoCnt);
408 if (kr == KERN_DENIED) {
409 Report(
410 "FATAL: mach_vm_region_recurse returned KERN_DENIED when checking "
411 "whether an address is mapped.\n");
412 Report("HINT: Is mach_vm_region_recurse allowed by sandbox?\n");
413 Die();
414 }
415 return kr;
416}
417
418// ----------------- sanitizer_common.h
419bool FileExists(const char *filename) {
420 if (ShouldMockFailureToOpen(filename))
421 return false;
422 struct stat st;
423 if (stat(filename, &st))
424 return false;
425 // Sanity check: filename is a regular file.
426 return S_ISREG(st.st_mode);
427}
428
429bool DirExists(const char *path) {
430 struct stat st;
431 if (stat(path, &st))
432 return false;
433 return S_ISDIR(st.st_mode);
434}
435
436ThreadID GetTid() {
437 ThreadID tid;
438 pthread_threadid_np(nullptr, &tid);
439 return tid;
440}
441
442void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
443 uptr *stack_bottom) {
444 CHECK(stack_top);
445 CHECK(stack_bottom);
446 uptr stacksize = pthread_get_stacksize_np(pthread_self());
447 // pthread_get_stacksize_np() returns an incorrect stack size for the main
448 // thread on Mavericks. See
449 // https://github.com/google/sanitizers/issues/261
450 if ((GetMacosAlignedVersion() >= MacosVersion(10, 9)) && at_initialization &&
451 stacksize == (1 << 19)) {
452 struct rlimit rl;
453 CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
454 // Most often rl.rlim_cur will be the desired 8M.
455 if (rl.rlim_cur < kMaxThreadStackSize) {
456 stacksize = rl.rlim_cur;
457 } else {
458 stacksize = kMaxThreadStackSize;
459 }
460 }
461 void *stackaddr = pthread_get_stackaddr_np(pthread_self());
462 *stack_top = (uptr)stackaddr;
463 *stack_bottom = *stack_top - stacksize;
464}
465
466char **GetEnviron() {
467#if !SANITIZER_IOS
468 char ***env_ptr = _NSGetEnviron();
469 if (!env_ptr) {
470 Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
471 "called after libSystem_initializer().\n");
472 CHECK(env_ptr);
473 }
474 char **environ = *env_ptr;
475#endif
476 CHECK(environ);
477 return environ;
478}
479
480const char *GetEnv(const char *name) {
481 char **env = GetEnviron();
482 uptr name_len = internal_strlen(name);
483 while (*env != 0) {
484 uptr len = internal_strlen(*env);
485 if (len > name_len) {
486 const char *p = *env;
487 if (!internal_memcmp(p, name, name_len) &&
488 p[name_len] == '=') { // Match.
489 return *env + name_len + 1; // String starting after =.
490 }
491 }
492 env++;
493 }
494 return 0;
495}
496
497uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
498 CHECK_LE(kMaxPathLength, buf_len);
499
500 // On OS X the executable path is saved to the stack by dyld. Reading it
501 // from there is much faster than calling dladdr, especially for large
502 // binaries with symbols.
503 InternalMmapVector<char> exe_path(kMaxPathLength);
504 uint32_t size = exe_path.size();
505 if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
506 realpath(exe_path.data(), buf) != 0) {
507 return internal_strlen(buf);
508 }
509 return 0;
510}
511
512uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
513 return ReadBinaryName(buf, buf_len);
514}
515
516void ReExec() {
517 UNIMPLEMENTED();
518}
519
520void CheckASLR() {
521 // Do nothing
522}
523
524void CheckMPROTECT() {
525 // Do nothing
526}
527
528uptr GetPageSize() {
529 return sysconf(_SC_PAGESIZE);
530}
531
532extern "C" unsigned malloc_num_zones;
533extern "C" malloc_zone_t **malloc_zones;
534malloc_zone_t sanitizer_zone;
535
536// We need to make sure that sanitizer_zone is registered as malloc_zones[0]. If
537// libmalloc tries to set up a different zone as malloc_zones[0], it will call
538// mprotect(malloc_zones, ..., PROT_READ). This interceptor will catch that and
539// make sure we are still the first (default) zone.
540void MprotectMallocZones(void *addr, int prot) {
541 if (addr == malloc_zones && prot == PROT_READ) {
542 if (malloc_num_zones > 1 && malloc_zones[0] != &sanitizer_zone) {
543 for (unsigned i = 1; i < malloc_num_zones; i++) {
544 if (malloc_zones[i] == &sanitizer_zone) {
545 // Swap malloc_zones[0] and malloc_zones[i].
546 malloc_zones[i] = malloc_zones[0];
547 malloc_zones[0] = &sanitizer_zone;
548 break;
549 }
550 }
551 }
552 }
553}
554
555void FutexWait(atomic_uint32_t *p, u32 cmp) {
556 // FIXME: implement actual blocking.
557 sched_yield();
558}
559
560void FutexWake(atomic_uint32_t *p, u32 count) {}
561
562u64 NanoTime() {
563 timeval tv;
564 internal_memset(&tv, 0, sizeof(tv));
565 gettimeofday(&tv, 0);
566 return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
567}
568
569// This needs to be called during initialization to avoid being racy.
570u64 MonotonicNanoTime() {
571 static mach_timebase_info_data_t timebase_info;
572 if (timebase_info.denom == 0) mach_timebase_info(&timebase_info);
573 return (mach_absolute_time() * timebase_info.numer) / timebase_info.denom;
574}
575
576uptr GetTlsSize() {
577 return 0;
578}
579
580uptr TlsBaseAddr() {
581 uptr segbase = 0;
582#if defined(__x86_64__)
583 asm("movq %%gs:0,%0" : "=r"(segbase));
584#elif defined(__i386__)
585 asm("movl %%gs:0,%0" : "=r"(segbase));
586#elif defined(__aarch64__)
587 asm("mrs %x0, tpidrro_el0" : "=r"(segbase));
588 segbase &= 0x07ul; // clearing lower bits, cpu id stored there
589#endif
590 return segbase;
591}
592
593// The size of the tls on darwin does not appear to be well documented,
594// however the vm memory map suggests that it is 1024 uptrs in size,
595// with a size of 0x2000 bytes on x86_64 and 0x1000 bytes on i386.
596uptr TlsSize() {
597#if defined(__x86_64__) || defined(__i386__)
598 return 1024 * sizeof(uptr);
599#else
600 return 0;
601#endif
602}
603
604void GetThreadStackAndTls(bool main, uptr *stk_begin, uptr *stk_end,
605 uptr *tls_begin, uptr *tls_end) {
606# if !SANITIZER_GO
607 GetThreadStackTopAndBottom(main, stk_end, stk_begin);
608 *tls_begin = TlsBaseAddr();
609 *tls_end = *tls_begin + TlsSize();
610# else
611 *stk_begin = 0;
612 *stk_end = 0;
613 *tls_begin = 0;
614 *tls_end = 0;
615# endif
616}
617
618void ListOfModules::init() {
619 clearOrInit();
620 MemoryMappingLayout memory_mapping(false);
621 memory_mapping.DumpListOfModules(&modules_);
622}
623
624void ListOfModules::fallbackInit() { clear(); }
625
626static HandleSignalMode GetHandleSignalModeImpl(int signum) {
627 switch (signum) {
628 case SIGABRT:
629 return common_flags()->handle_abort;
630 case SIGILL:
631 return common_flags()->handle_sigill;
632 case SIGTRAP:
633 return common_flags()->handle_sigtrap;
634 case SIGFPE:
635 return common_flags()->handle_sigfpe;
636 case SIGSEGV:
637 return common_flags()->handle_segv;
638 case SIGBUS:
639 return common_flags()->handle_sigbus;
640 }
641 return kHandleSignalNo;
642}
643
644HandleSignalMode GetHandleSignalMode(int signum) {
645 // Handling fatal signals on watchOS and tvOS devices is disallowed.
646 if ((SANITIZER_WATCHOS || SANITIZER_TVOS) && !(SANITIZER_IOSSIM))
647 return kHandleSignalNo;
648 HandleSignalMode result = GetHandleSignalModeImpl(signum);
649 if (result == kHandleSignalYes && !common_flags()->allow_user_segv_handler)
650 return kHandleSignalExclusive;
651 return result;
652}
653
654// Offset example:
655// XNU 17 -- macOS 10.13 -- iOS 11 -- tvOS 11 -- watchOS 4
656constexpr u16 GetOSMajorKernelOffset() {
657 if (TARGET_OS_OSX)
658 return 4;
659 if (TARGET_OS_IOS || TARGET_OS_TV)
660 return 6;
661 if (TARGET_OS_WATCH)
662 return 13;
663 if (TARGET_OS_VISION)
664 return 22;
665}
666
667using VersStr = char[64];
668
669static uptr ApproximateOSVersionViaKernelVersion(VersStr vers) {
670 u16 kernel_major = GetDarwinKernelVersion().major;
671 u16 offset = GetOSMajorKernelOffset();
672 CHECK_GE(kernel_major, offset);
673 u16 os_major = kernel_major - offset;
674
675 const char *format = "%d.0";
676 if (kernel_major >= 27) {
677 // with kernel major 27 <=> OSes 27.0, OS versions are aligned with kernel
678 os_major = kernel_major;
679 } else if (kernel_major >= 25) {
680 // with kernel_major 25 <=> OSes 26.0, OS versions are aligned
681 os_major = kernel_major + 1;
682 } else if (TARGET_OS_OSX) {
683 if (os_major >= 16) { // macOS 11+
684 os_major -= 5;
685 } else { // macOS 10.15 and below
686 format = "10.%d";
687 }
688 }
689 return internal_snprintf(vers, sizeof(VersStr), format, os_major);
690}
691
692static void GetOSVersion(VersStr vers) {
693 uptr len = sizeof(VersStr);
694 if (SANITIZER_IOSSIM) {
695 const char *vers_env = GetEnv("SIMULATOR_RUNTIME_VERSION");
696 if (!vers_env) {
697 Report("ERROR: Running in simulator but SIMULATOR_RUNTIME_VERSION env "
698 "var is not set.\n");
699 Die();
700 }
701 len = internal_strlcpy(vers, vers_env, len);
702 } else {
703 int res =
704 internal_sysctlbyname("kern.osproductversion", vers, &len, nullptr, 0);
705
706 // XNU 17 (macOS 10.13) and below do not provide the sysctl
707 // `kern.osproductversion` entry (res != 0).
708 bool no_os_version = res != 0;
709
710 // For launchd, sanitizer initialization runs before sysctl is setup
711 // (res == 0 && len != strlen(vers), vers is not a valid version). However,
712 // the kernel version `kern.osrelease` is available.
713 bool launchd = (res == 0 && internal_strlen(vers) < 3);
714 if (launchd) CHECK_EQ(internal_getpid(), 1);
715
716 if (no_os_version || launchd) {
717 len = ApproximateOSVersionViaKernelVersion(vers);
718 }
719 }
720 CHECK_LT(len, sizeof(VersStr));
721}
722
723void ParseVersion(const char *vers, u16 *major, u16 *minor) {
724 // Format: <major>.<minor>[.<patch>]\0
725 CHECK_GE(internal_strlen(vers), 3);
726 const char *p = vers;
727 *major = internal_simple_strtoll(p, &p, /*base=*/10);
728 CHECK_EQ(*p, '.');
729 p += 1;
730 *minor = internal_simple_strtoll(p, &p, /*base=*/10);
731}
732
733// Aligned versions example:
734// macOS 10.15 -- iOS 13 -- tvOS 13 -- watchOS 6
735static void MapToMacos(u16 *major, u16 *minor) {
736 if (TARGET_OS_OSX)
737 return;
738
739 // All supported platforms (including DriverKit) have
740 // aligned version numbers in macOS 27+
741 if (*major >= 27)
742 return;
743
744# if TARGET_OS_DRIVERKIT
745 // Driverkit 25.0+ aligns with macOS 26+
746 if (*major >= 25) {
747 *major += 1;
748 return;
749 }
750# else
751 // macOS 26 and later have aligned version strings.
752 if (*major >= 26)
753 return;
754# endif
755
756 // Below are mappings for pre-macOS-25-aligned releases
757 if (TARGET_OS_IOS || TARGET_OS_TV)
758 *major += 2;
759 else if (TARGET_OS_WATCH)
760 *major += 9;
761 else if (TARGET_OS_VISION)
762 *major += 18;
763 else
764 UNREACHABLE("unsupported platform");
765
766 if (*major >= 16) { // macOS 11+
767 *major -= 5;
768 } else { // macOS 10.15 and below
769 *minor = *major;
770 *major = 10;
771 }
772}
773
774static MacosVersion GetMacosAlignedVersionInternal() {
775 VersStr vers = {};
776 GetOSVersion(vers);
777
778 u16 major, minor;
779 ParseVersion(vers, &major, &minor);
780 MapToMacos(&major, &minor);
781
782 return MacosVersion(major, minor);
783}
784
785static_assert(sizeof(MacosVersion) == sizeof(atomic_uint32_t::Type),
786 "MacosVersion cache size");
787static atomic_uint32_t cached_macos_version;
788
789MacosVersion GetMacosAlignedVersion() {
790 atomic_uint32_t::Type result =
791 atomic_load(&cached_macos_version, memory_order_acquire);
792 if (!result) {
793 MacosVersion version = GetMacosAlignedVersionInternal();
794 result = *reinterpret_cast<atomic_uint32_t::Type *>(&version);
795 atomic_store(&cached_macos_version, result, memory_order_release);
796 }
797 return *reinterpret_cast<MacosVersion *>(&result);
798}
799
800DarwinKernelVersion GetDarwinKernelVersion() {
801 VersStr vers = {};
802 uptr len = sizeof(VersStr);
803 int res = internal_sysctlbyname("kern.osrelease", vers, &len, nullptr, 0);
804 CHECK_EQ(res, 0);
805 CHECK_LT(len, sizeof(VersStr));
806
807 u16 major, minor;
808 ParseVersion(vers, &major, &minor);
809
810 return DarwinKernelVersion(major, minor);
811}
812
813uptr GetRSS() {
814 struct task_basic_info info;
815 unsigned count = TASK_BASIC_INFO_COUNT;
816 kern_return_t result =
817 task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count);
818 if (UNLIKELY(result != KERN_SUCCESS)) {
819 Report("Cannot get task info. Error: %d\n", result);
820 Die();
821 }
822 return info.resident_size;
823}
824
825void *internal_start_thread(void *(*func)(void *arg), void *arg) {
826 // Start the thread with signals blocked, otherwise it can steal user signals.
827 __sanitizer_sigset_t set, old;
828 internal_sigfillset(&set);
829 internal_sigprocmask(SIG_SETMASK, &set, &old);
830 pthread_t th;
831 pthread_create(&th, 0, func, arg);
832 internal_sigprocmask(SIG_SETMASK, &old, 0);
833 return th;
834}
835
836void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); }
837
838#if !SANITIZER_GO
839static Mutex syslog_lock;
840# endif
841
842# if SANITIZER_DRIVERKIT
843# define SANITIZER_OS_LOG os_log
844# else
845# define SANITIZER_OS_LOG os_log_error
846# endif
847
848void WriteOneLineToSyslog(const char *s) {
849#if !SANITIZER_GO
850 syslog_lock.CheckLocked();
851 if (GetMacosAlignedVersion() >= MacosVersion(10, 12)) {
852 SANITIZER_OS_LOG(OS_LOG_DEFAULT, "%{public}s", s);
853 } else {
854#pragma clang diagnostic push
855// as_log is deprecated.
856#pragma clang diagnostic ignored "-Wdeprecated-declarations"
857 asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s);
858#pragma clang diagnostic pop
859 }
860#endif
861}
862
863// buffer to store crash report application information
864static char crashreporter_info_buff[__sanitizer::kErrorMessageBufferSize] = {};
865static Mutex crashreporter_info_mutex;
866
867extern "C" {
868
869#if HAVE_CRASHREPORTERCLIENT_H
870// Available in CRASHREPORTER_ANNOTATIONS_VERSION 5+
871# ifdef CRASHREPORTER_ANNOTATIONS_INITIALIZER
872CRASHREPORTER_ANNOTATIONS_INITIALIZER()
873# else
874// Support for older CrashRerporter annotiations
875CRASH_REPORTER_CLIENT_HIDDEN
876struct crashreporter_annotations_t gCRAnnotations
877 __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) = {
878 CRASHREPORTER_ANNOTATIONS_VERSION,
879 0,
880 0,
881 0,
882 0,
883 0,
884 0,
885# if CRASHREPORTER_ANNOTATIONS_VERSION > 4
886 0,
887# endif
888};
889# endif
890# else
891// Revert to previous crash reporter API if client header is not available
892static const char *__crashreporter_info__ __attribute__((__used__)) =
893 &crashreporter_info_buff[0];
894asm(".desc ___crashreporter_info__, 0x10");
895#endif // HAVE_CRASHREPORTERCLIENT_H
896
897} // extern "C"
898
899static void CRAppendCrashLogMessage(const char *msg) {
900 Lock l(&crashreporter_info_mutex);
901 internal_strlcat(crashreporter_info_buff, msg,
902 sizeof(crashreporter_info_buff));
903#if HAVE_CRASHREPORTERCLIENT_H
904 (void)CRSetCrashLogMessage(crashreporter_info_buff);
905#endif
906}
907
908void LogMessageOnPrintf(const char *str) {
909 // Log all printf output to CrashLog.
910 if (common_flags()->abort_on_error)
911 CRAppendCrashLogMessage(str);
912}
913
914void LogFullErrorReport(const char *buffer) {
915# if !SANITIZER_GO
916 // When logging with os_log_error this will make it into the crash log.
917 if (internal_strncmp(SanitizerToolName, "AddressSanitizer",
918 sizeof("AddressSanitizer") - 1) == 0)
919 SANITIZER_OS_LOG(OS_LOG_DEFAULT, "Address Sanitizer reported a failure.");
920 else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer",
921 sizeof("UndefinedBehaviorSanitizer") - 1) == 0)
922 SANITIZER_OS_LOG(OS_LOG_DEFAULT,
923 "Undefined Behavior Sanitizer reported a failure.");
924 else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer",
925 sizeof("ThreadSanitizer") - 1) == 0)
926 SANITIZER_OS_LOG(OS_LOG_DEFAULT, "Thread Sanitizer reported a failure.");
927 else
928 SANITIZER_OS_LOG(OS_LOG_DEFAULT, "Sanitizer tool reported a failure.");
929
930 if (common_flags()->log_to_syslog)
931 SANITIZER_OS_LOG(OS_LOG_DEFAULT, "Consult syslog for more information.");
932
933 // Log to syslog.
934 // The logging on OS X may call pthread_create so we need the threading
935 // environment to be fully initialized. Also, this should never be called when
936 // holding the thread registry lock since that may result in a deadlock. If
937 // the reporting thread holds the thread registry mutex, and asl_log waits
938 // for GCD to dispatch a new thread, the process will deadlock, because the
939 // pthread_create wrapper needs to acquire the lock as well.
940 Lock l(&syslog_lock);
941 if (common_flags()->log_to_syslog)
942 WriteToSyslog(buffer);
943
944 // The report is added to CrashLog as part of logging all of Printf output.
945# endif // !SANITIZER_GO
946}
947
948SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
949#if defined(__x86_64__) || defined(__i386__)
950 ucontext_t *ucontext = static_cast<ucontext_t*>(context);
951 return ucontext->uc_mcontext->__es.__err & 2 /*T_PF_WRITE*/ ? Write : Read;
952#elif defined(__arm64__)
953 ucontext_t *ucontext = static_cast<ucontext_t*>(context);
954 return ucontext->uc_mcontext->__es.__esr & 0x40 /*ISS_DA_WNR*/ ? Write : Read;
955#else
956 return Unknown;
957#endif
958}
959
960bool SignalContext::IsTrueFaultingAddress() const {
961 auto si = static_cast<const siginfo_t *>(siginfo);
962 // "Real" SIGSEGV codes (e.g., SEGV_MAPERR, SEGV_MAPERR) are non-zero.
963 return si->si_signo == SIGSEGV && si->si_code != 0;
964}
965
966#if defined(__aarch64__) && defined(arm_thread_state64_get_sp)
967 #define AARCH64_GET_REG(r) \
968 (uptr)ptrauth_strip( \
969 (void *)arm_thread_state64_get_##r(ucontext->uc_mcontext->__ss), 0)
970#else
971 #define AARCH64_GET_REG(r) (uptr)ucontext->uc_mcontext->__ss.__##r
972#endif
973
974static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
975 ucontext_t *ucontext = (ucontext_t*)context;
976# if defined(__aarch64__)
977 *pc = AARCH64_GET_REG(pc);
978 *bp = AARCH64_GET_REG(fp);
979 *sp = AARCH64_GET_REG(sp);
980# elif defined(__x86_64__)
981 *pc = ucontext->uc_mcontext->__ss.__rip;
982 *bp = ucontext->uc_mcontext->__ss.__rbp;
983 *sp = ucontext->uc_mcontext->__ss.__rsp;
984# elif defined(__arm__)
985 *pc = ucontext->uc_mcontext->__ss.__pc;
986 *bp = ucontext->uc_mcontext->__ss.__r[7];
987 *sp = ucontext->uc_mcontext->__ss.__sp;
988# elif defined(__i386__)
989 *pc = ucontext->uc_mcontext->__ss.__eip;
990 *bp = ucontext->uc_mcontext->__ss.__ebp;
991 *sp = ucontext->uc_mcontext->__ss.__esp;
992# else
993# error "Unknown architecture"
994# endif
995}
996
997void SignalContext::InitPcSpBp() {
998 addr = (uptr)ptrauth_strip((void *)addr, 0);
999 GetPcSpBp(context, &pc, &sp, &bp);
1000}
1001
1002// ASan/TSan use mmap in a way that creates “deallocation gaps” which triggers
1003// EXC_GUARD exceptions on macOS 10.15+ (XNU 19.0+).
1004static void DisableMmapExcGuardExceptions() {
1005 using task_exc_guard_behavior_t = uint32_t;
1006 using task_set_exc_guard_behavior_t =
1007 kern_return_t(task_t task, task_exc_guard_behavior_t behavior);
1008 auto *set_behavior = (task_set_exc_guard_behavior_t *)dlsym(
1009 RTLD_DEFAULT, "task_set_exc_guard_behavior");
1010 if (set_behavior == nullptr) return;
1011 const task_exc_guard_behavior_t task_exc_guard_none = 0;
1012 kern_return_t res = set_behavior(mach_task_self(), task_exc_guard_none);
1013 if (res != KERN_SUCCESS) {
1014 Report(
1015 "WARN: task_set_exc_guard_behavior returned %d (%s), "
1016 "mmap may fail unexpectedly.\n",
1017 res, mach_error_string(res));
1018 if (res == KERN_DENIED)
1019 Report(
1020 "HINT: Check that task_set_exc_guard_behavior is allowed by "
1021 "sandbox.\n");
1022 }
1023}
1024
1025static void VerifyInterceptorsWorking();
1026static void StripEnv();
1027
1028void InitializePlatformEarly() {
1029 // Only use xnu_fast_mmap when on x86_64 and the kernel supports it.
1030 use_xnu_fast_mmap =
1031#if defined(__x86_64__)
1032 GetDarwinKernelVersion() >= DarwinKernelVersion(17, 5);
1033#else
1034 false;
1035#endif
1036 if (GetDarwinKernelVersion() >= DarwinKernelVersion(19, 0))
1037 DisableMmapExcGuardExceptions();
1038
1039# if !SANITIZER_GO
1040 MonotonicNanoTime(); // Call to initialize mach_timebase_info
1041 VerifyInterceptorsWorking();
1042 StripEnv();
1043# endif
1044}
1045
1046#if !SANITIZER_GO
1047static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
1048LowLevelAllocator allocator_for_env;
1049
1050static bool ShouldCheckInterceptors() {
1051 // Restrict "interceptors working?" check
1052 const char *sanitizer_names[] = {"AddressSanitizer", "ThreadSanitizer",
1053 "RealtimeSanitizer"};
1054 size_t count = sizeof(sanitizer_names) / sizeof(sanitizer_names[0]);
1055 for (size_t i = 0; i < count; i++) {
1056 if (internal_strcmp(sanitizer_names[i], SanitizerToolName) == 0)
1057 return true;
1058 }
1059 return false;
1060}
1061
1062static void VerifyInterceptorsWorking() {
1063 if (!common_flags()->verify_interceptors || !ShouldCheckInterceptors())
1064 return;
1065
1066 // Verify that interceptors really work. We'll use dlsym to locate
1067 // "puts", if interceptors are working, it should really point to
1068 // "wrap_puts" within our own dylib.
1069 Dl_info info_puts, info_runtime;
1070 RAW_CHECK(dladdr(dlsym(RTLD_DEFAULT, "puts"), &info_puts));
1071 RAW_CHECK(dladdr((void *)&VerifyInterceptorsWorking, &info_runtime));
1072 if (internal_strcmp(info_puts.dli_fname, info_runtime.dli_fname) != 0) {
1073 Report(
1074 "ERROR: Interceptors are not working. This may be because %s is "
1075 "loaded too late (e.g. via dlopen). Please launch the executable "
1076 "with:\n%s=%s\n",
1077 SanitizerToolName, kDyldInsertLibraries, info_runtime.dli_fname);
1078 RAW_CHECK("interceptors not installed" && 0);
1079 }
1080}
1081
1082// Change the value of the env var |name|, leaking the original value.
1083// If |name_value| is NULL, the variable is deleted from the environment,
1084// otherwise the corresponding "NAME=value" string is replaced with
1085// |name_value|.
1086static void LeakyResetEnv(const char *name, const char *name_value) {
1087 char **env = GetEnviron();
1088 uptr name_len = internal_strlen(name);
1089 while (*env != 0) {
1090 uptr len = internal_strlen(*env);
1091 if (len > name_len) {
1092 const char *p = *env;
1093 if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
1094 // Match.
1095 if (name_value) {
1096 // Replace the old value with the new one.
1097 *env = const_cast<char*>(name_value);
1098 } else {
1099 // Shift the subsequent pointers back.
1100 char **del = env;
1101 do {
1102 del[0] = del[1];
1103 } while (*del++);
1104 }
1105 }
1106 }
1107 env++;
1108 }
1109}
1110
1111static void StripEnv() {
1112 if (!common_flags()->strip_env)
1113 return;
1114
1115 char *dyld_insert_libraries =
1116 const_cast<char *>(GetEnv(kDyldInsertLibraries));
1117 if (!dyld_insert_libraries)
1118 return;
1119
1120 Dl_info info;
1121 RAW_CHECK(dladdr((void *)&StripEnv, &info));
1122 const char *dylib_name = StripModuleName(info.dli_fname);
1123 bool lib_is_in_env = internal_strstr(dyld_insert_libraries, dylib_name);
1124 if (!lib_is_in_env)
1125 return;
1126
1127 // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
1128 // the dylib from the environment variable, because interceptors are installed
1129 // and we don't want our children to inherit the variable.
1130
1131 uptr old_env_len = internal_strlen(dyld_insert_libraries);
1132 uptr dylib_name_len = internal_strlen(dylib_name);
1133 uptr env_name_len = internal_strlen(kDyldInsertLibraries);
1134 // Allocate memory to hold the previous env var name, its value, the '='
1135 // sign and the '\0' char.
1136 char *new_env = (char*)allocator_for_env.Allocate(
1137 old_env_len + 2 + env_name_len);
1138 RAW_CHECK(new_env);
1139 internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
1140 internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
1141 new_env[env_name_len] = '=';
1142 char *new_env_pos = new_env + env_name_len + 1;
1143
1144 // Iterate over colon-separated pieces of |dyld_insert_libraries|.
1145 char *piece_start = dyld_insert_libraries;
1146 char *piece_end = NULL;
1147 char *old_env_end = dyld_insert_libraries + old_env_len;
1148 do {
1149 if (piece_start[0] == ':') piece_start++;
1150 piece_end = internal_strchr(piece_start, ':');
1151 if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
1152 if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
1153 uptr piece_len = piece_end - piece_start;
1154
1155 char *filename_start =
1156 (char *)internal_memrchr(piece_start, '/', piece_len);
1157 uptr filename_len = piece_len;
1158 if (filename_start) {
1159 filename_start += 1;
1160 filename_len = piece_len - (filename_start - piece_start);
1161 } else {
1162 filename_start = piece_start;
1163 }
1164
1165 // If the current piece isn't the runtime library name,
1166 // append it to new_env.
1167 if ((dylib_name_len != filename_len) ||
1168 (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
1169 if (new_env_pos != new_env + env_name_len + 1) {
1170 new_env_pos[0] = ':';
1171 new_env_pos++;
1172 }
1173 internal_strncpy(new_env_pos, piece_start, piece_len);
1174 new_env_pos += piece_len;
1175 }
1176 // Move on to the next piece.
1177 piece_start = piece_end;
1178 } while (piece_start < old_env_end);
1179
1180 // Can't use setenv() here, because it requires the allocator to be
1181 // initialized.
1182 // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
1183 // a separate function called after InitializeAllocator().
1184 if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
1185 LeakyResetEnv(kDyldInsertLibraries, new_env);
1186}
1187#endif // SANITIZER_GO
1188
1189// Prints out a consolidated memory map: contiguous regions
1190// are merged together.
1191static void PrintVmmap() {
1192 const mach_vm_address_t max_vm_address = GetMaxVirtualAddress() + 1;
1193 mach_vm_address_t address = GAP_SEARCH_START_ADDRESS;
1194 kern_return_t kr = KERN_SUCCESS;
1195
1196 Report("Memory map:\n");
1197 mach_vm_address_t last = 0;
1198 mach_vm_address_t lastsz = 0;
1199
1200 while (1) {
1201 mach_vm_size_t vmsize = 0;
1202 natural_t depth = 0;
1203 vm_region_submap_short_info_data_64_t vminfo;
1204 mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
1205 kr = internal_mach_vm_region_recurse(mach_task_self(), &address, &vmsize,
1206 &depth, (vm_region_info_t)&vminfo,
1207 &count);
1208
1209 if (kr == KERN_SUCCESS && address < max_vm_address) {
1210 if (last + lastsz == address) {
1211 // This region is contiguous with the last; merge together.
1212 lastsz += vmsize;
1213 } else {
1214 if (lastsz)
1215 Printf("|| `[%p, %p]` || size=0x%016" PRIx64 " ||\n", (void*)last,
1216 (void*)(last + lastsz), lastsz);
1217
1218 last = address;
1219 lastsz = vmsize;
1220 }
1221 address += vmsize;
1222 } else {
1223 // We've reached the end of the memory map. Print the last remaining
1224 // region, if there is one.
1225 if (lastsz)
1226 Printf("|| `[%p, %p]` || size=0x%016" PRIx64 " ||\n", (void*)last,
1227 (void*)(last + lastsz), lastsz);
1228
1229 break;
1230 }
1231 }
1232}
1233
1234static void ReportShadowAllocFail(uptr shadow_size_bytes, uptr alignment) {
1235 Report(
1236 "FATAL: Failed to allocate shadow memory. Tried to allocate %p bytes "
1237 "(alignment=%p).\n",
1238 (void*)shadow_size_bytes, (void*)alignment);
1239 PrintVmmap();
1240}
1241
1242char **GetArgv() {
1243 return *_NSGetArgv();
1244}
1245
1246// These platforms definitely support task_info.
1247# if SANITIZER_IOS || SANITIZER_DRIVERKIT || SANITIZER_OSX
1248
1249// The task_vm_info struct is normally provided by the macOS SDK, but we need
1250// fields only available in 10.12+. Declare the struct manually to be able to
1251// build against older SDKs.
1252struct __sanitizer_task_vm_info {
1253 mach_vm_size_t virtual_size;
1254 integer_t region_count;
1255 integer_t page_size;
1256 mach_vm_size_t resident_size;
1257 mach_vm_size_t resident_size_peak;
1258 mach_vm_size_t device;
1259 mach_vm_size_t device_peak;
1260 mach_vm_size_t internal;
1261 mach_vm_size_t internal_peak;
1262 mach_vm_size_t external;
1263 mach_vm_size_t external_peak;
1264 mach_vm_size_t reusable;
1265 mach_vm_size_t reusable_peak;
1266 mach_vm_size_t purgeable_volatile_pmap;
1267 mach_vm_size_t purgeable_volatile_resident;
1268 mach_vm_size_t purgeable_volatile_virtual;
1269 mach_vm_size_t compressed;
1270 mach_vm_size_t compressed_peak;
1271 mach_vm_size_t compressed_lifetime;
1272 mach_vm_size_t phys_footprint;
1273 mach_vm_address_t min_address;
1274 mach_vm_address_t max_address;
1275};
1276#define __SANITIZER_TASK_VM_INFO_COUNT ((mach_msg_type_number_t) \
1277 (sizeof(__sanitizer_task_vm_info) / sizeof(natural_t)))
1278
1279static uptr GetTaskInfoMaxAddress() {
1280 __sanitizer_task_vm_info vm_info = {} /* zero initialize */;
1281 mach_msg_type_number_t count = __SANITIZER_TASK_VM_INFO_COUNT;
1282 int err = task_info(mach_task_self(), TASK_VM_INFO, (int *)&vm_info, &count);
1283 return err ? 0 : vm_info.max_address;
1284}
1285
1286# endif
1287
1288# if SANITIZER_IOSDEVICE || SANITIZER_DRIVERKIT
1289
1290uptr GetMaxUserVirtualAddress() {
1291 static uptr max_vm = GetTaskInfoMaxAddress();
1292 if (max_vm != 0) {
1293 return max_vm - 1;
1294 }
1295
1296 // xnu cannot provide vm address limit
1297# if SANITIZER_WORDSIZE == 32
1298 constexpr uptr fallback_max_vm = 0xffe00000 - 1;
1299# else
1300 constexpr uptr fallback_max_vm = 0x200000000 - 1;
1301# endif
1302 static_assert(fallback_max_vm <= SANITIZER_MMAP_RANGE_SIZE,
1303 "Max virtual address must be less than mmap range size.");
1304 return fallback_max_vm;
1305}
1306
1307# else // !(SANITIZER_IOSDEVICE || SANITIZER_DRIVERKIT)
1308
1309uptr GetMaxUserVirtualAddress() {
1310# if SANITIZER_WORDSIZE == 64
1311 constexpr uptr max_vm = (1ULL << 47) - 1; // 0x00007fffffffffffUL;
1312# else // SANITIZER_WORDSIZE == 32
1313 static_assert(SANITIZER_WORDSIZE == 32, "Wrong wordsize");
1314 constexpr uptr max_vm = (1ULL << 32) - 1; // 0xffffffff;
1315# endif
1316 static_assert(max_vm <= SANITIZER_MMAP_RANGE_SIZE,
1317 "Max virtual address must be less than mmap range size.");
1318 return max_vm;
1319}
1320# endif
1321
1322bool ActivateDebugMemory() {
1323# if SANITIZER_EMBEDDED_VM_LAYOUT
1324 // On embedded devices, the debug region should be higher than the range
1325 // normally returned by mmap.
1326 static_assert(DARWIN_DEBUG_MEMORY_START >=
1327 SANITIZER_MMAP_BEGIN + SANITIZER_MMAP_RANGE_SIZE);
1328
1329 uptr cur_max = GetTaskInfoMaxAddress();
1330 // The "old layout" is used when the OS version is less than 27.0 or
1331 // when the max_offset is <= 448G (even in 27.0, not all processes
1332 // must use the new layout)
1333 if (cur_max <= DARWIN_DEBUG_MEMORY_VMOFFSET_FLOOR ||
1334 GetMacosAlignedVersion() < DARWIN_DEBUG_MEMORY_VERSION_FLOOR) {
1335 VReport(2, "Falling back to old layout cur_max=%p\n", (void*)cur_max);
1336 return false;
1337 }
1338
1339 // Now, we prepare to activate the "debug range". This expands the
1340 // max vm offset from ~512G to ~8TB, but doesn't change the space
1341 // available to applications. First, we check that the max vm offset
1342 // is not already expanded...
1343
1344 // Max address above the debug range is unexpected. It could suggest that
1345 // another tool has already activated the debug memory region, but that
1346 // would be unexpected as we are supposed to run early in process launch.
1347 // Since we do not guarantee compatbility with other tools, bail out here.
1348 if (cur_max > DARWIN_DEBUG_MEMORY_START) {
1349 Report(
1350 "FATAL: Unexpected VM layout. max addr = %p, expected <= %p. Is "
1351 "another tool using debug memory?",
1352 (void*)cur_max, DARWIN_DEBUG_MEMORY_START);
1353 Die();
1354 }
1355
1356 VReport(2, "Using debug memory range cur_max=%p\n", (void*)cur_max);
1357 uint32_t enabled = 1;
1358 int ret = sysctlbyname("vm.debug_range_enabled", NULL, NULL, &enabled,
1359 sizeof(enabled));
1360 uptr new_max = GetTaskInfoMaxAddress();
1361
1362 if (ret != 0 || new_max <= DARWIN_DEBUG_MEMORY_START) {
1363 // Not being able to activate debug memory is essentially guaranteed to be
1364 // fatal: for ASAN 512GB of VM requires 64G of contiguous shadow, which
1365 // there just isn't space for without debug range. So we do not attempt to
1366 // support that.
1367 Report(
1368 "FATAL: vm.debug_range_enabled sysctl failed to activate (ret=%d).\n",
1369 ret);
1370 Report(
1371 "HINT: Check that the device is in developer mode and that the "
1372 "sysctl is not denied by sandbox.\n");
1373 Die();
1374 }
1375
1376 // Success: The debug range was successfully enabled.
1377 VReport(2, "vm.debug_range_enabled updated successfully, new max=%p\n",
1378 (void*)new_max);
1379 debug_region_activated = true;
1380 return true;
1381
1382# endif // SANITIZER_EMBEDDED_VM_LAYOUT
1383 return false;
1384}
1385
1386bool GetDebugMemoryRange(mach_vm_range* debug_range) {
1387 size_t sz = sizeof(*debug_range);
1388 int ret =
1389 sysctlbyname("vm.vm_map_user_range_debug", debug_range, &sz, NULL, 0);
1390 return ret == 0 && sz == sizeof(*debug_range);
1391}
1392
1393uptr GetMaxVirtualAddress() {
1394 return GetMaxUserVirtualAddress();
1395}
1396
1397uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
1398 uptr min_shadow_base_alignment, uptr &high_mem_end,
1399 uptr granularity) {
1400 const uptr alignment =
1401 Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment);
1402 const uptr left_padding =
1403 Max<uptr>(granularity, 1ULL << min_shadow_base_alignment);
1404
1405 uptr space_size = shadow_size_bytes;
1406
1407 uptr largest_gap_found = 0;
1408 uptr max_occupied_addr = 0;
1409
1410 // Note that the below call may expand the address space reported by
1411 // GetTaskInfoMaxAddress. The original max_vm is stored in kHighMemEnd (and
1412 // was used to compute shadow_size_bytes.) and also
1413 // GetMaxUserVirtualAddress/GetMaxVirtualAddress use a static variable and
1414 // thus always return the original max.
1415 bool use_debug_vm = ActivateDebugMemory();
1416
1417 VReport(2, "FindDynamicShadowStart, space_size = %p\n", (void *)space_size);
1418 uptr shadow_start = FindAvailableMemoryRange(
1419 space_size, alignment, left_padding, &largest_gap_found,
1420 &max_occupied_addr, use_debug_vm);
1421 // If the shadow doesn't fit, restrict the address space to make it fit.
1422 if (!use_debug_vm && shadow_start == 0) {
1423 VReport(
1424 2,
1425 "Shadow doesn't fit, largest_gap_found = %p, max_occupied_addr = %p\n",
1426 (void *)largest_gap_found, (void *)max_occupied_addr);
1427 uptr new_max_vm = RoundDownTo(largest_gap_found << shadow_scale, alignment);
1428 if (new_max_vm < max_occupied_addr) {
1429 Report("Unable to find a memory range for dynamic shadow.\n");
1430 Report(
1431 "\tspace_size = %p\n\tlargest_gap_found = %p\n\tmax_occupied_addr "
1432 "= %p\n\tnew_max_vm = %p\n",
1433 (void*)space_size, (void*)largest_gap_found, (void*)max_occupied_addr,
1434 (void*)new_max_vm);
1435 ReportShadowAllocFail(shadow_size_bytes, alignment);
1436 CHECK(0 && "cannot place shadow");
1437 }
1438 RestrictMemoryToMaxAddress(new_max_vm);
1439 high_mem_end = new_max_vm - 1;
1440 space_size = (high_mem_end >> shadow_scale);
1441 VReport(2, "FindDynamicShadowStart, space_size = %p\n", (void *)space_size);
1442 shadow_start = FindAvailableMemoryRange(space_size, alignment, left_padding,
1443 nullptr, nullptr,
1444 /* use_debug_vm */ false);
1445 if (shadow_start == 0) {
1446 Report("Unable to find a memory range after restricting VM.\n");
1447 ReportShadowAllocFail(shadow_size_bytes, alignment);
1448 CHECK(0 && "cannot place shadow after restricting vm");
1449 }
1450 }
1451
1452 CHECK_NE((uptr)0, shadow_start);
1453 CHECK(IsAligned(shadow_start, alignment));
1454 return shadow_start;
1455}
1456
1457// Returns a list of ranges which must be covered by shadow memory,
1458// and cannot overlap with any fixed mappings made by a sanitizer.
1459//
1460// NOTE: Ranges must only be page-aligned.
1461//
1462// This list may be empty on macOS < 27.0, which implies no additional
1463// restriction on the placement of shadow.
1464void GetAppRanges(InternalMmapVector<ReservedRange>& ranges) {
1465 if (GetMacosAlignedVersion() < DARWIN_DEBUG_MEMORY_VERSION_FLOOR)
1466 return;
1467
1468# if SANITIZER_EMBEDDED_VM_LAYOUT
1469 // NOTE: Even in iOS 27.0, some embedded devices and processes may not
1470 // use the new layout.
1471 if (GetTaskInfoMaxAddress() <= DARWIN_DEBUG_MEMORY_VMOFFSET_FLOOR)
1472 return;
1473# endif
1474
1475 ranges.reserve(2);
1476# if SANITIZER_WORDSIZE == 64
1477# if SANITIZER_IOSDEVICE
1478
1479 struct mach_vm_range debug_range;
1480 if (!GetDebugMemoryRange(&debug_range)) {
1481 Report("vm.vm_map_user_range_debug sysctl failed. Denied by sandbox?\n");
1482 Die();
1483 }
1484
1485 // Get app ranges
1486 size_t all_ranges_sz = 0;
1487
1488 // Call 1: Get length
1489 int ret = sysctlbyname("vm.all_map_ranges", NULL, &all_ranges_sz, NULL, 0);
1490 if (ret != 0) {
1491 Report("vm.all_map_ranges sysctl failed. Denied by sandbox?\n");
1492 Die();
1493 }
1494
1495 struct mach_vm_range* all_ranges =
1496 (struct mach_vm_range*)InternalAlloc(all_ranges_sz);
1497 // Call 2: Actually get the ranges
1498 ret = sysctlbyname("vm.all_map_ranges", all_ranges, &all_ranges_sz, NULL, 0);
1499 if (ret != 0) {
1500 Report("vm.all_map_ranges sysctl failed. Denied by sandbox?\n");
1501 Die();
1502 }
1503
1504 bool has_gpu_carveout =
1505 MemoryRangeIsKernelReserved(MACH_VM_MIN_GPU_CARVEOUT_ADDRESS_RAW,
1506 MACH_VM_MAX_GPU_CARVEOUT_ADDRESS_RAW);
1507 for (unsigned i = 0; i < (all_ranges_sz / sizeof(all_ranges[0])); i++) {
1508 struct mach_vm_range range = all_ranges[i];
1509 // Skip the debug range: we don't have to cover it with shadow.
1510 if (range.min_address == debug_range.min_address &&
1511 range.max_address == debug_range.max_address)
1512 continue;
1513 // Skip zero-size ranges
1514 if (range.min_address == range.max_address)
1515 continue;
1516
1517 // Split ranges that contain the carveout
1518 if (has_gpu_carveout) {
1519 // Pre-carveout range, if any
1520 if (range.min_address < MACH_VM_MIN_GPU_CARVEOUT_ADDRESS_RAW)
1521 ranges.push_back(
1522 {range.min_address,
1523 Min(range.max_address, MACH_VM_MIN_GPU_CARVEOUT_ADDRESS_RAW)});
1524
1525 // Post-carveout range, if any
1526 if (range.max_address > MACH_VM_MAX_GPU_CARVEOUT_ADDRESS_RAW)
1527 ranges.push_back(
1528 {Max(range.min_address, MACH_VM_MAX_GPU_CARVEOUT_ADDRESS_RAW),
1529 range.max_address});
1530 } else {
1531 ranges.push_back({range.min_address, range.max_address});
1532 }
1533 }
1534
1535 InternalFree(all_ranges);
1536
1537 // Compute the shared cache range
1538 size_t shared_cache_size;
1539 mach_vm_address_t shared_cache_base =
1540 (mach_vm_address_t)_dyld_get_shared_cache_range(&shared_cache_size);
1541
1542 CHECK(shared_cache_base != 0 && "_dyld_get_shared_cache_range returned NULL");
1543 VReport(2, "Shared cache: [%p, %p]\n", (void*)shared_cache_base,
1544 (void*)(shared_cache_base + shared_cache_size));
1545
1546 // Verify that the shared cache range is within one of the known ranges
1547 bool shared_cache_in_range = false;
1548 for (auto& [range_start, range_end] : ranges) {
1549 shared_cache_in_range |=
1550 (shared_cache_base >= range_start &&
1551 shared_cache_base + shared_cache_size <= range_end);
1552 // Note: GetMaxUserVirtualAddress returns the cached, pre-expansion
1553 // maximum (debug memory increases the max_address reported by task_info)
1554 if (range_end - 1 > GetMaxUserVirtualAddress()) {
1555 Report("FATAL: Unsupported VM layout. Range [%p, %p] is above max %p\n",
1556 range_start, range_end, GetMaxUserVirtualAddress());
1557 Die();
1558 }
1559 }
1560
1561 if (!shared_cache_in_range) {
1562 Report(
1563 "FATAL: Unsupported VM layout. Shared cache [%p, %p] is outside of "
1564 "expected range\n",
1565 shared_cache_base, shared_cache_base + shared_cache_size);
1566 Die();
1567 }
1568# else
1569 // The first 512GB are platform-reserved on macOS.
1570 ranges.push_back({0x1000UL, 0x8000000000UL});
1571# endif
1572# endif
1573
1574 VReport(2, "App ranges:\n");
1575 for (auto& [range_start, range_end] : ranges) {
1576 VReport(2, " [%p, %p]\n", range_start, range_end);
1577 }
1578}
1579
1580uptr MapDynamicShadowAndAliases(uptr shadow_size, uptr alias_size,
1581 uptr num_aliases, uptr ring_buffer_size) {
1582 CHECK(false && "HWASan aliasing is unimplemented on Mac");
1583 return 0;
1584}
1585
1586uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
1587 uptr* largest_gap_found, uptr* max_occupied_addr,
1588 bool use_debug_vm) {
1589 mach_vm_address_t max_vm_address = GetMaxVirtualAddress() + 1;
1590 mach_vm_address_t address = GAP_SEARCH_START_ADDRESS;
1591 mach_vm_address_t free_begin = GAP_SEARCH_START_ADDRESS;
1592
1593 // When using the debug VM range, we restrict our search to start at the
1594 // reported beginning of that range
1595 if (use_debug_vm) {
1596 struct mach_vm_range debug_range;
1597 if (!GetDebugMemoryRange(&debug_range)) {
1598 Report("vm_map_user_range_debug sysctl failed. Denied by sandbox?\n");
1599 Die();
1600 }
1601
1602 // When use_debug_vm is true, we want to search up to the end of the debug
1603 // range.
1604 max_vm_address = debug_range.max_address;
1605
1606 VReport(2, "FindAvailableMemoryRange: Debug range is [%p, %p], max_vm=%p\n",
1607 debug_range.min_address, debug_range.max_address,
1608 GetTaskInfoMaxAddress());
1609
1610 // Begin our search at the beginning of the debug range.
1611 address = debug_range.min_address;
1612 free_begin = debug_range.min_address;
1613 }
1614
1615 // Restrict the search to be after any reserved ranges
1616 InternalMmapVector<ReservedRange> app_ranges;
1617 GetAppRanges(app_ranges);
1618
1619 for (auto& [range_start, range_end] : app_ranges) {
1620 address = Max(address, (mach_vm_address_t)range_end);
1621 free_begin = Max(free_begin, (mach_vm_address_t)range_end);
1622 }
1623
1624 kern_return_t kr = KERN_SUCCESS;
1625 if (largest_gap_found) *largest_gap_found = 0;
1626 if (max_occupied_addr) *max_occupied_addr = 0;
1627 while (kr == KERN_SUCCESS) {
1628 mach_vm_size_t vmsize = 0;
1629 natural_t depth = 0;
1630 vm_region_submap_short_info_data_64_t vminfo;
1631 mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
1632 kr = internal_mach_vm_region_recurse(mach_task_self(), &address, &vmsize,
1633 &depth, (vm_region_info_t)&vminfo,
1634 &count);
1635
1636 if (kr == KERN_SUCCESS) {
1637 // There are cases where going beyond the processes' max vm does
1638 // not return KERN_INVALID_ADDRESS so we check for going beyond that
1639 // max address as well.
1640 if (address > max_vm_address) {
1641 address = max_vm_address;
1642 kr = -1; // break after this iteration.
1643 } else if (max_occupied_addr) {
1644 *max_occupied_addr = address + vmsize;
1645 }
1646 } else if (kr == KERN_INVALID_ADDRESS) {
1647 // No more regions beyond "address", consider the gap at the end of VM.
1648 address = max_vm_address;
1649
1650 // We will break after this iteration anyway since kr != KERN_SUCCESS
1651 } else {
1652 Report(
1653 "WARNING: mach_vm_region_recurse returned unexpected code %d (%s)\n",
1654 kr, mach_error_string(kr));
1655 DCHECK(false && "mach_vm_region_recurse returned unexpected code");
1656 break; // address is not valid unless KERN_SUCCESS, therefore we must not
1657 // use it.
1658 }
1659
1660 if (free_begin != address) {
1661 // We found a free region [free_begin..address-1].
1662 uptr gap_start = RoundUpTo((uptr)free_begin + left_padding, alignment);
1663 uptr gap_end = RoundDownTo((uptr)Min(address, max_vm_address), alignment);
1664 uptr gap_size = gap_end > gap_start ? gap_end - gap_start : 0;
1665 if (size < gap_size) {
1666 return gap_start;
1667 }
1668
1669 if (largest_gap_found && *largest_gap_found < gap_size) {
1670 *largest_gap_found = gap_size;
1671 }
1672 }
1673 // Move to the next region.
1674 address += vmsize;
1675 free_begin = address;
1676 }
1677
1678 // We looked at all free regions and could not find one large enough.
1679 return 0;
1680}
1681
1682// This function (when used during initialization when there is
1683// only a single thread), can be used to verify that a range
1684// of memory hasn't already been mapped, and won't be mapped
1685// later in the shared cache.
1686//
1687// If the syscall mach_vm_region_recurse fails (due to sandbox),
1688// we assume that the memory is not mapped so that execution can continue.
1689//
1690// NOTE: range_end is inclusive
1691//
1692// WARNING: This function must NOT allocate memory, since it is
1693// used in InitializeShadowMemory between where we search for
1694// space for shadow and where we actually allocate it.
1695bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
1696 mach_vm_size_t vmsize = 0;
1697 natural_t depth = 0;
1698 vm_region_submap_short_info_data_64_t vminfo;
1699 mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
1700 mach_vm_address_t address = range_start;
1701
1702 // First, check if the range is already mapped.
1703 kern_return_t kr = internal_mach_vm_region_recurse(
1704 mach_task_self(), &address, &vmsize, &depth, (vm_region_info_t)&vminfo,
1705 &count);
1706
1707 if (kr == KERN_SUCCESS && !IntervalsAreSeparate(address, address + vmsize - 1,
1708 range_start, range_end)) {
1709 // Overlaps with already-mapped memory
1710 return false;
1711 }
1712
1713 size_t cacheLength;
1714 uptr cacheStart = (uptr)_dyld_get_shared_cache_range(&cacheLength);
1715
1716 if (cacheStart &&
1717 !IntervalsAreSeparate(cacheStart, cacheStart + cacheLength - 1,
1718 range_start, range_end)) {
1719 // Overlaps with shared cache region
1720 return false;
1721 }
1722
1723 // We believe this address is available.
1724 return true;
1725}
1726
1727// Returns true if this range (range_end is exclusive) corresponds exactly
1728// to a kernel-reserved region such as the GPU carveout.
1729bool MemoryRangeIsKernelReserved(uptr range_start, uptr range_end) {
1730 mach_vm_size_t vmsize = 0;
1731 natural_t depth = 0;
1732 vm_region_submap_short_info_data_64_t vminfo;
1733 mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
1734 mach_vm_address_t address = range_start;
1735
1736 // Kernel-reserved ranges (e.g. the GPU carve-out) have max_protection == 0,
1737 // i.e. they are permanently PROT_NONE
1738 kern_return_t kr = internal_mach_vm_region_recurse(
1739 mach_task_self(), &address, &vmsize, &depth, (vm_region_info_t)&vminfo,
1740 &count);
1741
1742 return (kr == KERN_SUCCESS && address == range_start &&
1743 address + vmsize == range_end && vminfo.max_protection == 0);
1744}
1745
1746// FIXME implement on this platform.
1747void GetMemoryProfile(fill_profile_f cb, uptr *stats) {}
1748
1749void SignalContext::DumpAllRegisters(void *context) {
1750 Report("Register values:\n");
1751
1752 ucontext_t *ucontext = (ucontext_t*)context;
1753# define DUMPREG64(r) \
1754 Printf("%s = 0x%016llx ", #r, ucontext->uc_mcontext->__ss.__ ## r);
1755# define DUMPREGA64(r) \
1756 Printf(" %s = 0x%016lx ", #r, AARCH64_GET_REG(r));
1757# define DUMPREG32(r) \
1758 Printf("%s = 0x%08x ", #r, ucontext->uc_mcontext->__ss.__ ## r);
1759# define DUMPREG_(r) Printf(" "); DUMPREG(r);
1760# define DUMPREG__(r) Printf(" "); DUMPREG(r);
1761# define DUMPREG___(r) Printf(" "); DUMPREG(r);
1762
1763# if defined(__x86_64__)
1764# define DUMPREG(r) DUMPREG64(r)
1765 DUMPREG(rax); DUMPREG(rbx); DUMPREG(rcx); DUMPREG(rdx); Printf("\n");
1766 DUMPREG(rdi); DUMPREG(rsi); DUMPREG(rbp); DUMPREG(rsp); Printf("\n");
1767 DUMPREG_(r8); DUMPREG_(r9); DUMPREG(r10); DUMPREG(r11); Printf("\n");
1768 DUMPREG(r12); DUMPREG(r13); DUMPREG(r14); DUMPREG(r15); Printf("\n");
1769# elif defined(__i386__)
1770# define DUMPREG(r) DUMPREG32(r)
1771 DUMPREG(eax); DUMPREG(ebx); DUMPREG(ecx); DUMPREG(edx); Printf("\n");
1772 DUMPREG(edi); DUMPREG(esi); DUMPREG(ebp); DUMPREG(esp); Printf("\n");
1773# elif defined(__aarch64__)
1774# define DUMPREG(r) DUMPREG64(r)
1775 DUMPREG_(x[0]); DUMPREG_(x[1]); DUMPREG_(x[2]); DUMPREG_(x[3]); Printf("\n");
1776 DUMPREG_(x[4]); DUMPREG_(x[5]); DUMPREG_(x[6]); DUMPREG_(x[7]); Printf("\n");
1777 DUMPREG_(x[8]); DUMPREG_(x[9]); DUMPREG(x[10]); DUMPREG(x[11]); Printf("\n");
1778 DUMPREG(x[12]); DUMPREG(x[13]); DUMPREG(x[14]); DUMPREG(x[15]); Printf("\n");
1779 DUMPREG(x[16]); DUMPREG(x[17]); DUMPREG(x[18]); DUMPREG(x[19]); Printf("\n");
1780 DUMPREG(x[20]); DUMPREG(x[21]); DUMPREG(x[22]); DUMPREG(x[23]); Printf("\n");
1781 DUMPREG(x[24]); DUMPREG(x[25]); DUMPREG(x[26]); DUMPREG(x[27]); Printf("\n");
1782 DUMPREG(x[28]); DUMPREGA64(fp); DUMPREGA64(lr); DUMPREGA64(sp); Printf("\n");
1783# elif defined(__arm__)
1784# define DUMPREG(r) DUMPREG32(r)
1785 DUMPREG_(r[0]); DUMPREG_(r[1]); DUMPREG_(r[2]); DUMPREG_(r[3]); Printf("\n");
1786 DUMPREG_(r[4]); DUMPREG_(r[5]); DUMPREG_(r[6]); DUMPREG_(r[7]); Printf("\n");
1787 DUMPREG_(r[8]); DUMPREG_(r[9]); DUMPREG(r[10]); DUMPREG(r[11]); Printf("\n");
1788 DUMPREG(r[12]); DUMPREG___(sp); DUMPREG___(lr); DUMPREG___(pc); Printf("\n");
1789# else
1790# error "Unknown architecture"
1791# endif
1792
1793# undef DUMPREG64
1794# undef DUMPREG32
1795# undef DUMPREG_
1796# undef DUMPREG__
1797# undef DUMPREG___
1798# undef DUMPREG
1799}
1800
1801static inline bool CompareBaseAddress(const LoadedModule &a,
1802 const LoadedModule &b) {
1803 return a.base_address() < b.base_address();
1804}
1805
1806void FormatUUID(char *out, uptr size, const u8 *uuid) {
1807 internal_snprintf(out, size,
1808 "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-"
1809 "%02X%02X%02X%02X%02X%02X>",
1810 uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
1811 uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
1812 uuid[12], uuid[13], uuid[14], uuid[15]);
1813}
1814
1815void DumpProcessMap() {
1816 Printf("Process module map:\n");
1817 MemoryMappingLayout memory_mapping(false);
1818 InternalMmapVector<LoadedModule> modules;
1819 modules.reserve(128);
1820 memory_mapping.DumpListOfModules(&modules);
1821 Sort(modules.data(), modules.size(), CompareBaseAddress);
1822 for (uptr i = 0; i < modules.size(); ++i) {
1823 char uuid_str[128];
1824 FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid());
1825 Printf("%p-%p %s (%s) %s\n", (void *)modules[i].base_address(),
1826 (void *)modules[i].max_address(), modules[i].full_name(),
1827 ModuleArchToString(modules[i].arch()), uuid_str);
1828 }
1829 Printf("End of module map.\n");
1830}
1831
1832void OnDlOpen(const char* filename, int flag) {
1833 // Do nothing.
1834}
1835
1836bool GetRandom(void *buffer, uptr length, bool blocking) {
1837 if (!buffer || !length || length > 256)
1838 return false;
1839 // arc4random never fails.
1840 REAL(arc4random_buf)(buffer, length);
1841 return true;
1842}
1843
1844u32 GetNumberOfCPUs() {
1845 return (u32)sysconf(_SC_NPROCESSORS_ONLN);
1846}
1847
1848void InitializePlatformCommonFlags(CommonFlags *cf) {}
1849
1850// Pthread introspection hook
1851//
1852// * GCD worker threads are created without a call to pthread_create(), but we
1853// still need to register these threads (with ThreadCreate/Start()).
1854// * We use the "pthread introspection hook" below to observe the creation of
1855// such threads.
1856// * GCD worker threads don't have parent threads and the CREATE event is
1857// delivered in the context of the thread itself. CREATE events for regular
1858// threads, are delivered on the parent. We use this to tell apart which
1859// threads are GCD workers with `thread == pthread_self()`.
1860//
1861static pthread_introspection_hook_t prev_pthread_introspection_hook;
1862static ThreadEventCallbacks thread_event_callbacks;
1863
1864static void sanitizer_pthread_introspection_hook(unsigned int event,
1865 pthread_t thread, void *addr,
1866 size_t size) {
1867 // create -> start -> terminate -> destroy
1868 // * create/destroy are usually (not guaranteed) delivered on the parent and
1869 // track resource allocation/reclamation
1870 // * start/terminate are guaranteed to be delivered in the context of the
1871 // thread and give hooks into "just after (before) thread starts (stops)
1872 // executing"
1873 DCHECK(event >= PTHREAD_INTROSPECTION_THREAD_CREATE &&
1874 event <= PTHREAD_INTROSPECTION_THREAD_DESTROY);
1875
1876 if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) {
1877 bool gcd_worker = (thread == pthread_self());
1878 if (thread_event_callbacks.create)
1879 thread_event_callbacks.create((uptr)thread, gcd_worker);
1880 } else if (event == PTHREAD_INTROSPECTION_THREAD_START) {
1881 CHECK_EQ(thread, pthread_self());
1882 if (thread_event_callbacks.start)
1883 thread_event_callbacks.start((uptr)thread);
1884 }
1885
1886 if (prev_pthread_introspection_hook)
1887 prev_pthread_introspection_hook(event, thread, addr, size);
1888
1889 if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) {
1890 CHECK_EQ(thread, pthread_self());
1891 if (thread_event_callbacks.terminate)
1892 thread_event_callbacks.terminate((uptr)thread);
1893 } else if (event == PTHREAD_INTROSPECTION_THREAD_DESTROY) {
1894 if (thread_event_callbacks.destroy)
1895 thread_event_callbacks.destroy((uptr)thread);
1896 }
1897}
1898
1899void InstallPthreadIntrospectionHook(const ThreadEventCallbacks &callbacks) {
1900 thread_event_callbacks = callbacks;
1901 prev_pthread_introspection_hook =
1902 pthread_introspection_hook_install(&sanitizer_pthread_introspection_hook);
1903}
1904
1905} // namespace __sanitizer
1906
1907#endif // SANITIZER_APPLE
1908