forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			264 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			264 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- msan_linux.cc -----------------------------------------------------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// This file is a part of MemorySanitizer.
 | 
						|
//
 | 
						|
// Linux-, NetBSD- and FreeBSD-specific code.
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "sanitizer_common/sanitizer_platform.h"
 | 
						|
#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
 | 
						|
 | 
						|
#include "msan.h"
 | 
						|
#include "msan_report.h"
 | 
						|
#include "msan_thread.h"
 | 
						|
 | 
						|
#include <elf.h>
 | 
						|
#include <link.h>
 | 
						|
#include <pthread.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <unwind.h>
 | 
						|
#include <execinfo.h>
 | 
						|
#include <sys/time.h>
 | 
						|
#include <sys/resource.h>
 | 
						|
 | 
						|
#include "sanitizer_common/sanitizer_common.h"
 | 
						|
#include "sanitizer_common/sanitizer_procmaps.h"
 | 
						|
 | 
						|
namespace __msan {
 | 
						|
 | 
						|
void ReportMapRange(const char *descr, uptr beg, uptr size) {
 | 
						|
  if (size > 0) {
 | 
						|
    uptr end = beg + size - 1;
 | 
						|
    VPrintf(1, "%s : %p - %p\n", descr, beg, end);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static bool CheckMemoryRangeAvailability(uptr beg, uptr size) {
 | 
						|
  if (size > 0) {
 | 
						|
    uptr end = beg + size - 1;
 | 
						|
    if (!MemoryRangeIsAvailable(beg, end)) {
 | 
						|
      Printf("FATAL: Memory range %p - %p is not available.\n", beg, end);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) {
 | 
						|
  if (size > 0) {
 | 
						|
    void *addr = MmapFixedNoAccess(beg, size, name);
 | 
						|
    if (beg == 0 && addr) {
 | 
						|
      // Depending on the kernel configuration, we may not be able to protect
 | 
						|
      // the page at address zero.
 | 
						|
      uptr gap = 16 * GetPageSizeCached();
 | 
						|
      beg += gap;
 | 
						|
      size -= gap;
 | 
						|
      addr = MmapFixedNoAccess(beg, size, name);
 | 
						|
    }
 | 
						|
    if ((uptr)addr != beg) {
 | 
						|
      uptr end = beg + size - 1;
 | 
						|
      Printf("FATAL: Cannot protect memory range %p - %p (%s).\n", beg, end,
 | 
						|
             name);
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static void CheckMemoryLayoutSanity() {
 | 
						|
  uptr prev_end = 0;
 | 
						|
  for (unsigned i = 0; i < kMemoryLayoutSize; ++i) {
 | 
						|
    uptr start = kMemoryLayout[i].start;
 | 
						|
    uptr end = kMemoryLayout[i].end;
 | 
						|
    MappingDesc::Type type = kMemoryLayout[i].type;
 | 
						|
    CHECK_LT(start, end);
 | 
						|
    CHECK_EQ(prev_end, start);
 | 
						|
    CHECK(addr_is_type(start, type));
 | 
						|
    CHECK(addr_is_type((start + end) / 2, type));
 | 
						|
    CHECK(addr_is_type(end - 1, type));
 | 
						|
    if (type == MappingDesc::APP) {
 | 
						|
      uptr addr = start;
 | 
						|
      CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));
 | 
						|
      CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));
 | 
						|
      CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr)));
 | 
						|
 | 
						|
      addr = (start + end) / 2;
 | 
						|
      CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));
 | 
						|
      CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));
 | 
						|
      CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr)));
 | 
						|
 | 
						|
      addr = end - 1;
 | 
						|
      CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));
 | 
						|
      CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));
 | 
						|
      CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr)));
 | 
						|
    }
 | 
						|
    prev_end = end;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool InitShadow(bool init_origins) {
 | 
						|
  // Let user know mapping parameters first.
 | 
						|
  VPrintf(1, "__msan_init %p\n", &__msan_init);
 | 
						|
  for (unsigned i = 0; i < kMemoryLayoutSize; ++i)
 | 
						|
    VPrintf(1, "%s: %zx - %zx\n", kMemoryLayout[i].name, kMemoryLayout[i].start,
 | 
						|
            kMemoryLayout[i].end - 1);
 | 
						|
 | 
						|
  CheckMemoryLayoutSanity();
 | 
						|
 | 
						|
  if (!MEM_IS_APP(&__msan_init)) {
 | 
						|
    Printf("FATAL: Code %p is out of application range. Non-PIE build?\n",
 | 
						|
           (uptr)&__msan_init);
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  const uptr maxVirtualAddress = GetMaxUserVirtualAddress();
 | 
						|
 | 
						|
  for (unsigned i = 0; i < kMemoryLayoutSize; ++i) {
 | 
						|
    uptr start = kMemoryLayout[i].start;
 | 
						|
    uptr end = kMemoryLayout[i].end;
 | 
						|
    uptr size= end - start;
 | 
						|
    MappingDesc::Type type = kMemoryLayout[i].type;
 | 
						|
 | 
						|
    // Check if the segment should be mapped based on platform constraints.
 | 
						|
    if (start >= maxVirtualAddress)
 | 
						|
      continue;
 | 
						|
 | 
						|
    bool map = type == MappingDesc::SHADOW ||
 | 
						|
               (init_origins && type == MappingDesc::ORIGIN);
 | 
						|
    bool protect = type == MappingDesc::INVALID ||
 | 
						|
                   (!init_origins && type == MappingDesc::ORIGIN);
 | 
						|
    CHECK(!(map && protect));
 | 
						|
    if (!map && !protect)
 | 
						|
      CHECK(type == MappingDesc::APP);
 | 
						|
    if (map) {
 | 
						|
      if (!CheckMemoryRangeAvailability(start, size))
 | 
						|
        return false;
 | 
						|
      if (!MmapFixedNoReserve(start, size, kMemoryLayout[i].name))
 | 
						|
        return false;
 | 
						|
      if (common_flags()->use_madv_dontdump)
 | 
						|
        DontDumpShadowMemory(start, size);
 | 
						|
    }
 | 
						|
    if (protect) {
 | 
						|
      if (!CheckMemoryRangeAvailability(start, size))
 | 
						|
        return false;
 | 
						|
      if (!ProtectMemoryRange(start, size, kMemoryLayout[i].name))
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static void MsanAtExit(void) {
 | 
						|
  if (flags()->print_stats && (flags()->atexit || msan_report_count > 0))
 | 
						|
    ReportStats();
 | 
						|
  if (msan_report_count > 0) {
 | 
						|
    ReportAtExitStatistics();
 | 
						|
    if (common_flags()->exitcode)
 | 
						|
      internal__exit(common_flags()->exitcode);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void InstallAtExitHandler() {
 | 
						|
  atexit(MsanAtExit);
 | 
						|
}
 | 
						|
 | 
						|
// ---------------------- TSD ---------------- {{{1
 | 
						|
 | 
						|
#if SANITIZER_NETBSD || SANITIZER_FREEBSD
 | 
						|
// Thread Static Data cannot be used in early init on NetBSD and FreeBSD.
 | 
						|
// Reuse the MSan TSD API for compatibility with existing code
 | 
						|
// with an alternative implementation.
 | 
						|
 | 
						|
static void (*tsd_destructor)(void *tsd) = nullptr;
 | 
						|
 | 
						|
struct tsd_key {
 | 
						|
  tsd_key() : key(nullptr) {}
 | 
						|
  ~tsd_key() {
 | 
						|
    CHECK(tsd_destructor);
 | 
						|
    if (key)
 | 
						|
      (*tsd_destructor)(key);
 | 
						|
  }
 | 
						|
  MsanThread *key;
 | 
						|
};
 | 
						|
 | 
						|
static thread_local struct tsd_key key;
 | 
						|
 | 
						|
void MsanTSDInit(void (*destructor)(void *tsd)) {
 | 
						|
  CHECK(!tsd_destructor);
 | 
						|
  tsd_destructor = destructor;
 | 
						|
}
 | 
						|
 | 
						|
MsanThread *GetCurrentThread() {
 | 
						|
  CHECK(tsd_destructor);
 | 
						|
  return key.key;
 | 
						|
}
 | 
						|
 | 
						|
void SetCurrentThread(MsanThread *tsd) {
 | 
						|
  CHECK(tsd_destructor);
 | 
						|
  CHECK(tsd);
 | 
						|
  CHECK(!key.key);
 | 
						|
  key.key = tsd;
 | 
						|
}
 | 
						|
 | 
						|
void MsanTSDDtor(void *tsd) {
 | 
						|
  CHECK(tsd_destructor);
 | 
						|
  CHECK_EQ(key.key, tsd);
 | 
						|
  key.key = nullptr;
 | 
						|
  // Make sure that signal handler can not see a stale current thread pointer.
 | 
						|
  atomic_signal_fence(memory_order_seq_cst);
 | 
						|
  MsanThread::TSDDtor(tsd);
 | 
						|
}
 | 
						|
#else
 | 
						|
static pthread_key_t tsd_key;
 | 
						|
static bool tsd_key_inited = false;
 | 
						|
 | 
						|
void MsanTSDInit(void (*destructor)(void *tsd)) {
 | 
						|
  CHECK(!tsd_key_inited);
 | 
						|
  tsd_key_inited = true;
 | 
						|
  CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
 | 
						|
}
 | 
						|
 | 
						|
static THREADLOCAL MsanThread* msan_current_thread;
 | 
						|
 | 
						|
MsanThread *GetCurrentThread() {
 | 
						|
  return msan_current_thread;
 | 
						|
}
 | 
						|
 | 
						|
void SetCurrentThread(MsanThread *t) {
 | 
						|
  // Make sure we do not reset the current MsanThread.
 | 
						|
  CHECK_EQ(0, msan_current_thread);
 | 
						|
  msan_current_thread = t;
 | 
						|
  // Make sure that MsanTSDDtor gets called at the end.
 | 
						|
  CHECK(tsd_key_inited);
 | 
						|
  pthread_setspecific(tsd_key, (void *)t);
 | 
						|
}
 | 
						|
 | 
						|
void MsanTSDDtor(void *tsd) {
 | 
						|
  MsanThread *t = (MsanThread*)tsd;
 | 
						|
  if (t->destructor_iterations_ > 1) {
 | 
						|
    t->destructor_iterations_--;
 | 
						|
    CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  msan_current_thread = nullptr;
 | 
						|
  // Make sure that signal handler can not see a stale current thread pointer.
 | 
						|
  atomic_signal_fence(memory_order_seq_cst);
 | 
						|
  MsanThread::TSDDtor(tsd);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
} // namespace __msan
 | 
						|
 | 
						|
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
 |