diff options
Diffstat (limited to 'lib/hwasan')
-rw-r--r-- | lib/hwasan/hwasan.cpp | 155 | ||||
-rw-r--r-- | lib/hwasan/hwasan.h | 2 | ||||
-rw-r--r-- | lib/hwasan/hwasan_allocator.cpp | 29 | ||||
-rw-r--r-- | lib/hwasan/hwasan_allocator.h | 6 | ||||
-rw-r--r-- | lib/hwasan/hwasan_exceptions.cpp | 67 | ||||
-rw-r--r-- | lib/hwasan/hwasan_flags.inc | 2 | ||||
-rw-r--r-- | lib/hwasan/hwasan_interceptors.cpp | 2 | ||||
-rw-r--r-- | lib/hwasan/hwasan_interface_internal.h | 9 | ||||
-rw-r--r-- | lib/hwasan/hwasan_linux.cpp | 42 | ||||
-rw-r--r-- | lib/hwasan/hwasan_new_delete.cpp | 2 | ||||
-rw-r--r-- | lib/hwasan/hwasan_report.cpp | 25 | ||||
-rw-r--r-- | lib/hwasan/hwasan_tag_mismatch_aarch64.S | 50 |
12 files changed, 328 insertions, 63 deletions
diff --git a/lib/hwasan/hwasan.cpp b/lib/hwasan/hwasan.cpp index 6f2246552164..7b5c6c694be9 100644 --- a/lib/hwasan/hwasan.cpp +++ b/lib/hwasan/hwasan.cpp @@ -193,27 +193,12 @@ void UpdateMemoryUsage() { void UpdateMemoryUsage() {} #endif -// Prepare to run instrumented code on the main thread. -void InitInstrumentation() { - if (hwasan_instrumentation_inited) return; - - if (!InitShadow()) { - Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n"); - DumpProcessMap(); - Die(); - } - - InitThreads(); - hwasanThreadList().CreateCurrentThread(); - - hwasan_instrumentation_inited = 1; -} - } // namespace __hwasan +using namespace __hwasan; + void __sanitizer::BufferedStackTrace::UnwindImpl( uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) { - using namespace __hwasan; Thread *t = GetCurrentThread(); if (!t) { // the thread is still being created. @@ -231,9 +216,117 @@ void __sanitizer::BufferedStackTrace::UnwindImpl( Unwind(max_depth, pc, 0, context, 0, 0, false); } -// Interface. +struct hwasan_global { + s32 gv_relptr; + u32 info; +}; + +static void InitGlobals(const hwasan_global *begin, const hwasan_global *end) { + for (auto *desc = begin; desc != end; ++desc) { + uptr gv = reinterpret_cast<uptr>(desc) + desc->gv_relptr; + uptr size = desc->info & 0xffffff; + uptr full_granule_size = RoundDownTo(size, 16); + u8 tag = desc->info >> 24; + TagMemoryAligned(gv, full_granule_size, tag); + if (size % 16) + TagMemoryAligned(gv + full_granule_size, 16, size % 16); + } +} -using namespace __hwasan; +enum { NT_LLVM_HWASAN_GLOBALS = 3 }; + +struct hwasan_global_note { + s32 begin_relptr; + s32 end_relptr; +}; + +// Check that the given library meets the code model requirements for tagged +// globals. These properties are not checked at link time so they need to be +// checked at runtime. +static void CheckCodeModel(ElfW(Addr) base, const ElfW(Phdr) * phdr, + ElfW(Half) phnum) { + ElfW(Addr) min_addr = -1ull, max_addr = 0; + for (unsigned i = 0; i != phnum; ++i) { + if (phdr[i].p_type != PT_LOAD) + continue; + ElfW(Addr) lo = base + phdr[i].p_vaddr, hi = lo + phdr[i].p_memsz; + if (min_addr > lo) + min_addr = lo; + if (max_addr < hi) + max_addr = hi; + } + + if (max_addr - min_addr > 1ull << 32) { + Report("FATAL: HWAddressSanitizer: library size exceeds 2^32\n"); + Die(); + } + if (max_addr > 1ull << 48) { + Report("FATAL: HWAddressSanitizer: library loaded above address 2^48\n"); + Die(); + } +} + +static void InitGlobalsFromPhdrs(ElfW(Addr) base, const ElfW(Phdr) * phdr, + ElfW(Half) phnum) { + for (unsigned i = 0; i != phnum; ++i) { + if (phdr[i].p_type != PT_NOTE) + continue; + const char *note = reinterpret_cast<const char *>(base + phdr[i].p_vaddr); + const char *nend = note + phdr[i].p_memsz; + while (note < nend) { + auto *nhdr = reinterpret_cast<const ElfW(Nhdr) *>(note); + const char *name = note + sizeof(ElfW(Nhdr)); + const char *desc = name + RoundUpTo(nhdr->n_namesz, 4); + if (nhdr->n_type != NT_LLVM_HWASAN_GLOBALS || + internal_strcmp(name, "LLVM") != 0) { + note = desc + RoundUpTo(nhdr->n_descsz, 4); + continue; + } + + // Only libraries with instrumented globals need to be checked against the + // code model since they use relocations that aren't checked at link time. + CheckCodeModel(base, phdr, phnum); + + auto *global_note = reinterpret_cast<const hwasan_global_note *>(desc); + auto *global_begin = reinterpret_cast<const hwasan_global *>( + note + global_note->begin_relptr); + auto *global_end = reinterpret_cast<const hwasan_global *>( + note + global_note->end_relptr); + InitGlobals(global_begin, global_end); + return; + } + } +} + +static void InitLoadedGlobals() { + dl_iterate_phdr( + [](dl_phdr_info *info, size_t size, void *data) { + InitGlobalsFromPhdrs(info->dlpi_addr, info->dlpi_phdr, + info->dlpi_phnum); + return 0; + }, + nullptr); +} + +// Prepare to run instrumented code on the main thread. +static void InitInstrumentation() { + if (hwasan_instrumentation_inited) return; + + InitPrctl(); + + if (!InitShadow()) { + Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n"); + DumpProcessMap(); + Die(); + } + + InitThreads(); + hwasanThreadList().CreateCurrentThread(); + + hwasan_instrumentation_inited = 1; +} + +// Interface. uptr __hwasan_shadow_memory_dynamic_address; // Global interface symbol. @@ -244,6 +337,17 @@ void __hwasan_init_frames(uptr beg, uptr end) {} void __hwasan_init_static() { InitShadowGOT(); InitInstrumentation(); + + // In the non-static code path we call dl_iterate_phdr here. But at this point + // libc might not have been initialized enough for dl_iterate_phdr to work. + // Fortunately, since this is a statically linked executable we can use the + // linker-defined symbol __ehdr_start to find the only relevant set of phdrs. + extern ElfW(Ehdr) __ehdr_start; + InitGlobalsFromPhdrs( + 0, + reinterpret_cast<const ElfW(Phdr) *>( + reinterpret_cast<const char *>(&__ehdr_start) + __ehdr_start.e_phoff), + __ehdr_start.e_phnum); } void __hwasan_init() { @@ -267,6 +371,7 @@ void __hwasan_init() { DisableCoreDumperIfNecessary(); InitInstrumentation(); + InitLoadedGlobals(); // Needs to be called here because flags()->random_tags might not have been // initialized when InitInstrumentation() was called. @@ -301,6 +406,18 @@ void __hwasan_init() { hwasan_inited = 1; } +void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr, + ElfW(Half) phnum) { + InitGlobalsFromPhdrs(base, phdr, phnum); +} + +void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr, + ElfW(Half) phnum) { + for (; phnum != 0; ++phdr, --phnum) + if (phdr->p_type == PT_LOAD) + TagMemory(base + phdr->p_vaddr, phdr->p_memsz, 0); +} + void __hwasan_print_shadow(const void *p, uptr sz) { uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p)); uptr shadow_first = MemToShadow(ptr_raw); diff --git a/lib/hwasan/hwasan.h b/lib/hwasan/hwasan.h index 465e56c3a8cc..9e0ced93b55d 100644 --- a/lib/hwasan/hwasan.h +++ b/lib/hwasan/hwasan.h @@ -74,8 +74,8 @@ extern int hwasan_report_count; bool ProtectRange(uptr beg, uptr end); bool InitShadow(); +void InitPrctl(); void InitThreads(); -void InitInstrumentation(); void MadviseShadow(); char *GetProcSelfMaps(); void InitializeInterceptors(); diff --git a/lib/hwasan/hwasan_allocator.cpp b/lib/hwasan/hwasan_allocator.cpp index b4fae5820d0a..81a57d3afd4d 100644 --- a/lib/hwasan/hwasan_allocator.cpp +++ b/lib/hwasan/hwasan_allocator.cpp @@ -22,11 +22,6 @@ #include "hwasan_thread.h" #include "hwasan_report.h" -#if HWASAN_WITH_INTERCEPTORS -DEFINE_REAL(void *, realloc, void *ptr, uptr size) -DEFINE_REAL(void, free, void *ptr) -#endif - namespace __hwasan { static Allocator allocator; @@ -301,14 +296,6 @@ void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) { void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) { if (!ptr) return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false)); - -#if HWASAN_WITH_INTERCEPTORS - // A tag of 0 means that this is a system allocator allocation, so we must use - // the system allocator to realloc it. - if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0) - return REAL(realloc)(ptr, size); -#endif - if (size == 0) { HwasanDeallocate(stack, ptr); return nullptr; @@ -381,13 +368,6 @@ int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size, } void hwasan_free(void *ptr, StackTrace *stack) { -#if HWASAN_WITH_INTERCEPTORS - // A tag of 0 means that this is a system allocator allocation, so we must use - // the system allocator to free it. - if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0) - return REAL(free)(ptr); -#endif - return HwasanDeallocate(stack, ptr); } @@ -400,15 +380,6 @@ void __hwasan_enable_allocator_tagging() { } void __hwasan_disable_allocator_tagging() { -#if HWASAN_WITH_INTERCEPTORS - // Allocator tagging must be enabled for the system allocator fallback to work - // correctly. This means that we can't disable it at runtime if it was enabled - // at startup since that might result in our deallocations going to the system - // allocator. If tagging was disabled at startup we avoid this problem by - // disabling the fallback altogether. - CHECK(flags()->disable_allocator_tagging); -#endif - atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 0); } diff --git a/lib/hwasan/hwasan_allocator.h b/lib/hwasan/hwasan_allocator.h index 3a50a11f3526..f62be2696021 100644 --- a/lib/hwasan/hwasan_allocator.h +++ b/lib/hwasan/hwasan_allocator.h @@ -13,7 +13,6 @@ #ifndef HWASAN_ALLOCATOR_H #define HWASAN_ALLOCATOR_H -#include "interception/interception.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_checks.h" #include "sanitizer_common/sanitizer_allocator_interface.h" @@ -26,11 +25,6 @@ #error Unsupported platform #endif -#if HWASAN_WITH_INTERCEPTORS -DECLARE_REAL(void *, realloc, void *ptr, uptr size) -DECLARE_REAL(void, free, void *ptr) -#endif - namespace __hwasan { struct Metadata { diff --git a/lib/hwasan/hwasan_exceptions.cpp b/lib/hwasan/hwasan_exceptions.cpp new file mode 100644 index 000000000000..169e7876cb58 --- /dev/null +++ b/lib/hwasan/hwasan_exceptions.cpp @@ -0,0 +1,67 @@ +//===-- hwasan_exceptions.cpp ---------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file is a part of HWAddressSanitizer. +// +// HWAddressSanitizer runtime. +//===----------------------------------------------------------------------===// + +#include "hwasan_poisoning.h" +#include "sanitizer_common/sanitizer_common.h" + +#include <unwind.h> + +using namespace __hwasan; +using namespace __sanitizer; + +typedef _Unwind_Reason_Code PersonalityFn(int version, _Unwind_Action actions, + uint64_t exception_class, + _Unwind_Exception* unwind_exception, + _Unwind_Context* context); + +// Pointers to the _Unwind_GetGR and _Unwind_GetCFA functions are passed in +// instead of being called directly. This is to handle cases where the unwinder +// is statically linked and the sanitizer runtime and the program are linked +// against different unwinders. The _Unwind_Context data structure is opaque so +// it may be incompatible between unwinders. +typedef _Unwind_Word GetGRFn(_Unwind_Context* context, int index); +typedef _Unwind_Word GetCFAFn(_Unwind_Context* context); + +extern "C" SANITIZER_INTERFACE_ATTRIBUTE _Unwind_Reason_Code +__hwasan_personality_wrapper(int version, _Unwind_Action actions, + uint64_t exception_class, + _Unwind_Exception* unwind_exception, + _Unwind_Context* context, + PersonalityFn* real_personality, GetGRFn* get_gr, + GetCFAFn* get_cfa) { + _Unwind_Reason_Code rc; + if (real_personality) + rc = real_personality(version, actions, exception_class, unwind_exception, + context); + else + rc = _URC_CONTINUE_UNWIND; + + // We only untag frames without a landing pad because landing pads are + // responsible for untagging the stack themselves if they resume. + // + // Here we assume that the frame record appears after any locals. This is not + // required by AAPCS but is a requirement for HWASAN instrumented functions. + if ((actions & _UA_CLEANUP_PHASE) && rc == _URC_CONTINUE_UNWIND) { +#if defined(__x86_64__) + uptr fp = get_gr(context, 6); // rbp +#elif defined(__aarch64__) + uptr fp = get_gr(context, 29); // x29 +#else +#error Unsupported architecture +#endif + uptr sp = get_cfa(context); + TagMemory(sp, fp - sp, 0); + } + + return rc; +} diff --git a/lib/hwasan/hwasan_flags.inc b/lib/hwasan/hwasan_flags.inc index 2dff2b9aca6e..dffbf56cb155 100644 --- a/lib/hwasan/hwasan_flags.inc +++ b/lib/hwasan/hwasan_flags.inc @@ -1,4 +1,4 @@ -//===-- hwasan_flags.inc ------------------------------------------*- C++ -*-===// +//===-- hwasan_flags.inc ----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/lib/hwasan/hwasan_interceptors.cpp b/lib/hwasan/hwasan_interceptors.cpp index 47fed0fc9abb..95e2e865717d 100644 --- a/lib/hwasan/hwasan_interceptors.cpp +++ b/lib/hwasan/hwasan_interceptors.cpp @@ -260,8 +260,6 @@ void InitializeInterceptors() { #if !defined(__aarch64__) INTERCEPT_FUNCTION(pthread_create); #endif // __aarch64__ - INTERCEPT_FUNCTION(realloc); - INTERCEPT_FUNCTION(free); #endif inited = 1; diff --git a/lib/hwasan/hwasan_interface_internal.h b/lib/hwasan/hwasan_interface_internal.h index 1b10d76c78e9..ca57f0fe437b 100644 --- a/lib/hwasan/hwasan_interface_internal.h +++ b/lib/hwasan/hwasan_interface_internal.h @@ -16,6 +16,7 @@ #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" +#include <link.h> extern "C" { @@ -25,6 +26,14 @@ void __hwasan_init_static(); SANITIZER_INTERFACE_ATTRIBUTE void __hwasan_init(); +SANITIZER_INTERFACE_ATTRIBUTE +void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr, + ElfW(Half) phnum); + +SANITIZER_INTERFACE_ATTRIBUTE +void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr, + ElfW(Half) phnum); + using __sanitizer::uptr; using __sanitizer::sptr; using __sanitizer::uu64; diff --git a/lib/hwasan/hwasan_linux.cpp b/lib/hwasan/hwasan_linux.cpp index d932976489e9..948e40154fec 100644 --- a/lib/hwasan/hwasan_linux.cpp +++ b/lib/hwasan/hwasan_linux.cpp @@ -34,6 +34,8 @@ #include <sys/time.h> #include <unistd.h> #include <unwind.h> +#include <sys/prctl.h> +#include <errno.h> #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_procmaps.h" @@ -144,6 +146,43 @@ static void InitializeShadowBaseAddress(uptr shadow_size_bytes) { FindDynamicShadowStart(shadow_size_bytes); } +void InitPrctl() { +#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. + if (internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0) == (uptr)-1 && + errno == EINVAL) { +#if SANITIZER_ANDROID + // 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 + Printf( + "FATAL: " + "HWAddressSanitizer requires a kernel with tagged address ABI.\n"); + Die(); +#endif + } + + // Turn on the tagged address ABI. + if (internal_prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) == + (uptr)-1 || + !internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0)) { + 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(); @@ -211,8 +250,7 @@ void InitThreads() { static void MadviseShadowRegion(uptr beg, uptr end) { uptr size = end - beg + 1; - if (common_flags()->no_huge_pages_for_shadow) - NoHugePagesInRegion(beg, size); + SetShadowRegionHugePageMode(beg, size); if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size); } diff --git a/lib/hwasan/hwasan_new_delete.cpp b/lib/hwasan/hwasan_new_delete.cpp index 4a9c79fe41b3..191c17e56a74 100644 --- a/lib/hwasan/hwasan_new_delete.cpp +++ b/lib/hwasan/hwasan_new_delete.cpp @@ -20,7 +20,7 @@ #include <stddef.h> -using namespace __hwasan; // NOLINT +using namespace __hwasan; // Fake std::nothrow_t to avoid including <new>. namespace std { diff --git a/lib/hwasan/hwasan_report.cpp b/lib/hwasan/hwasan_report.cpp index 346889797888..19cb27554bc6 100644 --- a/lib/hwasan/hwasan_report.cpp +++ b/lib/hwasan/hwasan_report.cpp @@ -278,6 +278,31 @@ void PrintAddressDescription( Printf("%s", d.Default()); GetStackTraceFromId(chunk.GetAllocStackId()).Print(); num_descriptions_printed++; + } else { + // Check whether the address points into a loaded library. If so, this is + // most likely a global variable. + const char *module_name; + uptr module_address; + Symbolizer *sym = Symbolizer::GetOrInit(); + if (sym->GetModuleNameAndOffsetForPC(mem, &module_name, + &module_address)) { + DataInfo info; + if (sym->SymbolizeData(mem, &info) && info.start) { + Printf( + "%p is located %zd bytes to the %s of %zd-byte global variable " + "%s [%p,%p) in %s\n", + untagged_addr, + candidate == left ? untagged_addr - (info.start + info.size) + : info.start - untagged_addr, + candidate == left ? "right" : "left", info.size, info.name, + info.start, info.start + info.size, module_name); + } else { + Printf("%p is located to the %s of a global variable in (%s+0x%x)\n", + untagged_addr, candidate == left ? "right" : "left", + module_name, module_address); + } + num_descriptions_printed++; + } } } diff --git a/lib/hwasan/hwasan_tag_mismatch_aarch64.S b/lib/hwasan/hwasan_tag_mismatch_aarch64.S index 92f627480486..4c060a61e98e 100644 --- a/lib/hwasan/hwasan_tag_mismatch_aarch64.S +++ b/lib/hwasan/hwasan_tag_mismatch_aarch64.S @@ -51,14 +51,60 @@ // +---------------------------------+ <-- [x30 / SP] // This function takes two arguments: -// * x0: The address of read/write instruction that caused HWASan check fail. -// * x1: The tag size. +// * x0: The data address. +// * x1: The encoded access info for the failing access. +// This function has two entry points. The first, __hwasan_tag_mismatch, is used +// by clients that were compiled without short tag checks (i.e. binaries built +// by older compilers and binaries targeting older runtimes). In this case the +// outlined tag check will be missing the code handling short tags (which won't +// be used in the binary's own stack variables but may be used on the heap +// or stack variables in other binaries), so the check needs to be done here. +// +// The second, __hwasan_tag_mismatch_v2, is used by binaries targeting newer +// runtimes. This entry point bypasses the short tag check since it will have +// already been done as part of the outlined tag check. Since tag mismatches are +// uncommon, there isn't a significant performance benefit to being able to +// bypass the check; the main benefits are that we can sometimes avoid +// clobbering the x17 register in error reports, and that the program will have +// a runtime dependency on the __hwasan_tag_mismatch_v2 symbol therefore it will +// fail to start up given an older (i.e. incompatible) runtime. .section .text .file "hwasan_tag_mismatch_aarch64.S" .global __hwasan_tag_mismatch .type __hwasan_tag_mismatch, %function __hwasan_tag_mismatch: + // Compute the granule position one past the end of the access. + mov x16, #1 + and x17, x1, #0xf + lsl x16, x16, x17 + and x17, x0, #0xf + add x17, x16, x17 + + // Load the shadow byte again and check whether it is a short tag within the + // range of the granule position computed above. + ubfx x16, x0, #4, #52 + ldrb w16, [x9, x16] + cmp w16, #0xf + b.hi __hwasan_tag_mismatch_v2 + cmp w16, w17 + b.lo __hwasan_tag_mismatch_v2 + + // Load the real tag from the last byte of the granule and compare against + // the pointer tag. + orr x16, x0, #0xf + ldrb w16, [x16] + cmp x16, x0, lsr #56 + b.ne __hwasan_tag_mismatch_v2 + + // Restore x0, x1 and sp to their values from before the __hwasan_tag_mismatch + // call and resume execution. + ldp x0, x1, [sp], #256 + ret + +.global __hwasan_tag_mismatch_v2 +.type __hwasan_tag_mismatch_v2, %function +__hwasan_tag_mismatch_v2: CFI_STARTPROC // Set the CFA to be the return address for caller of __hwasan_check_*. Note |