Skip to content

Commit

Permalink
hwasan: Compatibility fixes for short granules.
Browse files Browse the repository at this point in the history
We can't use short granules with stack instrumentation when targeting older
API levels because the rest of the system won't understand the short granule
tags stored in shadow memory.

Moreover, we need to be able to let old binaries (which won't understand
short granule tags) run on a new system that supports short granule
tags. Such binaries will call the __hwasan_tag_mismatch function when their
outlined checks fail. We can compensate for the binary's lack of support
for short granules by implementing the short granule part of the check in
the __hwasan_tag_mismatch function. Unfortunately we can't do anything about
inline checks, but I don't believe that we can generate these by default on
aarch64, nor did we do so when the ABI was fixed.

A new function, __hwasan_tag_mismatch_v2, is introduced that lets code
targeting the new runtime avoid redoing the short granule check. Because tag
mismatches are rare this isn't important from a performance perspective; the
main benefit is that it introduces a symbol dependency that prevents binaries
targeting the new runtime from running on older (i.e. incompatible) runtimes.

Differential Revision: https://reviews.llvm.org/D68059

llvm-svn: 373035
  • Loading branch information
pcc committed Sep 27, 2019
1 parent d5d62a9 commit c336557
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 137 deletions.
50 changes: 48 additions & 2 deletions compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions compiler-rt/test/hwasan/TestCases/stack-oob.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// RUN: %clang_hwasan_oldrt -DSIZE=2 -O0 %s -o %t && %run %t
// RUN: %clang_hwasan -DSIZE=2 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_hwasan_oldrt -DSIZE=15 -O0 %s -o %t && %run %t
// RUN: %clang_hwasan -DSIZE=15 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_hwasan_oldrt -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_hwasan -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_hwasan -DSIZE=64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_hwasan -DSIZE=0x1000 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
Expand All @@ -16,9 +20,9 @@ int f() {
}

int main() {
return f();
f();
// CHECK: READ of size 1 at
// CHECK: #0 {{.*}} in f{{.*}}stack-oob.c:15
// CHECK: #0 {{.*}} in f{{.*}}stack-oob.c:19

// CHECK: is located in stack of threa

Expand Down
5 changes: 4 additions & 1 deletion compiler-rt/test/hwasan/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
# equivalent target feature implemented on x86_64.
clang_hwasan_common_cflags += ["-mcmodel=large"]
clang_hwasan_cflags = clang_hwasan_common_cflags + ["-mllvm", "-hwasan-globals",
"-mllvm", "-hwasan-use-short-granules",
"-mllvm", "-hwasan-instrument-landing-pads=0",
"-mllvm", "-hwasan-instrument-personality-functions"]
clang_hwasan_oldrt_cflags = clang_hwasan_common_cflags + ["-mllvm", "-hwasan-instrument-landing-pads=1",
clang_hwasan_oldrt_cflags = clang_hwasan_common_cflags + ["-mllvm", "-hwasan-use-short-granules=0",
"-mllvm", "-hwasan-instrument-landing-pads=1",
"-mllvm", "-hwasan-instrument-personality-functions=0"]

clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags
Expand All @@ -31,6 +33,7 @@ def build_invocation(compile_flags):

config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) )
config.substitutions.append( ("%clang_hwasan ", build_invocation(clang_hwasan_cflags)) )
config.substitutions.append( ("%clang_hwasan_oldrt ", build_invocation(clang_hwasan_oldrt_cflags)) )
config.substitutions.append( ("%clangxx_hwasan ", build_invocation(clang_hwasan_cxxflags)) )
config.substitutions.append( ("%clangxx_hwasan_oldrt ", build_invocation(clang_hwasan_oldrt_cxxflags)) )
config.substitutions.append( ("%compiler_rt_libdir", config.compiler_rt_libdir) )
Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,8 @@ def int_load_relative: Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty, llvm_anyint_ty],

def int_hwasan_check_memaccess :
Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty, llvm_i32_ty], [IntrInaccessibleMemOnly, ImmArg<2>]>;
def int_hwasan_check_memaccess_shortgranules :
Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty, llvm_i32_ty], [IntrInaccessibleMemOnly, ImmArg<2>]>;

// Xray intrinsics
//===----------------------------------------------------------------------===//
Expand Down
166 changes: 91 additions & 75 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class AArch64AsmPrinter : public AsmPrinter {
void LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI);
void LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI);

std::map<std::pair<unsigned, uint32_t>, MCSymbol *> HwasanMemaccessSymbols;
std::map<std::tuple<unsigned, bool, uint32_t>, MCSymbol *> HwasanMemaccessSymbols;
void LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI);
void EmitHwasanMemaccessSymbols(Module &M);

Expand Down Expand Up @@ -237,15 +237,19 @@ void AArch64AsmPrinter::EmitSled(const MachineInstr &MI, SledKind Kind)

void AArch64AsmPrinter::LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI) {
Register Reg = MI.getOperand(0).getReg();
bool IsShort =
MI.getOpcode() == AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES;
uint32_t AccessInfo = MI.getOperand(1).getImm();
MCSymbol *&Sym = HwasanMemaccessSymbols[{Reg, AccessInfo}];
MCSymbol *&Sym = HwasanMemaccessSymbols[{Reg, IsShort, AccessInfo}];
if (!Sym) {
// FIXME: Make this work on non-ELF.
if (!TM.getTargetTriple().isOSBinFormatELF())
report_fatal_error("llvm.hwasan.check.memaccess only supported on ELF");

std::string SymName = "__hwasan_check_x" + utostr(Reg - AArch64::X0) + "_" +
utostr(AccessInfo);
if (IsShort)
SymName += "_short";
Sym = OutContext.getOrCreateSymbol(SymName);
}

Expand All @@ -263,15 +267,22 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
std::unique_ptr<MCSubtargetInfo> STI(
TM.getTarget().createMCSubtargetInfo(TT.str(), "", ""));

MCSymbol *HwasanTagMismatchSym =
MCSymbol *HwasanTagMismatchV1Sym =
OutContext.getOrCreateSymbol("__hwasan_tag_mismatch");
MCSymbol *HwasanTagMismatchV2Sym =
OutContext.getOrCreateSymbol("__hwasan_tag_mismatch_v2");

const MCSymbolRefExpr *HwasanTagMismatchRef =
MCSymbolRefExpr::create(HwasanTagMismatchSym, OutContext);
const MCSymbolRefExpr *HwasanTagMismatchV1Ref =
MCSymbolRefExpr::create(HwasanTagMismatchV1Sym, OutContext);
const MCSymbolRefExpr *HwasanTagMismatchV2Ref =
MCSymbolRefExpr::create(HwasanTagMismatchV2Sym, OutContext);

for (auto &P : HwasanMemaccessSymbols) {
unsigned Reg = P.first.first;
uint32_t AccessInfo = P.first.second;
unsigned Reg = std::get<0>(P.first);
bool IsShort = std::get<1>(P.first);
uint32_t AccessInfo = std::get<2>(P.first);
const MCSymbolRefExpr *HwasanTagMismatchRef =
IsShort ? HwasanTagMismatchV2Ref : HwasanTagMismatchV1Ref;
MCSymbol *Sym = P.second;

OutStreamer->SwitchSection(OutContext.getELFSection(
Expand Down Expand Up @@ -304,82 +315,86 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
.addReg(Reg)
.addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR, 56)),
*STI);
MCSymbol *HandlePartialSym = OutContext.createTempSymbol();
MCSymbol *HandleMismatchOrPartialSym = OutContext.createTempSymbol();
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::NE)
.addExpr(MCSymbolRefExpr::create(HandlePartialSym, OutContext)),
.addExpr(MCSymbolRefExpr::create(HandleMismatchOrPartialSym,
OutContext)),
*STI);
MCSymbol *ReturnSym = OutContext.createTempSymbol();
OutStreamer->EmitLabel(ReturnSym);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::RET).addReg(AArch64::LR), *STI);
OutStreamer->EmitLabel(HandleMismatchOrPartialSym);

OutStreamer->EmitLabel(HandlePartialSym);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWri)
.addReg(AArch64::WZR)
.addReg(AArch64::W16)
.addImm(15)
.addImm(0),
*STI);
MCSymbol *HandleMismatchSym = OutContext.createTempSymbol();
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::HI)
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
*STI);

OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::ANDXri)
.addReg(AArch64::X17)
.addReg(Reg)
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
*STI);
unsigned Size = 1 << (AccessInfo & 0xf);
if (Size != 1)
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::ADDXri)
.addReg(AArch64::X17)
.addReg(AArch64::X17)
.addImm(Size - 1)
if (IsShort) {
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWri)
.addReg(AArch64::WZR)
.addReg(AArch64::W16)
.addImm(15)
.addImm(0),
*STI);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWrs)
.addReg(AArch64::WZR)
.addReg(AArch64::W16)
.addReg(AArch64::W17)
.addImm(0),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::LS)
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
*STI);

OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::ORRXri)
.addReg(AArch64::X16)
.addReg(Reg)
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
*STI);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::LDRBBui)
.addReg(AArch64::W16)
.addReg(AArch64::X16)
.addImm(0),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::SUBSXrs)
.addReg(AArch64::XZR)
.addReg(AArch64::X16)
.addReg(Reg)
.addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR, 56)),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::EQ)
.addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext)),
*STI);
MCSymbol *HandleMismatchSym = OutContext.createTempSymbol();
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::HI)
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
*STI);

OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::ANDXri)
.addReg(AArch64::X17)
.addReg(Reg)
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
*STI);
unsigned Size = 1 << (AccessInfo & 0xf);
if (Size != 1)
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::ADDXri)
.addReg(AArch64::X17)
.addReg(AArch64::X17)
.addImm(Size - 1)
.addImm(0),
*STI);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWrs)
.addReg(AArch64::WZR)
.addReg(AArch64::W16)
.addReg(AArch64::W17)
.addImm(0),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::LS)
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
*STI);

OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::ORRXri)
.addReg(AArch64::X16)
.addReg(Reg)
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
*STI);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::LDRBBui)
.addReg(AArch64::W16)
.addReg(AArch64::X16)
.addImm(0),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::SUBSXrs)
.addReg(AArch64::XZR)
.addReg(AArch64::X16)
.addReg(Reg)
.addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR, 56)),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::EQ)
.addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext)),
*STI);

OutStreamer->EmitLabel(HandleMismatchSym);
}

OutStreamer->EmitLabel(HandleMismatchSym);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::STPXpre)
.addReg(AArch64::SP)
.addReg(AArch64::X0)
Expand Down Expand Up @@ -414,16 +429,16 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
MCInstBuilder(AArch64::ADRP)
.addReg(AArch64::X16)
.addExpr(AArch64MCExpr::create(
HwasanTagMismatchRef,
AArch64MCExpr::VariantKind::VK_GOT_PAGE, OutContext)),
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)),
HwasanTagMismatchRef, AArch64MCExpr::VariantKind::VK_GOT_LO12,
OutContext)),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::BR).addReg(AArch64::X16), *STI);
Expand Down Expand Up @@ -1096,6 +1111,7 @@ void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) {
return;

case AArch64::HWASAN_CHECK_MEMACCESS:
case AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES:
LowerHWASAN_CHECK_MEMACCESS(*MI);
return;

Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/AArch64/AArch64InstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,10 @@ def HWASAN_CHECK_MEMACCESS : Pseudo<
(outs), (ins GPR64noip:$ptr, i32imm:$accessinfo),
[(int_hwasan_check_memaccess X9, GPR64noip:$ptr, (i32 timm:$accessinfo))]>,
Sched<[]>;
def HWASAN_CHECK_MEMACCESS_SHORTGRANULES : Pseudo<
(outs), (ins GPR64noip:$ptr, i32imm:$accessinfo),
[(int_hwasan_check_memaccess_shortgranules X9, GPR64noip:$ptr, (i32 timm:$accessinfo))]>,
Sched<[]>;
}

// The cycle counter PMC register is PMCCNTR_EL0.
Expand Down
Loading

0 comments on commit c336557

Please sign in to comment.