2012-06-04 22:27:50 +08:00
|
|
|
//===-- sanitizer_mac.cc --------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2014-02-03 23:32:19 +08:00
|
|
|
// This file is shared between various sanitizers' runtime libraries and
|
|
|
|
// implements OSX-specific functions.
|
2012-06-04 22:27:50 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-03-19 22:33:38 +08:00
|
|
|
#include "sanitizer_platform.h"
|
|
|
|
#if SANITIZER_MAC
|
2015-11-21 02:42:01 +08:00
|
|
|
#include "sanitizer_mac.h"
|
2013-03-19 22:33:38 +08:00
|
|
|
|
2013-02-06 22:41:15 +08:00
|
|
|
// Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so
|
|
|
|
// the clients will most certainly use 64-bit ones as well.
|
|
|
|
#ifndef _DARWIN_USE_64_BIT_INODE
|
|
|
|
#define _DARWIN_USE_64_BIT_INODE 1
|
|
|
|
#endif
|
|
|
|
#include <stdio.h>
|
2012-06-04 22:27:50 +08:00
|
|
|
|
2012-06-07 15:13:46 +08:00
|
|
|
#include "sanitizer_common.h"
|
2014-01-31 21:10:07 +08:00
|
|
|
#include "sanitizer_flags.h"
|
2012-06-05 22:25:27 +08:00
|
|
|
#include "sanitizer_internal_defs.h"
|
2012-06-04 22:27:50 +08:00
|
|
|
#include "sanitizer_libc.h"
|
2013-09-10 22:36:16 +08:00
|
|
|
#include "sanitizer_placement_new.h"
|
2015-05-13 05:30:16 +08:00
|
|
|
#include "sanitizer_platform_limits_posix.h"
|
2012-06-07 14:15:12 +08:00
|
|
|
#include "sanitizer_procmaps.h"
|
2012-06-04 22:27:50 +08:00
|
|
|
|
2015-06-24 05:39:49 +08:00
|
|
|
#if !SANITIZER_IOS
|
2012-06-14 22:07:21 +08:00
|
|
|
#include <crt_externs.h> // for _NSGetEnviron
|
2015-06-24 05:39:49 +08:00
|
|
|
#else
|
|
|
|
extern char **environ;
|
|
|
|
#endif
|
|
|
|
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
#if defined(__has_include) && __has_include(<os/trace.h>)
|
|
|
|
#define SANITIZER_OS_TRACE 1
|
|
|
|
#include <os/trace.h>
|
|
|
|
#else
|
|
|
|
#define SANITIZER_OS_TRACE 0
|
|
|
|
#endif
|
|
|
|
|
2015-12-03 18:39:43 +08:00
|
|
|
#if !SANITIZER_IOS
|
|
|
|
#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
|
|
|
|
#else
|
|
|
|
extern "C" {
|
|
|
|
extern char ***_NSGetArgv(void);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
#include <asl.h>
|
2015-12-03 18:39:43 +08:00
|
|
|
#include <dlfcn.h> // for dladdr()
|
2015-05-13 04:47:21 +08:00
|
|
|
#include <errno.h>
|
2012-06-07 15:13:46 +08:00
|
|
|
#include <fcntl.h>
|
2015-05-13 04:47:21 +08:00
|
|
|
#include <libkern/OSAtomic.h>
|
2015-02-27 11:12:19 +08:00
|
|
|
#include <mach-o/dyld.h>
|
2015-05-13 04:47:21 +08:00
|
|
|
#include <mach/mach.h>
|
2015-10-28 04:12:53 +08:00
|
|
|
#include <mach/vm_statistics.h>
|
2012-06-07 15:13:46 +08:00
|
|
|
#include <pthread.h>
|
2012-06-18 16:44:30 +08:00
|
|
|
#include <sched.h>
|
2014-01-31 21:10:07 +08:00
|
|
|
#include <signal.h>
|
2015-02-27 11:12:19 +08:00
|
|
|
#include <stdlib.h>
|
2012-06-04 22:27:50 +08:00
|
|
|
#include <sys/mman.h>
|
2012-06-07 15:13:46 +08:00
|
|
|
#include <sys/resource.h>
|
2012-06-05 15:05:10 +08:00
|
|
|
#include <sys/stat.h>
|
2014-02-03 23:32:19 +08:00
|
|
|
#include <sys/sysctl.h>
|
2012-06-05 15:05:10 +08:00
|
|
|
#include <sys/types.h>
|
2016-01-27 06:53:52 +08:00
|
|
|
#include <sys/wait.h>
|
2012-06-05 16:32:53 +08:00
|
|
|
#include <unistd.h>
|
2015-11-20 22:28:33 +08:00
|
|
|
#include <util.h>
|
2012-06-04 22:27:50 +08:00
|
|
|
|
2016-02-02 10:01:17 +08:00
|
|
|
// from <crt_externs.h>, but we don't have that file on iOS
|
|
|
|
extern "C" {
|
|
|
|
extern char ***_NSGetArgv(void);
|
|
|
|
extern char ***_NSGetEnviron(void);
|
|
|
|
}
|
|
|
|
|
2012-06-04 22:27:50 +08:00
|
|
|
namespace __sanitizer {
|
|
|
|
|
2013-05-08 22:43:49 +08:00
|
|
|
#include "sanitizer_syscall_generic.inc"
|
|
|
|
|
2012-06-07 15:13:46 +08:00
|
|
|
// ---------------------- sanitizer_libc.h
|
2013-05-08 23:07:12 +08:00
|
|
|
uptr internal_mmap(void *addr, size_t length, int prot, int flags,
|
|
|
|
int fd, u64 offset) {
|
2015-10-28 04:12:53 +08:00
|
|
|
if (fd == -1) fd = VM_MAKE_TAG(VM_MEMORY_ANALYSIS_TOOL);
|
2013-05-08 23:07:12 +08:00
|
|
|
return (uptr)mmap(addr, length, prot, flags, fd, offset);
|
2012-06-04 22:27:50 +08:00
|
|
|
}
|
|
|
|
|
2013-05-08 23:07:12 +08:00
|
|
|
uptr internal_munmap(void *addr, uptr length) {
|
2012-06-05 17:49:25 +08:00
|
|
|
return munmap(addr, length);
|
|
|
|
}
|
|
|
|
|
2015-04-10 23:02:19 +08:00
|
|
|
int internal_mprotect(void *addr, uptr length, int prot) {
|
|
|
|
return mprotect(addr, length, prot);
|
|
|
|
}
|
|
|
|
|
2013-05-08 22:43:49 +08:00
|
|
|
uptr internal_close(fd_t fd) {
|
2012-06-05 16:32:53 +08:00
|
|
|
return close(fd);
|
|
|
|
}
|
|
|
|
|
2013-05-08 22:43:49 +08:00
|
|
|
uptr internal_open(const char *filename, int flags) {
|
2013-02-01 23:58:46 +08:00
|
|
|
return open(filename, flags);
|
|
|
|
}
|
|
|
|
|
2013-05-08 22:43:49 +08:00
|
|
|
uptr internal_open(const char *filename, int flags, u32 mode) {
|
2013-02-01 23:58:46 +08:00
|
|
|
return open(filename, flags, mode);
|
|
|
|
}
|
|
|
|
|
2012-06-05 16:32:53 +08:00
|
|
|
uptr internal_read(fd_t fd, void *buf, uptr count) {
|
|
|
|
return read(fd, buf, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
uptr internal_write(fd_t fd, const void *buf, uptr count) {
|
|
|
|
return write(fd, buf, count);
|
|
|
|
}
|
|
|
|
|
2013-05-08 22:43:49 +08:00
|
|
|
uptr internal_stat(const char *path, void *buf) {
|
2013-02-04 18:31:39 +08:00
|
|
|
return stat(path, (struct stat *)buf);
|
2013-02-04 18:16:50 +08:00
|
|
|
}
|
|
|
|
|
2013-05-08 22:43:49 +08:00
|
|
|
uptr internal_lstat(const char *path, void *buf) {
|
2013-02-04 18:31:39 +08:00
|
|
|
return lstat(path, (struct stat *)buf);
|
2013-02-04 18:16:50 +08:00
|
|
|
}
|
|
|
|
|
2013-05-08 22:43:49 +08:00
|
|
|
uptr internal_fstat(fd_t fd, void *buf) {
|
2013-02-04 18:31:39 +08:00
|
|
|
return fstat(fd, (struct stat *)buf);
|
2013-02-04 18:16:50 +08:00
|
|
|
}
|
|
|
|
|
2012-06-06 15:30:33 +08:00
|
|
|
uptr internal_filesize(fd_t fd) {
|
2012-08-02 18:09:31 +08:00
|
|
|
struct stat st;
|
2013-02-04 18:16:50 +08:00
|
|
|
if (internal_fstat(fd, &st))
|
2012-06-06 15:30:33 +08:00
|
|
|
return -1;
|
|
|
|
return (uptr)st.st_size;
|
|
|
|
}
|
|
|
|
|
2013-05-08 22:43:49 +08:00
|
|
|
uptr internal_dup2(int oldfd, int newfd) {
|
2012-06-06 15:30:33 +08:00
|
|
|
return dup2(oldfd, newfd);
|
|
|
|
}
|
|
|
|
|
2012-09-05 22:48:24 +08:00
|
|
|
uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
|
|
|
|
return readlink(path, buf, bufsize);
|
|
|
|
}
|
|
|
|
|
2014-12-29 10:18:59 +08:00
|
|
|
uptr internal_unlink(const char *path) {
|
|
|
|
return unlink(path);
|
|
|
|
}
|
|
|
|
|
2013-05-08 22:43:49 +08:00
|
|
|
uptr internal_sched_yield() {
|
2012-06-18 16:44:30 +08:00
|
|
|
return sched_yield();
|
|
|
|
}
|
|
|
|
|
2013-02-20 21:54:32 +08:00
|
|
|
void internal__exit(int exitcode) {
|
|
|
|
_exit(exitcode);
|
|
|
|
}
|
|
|
|
|
2013-05-18 00:56:53 +08:00
|
|
|
uptr internal_getpid() {
|
|
|
|
return getpid();
|
|
|
|
}
|
|
|
|
|
2014-01-31 21:10:07 +08:00
|
|
|
int internal_sigaction(int signum, const void *act, void *oldact) {
|
|
|
|
return sigaction(signum,
|
|
|
|
(struct sigaction *)act, (struct sigaction *)oldact);
|
|
|
|
}
|
|
|
|
|
2015-05-13 04:56:44 +08:00
|
|
|
void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); }
|
|
|
|
|
|
|
|
uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
|
|
|
|
__sanitizer_sigset_t *oldset) {
|
|
|
|
return sigprocmask(how, set, oldset);
|
|
|
|
}
|
|
|
|
|
2015-11-20 22:28:33 +08:00
|
|
|
// Doesn't call pthread_atfork() handlers.
|
|
|
|
extern "C" pid_t __fork(void);
|
|
|
|
|
2014-05-14 00:17:54 +08:00
|
|
|
int internal_fork() {
|
2015-11-20 22:28:33 +08:00
|
|
|
return __fork();
|
|
|
|
}
|
|
|
|
|
|
|
|
int internal_forkpty(int *amaster) {
|
|
|
|
int master, slave;
|
|
|
|
if (openpty(&master, &slave, nullptr, nullptr, nullptr) == -1) return -1;
|
|
|
|
int pid = __fork();
|
|
|
|
if (pid == -1) {
|
|
|
|
close(master);
|
|
|
|
close(slave);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (pid == 0) {
|
|
|
|
close(master);
|
|
|
|
CHECK_EQ(login_tty(slave), 0);
|
|
|
|
} else {
|
|
|
|
*amaster = master;
|
|
|
|
close(slave);
|
|
|
|
}
|
|
|
|
return pid;
|
2014-05-14 00:17:54 +08:00
|
|
|
}
|
|
|
|
|
2014-05-27 20:37:52 +08:00
|
|
|
uptr internal_rename(const char *oldpath, const char *newpath) {
|
|
|
|
return rename(oldpath, newpath);
|
|
|
|
}
|
|
|
|
|
|
|
|
uptr internal_ftruncate(fd_t fd, uptr size) {
|
|
|
|
return ftruncate(fd, size);
|
|
|
|
}
|
|
|
|
|
2016-01-27 06:53:52 +08:00
|
|
|
uptr internal_execve(const char *filename, char *const argv[],
|
|
|
|
char *const envp[]) {
|
|
|
|
return execve(filename, argv, envp);
|
|
|
|
}
|
|
|
|
|
|
|
|
uptr internal_waitpid(int pid, int *status, int options) {
|
|
|
|
return waitpid(pid, status, options);
|
|
|
|
}
|
|
|
|
|
2012-06-07 15:13:46 +08:00
|
|
|
// ----------------- sanitizer_common.h
|
2012-11-09 22:45:30 +08:00
|
|
|
bool FileExists(const char *filename) {
|
|
|
|
struct stat st;
|
|
|
|
if (stat(filename, &st))
|
|
|
|
return false;
|
|
|
|
// Sanity check: filename is a regular file.
|
|
|
|
return S_ISREG(st.st_mode);
|
|
|
|
}
|
|
|
|
|
2012-10-02 20:58:14 +08:00
|
|
|
uptr GetTid() {
|
|
|
|
return reinterpret_cast<uptr>(pthread_self());
|
|
|
|
}
|
|
|
|
|
2012-06-07 15:32:00 +08:00
|
|
|
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
|
2012-06-07 15:13:46 +08:00
|
|
|
uptr *stack_bottom) {
|
|
|
|
CHECK(stack_top);
|
|
|
|
CHECK(stack_bottom);
|
|
|
|
uptr stacksize = pthread_get_stacksize_np(pthread_self());
|
2014-02-04 00:42:29 +08:00
|
|
|
// pthread_get_stacksize_np() returns an incorrect stack size for the main
|
|
|
|
// thread on Mavericks. See
|
2015-12-05 01:50:03 +08:00
|
|
|
// https://github.com/google/sanitizers/issues/261
|
2014-11-06 02:55:38 +08:00
|
|
|
if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization &&
|
2014-02-04 00:42:29 +08:00
|
|
|
stacksize == (1 << 19)) {
|
|
|
|
struct rlimit rl;
|
|
|
|
CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
|
|
|
|
// Most often rl.rlim_cur will be the desired 8M.
|
|
|
|
if (rl.rlim_cur < kMaxThreadStackSize) {
|
|
|
|
stacksize = rl.rlim_cur;
|
|
|
|
} else {
|
|
|
|
stacksize = kMaxThreadStackSize;
|
|
|
|
}
|
|
|
|
}
|
2012-06-07 15:13:46 +08:00
|
|
|
void *stackaddr = pthread_get_stackaddr_np(pthread_self());
|
|
|
|
*stack_top = (uptr)stackaddr;
|
2012-06-07 15:32:00 +08:00
|
|
|
*stack_bottom = *stack_top - stacksize;
|
2012-06-07 15:13:46 +08:00
|
|
|
}
|
|
|
|
|
2015-06-24 05:39:49 +08:00
|
|
|
char **GetEnviron() {
|
|
|
|
#if !SANITIZER_IOS
|
2012-06-14 22:07:21 +08:00
|
|
|
char ***env_ptr = _NSGetEnviron();
|
2013-11-13 21:34:53 +08:00
|
|
|
if (!env_ptr) {
|
|
|
|
Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
|
|
|
|
"called after libSystem_initializer().\n");
|
|
|
|
CHECK(env_ptr);
|
|
|
|
}
|
2012-06-14 22:07:21 +08:00
|
|
|
char **environ = *env_ptr;
|
2015-06-24 05:39:49 +08:00
|
|
|
#endif
|
2012-06-14 22:07:21 +08:00
|
|
|
CHECK(environ);
|
2015-06-24 05:39:49 +08:00
|
|
|
return environ;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *GetEnv(const char *name) {
|
|
|
|
char **env = GetEnviron();
|
2012-06-14 22:07:21 +08:00
|
|
|
uptr name_len = internal_strlen(name);
|
2015-06-24 05:39:49 +08:00
|
|
|
while (*env != 0) {
|
|
|
|
uptr len = internal_strlen(*env);
|
2012-06-14 22:07:21 +08:00
|
|
|
if (len > name_len) {
|
2015-06-24 05:39:49 +08:00
|
|
|
const char *p = *env;
|
2012-06-14 22:07:21 +08:00
|
|
|
if (!internal_memcmp(p, name, name_len) &&
|
|
|
|
p[name_len] == '=') { // Match.
|
2015-06-24 05:39:49 +08:00
|
|
|
return *env + name_len + 1; // String starting after =.
|
2012-06-14 22:07:21 +08:00
|
|
|
}
|
|
|
|
}
|
2015-06-24 05:39:49 +08:00
|
|
|
env++;
|
2012-06-14 22:07:21 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2012-06-07 15:13:46 +08:00
|
|
|
|
2015-02-27 11:12:19 +08:00
|
|
|
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
|
|
|
|
CHECK_LE(kMaxPathLength, buf_len);
|
|
|
|
|
|
|
|
// On OS X the executable path is saved to the stack by dyld. Reading it
|
|
|
|
// from there is much faster than calling dladdr, especially for large
|
|
|
|
// binaries with symbols.
|
|
|
|
InternalScopedString exe_path(kMaxPathLength);
|
|
|
|
uint32_t size = exe_path.size();
|
|
|
|
if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
|
|
|
|
realpath(exe_path.data(), buf) != 0) {
|
|
|
|
return internal_strlen(buf);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-29 05:01:42 +08:00
|
|
|
uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
|
|
|
|
return ReadBinaryName(buf, buf_len);
|
|
|
|
}
|
|
|
|
|
2012-09-17 17:12:39 +08:00
|
|
|
void ReExec() {
|
|
|
|
UNIMPLEMENTED();
|
|
|
|
}
|
|
|
|
|
2013-05-21 01:05:29 +08:00
|
|
|
uptr GetPageSize() {
|
|
|
|
return sysconf(_SC_PAGESIZE);
|
|
|
|
}
|
|
|
|
|
2013-03-14 21:30:56 +08:00
|
|
|
BlockingMutex::BlockingMutex() {
|
|
|
|
internal_memset(this, 0, sizeof(*this));
|
|
|
|
}
|
|
|
|
|
2013-01-14 15:51:39 +08:00
|
|
|
void BlockingMutex::Lock() {
|
|
|
|
CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
|
2013-02-26 20:59:06 +08:00
|
|
|
CHECK_EQ(OS_SPINLOCK_INIT, 0);
|
|
|
|
CHECK_NE(owner_, (uptr)pthread_self());
|
2013-01-14 15:51:39 +08:00
|
|
|
OSSpinLockLock((OSSpinLock*)&opaque_storage_);
|
|
|
|
CHECK(!owner_);
|
|
|
|
owner_ = (uptr)pthread_self();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlockingMutex::Unlock() {
|
|
|
|
CHECK(owner_ == (uptr)pthread_self());
|
|
|
|
owner_ = 0;
|
|
|
|
OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
|
|
|
|
}
|
|
|
|
|
2013-03-11 23:45:20 +08:00
|
|
|
void BlockingMutex::CheckLocked() {
|
|
|
|
CHECK_EQ((uptr)pthread_self(), owner_);
|
|
|
|
}
|
|
|
|
|
2013-06-06 21:00:32 +08:00
|
|
|
u64 NanoTime() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-13 16:19:53 +08:00
|
|
|
uptr GetTlsSize() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InitTlsSize() {
|
|
|
|
}
|
|
|
|
|
2013-05-07 22:41:43 +08:00
|
|
|
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
|
|
|
|
uptr *tls_addr, uptr *tls_size) {
|
2013-06-06 21:20:40 +08:00
|
|
|
#ifndef SANITIZER_GO
|
2013-05-07 22:41:43 +08:00
|
|
|
uptr stack_top, stack_bottom;
|
|
|
|
GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
|
|
|
|
*stk_addr = stack_bottom;
|
|
|
|
*stk_size = stack_top - stack_bottom;
|
|
|
|
*tls_addr = 0;
|
|
|
|
*tls_size = 0;
|
2013-06-06 21:20:40 +08:00
|
|
|
#else
|
|
|
|
*stk_addr = 0;
|
|
|
|
*stk_size = 0;
|
|
|
|
*tls_addr = 0;
|
|
|
|
*tls_size = 0;
|
|
|
|
#endif
|
2013-05-07 22:41:43 +08:00
|
|
|
}
|
|
|
|
|
2016-02-23 02:52:51 +08:00
|
|
|
void ListOfModules::init() {
|
|
|
|
clear();
|
2013-09-10 22:36:16 +08:00
|
|
|
MemoryMappingLayout memory_mapping(false);
|
2016-02-23 02:52:51 +08:00
|
|
|
memory_mapping.DumpListOfModules(&modules_);
|
2013-09-10 22:36:16 +08:00
|
|
|
}
|
|
|
|
|
2016-02-02 10:01:17 +08:00
|
|
|
bool IsHandledDeadlySignal(int signum) {
|
|
|
|
if ((SANITIZER_WATCHOS || SANITIZER_TVOS) && !(SANITIZER_IOSSIM))
|
|
|
|
// Handling fatal signals on watchOS and tvOS devices is disallowed.
|
|
|
|
return false;
|
2014-01-31 21:10:07 +08:00
|
|
|
return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv;
|
|
|
|
}
|
|
|
|
|
2014-02-03 23:32:19 +08:00
|
|
|
MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED;
|
|
|
|
|
|
|
|
MacosVersion GetMacosVersionInternal() {
|
|
|
|
int mib[2] = { CTL_KERN, KERN_OSRELEASE };
|
|
|
|
char version[100];
|
|
|
|
uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
|
|
|
|
for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
|
|
|
|
// Get the version length.
|
|
|
|
CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1);
|
|
|
|
CHECK_LT(len, maxlen);
|
|
|
|
CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1);
|
|
|
|
switch (version[0]) {
|
|
|
|
case '9': return MACOS_VERSION_LEOPARD;
|
|
|
|
case '1': {
|
|
|
|
switch (version[1]) {
|
|
|
|
case '0': return MACOS_VERSION_SNOW_LEOPARD;
|
|
|
|
case '1': return MACOS_VERSION_LION;
|
|
|
|
case '2': return MACOS_VERSION_MOUNTAIN_LION;
|
|
|
|
case '3': return MACOS_VERSION_MAVERICKS;
|
2014-11-06 02:53:22 +08:00
|
|
|
case '4': return MACOS_VERSION_YOSEMITE;
|
2014-12-16 12:46:15 +08:00
|
|
|
default:
|
|
|
|
if (IsDigit(version[1]))
|
|
|
|
return MACOS_VERSION_UNKNOWN_NEWER;
|
|
|
|
else
|
|
|
|
return MACOS_VERSION_UNKNOWN;
|
2014-02-03 23:32:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
default: return MACOS_VERSION_UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MacosVersion GetMacosVersion() {
|
|
|
|
atomic_uint32_t *cache =
|
|
|
|
reinterpret_cast<atomic_uint32_t*>(&cached_macos_version);
|
|
|
|
MacosVersion result =
|
|
|
|
static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire));
|
|
|
|
if (result == MACOS_VERSION_UNINITIALIZED) {
|
|
|
|
result = GetMacosVersionInternal();
|
|
|
|
atomic_store(cache, result, memory_order_release);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-12-09 09:22:59 +08:00
|
|
|
uptr GetRSS() {
|
2015-05-13 04:47:21 +08:00
|
|
|
struct task_basic_info info;
|
|
|
|
unsigned count = TASK_BASIC_INFO_COUNT;
|
|
|
|
kern_return_t result =
|
|
|
|
task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count);
|
|
|
|
if (UNLIKELY(result != KERN_SUCCESS)) {
|
|
|
|
Report("Cannot get task info. Error: %d\n", result);
|
|
|
|
Die();
|
|
|
|
}
|
|
|
|
return info.resident_size;
|
2014-12-09 09:22:59 +08:00
|
|
|
}
|
|
|
|
|
2015-11-11 10:44:19 +08:00
|
|
|
void *internal_start_thread(void(*func)(void *arg), void *arg) {
|
|
|
|
// Start the thread with signals blocked, otherwise it can steal user signals.
|
|
|
|
__sanitizer_sigset_t set, old;
|
|
|
|
internal_sigfillset(&set);
|
|
|
|
internal_sigprocmask(SIG_SETMASK, &set, &old);
|
|
|
|
pthread_t th;
|
|
|
|
pthread_create(&th, 0, (void*(*)(void *arg))func, arg);
|
|
|
|
internal_sigprocmask(SIG_SETMASK, &old, 0);
|
|
|
|
return th;
|
|
|
|
}
|
|
|
|
|
|
|
|
void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); }
|
2014-12-17 03:13:01 +08:00
|
|
|
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
static BlockingMutex syslog_lock(LINKER_INITIALIZED);
|
|
|
|
|
|
|
|
void WriteOneLineToSyslog(const char *s) {
|
|
|
|
syslog_lock.CheckLocked();
|
|
|
|
asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s);
|
|
|
|
}
|
|
|
|
|
2016-01-07 07:15:01 +08:00
|
|
|
void LogMessageOnPrintf(const char *str) {
|
|
|
|
// Log all printf output to CrashLog.
|
|
|
|
if (common_flags()->abort_on_error)
|
|
|
|
CRAppendCrashLogMessage(str);
|
|
|
|
}
|
|
|
|
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
void LogFullErrorReport(const char *buffer) {
|
|
|
|
// Log with os_trace. This will make it into the crash log.
|
|
|
|
#if SANITIZER_OS_TRACE
|
|
|
|
if (GetMacosVersion() >= MACOS_VERSION_YOSEMITE) {
|
|
|
|
// os_trace requires the message (format parameter) to be a string literal.
|
|
|
|
if (internal_strncmp(SanitizerToolName, "AddressSanitizer",
|
|
|
|
sizeof("AddressSanitizer") - 1) == 0)
|
|
|
|
os_trace("Address Sanitizer reported a failure.");
|
|
|
|
else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer",
|
|
|
|
sizeof("UndefinedBehaviorSanitizer") - 1) == 0)
|
|
|
|
os_trace("Undefined Behavior Sanitizer reported a failure.");
|
|
|
|
else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer",
|
|
|
|
sizeof("ThreadSanitizer") - 1) == 0)
|
|
|
|
os_trace("Thread Sanitizer reported a failure.");
|
|
|
|
else
|
|
|
|
os_trace("Sanitizer tool reported a failure.");
|
|
|
|
|
|
|
|
if (common_flags()->log_to_syslog)
|
|
|
|
os_trace("Consult syslog for more information.");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Log to syslog.
|
|
|
|
// The logging on OS X may call pthread_create so we need the threading
|
|
|
|
// environment to be fully initialized. Also, this should never be called when
|
|
|
|
// holding the thread registry lock since that may result in a deadlock. If
|
|
|
|
// the reporting thread holds the thread registry mutex, and asl_log waits
|
|
|
|
// for GCD to dispatch a new thread, the process will deadlock, because the
|
|
|
|
// pthread_create wrapper needs to acquire the lock as well.
|
|
|
|
BlockingMutexLock l(&syslog_lock);
|
|
|
|
if (common_flags()->log_to_syslog)
|
|
|
|
WriteToSyslog(buffer);
|
2015-11-21 02:42:07 +08:00
|
|
|
|
2016-01-07 07:15:01 +08:00
|
|
|
// The report is added to CrashLog as part of logging all of Printf output.
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
}
|
|
|
|
|
2016-02-09 06:50:25 +08:00
|
|
|
SignalContext::WriteFlag SignalContext::GetWriteFlag(void *context) {
|
|
|
|
return UNKNOWN; // FIXME: implement this.
|
2016-02-04 10:02:09 +08:00
|
|
|
}
|
|
|
|
|
2015-03-03 01:36:02 +08:00
|
|
|
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
|
|
|
|
ucontext_t *ucontext = (ucontext_t*)context;
|
2015-06-24 05:39:49 +08:00
|
|
|
# if defined(__aarch64__)
|
|
|
|
*pc = ucontext->uc_mcontext->__ss.__pc;
|
|
|
|
# if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
|
|
|
|
*bp = ucontext->uc_mcontext->__ss.__fp;
|
|
|
|
# else
|
|
|
|
*bp = ucontext->uc_mcontext->__ss.__lr;
|
|
|
|
# endif
|
|
|
|
*sp = ucontext->uc_mcontext->__ss.__sp;
|
|
|
|
# elif defined(__x86_64__)
|
2015-03-03 01:36:02 +08:00
|
|
|
*pc = ucontext->uc_mcontext->__ss.__rip;
|
|
|
|
*bp = ucontext->uc_mcontext->__ss.__rbp;
|
|
|
|
*sp = ucontext->uc_mcontext->__ss.__rsp;
|
2015-06-24 05:39:49 +08:00
|
|
|
# elif defined(__arm__)
|
|
|
|
*pc = ucontext->uc_mcontext->__ss.__pc;
|
|
|
|
*bp = ucontext->uc_mcontext->__ss.__r[7];
|
|
|
|
*sp = ucontext->uc_mcontext->__ss.__sp;
|
|
|
|
# elif defined(__i386__)
|
2015-03-03 01:36:02 +08:00
|
|
|
*pc = ucontext->uc_mcontext->__ss.__eip;
|
|
|
|
*bp = ucontext->uc_mcontext->__ss.__ebp;
|
|
|
|
*sp = ucontext->uc_mcontext->__ss.__esp;
|
2015-06-24 05:39:49 +08:00
|
|
|
# else
|
|
|
|
# error "Unknown architecture"
|
|
|
|
# endif
|
2015-03-03 01:36:02 +08:00
|
|
|
}
|
|
|
|
|
2015-12-03 18:39:43 +08:00
|
|
|
static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
|
|
|
|
LowLevelAllocator allocator_for_env;
|
|
|
|
|
|
|
|
// Change the value of the env var |name|, leaking the original value.
|
|
|
|
// If |name_value| is NULL, the variable is deleted from the environment,
|
|
|
|
// otherwise the corresponding "NAME=value" string is replaced with
|
|
|
|
// |name_value|.
|
|
|
|
void LeakyResetEnv(const char *name, const char *name_value) {
|
|
|
|
char **env = GetEnviron();
|
|
|
|
uptr name_len = internal_strlen(name);
|
|
|
|
while (*env != 0) {
|
|
|
|
uptr len = internal_strlen(*env);
|
|
|
|
if (len > name_len) {
|
|
|
|
const char *p = *env;
|
|
|
|
if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
|
|
|
|
// Match.
|
|
|
|
if (name_value) {
|
|
|
|
// Replace the old value with the new one.
|
|
|
|
*env = const_cast<char*>(name_value);
|
|
|
|
} else {
|
|
|
|
// Shift the subsequent pointers back.
|
|
|
|
char **del = env;
|
|
|
|
do {
|
|
|
|
del[0] = del[1];
|
|
|
|
} while (*del++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
env++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool reexec_disabled = false;
|
|
|
|
|
|
|
|
void DisableReexec() {
|
|
|
|
reexec_disabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" double dyldVersionNumber;
|
|
|
|
static const double kMinDyldVersionWithAutoInterposition = 360.0;
|
|
|
|
|
|
|
|
bool DyldNeedsEnvVariable() {
|
|
|
|
// If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
|
|
|
|
// DYLD_INSERT_LIBRARIES is not set. However, checking OS version via
|
|
|
|
// GetMacosVersion() doesn't work for the simulator. Let's instead check
|
|
|
|
// `dyldVersionNumber`, which is exported by dyld, against a known version
|
|
|
|
// number from the first OS release where this appeared.
|
|
|
|
return dyldVersionNumber < kMinDyldVersionWithAutoInterposition;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MaybeReexec() {
|
|
|
|
if (reexec_disabled) return;
|
|
|
|
|
|
|
|
// Make sure the dynamic runtime library is preloaded so that the
|
|
|
|
// wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
|
|
|
|
// ourselves.
|
|
|
|
Dl_info info;
|
|
|
|
CHECK(dladdr((void*)((uptr)&__sanitizer_report_error_summary), &info));
|
|
|
|
char *dyld_insert_libraries =
|
|
|
|
const_cast<char*>(GetEnv(kDyldInsertLibraries));
|
|
|
|
uptr old_env_len = dyld_insert_libraries ?
|
|
|
|
internal_strlen(dyld_insert_libraries) : 0;
|
|
|
|
uptr fname_len = internal_strlen(info.dli_fname);
|
|
|
|
const char *dylib_name = StripModuleName(info.dli_fname);
|
|
|
|
uptr dylib_name_len = internal_strlen(dylib_name);
|
|
|
|
|
|
|
|
bool lib_is_in_env = dyld_insert_libraries &&
|
|
|
|
internal_strstr(dyld_insert_libraries, dylib_name);
|
|
|
|
if (DyldNeedsEnvVariable() && !lib_is_in_env) {
|
|
|
|
// DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
|
|
|
|
// library.
|
2015-12-04 01:05:43 +08:00
|
|
|
InternalScopedString program_name(1024);
|
|
|
|
uint32_t buf_size = program_name.size();
|
|
|
|
_NSGetExecutablePath(program_name.data(), &buf_size);
|
2015-12-03 18:39:43 +08:00
|
|
|
char *new_env = const_cast<char*>(info.dli_fname);
|
|
|
|
if (dyld_insert_libraries) {
|
|
|
|
// Append the runtime dylib name to the existing value of
|
|
|
|
// DYLD_INSERT_LIBRARIES.
|
|
|
|
new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
|
|
|
|
internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
|
|
|
|
new_env[old_env_len] = ':';
|
|
|
|
// Copy fname_len and add a trailing zero.
|
|
|
|
internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
|
|
|
|
fname_len + 1);
|
|
|
|
// Ok to use setenv() since the wrappers don't depend on the value of
|
|
|
|
// asan_inited.
|
|
|
|
setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
|
|
|
|
} else {
|
|
|
|
// Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
|
|
|
|
setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
|
|
|
|
}
|
|
|
|
VReport(1, "exec()-ing the program with\n");
|
|
|
|
VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env);
|
|
|
|
VReport(1, "to enable wrappers.\n");
|
2015-12-04 01:05:43 +08:00
|
|
|
execv(program_name.data(), *_NSGetArgv());
|
2015-12-03 18:39:43 +08:00
|
|
|
|
|
|
|
// We get here only if execv() failed.
|
|
|
|
Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, "
|
|
|
|
"which is required for the sanitizer to work. We tried to set the "
|
|
|
|
"environment variable and re-execute itself, but execv() failed, "
|
|
|
|
"possibly because of sandbox restrictions. Make sure to launch the "
|
|
|
|
"executable with:\n%s=%s\n", kDyldInsertLibraries, new_env);
|
|
|
|
CHECK("execv failed" && 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!lib_is_in_env)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
|
|
|
|
// the dylib from the environment variable, because interceptors are installed
|
|
|
|
// and we don't want our children to inherit the variable.
|
|
|
|
|
|
|
|
uptr env_name_len = internal_strlen(kDyldInsertLibraries);
|
|
|
|
// Allocate memory to hold the previous env var name, its value, the '='
|
|
|
|
// sign and the '\0' char.
|
|
|
|
char *new_env = (char*)allocator_for_env.Allocate(
|
|
|
|
old_env_len + 2 + env_name_len);
|
|
|
|
CHECK(new_env);
|
|
|
|
internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
|
|
|
|
internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
|
|
|
|
new_env[env_name_len] = '=';
|
|
|
|
char *new_env_pos = new_env + env_name_len + 1;
|
|
|
|
|
|
|
|
// Iterate over colon-separated pieces of |dyld_insert_libraries|.
|
|
|
|
char *piece_start = dyld_insert_libraries;
|
|
|
|
char *piece_end = NULL;
|
|
|
|
char *old_env_end = dyld_insert_libraries + old_env_len;
|
|
|
|
do {
|
|
|
|
if (piece_start[0] == ':') piece_start++;
|
|
|
|
piece_end = internal_strchr(piece_start, ':');
|
|
|
|
if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
|
|
|
|
if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
|
|
|
|
uptr piece_len = piece_end - piece_start;
|
|
|
|
|
|
|
|
char *filename_start =
|
|
|
|
(char *)internal_memrchr(piece_start, '/', piece_len);
|
|
|
|
uptr filename_len = piece_len;
|
|
|
|
if (filename_start) {
|
|
|
|
filename_start += 1;
|
|
|
|
filename_len = piece_len - (filename_start - piece_start);
|
|
|
|
} else {
|
|
|
|
filename_start = piece_start;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the current piece isn't the runtime library name,
|
|
|
|
// append it to new_env.
|
|
|
|
if ((dylib_name_len != filename_len) ||
|
|
|
|
(internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
|
|
|
|
if (new_env_pos != new_env + env_name_len + 1) {
|
|
|
|
new_env_pos[0] = ':';
|
|
|
|
new_env_pos++;
|
|
|
|
}
|
|
|
|
internal_strncpy(new_env_pos, piece_start, piece_len);
|
|
|
|
new_env_pos += piece_len;
|
|
|
|
}
|
|
|
|
// Move on to the next piece.
|
|
|
|
piece_start = piece_end;
|
|
|
|
} while (piece_start < old_env_end);
|
|
|
|
|
|
|
|
// Can't use setenv() here, because it requires the allocator to be
|
|
|
|
// initialized.
|
|
|
|
// FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
|
|
|
|
// a separate function called after InitializeAllocator().
|
|
|
|
if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
|
|
|
|
LeakyResetEnv(kDyldInsertLibraries, new_env);
|
|
|
|
}
|
|
|
|
|
2016-01-18 15:55:12 +08:00
|
|
|
char **GetArgv() {
|
|
|
|
return *_NSGetArgv();
|
|
|
|
}
|
|
|
|
|
2012-06-04 22:27:50 +08:00
|
|
|
} // namespace __sanitizer
|
|
|
|
|
2013-04-03 15:24:35 +08:00
|
|
|
#endif // SANITIZER_MAC
|