summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler-rt/lib/hwasan/CMakeLists.txt1
-rw-r--r--compiler-rt/lib/hwasan/hwasan_interface_internal.h3
-rw-r--r--compiler-rt/lib/hwasan/hwasan_linux.cpp23
-rw-r--r--compiler-rt/lib/hwasan/hwasan_report.cpp27
-rw-r--r--compiler-rt/lib/hwasan/hwasan_report.h4
-rw-r--r--compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S108
-rw-r--r--compiler-rt/test/hwasan/TestCases/register-dump-read.c43
-rw-r--r--llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp40
-rw-r--r--llvm/test/CodeGen/AArch64/hwasan-check-memaccess.ll13
-rw-r--r--llvm/utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn1
10 files changed, 251 insertions, 12 deletions
diff --git a/compiler-rt/lib/hwasan/CMakeLists.txt b/compiler-rt/lib/hwasan/CMakeLists.txt
index 1eb16e7e5cd..8fa59199ef5 100644
--- a/compiler-rt/lib/hwasan/CMakeLists.txt
+++ b/compiler-rt/lib/hwasan/CMakeLists.txt
@@ -11,6 +11,7 @@ set(HWASAN_RTL_SOURCES
hwasan_memintrinsics.cpp
hwasan_poisoning.cpp
hwasan_report.cpp
+ hwasan_tag_mismatch_aarch64.S
hwasan_thread.cpp
hwasan_thread_list.cpp
)
diff --git a/compiler-rt/lib/hwasan/hwasan_interface_internal.h b/compiler-rt/lib/hwasan/hwasan_interface_internal.h
index 315b0daef5c..3e5ac90beff 100644
--- a/compiler-rt/lib/hwasan/hwasan_interface_internal.h
+++ b/compiler-rt/lib/hwasan/hwasan_interface_internal.h
@@ -100,6 +100,9 @@ SANITIZER_INTERFACE_ATTRIBUTE
uptr __hwasan_tag_pointer(uptr p, u8 tag);
SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_tag_mismatch(uptr addr, u8 ts);
+
+SANITIZER_INTERFACE_ATTRIBUTE
u8 __hwasan_generate_tag();
// Returns the offset of the first tag mismatch or -1 if the whole range is
diff --git a/compiler-rt/lib/hwasan/hwasan_linux.cpp b/compiler-rt/lib/hwasan/hwasan_linux.cpp
index ce0c55f0668..927eed54a7e 100644
--- a/compiler-rt/lib/hwasan/hwasan_linux.cpp
+++ b/compiler-rt/lib/hwasan/hwasan_linux.cpp
@@ -374,14 +374,25 @@ static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) {
}
static void HandleTagMismatch(AccessInfo ai, uptr pc, uptr frame,
- ucontext_t *uc) {
+ ucontext_t *uc, uptr *registers_frame = nullptr) {
InternalMmapVector<BufferedStackTrace> stack_buffer(1);
BufferedStackTrace *stack = stack_buffer.data();
stack->Reset();
stack->Unwind(pc, frame, uc, common_flags()->fast_unwind_on_fatal);
+ // The second stack frame contains the failure __hwasan_check function, as
+ // we have a stack frame for the registers saved in __hwasan_tag_mismatch that
+ // we wish to ignore. This (currently) only occurs on AArch64, as x64
+ // implementations use SIGTRAP to implement the failure, and thus do not go
+ // through the stack saver.
+ if (registers_frame && stack->trace && stack->size > 0) {
+ stack->trace++;
+ stack->size--;
+ }
+
bool fatal = flags()->halt_on_error || !ai.recover;
- ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store, fatal);
+ ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store, fatal,
+ registers_frame);
}
static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
@@ -401,8 +412,10 @@ static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
return true;
}
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __hwasan_tag_mismatch(
- uptr addr, uptr access_info) {
+// Entry point stub for interoperability between __hwasan_tag_mismatch (ASM) and
+// the rest of the mismatch handling code (C++).
+extern "C" void __hwasan_tag_mismatch_stub(uptr addr, uptr access_info,
+ uptr *registers_frame) {
AccessInfo ai;
ai.is_store = access_info & 0x10;
ai.recover = false;
@@ -410,7 +423,7 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __hwasan_tag_mismatch(
ai.size = 1 << (access_info & 0xf);
HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
- (uptr)__builtin_frame_address(0), nullptr);
+ (uptr)__builtin_frame_address(0), nullptr, registers_frame);
__builtin_unreachable();
}
diff --git a/compiler-rt/lib/hwasan/hwasan_report.cpp b/compiler-rt/lib/hwasan/hwasan_report.cpp
index b1002c546f8..ddc2749c2ed 100644
--- a/compiler-rt/lib/hwasan/hwasan_report.cpp
+++ b/compiler-rt/lib/hwasan/hwasan_report.cpp
@@ -14,6 +14,7 @@
#include "hwasan.h"
#include "hwasan_allocator.h"
#include "hwasan_mapping.h"
+#include "hwasan_report.h"
#include "hwasan_thread.h"
#include "hwasan_thread_list.h"
#include "sanitizer_common/sanitizer_allocator_internal.h"
@@ -389,7 +390,7 @@ void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
}
void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
- bool is_store, bool fatal) {
+ bool is_store, bool fatal, uptr *registers_frame) {
ScopedReport R(fatal);
SavedStackAllocations current_stack_allocations(
GetCurrentThread()->stack_allocations());
@@ -430,7 +431,31 @@ void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
PrintTagsAroundAddr(tag_ptr);
+ if (registers_frame)
+ ReportRegisters(registers_frame, pc);
+
ReportErrorSummary(bug_type, stack);
}
+// See the frame breakdown defined in __hwasan_tag_mismatch (from
+// hwasan_tag_mismatch_aarch64.S).
+static const char *kDoubleSpace = " ";
+static const char *kSingleSpace = " ";
+void ReportRegisters(uptr *frame, uptr pc) {
+ Printf("Registers where the failure occurred (pc %p):", pc);
+
+ for (unsigned i = 0; i <= 30; i++) {
+ if (i % 4 == 0)
+ Printf("\n ");
+
+ // Note - manually inserting a double or single space here based on the
+ // number of digits in the register name, as our sanitizer Printf does not
+ // support padding where the content is left aligned (i.e. the format
+ // specifier "%-2d" will CHECK fail).
+ Printf(" x%d%s%016llx", i, (i < 10) ? kDoubleSpace : kSingleSpace,
+ frame[i]);
+ }
+ Printf("\n");
+}
+
} // namespace __hwasan
diff --git a/compiler-rt/lib/hwasan/hwasan_report.h b/compiler-rt/lib/hwasan/hwasan_report.h
index 6fc53ecc55c..f03eb7a69f5 100644
--- a/compiler-rt/lib/hwasan/hwasan_report.h
+++ b/compiler-rt/lib/hwasan/hwasan_report.h
@@ -22,11 +22,11 @@ namespace __hwasan {
void ReportStats();
void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
- bool is_store, bool fatal);
+ bool is_store, bool fatal, uptr *registers_frame);
void ReportInvalidFree(StackTrace *stack, uptr addr);
void ReportTailOverwritten(StackTrace *stack, uptr addr, uptr orig_size,
uptr tail_size, const u8 *expected);
-
+void ReportRegisters(uptr *registers_frame, uptr pc);
void ReportAtExitStatistics();
diff --git a/compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S b/compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S
new file mode 100644
index 00000000000..dbc9cc93bcc
--- /dev/null
+++ b/compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S
@@ -0,0 +1,108 @@
+#include "sanitizer_common/sanitizer_asm.h"
+
+// The content of this file is AArch64-only:
+#if defined(__aarch64__)
+
+// The responsibility of the HWASan entry point in compiler-rt is to primarily
+// readjust the stack from the callee and save the current register values to
+// the stack.
+// This entry point function should be called from a __hwasan_check_* symbol.
+// These are generated during a lowering pass in the backend, and are found in
+// AArch64AsmPrinter::EmitHwasanMemaccessSymbols(). Please look there for
+// further information.
+// The __hwasan_check_* caller of this function should have expanded the stack
+// and saved the previous values of x0, x1, x29, and x30. This function will
+// "consume" these saved values and treats it as part of its own stack frame.
+// In this sense, the __hwasan_check_* callee and this function "share" a stack
+// frame. This allows us to omit having unwinding information (.cfi_*) present
+// in every __hwasan_check_* function, therefore reducing binary size. This is
+// particularly important as hwasan_check_* instances are duplicated in every
+// translation unit where HWASan is enabled.
+// This function calls HwasanTagMismatch to step back into the C++ code that
+// completes the stack unwinding and error printing. This function is is not
+// permitted to return.
+
+
+// Frame from __hwasan_check_:
+// | ... |
+// | ... |
+// | Previous stack frames... |
+// +=================================+
+// | Unused 8-bytes for maintaining |
+// | 16-byte SP alignment. |
+// +---------------------------------+
+// | Return address (x30) for caller |
+// | of __hwasan_check_*. |
+// +---------------------------------+
+// | Frame address (x29) for caller |
+// | of __hwasan_check_* |
+// +---------------------------------+ <-- [SP + 232]
+// | ... |
+// | |
+// | Stack frame space for x2 - x28. |
+// | |
+// | ... |
+// +---------------------------------+ <-- [SP + 16]
+// | |
+// | Saved x1, as __hwasan_check_* |
+// | clobbers it. |
+// +---------------------------------+
+// | Saved x0, likewise above. |
+// +---------------------------------+ <-- [x30 / SP]
+
+// This function takes two arguments:
+// * x0: The address of read/write instruction that caused HWASan check fail.
+// * x1: The tag size.
+
+.section .text
+.file "hwasan_tag_mismatch_aarch64.S"
+.global __hwasan_tag_mismatch
+.type __hwasan_tag_mismatch, %function
+__hwasan_tag_mismatch:
+ CFI_STARTPROC
+
+ // Set the CFA to be the return address for caller of __hwasan_check_*. Note
+ // that we do not emit CFI predicates to describe the contents of this stack
+ // frame, as this proxy entry point should never be debugged. The contents
+ // are static and are handled by the unwinder after calling
+ // __hwasan_tag_mismatch. The frame pointer is already correctly setup
+ // by __hwasan_check_*.
+ add x29, sp, #232
+ CFI_DEF_CFA(w29, 16)
+ CFI_OFFSET(w30, -8)
+ CFI_OFFSET(w29, -16)
+
+ // Save the rest of the registers into the preallocated space left by
+ // __hwasan_check.
+ str x28, [sp, #224]
+ stp x26, x27, [sp, #208]
+ stp x24, x25, [sp, #192]
+ stp x22, x23, [sp, #176]
+ stp x20, x21, [sp, #160]
+ stp x18, x19, [sp, #144]
+ stp x16, x17, [sp, #128]
+ stp x14, x15, [sp, #112]
+ stp x12, x13, [sp, #96]
+ stp x10, x11, [sp, #80]
+ stp x8, x9, [sp, #64]
+ stp x6, x7, [sp, #48]
+ stp x4, x5, [sp, #32]
+ stp x2, x3, [sp, #16]
+
+ // Pass the address of the frame to __hwasan_tag_mismatch_stub, so that it can
+ // extract the saved registers from this frame without having to worry about
+ // finding this frame.
+ mov x2, sp
+
+ bl __hwasan_tag_mismatch_stub
+ CFI_ENDPROC
+
+.Lfunc_end0:
+ .size __hwasan_tag_mismatch, .Lfunc_end0-__hwasan_tag_mismatch
+
+.addrsig
+
+#endif // defined(__aarch64__)
+
+// We do not need executable stack.
+NO_EXEC_STACK_DIRECTIVE
diff --git a/compiler-rt/test/hwasan/TestCases/register-dump-read.c b/compiler-rt/test/hwasan/TestCases/register-dump-read.c
new file mode 100644
index 00000000000..19bf03f5d03
--- /dev/null
+++ b/compiler-rt/test/hwasan/TestCases/register-dump-read.c
@@ -0,0 +1,43 @@
+// RUN: %clang_hwasan -ffixed-x10 -ffixed-x20 -ffixed-x27 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_hwasan -ffixed-x10 -ffixed-x20 -ffixed-x27 -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_hwasan -ffixed-x10 -ffixed-x20 -ffixed-x27 -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_hwasan -ffixed-x10 -ffixed-x20 -ffixed-x27 -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// REQUIRES: aarch64-target-arch
+
+// RUN: %clang_hwasan -ffixed-x10 -ffixed-x20 -ffixed-x27 -O2 %s -o %t && not %env_hwasan_opts=fast_unwind_on_fatal=true %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_hwasan -ffixed-x10 -ffixed-x20 -ffixed-x27 -O2 %s -o %t && not %env_hwasan_opts=fast_unwind_on_fatal=false %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/hwasan_interface.h>
+
+int main() {
+ __hwasan_enable_allocator_tagging();
+ char * volatile x = (char*) malloc(10);
+ asm volatile("mov x10, #0x2222\n"
+ "mov x20, #0x3333\n"
+ "mov x27, #0x4444\n");
+ return x[16];
+
+ // CHECK: ERROR: HWAddressSanitizer:
+ // CHECK: #0 {{.*}} in main {{.*}}register-dump-read.c:[[@LINE-3]]
+
+ // Developer note: FileCheck really doesn't like when you have a regex that
+ // ends with a '}' character, e.g. the regex "[0-9]{10}" will fail, because
+ // the closing '}' fails as an "unbalanced regex". We work around this by
+ // encasing the trailing space after a register, or the end-of-line specifier.
+
+ // CHECK: Registers where the failure occurred
+ // CHECK-NEXT: x0{{[ ]+[0-9a-f]{16}[ ]}}x1{{[ ]+[0-9a-f]{16}[ ]}}x2{{[ ]+[0-9a-f]{16}[ ]}}x3{{[ ]+[0-9a-f]{16}$}}
+ // CHECK-NEXT: x4{{[ ]+[0-9a-f]{16}[ ]}}x5{{[ ]+[0-9a-f]{16}[ ]}}x6{{[ ]+[0-9a-f]{16}[ ]}}x7{{[ ]+[0-9a-f]{16}$}}
+ // CHECK-NEXT: x8{{[ ]+[0-9a-f]{16}[ ]}}x9{{[ ]+[0-9a-f]{16}[ ]}}
+ // CHECK-SAME: x10 0000000000002222
+ // CHECK-SAME: x11{{[ ]+[0-9a-f]{16}$}}
+ // CHECK-NEXT: x12{{[ ]+[0-9a-f]{16}[ ]}}x13{{[ ]+[0-9a-f]{16}[ ]}}x14{{[ ]+[0-9a-f]{16}[ ]}}x15{{[ ]+[0-9a-f]{16}$}}
+ // CHECK-NEXT: x16{{[ ]+[0-9a-f]{16}[ ]}}x17{{[ ]+[0-9a-f]{16}[ ]}}x18{{[ ]+[0-9a-f]{16}[ ]}}x19{{[ ]+[0-9a-f]{16}$}}
+ // CHECK-NEXT: x20 0000000000003333
+ // CHECK-SAME: x21{{[ ]+[0-9a-f]{16}[ ]}}x22{{[ ]+[0-9a-f]{16}[ ]}}x23{{[ ]+[0-9a-f]{16}$}}
+ // CHECK-NEXT: x24{{[ ]+[0-9a-f]{16}[ ]}}x25{{[ ]+[0-9a-f]{16}[ ]}}x26{{[ ]+[0-9a-f]{16}[ ]}}
+ // CHECK-SAME: x27 0000000000004444
+ // CHECK-NEXT: x28{{[ ]+[0-9a-f]{16}[ ]}}x29{{[ ]+[0-9a-f]{16}[ ]}}x30{{[ ]+[0-9a-f]{16}$}}
+}
diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 61196ba2d85..77a2cdce5df 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -19,6 +19,7 @@
#include "AArch64TargetObjectFile.h"
#include "InstPrinter/AArch64InstPrinter.h"
#include "MCTargetDesc/AArch64AddressingModes.h"
+#include "MCTargetDesc/AArch64MCExpr.h"
#include "MCTargetDesc/AArch64MCTargetDesc.h"
#include "MCTargetDesc/AArch64TargetStreamer.h"
#include "Utils/AArch64BaseInfo.h"
@@ -266,6 +267,9 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
MCSymbol *HwasanTagMismatchSym =
OutContext.getOrCreateSymbol("__hwasan_tag_mismatch");
+ const MCSymbolRefExpr *HwasanTagMismatchRef =
+ MCSymbolRefExpr::create(HwasanTagMismatchSym, OutContext);
+
for (auto &P : HwasanMemaccessSymbols) {
unsigned Reg = P.first.first;
uint32_t AccessInfo = P.first.second;
@@ -316,6 +320,21 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
MCInstBuilder(AArch64::RET).addReg(AArch64::LR), *STI);
OutStreamer->EmitLabel(HandleMismatchSym);
+
+ OutStreamer->EmitInstruction(MCInstBuilder(AArch64::STPXpre)
+ .addReg(AArch64::SP)
+ .addReg(AArch64::X0)
+ .addReg(AArch64::X1)
+ .addReg(AArch64::SP)
+ .addImm(-32),
+ *STI);
+ OutStreamer->EmitInstruction(MCInstBuilder(AArch64::STPXi)
+ .addReg(AArch64::FP)
+ .addReg(AArch64::LR)
+ .addReg(AArch64::SP)
+ .addImm(29),
+ *STI);
+
if (Reg != AArch64::X0)
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::ORRXrs)
.addReg(AArch64::X0)
@@ -328,10 +347,27 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
.addImm(AccessInfo)
.addImm(0),
*STI);
+
+ // Intentionally load the GOT entry and branch to it, rather than possibly
+ // late binding the function, which may clobber the registers before we have
+ // a chance to save them.
OutStreamer->EmitInstruction(
- MCInstBuilder(AArch64::B)
- .addExpr(MCSymbolRefExpr::create(HwasanTagMismatchSym, OutContext)),
+ MCInstBuilder(AArch64::ADRP)
+ .addReg(AArch64::X16)
+ .addExpr(AArch64MCExpr::create(
+ HwasanTagMismatchRef,
+ AArch64MCExpr::VariantKind::VK_GOT_PAGE, OutContext)),
*STI);
+ OutStreamer->EmitInstruction(
+ MCInstBuilder(AArch64::LDRXui)
+ .addReg(AArch64::X16)
+ .addReg(AArch64::X16)
+ .addExpr(AArch64MCExpr::create(
+ HwasanTagMismatchRef,
+ AArch64MCExpr::VariantKind::VK_GOT_LO12, OutContext)),
+ *STI);
+ OutStreamer->EmitInstruction(
+ MCInstBuilder(AArch64::BR).addReg(AArch64::X16), *STI);
}
}
diff --git a/llvm/test/CodeGen/AArch64/hwasan-check-memaccess.ll b/llvm/test/CodeGen/AArch64/hwasan-check-memaccess.ll
index 3d5340b4aa0..46a6b935201 100644
--- a/llvm/test/CodeGen/AArch64/hwasan-check-memaccess.ll
+++ b/llvm/test/CodeGen/AArch64/hwasan-check-memaccess.ll
@@ -43,8 +43,13 @@ declare void @llvm.hwasan.check.memaccess(i8*, i8*, i32)
; CHECK-NEXT: b.ne .Ltmp0
; CHECK-NEXT: ret
; CHECK-NEXT: .Ltmp0:
+; CHECK-NEXT: stp x0, x1, [sp, #-256]!
+; CHECK-NEXT: stp x29, x30, [sp, #232]
; CHECK-NEXT: mov x1, #456
-; CHECK-NEXT: b __hwasan_tag_mismatch
+; CHECK-NEXT: adrp x16, :got:__hwasan_tag_mismatch
+; CHECK-NEXT: ldr x16, [x16, :got_lo12:__hwasan_tag_mismatch]
+; CHECK-NEXT: br x16
+
; CHECK: .section .text.hot,"axG",@progbits,__hwasan_check_x1_123,comdat
; CHECK-NEXT: .type __hwasan_check_x1_123,@function
@@ -58,6 +63,10 @@ declare void @llvm.hwasan.check.memaccess(i8*, i8*, i32)
; CHECK-NEXT: b.ne .Ltmp1
; CHECK-NEXT: ret
; CHECK-NEXT: .Ltmp1:
+; CHECK-NEXT: stp x0, x1, [sp, #-256]!
+; CHECK-NEXT: stp x29, x30, [sp, #232]
; CHECK-NEXT: mov x0, x1
; CHECK-NEXT: mov x1, #123
-; CHECK-NEXT: b __hwasan_tag_mismatch
+; CHECK-NEXT: adrp x16, :got:__hwasan_tag_mismatch
+; CHECK-NEXT: ldr x16, [x16, :got_lo12:__hwasan_tag_mismatch]
+; CHECK-NEXT: br x16
diff --git a/llvm/utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn b/llvm/utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn
index f5db5cae6ac..f5da8edc25d 100644
--- a/llvm/utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn
+++ b/llvm/utils/gn/secondary/compiler-rt/lib/hwasan/BUILD.gn
@@ -54,6 +54,7 @@ source_set("sources") {
"hwasan_poisoning.h",
"hwasan_report.cpp",
"hwasan_report.h",
+ "hwasan_tag_mismatch_aarch64.S",
"hwasan_thread.cpp",
"hwasan_thread.h",
"hwasan_thread_list.cpp",
OpenPOWER on IntegriCloud