Skip to content

Commit

Permalink
[compiler-rt][hwasan] Let CheckAddressSized eventually call HandleTag…
Browse files Browse the repository at this point in the history
…Mismatch on Fuchsia

Any hwasan tag checking done through runtime calls like __hwasan_mem* or
__hwasan_load/store* currently raise a sigtrap on a tag mismatch. Hwasan
dumps as much information it knows on the tag mismatch by placing
important values in specific registers before the brk and encoding the
access information in the optional argument supplied to the brk. If the
platform hwasan runs on uses signal handlers, then users can see the
typical pretty hwasan error report, but Fuchsia doesn't use signal
handlers, so it's left up to the platform exception handler to print all
this encoded information.

This patch attempts to enter the regular error reporting path via
HandleTagMismatch if a new macro CAN_GET_REGISTERS is set. For now this
is only defined for Fuchsia + aarch64, but can be expanded for other
platforms.

Differential Revision: https://reviews.llvm.org/D139377
  • Loading branch information
PiJoules committed Dec 7, 2022
1 parent f6e3a89 commit bcc4470
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 19 deletions.
78 changes: 59 additions & 19 deletions compiler-rt/lib/hwasan/hwasan_checks.h
Expand Up @@ -15,17 +15,49 @@

#include "hwasan_allocator.h"
#include "hwasan_mapping.h"
#include "hwasan_registers.h"
#include "sanitizer_common/sanitizer_common.h"

namespace __hwasan {
template <unsigned X>

enum class ErrorAction { Abort, Recover };
enum class AccessType { Load, Store };

// Used when the access size is known.
constexpr unsigned SigTrapEncoding(ErrorAction EA, AccessType AT,
unsigned LogSize) {
return 0x20 * (EA == ErrorAction::Recover) +
0x10 * (AT == AccessType::Store) + LogSize;
}

// Used when the access size varies at runtime.
constexpr unsigned SigTrapEncoding(ErrorAction EA, AccessType AT) {
return SigTrapEncoding(EA, AT, 0xf);
}

template <ErrorAction EA, AccessType AT, size_t LogSize>
__attribute__((always_inline)) static void SigTrap(uptr p) {
#if defined(__aarch64__)
// Other platforms like linux can use signals for intercepting an exception
// and dispatching to HandleTagMismatch. The fuchsias implementation doesn't
// use signals so we can call it here directly instead.
#if CAN_GET_REGISTERS && SANITIZER_FUCHSIA
auto regs = GetRegisters();
size_t size = 2 << LogSize;
AccessInfo access_info = {
.addr = p,
.size = size,
.is_store = AT == AccessType::Store,
.is_load = AT == AccessType::Load,
.recover = EA == ErrorAction::Recover,
};
HandleTagMismatch(access_info, (uptr)__builtin_return_address(0),
(uptr)__builtin_frame_address(0), /*uc=*/nullptr, regs.x);
#elif defined(__aarch64__)
(void)p;
// 0x900 is added to do not interfere with the kernel use of lower values of
// brk immediate.
register uptr x0 asm("x0") = p;
asm("brk %1\n\t" ::"r"(x0), "n"(0x900 + X));
asm("brk %1\n\t" ::"r"(x0), "n"(0x900 + SigTrapEncoding(EA, AT, LogSize)));
#elif defined(__x86_64__)
// INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
// total. The pointer is passed via rdi.
Expand All @@ -34,7 +66,7 @@ __attribute__((always_inline)) static void SigTrap(uptr p) {
// different nop command, the three bytes one).
asm volatile(
"int3\n"
"nopl %c0(%%rax)\n" ::"n"(0x40 + X),
"nopl %c0(%%rax)\n" ::"n"(0x40 + SigTrapEncoding(EA, AT, LogSize)),
"D"(p));
#elif SANITIZER_RISCV64
// Put pointer into x10
Expand All @@ -44,7 +76,7 @@ __attribute__((always_inline)) static void SigTrap(uptr p) {
asm volatile(
"ebreak\n"
"addiw x0, x0, %1\n" ::"r"(x10),
"I"(0x40 + X));
"I"(0x40 + SigTrapEncoding(EA, AT, LogSize)));
#else
// FIXME: not always sigill.
__builtin_trap();
Expand All @@ -53,17 +85,31 @@ __attribute__((always_inline)) static void SigTrap(uptr p) {
}

// Version with access size which is not power of 2
template <unsigned X>
template <ErrorAction EA, AccessType AT>
__attribute__((always_inline)) static void SigTrap(uptr p, uptr size) {
#if defined(__aarch64__)
// Other platforms like linux can use signals for intercepting an exception
// and dispatching to HandleTagMismatch. The fuchsias implementation doesn't
// use signals so we can call it here directly instead.
#if CAN_GET_REGISTERS && SANITIZER_FUCHSIA
auto regs = GetRegisters();
AccessInfo access_info = {
.addr = p,
.size = size,
.is_store = AT == AccessType::Store,
.is_load = AT == AccessType::Load,
.recover = EA == ErrorAction::Recover,
};
HandleTagMismatch(access_info, (uptr)__builtin_return_address(0),
(uptr)__builtin_frame_address(0), /*uc=*/nullptr, regs.x);
#elif defined(__aarch64__)
register uptr x0 asm("x0") = p;
register uptr x1 asm("x1") = size;
asm("brk %2\n\t" ::"r"(x0), "r"(x1), "n"(0x900 + X));
asm("brk %2\n\t" ::"r"(x0), "r"(x1), "n"(0x900 + SigTrapEncoding(EA, AT)));
#elif defined(__x86_64__)
// Size is stored in rsi.
asm volatile(
"int3\n"
"nopl %c0(%%rax)\n" ::"n"(0x40 + X),
"nopl %c0(%%rax)\n" ::"n"(0x40 + SigTrapEncoding(EA, AT)),
"D"(p), "S"(size));
#elif SANITIZER_RISCV64
// Put access size into x11
Expand All @@ -72,7 +118,7 @@ __attribute__((always_inline)) static void SigTrap(uptr p, uptr size) {
asm volatile(
"ebreak\n"
"addiw x0, x0, %2\n" ::"r"(x10),
"r"(x11), "I"(0x40 + X));
"r"(x11), "I"(0x40 + SigTrapEncoding(EA, AT)));
#else
__builtin_trap();
#endif
Expand All @@ -94,18 +140,14 @@ __attribute__((always_inline, nodebug)) static bool PossiblyShortTagMatches(
return *(u8 *)(ptr | (kShadowAlignment - 1)) == ptr_tag;
}

enum class ErrorAction { Abort, Recover };
enum class AccessType { Load, Store };

template <ErrorAction EA, AccessType AT, unsigned LogSize>
__attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) {
if (!InTaggableRegion(p))
return;
uptr ptr_raw = p & ~kAddressTagMask;
tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw);
if (UNLIKELY(!PossiblyShortTagMatches(mem_tag, p, 1 << LogSize))) {
SigTrap<0x20 * (EA == ErrorAction::Recover) +
0x10 * (AT == AccessType::Store) + LogSize>(p);
SigTrap<EA, AT, LogSize>(p);
if (EA == ErrorAction::Abort)
__builtin_unreachable();
}
Expand All @@ -122,8 +164,7 @@ __attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
tag_t *shadow_last = (tag_t *)MemToShadow(ptr_raw + sz);
for (tag_t *t = shadow_first; t < shadow_last; ++t)
if (UNLIKELY(ptr_tag != *t)) {
SigTrap<0x20 * (EA == ErrorAction::Recover) +
0x10 * (AT == AccessType::Store) + 0xf>(p, sz);
SigTrap<EA, AT>(p, sz);
if (EA == ErrorAction::Abort)
__builtin_unreachable();
}
Expand All @@ -132,8 +173,7 @@ __attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
if (UNLIKELY(tail_sz != 0 &&
!PossiblyShortTagMatches(
*shadow_last, end & ~(kShadowAlignment - 1), tail_sz))) {
SigTrap<0x20 * (EA == ErrorAction::Recover) +
0x10 * (AT == AccessType::Store) + 0xf>(p, sz);
SigTrap<EA, AT>(p, sz);
if (EA == ErrorAction::Abort)
__builtin_unreachable();
}
Expand Down
56 changes: 56 additions & 0 deletions compiler-rt/lib/hwasan/hwasan_registers.h
@@ -0,0 +1,56 @@
//===-- hwasan_registers.h --------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// This describes the register state retrieved by hwasan when error reporting.
//
//===----------------------------------------------------------------------===//

#ifndef HWASAN_REGISTERS_H
#define HWASAN_REGISTERS_H

#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_platform.h"

#if defined(__aarch64__)

# define CAN_GET_REGISTERS 1

struct Registers {
uptr x[32];
};

__attribute__((always_inline)) static Registers GetRegisters() {
Registers regs;
__asm__ volatile(
"stp x0, x1, [%1, #(8 * 0)]\n"
"stp x2, x3, [%1, #(8 * 2)]\n"
"stp x4, x5, [%1, #(8 * 4)]\n"
"stp x6, x7, [%1, #(8 * 6)]\n"
"stp x8, x9, [%1, #(8 * 8)]\n"
"stp x10, x11, [%1, #(8 * 10)]\n"
"stp x12, x13, [%1, #(8 * 12)]\n"
"stp x14, x15, [%1, #(8 * 14)]\n"
"stp x16, x17, [%1, #(8 * 16)]\n"
"stp x18, x19, [%1, #(8 * 18)]\n"
"stp x20, x21, [%1, #(8 * 20)]\n"
"stp x22, x23, [%1, #(8 * 22)]\n"
"stp x24, x25, [%1, #(8 * 24)]\n"
"stp x26, x27, [%1, #(8 * 26)]\n"
"stp x28, x29, [%1, #(8 * 28)]\n"
: "=m"(regs)
: "r"(regs.x));
regs.x[30] = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
regs.x[31] = reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
return regs;
}

#else
# define CAN_GET_REGISTERS 0
#endif

#endif // HWASAN_REGISTERS_H

0 comments on commit bcc4470

Please sign in to comment.