brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.0 KiB · da735fb Raw
321 lines · cpp
1//===-- tsan_platform_mac.cpp ---------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of ThreadSanitizer (TSan), a race detector.10//11// Mac-specific code.12//===----------------------------------------------------------------------===//13 14#include "sanitizer_common/sanitizer_platform.h"15#if SANITIZER_APPLE16 17#include "sanitizer_common/sanitizer_atomic.h"18#include "sanitizer_common/sanitizer_common.h"19#include "sanitizer_common/sanitizer_libc.h"20#include "sanitizer_common/sanitizer_posix.h"21#include "sanitizer_common/sanitizer_procmaps.h"22#include "sanitizer_common/sanitizer_ptrauth.h"23#include "sanitizer_common/sanitizer_stackdepot.h"24#include "tsan_platform.h"25#include "tsan_rtl.h"26#include "tsan_flags.h"27 28#include <limits.h>29#include <mach/mach.h>30#include <pthread.h>31#include <signal.h>32#include <stdio.h>33#include <stdlib.h>34#include <string.h>35#include <stdarg.h>36#include <sys/mman.h>37#include <sys/syscall.h>38#include <sys/time.h>39#include <sys/types.h>40#include <sys/resource.h>41#include <sys/stat.h>42#include <unistd.h>43#include <errno.h>44#include <sched.h>45 46namespace __tsan {47 48#if !SANITIZER_GO49alignas(SANITIZER_CACHE_LINE_SIZE) static char main_thread_state[sizeof(50    ThreadState)];51static ThreadState *dead_thread_state;52static pthread_key_t thread_state_key;53 54// We rely on the following documented, but Darwin-specific behavior to keep the55// reference to the ThreadState object alive in TLS:56// pthread_key_create man page:57//   If, after all the destructors have been called for all non-NULL values with58//   associated destructors, there are still some non-NULL values with59//   associated destructors, then the process is repeated.  If, after at least60//   [PTHREAD_DESTRUCTOR_ITERATIONS] iterations of destructor calls for61//   outstanding non-NULL values, there are still some non-NULL values with62//   associated destructors, the implementation stops calling destructors.63static_assert(PTHREAD_DESTRUCTOR_ITERATIONS == 4, "Small number of iterations");64static void ThreadStateDestructor(void *thr) {65  int res = pthread_setspecific(thread_state_key, thr);66  CHECK_EQ(res, 0);67}68 69static void InitializeThreadStateStorage() {70  int res;71  CHECK_EQ(thread_state_key, 0);72  res = pthread_key_create(&thread_state_key, ThreadStateDestructor);73  CHECK_EQ(res, 0);74  res = pthread_setspecific(thread_state_key, main_thread_state);75  CHECK_EQ(res, 0);76 77  auto dts = (ThreadState *)MmapOrDie(sizeof(ThreadState), "ThreadState");78  dts->fast_state.SetIgnoreBit();79  dts->ignore_interceptors = 1;80  dts->is_dead = true;81  const_cast<Tid &>(dts->tid) = kInvalidTid;82  res = internal_mprotect(dts, sizeof(ThreadState), PROT_READ);  // immutable83  CHECK_EQ(res, 0);84  dead_thread_state = dts;85}86 87ThreadState *cur_thread() {88  // Some interceptors get called before libpthread has been initialized and in89  // these cases we must avoid calling any pthread APIs.90  if (UNLIKELY(!thread_state_key)) {91    return (ThreadState *)main_thread_state;92  }93 94  // We only reach this line after InitializeThreadStateStorage() ran, i.e,95  // after TSan (and therefore libpthread) have been initialized.96  ThreadState *thr = (ThreadState *)pthread_getspecific(thread_state_key);97  if (UNLIKELY(!thr)) {98    thr = (ThreadState *)MmapOrDie(sizeof(ThreadState), "ThreadState");99    int res = pthread_setspecific(thread_state_key, thr);100    CHECK_EQ(res, 0);101  }102  return thr;103}104 105void set_cur_thread(ThreadState *thr) {106  int res = pthread_setspecific(thread_state_key, thr);107  CHECK_EQ(res, 0);108}109 110void cur_thread_finalize() {111  ThreadState *thr = (ThreadState *)pthread_getspecific(thread_state_key);112  CHECK(thr);113  if (thr == (ThreadState *)main_thread_state) {114    // Calling dispatch_main() or xpc_main() actually invokes pthread_exit to115    // exit the main thread. Let's keep the main thread's ThreadState.116    return;117  }118  // Intercepted functions can still get called after cur_thread_finalize()119  // (called from DestroyThreadState()), so put a fake thread state for "dead"120  // threads.  An alternative solution would be to release the ThreadState121  // object from THREAD_DESTROY (which is delivered later and on the parent122  // thread) instead of THREAD_TERMINATE.123  int res = pthread_setspecific(thread_state_key, dead_thread_state);124  CHECK_EQ(res, 0);125  UnmapOrDie(thr, sizeof(ThreadState));126}127#endif128 129static void RegionMemUsage(uptr start, uptr end, uptr *res, uptr *dirty) {130  vm_address_t address = start;131  vm_address_t end_address = end;132  uptr resident_pages = 0;133  uptr dirty_pages = 0;134  while (address < end_address) {135    vm_size_t vm_region_size;136    mach_msg_type_number_t count = VM_REGION_EXTENDED_INFO_COUNT;137    vm_region_extended_info_data_t vm_region_info;138    mach_port_t object_name;139    kern_return_t ret = vm_region_64(140        mach_task_self(), &address, &vm_region_size, VM_REGION_EXTENDED_INFO,141        (vm_region_info_t)&vm_region_info, &count, &object_name);142    if (ret != KERN_SUCCESS) break;143 144    resident_pages += vm_region_info.pages_resident;145    dirty_pages += vm_region_info.pages_dirtied;146 147    address += vm_region_size;148  }149  *res = resident_pages * GetPageSizeCached();150  *dirty = dirty_pages * GetPageSizeCached();151}152 153void WriteMemoryProfile(char *buf, uptr buf_size, u64 uptime_ns) {154  uptr shadow_res, shadow_dirty;155  uptr meta_res, meta_dirty;156  RegionMemUsage(ShadowBeg(), ShadowEnd(), &shadow_res, &shadow_dirty);157  RegionMemUsage(MetaShadowBeg(), MetaShadowEnd(), &meta_res, &meta_dirty);158 159#  if !SANITIZER_GO160  uptr low_res, low_dirty;161  uptr high_res, high_dirty;162  uptr heap_res, heap_dirty;163  RegionMemUsage(LoAppMemBeg(), LoAppMemEnd(), &low_res, &low_dirty);164  RegionMemUsage(HiAppMemBeg(), HiAppMemEnd(), &high_res, &high_dirty);165  RegionMemUsage(HeapMemBeg(), HeapMemEnd(), &heap_res, &heap_dirty);166#else  // !SANITIZER_GO167  uptr app_res, app_dirty;168  RegionMemUsage(LoAppMemBeg(), LoAppMemEnd(), &app_res, &app_dirty);169#endif170 171  StackDepotStats stacks = StackDepotGetStats();172  uptr nthread, nlive;173  ctx->thread_registry.GetNumberOfThreads(&nthread, &nlive);174  internal_snprintf(175      buf, buf_size,176      "shadow   (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"177      "meta     (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"178#  if !SANITIZER_GO179      "low app  (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"180      "high app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"181      "heap     (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"182#  else  // !SANITIZER_GO183      "app      (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"184#  endif185      "stacks: %zd unique IDs, %zd kB allocated\n"186      "threads: %zd total, %zd live\n"187      "------------------------------\n",188      ShadowBeg(), ShadowEnd(), shadow_res / 1024, shadow_dirty / 1024,189      MetaShadowBeg(), MetaShadowEnd(), meta_res / 1024, meta_dirty / 1024,190#  if !SANITIZER_GO191      LoAppMemBeg(), LoAppMemEnd(), low_res / 1024, low_dirty / 1024,192      HiAppMemBeg(), HiAppMemEnd(), high_res / 1024, high_dirty / 1024,193      HeapMemBeg(), HeapMemEnd(), heap_res / 1024, heap_dirty / 1024,194#  else  // !SANITIZER_GO195      LoAppMemBeg(), LoAppMemEnd(), app_res / 1024, app_dirty / 1024,196#  endif197      stacks.n_uniq_ids, stacks.allocated / 1024, nthread, nlive);198}199 200#  if !SANITIZER_GO201void InitializeShadowMemoryPlatform() { }202 203// Register GCD worker threads, which are created without an observable call to204// pthread_create().205static void ThreadCreateCallback(uptr thread, bool gcd_worker) {206  if (gcd_worker) {207    ThreadState *thr = cur_thread();208    Processor *proc = ProcCreate();209    ProcWire(proc, thr);210    ThreadState *parent_thread_state = nullptr;  // No parent.211    Tid tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true);212    CHECK_NE(tid, kMainTid);213    ThreadStart(thr, tid, GetTid(), ThreadType::Worker);214  }215}216 217// Destroy thread state for *all* threads.218static void ThreadTerminateCallback(uptr thread) {219  ThreadState *thr = cur_thread();220  if (thr->tctx) {221    DestroyThreadState();222  }223}224#endif225 226void InitializePlatformEarly() {227#  if !SANITIZER_GO && SANITIZER_IOS228  uptr max_vm = GetMaxUserVirtualAddress() + 1;229  if (max_vm < HiAppMemEnd()) {230    Report(231        "ThreadSanitizer: Unsupported virtual memory layout:\n\tVM address "232        "limit = %p\n\tExpected %p.\n",233        (void*)max_vm, (void*)HiAppMemEnd());234    Die();235  }236  // In some configurations, the max_vm is expanded, but much of this space is237  // already mapped. TSAN will not work in this configuration.238  if (!MemoryRangeIsAvailable(HiAppMemEnd() - 1, HiAppMemEnd() - 1)) {239    Report(240        "ThreadSanitizer: Unsupported virtual memory layout: Address %p is "241        "already mapped.\n",242        (void*)(HiAppMemEnd() - 1));243    Die();244  }245#endif246}247 248static uptr longjmp_xor_key = 0;249 250void InitializePlatform() {251  DisableCoreDumperIfNecessary();252#if !SANITIZER_GO253  if (!CheckAndProtect(true, true, true)) {254    Printf("FATAL: ThreadSanitizer: found incompatible memory layout.\n");255    Die();256  }257 258  InitializeThreadStateStorage();259 260  ThreadEventCallbacks callbacks = {261      .create = ThreadCreateCallback,262      .start = nullptr,263      .terminate = ThreadTerminateCallback,264      .destroy = nullptr,265  };266  InstallPthreadIntrospectionHook(callbacks);267#endif268 269  if (GetMacosAlignedVersion() >= MacosVersion(10, 14)) {270    // Libsystem currently uses a process-global key; this might change.271    const unsigned kTLSLongjmpXorKeySlot = 0x7;272    longjmp_xor_key = (uptr)pthread_getspecific(kTLSLongjmpXorKeySlot);273  }274}275 276#ifdef __aarch64__277# define LONG_JMP_SP_ENV_SLOT \278    ((GetMacosAlignedVersion() >= MacosVersion(10, 14)) ? 12 : 13)279#else280# define LONG_JMP_SP_ENV_SLOT 2281#endif282 283uptr ExtractLongJmpSp(uptr *env) {284  uptr mangled_sp = env[LONG_JMP_SP_ENV_SLOT];285  uptr sp = mangled_sp ^ longjmp_xor_key;286  sp = (uptr)ptrauth_auth_data((void *)sp, ptrauth_key_asdb,287                               ptrauth_string_discriminator("sp"));288  return sp;289}290 291#if !SANITIZER_GO292extern "C" void __tsan_tls_initialization() {}293 294void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {295  const uptr pc = StackTrace::GetNextInstructionPc(296      reinterpret_cast<uptr>(__tsan_tls_initialization));297  // Unlike Linux, we only store a pointer to the ThreadState object in TLS;298  // just mark the entire range as written to.299  MemoryRangeImitateWrite(thr, pc, tls_addr, tls_size);300}301#endif302 303#if !SANITIZER_GO304// Note: this function runs with async signals enabled,305// so it must not touch any tsan state.306int call_pthread_cancel_with_cleanup(int (*fn)(void *arg),307                                     void (*cleanup)(void *arg), void *arg) {308  // pthread_cleanup_push/pop are hardcore macros mess.309  // We can't intercept nor call them w/o including pthread.h.310  int res;311  pthread_cleanup_push(cleanup, arg);312  res = fn(arg);313  pthread_cleanup_pop(0);314  return res;315}316#endif317 318}  // namespace __tsan319 320#endif  // SANITIZER_APPLE321