| //===-- 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 |
| |
| 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); |
| } |
| |
| } // namespace __msan |
| |
| #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD |