diff options
Diffstat (limited to 'lib/hwasan/hwasan_linux.cc')
-rw-r--r-- | lib/hwasan/hwasan_linux.cc | 294 |
1 files changed, 209 insertions, 85 deletions
diff --git a/lib/hwasan/hwasan_linux.cc b/lib/hwasan/hwasan_linux.cc index 48dea8eb6ff4..5ab98dca594d 100644 --- a/lib/hwasan/hwasan_linux.cc +++ b/lib/hwasan/hwasan_linux.cc @@ -1,4 +1,4 @@ -//===-- hwasan_linux.cc -----------------------------------------------------===// +//===-- hwasan_linux.cc -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -6,41 +6,45 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// This file is a part of HWAddressSanitizer. -// -// Linux-, NetBSD- and FreeBSD-specific code. +/// +/// \file +/// This file is a part of HWAddressSanitizer and contains Linux-, NetBSD- and +/// FreeBSD-specific code. +/// //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD #include "hwasan.h" +#include "hwasan_dynamic_shadow.h" +#include "hwasan_interface_internal.h" +#include "hwasan_mapping.h" +#include "hwasan_report.h" #include "hwasan_thread.h" #include <elf.h> #include <link.h> #include <pthread.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> -#include <signal.h> +#include <sys/resource.h> +#include <sys/time.h> #include <unistd.h> #include <unwind.h> -#include <sys/time.h> -#include <sys/resource.h> #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_procmaps.h" namespace __hwasan { -void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) { +static void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) { CHECK_EQ((beg % GetMmapGranularity()), 0); CHECK_EQ(((end + 1) % GetMmapGranularity()), 0); uptr size = end - beg + 1; DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb. - void *res = MmapFixedNoReserve(beg, size, name); - if (res != (void *)beg) { + if (!MmapFixedNoReserve(beg, size, name)) { Report( "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " "Perhaps you're using ulimit -v\n", @@ -52,8 +56,11 @@ void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) { } static void ProtectGap(uptr addr, uptr size) { + if (!size) + return; void *res = MmapFixedNoAccess(addr, size, "shadow gap"); - if (addr == (uptr)res) return; + if (addr == (uptr)res) + return; // A few pages at the start of the address space can not be protected. // But we really want to protect as much as possible, to prevent this memory // being returned as a result of a non-FIXED mmap(). @@ -63,63 +70,160 @@ static void ProtectGap(uptr addr, uptr size) { addr += step; size -= step; void *res = MmapFixedNoAccess(addr, size, "shadow gap"); - if (addr == (uptr)res) return; + if (addr == (uptr)res) + return; } } Report( - "ERROR: Failed to protect the shadow gap. " - "ASan cannot proceed correctly. ABORTING.\n"); + "ERROR: Failed to protect shadow gap [%p, %p]. " + "HWASan cannot proceed correctly. ABORTING.\n", (void *)addr, + (void *)(addr + size)); DumpProcessMap(); Die(); } -bool InitShadow() { - const uptr maxVirtualAddress = GetMaxUserVirtualAddress(); +static uptr kLowMemStart; +static uptr kLowMemEnd; +static uptr kLowShadowEnd; +static uptr kLowShadowStart; +static uptr kHighShadowStart; +static uptr kHighShadowEnd; +static uptr kHighMemStart; +static uptr kHighMemEnd; + +static void PrintRange(uptr start, uptr end, const char *name) { + Printf("|| [%p, %p] || %.*s ||\n", (void *)start, (void *)end, 10, name); +} - // LowMem covers as much of the first 4GB as possible. - const uptr kLowMemEnd = 1UL<<32; - const uptr kLowShadowEnd = kLowMemEnd >> kShadowScale; - const uptr kLowShadowStart = kLowShadowEnd >> kShadowScale; +static void PrintAddressSpaceLayout() { + PrintRange(kHighMemStart, kHighMemEnd, "HighMem"); + if (kHighShadowEnd + 1 < kHighMemStart) + PrintRange(kHighShadowEnd + 1, kHighMemStart - 1, "ShadowGap"); + else + CHECK_EQ(kHighShadowEnd + 1, kHighMemStart); + PrintRange(kHighShadowStart, kHighShadowEnd, "HighShadow"); + if (SHADOW_OFFSET) { + if (kLowShadowEnd + 1 < kHighShadowStart) + PrintRange(kLowShadowEnd + 1, kHighShadowStart - 1, "ShadowGap"); + else + CHECK_EQ(kLowMemEnd + 1, kHighShadowStart); + PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow"); + if (kLowMemEnd + 1 < kLowShadowStart) + PrintRange(kLowMemEnd + 1, kLowShadowStart - 1, "ShadowGap"); + else + CHECK_EQ(kLowMemEnd + 1, kLowShadowStart); + PrintRange(kLowMemStart, kLowMemEnd, "LowMem"); + CHECK_EQ(0, kLowMemStart); + } else { + if (kLowMemEnd + 1 < kHighShadowStart) + PrintRange(kLowMemEnd + 1, kHighShadowStart - 1, "ShadowGap"); + else + CHECK_EQ(kLowMemEnd + 1, kHighShadowStart); + PrintRange(kLowMemStart, kLowMemEnd, "LowMem"); + CHECK_EQ(kLowShadowEnd + 1, kLowMemStart); + PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow"); + PrintRange(0, kLowShadowStart - 1, "ShadowGap"); + } +} +static uptr GetHighMemEnd() { // HighMem covers the upper part of the address space. - const uptr kHighShadowEnd = (maxVirtualAddress >> kShadowScale) + 1; - const uptr kHighShadowStart = Max(kLowMemEnd, kHighShadowEnd >> kShadowScale); - CHECK(kHighShadowStart < kHighShadowEnd); - - const uptr kHighMemStart = kHighShadowStart << kShadowScale; - CHECK(kHighShadowEnd <= kHighMemStart); - - if (Verbosity()) { - Printf("|| `[%p, %p]` || HighMem ||\n", (void *)kHighMemStart, - (void *)maxVirtualAddress); - if (kHighMemStart > kHighShadowEnd) - Printf("|| `[%p, %p]` || ShadowGap2 ||\n", (void *)kHighShadowEnd, - (void *)kHighMemStart); - Printf("|| `[%p, %p]` || HighShadow ||\n", (void *)kHighShadowStart, - (void *)kHighShadowEnd); - if (kHighShadowStart > kLowMemEnd) - Printf("|| `[%p, %p]` || ShadowGap2 ||\n", (void *)kHighShadowEnd, - (void *)kHighMemStart); - Printf("|| `[%p, %p]` || LowMem ||\n", (void *)kLowShadowEnd, - (void *)kLowMemEnd); - Printf("|| `[%p, %p]` || LowShadow ||\n", (void *)kLowShadowStart, - (void *)kLowShadowEnd); - Printf("|| `[%p, %p]` || ShadowGap1 ||\n", (void *)0, - (void *)kLowShadowStart); + uptr max_address = GetMaxUserVirtualAddress(); + if (SHADOW_OFFSET) + // Adjust max address to make sure that kHighMemEnd and kHighMemStart are + // properly aligned: + max_address |= SHADOW_GRANULARITY * GetMmapGranularity() - 1; + return max_address; +} + +static void InitializeShadowBaseAddress(uptr shadow_size_bytes) { + // Set the shadow memory address to uninitialized. + __hwasan_shadow_memory_dynamic_address = kDefaultShadowSentinel; + uptr shadow_start = SHADOW_OFFSET; + // Detect if a dynamic shadow address must be used and find the available + // location when necessary. When dynamic address is used, the macro + // kLowShadowBeg expands to __hwasan_shadow_memory_dynamic_address which + // was just set to kDefaultShadowSentinel. + if (shadow_start == kDefaultShadowSentinel) { + __hwasan_shadow_memory_dynamic_address = 0; + CHECK_EQ(0, SHADOW_OFFSET); + shadow_start = FindDynamicShadowStart(shadow_size_bytes); } + // Update the shadow memory address (potentially) used by instrumentation. + __hwasan_shadow_memory_dynamic_address = shadow_start; +} - ReserveShadowMemoryRange(kLowShadowStart, kLowShadowEnd - 1, "low shadow"); - ReserveShadowMemoryRange(kHighShadowStart, kHighShadowEnd - 1, "high shadow"); - ProtectGap(0, kLowShadowStart); - if (kHighShadowStart > kLowMemEnd) - ProtectGap(kLowMemEnd, kHighShadowStart - kLowMemEnd); - if (kHighMemStart > kHighShadowEnd) - ProtectGap(kHighShadowEnd, kHighMemStart - kHighShadowEnd); +bool InitShadow() { + // Define the entire memory range. + kHighMemEnd = GetHighMemEnd(); + + // Determine shadow memory base offset. + InitializeShadowBaseAddress(MEM_TO_SHADOW_SIZE(kHighMemEnd)); + + // Place the low memory first. + if (SHADOW_OFFSET) { + kLowMemEnd = SHADOW_OFFSET - 1; + kLowMemStart = 0; + } else { + // LowMem covers as much of the first 4GB as possible. + kLowMemEnd = (1UL << 32) - 1; + kLowMemStart = MEM_TO_SHADOW(kLowMemEnd) + 1; + } + + // Define the low shadow based on the already placed low memory. + kLowShadowEnd = MEM_TO_SHADOW(kLowMemEnd); + kLowShadowStart = SHADOW_OFFSET ? SHADOW_OFFSET : MEM_TO_SHADOW(kLowMemStart); + + // High shadow takes whatever memory is left up there (making sure it is not + // interfering with low memory in the fixed case). + kHighShadowEnd = MEM_TO_SHADOW(kHighMemEnd); + kHighShadowStart = Max(kLowMemEnd, MEM_TO_SHADOW(kHighShadowEnd)) + 1; + + // High memory starts where allocated shadow allows. + kHighMemStart = SHADOW_TO_MEM(kHighShadowStart); + + // Check the sanity of the defined memory ranges (there might be gaps). + CHECK_EQ(kHighMemStart % GetMmapGranularity(), 0); + CHECK_GT(kHighMemStart, kHighShadowEnd); + CHECK_GT(kHighShadowEnd, kHighShadowStart); + CHECK_GT(kHighShadowStart, kLowMemEnd); + CHECK_GT(kLowMemEnd, kLowMemStart); + CHECK_GT(kLowShadowEnd, kLowShadowStart); + if (SHADOW_OFFSET) + CHECK_GT(kLowShadowStart, kLowMemEnd); + else + CHECK_GT(kLowMemEnd, kLowShadowStart); + + if (Verbosity()) + PrintAddressSpaceLayout(); + + // Reserve shadow memory. + ReserveShadowMemoryRange(kLowShadowStart, kLowShadowEnd, "low shadow"); + ReserveShadowMemoryRange(kHighShadowStart, kHighShadowEnd, "high shadow"); + + // Protect all the gaps. + ProtectGap(0, Min(kLowMemStart, kLowShadowStart)); + if (SHADOW_OFFSET) { + if (kLowMemEnd + 1 < kLowShadowStart) + ProtectGap(kLowMemEnd + 1, kLowShadowStart - kLowMemEnd - 1); + if (kLowShadowEnd + 1 < kHighShadowStart) + ProtectGap(kLowShadowEnd + 1, kHighShadowStart - kLowShadowEnd - 1); + } else { + if (kLowMemEnd + 1 < kHighShadowStart) + ProtectGap(kLowMemEnd + 1, kHighShadowStart - kLowMemEnd - 1); + } + if (kHighShadowEnd + 1 < kHighMemStart) + ProtectGap(kHighShadowEnd + 1, kHighMemStart - kHighShadowEnd - 1); return true; } +bool MemIsApp(uptr p) { + CHECK(GetTagFromPointer(p) == 0); + return p >= kHighMemStart || (p >= kLowMemStart && p <= kLowMemEnd); +} + static void HwasanAtExit(void) { if (flags()->print_stats && (flags()->atexit || hwasan_report_count > 0)) ReportStats(); @@ -177,50 +281,65 @@ struct AccessInfo { bool recover; }; -#if defined(__aarch64__) static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) { - // Access type is encoded in HLT immediate as 0x1XY, - // where X&1 is 1 for store, 0 for load, - // and X&2 is 1 if the error is recoverable. - // Valid values of Y are 0 to 4, which are interpreted as log2(access_size), - // and 0xF, which means that access size is stored in X1 register. - // Access address is always in X0 register. - AccessInfo ai; + // Access type is passed in a platform dependent way (see below) and encoded + // as 0xXY, where X&1 is 1 for store, 0 for load, and X&2 is 1 if the error is + // recoverable. Valid values of Y are 0 to 4, which are interpreted as + // log2(access_size), and 0xF, which means that access size is passed via + // platform dependent register (see below). +#if defined(__aarch64__) + // Access type is encoded in BRK immediate as 0x900 + 0xXY. For Y == 0xF, + // access size is stored in X1 register. Access address is always in X0 + // register. uptr pc = (uptr)info->si_addr; - unsigned code = ((*(u32 *)pc) >> 5) & 0xffff; - if ((code & 0xff00) != 0x100) - return AccessInfo{0, 0, false, false}; // Not ours. - bool is_store = code & 0x10; - bool recover = code & 0x20; - unsigned size_log = code & 0xf; + const unsigned code = ((*(u32 *)pc) >> 5) & 0xffff; + if ((code & 0xff00) != 0x900) + return AccessInfo{}; // Not ours. + + const bool is_store = code & 0x10; + const bool recover = code & 0x20; + const uptr addr = uc->uc_mcontext.regs[0]; + const unsigned size_log = code & 0xf; + if (size_log > 4 && size_log != 0xf) + return AccessInfo{}; // Not ours. + const uptr size = size_log == 0xf ? uc->uc_mcontext.regs[1] : 1U << size_log; + +#elif defined(__x86_64__) + // Access type is encoded in the instruction following INT3 as + // NOP DWORD ptr [EAX + 0x40 + 0xXY]. For Y == 0xF, access size is stored in + // RSI register. Access address is always in RDI register. + uptr pc = (uptr)uc->uc_mcontext.gregs[REG_RIP]; + uint8_t *nop = (uint8_t*)pc; + if (*nop != 0x0f || *(nop + 1) != 0x1f || *(nop + 2) != 0x40 || + *(nop + 3) < 0x40) + return AccessInfo{}; // Not ours. + const unsigned code = *(nop + 3); + + const bool is_store = code & 0x10; + const bool recover = code & 0x20; + const uptr addr = uc->uc_mcontext.gregs[REG_RDI]; + const unsigned size_log = code & 0xf; if (size_log > 4 && size_log != 0xf) - return AccessInfo{0, 0, false, false}; // Not ours. + return AccessInfo{}; // Not ours. + const uptr size = + size_log == 0xf ? uc->uc_mcontext.gregs[REG_RSI] : 1U << size_log; - ai.is_store = is_store; - ai.is_load = !is_store; - ai.addr = uc->uc_mcontext.regs[0]; - if (size_log == 0xf) - ai.size = uc->uc_mcontext.regs[1]; - else - ai.size = 1U << size_log; - ai.recover = recover; - return ai; -} #else -static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) { - return AccessInfo{0, 0, false, false}; -} +# error Unsupported architecture #endif -static bool HwasanOnSIGILL(int signo, siginfo_t *info, ucontext_t *uc) { - SignalContext sig{info, uc}; + return AccessInfo{addr, size, is_store, !is_store, recover}; +} + +static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) { AccessInfo ai = GetAccessInfo(info, uc); if (!ai.is_store && !ai.is_load) return false; - InternalScopedBuffer<BufferedStackTrace> stack_buffer(1); + InternalMmapVector<BufferedStackTrace> stack_buffer(1); BufferedStackTrace *stack = stack_buffer.data(); stack->Reset(); + SignalContext sig{info, uc}; GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, uc, common_flags()->fast_unwind_on_fatal); @@ -230,7 +349,12 @@ static bool HwasanOnSIGILL(int signo, siginfo_t *info, ucontext_t *uc) { if (flags()->halt_on_error || !ai.recover) Die(); +#if defined(__aarch64__) uc->uc_mcontext.pc += 4; +#elif defined(__x86_64__) +#else +# error Unsupported architecture +#endif return true; } @@ -242,8 +366,8 @@ static void OnStackUnwind(const SignalContext &sig, const void *, void HwasanOnDeadlySignal(int signo, void *info, void *context) { // Probably a tag mismatch. - if (signo == SIGILL) - if (HwasanOnSIGILL(signo, (siginfo_t *)info, (ucontext_t*)context)) + if (signo == SIGTRAP) + if (HwasanOnSIGTRAP(signo, (siginfo_t *)info, (ucontext_t*)context)) return; HandleDeadlySignal(info, context, GetTid(), &OnStackUnwind, nullptr); |