13cab2bb3Spatrick //===-- sanitizer_mac.cpp -------------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is shared between various sanitizers' runtime libraries and
103cab2bb3Spatrick // implements OSX-specific functions.
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick
133cab2bb3Spatrick #include "sanitizer_platform.h"
14*810390e3Srobert #if SANITIZER_APPLE
153cab2bb3Spatrick #include "sanitizer_mac.h"
163cab2bb3Spatrick #include "interception/interception.h"
173cab2bb3Spatrick
183cab2bb3Spatrick // Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so
193cab2bb3Spatrick // the clients will most certainly use 64-bit ones as well.
203cab2bb3Spatrick #ifndef _DARWIN_USE_64_BIT_INODE
213cab2bb3Spatrick #define _DARWIN_USE_64_BIT_INODE 1
223cab2bb3Spatrick #endif
233cab2bb3Spatrick #include <stdio.h>
243cab2bb3Spatrick
253cab2bb3Spatrick #include "sanitizer_common.h"
263cab2bb3Spatrick #include "sanitizer_file.h"
273cab2bb3Spatrick #include "sanitizer_flags.h"
28*810390e3Srobert #include "sanitizer_interface_internal.h"
293cab2bb3Spatrick #include "sanitizer_internal_defs.h"
303cab2bb3Spatrick #include "sanitizer_libc.h"
313cab2bb3Spatrick #include "sanitizer_platform_limits_posix.h"
323cab2bb3Spatrick #include "sanitizer_procmaps.h"
331f9cb04fSpatrick #include "sanitizer_ptrauth.h"
343cab2bb3Spatrick
353cab2bb3Spatrick #if !SANITIZER_IOS
363cab2bb3Spatrick #include <crt_externs.h> // for _NSGetEnviron
373cab2bb3Spatrick #else
383cab2bb3Spatrick extern char **environ;
393cab2bb3Spatrick #endif
403cab2bb3Spatrick
413cab2bb3Spatrick #if defined(__has_include) && __has_include(<os/trace.h>)
423cab2bb3Spatrick #define SANITIZER_OS_TRACE 1
433cab2bb3Spatrick #include <os/trace.h>
443cab2bb3Spatrick #else
453cab2bb3Spatrick #define SANITIZER_OS_TRACE 0
463cab2bb3Spatrick #endif
473cab2bb3Spatrick
48d89ec533Spatrick // import new crash reporting api
49d89ec533Spatrick #if defined(__has_include) && __has_include(<CrashReporterClient.h>)
50d89ec533Spatrick #define HAVE_CRASHREPORTERCLIENT_H 1
51d89ec533Spatrick #include <CrashReporterClient.h>
52d89ec533Spatrick #else
53d89ec533Spatrick #define HAVE_CRASHREPORTERCLIENT_H 0
54d89ec533Spatrick #endif
55d89ec533Spatrick
563cab2bb3Spatrick #if !SANITIZER_IOS
573cab2bb3Spatrick #include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
583cab2bb3Spatrick #else
593cab2bb3Spatrick extern "C" {
603cab2bb3Spatrick extern char ***_NSGetArgv(void);
613cab2bb3Spatrick }
623cab2bb3Spatrick #endif
633cab2bb3Spatrick
643cab2bb3Spatrick #include <asl.h>
653cab2bb3Spatrick #include <dlfcn.h> // for dladdr()
663cab2bb3Spatrick #include <errno.h>
673cab2bb3Spatrick #include <fcntl.h>
683cab2bb3Spatrick #include <libkern/OSAtomic.h>
693cab2bb3Spatrick #include <mach-o/dyld.h>
703cab2bb3Spatrick #include <mach/mach.h>
713cab2bb3Spatrick #include <mach/mach_time.h>
723cab2bb3Spatrick #include <mach/vm_statistics.h>
733cab2bb3Spatrick #include <malloc/malloc.h>
74d89ec533Spatrick #include <os/log.h>
753cab2bb3Spatrick #include <pthread.h>
76*810390e3Srobert #include <pthread/introspection.h>
773cab2bb3Spatrick #include <sched.h>
783cab2bb3Spatrick #include <signal.h>
793cab2bb3Spatrick #include <spawn.h>
803cab2bb3Spatrick #include <stdlib.h>
813cab2bb3Spatrick #include <sys/ioctl.h>
823cab2bb3Spatrick #include <sys/mman.h>
833cab2bb3Spatrick #include <sys/resource.h>
843cab2bb3Spatrick #include <sys/stat.h>
853cab2bb3Spatrick #include <sys/sysctl.h>
863cab2bb3Spatrick #include <sys/types.h>
873cab2bb3Spatrick #include <sys/wait.h>
883cab2bb3Spatrick #include <unistd.h>
893cab2bb3Spatrick #include <util.h>
903cab2bb3Spatrick
913cab2bb3Spatrick // From <crt_externs.h>, but we don't have that file on iOS.
923cab2bb3Spatrick extern "C" {
933cab2bb3Spatrick extern char ***_NSGetArgv(void);
943cab2bb3Spatrick extern char ***_NSGetEnviron(void);
953cab2bb3Spatrick }
963cab2bb3Spatrick
973cab2bb3Spatrick // From <mach/mach_vm.h>, but we don't have that file on iOS.
983cab2bb3Spatrick extern "C" {
993cab2bb3Spatrick extern kern_return_t mach_vm_region_recurse(
1003cab2bb3Spatrick vm_map_t target_task,
1013cab2bb3Spatrick mach_vm_address_t *address,
1023cab2bb3Spatrick mach_vm_size_t *size,
1033cab2bb3Spatrick natural_t *nesting_depth,
1043cab2bb3Spatrick vm_region_recurse_info_t info,
1053cab2bb3Spatrick mach_msg_type_number_t *infoCnt);
1063cab2bb3Spatrick }
1073cab2bb3Spatrick
1083cab2bb3Spatrick namespace __sanitizer {
1093cab2bb3Spatrick
1103cab2bb3Spatrick #include "sanitizer_syscall_generic.inc"
1113cab2bb3Spatrick
1123cab2bb3Spatrick // Direct syscalls, don't call libmalloc hooks (but not available on 10.6).
1133cab2bb3Spatrick extern "C" void *__mmap(void *addr, size_t len, int prot, int flags, int fildes,
1143cab2bb3Spatrick off_t off) SANITIZER_WEAK_ATTRIBUTE;
1153cab2bb3Spatrick extern "C" int __munmap(void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
1163cab2bb3Spatrick
1173cab2bb3Spatrick // ---------------------- sanitizer_libc.h
1183cab2bb3Spatrick
1193cab2bb3Spatrick // From <mach/vm_statistics.h>, but not on older OSs.
1203cab2bb3Spatrick #ifndef VM_MEMORY_SANITIZER
1213cab2bb3Spatrick #define VM_MEMORY_SANITIZER 99
1223cab2bb3Spatrick #endif
1233cab2bb3Spatrick
1243cab2bb3Spatrick // XNU on Darwin provides a mmap flag that optimizes allocation/deallocation of
1253cab2bb3Spatrick // giant memory regions (i.e. shadow memory regions).
1263cab2bb3Spatrick #define kXnuFastMmapFd 0x4
1273cab2bb3Spatrick static size_t kXnuFastMmapThreshold = 2 << 30; // 2 GB
1283cab2bb3Spatrick static bool use_xnu_fast_mmap = false;
1293cab2bb3Spatrick
internal_mmap(void * addr,size_t length,int prot,int flags,int fd,u64 offset)1303cab2bb3Spatrick uptr internal_mmap(void *addr, size_t length, int prot, int flags,
1313cab2bb3Spatrick int fd, u64 offset) {
1323cab2bb3Spatrick if (fd == -1) {
1333cab2bb3Spatrick fd = VM_MAKE_TAG(VM_MEMORY_SANITIZER);
1343cab2bb3Spatrick if (length >= kXnuFastMmapThreshold) {
1353cab2bb3Spatrick if (use_xnu_fast_mmap) fd |= kXnuFastMmapFd;
1363cab2bb3Spatrick }
1373cab2bb3Spatrick }
1383cab2bb3Spatrick if (&__mmap) return (uptr)__mmap(addr, length, prot, flags, fd, offset);
1393cab2bb3Spatrick return (uptr)mmap(addr, length, prot, flags, fd, offset);
1403cab2bb3Spatrick }
1413cab2bb3Spatrick
internal_munmap(void * addr,uptr length)1423cab2bb3Spatrick uptr internal_munmap(void *addr, uptr length) {
1433cab2bb3Spatrick if (&__munmap) return __munmap(addr, length);
1443cab2bb3Spatrick return munmap(addr, length);
1453cab2bb3Spatrick }
1463cab2bb3Spatrick
internal_mremap(void * old_address,uptr old_size,uptr new_size,int flags,void * new_address)147d89ec533Spatrick uptr internal_mremap(void *old_address, uptr old_size, uptr new_size, int flags,
148d89ec533Spatrick void *new_address) {
149d89ec533Spatrick CHECK(false && "internal_mremap is unimplemented on Mac");
150d89ec533Spatrick return 0;
151d89ec533Spatrick }
152d89ec533Spatrick
internal_mprotect(void * addr,uptr length,int prot)1533cab2bb3Spatrick int internal_mprotect(void *addr, uptr length, int prot) {
1543cab2bb3Spatrick return mprotect(addr, length, prot);
1553cab2bb3Spatrick }
1563cab2bb3Spatrick
internal_madvise(uptr addr,uptr length,int advice)157d89ec533Spatrick int internal_madvise(uptr addr, uptr length, int advice) {
158d89ec533Spatrick return madvise((void *)addr, length, advice);
159d89ec533Spatrick }
160d89ec533Spatrick
internal_close(fd_t fd)1613cab2bb3Spatrick uptr internal_close(fd_t fd) {
1623cab2bb3Spatrick return close(fd);
1633cab2bb3Spatrick }
1643cab2bb3Spatrick
internal_open(const char * filename,int flags)1653cab2bb3Spatrick uptr internal_open(const char *filename, int flags) {
1663cab2bb3Spatrick return open(filename, flags);
1673cab2bb3Spatrick }
1683cab2bb3Spatrick
internal_open(const char * filename,int flags,u32 mode)1693cab2bb3Spatrick uptr internal_open(const char *filename, int flags, u32 mode) {
1703cab2bb3Spatrick return open(filename, flags, mode);
1713cab2bb3Spatrick }
1723cab2bb3Spatrick
internal_read(fd_t fd,void * buf,uptr count)1733cab2bb3Spatrick uptr internal_read(fd_t fd, void *buf, uptr count) {
1743cab2bb3Spatrick return read(fd, buf, count);
1753cab2bb3Spatrick }
1763cab2bb3Spatrick
internal_write(fd_t fd,const void * buf,uptr count)1773cab2bb3Spatrick uptr internal_write(fd_t fd, const void *buf, uptr count) {
1783cab2bb3Spatrick return write(fd, buf, count);
1793cab2bb3Spatrick }
1803cab2bb3Spatrick
internal_stat(const char * path,void * buf)1813cab2bb3Spatrick uptr internal_stat(const char *path, void *buf) {
1823cab2bb3Spatrick return stat(path, (struct stat *)buf);
1833cab2bb3Spatrick }
1843cab2bb3Spatrick
internal_lstat(const char * path,void * buf)1853cab2bb3Spatrick uptr internal_lstat(const char *path, void *buf) {
1863cab2bb3Spatrick return lstat(path, (struct stat *)buf);
1873cab2bb3Spatrick }
1883cab2bb3Spatrick
internal_fstat(fd_t fd,void * buf)1893cab2bb3Spatrick uptr internal_fstat(fd_t fd, void *buf) {
1903cab2bb3Spatrick return fstat(fd, (struct stat *)buf);
1913cab2bb3Spatrick }
1923cab2bb3Spatrick
internal_filesize(fd_t fd)1933cab2bb3Spatrick uptr internal_filesize(fd_t fd) {
1943cab2bb3Spatrick struct stat st;
1953cab2bb3Spatrick if (internal_fstat(fd, &st))
1963cab2bb3Spatrick return -1;
1973cab2bb3Spatrick return (uptr)st.st_size;
1983cab2bb3Spatrick }
1993cab2bb3Spatrick
internal_dup(int oldfd)2003cab2bb3Spatrick uptr internal_dup(int oldfd) {
2013cab2bb3Spatrick return dup(oldfd);
2023cab2bb3Spatrick }
2033cab2bb3Spatrick
internal_dup2(int oldfd,int newfd)2043cab2bb3Spatrick uptr internal_dup2(int oldfd, int newfd) {
2053cab2bb3Spatrick return dup2(oldfd, newfd);
2063cab2bb3Spatrick }
2073cab2bb3Spatrick
internal_readlink(const char * path,char * buf,uptr bufsize)2083cab2bb3Spatrick uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
2093cab2bb3Spatrick return readlink(path, buf, bufsize);
2103cab2bb3Spatrick }
2113cab2bb3Spatrick
internal_unlink(const char * path)2123cab2bb3Spatrick uptr internal_unlink(const char *path) {
2133cab2bb3Spatrick return unlink(path);
2143cab2bb3Spatrick }
2153cab2bb3Spatrick
internal_sched_yield()2163cab2bb3Spatrick uptr internal_sched_yield() {
2173cab2bb3Spatrick return sched_yield();
2183cab2bb3Spatrick }
2193cab2bb3Spatrick
internal__exit(int exitcode)2203cab2bb3Spatrick void internal__exit(int exitcode) {
2213cab2bb3Spatrick _exit(exitcode);
2223cab2bb3Spatrick }
2233cab2bb3Spatrick
internal_usleep(u64 useconds)224d89ec533Spatrick void internal_usleep(u64 useconds) { usleep(useconds); }
2253cab2bb3Spatrick
internal_getpid()2263cab2bb3Spatrick uptr internal_getpid() {
2273cab2bb3Spatrick return getpid();
2283cab2bb3Spatrick }
2293cab2bb3Spatrick
internal_dlinfo(void * handle,int request,void * p)2301f9cb04fSpatrick int internal_dlinfo(void *handle, int request, void *p) {
2311f9cb04fSpatrick UNIMPLEMENTED();
2321f9cb04fSpatrick }
2331f9cb04fSpatrick
internal_sigaction(int signum,const void * act,void * oldact)2343cab2bb3Spatrick int internal_sigaction(int signum, const void *act, void *oldact) {
2353cab2bb3Spatrick return sigaction(signum,
2363cab2bb3Spatrick (const struct sigaction *)act, (struct sigaction *)oldact);
2373cab2bb3Spatrick }
2383cab2bb3Spatrick
internal_sigfillset(__sanitizer_sigset_t * set)2393cab2bb3Spatrick void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); }
2403cab2bb3Spatrick
internal_sigprocmask(int how,__sanitizer_sigset_t * set,__sanitizer_sigset_t * oldset)2413cab2bb3Spatrick uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
2423cab2bb3Spatrick __sanitizer_sigset_t *oldset) {
2433cab2bb3Spatrick // Don't use sigprocmask here, because it affects all threads.
2443cab2bb3Spatrick return pthread_sigmask(how, set, oldset);
2453cab2bb3Spatrick }
2463cab2bb3Spatrick
2473cab2bb3Spatrick // Doesn't call pthread_atfork() handlers (but not available on 10.6).
2483cab2bb3Spatrick extern "C" pid_t __fork(void) SANITIZER_WEAK_ATTRIBUTE;
2493cab2bb3Spatrick
internal_fork()2503cab2bb3Spatrick int internal_fork() {
2513cab2bb3Spatrick if (&__fork)
2523cab2bb3Spatrick return __fork();
2533cab2bb3Spatrick return fork();
2543cab2bb3Spatrick }
2553cab2bb3Spatrick
internal_sysctl(const int * name,unsigned int namelen,void * oldp,uptr * oldlenp,const void * newp,uptr newlen)2563cab2bb3Spatrick int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
2573cab2bb3Spatrick uptr *oldlenp, const void *newp, uptr newlen) {
2583cab2bb3Spatrick return sysctl(const_cast<int *>(name), namelen, oldp, (size_t *)oldlenp,
2593cab2bb3Spatrick const_cast<void *>(newp), (size_t)newlen);
2603cab2bb3Spatrick }
2613cab2bb3Spatrick
internal_sysctlbyname(const char * sname,void * oldp,uptr * oldlenp,const void * newp,uptr newlen)2623cab2bb3Spatrick int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
2633cab2bb3Spatrick const void *newp, uptr newlen) {
2643cab2bb3Spatrick return sysctlbyname(sname, oldp, (size_t *)oldlenp, const_cast<void *>(newp),
2653cab2bb3Spatrick (size_t)newlen);
2663cab2bb3Spatrick }
2673cab2bb3Spatrick
internal_spawn_impl(const char * argv[],const char * envp[],pid_t * pid)2681f9cb04fSpatrick static fd_t internal_spawn_impl(const char *argv[], const char *envp[],
2691f9cb04fSpatrick pid_t *pid) {
270*810390e3Srobert fd_t primary_fd = kInvalidFd;
271*810390e3Srobert fd_t secondary_fd = kInvalidFd;
2723cab2bb3Spatrick
2733cab2bb3Spatrick auto fd_closer = at_scope_exit([&] {
274*810390e3Srobert internal_close(primary_fd);
275*810390e3Srobert internal_close(secondary_fd);
2763cab2bb3Spatrick });
2773cab2bb3Spatrick
2783cab2bb3Spatrick // We need a new pseudoterminal to avoid buffering problems. The 'atos' tool
2793cab2bb3Spatrick // in particular detects when it's talking to a pipe and forgets to flush the
2803cab2bb3Spatrick // output stream after sending a response.
281*810390e3Srobert primary_fd = posix_openpt(O_RDWR);
282*810390e3Srobert if (primary_fd == kInvalidFd)
283*810390e3Srobert return kInvalidFd;
2843cab2bb3Spatrick
285*810390e3Srobert int res = grantpt(primary_fd) || unlockpt(primary_fd);
2863cab2bb3Spatrick if (res != 0) return kInvalidFd;
2873cab2bb3Spatrick
2883cab2bb3Spatrick // Use TIOCPTYGNAME instead of ptsname() to avoid threading problems.
289*810390e3Srobert char secondary_pty_name[128];
290*810390e3Srobert res = ioctl(primary_fd, TIOCPTYGNAME, secondary_pty_name);
2913cab2bb3Spatrick if (res == -1) return kInvalidFd;
2923cab2bb3Spatrick
293*810390e3Srobert secondary_fd = internal_open(secondary_pty_name, O_RDWR);
294*810390e3Srobert if (secondary_fd == kInvalidFd)
295*810390e3Srobert return kInvalidFd;
2963cab2bb3Spatrick
2973cab2bb3Spatrick // File descriptor actions
2983cab2bb3Spatrick posix_spawn_file_actions_t acts;
2993cab2bb3Spatrick res = posix_spawn_file_actions_init(&acts);
3003cab2bb3Spatrick if (res != 0) return kInvalidFd;
3013cab2bb3Spatrick
3023cab2bb3Spatrick auto acts_cleanup = at_scope_exit([&] {
3033cab2bb3Spatrick posix_spawn_file_actions_destroy(&acts);
3043cab2bb3Spatrick });
3053cab2bb3Spatrick
306*810390e3Srobert res = posix_spawn_file_actions_adddup2(&acts, secondary_fd, STDIN_FILENO) ||
307*810390e3Srobert posix_spawn_file_actions_adddup2(&acts, secondary_fd, STDOUT_FILENO) ||
308*810390e3Srobert posix_spawn_file_actions_addclose(&acts, secondary_fd);
3093cab2bb3Spatrick if (res != 0) return kInvalidFd;
3103cab2bb3Spatrick
3113cab2bb3Spatrick // Spawn attributes
3123cab2bb3Spatrick posix_spawnattr_t attrs;
3133cab2bb3Spatrick res = posix_spawnattr_init(&attrs);
3143cab2bb3Spatrick if (res != 0) return kInvalidFd;
3153cab2bb3Spatrick
3163cab2bb3Spatrick auto attrs_cleanup = at_scope_exit([&] {
3173cab2bb3Spatrick posix_spawnattr_destroy(&attrs);
3183cab2bb3Spatrick });
3193cab2bb3Spatrick
3203cab2bb3Spatrick // In the spawned process, close all file descriptors that are not explicitly
3213cab2bb3Spatrick // described by the file actions object. This is Darwin-specific extension.
3223cab2bb3Spatrick res = posix_spawnattr_setflags(&attrs, POSIX_SPAWN_CLOEXEC_DEFAULT);
3233cab2bb3Spatrick if (res != 0) return kInvalidFd;
3243cab2bb3Spatrick
3253cab2bb3Spatrick // posix_spawn
3263cab2bb3Spatrick char **argv_casted = const_cast<char **>(argv);
3271f9cb04fSpatrick char **envp_casted = const_cast<char **>(envp);
3281f9cb04fSpatrick res = posix_spawn(pid, argv[0], &acts, &attrs, argv_casted, envp_casted);
3293cab2bb3Spatrick if (res != 0) return kInvalidFd;
3303cab2bb3Spatrick
3313cab2bb3Spatrick // Disable echo in the new terminal, disable CR.
3323cab2bb3Spatrick struct termios termflags;
333*810390e3Srobert tcgetattr(primary_fd, &termflags);
3343cab2bb3Spatrick termflags.c_oflag &= ~ONLCR;
3353cab2bb3Spatrick termflags.c_lflag &= ~ECHO;
336*810390e3Srobert tcsetattr(primary_fd, TCSANOW, &termflags);
3373cab2bb3Spatrick
338*810390e3Srobert // On success, do not close primary_fd on scope exit.
339*810390e3Srobert fd_t fd = primary_fd;
340*810390e3Srobert primary_fd = kInvalidFd;
3413cab2bb3Spatrick
3423cab2bb3Spatrick return fd;
3433cab2bb3Spatrick }
3443cab2bb3Spatrick
internal_spawn(const char * argv[],const char * envp[],pid_t * pid)3451f9cb04fSpatrick fd_t internal_spawn(const char *argv[], const char *envp[], pid_t *pid) {
3463cab2bb3Spatrick // The client program may close its stdin and/or stdout and/or stderr thus
3473cab2bb3Spatrick // allowing open/posix_openpt to reuse file descriptors 0, 1 or 2. In this
3483cab2bb3Spatrick // case the communication is broken if either the parent or the child tries to
3493cab2bb3Spatrick // close or duplicate these descriptors. We temporarily reserve these
3503cab2bb3Spatrick // descriptors here to prevent this.
3513cab2bb3Spatrick fd_t low_fds[3];
3523cab2bb3Spatrick size_t count = 0;
3533cab2bb3Spatrick
3543cab2bb3Spatrick for (; count < 3; count++) {
3553cab2bb3Spatrick low_fds[count] = posix_openpt(O_RDWR);
3563cab2bb3Spatrick if (low_fds[count] >= STDERR_FILENO)
3573cab2bb3Spatrick break;
3583cab2bb3Spatrick }
3593cab2bb3Spatrick
3601f9cb04fSpatrick fd_t fd = internal_spawn_impl(argv, envp, pid);
3613cab2bb3Spatrick
3623cab2bb3Spatrick for (; count > 0; count--) {
3633cab2bb3Spatrick internal_close(low_fds[count]);
3643cab2bb3Spatrick }
3653cab2bb3Spatrick
3663cab2bb3Spatrick return fd;
3673cab2bb3Spatrick }
3683cab2bb3Spatrick
internal_rename(const char * oldpath,const char * newpath)3693cab2bb3Spatrick uptr internal_rename(const char *oldpath, const char *newpath) {
3703cab2bb3Spatrick return rename(oldpath, newpath);
3713cab2bb3Spatrick }
3723cab2bb3Spatrick
internal_ftruncate(fd_t fd,uptr size)3733cab2bb3Spatrick uptr internal_ftruncate(fd_t fd, uptr size) {
3743cab2bb3Spatrick return ftruncate(fd, size);
3753cab2bb3Spatrick }
3763cab2bb3Spatrick
internal_execve(const char * filename,char * const argv[],char * const envp[])3773cab2bb3Spatrick uptr internal_execve(const char *filename, char *const argv[],
3783cab2bb3Spatrick char *const envp[]) {
3793cab2bb3Spatrick return execve(filename, argv, envp);
3803cab2bb3Spatrick }
3813cab2bb3Spatrick
internal_waitpid(int pid,int * status,int options)3823cab2bb3Spatrick uptr internal_waitpid(int pid, int *status, int options) {
3833cab2bb3Spatrick return waitpid(pid, status, options);
3843cab2bb3Spatrick }
3853cab2bb3Spatrick
3863cab2bb3Spatrick // ----------------- sanitizer_common.h
FileExists(const char * filename)3873cab2bb3Spatrick bool FileExists(const char *filename) {
3883cab2bb3Spatrick if (ShouldMockFailureToOpen(filename))
3893cab2bb3Spatrick return false;
3903cab2bb3Spatrick struct stat st;
3913cab2bb3Spatrick if (stat(filename, &st))
3923cab2bb3Spatrick return false;
3933cab2bb3Spatrick // Sanity check: filename is a regular file.
3943cab2bb3Spatrick return S_ISREG(st.st_mode);
3953cab2bb3Spatrick }
3963cab2bb3Spatrick
DirExists(const char * path)397*810390e3Srobert bool DirExists(const char *path) {
398*810390e3Srobert struct stat st;
399*810390e3Srobert if (stat(path, &st))
400*810390e3Srobert return false;
401*810390e3Srobert return S_ISDIR(st.st_mode);
402*810390e3Srobert }
403*810390e3Srobert
GetTid()4043cab2bb3Spatrick tid_t GetTid() {
4053cab2bb3Spatrick tid_t tid;
4063cab2bb3Spatrick pthread_threadid_np(nullptr, &tid);
4073cab2bb3Spatrick return tid;
4083cab2bb3Spatrick }
4093cab2bb3Spatrick
GetThreadStackTopAndBottom(bool at_initialization,uptr * stack_top,uptr * stack_bottom)4103cab2bb3Spatrick void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
4113cab2bb3Spatrick uptr *stack_bottom) {
4123cab2bb3Spatrick CHECK(stack_top);
4133cab2bb3Spatrick CHECK(stack_bottom);
4143cab2bb3Spatrick uptr stacksize = pthread_get_stacksize_np(pthread_self());
4153cab2bb3Spatrick // pthread_get_stacksize_np() returns an incorrect stack size for the main
4163cab2bb3Spatrick // thread on Mavericks. See
4173cab2bb3Spatrick // https://github.com/google/sanitizers/issues/261
4181f9cb04fSpatrick if ((GetMacosAlignedVersion() >= MacosVersion(10, 9)) && at_initialization &&
4193cab2bb3Spatrick stacksize == (1 << 19)) {
4203cab2bb3Spatrick struct rlimit rl;
4213cab2bb3Spatrick CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
4223cab2bb3Spatrick // Most often rl.rlim_cur will be the desired 8M.
4233cab2bb3Spatrick if (rl.rlim_cur < kMaxThreadStackSize) {
4243cab2bb3Spatrick stacksize = rl.rlim_cur;
4253cab2bb3Spatrick } else {
4263cab2bb3Spatrick stacksize = kMaxThreadStackSize;
4273cab2bb3Spatrick }
4283cab2bb3Spatrick }
4293cab2bb3Spatrick void *stackaddr = pthread_get_stackaddr_np(pthread_self());
4303cab2bb3Spatrick *stack_top = (uptr)stackaddr;
4313cab2bb3Spatrick *stack_bottom = *stack_top - stacksize;
4323cab2bb3Spatrick }
4333cab2bb3Spatrick
GetEnviron()4343cab2bb3Spatrick char **GetEnviron() {
4353cab2bb3Spatrick #if !SANITIZER_IOS
4363cab2bb3Spatrick char ***env_ptr = _NSGetEnviron();
4373cab2bb3Spatrick if (!env_ptr) {
4383cab2bb3Spatrick Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
4393cab2bb3Spatrick "called after libSystem_initializer().\n");
4403cab2bb3Spatrick CHECK(env_ptr);
4413cab2bb3Spatrick }
4423cab2bb3Spatrick char **environ = *env_ptr;
4433cab2bb3Spatrick #endif
4443cab2bb3Spatrick CHECK(environ);
4453cab2bb3Spatrick return environ;
4463cab2bb3Spatrick }
4473cab2bb3Spatrick
GetEnv(const char * name)4483cab2bb3Spatrick const char *GetEnv(const char *name) {
4493cab2bb3Spatrick char **env = GetEnviron();
4503cab2bb3Spatrick uptr name_len = internal_strlen(name);
4513cab2bb3Spatrick while (*env != 0) {
4523cab2bb3Spatrick uptr len = internal_strlen(*env);
4533cab2bb3Spatrick if (len > name_len) {
4543cab2bb3Spatrick const char *p = *env;
4553cab2bb3Spatrick if (!internal_memcmp(p, name, name_len) &&
4563cab2bb3Spatrick p[name_len] == '=') { // Match.
4573cab2bb3Spatrick return *env + name_len + 1; // String starting after =.
4583cab2bb3Spatrick }
4593cab2bb3Spatrick }
4603cab2bb3Spatrick env++;
4613cab2bb3Spatrick }
4623cab2bb3Spatrick return 0;
4633cab2bb3Spatrick }
4643cab2bb3Spatrick
ReadBinaryName(char * buf,uptr buf_len)4653cab2bb3Spatrick uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
4663cab2bb3Spatrick CHECK_LE(kMaxPathLength, buf_len);
4673cab2bb3Spatrick
4683cab2bb3Spatrick // On OS X the executable path is saved to the stack by dyld. Reading it
4693cab2bb3Spatrick // from there is much faster than calling dladdr, especially for large
4703cab2bb3Spatrick // binaries with symbols.
471d89ec533Spatrick InternalMmapVector<char> exe_path(kMaxPathLength);
4723cab2bb3Spatrick uint32_t size = exe_path.size();
4733cab2bb3Spatrick if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
4743cab2bb3Spatrick realpath(exe_path.data(), buf) != 0) {
4753cab2bb3Spatrick return internal_strlen(buf);
4763cab2bb3Spatrick }
4773cab2bb3Spatrick return 0;
4783cab2bb3Spatrick }
4793cab2bb3Spatrick
ReadLongProcessName(char * buf,uptr buf_len)4803cab2bb3Spatrick uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
4813cab2bb3Spatrick return ReadBinaryName(buf, buf_len);
4823cab2bb3Spatrick }
4833cab2bb3Spatrick
ReExec()4843cab2bb3Spatrick void ReExec() {
4853cab2bb3Spatrick UNIMPLEMENTED();
4863cab2bb3Spatrick }
4873cab2bb3Spatrick
CheckASLR()4883cab2bb3Spatrick void CheckASLR() {
4893cab2bb3Spatrick // Do nothing
4903cab2bb3Spatrick }
4913cab2bb3Spatrick
CheckMPROTECT()4923cab2bb3Spatrick void CheckMPROTECT() {
4933cab2bb3Spatrick // Do nothing
4943cab2bb3Spatrick }
4953cab2bb3Spatrick
GetPageSize()4963cab2bb3Spatrick uptr GetPageSize() {
4973cab2bb3Spatrick return sysconf(_SC_PAGESIZE);
4983cab2bb3Spatrick }
4993cab2bb3Spatrick
5003cab2bb3Spatrick extern "C" unsigned malloc_num_zones;
5013cab2bb3Spatrick extern "C" malloc_zone_t **malloc_zones;
5023cab2bb3Spatrick malloc_zone_t sanitizer_zone;
5033cab2bb3Spatrick
5043cab2bb3Spatrick // We need to make sure that sanitizer_zone is registered as malloc_zones[0]. If
5053cab2bb3Spatrick // libmalloc tries to set up a different zone as malloc_zones[0], it will call
5063cab2bb3Spatrick // mprotect(malloc_zones, ..., PROT_READ). This interceptor will catch that and
5073cab2bb3Spatrick // make sure we are still the first (default) zone.
MprotectMallocZones(void * addr,int prot)5083cab2bb3Spatrick void MprotectMallocZones(void *addr, int prot) {
5093cab2bb3Spatrick if (addr == malloc_zones && prot == PROT_READ) {
5103cab2bb3Spatrick if (malloc_num_zones > 1 && malloc_zones[0] != &sanitizer_zone) {
5113cab2bb3Spatrick for (unsigned i = 1; i < malloc_num_zones; i++) {
5123cab2bb3Spatrick if (malloc_zones[i] == &sanitizer_zone) {
5133cab2bb3Spatrick // Swap malloc_zones[0] and malloc_zones[i].
5143cab2bb3Spatrick malloc_zones[i] = malloc_zones[0];
5153cab2bb3Spatrick malloc_zones[0] = &sanitizer_zone;
5163cab2bb3Spatrick break;
5173cab2bb3Spatrick }
5183cab2bb3Spatrick }
5193cab2bb3Spatrick }
5203cab2bb3Spatrick }
5213cab2bb3Spatrick }
5223cab2bb3Spatrick
FutexWait(atomic_uint32_t * p,u32 cmp)523d89ec533Spatrick void FutexWait(atomic_uint32_t *p, u32 cmp) {
524d89ec533Spatrick // FIXME: implement actual blocking.
525d89ec533Spatrick sched_yield();
526d89ec533Spatrick }
527d89ec533Spatrick
FutexWake(atomic_uint32_t * p,u32 count)528d89ec533Spatrick void FutexWake(atomic_uint32_t *p, u32 count) {}
529d89ec533Spatrick
NanoTime()5303cab2bb3Spatrick u64 NanoTime() {
5313cab2bb3Spatrick timeval tv;
5323cab2bb3Spatrick internal_memset(&tv, 0, sizeof(tv));
5333cab2bb3Spatrick gettimeofday(&tv, 0);
5343cab2bb3Spatrick return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
5353cab2bb3Spatrick }
5363cab2bb3Spatrick
5373cab2bb3Spatrick // This needs to be called during initialization to avoid being racy.
MonotonicNanoTime()5383cab2bb3Spatrick u64 MonotonicNanoTime() {
5393cab2bb3Spatrick static mach_timebase_info_data_t timebase_info;
5403cab2bb3Spatrick if (timebase_info.denom == 0) mach_timebase_info(&timebase_info);
5413cab2bb3Spatrick return (mach_absolute_time() * timebase_info.numer) / timebase_info.denom;
5423cab2bb3Spatrick }
5433cab2bb3Spatrick
GetTlsSize()5443cab2bb3Spatrick uptr GetTlsSize() {
5453cab2bb3Spatrick return 0;
5463cab2bb3Spatrick }
5473cab2bb3Spatrick
InitTlsSize()5483cab2bb3Spatrick void InitTlsSize() {
5493cab2bb3Spatrick }
5503cab2bb3Spatrick
TlsBaseAddr()5513cab2bb3Spatrick uptr TlsBaseAddr() {
5523cab2bb3Spatrick uptr segbase = 0;
5533cab2bb3Spatrick #if defined(__x86_64__)
5543cab2bb3Spatrick asm("movq %%gs:0,%0" : "=r"(segbase));
5553cab2bb3Spatrick #elif defined(__i386__)
5563cab2bb3Spatrick asm("movl %%gs:0,%0" : "=r"(segbase));
557*810390e3Srobert #elif defined(__aarch64__)
558*810390e3Srobert asm("mrs %x0, tpidrro_el0" : "=r"(segbase));
559*810390e3Srobert segbase &= 0x07ul; // clearing lower bits, cpu id stored there
5603cab2bb3Spatrick #endif
5613cab2bb3Spatrick return segbase;
5623cab2bb3Spatrick }
5633cab2bb3Spatrick
5643cab2bb3Spatrick // The size of the tls on darwin does not appear to be well documented,
5653cab2bb3Spatrick // however the vm memory map suggests that it is 1024 uptrs in size,
5663cab2bb3Spatrick // with a size of 0x2000 bytes on x86_64 and 0x1000 bytes on i386.
TlsSize()5673cab2bb3Spatrick uptr TlsSize() {
5683cab2bb3Spatrick #if defined(__x86_64__) || defined(__i386__)
5693cab2bb3Spatrick return 1024 * sizeof(uptr);
5703cab2bb3Spatrick #else
5713cab2bb3Spatrick return 0;
5723cab2bb3Spatrick #endif
5733cab2bb3Spatrick }
5743cab2bb3Spatrick
GetThreadStackAndTls(bool main,uptr * stk_addr,uptr * stk_size,uptr * tls_addr,uptr * tls_size)5753cab2bb3Spatrick void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
5763cab2bb3Spatrick uptr *tls_addr, uptr *tls_size) {
5773cab2bb3Spatrick #if !SANITIZER_GO
5783cab2bb3Spatrick uptr stack_top, stack_bottom;
5793cab2bb3Spatrick GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
5803cab2bb3Spatrick *stk_addr = stack_bottom;
5813cab2bb3Spatrick *stk_size = stack_top - stack_bottom;
5823cab2bb3Spatrick *tls_addr = TlsBaseAddr();
5833cab2bb3Spatrick *tls_size = TlsSize();
5843cab2bb3Spatrick #else
5853cab2bb3Spatrick *stk_addr = 0;
5863cab2bb3Spatrick *stk_size = 0;
5873cab2bb3Spatrick *tls_addr = 0;
5883cab2bb3Spatrick *tls_size = 0;
5893cab2bb3Spatrick #endif
5903cab2bb3Spatrick }
5913cab2bb3Spatrick
init()5923cab2bb3Spatrick void ListOfModules::init() {
5933cab2bb3Spatrick clearOrInit();
5943cab2bb3Spatrick MemoryMappingLayout memory_mapping(false);
5953cab2bb3Spatrick memory_mapping.DumpListOfModules(&modules_);
5963cab2bb3Spatrick }
5973cab2bb3Spatrick
fallbackInit()5983cab2bb3Spatrick void ListOfModules::fallbackInit() { clear(); }
5993cab2bb3Spatrick
GetHandleSignalModeImpl(int signum)6003cab2bb3Spatrick static HandleSignalMode GetHandleSignalModeImpl(int signum) {
6013cab2bb3Spatrick switch (signum) {
6023cab2bb3Spatrick case SIGABRT:
6033cab2bb3Spatrick return common_flags()->handle_abort;
6043cab2bb3Spatrick case SIGILL:
6053cab2bb3Spatrick return common_flags()->handle_sigill;
6063cab2bb3Spatrick case SIGTRAP:
6073cab2bb3Spatrick return common_flags()->handle_sigtrap;
6083cab2bb3Spatrick case SIGFPE:
6093cab2bb3Spatrick return common_flags()->handle_sigfpe;
6103cab2bb3Spatrick case SIGSEGV:
6113cab2bb3Spatrick return common_flags()->handle_segv;
6123cab2bb3Spatrick case SIGBUS:
6133cab2bb3Spatrick return common_flags()->handle_sigbus;
6143cab2bb3Spatrick }
6153cab2bb3Spatrick return kHandleSignalNo;
6163cab2bb3Spatrick }
6173cab2bb3Spatrick
GetHandleSignalMode(int signum)6183cab2bb3Spatrick HandleSignalMode GetHandleSignalMode(int signum) {
6193cab2bb3Spatrick // Handling fatal signals on watchOS and tvOS devices is disallowed.
6203cab2bb3Spatrick if ((SANITIZER_WATCHOS || SANITIZER_TVOS) && !(SANITIZER_IOSSIM))
6213cab2bb3Spatrick return kHandleSignalNo;
6223cab2bb3Spatrick HandleSignalMode result = GetHandleSignalModeImpl(signum);
6233cab2bb3Spatrick if (result == kHandleSignalYes && !common_flags()->allow_user_segv_handler)
6243cab2bb3Spatrick return kHandleSignalExclusive;
6253cab2bb3Spatrick return result;
6263cab2bb3Spatrick }
6273cab2bb3Spatrick
628d89ec533Spatrick // Offset example:
629d89ec533Spatrick // XNU 17 -- macOS 10.13 -- iOS 11 -- tvOS 11 -- watchOS 4
GetOSMajorKernelOffset()630d89ec533Spatrick constexpr u16 GetOSMajorKernelOffset() {
631d89ec533Spatrick if (TARGET_OS_OSX) return 4;
632d89ec533Spatrick if (TARGET_OS_IOS || TARGET_OS_TV) return 6;
633d89ec533Spatrick if (TARGET_OS_WATCH) return 13;
6341f9cb04fSpatrick }
635d89ec533Spatrick
636d89ec533Spatrick using VersStr = char[64];
637d89ec533Spatrick
ApproximateOSVersionViaKernelVersion(VersStr vers)638d89ec533Spatrick static uptr ApproximateOSVersionViaKernelVersion(VersStr vers) {
639d89ec533Spatrick u16 kernel_major = GetDarwinKernelVersion().major;
640d89ec533Spatrick u16 offset = GetOSMajorKernelOffset();
641d89ec533Spatrick CHECK_GE(kernel_major, offset);
642d89ec533Spatrick u16 os_major = kernel_major - offset;
643d89ec533Spatrick
644d89ec533Spatrick const char *format = "%d.0";
645d89ec533Spatrick if (TARGET_OS_OSX) {
646d89ec533Spatrick if (os_major >= 16) { // macOS 11+
647d89ec533Spatrick os_major -= 5;
648d89ec533Spatrick } else { // macOS 10.15 and below
649d89ec533Spatrick format = "10.%d";
650d89ec533Spatrick }
651d89ec533Spatrick }
652d89ec533Spatrick return internal_snprintf(vers, sizeof(VersStr), format, os_major);
653d89ec533Spatrick }
654d89ec533Spatrick
GetOSVersion(VersStr vers)655d89ec533Spatrick static void GetOSVersion(VersStr vers) {
656d89ec533Spatrick uptr len = sizeof(VersStr);
657d89ec533Spatrick if (SANITIZER_IOSSIM) {
658d89ec533Spatrick const char *vers_env = GetEnv("SIMULATOR_RUNTIME_VERSION");
659d89ec533Spatrick if (!vers_env) {
660d89ec533Spatrick Report("ERROR: Running in simulator but SIMULATOR_RUNTIME_VERSION env "
661d89ec533Spatrick "var is not set.\n");
662d89ec533Spatrick Die();
663d89ec533Spatrick }
664d89ec533Spatrick len = internal_strlcpy(vers, vers_env, len);
665d89ec533Spatrick } else {
666d89ec533Spatrick int res =
667d89ec533Spatrick internal_sysctlbyname("kern.osproductversion", vers, &len, nullptr, 0);
668d89ec533Spatrick
669d89ec533Spatrick // XNU 17 (macOS 10.13) and below do not provide the sysctl
670d89ec533Spatrick // `kern.osproductversion` entry (res != 0).
671d89ec533Spatrick bool no_os_version = res != 0;
672d89ec533Spatrick
673d89ec533Spatrick // For launchd, sanitizer initialization runs before sysctl is setup
674d89ec533Spatrick // (res == 0 && len != strlen(vers), vers is not a valid version). However,
675d89ec533Spatrick // the kernel version `kern.osrelease` is available.
676d89ec533Spatrick bool launchd = (res == 0 && internal_strlen(vers) < 3);
677d89ec533Spatrick if (launchd) CHECK_EQ(internal_getpid(), 1);
678d89ec533Spatrick
679d89ec533Spatrick if (no_os_version || launchd) {
680d89ec533Spatrick len = ApproximateOSVersionViaKernelVersion(vers);
681d89ec533Spatrick }
682d89ec533Spatrick }
683d89ec533Spatrick CHECK_LT(len, sizeof(VersStr));
684d89ec533Spatrick }
685d89ec533Spatrick
ParseVersion(const char * vers,u16 * major,u16 * minor)686d89ec533Spatrick void ParseVersion(const char *vers, u16 *major, u16 *minor) {
687d89ec533Spatrick // Format: <major>.<minor>[.<patch>]\0
688d89ec533Spatrick CHECK_GE(internal_strlen(vers), 3);
689d89ec533Spatrick const char *p = vers;
690d89ec533Spatrick *major = internal_simple_strtoll(p, &p, /*base=*/10);
691d89ec533Spatrick CHECK_EQ(*p, '.');
692d89ec533Spatrick p += 1;
693d89ec533Spatrick *minor = internal_simple_strtoll(p, &p, /*base=*/10);
694d89ec533Spatrick }
695d89ec533Spatrick
696d89ec533Spatrick // Aligned versions example:
697d89ec533Spatrick // macOS 10.15 -- iOS 13 -- tvOS 13 -- watchOS 6
MapToMacos(u16 * major,u16 * minor)698d89ec533Spatrick static void MapToMacos(u16 *major, u16 *minor) {
699d89ec533Spatrick if (TARGET_OS_OSX)
700d89ec533Spatrick return;
701d89ec533Spatrick
702d89ec533Spatrick if (TARGET_OS_IOS || TARGET_OS_TV)
703d89ec533Spatrick *major += 2;
704d89ec533Spatrick else if (TARGET_OS_WATCH)
705d89ec533Spatrick *major += 9;
706d89ec533Spatrick else
707d89ec533Spatrick UNREACHABLE("unsupported platform");
708d89ec533Spatrick
709d89ec533Spatrick if (*major >= 16) { // macOS 11+
710d89ec533Spatrick *major -= 5;
711d89ec533Spatrick } else { // macOS 10.15 and below
712d89ec533Spatrick *minor = *major;
713d89ec533Spatrick *major = 10;
714d89ec533Spatrick }
715d89ec533Spatrick }
716d89ec533Spatrick
GetMacosAlignedVersionInternal()717d89ec533Spatrick static MacosVersion GetMacosAlignedVersionInternal() {
718d89ec533Spatrick VersStr vers = {};
719d89ec533Spatrick GetOSVersion(vers);
720d89ec533Spatrick
721d89ec533Spatrick u16 major, minor;
722d89ec533Spatrick ParseVersion(vers, &major, &minor);
723d89ec533Spatrick MapToMacos(&major, &minor);
724d89ec533Spatrick
7251f9cb04fSpatrick return MacosVersion(major, minor);
7261f9cb04fSpatrick }
7273cab2bb3Spatrick
7281f9cb04fSpatrick static_assert(sizeof(MacosVersion) == sizeof(atomic_uint32_t::Type),
7291f9cb04fSpatrick "MacosVersion cache size");
7301f9cb04fSpatrick static atomic_uint32_t cached_macos_version;
7313cab2bb3Spatrick
GetMacosAlignedVersion()7321f9cb04fSpatrick MacosVersion GetMacosAlignedVersion() {
7331f9cb04fSpatrick atomic_uint32_t::Type result =
7341f9cb04fSpatrick atomic_load(&cached_macos_version, memory_order_acquire);
7351f9cb04fSpatrick if (!result) {
7361f9cb04fSpatrick MacosVersion version = GetMacosAlignedVersionInternal();
7371f9cb04fSpatrick result = *reinterpret_cast<atomic_uint32_t::Type *>(&version);
7381f9cb04fSpatrick atomic_store(&cached_macos_version, result, memory_order_release);
7391f9cb04fSpatrick }
7401f9cb04fSpatrick return *reinterpret_cast<MacosVersion *>(&result);
7411f9cb04fSpatrick }
7421f9cb04fSpatrick
GetDarwinKernelVersion()7431f9cb04fSpatrick DarwinKernelVersion GetDarwinKernelVersion() {
744d89ec533Spatrick VersStr vers = {};
745d89ec533Spatrick uptr len = sizeof(VersStr);
746d89ec533Spatrick int res = internal_sysctlbyname("kern.osrelease", vers, &len, nullptr, 0);
7471f9cb04fSpatrick CHECK_EQ(res, 0);
748d89ec533Spatrick CHECK_LT(len, sizeof(VersStr));
7493cab2bb3Spatrick
7501f9cb04fSpatrick u16 major, minor;
751d89ec533Spatrick ParseVersion(vers, &major, &minor);
7521f9cb04fSpatrick
7531f9cb04fSpatrick return DarwinKernelVersion(major, minor);
7543cab2bb3Spatrick }
7553cab2bb3Spatrick
GetRSS()7563cab2bb3Spatrick uptr GetRSS() {
7573cab2bb3Spatrick struct task_basic_info info;
7583cab2bb3Spatrick unsigned count = TASK_BASIC_INFO_COUNT;
7593cab2bb3Spatrick kern_return_t result =
7603cab2bb3Spatrick task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count);
7613cab2bb3Spatrick if (UNLIKELY(result != KERN_SUCCESS)) {
7623cab2bb3Spatrick Report("Cannot get task info. Error: %d\n", result);
7633cab2bb3Spatrick Die();
7643cab2bb3Spatrick }
7653cab2bb3Spatrick return info.resident_size;
7663cab2bb3Spatrick }
7673cab2bb3Spatrick
internal_start_thread(void * (* func)(void * arg),void * arg)7681f9cb04fSpatrick void *internal_start_thread(void *(*func)(void *arg), void *arg) {
7693cab2bb3Spatrick // Start the thread with signals blocked, otherwise it can steal user signals.
7703cab2bb3Spatrick __sanitizer_sigset_t set, old;
7713cab2bb3Spatrick internal_sigfillset(&set);
7723cab2bb3Spatrick internal_sigprocmask(SIG_SETMASK, &set, &old);
7733cab2bb3Spatrick pthread_t th;
7741f9cb04fSpatrick pthread_create(&th, 0, func, arg);
7753cab2bb3Spatrick internal_sigprocmask(SIG_SETMASK, &old, 0);
7763cab2bb3Spatrick return th;
7773cab2bb3Spatrick }
7783cab2bb3Spatrick
internal_join_thread(void * th)7793cab2bb3Spatrick void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); }
7803cab2bb3Spatrick
7813cab2bb3Spatrick #if !SANITIZER_GO
782*810390e3Srobert static Mutex syslog_lock;
7833cab2bb3Spatrick # endif
7843cab2bb3Spatrick
WriteOneLineToSyslog(const char * s)7853cab2bb3Spatrick void WriteOneLineToSyslog(const char *s) {
7863cab2bb3Spatrick #if !SANITIZER_GO
7873cab2bb3Spatrick syslog_lock.CheckLocked();
788d89ec533Spatrick if (GetMacosAlignedVersion() >= MacosVersion(10, 12)) {
789d89ec533Spatrick os_log_error(OS_LOG_DEFAULT, "%{public}s", s);
790d89ec533Spatrick } else {
7913cab2bb3Spatrick asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s);
792d89ec533Spatrick }
793d89ec533Spatrick #endif
794d89ec533Spatrick }
795d89ec533Spatrick
796d89ec533Spatrick // buffer to store crash report application information
797d89ec533Spatrick static char crashreporter_info_buff[__sanitizer::kErrorMessageBufferSize] = {};
798*810390e3Srobert static Mutex crashreporter_info_mutex;
799d89ec533Spatrick
800d89ec533Spatrick extern "C" {
801d89ec533Spatrick // Integrate with crash reporter libraries.
802d89ec533Spatrick #if HAVE_CRASHREPORTERCLIENT_H
803d89ec533Spatrick CRASH_REPORTER_CLIENT_HIDDEN
804d89ec533Spatrick struct crashreporter_annotations_t gCRAnnotations
805d89ec533Spatrick __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) = {
806d89ec533Spatrick CRASHREPORTER_ANNOTATIONS_VERSION,
807d89ec533Spatrick 0,
808d89ec533Spatrick 0,
809d89ec533Spatrick 0,
810d89ec533Spatrick 0,
811d89ec533Spatrick 0,
812d89ec533Spatrick 0,
813d89ec533Spatrick #if CRASHREPORTER_ANNOTATIONS_VERSION > 4
814d89ec533Spatrick 0,
815d89ec533Spatrick #endif
816d89ec533Spatrick };
817d89ec533Spatrick
818d89ec533Spatrick #else
819d89ec533Spatrick // fall back to old crashreporter api
820d89ec533Spatrick static const char *__crashreporter_info__ __attribute__((__used__)) =
821d89ec533Spatrick &crashreporter_info_buff[0];
822d89ec533Spatrick asm(".desc ___crashreporter_info__, 0x10");
823d89ec533Spatrick #endif
824d89ec533Spatrick
825d89ec533Spatrick } // extern "C"
826d89ec533Spatrick
CRAppendCrashLogMessage(const char * msg)827d89ec533Spatrick static void CRAppendCrashLogMessage(const char *msg) {
828*810390e3Srobert Lock l(&crashreporter_info_mutex);
829d89ec533Spatrick internal_strlcat(crashreporter_info_buff, msg,
830d89ec533Spatrick sizeof(crashreporter_info_buff));
831d89ec533Spatrick #if HAVE_CRASHREPORTERCLIENT_H
832d89ec533Spatrick (void)CRSetCrashLogMessage(crashreporter_info_buff);
8333cab2bb3Spatrick #endif
8343cab2bb3Spatrick }
8353cab2bb3Spatrick
LogMessageOnPrintf(const char * str)8363cab2bb3Spatrick void LogMessageOnPrintf(const char *str) {
8373cab2bb3Spatrick // Log all printf output to CrashLog.
8383cab2bb3Spatrick if (common_flags()->abort_on_error)
8393cab2bb3Spatrick CRAppendCrashLogMessage(str);
8403cab2bb3Spatrick }
8413cab2bb3Spatrick
LogFullErrorReport(const char * buffer)8423cab2bb3Spatrick void LogFullErrorReport(const char *buffer) {
8433cab2bb3Spatrick #if !SANITIZER_GO
8443cab2bb3Spatrick // Log with os_trace. This will make it into the crash log.
8453cab2bb3Spatrick #if SANITIZER_OS_TRACE
8461f9cb04fSpatrick if (GetMacosAlignedVersion() >= MacosVersion(10, 10)) {
8473cab2bb3Spatrick // os_trace requires the message (format parameter) to be a string literal.
8483cab2bb3Spatrick if (internal_strncmp(SanitizerToolName, "AddressSanitizer",
8493cab2bb3Spatrick sizeof("AddressSanitizer") - 1) == 0)
8503cab2bb3Spatrick os_trace("Address Sanitizer reported a failure.");
8513cab2bb3Spatrick else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer",
8523cab2bb3Spatrick sizeof("UndefinedBehaviorSanitizer") - 1) == 0)
8533cab2bb3Spatrick os_trace("Undefined Behavior Sanitizer reported a failure.");
8543cab2bb3Spatrick else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer",
8553cab2bb3Spatrick sizeof("ThreadSanitizer") - 1) == 0)
8563cab2bb3Spatrick os_trace("Thread Sanitizer reported a failure.");
8573cab2bb3Spatrick else
8583cab2bb3Spatrick os_trace("Sanitizer tool reported a failure.");
8593cab2bb3Spatrick
8603cab2bb3Spatrick if (common_flags()->log_to_syslog)
8613cab2bb3Spatrick os_trace("Consult syslog for more information.");
8623cab2bb3Spatrick }
8633cab2bb3Spatrick #endif
8643cab2bb3Spatrick
8653cab2bb3Spatrick // Log to syslog.
8663cab2bb3Spatrick // The logging on OS X may call pthread_create so we need the threading
8673cab2bb3Spatrick // environment to be fully initialized. Also, this should never be called when
8683cab2bb3Spatrick // holding the thread registry lock since that may result in a deadlock. If
8693cab2bb3Spatrick // the reporting thread holds the thread registry mutex, and asl_log waits
8703cab2bb3Spatrick // for GCD to dispatch a new thread, the process will deadlock, because the
8713cab2bb3Spatrick // pthread_create wrapper needs to acquire the lock as well.
872*810390e3Srobert Lock l(&syslog_lock);
8733cab2bb3Spatrick if (common_flags()->log_to_syslog)
8743cab2bb3Spatrick WriteToSyslog(buffer);
8753cab2bb3Spatrick
8763cab2bb3Spatrick // The report is added to CrashLog as part of logging all of Printf output.
8773cab2bb3Spatrick #endif
8783cab2bb3Spatrick }
8793cab2bb3Spatrick
GetWriteFlag() const8803cab2bb3Spatrick SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
8813cab2bb3Spatrick #if defined(__x86_64__) || defined(__i386__)
8823cab2bb3Spatrick ucontext_t *ucontext = static_cast<ucontext_t*>(context);
883*810390e3Srobert return ucontext->uc_mcontext->__es.__err & 2 /*T_PF_WRITE*/ ? Write : Read;
884*810390e3Srobert #elif defined(__arm64__)
885*810390e3Srobert ucontext_t *ucontext = static_cast<ucontext_t*>(context);
886*810390e3Srobert return ucontext->uc_mcontext->__es.__esr & 0x40 /*ISS_DA_WNR*/ ? Write : Read;
8873cab2bb3Spatrick #else
888*810390e3Srobert return Unknown;
8893cab2bb3Spatrick #endif
8903cab2bb3Spatrick }
8913cab2bb3Spatrick
IsTrueFaultingAddress() const8923cab2bb3Spatrick bool SignalContext::IsTrueFaultingAddress() const {
8933cab2bb3Spatrick auto si = static_cast<const siginfo_t *>(siginfo);
8943cab2bb3Spatrick // "Real" SIGSEGV codes (e.g., SEGV_MAPERR, SEGV_MAPERR) are non-zero.
8953cab2bb3Spatrick return si->si_signo == SIGSEGV && si->si_code != 0;
8963cab2bb3Spatrick }
8973cab2bb3Spatrick
8981f9cb04fSpatrick #if defined(__aarch64__) && defined(arm_thread_state64_get_sp)
8991f9cb04fSpatrick #define AARCH64_GET_REG(r) \
9001f9cb04fSpatrick (uptr)ptrauth_strip( \
9011f9cb04fSpatrick (void *)arm_thread_state64_get_##r(ucontext->uc_mcontext->__ss), 0)
9021f9cb04fSpatrick #else
903*810390e3Srobert #define AARCH64_GET_REG(r) (uptr)ucontext->uc_mcontext->__ss.__##r
9041f9cb04fSpatrick #endif
9051f9cb04fSpatrick
GetPcSpBp(void * context,uptr * pc,uptr * sp,uptr * bp)9063cab2bb3Spatrick static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
9073cab2bb3Spatrick ucontext_t *ucontext = (ucontext_t*)context;
9083cab2bb3Spatrick # if defined(__aarch64__)
9091f9cb04fSpatrick *pc = AARCH64_GET_REG(pc);
9101f9cb04fSpatrick *bp = AARCH64_GET_REG(fp);
9111f9cb04fSpatrick *sp = AARCH64_GET_REG(sp);
9123cab2bb3Spatrick # elif defined(__x86_64__)
9133cab2bb3Spatrick *pc = ucontext->uc_mcontext->__ss.__rip;
9143cab2bb3Spatrick *bp = ucontext->uc_mcontext->__ss.__rbp;
9153cab2bb3Spatrick *sp = ucontext->uc_mcontext->__ss.__rsp;
9163cab2bb3Spatrick # elif defined(__arm__)
9173cab2bb3Spatrick *pc = ucontext->uc_mcontext->__ss.__pc;
9183cab2bb3Spatrick *bp = ucontext->uc_mcontext->__ss.__r[7];
9193cab2bb3Spatrick *sp = ucontext->uc_mcontext->__ss.__sp;
9203cab2bb3Spatrick # elif defined(__i386__)
9213cab2bb3Spatrick *pc = ucontext->uc_mcontext->__ss.__eip;
9223cab2bb3Spatrick *bp = ucontext->uc_mcontext->__ss.__ebp;
9233cab2bb3Spatrick *sp = ucontext->uc_mcontext->__ss.__esp;
9243cab2bb3Spatrick # else
9253cab2bb3Spatrick # error "Unknown architecture"
9263cab2bb3Spatrick # endif
9273cab2bb3Spatrick }
9283cab2bb3Spatrick
InitPcSpBp()9291f9cb04fSpatrick void SignalContext::InitPcSpBp() {
9301f9cb04fSpatrick addr = (uptr)ptrauth_strip((void *)addr, 0);
9311f9cb04fSpatrick GetPcSpBp(context, &pc, &sp, &bp);
9321f9cb04fSpatrick }
9333cab2bb3Spatrick
934d89ec533Spatrick // ASan/TSan use mmap in a way that creates “deallocation gaps” which triggers
935d89ec533Spatrick // EXC_GUARD exceptions on macOS 10.15+ (XNU 19.0+).
DisableMmapExcGuardExceptions()936d89ec533Spatrick static void DisableMmapExcGuardExceptions() {
937d89ec533Spatrick using task_exc_guard_behavior_t = uint32_t;
938d89ec533Spatrick using task_set_exc_guard_behavior_t =
939d89ec533Spatrick kern_return_t(task_t task, task_exc_guard_behavior_t behavior);
940d89ec533Spatrick auto *set_behavior = (task_set_exc_guard_behavior_t *)dlsym(
941d89ec533Spatrick RTLD_DEFAULT, "task_set_exc_guard_behavior");
942d89ec533Spatrick if (set_behavior == nullptr) return;
943d89ec533Spatrick const task_exc_guard_behavior_t task_exc_guard_none = 0;
944d89ec533Spatrick set_behavior(mach_task_self(), task_exc_guard_none);
945d89ec533Spatrick }
946d89ec533Spatrick
947*810390e3Srobert static void VerifyInterceptorsWorking();
948*810390e3Srobert static void StripEnv();
949*810390e3Srobert
InitializePlatformEarly()9503cab2bb3Spatrick void InitializePlatformEarly() {
9511f9cb04fSpatrick // Only use xnu_fast_mmap when on x86_64 and the kernel supports it.
9523cab2bb3Spatrick use_xnu_fast_mmap =
9533cab2bb3Spatrick #if defined(__x86_64__)
9541f9cb04fSpatrick GetDarwinKernelVersion() >= DarwinKernelVersion(17, 5);
9553cab2bb3Spatrick #else
9563cab2bb3Spatrick false;
9573cab2bb3Spatrick #endif
958d89ec533Spatrick if (GetDarwinKernelVersion() >= DarwinKernelVersion(19, 0))
959d89ec533Spatrick DisableMmapExcGuardExceptions();
960*810390e3Srobert
961*810390e3Srobert # if !SANITIZER_GO
962*810390e3Srobert MonotonicNanoTime(); // Call to initialize mach_timebase_info
963*810390e3Srobert VerifyInterceptorsWorking();
964*810390e3Srobert StripEnv();
965*810390e3Srobert # endif
9663cab2bb3Spatrick }
9673cab2bb3Spatrick
9683cab2bb3Spatrick #if !SANITIZER_GO
9693cab2bb3Spatrick static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
9703cab2bb3Spatrick LowLevelAllocator allocator_for_env;
9713cab2bb3Spatrick
ShouldCheckInterceptors()972*810390e3Srobert static bool ShouldCheckInterceptors() {
973*810390e3Srobert // Restrict "interceptors working?" check to ASan and TSan.
974*810390e3Srobert const char *sanitizer_names[] = {"AddressSanitizer", "ThreadSanitizer"};
975*810390e3Srobert size_t count = sizeof(sanitizer_names) / sizeof(sanitizer_names[0]);
976*810390e3Srobert for (size_t i = 0; i < count; i++) {
977*810390e3Srobert if (internal_strcmp(sanitizer_names[i], SanitizerToolName) == 0)
978*810390e3Srobert return true;
979*810390e3Srobert }
980*810390e3Srobert return false;
981*810390e3Srobert }
982*810390e3Srobert
VerifyInterceptorsWorking()983*810390e3Srobert static void VerifyInterceptorsWorking() {
984*810390e3Srobert if (!common_flags()->verify_interceptors || !ShouldCheckInterceptors())
985*810390e3Srobert return;
986*810390e3Srobert
987*810390e3Srobert // Verify that interceptors really work. We'll use dlsym to locate
988*810390e3Srobert // "puts", if interceptors are working, it should really point to
989*810390e3Srobert // "wrap_puts" within our own dylib.
990*810390e3Srobert Dl_info info_puts, info_runtime;
991*810390e3Srobert RAW_CHECK(dladdr(dlsym(RTLD_DEFAULT, "puts"), &info_puts));
992*810390e3Srobert RAW_CHECK(dladdr((void *)__sanitizer_report_error_summary, &info_runtime));
993*810390e3Srobert if (internal_strcmp(info_puts.dli_fname, info_runtime.dli_fname) != 0) {
994*810390e3Srobert Report(
995*810390e3Srobert "ERROR: Interceptors are not working. This may be because %s is "
996*810390e3Srobert "loaded too late (e.g. via dlopen). Please launch the executable "
997*810390e3Srobert "with:\n%s=%s\n",
998*810390e3Srobert SanitizerToolName, kDyldInsertLibraries, info_runtime.dli_fname);
999*810390e3Srobert RAW_CHECK("interceptors not installed" && 0);
1000*810390e3Srobert }
1001*810390e3Srobert }
1002*810390e3Srobert
10033cab2bb3Spatrick // Change the value of the env var |name|, leaking the original value.
10043cab2bb3Spatrick // If |name_value| is NULL, the variable is deleted from the environment,
10053cab2bb3Spatrick // otherwise the corresponding "NAME=value" string is replaced with
10063cab2bb3Spatrick // |name_value|.
LeakyResetEnv(const char * name,const char * name_value)1007*810390e3Srobert static void LeakyResetEnv(const char *name, const char *name_value) {
10083cab2bb3Spatrick char **env = GetEnviron();
10093cab2bb3Spatrick uptr name_len = internal_strlen(name);
10103cab2bb3Spatrick while (*env != 0) {
10113cab2bb3Spatrick uptr len = internal_strlen(*env);
10123cab2bb3Spatrick if (len > name_len) {
10133cab2bb3Spatrick const char *p = *env;
10143cab2bb3Spatrick if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
10153cab2bb3Spatrick // Match.
10163cab2bb3Spatrick if (name_value) {
10173cab2bb3Spatrick // Replace the old value with the new one.
10183cab2bb3Spatrick *env = const_cast<char*>(name_value);
10193cab2bb3Spatrick } else {
10203cab2bb3Spatrick // Shift the subsequent pointers back.
10213cab2bb3Spatrick char **del = env;
10223cab2bb3Spatrick do {
10233cab2bb3Spatrick del[0] = del[1];
10243cab2bb3Spatrick } while (*del++);
10253cab2bb3Spatrick }
10263cab2bb3Spatrick }
10273cab2bb3Spatrick }
10283cab2bb3Spatrick env++;
10293cab2bb3Spatrick }
10303cab2bb3Spatrick }
10313cab2bb3Spatrick
StripEnv()1032*810390e3Srobert static void StripEnv() {
1033*810390e3Srobert if (!common_flags()->strip_env)
10343cab2bb3Spatrick return;
10353cab2bb3Spatrick
1036*810390e3Srobert char *dyld_insert_libraries =
1037*810390e3Srobert const_cast<char *>(GetEnv(kDyldInsertLibraries));
1038*810390e3Srobert if (!dyld_insert_libraries)
1039*810390e3Srobert return;
1040*810390e3Srobert
1041*810390e3Srobert Dl_info info;
1042*810390e3Srobert RAW_CHECK(dladdr((void *)__sanitizer_report_error_summary, &info));
1043*810390e3Srobert const char *dylib_name = StripModuleName(info.dli_fname);
1044*810390e3Srobert bool lib_is_in_env = internal_strstr(dyld_insert_libraries, dylib_name);
1045*810390e3Srobert if (!lib_is_in_env)
10463cab2bb3Spatrick return;
10473cab2bb3Spatrick
10483cab2bb3Spatrick // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
10493cab2bb3Spatrick // the dylib from the environment variable, because interceptors are installed
10503cab2bb3Spatrick // and we don't want our children to inherit the variable.
10513cab2bb3Spatrick
1052*810390e3Srobert uptr old_env_len = internal_strlen(dyld_insert_libraries);
1053*810390e3Srobert uptr dylib_name_len = internal_strlen(dylib_name);
10543cab2bb3Spatrick uptr env_name_len = internal_strlen(kDyldInsertLibraries);
10553cab2bb3Spatrick // Allocate memory to hold the previous env var name, its value, the '='
10563cab2bb3Spatrick // sign and the '\0' char.
10573cab2bb3Spatrick char *new_env = (char*)allocator_for_env.Allocate(
10583cab2bb3Spatrick old_env_len + 2 + env_name_len);
10593cab2bb3Spatrick RAW_CHECK(new_env);
10603cab2bb3Spatrick internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
10613cab2bb3Spatrick internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
10623cab2bb3Spatrick new_env[env_name_len] = '=';
10633cab2bb3Spatrick char *new_env_pos = new_env + env_name_len + 1;
10643cab2bb3Spatrick
10653cab2bb3Spatrick // Iterate over colon-separated pieces of |dyld_insert_libraries|.
10663cab2bb3Spatrick char *piece_start = dyld_insert_libraries;
10673cab2bb3Spatrick char *piece_end = NULL;
10683cab2bb3Spatrick char *old_env_end = dyld_insert_libraries + old_env_len;
10693cab2bb3Spatrick do {
10703cab2bb3Spatrick if (piece_start[0] == ':') piece_start++;
10713cab2bb3Spatrick piece_end = internal_strchr(piece_start, ':');
10723cab2bb3Spatrick if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
10733cab2bb3Spatrick if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
10743cab2bb3Spatrick uptr piece_len = piece_end - piece_start;
10753cab2bb3Spatrick
10763cab2bb3Spatrick char *filename_start =
10773cab2bb3Spatrick (char *)internal_memrchr(piece_start, '/', piece_len);
10783cab2bb3Spatrick uptr filename_len = piece_len;
10793cab2bb3Spatrick if (filename_start) {
10803cab2bb3Spatrick filename_start += 1;
10813cab2bb3Spatrick filename_len = piece_len - (filename_start - piece_start);
10823cab2bb3Spatrick } else {
10833cab2bb3Spatrick filename_start = piece_start;
10843cab2bb3Spatrick }
10853cab2bb3Spatrick
10863cab2bb3Spatrick // If the current piece isn't the runtime library name,
10873cab2bb3Spatrick // append it to new_env.
10883cab2bb3Spatrick if ((dylib_name_len != filename_len) ||
10893cab2bb3Spatrick (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
10903cab2bb3Spatrick if (new_env_pos != new_env + env_name_len + 1) {
10913cab2bb3Spatrick new_env_pos[0] = ':';
10923cab2bb3Spatrick new_env_pos++;
10933cab2bb3Spatrick }
10943cab2bb3Spatrick internal_strncpy(new_env_pos, piece_start, piece_len);
10953cab2bb3Spatrick new_env_pos += piece_len;
10963cab2bb3Spatrick }
10973cab2bb3Spatrick // Move on to the next piece.
10983cab2bb3Spatrick piece_start = piece_end;
10993cab2bb3Spatrick } while (piece_start < old_env_end);
11003cab2bb3Spatrick
11013cab2bb3Spatrick // Can't use setenv() here, because it requires the allocator to be
11023cab2bb3Spatrick // initialized.
11033cab2bb3Spatrick // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
11043cab2bb3Spatrick // a separate function called after InitializeAllocator().
11053cab2bb3Spatrick if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
11063cab2bb3Spatrick LeakyResetEnv(kDyldInsertLibraries, new_env);
11073cab2bb3Spatrick }
11083cab2bb3Spatrick #endif // SANITIZER_GO
11093cab2bb3Spatrick
GetArgv()11103cab2bb3Spatrick char **GetArgv() {
11113cab2bb3Spatrick return *_NSGetArgv();
11123cab2bb3Spatrick }
11133cab2bb3Spatrick
1114d89ec533Spatrick #if SANITIZER_IOS && !SANITIZER_IOSSIM
11153cab2bb3Spatrick // The task_vm_info struct is normally provided by the macOS SDK, but we need
11163cab2bb3Spatrick // fields only available in 10.12+. Declare the struct manually to be able to
11173cab2bb3Spatrick // build against older SDKs.
11183cab2bb3Spatrick struct __sanitizer_task_vm_info {
11193cab2bb3Spatrick mach_vm_size_t virtual_size;
11203cab2bb3Spatrick integer_t region_count;
11213cab2bb3Spatrick integer_t page_size;
11223cab2bb3Spatrick mach_vm_size_t resident_size;
11233cab2bb3Spatrick mach_vm_size_t resident_size_peak;
11243cab2bb3Spatrick mach_vm_size_t device;
11253cab2bb3Spatrick mach_vm_size_t device_peak;
11263cab2bb3Spatrick mach_vm_size_t internal;
11273cab2bb3Spatrick mach_vm_size_t internal_peak;
11283cab2bb3Spatrick mach_vm_size_t external;
11293cab2bb3Spatrick mach_vm_size_t external_peak;
11303cab2bb3Spatrick mach_vm_size_t reusable;
11313cab2bb3Spatrick mach_vm_size_t reusable_peak;
11323cab2bb3Spatrick mach_vm_size_t purgeable_volatile_pmap;
11333cab2bb3Spatrick mach_vm_size_t purgeable_volatile_resident;
11343cab2bb3Spatrick mach_vm_size_t purgeable_volatile_virtual;
11353cab2bb3Spatrick mach_vm_size_t compressed;
11363cab2bb3Spatrick mach_vm_size_t compressed_peak;
11373cab2bb3Spatrick mach_vm_size_t compressed_lifetime;
11383cab2bb3Spatrick mach_vm_size_t phys_footprint;
11393cab2bb3Spatrick mach_vm_address_t min_address;
11403cab2bb3Spatrick mach_vm_address_t max_address;
11413cab2bb3Spatrick };
11423cab2bb3Spatrick #define __SANITIZER_TASK_VM_INFO_COUNT ((mach_msg_type_number_t) \
11433cab2bb3Spatrick (sizeof(__sanitizer_task_vm_info) / sizeof(natural_t)))
11443cab2bb3Spatrick
GetTaskInfoMaxAddress()11453cab2bb3Spatrick static uptr GetTaskInfoMaxAddress() {
11463cab2bb3Spatrick __sanitizer_task_vm_info vm_info = {} /* zero initialize */;
11473cab2bb3Spatrick mach_msg_type_number_t count = __SANITIZER_TASK_VM_INFO_COUNT;
11483cab2bb3Spatrick int err = task_info(mach_task_self(), TASK_VM_INFO, (int *)&vm_info, &count);
11493cab2bb3Spatrick return err ? 0 : vm_info.max_address;
11503cab2bb3Spatrick }
11513cab2bb3Spatrick
GetMaxUserVirtualAddress()11523cab2bb3Spatrick uptr GetMaxUserVirtualAddress() {
11533cab2bb3Spatrick static uptr max_vm = GetTaskInfoMaxAddress();
1154d89ec533Spatrick if (max_vm != 0) {
1155d89ec533Spatrick const uptr ret_value = max_vm - 1;
1156d89ec533Spatrick CHECK_LE(ret_value, SANITIZER_MMAP_RANGE_SIZE);
1157d89ec533Spatrick return ret_value;
1158d89ec533Spatrick }
11593cab2bb3Spatrick
11603cab2bb3Spatrick // xnu cannot provide vm address limit
11613cab2bb3Spatrick # if SANITIZER_WORDSIZE == 32
1162d89ec533Spatrick constexpr uptr fallback_max_vm = 0xffe00000 - 1;
11633cab2bb3Spatrick # else
1164d89ec533Spatrick constexpr uptr fallback_max_vm = 0x200000000 - 1;
11653cab2bb3Spatrick # endif
1166d89ec533Spatrick static_assert(fallback_max_vm <= SANITIZER_MMAP_RANGE_SIZE,
1167d89ec533Spatrick "Max virtual address must be less than mmap range size.");
1168d89ec533Spatrick return fallback_max_vm;
11693cab2bb3Spatrick }
11703cab2bb3Spatrick
11713cab2bb3Spatrick #else // !SANITIZER_IOS
11723cab2bb3Spatrick
GetMaxUserVirtualAddress()11733cab2bb3Spatrick uptr GetMaxUserVirtualAddress() {
11743cab2bb3Spatrick # if SANITIZER_WORDSIZE == 64
1175d89ec533Spatrick constexpr uptr max_vm = (1ULL << 47) - 1; // 0x00007fffffffffffUL;
11763cab2bb3Spatrick # else // SANITIZER_WORDSIZE == 32
11773cab2bb3Spatrick static_assert(SANITIZER_WORDSIZE == 32, "Wrong wordsize");
1178d89ec533Spatrick constexpr uptr max_vm = (1ULL << 32) - 1; // 0xffffffff;
11793cab2bb3Spatrick # endif
1180d89ec533Spatrick static_assert(max_vm <= SANITIZER_MMAP_RANGE_SIZE,
1181d89ec533Spatrick "Max virtual address must be less than mmap range size.");
1182d89ec533Spatrick return max_vm;
11833cab2bb3Spatrick }
11843cab2bb3Spatrick #endif
11853cab2bb3Spatrick
GetMaxVirtualAddress()11863cab2bb3Spatrick uptr GetMaxVirtualAddress() {
11873cab2bb3Spatrick return GetMaxUserVirtualAddress();
11883cab2bb3Spatrick }
11893cab2bb3Spatrick
MapDynamicShadow(uptr shadow_size_bytes,uptr shadow_scale,uptr min_shadow_base_alignment,uptr & high_mem_end)1190d89ec533Spatrick uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale,
1191d89ec533Spatrick uptr min_shadow_base_alignment, uptr &high_mem_end) {
1192d89ec533Spatrick const uptr granularity = GetMmapGranularity();
1193d89ec533Spatrick const uptr alignment =
1194d89ec533Spatrick Max<uptr>(granularity << shadow_scale, 1ULL << min_shadow_base_alignment);
1195d89ec533Spatrick const uptr left_padding =
1196d89ec533Spatrick Max<uptr>(granularity, 1ULL << min_shadow_base_alignment);
1197d89ec533Spatrick
1198d89ec533Spatrick uptr space_size = shadow_size_bytes + left_padding;
1199d89ec533Spatrick
1200d89ec533Spatrick uptr largest_gap_found = 0;
1201d89ec533Spatrick uptr max_occupied_addr = 0;
1202*810390e3Srobert VReport(2, "FindDynamicShadowStart, space_size = %p\n", (void *)space_size);
1203d89ec533Spatrick uptr shadow_start =
1204d89ec533Spatrick FindAvailableMemoryRange(space_size, alignment, granularity,
1205d89ec533Spatrick &largest_gap_found, &max_occupied_addr);
1206d89ec533Spatrick // If the shadow doesn't fit, restrict the address space to make it fit.
1207d89ec533Spatrick if (shadow_start == 0) {
1208d89ec533Spatrick VReport(
1209d89ec533Spatrick 2,
1210d89ec533Spatrick "Shadow doesn't fit, largest_gap_found = %p, max_occupied_addr = %p\n",
1211*810390e3Srobert (void *)largest_gap_found, (void *)max_occupied_addr);
1212d89ec533Spatrick uptr new_max_vm = RoundDownTo(largest_gap_found << shadow_scale, alignment);
1213d89ec533Spatrick if (new_max_vm < max_occupied_addr) {
1214d89ec533Spatrick Report("Unable to find a memory range for dynamic shadow.\n");
1215d89ec533Spatrick Report(
1216d89ec533Spatrick "space_size = %p, largest_gap_found = %p, max_occupied_addr = %p, "
1217d89ec533Spatrick "new_max_vm = %p\n",
1218*810390e3Srobert (void *)space_size, (void *)largest_gap_found,
1219*810390e3Srobert (void *)max_occupied_addr, (void *)new_max_vm);
1220d89ec533Spatrick CHECK(0 && "cannot place shadow");
1221d89ec533Spatrick }
1222d89ec533Spatrick RestrictMemoryToMaxAddress(new_max_vm);
1223d89ec533Spatrick high_mem_end = new_max_vm - 1;
1224d89ec533Spatrick space_size = (high_mem_end >> shadow_scale) + left_padding;
1225*810390e3Srobert VReport(2, "FindDynamicShadowStart, space_size = %p\n", (void *)space_size);
1226d89ec533Spatrick shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity,
1227d89ec533Spatrick nullptr, nullptr);
1228d89ec533Spatrick if (shadow_start == 0) {
1229d89ec533Spatrick Report("Unable to find a memory range after restricting VM.\n");
1230d89ec533Spatrick CHECK(0 && "cannot place shadow after restricting vm");
1231d89ec533Spatrick }
1232d89ec533Spatrick }
1233d89ec533Spatrick CHECK_NE((uptr)0, shadow_start);
1234d89ec533Spatrick CHECK(IsAligned(shadow_start, alignment));
1235d89ec533Spatrick return shadow_start;
1236d89ec533Spatrick }
1237d89ec533Spatrick
MapDynamicShadowAndAliases(uptr shadow_size,uptr alias_size,uptr num_aliases,uptr ring_buffer_size)1238d89ec533Spatrick uptr MapDynamicShadowAndAliases(uptr shadow_size, uptr alias_size,
1239d89ec533Spatrick uptr num_aliases, uptr ring_buffer_size) {
1240d89ec533Spatrick CHECK(false && "HWASan aliasing is unimplemented on Mac");
1241d89ec533Spatrick return 0;
1242d89ec533Spatrick }
1243d89ec533Spatrick
FindAvailableMemoryRange(uptr size,uptr alignment,uptr left_padding,uptr * largest_gap_found,uptr * max_occupied_addr)12443cab2bb3Spatrick uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
12453cab2bb3Spatrick uptr *largest_gap_found,
12463cab2bb3Spatrick uptr *max_occupied_addr) {
12473cab2bb3Spatrick typedef vm_region_submap_short_info_data_64_t RegionInfo;
12483cab2bb3Spatrick enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 };
12493cab2bb3Spatrick // Start searching for available memory region past PAGEZERO, which is
12503cab2bb3Spatrick // 4KB on 32-bit and 4GB on 64-bit.
12513cab2bb3Spatrick mach_vm_address_t start_address =
12523cab2bb3Spatrick (SANITIZER_WORDSIZE == 32) ? 0x000000001000 : 0x000100000000;
12533cab2bb3Spatrick
1254*810390e3Srobert const mach_vm_address_t max_vm_address = GetMaxVirtualAddress() + 1;
12553cab2bb3Spatrick mach_vm_address_t address = start_address;
12563cab2bb3Spatrick mach_vm_address_t free_begin = start_address;
12573cab2bb3Spatrick kern_return_t kr = KERN_SUCCESS;
12583cab2bb3Spatrick if (largest_gap_found) *largest_gap_found = 0;
12593cab2bb3Spatrick if (max_occupied_addr) *max_occupied_addr = 0;
12603cab2bb3Spatrick while (kr == KERN_SUCCESS) {
12613cab2bb3Spatrick mach_vm_size_t vmsize = 0;
12623cab2bb3Spatrick natural_t depth = 0;
12633cab2bb3Spatrick RegionInfo vminfo;
12643cab2bb3Spatrick mach_msg_type_number_t count = kRegionInfoSize;
12653cab2bb3Spatrick kr = mach_vm_region_recurse(mach_task_self(), &address, &vmsize, &depth,
12663cab2bb3Spatrick (vm_region_info_t)&vminfo, &count);
12673cab2bb3Spatrick if (kr == KERN_INVALID_ADDRESS) {
12683cab2bb3Spatrick // No more regions beyond "address", consider the gap at the end of VM.
1269*810390e3Srobert address = max_vm_address;
12703cab2bb3Spatrick vmsize = 0;
12713cab2bb3Spatrick } else {
12723cab2bb3Spatrick if (max_occupied_addr) *max_occupied_addr = address + vmsize;
12733cab2bb3Spatrick }
12743cab2bb3Spatrick if (free_begin != address) {
12753cab2bb3Spatrick // We found a free region [free_begin..address-1].
12763cab2bb3Spatrick uptr gap_start = RoundUpTo((uptr)free_begin + left_padding, alignment);
1277*810390e3Srobert uptr gap_end = RoundDownTo((uptr)Min(address, max_vm_address), alignment);
12783cab2bb3Spatrick uptr gap_size = gap_end > gap_start ? gap_end - gap_start : 0;
12793cab2bb3Spatrick if (size < gap_size) {
12803cab2bb3Spatrick return gap_start;
12813cab2bb3Spatrick }
12823cab2bb3Spatrick
12833cab2bb3Spatrick if (largest_gap_found && *largest_gap_found < gap_size) {
12843cab2bb3Spatrick *largest_gap_found = gap_size;
12853cab2bb3Spatrick }
12863cab2bb3Spatrick }
12873cab2bb3Spatrick // Move to the next region.
12883cab2bb3Spatrick address += vmsize;
12893cab2bb3Spatrick free_begin = address;
12903cab2bb3Spatrick }
12913cab2bb3Spatrick
12923cab2bb3Spatrick // We looked at all free regions and could not find one large enough.
12933cab2bb3Spatrick return 0;
12943cab2bb3Spatrick }
12953cab2bb3Spatrick
12963cab2bb3Spatrick // FIXME implement on this platform.
GetMemoryProfile(fill_profile_f cb,uptr * stats)1297*810390e3Srobert void GetMemoryProfile(fill_profile_f cb, uptr *stats) {}
12983cab2bb3Spatrick
DumpAllRegisters(void * context)12993cab2bb3Spatrick void SignalContext::DumpAllRegisters(void *context) {
13003cab2bb3Spatrick Report("Register values:\n");
13013cab2bb3Spatrick
13023cab2bb3Spatrick ucontext_t *ucontext = (ucontext_t*)context;
13033cab2bb3Spatrick # define DUMPREG64(r) \
13043cab2bb3Spatrick Printf("%s = 0x%016llx ", #r, ucontext->uc_mcontext->__ss.__ ## r);
13051f9cb04fSpatrick # define DUMPREGA64(r) \
1306*810390e3Srobert Printf(" %s = 0x%016lx ", #r, AARCH64_GET_REG(r));
13073cab2bb3Spatrick # define DUMPREG32(r) \
13083cab2bb3Spatrick Printf("%s = 0x%08x ", #r, ucontext->uc_mcontext->__ss.__ ## r);
13093cab2bb3Spatrick # define DUMPREG_(r) Printf(" "); DUMPREG(r);
13103cab2bb3Spatrick # define DUMPREG__(r) Printf(" "); DUMPREG(r);
13113cab2bb3Spatrick # define DUMPREG___(r) Printf(" "); DUMPREG(r);
13123cab2bb3Spatrick
13133cab2bb3Spatrick # if defined(__x86_64__)
13143cab2bb3Spatrick # define DUMPREG(r) DUMPREG64(r)
13153cab2bb3Spatrick DUMPREG(rax); DUMPREG(rbx); DUMPREG(rcx); DUMPREG(rdx); Printf("\n");
13163cab2bb3Spatrick DUMPREG(rdi); DUMPREG(rsi); DUMPREG(rbp); DUMPREG(rsp); Printf("\n");
13173cab2bb3Spatrick DUMPREG_(r8); DUMPREG_(r9); DUMPREG(r10); DUMPREG(r11); Printf("\n");
13183cab2bb3Spatrick DUMPREG(r12); DUMPREG(r13); DUMPREG(r14); DUMPREG(r15); Printf("\n");
13193cab2bb3Spatrick # elif defined(__i386__)
13203cab2bb3Spatrick # define DUMPREG(r) DUMPREG32(r)
13213cab2bb3Spatrick DUMPREG(eax); DUMPREG(ebx); DUMPREG(ecx); DUMPREG(edx); Printf("\n");
13223cab2bb3Spatrick DUMPREG(edi); DUMPREG(esi); DUMPREG(ebp); DUMPREG(esp); Printf("\n");
13233cab2bb3Spatrick # elif defined(__aarch64__)
13243cab2bb3Spatrick # define DUMPREG(r) DUMPREG64(r)
13253cab2bb3Spatrick DUMPREG_(x[0]); DUMPREG_(x[1]); DUMPREG_(x[2]); DUMPREG_(x[3]); Printf("\n");
13263cab2bb3Spatrick DUMPREG_(x[4]); DUMPREG_(x[5]); DUMPREG_(x[6]); DUMPREG_(x[7]); Printf("\n");
13273cab2bb3Spatrick DUMPREG_(x[8]); DUMPREG_(x[9]); DUMPREG(x[10]); DUMPREG(x[11]); Printf("\n");
13283cab2bb3Spatrick DUMPREG(x[12]); DUMPREG(x[13]); DUMPREG(x[14]); DUMPREG(x[15]); Printf("\n");
13293cab2bb3Spatrick DUMPREG(x[16]); DUMPREG(x[17]); DUMPREG(x[18]); DUMPREG(x[19]); Printf("\n");
13303cab2bb3Spatrick DUMPREG(x[20]); DUMPREG(x[21]); DUMPREG(x[22]); DUMPREG(x[23]); Printf("\n");
13313cab2bb3Spatrick DUMPREG(x[24]); DUMPREG(x[25]); DUMPREG(x[26]); DUMPREG(x[27]); Printf("\n");
13321f9cb04fSpatrick DUMPREG(x[28]); DUMPREGA64(fp); DUMPREGA64(lr); DUMPREGA64(sp); Printf("\n");
13333cab2bb3Spatrick # elif defined(__arm__)
13343cab2bb3Spatrick # define DUMPREG(r) DUMPREG32(r)
13353cab2bb3Spatrick DUMPREG_(r[0]); DUMPREG_(r[1]); DUMPREG_(r[2]); DUMPREG_(r[3]); Printf("\n");
13363cab2bb3Spatrick DUMPREG_(r[4]); DUMPREG_(r[5]); DUMPREG_(r[6]); DUMPREG_(r[7]); Printf("\n");
13373cab2bb3Spatrick DUMPREG_(r[8]); DUMPREG_(r[9]); DUMPREG(r[10]); DUMPREG(r[11]); Printf("\n");
13383cab2bb3Spatrick DUMPREG(r[12]); DUMPREG___(sp); DUMPREG___(lr); DUMPREG___(pc); Printf("\n");
13393cab2bb3Spatrick # else
13403cab2bb3Spatrick # error "Unknown architecture"
13413cab2bb3Spatrick # endif
13423cab2bb3Spatrick
13433cab2bb3Spatrick # undef DUMPREG64
13443cab2bb3Spatrick # undef DUMPREG32
13453cab2bb3Spatrick # undef DUMPREG_
13463cab2bb3Spatrick # undef DUMPREG__
13473cab2bb3Spatrick # undef DUMPREG___
13483cab2bb3Spatrick # undef DUMPREG
13493cab2bb3Spatrick }
13503cab2bb3Spatrick
CompareBaseAddress(const LoadedModule & a,const LoadedModule & b)13513cab2bb3Spatrick static inline bool CompareBaseAddress(const LoadedModule &a,
13523cab2bb3Spatrick const LoadedModule &b) {
13533cab2bb3Spatrick return a.base_address() < b.base_address();
13543cab2bb3Spatrick }
13553cab2bb3Spatrick
FormatUUID(char * out,uptr size,const u8 * uuid)13563cab2bb3Spatrick void FormatUUID(char *out, uptr size, const u8 *uuid) {
13573cab2bb3Spatrick internal_snprintf(out, size,
13583cab2bb3Spatrick "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-"
13593cab2bb3Spatrick "%02X%02X%02X%02X%02X%02X>",
13603cab2bb3Spatrick uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
13613cab2bb3Spatrick uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
13623cab2bb3Spatrick uuid[12], uuid[13], uuid[14], uuid[15]);
13633cab2bb3Spatrick }
13643cab2bb3Spatrick
DumpProcessMap()1365d89ec533Spatrick void DumpProcessMap() {
13663cab2bb3Spatrick Printf("Process module map:\n");
13673cab2bb3Spatrick MemoryMappingLayout memory_mapping(false);
13683cab2bb3Spatrick InternalMmapVector<LoadedModule> modules;
13693cab2bb3Spatrick modules.reserve(128);
13703cab2bb3Spatrick memory_mapping.DumpListOfModules(&modules);
13713cab2bb3Spatrick Sort(modules.data(), modules.size(), CompareBaseAddress);
13723cab2bb3Spatrick for (uptr i = 0; i < modules.size(); ++i) {
13733cab2bb3Spatrick char uuid_str[128];
13743cab2bb3Spatrick FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid());
13753cab2bb3Spatrick Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(),
1376*810390e3Srobert modules[i].max_address(), modules[i].full_name(),
13773cab2bb3Spatrick ModuleArchToString(modules[i].arch()), uuid_str);
13783cab2bb3Spatrick }
13793cab2bb3Spatrick Printf("End of module map.\n");
13803cab2bb3Spatrick }
13813cab2bb3Spatrick
CheckNoDeepBind(const char * filename,int flag)13823cab2bb3Spatrick void CheckNoDeepBind(const char *filename, int flag) {
13833cab2bb3Spatrick // Do nothing.
13843cab2bb3Spatrick }
13853cab2bb3Spatrick
GetRandom(void * buffer,uptr length,bool blocking)13863cab2bb3Spatrick bool GetRandom(void *buffer, uptr length, bool blocking) {
13873cab2bb3Spatrick if (!buffer || !length || length > 256)
13883cab2bb3Spatrick return false;
13893cab2bb3Spatrick // arc4random never fails.
13903cab2bb3Spatrick REAL(arc4random_buf)(buffer, length);
13913cab2bb3Spatrick return true;
13923cab2bb3Spatrick }
13933cab2bb3Spatrick
GetNumberOfCPUs()13943cab2bb3Spatrick u32 GetNumberOfCPUs() {
13953cab2bb3Spatrick return (u32)sysconf(_SC_NPROCESSORS_ONLN);
13963cab2bb3Spatrick }
13973cab2bb3Spatrick
InitializePlatformCommonFlags(CommonFlags * cf)1398d89ec533Spatrick void InitializePlatformCommonFlags(CommonFlags *cf) {}
1399d89ec533Spatrick
1400*810390e3Srobert // Pthread introspection hook
1401*810390e3Srobert //
1402*810390e3Srobert // * GCD worker threads are created without a call to pthread_create(), but we
1403*810390e3Srobert // still need to register these threads (with ThreadCreate/Start()).
1404*810390e3Srobert // * We use the "pthread introspection hook" below to observe the creation of
1405*810390e3Srobert // such threads.
1406*810390e3Srobert // * GCD worker threads don't have parent threads and the CREATE event is
1407*810390e3Srobert // delivered in the context of the thread itself. CREATE events for regular
1408*810390e3Srobert // threads, are delivered on the parent. We use this to tell apart which
1409*810390e3Srobert // threads are GCD workers with `thread == pthread_self()`.
1410*810390e3Srobert //
1411*810390e3Srobert static pthread_introspection_hook_t prev_pthread_introspection_hook;
1412*810390e3Srobert static ThreadEventCallbacks thread_event_callbacks;
1413*810390e3Srobert
sanitizer_pthread_introspection_hook(unsigned int event,pthread_t thread,void * addr,size_t size)1414*810390e3Srobert static void sanitizer_pthread_introspection_hook(unsigned int event,
1415*810390e3Srobert pthread_t thread, void *addr,
1416*810390e3Srobert size_t size) {
1417*810390e3Srobert // create -> start -> terminate -> destroy
1418*810390e3Srobert // * create/destroy are usually (not guaranteed) delivered on the parent and
1419*810390e3Srobert // track resource allocation/reclamation
1420*810390e3Srobert // * start/terminate are guaranteed to be delivered in the context of the
1421*810390e3Srobert // thread and give hooks into "just after (before) thread starts (stops)
1422*810390e3Srobert // executing"
1423*810390e3Srobert DCHECK(event >= PTHREAD_INTROSPECTION_THREAD_CREATE &&
1424*810390e3Srobert event <= PTHREAD_INTROSPECTION_THREAD_DESTROY);
1425*810390e3Srobert
1426*810390e3Srobert if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) {
1427*810390e3Srobert bool gcd_worker = (thread == pthread_self());
1428*810390e3Srobert if (thread_event_callbacks.create)
1429*810390e3Srobert thread_event_callbacks.create((uptr)thread, gcd_worker);
1430*810390e3Srobert } else if (event == PTHREAD_INTROSPECTION_THREAD_START) {
1431*810390e3Srobert CHECK_EQ(thread, pthread_self());
1432*810390e3Srobert if (thread_event_callbacks.start)
1433*810390e3Srobert thread_event_callbacks.start((uptr)thread);
1434*810390e3Srobert }
1435*810390e3Srobert
1436*810390e3Srobert if (prev_pthread_introspection_hook)
1437*810390e3Srobert prev_pthread_introspection_hook(event, thread, addr, size);
1438*810390e3Srobert
1439*810390e3Srobert if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) {
1440*810390e3Srobert CHECK_EQ(thread, pthread_self());
1441*810390e3Srobert if (thread_event_callbacks.terminate)
1442*810390e3Srobert thread_event_callbacks.terminate((uptr)thread);
1443*810390e3Srobert } else if (event == PTHREAD_INTROSPECTION_THREAD_DESTROY) {
1444*810390e3Srobert if (thread_event_callbacks.destroy)
1445*810390e3Srobert thread_event_callbacks.destroy((uptr)thread);
1446*810390e3Srobert }
1447*810390e3Srobert }
1448*810390e3Srobert
InstallPthreadIntrospectionHook(const ThreadEventCallbacks & callbacks)1449*810390e3Srobert void InstallPthreadIntrospectionHook(const ThreadEventCallbacks &callbacks) {
1450*810390e3Srobert thread_event_callbacks = callbacks;
1451*810390e3Srobert prev_pthread_introspection_hook =
1452*810390e3Srobert pthread_introspection_hook_install(&sanitizer_pthread_introspection_hook);
1453*810390e3Srobert }
1454*810390e3Srobert
14553cab2bb3Spatrick } // namespace __sanitizer
14563cab2bb3Spatrick
1457*810390e3Srobert #endif // SANITIZER_APPLE
1458