1//===-- sanitizer_mac.cc --------------------------------------------------===//
2//
3// This file is distributed under the University of Illinois Open Source
4// License. See LICENSE.TXT for details.
5//
6//===----------------------------------------------------------------------===//
7//
8// This file is shared between various sanitizers' runtime libraries and
9// implements OSX-specific functions.
10//===----------------------------------------------------------------------===//
11
12#include "sanitizer_platform.h"
13#if SANITIZER_MAC
14
15// Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so
16// the clients will most certainly use 64-bit ones as well.
17#ifndef _DARWIN_USE_64_BIT_INODE
18#define _DARWIN_USE_64_BIT_INODE 1
19#endif
20#include <stdio.h>
21
22#include "sanitizer_common.h"
23#include "sanitizer_flags.h"
24#include "sanitizer_internal_defs.h"
25#include "sanitizer_libc.h"
26#include "sanitizer_mac.h"
27#include "sanitizer_placement_new.h"
28#include "sanitizer_procmaps.h"
29
30#include <crt_externs.h>  // for _NSGetEnviron
31#include <fcntl.h>
32#include <pthread.h>
33#include <sched.h>
34#include <signal.h>
35#include <sys/mman.h>
36#include <sys/resource.h>
37#include <sys/stat.h>
38#include <sys/sysctl.h>
39#include <sys/types.h>
40#include <unistd.h>
41#include <libkern/OSAtomic.h>
42#include <errno.h>
43
44namespace __sanitizer {
45
46#include "sanitizer_syscall_generic.inc"
47
48// ---------------------- sanitizer_libc.h
49uptr internal_mmap(void *addr, size_t length, int prot, int flags,
50                   int fd, u64 offset) {
51  return (uptr)mmap(addr, length, prot, flags, fd, offset);
52}
53
54uptr internal_munmap(void *addr, uptr length) {
55  return munmap(addr, length);
56}
57
58uptr internal_close(fd_t fd) {
59  return close(fd);
60}
61
62uptr internal_open(const char *filename, int flags) {
63  return open(filename, flags);
64}
65
66uptr internal_open(const char *filename, int flags, u32 mode) {
67  return open(filename, flags, mode);
68}
69
70uptr OpenFile(const char *filename, bool write) {
71  return internal_open(filename,
72      write ? O_WRONLY | O_CREAT : O_RDONLY, 0660);
73}
74
75uptr internal_read(fd_t fd, void *buf, uptr count) {
76  return read(fd, buf, count);
77}
78
79uptr internal_write(fd_t fd, const void *buf, uptr count) {
80  return write(fd, buf, count);
81}
82
83uptr internal_stat(const char *path, void *buf) {
84  return stat(path, (struct stat *)buf);
85}
86
87uptr internal_lstat(const char *path, void *buf) {
88  return lstat(path, (struct stat *)buf);
89}
90
91uptr internal_fstat(fd_t fd, void *buf) {
92  return fstat(fd, (struct stat *)buf);
93}
94
95uptr internal_filesize(fd_t fd) {
96  struct stat st;
97  if (internal_fstat(fd, &st))
98    return -1;
99  return (uptr)st.st_size;
100}
101
102uptr internal_dup2(int oldfd, int newfd) {
103  return dup2(oldfd, newfd);
104}
105
106uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
107  return readlink(path, buf, bufsize);
108}
109
110uptr internal_sched_yield() {
111  return sched_yield();
112}
113
114void internal__exit(int exitcode) {
115  _exit(exitcode);
116}
117
118uptr internal_getpid() {
119  return getpid();
120}
121
122int internal_sigaction(int signum, const void *act, void *oldact) {
123  return sigaction(signum,
124                   (struct sigaction *)act, (struct sigaction *)oldact);
125}
126
127int internal_fork() {
128  // TODO(glider): this may call user's pthread_atfork() handlers which is bad.
129  return fork();
130}
131
132uptr internal_rename(const char *oldpath, const char *newpath) {
133  return rename(oldpath, newpath);
134}
135
136uptr internal_ftruncate(fd_t fd, uptr size) {
137  return ftruncate(fd, size);
138}
139
140// ----------------- sanitizer_common.h
141bool FileExists(const char *filename) {
142  struct stat st;
143  if (stat(filename, &st))
144    return false;
145  // Sanity check: filename is a regular file.
146  return S_ISREG(st.st_mode);
147}
148
149uptr GetTid() {
150  return reinterpret_cast<uptr>(pthread_self());
151}
152
153void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
154                                uptr *stack_bottom) {
155  CHECK(stack_top);
156  CHECK(stack_bottom);
157  uptr stacksize = pthread_get_stacksize_np(pthread_self());
158  // pthread_get_stacksize_np() returns an incorrect stack size for the main
159  // thread on Mavericks. See
160  // https://code.google.com/p/address-sanitizer/issues/detail?id=261
161  if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization &&
162      stacksize == (1 << 19))  {
163    struct rlimit rl;
164    CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
165    // Most often rl.rlim_cur will be the desired 8M.
166    if (rl.rlim_cur < kMaxThreadStackSize) {
167      stacksize = rl.rlim_cur;
168    } else {
169      stacksize = kMaxThreadStackSize;
170    }
171  }
172  void *stackaddr = pthread_get_stackaddr_np(pthread_self());
173  *stack_top = (uptr)stackaddr;
174  *stack_bottom = *stack_top - stacksize;
175}
176
177const char *GetEnv(const char *name) {
178  char ***env_ptr = _NSGetEnviron();
179  if (!env_ptr) {
180    Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
181           "called after libSystem_initializer().\n");
182    CHECK(env_ptr);
183  }
184  char **environ = *env_ptr;
185  CHECK(environ);
186  uptr name_len = internal_strlen(name);
187  while (*environ != 0) {
188    uptr len = internal_strlen(*environ);
189    if (len > name_len) {
190      const char *p = *environ;
191      if (!internal_memcmp(p, name, name_len) &&
192          p[name_len] == '=') {  // Match.
193        return *environ + name_len + 1;  // String starting after =.
194      }
195    }
196    environ++;
197  }
198  return 0;
199}
200
201void ReExec() {
202  UNIMPLEMENTED();
203}
204
205void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
206  (void)args;
207  // Nothing here for now.
208}
209
210uptr GetPageSize() {
211  return sysconf(_SC_PAGESIZE);
212}
213
214BlockingMutex::BlockingMutex(LinkerInitialized) {
215  // We assume that OS_SPINLOCK_INIT is zero
216}
217
218BlockingMutex::BlockingMutex() {
219  internal_memset(this, 0, sizeof(*this));
220}
221
222void BlockingMutex::Lock() {
223  CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
224  CHECK_EQ(OS_SPINLOCK_INIT, 0);
225  CHECK_NE(owner_, (uptr)pthread_self());
226  OSSpinLockLock((OSSpinLock*)&opaque_storage_);
227  CHECK(!owner_);
228  owner_ = (uptr)pthread_self();
229}
230
231void BlockingMutex::Unlock() {
232  CHECK(owner_ == (uptr)pthread_self());
233  owner_ = 0;
234  OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
235}
236
237void BlockingMutex::CheckLocked() {
238  CHECK_EQ((uptr)pthread_self(), owner_);
239}
240
241u64 NanoTime() {
242  return 0;
243}
244
245uptr GetTlsSize() {
246  return 0;
247}
248
249void InitTlsSize() {
250}
251
252void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
253                          uptr *tls_addr, uptr *tls_size) {
254#ifndef SANITIZER_GO
255  uptr stack_top, stack_bottom;
256  GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
257  *stk_addr = stack_bottom;
258  *stk_size = stack_top - stack_bottom;
259  *tls_addr = 0;
260  *tls_size = 0;
261#else
262  *stk_addr = 0;
263  *stk_size = 0;
264  *tls_addr = 0;
265  *tls_size = 0;
266#endif
267}
268
269uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
270                      string_predicate_t filter) {
271  MemoryMappingLayout memory_mapping(false);
272  return memory_mapping.DumpListOfModules(modules, max_modules, filter);
273}
274
275bool IsDeadlySignal(int signum) {
276  return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv;
277}
278
279MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED;
280
281MacosVersion GetMacosVersionInternal() {
282  int mib[2] = { CTL_KERN, KERN_OSRELEASE };
283  char version[100];
284  uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
285  for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
286  // Get the version length.
287  CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1);
288  CHECK_LT(len, maxlen);
289  CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1);
290  switch (version[0]) {
291    case '9': return MACOS_VERSION_LEOPARD;
292    case '1': {
293      switch (version[1]) {
294        case '0': return MACOS_VERSION_SNOW_LEOPARD;
295        case '1': return MACOS_VERSION_LION;
296        case '2': return MACOS_VERSION_MOUNTAIN_LION;
297        case '3': return MACOS_VERSION_MAVERICKS;
298        case '4': return MACOS_VERSION_YOSEMITE;
299        default:
300          if (IsDigit(version[1]))
301            return MACOS_VERSION_UNKNOWN_NEWER;
302          else
303            return MACOS_VERSION_UNKNOWN;
304      }
305    }
306    default: return MACOS_VERSION_UNKNOWN;
307  }
308}
309
310MacosVersion GetMacosVersion() {
311  atomic_uint32_t *cache =
312      reinterpret_cast<atomic_uint32_t*>(&cached_macos_version);
313  MacosVersion result =
314      static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire));
315  if (result == MACOS_VERSION_UNINITIALIZED) {
316    result = GetMacosVersionInternal();
317    atomic_store(cache, result, memory_order_release);
318  }
319  return result;
320}
321
322}  // namespace __sanitizer
323
324#endif  // SANITIZER_MAC
325