123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- //===-- hwasan_linux.cpp ----------------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- ///
- /// \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 <dlfcn.h>
- # include <elf.h>
- # include <errno.h>
- # include <link.h>
- # include <pthread.h>
- # include <signal.h>
- # include <stdio.h>
- # include <stdlib.h>
- # include <sys/prctl.h>
- # include <sys/resource.h>
- # include <sys/time.h>
- # include <unistd.h>
- # include <unwind.h>
- # 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 "hwasan_thread_list.h"
- # include "sanitizer_common/sanitizer_common.h"
- # include "sanitizer_common/sanitizer_procmaps.h"
- # include "sanitizer_common/sanitizer_stackdepot.h"
- // Configurations of HWASAN_WITH_INTERCEPTORS and SANITIZER_ANDROID.
- //
- // HWASAN_WITH_INTERCEPTORS=OFF, SANITIZER_ANDROID=OFF
- // Not currently tested.
- // HWASAN_WITH_INTERCEPTORS=OFF, SANITIZER_ANDROID=ON
- // Integration tests downstream exist.
- // HWASAN_WITH_INTERCEPTORS=ON, SANITIZER_ANDROID=OFF
- // Tested with check-hwasan on x86_64-linux.
- // HWASAN_WITH_INTERCEPTORS=ON, SANITIZER_ANDROID=ON
- // Tested with check-hwasan on aarch64-linux-android.
- # if !SANITIZER_ANDROID
- SANITIZER_INTERFACE_ATTRIBUTE
- THREADLOCAL uptr __hwasan_tls;
- # endif
- namespace __hwasan {
- // With the zero shadow base we can not actually map pages starting from 0.
- // This constant is somewhat arbitrary.
- constexpr uptr kZeroBaseShadowStart = 0;
- constexpr uptr kZeroBaseMaxShadowStart = 1 << 18;
- static void ProtectGap(uptr addr, uptr size) {
- __sanitizer::ProtectGap(addr, size, kZeroBaseShadowStart,
- kZeroBaseMaxShadowStart);
- }
- uptr kLowMemStart;
- uptr kLowMemEnd;
- uptr kHighMemStart;
- uptr kHighMemEnd;
- static void PrintRange(uptr start, uptr end, const char *name) {
- Printf("|| [%p, %p] || %.*s ||\n", (void *)start, (void *)end, 10, name);
- }
- 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 (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);
- }
- static uptr GetHighMemEnd() {
- // HighMem covers the upper part of the address space.
- uptr max_address = GetMaxUserVirtualAddress();
- // Adjust max address to make sure that kHighMemEnd and kHighMemStart are
- // properly aligned:
- max_address |= (GetMmapGranularity() << kShadowScale) - 1;
- return max_address;
- }
- static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
- __hwasan_shadow_memory_dynamic_address =
- FindDynamicShadowStart(shadow_size_bytes);
- }
- void InitializeOsSupport() {
- # define PR_SET_TAGGED_ADDR_CTRL 55
- # define PR_GET_TAGGED_ADDR_CTRL 56
- # define PR_TAGGED_ADDR_ENABLE (1UL << 0)
- // Check we're running on a kernel that can use the tagged address ABI.
- int local_errno = 0;
- if (internal_iserror(internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
- &local_errno) &&
- local_errno == EINVAL) {
- # if SANITIZER_ANDROID || defined(HWASAN_ALIASING_MODE)
- // Some older Android kernels have the tagged pointer ABI on
- // unconditionally, and hence don't have the tagged-addr prctl while still
- // allow the ABI.
- // If targeting Android and the prctl is not around we assume this is the
- // case.
- return;
- # else
- if (flags()->fail_without_syscall_abi) {
- Printf(
- "FATAL: "
- "HWAddressSanitizer requires a kernel with tagged address ABI.\n");
- Die();
- }
- # endif
- }
- // Turn on the tagged address ABI.
- if ((internal_iserror(internal_prctl(PR_SET_TAGGED_ADDR_CTRL,
- PR_TAGGED_ADDR_ENABLE, 0, 0, 0)) ||
- !internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0))) {
- # if defined(__x86_64__) && !defined(HWASAN_ALIASING_MODE)
- // Try the new prctl API for Intel LAM. The API is based on a currently
- // unsubmitted patch to the Linux kernel (as of May 2021) and is thus
- // subject to change. Patch is here:
- // https://lore.kernel.org/linux-mm/20210205151631.43511-12-kirill.shutemov@linux.intel.com/
- int tag_bits = kTagBits;
- int tag_shift = kAddressTagShift;
- if (!internal_iserror(
- internal_prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE,
- reinterpret_cast<unsigned long>(&tag_bits),
- reinterpret_cast<unsigned long>(&tag_shift), 0))) {
- CHECK_EQ(tag_bits, kTagBits);
- CHECK_EQ(tag_shift, kAddressTagShift);
- return;
- }
- # endif // defined(__x86_64__) && !defined(HWASAN_ALIASING_MODE)
- if (flags()->fail_without_syscall_abi) {
- Printf(
- "FATAL: HWAddressSanitizer failed to enable tagged address syscall "
- "ABI.\nSuggest check `sysctl abi.tagged_addr_disabled` "
- "configuration.\n");
- Die();
- }
- }
- # undef PR_SET_TAGGED_ADDR_CTRL
- # undef PR_GET_TAGGED_ADDR_CTRL
- # undef PR_TAGGED_ADDR_ENABLE
- }
- bool InitShadow() {
- // Define the entire memory range.
- kHighMemEnd = GetHighMemEnd();
- // Determine shadow memory base offset.
- InitializeShadowBaseAddress(MemToShadowSize(kHighMemEnd));
- // Place the low memory first.
- kLowMemEnd = __hwasan_shadow_memory_dynamic_address - 1;
- kLowMemStart = 0;
- // Define the low shadow based on the already placed low memory.
- kLowShadowEnd = MemToShadow(kLowMemEnd);
- kLowShadowStart = __hwasan_shadow_memory_dynamic_address;
- // High shadow takes whatever memory is left up there (making sure it is not
- // interfering with low memory in the fixed case).
- kHighShadowEnd = MemToShadow(kHighMemEnd);
- kHighShadowStart = Max(kLowMemEnd, MemToShadow(kHighShadowEnd)) + 1;
- // High memory starts where allocated shadow allows.
- kHighMemStart = ShadowToMem(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);
- CHECK_GT(kLowShadowStart, kLowMemEnd);
- 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 (kLowMemEnd + 1 < kLowShadowStart)
- ProtectGap(kLowMemEnd + 1, kLowShadowStart - kLowMemEnd - 1);
- if (kLowShadowEnd + 1 < kHighShadowStart)
- ProtectGap(kLowShadowEnd + 1, kHighShadowStart - kLowShadowEnd - 1);
- if (kHighShadowEnd + 1 < kHighMemStart)
- ProtectGap(kHighShadowEnd + 1, kHighMemStart - kHighShadowEnd - 1);
- return true;
- }
- void InitThreads() {
- CHECK(__hwasan_shadow_memory_dynamic_address);
- uptr guard_page_size = GetMmapGranularity();
- uptr thread_space_start =
- __hwasan_shadow_memory_dynamic_address - (1ULL << kShadowBaseAlignment);
- uptr thread_space_end =
- __hwasan_shadow_memory_dynamic_address - guard_page_size;
- ReserveShadowMemoryRange(thread_space_start, thread_space_end - 1,
- "hwasan threads", /*madvise_shadow*/ false);
- ProtectGap(thread_space_end,
- __hwasan_shadow_memory_dynamic_address - thread_space_end);
- InitThreadList(thread_space_start, thread_space_end - thread_space_start);
- hwasanThreadList().CreateCurrentThread();
- }
- bool MemIsApp(uptr p) {
- // Memory outside the alias range has non-zero tags.
- # if !defined(HWASAN_ALIASING_MODE)
- CHECK(GetTagFromPointer(p) == 0);
- # endif
- return (p >= kHighMemStart && p <= kHighMemEnd) ||
- (p >= kLowMemStart && p <= kLowMemEnd);
- }
- void InstallAtExitHandler() { atexit(HwasanAtExit); }
- // ---------------------- TSD ---------------- {{{1
- extern "C" void __hwasan_thread_enter() {
- hwasanThreadList().CreateCurrentThread()->EnsureRandomStateInited();
- }
- extern "C" void __hwasan_thread_exit() {
- Thread *t = GetCurrentThread();
- // Make sure that signal handler can not see a stale current thread pointer.
- atomic_signal_fence(memory_order_seq_cst);
- if (t)
- hwasanThreadList().ReleaseThread(t);
- }
- # if HWASAN_WITH_INTERCEPTORS
- static pthread_key_t tsd_key;
- static bool tsd_key_inited = false;
- void HwasanTSDThreadInit() {
- if (tsd_key_inited)
- CHECK_EQ(0, pthread_setspecific(tsd_key,
- (void *)GetPthreadDestructorIterations()));
- }
- void HwasanTSDDtor(void *tsd) {
- uptr iterations = (uptr)tsd;
- if (iterations > 1) {
- CHECK_EQ(0, pthread_setspecific(tsd_key, (void *)(iterations - 1)));
- return;
- }
- __hwasan_thread_exit();
- }
- void HwasanTSDInit() {
- CHECK(!tsd_key_inited);
- tsd_key_inited = true;
- CHECK_EQ(0, pthread_key_create(&tsd_key, HwasanTSDDtor));
- }
- # else
- void HwasanTSDInit() {}
- void HwasanTSDThreadInit() {}
- # endif
- # if SANITIZER_ANDROID
- uptr *GetCurrentThreadLongPtr() { return (uptr *)get_android_tls_ptr(); }
- # else
- uptr *GetCurrentThreadLongPtr() { return &__hwasan_tls; }
- # endif
- # if SANITIZER_ANDROID
- void AndroidTestTlsSlot() {
- uptr kMagicValue = 0x010203040A0B0C0D;
- uptr *tls_ptr = GetCurrentThreadLongPtr();
- uptr old_value = *tls_ptr;
- *tls_ptr = kMagicValue;
- dlerror();
- if (*(uptr *)get_android_tls_ptr() != kMagicValue) {
- Printf(
- "ERROR: Incompatible version of Android: TLS_SLOT_SANITIZER(6) is used "
- "for dlerror().\n");
- Die();
- }
- *tls_ptr = old_value;
- }
- # else
- void AndroidTestTlsSlot() {}
- # endif
- static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) {
- // 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;
- 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{}; // Not ours.
- const uptr size =
- size_log == 0xf ? uc->uc_mcontext.gregs[REG_RSI] : 1U << size_log;
- # else
- # error Unsupported architecture
- # endif
- 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;
- SignalContext sig{info, uc};
- HandleTagMismatch(ai, StackTrace::GetNextInstructionPc(sig.pc), sig.bp, uc);
- # if defined(__aarch64__)
- uc->uc_mcontext.pc += 4;
- # elif defined(__x86_64__)
- # else
- # error Unsupported architecture
- # endif
- return true;
- }
- static void OnStackUnwind(const SignalContext &sig, const void *,
- BufferedStackTrace *stack) {
- stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context,
- common_flags()->fast_unwind_on_fatal);
- }
- void HwasanOnDeadlySignal(int signo, void *info, void *context) {
- // Probably a tag mismatch.
- if (signo == SIGTRAP)
- if (HwasanOnSIGTRAP(signo, (siginfo_t *)info, (ucontext_t *)context))
- return;
- HandleDeadlySignal(info, context, GetTid(), &OnStackUnwind, nullptr);
- }
- void Thread::InitStackAndTls(const InitState *) {
- uptr tls_size;
- uptr stack_size;
- GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, &tls_begin_,
- &tls_size);
- stack_top_ = stack_bottom_ + stack_size;
- tls_end_ = tls_begin_ + tls_size;
- }
- uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) {
- CHECK(IsAligned(p, kShadowAlignment));
- CHECK(IsAligned(size, kShadowAlignment));
- uptr shadow_start = MemToShadow(p);
- uptr shadow_size = MemToShadowSize(size);
- uptr page_size = GetPageSizeCached();
- uptr page_start = RoundUpTo(shadow_start, page_size);
- uptr page_end = RoundDownTo(shadow_start + shadow_size, page_size);
- uptr threshold = common_flags()->clear_shadow_mmap_threshold;
- if (SANITIZER_LINUX &&
- UNLIKELY(page_end >= page_start + threshold && tag == 0)) {
- internal_memset((void *)shadow_start, tag, page_start - shadow_start);
- internal_memset((void *)page_end, tag,
- shadow_start + shadow_size - page_end);
- // For an anonymous private mapping MADV_DONTNEED will return a zero page on
- // Linux.
- ReleaseMemoryPagesToOSAndZeroFill(page_start, page_end);
- } else {
- internal_memset((void *)shadow_start, tag, shadow_size);
- }
- return AddTagToPointer(p, tag);
- }
- void HwasanInstallAtForkHandler() {
- auto before = []() {
- HwasanAllocatorLock();
- StackDepotLockAll();
- };
- auto after = []() {
- StackDepotUnlockAll();
- HwasanAllocatorUnlock();
- };
- pthread_atfork(before, after, after);
- }
- } // namespace __hwasan
- #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
|