Skip to content

Commit c336557

Browse files
committed
hwasan: Compatibility fixes for short granules.
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
1 parent d5d62a9 commit c336557

File tree

12 files changed

+245
-137
lines changed

12 files changed

+245
-137
lines changed

compiler-rt/lib/hwasan/hwasan_tag_mismatch_aarch64.S

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,60 @@
5151
// +---------------------------------+ <-- [x30 / SP]
5252

5353
// This function takes two arguments:
54-
// * x0: The address of read/write instruction that caused HWASan check fail.
55-
// * x1: The tag size.
54+
// * x0: The data address.
55+
// * x1: The encoded access info for the failing access.
5656

57+
// This function has two entry points. The first, __hwasan_tag_mismatch, is used
58+
// by clients that were compiled without short tag checks (i.e. binaries built
59+
// by older compilers and binaries targeting older runtimes). In this case the
60+
// outlined tag check will be missing the code handling short tags (which won't
61+
// be used in the binary's own stack variables but may be used on the heap
62+
// or stack variables in other binaries), so the check needs to be done here.
63+
//
64+
// The second, __hwasan_tag_mismatch_v2, is used by binaries targeting newer
65+
// runtimes. This entry point bypasses the short tag check since it will have
66+
// already been done as part of the outlined tag check. Since tag mismatches are
67+
// uncommon, there isn't a significant performance benefit to being able to
68+
// bypass the check; the main benefits are that we can sometimes avoid
69+
// clobbering the x17 register in error reports, and that the program will have
70+
// a runtime dependency on the __hwasan_tag_mismatch_v2 symbol therefore it will
71+
// fail to start up given an older (i.e. incompatible) runtime.
5772
.section .text
5873
.file "hwasan_tag_mismatch_aarch64.S"
5974
.global __hwasan_tag_mismatch
6075
.type __hwasan_tag_mismatch, %function
6176
__hwasan_tag_mismatch:
77+
// Compute the granule position one past the end of the access.
78+
mov x16, #1
79+
and x17, x1, #0xf
80+
lsl x16, x16, x17
81+
and x17, x0, #0xf
82+
add x17, x16, x17
83+
84+
// Load the shadow byte again and check whether it is a short tag within the
85+
// range of the granule position computed above.
86+
ubfx x16, x0, #4, #52
87+
ldrb w16, [x9, x16]
88+
cmp w16, #0xf
89+
b.hi __hwasan_tag_mismatch_v2
90+
cmp w16, w17
91+
b.lo __hwasan_tag_mismatch_v2
92+
93+
// Load the real tag from the last byte of the granule and compare against
94+
// the pointer tag.
95+
orr x16, x0, #0xf
96+
ldrb w16, [x16]
97+
cmp x16, x0, lsr #56
98+
b.ne __hwasan_tag_mismatch_v2
99+
100+
// Restore x0, x1 and sp to their values from before the __hwasan_tag_mismatch
101+
// call and resume execution.
102+
ldp x0, x1, [sp], #256
103+
ret
104+
105+
.global __hwasan_tag_mismatch_v2
106+
.type __hwasan_tag_mismatch_v2, %function
107+
__hwasan_tag_mismatch_v2:
62108
CFI_STARTPROC
63109

64110
// Set the CFA to be the return address for caller of __hwasan_check_*. Note

compiler-rt/test/hwasan/TestCases/stack-oob.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
// RUN: %clang_hwasan_oldrt -DSIZE=2 -O0 %s -o %t && %run %t
2+
// RUN: %clang_hwasan -DSIZE=2 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
3+
// RUN: %clang_hwasan_oldrt -DSIZE=15 -O0 %s -o %t && %run %t
14
// RUN: %clang_hwasan -DSIZE=15 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
5+
// RUN: %clang_hwasan_oldrt -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
26
// RUN: %clang_hwasan -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
37
// RUN: %clang_hwasan -DSIZE=64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
48
// RUN: %clang_hwasan -DSIZE=0x1000 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
@@ -16,9 +20,9 @@ int f() {
1620
}
1721

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

2327
// CHECK: is located in stack of threa
2428

compiler-rt/test/hwasan/lit.cfg.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
# equivalent target feature implemented on x86_64.
1919
clang_hwasan_common_cflags += ["-mcmodel=large"]
2020
clang_hwasan_cflags = clang_hwasan_common_cflags + ["-mllvm", "-hwasan-globals",
21+
"-mllvm", "-hwasan-use-short-granules",
2122
"-mllvm", "-hwasan-instrument-landing-pads=0",
2223
"-mllvm", "-hwasan-instrument-personality-functions"]
23-
clang_hwasan_oldrt_cflags = clang_hwasan_common_cflags + ["-mllvm", "-hwasan-instrument-landing-pads=1",
24+
clang_hwasan_oldrt_cflags = clang_hwasan_common_cflags + ["-mllvm", "-hwasan-use-short-granules=0",
25+
"-mllvm", "-hwasan-instrument-landing-pads=1",
2426
"-mllvm", "-hwasan-instrument-personality-functions=0"]
2527

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

3234
config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) )
3335
config.substitutions.append( ("%clang_hwasan ", build_invocation(clang_hwasan_cflags)) )
36+
config.substitutions.append( ("%clang_hwasan_oldrt ", build_invocation(clang_hwasan_oldrt_cflags)) )
3437
config.substitutions.append( ("%clangxx_hwasan ", build_invocation(clang_hwasan_cxxflags)) )
3538
config.substitutions.append( ("%clangxx_hwasan_oldrt ", build_invocation(clang_hwasan_oldrt_cxxflags)) )
3639
config.substitutions.append( ("%compiler_rt_libdir", config.compiler_rt_libdir) )

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,8 @@ def int_load_relative: Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty, llvm_anyint_ty],
11361136

11371137
def int_hwasan_check_memaccess :
11381138
Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty, llvm_i32_ty], [IntrInaccessibleMemOnly, ImmArg<2>]>;
1139+
def int_hwasan_check_memaccess_shortgranules :
1140+
Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty, llvm_i32_ty], [IntrInaccessibleMemOnly, ImmArg<2>]>;
11391141

11401142
// Xray intrinsics
11411143
//===----------------------------------------------------------------------===//

llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp

Lines changed: 91 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class AArch64AsmPrinter : public AsmPrinter {
9999
void LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI);
100100
void LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI);
101101

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

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

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

247249
std::string SymName = "__hwasan_check_x" + utostr(Reg - AArch64::X0) + "_" +
248250
utostr(AccessInfo);
251+
if (IsShort)
252+
SymName += "_short";
249253
Sym = OutContext.getOrCreateSymbol(SymName);
250254
}
251255

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

266-
MCSymbol *HwasanTagMismatchSym =
270+
MCSymbol *HwasanTagMismatchV1Sym =
267271
OutContext.getOrCreateSymbol("__hwasan_tag_mismatch");
272+
MCSymbol *HwasanTagMismatchV2Sym =
273+
OutContext.getOrCreateSymbol("__hwasan_tag_mismatch_v2");
268274

269-
const MCSymbolRefExpr *HwasanTagMismatchRef =
270-
MCSymbolRefExpr::create(HwasanTagMismatchSym, OutContext);
275+
const MCSymbolRefExpr *HwasanTagMismatchV1Ref =
276+
MCSymbolRefExpr::create(HwasanTagMismatchV1Sym, OutContext);
277+
const MCSymbolRefExpr *HwasanTagMismatchV2Ref =
278+
MCSymbolRefExpr::create(HwasanTagMismatchV2Sym, OutContext);
271279

272280
for (auto &P : HwasanMemaccessSymbols) {
273-
unsigned Reg = P.first.first;
274-
uint32_t AccessInfo = P.first.second;
281+
unsigned Reg = std::get<0>(P.first);
282+
bool IsShort = std::get<1>(P.first);
283+
uint32_t AccessInfo = std::get<2>(P.first);
284+
const MCSymbolRefExpr *HwasanTagMismatchRef =
285+
IsShort ? HwasanTagMismatchV2Ref : HwasanTagMismatchV1Ref;
275286
MCSymbol *Sym = P.second;
276287

277288
OutStreamer->SwitchSection(OutContext.getELFSection(
@@ -304,82 +315,86 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
304315
.addReg(Reg)
305316
.addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR, 56)),
306317
*STI);
307-
MCSymbol *HandlePartialSym = OutContext.createTempSymbol();
318+
MCSymbol *HandleMismatchOrPartialSym = OutContext.createTempSymbol();
308319
OutStreamer->EmitInstruction(
309320
MCInstBuilder(AArch64::Bcc)
310321
.addImm(AArch64CC::NE)
311-
.addExpr(MCSymbolRefExpr::create(HandlePartialSym, OutContext)),
322+
.addExpr(MCSymbolRefExpr::create(HandleMismatchOrPartialSym,
323+
OutContext)),
312324
*STI);
313325
MCSymbol *ReturnSym = OutContext.createTempSymbol();
314326
OutStreamer->EmitLabel(ReturnSym);
315327
OutStreamer->EmitInstruction(
316328
MCInstBuilder(AArch64::RET).addReg(AArch64::LR), *STI);
329+
OutStreamer->EmitLabel(HandleMismatchOrPartialSym);
317330

318-
OutStreamer->EmitLabel(HandlePartialSym);
319-
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWri)
320-
.addReg(AArch64::WZR)
321-
.addReg(AArch64::W16)
322-
.addImm(15)
323-
.addImm(0),
324-
*STI);
325-
MCSymbol *HandleMismatchSym = OutContext.createTempSymbol();
326-
OutStreamer->EmitInstruction(
327-
MCInstBuilder(AArch64::Bcc)
328-
.addImm(AArch64CC::HI)
329-
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
330-
*STI);
331-
332-
OutStreamer->EmitInstruction(
333-
MCInstBuilder(AArch64::ANDXri)
334-
.addReg(AArch64::X17)
335-
.addReg(Reg)
336-
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
337-
*STI);
338-
unsigned Size = 1 << (AccessInfo & 0xf);
339-
if (Size != 1)
340-
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::ADDXri)
341-
.addReg(AArch64::X17)
342-
.addReg(AArch64::X17)
343-
.addImm(Size - 1)
331+
if (IsShort) {
332+
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWri)
333+
.addReg(AArch64::WZR)
334+
.addReg(AArch64::W16)
335+
.addImm(15)
344336
.addImm(0),
345337
*STI);
346-
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWrs)
347-
.addReg(AArch64::WZR)
348-
.addReg(AArch64::W16)
349-
.addReg(AArch64::W17)
350-
.addImm(0),
351-
*STI);
352-
OutStreamer->EmitInstruction(
353-
MCInstBuilder(AArch64::Bcc)
354-
.addImm(AArch64CC::LS)
355-
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
356-
*STI);
357-
358-
OutStreamer->EmitInstruction(
359-
MCInstBuilder(AArch64::ORRXri)
360-
.addReg(AArch64::X16)
361-
.addReg(Reg)
362-
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
363-
*STI);
364-
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::LDRBBui)
365-
.addReg(AArch64::W16)
366-
.addReg(AArch64::X16)
367-
.addImm(0),
368-
*STI);
369-
OutStreamer->EmitInstruction(
370-
MCInstBuilder(AArch64::SUBSXrs)
371-
.addReg(AArch64::XZR)
372-
.addReg(AArch64::X16)
373-
.addReg(Reg)
374-
.addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR, 56)),
375-
*STI);
376-
OutStreamer->EmitInstruction(
377-
MCInstBuilder(AArch64::Bcc)
378-
.addImm(AArch64CC::EQ)
379-
.addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext)),
380-
*STI);
338+
MCSymbol *HandleMismatchSym = OutContext.createTempSymbol();
339+
OutStreamer->EmitInstruction(
340+
MCInstBuilder(AArch64::Bcc)
341+
.addImm(AArch64CC::HI)
342+
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
343+
*STI);
344+
345+
OutStreamer->EmitInstruction(
346+
MCInstBuilder(AArch64::ANDXri)
347+
.addReg(AArch64::X17)
348+
.addReg(Reg)
349+
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
350+
*STI);
351+
unsigned Size = 1 << (AccessInfo & 0xf);
352+
if (Size != 1)
353+
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::ADDXri)
354+
.addReg(AArch64::X17)
355+
.addReg(AArch64::X17)
356+
.addImm(Size - 1)
357+
.addImm(0),
358+
*STI);
359+
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWrs)
360+
.addReg(AArch64::WZR)
361+
.addReg(AArch64::W16)
362+
.addReg(AArch64::W17)
363+
.addImm(0),
364+
*STI);
365+
OutStreamer->EmitInstruction(
366+
MCInstBuilder(AArch64::Bcc)
367+
.addImm(AArch64CC::LS)
368+
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
369+
*STI);
370+
371+
OutStreamer->EmitInstruction(
372+
MCInstBuilder(AArch64::ORRXri)
373+
.addReg(AArch64::X16)
374+
.addReg(Reg)
375+
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
376+
*STI);
377+
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::LDRBBui)
378+
.addReg(AArch64::W16)
379+
.addReg(AArch64::X16)
380+
.addImm(0),
381+
*STI);
382+
OutStreamer->EmitInstruction(
383+
MCInstBuilder(AArch64::SUBSXrs)
384+
.addReg(AArch64::XZR)
385+
.addReg(AArch64::X16)
386+
.addReg(Reg)
387+
.addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR, 56)),
388+
*STI);
389+
OutStreamer->EmitInstruction(
390+
MCInstBuilder(AArch64::Bcc)
391+
.addImm(AArch64CC::EQ)
392+
.addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext)),
393+
*STI);
394+
395+
OutStreamer->EmitLabel(HandleMismatchSym);
396+
}
381397

382-
OutStreamer->EmitLabel(HandleMismatchSym);
383398
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::STPXpre)
384399
.addReg(AArch64::SP)
385400
.addReg(AArch64::X0)
@@ -414,16 +429,16 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
414429
MCInstBuilder(AArch64::ADRP)
415430
.addReg(AArch64::X16)
416431
.addExpr(AArch64MCExpr::create(
417-
HwasanTagMismatchRef,
418-
AArch64MCExpr::VariantKind::VK_GOT_PAGE, OutContext)),
432+
HwasanTagMismatchRef, AArch64MCExpr::VariantKind::VK_GOT_PAGE,
433+
OutContext)),
419434
*STI);
420435
OutStreamer->EmitInstruction(
421436
MCInstBuilder(AArch64::LDRXui)
422437
.addReg(AArch64::X16)
423438
.addReg(AArch64::X16)
424439
.addExpr(AArch64MCExpr::create(
425-
HwasanTagMismatchRef,
426-
AArch64MCExpr::VariantKind::VK_GOT_LO12, OutContext)),
440+
HwasanTagMismatchRef, AArch64MCExpr::VariantKind::VK_GOT_LO12,
441+
OutContext)),
427442
*STI);
428443
OutStreamer->EmitInstruction(
429444
MCInstBuilder(AArch64::BR).addReg(AArch64::X16), *STI);
@@ -1096,6 +1111,7 @@ void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) {
10961111
return;
10971112

10981113
case AArch64::HWASAN_CHECK_MEMACCESS:
1114+
case AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES:
10991115
LowerHWASAN_CHECK_MEMACCESS(*MI);
11001116
return;
11011117

llvm/lib/Target/AArch64/AArch64InstrInfo.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,10 @@ def HWASAN_CHECK_MEMACCESS : Pseudo<
800800
(outs), (ins GPR64noip:$ptr, i32imm:$accessinfo),
801801
[(int_hwasan_check_memaccess X9, GPR64noip:$ptr, (i32 timm:$accessinfo))]>,
802802
Sched<[]>;
803+
def HWASAN_CHECK_MEMACCESS_SHORTGRANULES : Pseudo<
804+
(outs), (ins GPR64noip:$ptr, i32imm:$accessinfo),
805+
[(int_hwasan_check_memaccess_shortgranules X9, GPR64noip:$ptr, (i32 timm:$accessinfo))]>,
806+
Sched<[]>;
803807
}
804808

805809
// The cycle counter PMC register is PMCCNTR_EL0.

0 commit comments

Comments
 (0)