337 lines · cpp
1//===-- msan_linux.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 MemorySanitizer.10//11// Linux-, NetBSD- and FreeBSD-specific code.12//===----------------------------------------------------------------------===//13 14#include "sanitizer_common/sanitizer_platform.h"15#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD16 17# include <elf.h>18# include <link.h>19# include <pthread.h>20# include <signal.h>21# include <stdio.h>22# include <stdlib.h>23# if SANITIZER_LINUX24# include <sys/personality.h>25# endif26# include <sys/resource.h>27# include <sys/time.h>28# include <unistd.h>29# include <unwind.h>30 31# include "msan.h"32# include "msan_allocator.h"33# include "msan_chained_origin_depot.h"34# include "msan_report.h"35# include "msan_thread.h"36# include "sanitizer_common/sanitizer_common.h"37# include "sanitizer_common/sanitizer_procmaps.h"38# include "sanitizer_common/sanitizer_stackdepot.h"39 40namespace __msan {41 42void ReportMapRange(const char *descr, uptr beg, uptr size) {43 if (size > 0) {44 uptr end = beg + size - 1;45 VPrintf(1, "%s : %p-%p\n", descr, (void *)beg, (void *)end);46 }47}48 49static bool CheckMemoryRangeAvailability(uptr beg, uptr size, bool verbose) {50 if (size > 0) {51 uptr end = beg + size - 1;52 if (!MemoryRangeIsAvailable(beg, end)) {53 if (verbose)54 Printf("FATAL: MemorySanitizer: Shadow range %p-%p is not available.\n",55 (void *)beg, (void *)end);56 return false;57 }58 }59 return true;60}61 62static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) {63 if (size > 0) {64 void *addr = MmapFixedNoAccess(beg, size, name);65 if (beg == 0 && addr) {66 // Depending on the kernel configuration, we may not be able to protect67 // the page at address zero.68 uptr gap = 16 * GetPageSizeCached();69 beg += gap;70 size -= gap;71 addr = MmapFixedNoAccess(beg, size, name);72 }73 if ((uptr)addr != beg) {74 uptr end = beg + size - 1;75 Printf(76 "FATAL: MemorySanitizer: Cannot protect memory range %p-%p (%s).\n",77 (void *)beg, (void *)end, name);78 return false;79 }80 }81 return true;82}83 84static void CheckMemoryLayoutSanity() {85 uptr prev_end = 0;86 for (unsigned i = 0; i < kMemoryLayoutSize; ++i) {87 uptr start = kMemoryLayout[i].start;88 uptr end = kMemoryLayout[i].end;89 MappingDesc::Type type = kMemoryLayout[i].type;90 CHECK_LT(start, end);91 CHECK_EQ(prev_end, start);92 CHECK(addr_is_type(start, type));93 CHECK(addr_is_type((start + end) / 2, type));94 CHECK(addr_is_type(end - 1, type));95 if (type == MappingDesc::APP || type == MappingDesc::ALLOCATOR) {96 uptr addr = start;97 CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));98 CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));99 CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr)));100 101 addr = (start + end) / 2;102 CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));103 CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));104 CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr)));105 106 addr = end - 1;107 CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));108 CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));109 CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr)));110 }111 prev_end = end;112 }113}114 115static bool InitShadow(bool init_origins, bool dry_run) {116 // Let user know mapping parameters first.117 VPrintf(1, "__msan_init %p\n", reinterpret_cast<void *>(&__msan_init));118 for (unsigned i = 0; i < kMemoryLayoutSize; ++i)119 VPrintf(1, "%s: %zx - %zx\n", kMemoryLayout[i].name, kMemoryLayout[i].start,120 kMemoryLayout[i].end - 1);121 122 CheckMemoryLayoutSanity();123 124 if (!MEM_IS_APP(&__msan_init)) {125 if (!dry_run)126 Printf("FATAL: Code %p is out of application range. Non-PIE build?\n",127 reinterpret_cast<void *>(&__msan_init));128 return false;129 }130 131 const uptr maxVirtualAddress = GetMaxUserVirtualAddress();132 133 for (unsigned i = 0; i < kMemoryLayoutSize; ++i) {134 uptr start = kMemoryLayout[i].start;135 uptr end = kMemoryLayout[i].end;136 uptr size = end - start;137 MappingDesc::Type type = kMemoryLayout[i].type;138 139 // Check if the segment should be mapped based on platform constraints.140 if (start >= maxVirtualAddress)141 continue;142 143 bool map = type == MappingDesc::SHADOW ||144 (init_origins && type == MappingDesc::ORIGIN);145 bool protect = type == MappingDesc::INVALID ||146 (!init_origins && type == MappingDesc::ORIGIN);147 CHECK(!(map && protect));148 if (!map && !protect) {149 CHECK(type == MappingDesc::APP || type == MappingDesc::ALLOCATOR);150 151 if (dry_run && type == MappingDesc::ALLOCATOR &&152 !CheckMemoryRangeAvailability(start, size, !dry_run))153 return false;154 }155 if (map) {156 if (dry_run && !CheckMemoryRangeAvailability(start, size, !dry_run))157 return false;158 if (!dry_run &&159 !MmapFixedSuperNoReserve(start, size, kMemoryLayout[i].name))160 return false;161 if (!dry_run && common_flags()->use_madv_dontdump)162 DontDumpShadowMemory(start, size);163 }164 if (protect) {165 if (dry_run && !CheckMemoryRangeAvailability(start, size, !dry_run))166 return false;167 if (!dry_run && !ProtectMemoryRange(start, size, kMemoryLayout[i].name))168 return false;169 }170 }171 172 return true;173}174 175bool InitShadowWithReExec(bool init_origins) {176 // Start with dry run: check layout is ok, but don't print warnings because177 // warning messages will cause tests to fail (even if we successfully re-exec178 // after the warning).179 bool success = InitShadow(init_origins, true);180 if (!success) {181# if SANITIZER_LINUX182 // Perhaps ASLR entropy is too high. If ASLR is enabled, re-exec without it.183 int old_personality = personality(0xffffffff);184 bool aslr_on =185 (old_personality != -1) && ((old_personality & ADDR_NO_RANDOMIZE) == 0);186 187 if (aslr_on) {188 VReport(1,189 "WARNING: MemorySanitizer: memory layout is incompatible, "190 "possibly due to high-entropy ASLR.\n"191 "Re-execing with fixed virtual address space.\n"192 "N.B. reducing ASLR entropy is preferable.\n");193 194 if (personality(old_personality | ADDR_NO_RANDOMIZE) == -1) {195 Printf(196 "FATAL: MemorySanitizer: unable to disable ASLR (perhaps "197 "sandboxing is enabled?).\n");198 Printf("FATAL: Please rerun without sandboxing and/or ASLR.\n");199 Die();200 }201 202 ReExec();203 }204# endif205 }206 207 // The earlier dry run didn't actually map or protect anything. Run again in208 // non-dry run mode.209 return success && InitShadow(init_origins, false);210}211 212static void MsanAtExit(void) {213 if (flags()->print_stats && (flags()->atexit || msan_report_count > 0))214 ReportStats();215 if (msan_report_count > 0) {216 ReportAtExitStatistics();217 if (common_flags()->exitcode)218 internal__exit(common_flags()->exitcode);219 }220}221 222void InstallAtExitHandler() {223 atexit(MsanAtExit);224}225 226// ---------------------- TSD ---------------- {{{1227 228#if SANITIZER_NETBSD229// Thread Static Data cannot be used in early init on NetBSD.230// Reuse the MSan TSD API for compatibility with existing code231// with an alternative implementation.232 233static void (*tsd_destructor)(void *tsd) = nullptr;234 235struct tsd_key {236 tsd_key() : key(nullptr) {}237 ~tsd_key() {238 CHECK(tsd_destructor);239 if (key)240 (*tsd_destructor)(key);241 }242 MsanThread *key;243};244 245static thread_local struct tsd_key key;246 247void MsanTSDInit(void (*destructor)(void *tsd)) {248 CHECK(!tsd_destructor);249 tsd_destructor = destructor;250}251 252MsanThread *GetCurrentThread() {253 CHECK(tsd_destructor);254 return key.key;255}256 257void SetCurrentThread(MsanThread *tsd) {258 CHECK(tsd_destructor);259 CHECK(tsd);260 CHECK(!key.key);261 key.key = tsd;262}263 264void MsanTSDDtor(void *tsd) {265 CHECK(tsd_destructor);266 CHECK_EQ(key.key, tsd);267 key.key = nullptr;268 // Make sure that signal handler can not see a stale current thread pointer.269 atomic_signal_fence(memory_order_seq_cst);270 MsanThread::TSDDtor(tsd);271}272#else273static pthread_key_t tsd_key;274static bool tsd_key_inited = false;275 276void MsanTSDInit(void (*destructor)(void *tsd)) {277 CHECK(!tsd_key_inited);278 tsd_key_inited = true;279 CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));280}281 282static THREADLOCAL MsanThread* msan_current_thread;283 284MsanThread *GetCurrentThread() {285 return msan_current_thread;286}287 288void SetCurrentThread(MsanThread *t) {289 // Make sure we do not reset the current MsanThread.290 CHECK_EQ(0, msan_current_thread);291 msan_current_thread = t;292 // Make sure that MsanTSDDtor gets called at the end.293 CHECK(tsd_key_inited);294 pthread_setspecific(tsd_key, (void *)t);295}296 297void MsanTSDDtor(void *tsd) {298 MsanThread *t = (MsanThread*)tsd;299 if (t->destructor_iterations_ > 1) {300 t->destructor_iterations_--;301 CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));302 return;303 }304 ScopedBlockSignals block(nullptr);305 msan_current_thread = nullptr;306 // Make sure that signal handler can not see a stale current thread pointer.307 atomic_signal_fence(memory_order_seq_cst);308 MsanThread::TSDDtor(tsd);309}310# endif311 312static void BeforeFork() {313 VReport(2, "BeforeFork tid: %llu\n", GetTid());314 // Usually we lock ThreadRegistry, but msan does not have one.315 LockAllocator();316 StackDepotLockBeforeFork();317 ChainedOriginDepotBeforeFork();318}319 320static void AfterFork(bool fork_child) {321 ChainedOriginDepotAfterFork(fork_child);322 StackDepotUnlockAfterFork(fork_child);323 UnlockAllocator();324 // Usually we unlock ThreadRegistry, but msan does not have one.325 VReport(2, "AfterFork tid: %llu\n", GetTid());326}327 328void InstallAtForkHandler() {329 pthread_atfork(330 &BeforeFork, []() { AfterFork(/* fork_child= */ false); },331 []() { AfterFork(/* fork_child= */ true); });332}333 334} // namespace __msan335 336#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD337