69 changes: 45 additions & 24 deletions llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ static cl::opt<bool>
ClInstrumentMemIntrinsics("hwasan-instrument-mem-intrinsics",
cl::desc("instrument memory intrinsics"),
cl::Hidden, cl::init(true));

static cl::opt<bool> ClInlineAllChecks("hwasan-inline-all-checks",
cl::desc("inline all checks"),
cl::Hidden, cl::init(false));

namespace {

/// An instrumentation pass implementing detection of addressability bugs
Expand All @@ -181,8 +186,9 @@ class HWAddressSanitizer : public FunctionPass {
Value *getDynamicShadowNonTls(IRBuilder<> &IRB);

void untagPointerOperand(Instruction *I, Value *Addr);
Value *memToShadow(Value *Shadow, Type *Ty, IRBuilder<> &IRB);
void instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
Value *shadowBase();
Value *memToShadow(Value *Shadow, IRBuilder<> &IRB);
void instrumentMemAccessInline(Value *Ptr, bool IsWrite,
unsigned AccessSizeIndex,
Instruction *InsertBefore);
void instrumentMemIntrinsic(MemIntrinsic *MI);
Expand Down Expand Up @@ -252,6 +258,7 @@ class HWAddressSanitizer : public FunctionPass {
Type *IntptrTy;
Type *Int8PtrTy;
Type *Int8Ty;
Type *Int32Ty;

bool CompileKernel;
bool Recover;
Expand Down Expand Up @@ -307,6 +314,7 @@ bool HWAddressSanitizer::doInitialization(Module &M) {
IntptrTy = IRB.getIntPtrTy(DL);
Int8PtrTy = IRB.getInt8PtrTy();
Int8Ty = IRB.getInt8Ty();
Int32Ty = IRB.getInt32Ty();

HwasanCtorFunction = nullptr;
if (!CompileKernel) {
Expand Down Expand Up @@ -403,16 +411,16 @@ Value *HWAddressSanitizer::getDynamicShadowNonTls(IRBuilder<> &IRB) {

if (Mapping.InGlobal) {
// An empty inline asm with input reg == output reg.
// An opaque pointer-to-int cast, basically.
// An opaque no-op cast, basically.
InlineAsm *Asm = InlineAsm::get(
FunctionType::get(IntptrTy, {ShadowGlobal->getType()}, false),
FunctionType::get(Int8PtrTy, {ShadowGlobal->getType()}, false),
StringRef(""), StringRef("=r,0"),
/*hasSideEffects=*/false);
return IRB.CreateCall(Asm, {ShadowGlobal}, ".hwasan.shadow");
} else {
Value *GlobalDynamicAddress =
IRB.GetInsertBlock()->getParent()->getParent()->getOrInsertGlobal(
kHwasanShadowMemoryDynamicAddress, IntptrTy);
kHwasanShadowMemoryDynamicAddress, Int8PtrTy);
return IRB.CreateLoad(GlobalDynamicAddress);
}
}
Expand Down Expand Up @@ -505,29 +513,44 @@ void HWAddressSanitizer::untagPointerOperand(Instruction *I, Value *Addr) {
I->setOperand(getPointerOperandIndex(I), UntaggedPtr);
}

Value *HWAddressSanitizer::memToShadow(Value *Mem, Type *Ty, IRBuilder<> &IRB) {
Value *HWAddressSanitizer::shadowBase() {
if (LocalDynamicShadow)
return LocalDynamicShadow;
return ConstantExpr::getIntToPtr(ConstantInt::get(IntptrTy, Mapping.Offset),
Int8PtrTy);
}

Value *HWAddressSanitizer::memToShadow(Value *Mem, IRBuilder<> &IRB) {
// Mem >> Scale
Value *Shadow = IRB.CreateLShr(Mem, Mapping.Scale);
if (Mapping.Offset == 0)
return Shadow;
return IRB.CreateIntToPtr(Shadow, Int8PtrTy);
// (Mem >> Scale) + Offset
Value *ShadowBase;
if (LocalDynamicShadow)
ShadowBase = LocalDynamicShadow;
else
ShadowBase = ConstantInt::get(Ty, Mapping.Offset);
return IRB.CreateAdd(Shadow, ShadowBase);
return IRB.CreateGEP(Int8Ty, shadowBase(), Shadow);
}

void HWAddressSanitizer::instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
void HWAddressSanitizer::instrumentMemAccessInline(Value *Ptr, bool IsWrite,
unsigned AccessSizeIndex,
Instruction *InsertBefore) {
const int64_t AccessInfo = Recover * 0x20 + IsWrite * 0x10 + AccessSizeIndex;
IRBuilder<> IRB(InsertBefore);

if (!ClInlineAllChecks && TargetTriple.isAArch64() &&
TargetTriple.isOSBinFormatELF() && !Recover) {
Module *M = IRB.GetInsertBlock()->getParent()->getParent();
Ptr = IRB.CreateBitCast(Ptr, Int8PtrTy);
IRB.CreateCall(
Intrinsic::getDeclaration(M, Intrinsic::hwasan_check_memaccess),
{shadowBase(), Ptr, ConstantInt::get(Int32Ty, AccessInfo)});
return;
}

Value *PtrLong = IRB.CreatePointerCast(Ptr, IntptrTy);
Value *PtrTag = IRB.CreateTrunc(IRB.CreateLShr(PtrLong, kPointerTagShift),
IRB.getInt8Ty());
Value *AddrLong = untagPointer(IRB, PtrLong);
Value *ShadowLong = memToShadow(AddrLong, PtrLong->getType(), IRB);
Value *MemTag = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowLong, Int8PtrTy));
Value *Shadow = memToShadow(AddrLong, IRB);
Value *MemTag = IRB.CreateLoad(Shadow);
Value *TagMismatch = IRB.CreateICmpNE(PtrTag, MemTag);

int matchAllTag = ClMatchAllTag.getNumOccurrences() > 0 ?
Expand All @@ -543,7 +566,6 @@ void HWAddressSanitizer::instrumentMemAccessInline(Value *PtrLong, bool IsWrite,
MDBuilder(*C).createBranchWeights(1, 100000));

IRB.SetInsertPoint(CheckTerm);
const int64_t AccessInfo = Recover * 0x20 + IsWrite * 0x10 + AccessSizeIndex;
InlineAsm *Asm;
switch (TargetTriple.getArch()) {
case Triple::x86_64:
Expand Down Expand Up @@ -609,21 +631,21 @@ bool HWAddressSanitizer::instrumentMemAccess(Instruction *I) {
return false; //FIXME

IRBuilder<> IRB(I);
Value *AddrLong = IRB.CreatePointerCast(Addr, IntptrTy);
if (isPowerOf2_64(TypeSize) &&
(TypeSize / 8 <= (1UL << (kNumberOfAccessSizes - 1))) &&
(Alignment >= (1UL << Mapping.Scale) || Alignment == 0 ||
Alignment >= TypeSize / 8)) {
size_t AccessSizeIndex = TypeSizeToSizeIndex(TypeSize);
if (ClInstrumentWithCalls) {
IRB.CreateCall(HwasanMemoryAccessCallback[IsWrite][AccessSizeIndex],
AddrLong);
IRB.CreatePointerCast(Addr, IntptrTy));
} else {
instrumentMemAccessInline(AddrLong, IsWrite, AccessSizeIndex, I);
instrumentMemAccessInline(Addr, IsWrite, AccessSizeIndex, I);
}
} else {
IRB.CreateCall(HwasanMemoryAccessCallbackSized[IsWrite],
{AddrLong, ConstantInt::get(IntptrTy, TypeSize / 8)});
{IRB.CreatePointerCast(Addr, IntptrTy),
ConstantInt::get(IntptrTy, TypeSize / 8)});
}
untagPointerOperand(I, Addr);

Expand Down Expand Up @@ -654,9 +676,7 @@ bool HWAddressSanitizer::tagAlloca(IRBuilder<> &IRB, AllocaInst *AI,
ConstantInt::get(IntptrTy, Size)});
} else {
size_t ShadowSize = Size >> Mapping.Scale;
Value *ShadowPtr = IRB.CreateIntToPtr(
memToShadow(IRB.CreatePointerCast(AI, IntptrTy), AI->getType(), IRB),
Int8PtrTy);
Value *ShadowPtr = memToShadow(IRB.CreatePointerCast(AI, IntptrTy), IRB);
// If this memset is not inlined, it will be intercepted in the hwasan
// runtime library. That's OK, because the interceptor skips the checks if
// the address is in the shadow region.
Expand Down Expand Up @@ -883,6 +903,7 @@ Value *HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB,
ThreadLongMaybeUntagged,
ConstantInt::get(IntptrTy, (1ULL << kShadowBaseAlignment) - 1)),
ConstantInt::get(IntptrTy, 1), "hwasan.shadow");
ShadowBase = IRB.CreateIntToPtr(ShadowBase, Int8PtrTy);
return ShadowBase;
}

Expand Down
63 changes: 63 additions & 0 deletions llvm/test/CodeGen/AArch64/hwasan-check-memaccess.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
; RUN: llc < %s | FileCheck %s

target triple = "aarch64--linux-android"

define i8* @f1(i8* %x0, i8* %x1) {
; CHECK: f1:
; CHECK: str x30, [sp, #-16]!
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: .cfi_offset w30, -16
; CHECK-NEXT: mov x9, x0
; CHECK-NEXT: bl __hwasan_check_x1_123
; CHECK-NEXT: mov x0, x1
; CHECK-NEXT: ldr x30, [sp], #16
; CHECK-NEXT: ret
call void @llvm.hwasan.check.memaccess(i8* %x0, i8* %x1, i32 123)
ret i8* %x1
}

define i8* @f2(i8* %x0, i8* %x1) {
; CHECK: f2:
; CHECK: str x30, [sp, #-16]!
; CHECK-NEXT: .cfi_def_cfa_offset 16
; CHECK-NEXT: .cfi_offset w30, -16
; CHECK-NEXT: mov x9, x1
; CHECK-NEXT: bl __hwasan_check_x0_456
; CHECK-NEXT: ldr x30, [sp], #16
; CHECK-NEXT: ret
call void @llvm.hwasan.check.memaccess(i8* %x1, i8* %x0, i32 456)
ret i8* %x0
}

declare void @llvm.hwasan.check.memaccess(i8*, i8*, i32)

; CHECK: .section .text.hot,"axG",@progbits,__hwasan_check_x0_456,comdat
; CHECK-NEXT: .type __hwasan_check_x0_456,@function
; CHECK-NEXT: .weak __hwasan_check_x0_456
; CHECK-NEXT: .hidden __hwasan_check_x0_456
; CHECK-NEXT: __hwasan_check_x0_456:
; CHECK-NEXT: ubfx x16, x0, #4, #52
; CHECK-NEXT: ldrb w16, [x9, x16]
; CHECK-NEXT: lsr x17, x0, #56
; CHECK-NEXT: cmp w16, w17
; CHECK-NEXT: b.ne .Ltmp0
; CHECK-NEXT: ret
; CHECK-NEXT: .Ltmp0:
; CHECK-NEXT: mov x1, #456
; CHECK-NEXT: b __hwasan_tag_mismatch

; CHECK: .section .text.hot,"axG",@progbits,__hwasan_check_x1_123,comdat
; CHECK-NEXT: .type __hwasan_check_x1_123,@function
; CHECK-NEXT: .weak __hwasan_check_x1_123
; CHECK-NEXT: .hidden __hwasan_check_x1_123
; CHECK-NEXT: __hwasan_check_x1_123:
; CHECK-NEXT: ubfx x16, x1, #4, #52
; CHECK-NEXT: ldrb w16, [x9, x16]
; CHECK-NEXT: lsr x17, x1, #56
; CHECK-NEXT: cmp w16, w17
; CHECK-NEXT: b.ne .Ltmp1
; CHECK-NEXT: ret
; CHECK-NEXT: .Ltmp1:
; CHECK-NEXT: mov x0, x1
; CHECK-NEXT: mov x1, #123
; CHECK-NEXT: b __hwasan_tag_mismatch
6 changes: 2 additions & 4 deletions llvm/test/Instrumentation/HWAddressSanitizer/alloca.ll
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ define void @test_alloca() sanitize_hwaddress {
; CHECK: %[[X_TAG2:[^ ]*]] = trunc i64 %[[X_TAG]] to i8
; CHECK: %[[E:[^ ]*]] = ptrtoint i32* %[[X]] to i64
; CHECK: %[[F:[^ ]*]] = lshr i64 %[[E]], 4
; DYNAMIC-SHADOW: %[[F_DYN:[^ ]*]] = add i64 %[[F]], %.hwasan.shadow
; DYNAMIC-SHADOW: %[[X_SHADOW:[^ ]*]] = inttoptr i64 %[[F_DYN]] to i8*
; DYNAMIC-SHADOW: %[[X_SHADOW:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %[[F]]
; ZERO-BASED-SHADOW: %[[X_SHADOW:[^ ]*]] = inttoptr i64 %[[F]] to i8*
; CHECK: call void @llvm.memset.p0i8.i64(i8* align 1 %[[X_SHADOW]], i8 %[[X_TAG2]], i64 1, i1 false)
; CHECK: call void @use32(i32* nonnull %[[X_HWASAN]])
Expand All @@ -36,8 +35,7 @@ define void @test_alloca() sanitize_hwaddress {
; UAR-TAGS: %[[X_TAG_UAR:[^ ]*]] = trunc i64 %[[BASE_TAG_COMPL]] to i8
; CHECK: %[[E2:[^ ]*]] = ptrtoint i32* %[[X]] to i64
; CHECK: %[[F2:[^ ]*]] = lshr i64 %[[E2]], 4
; DYNAMIC-SHADOW: %[[F2_DYN:[^ ]*]] = add i64 %[[F2]], %.hwasan.shadow
; DYNAMIC-SHADOW: %[[X_SHADOW2:[^ ]*]] = inttoptr i64 %[[F2_DYN]] to i8*
; DYNAMIC-SHADOW: %[[X_SHADOW2:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %[[F2]]
; ZERO-BASED-SHADOW: %[[X_SHADOW2:[^ ]*]] = inttoptr i64 %[[F2]] to i8*
; NO-UAR-TAGS: call void @llvm.memset.p0i8.i64(i8* align 1 %[[X_SHADOW2]], i8 0, i64 1, i1 false)
; UAR-TAGS: call void @llvm.memset.p0i8.i64(i8* align 1 %[[X_SHADOW2]], i8 %[[X_TAG_UAR]], i64 1, i1 false)
Expand Down
8 changes: 4 additions & 4 deletions llvm/test/Instrumentation/HWAddressSanitizer/atomic.ll
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ target triple = "aarch64--linux-android"

define void @atomicrmw(i64* %ptr) sanitize_hwaddress {
; CHECK-LABEL: @atomicrmw(
; CHECK: lshr i64 %[[A:[^ ]*]], 56
; CHECK: call void asm sideeffect "brk #2323", "{x0}"(i64 %[[A]])
; CHECK: [[PTRI8:%[^ ]*]] = bitcast i64* %ptr to i8*
; CHECK: call void @llvm.hwasan.check.memaccess({{.*}}, i8* [[PTRI8]], i32 19)
; CHECK: atomicrmw add i64* %ptr, i64 1 seq_cst
; CHECK: ret void

Expand All @@ -19,8 +19,8 @@ entry:

define void @cmpxchg(i64* %ptr, i64 %compare_to, i64 %new_value) sanitize_hwaddress {
; CHECK-LABEL: @cmpxchg(
; CHECK: lshr i64 %[[A:[^ ]*]], 56
; CHECK: call void asm sideeffect "brk #2323", "{x0}"(i64 %[[A]])
; CHECK: [[PTRI8:%[^ ]*]] = bitcast i64* %ptr to i8*
; CHECK: call void @llvm.hwasan.check.memaccess({{.*}}, i8* [[PTRI8]], i32 19)
; CHECK: cmpxchg i64* %ptr, i64 %compare_to, i64 %new_value seq_cst seq_cst
; CHECK: ret void

Expand Down
296 changes: 152 additions & 144 deletions llvm/test/Instrumentation/HWAddressSanitizer/basic.ll

Large diffs are not rendered by default.

19 changes: 7 additions & 12 deletions llvm/test/Instrumentation/HWAddressSanitizer/kernel.ll
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
; Test KHWASan instrumentation.
;
; RUN: opt < %s -hwasan -hwasan-kernel=1 -S | FileCheck %s --allow-empty --check-prefixes=INIT
; RUN: opt < %s -hwasan -hwasan-kernel=1 -S | FileCheck %s --check-prefixes=CHECK,NOOFFSET,MATCH-ALL
; RUN: opt < %s -hwasan -hwasan-kernel=1 -hwasan-mapping-offset=12345678 -S | FileCheck %s --check-prefixes=CHECK,OFFSET,MATCH-ALL
; RUN: opt < %s -hwasan -hwasan-kernel=1 -hwasan-recover=0 -S | FileCheck %s --check-prefixes=CHECK,NOOFFSET,ABORT,MATCH-ALL
; RUN: opt < %s -hwasan -hwasan-kernel=1 -hwasan-recover=1 -S | FileCheck %s --check-prefixes=CHECK,NOOFFSET,RECOVER,MATCH-ALL
; RUN: opt < %s -hwasan -hwasan-kernel=1 -hwasan-recover=1 -hwasan-match-all-tag=-1 -S | FileCheck %s --check-prefixes=CHECK,NOOFFSET,RECOVER,NO-MATCH-ALL
; RUN: opt < %s -hwasan -hwasan-kernel=1 -hwasan-recover=1 -S | FileCheck %s --allow-empty --check-prefixes=INIT
; RUN: opt < %s -hwasan -hwasan-kernel=1 -hwasan-recover=1 -S | FileCheck %s --check-prefixes=CHECK,NOOFFSET,MATCH-ALL
; RUN: opt < %s -hwasan -hwasan-kernel=1 -hwasan-recover=1 -hwasan-mapping-offset=12345678 -S | FileCheck %s --check-prefixes=CHECK,OFFSET,MATCH-ALL
; RUN: opt < %s -hwasan -hwasan-kernel=1 -hwasan-recover=1 -hwasan-match-all-tag=-1 -S | FileCheck %s --check-prefixes=CHECK,NOOFFSET,NO-MATCH-ALL

target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64--linux-android"
Expand All @@ -20,8 +18,7 @@ define i8 @test_load(i8* %a) sanitize_hwaddress {

; NOOFFSET: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8*

; OFFSET: %[[D1:[^ ]*]] = add i64 %[[D]], 12345678
; OFFSET: %[[E:[^ ]*]] = inttoptr i64 %[[D1]] to i8*
; OFFSET: %[[E:[^ ]*]] = getelementptr i8, i8* inttoptr (i64 12345678 to i8*), i64 %[[D]]

; CHECK: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]]
; CHECK: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]]
Expand All @@ -32,10 +29,8 @@ define i8 @test_load(i8* %a) sanitize_hwaddress {

; NO-MATCH-ALL: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}}

; ABORT: call void asm sideeffect "brk #2304", "{x0}"(i64 %[[A]])
; ABORT: unreachable
; RECOVER: call void asm sideeffect "brk #2336", "{x0}"(i64 %[[A]])
; RECOVER: br label
; CHECK: call void asm sideeffect "brk #2336", "{x0}"(i64 %[[A]])
; CHECK: br label

; CHECK: %[[G:[^ ]*]] = load i8, i8* %a, align 4
; CHECK: ret i8 %[[G]]
Expand Down
12 changes: 6 additions & 6 deletions llvm/test/Instrumentation/HWAddressSanitizer/prologue.ll
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ define i32 @test_load(i32* %a) sanitize_hwaddress {
; CHECK-LABEL: @test_load
; CHECK: entry:

; CHECK-IFUNC: %[[A:[^ ]*]] = call i64 asm "", "=r,0"([0 x i8]* @__hwasan_shadow)
; CHECK-IFUNC: add i64 %{{.*}}, %[[A]]
; CHECK-IFUNC: %[[A:[^ ]*]] = call i8* asm "", "=r,0"([0 x i8]* @__hwasan_shadow)
; CHECK-IFUNC: @llvm.hwasan.check.memaccess(i8* %[[A]]

; CHECK-GLOBAL: load i64, i64* @__hwasan_shadow_memory_dynamic_address
; CHECK-GLOBAL: load i8*, i8** @__hwasan_shadow_memory_dynamic_address

; CHECK-TLS: %[[A:[^ ]*]] = call i8* @llvm.thread.pointer()
; CHECK-TLS: %[[B:[^ ]*]] = getelementptr i8, i8* %[[A]], i32 48
Expand Down Expand Up @@ -54,10 +54,10 @@ define void @test_alloca() sanitize_hwaddress {
; CHECK-LABEL: @test_alloca
; CHECK: entry:

; CHECK-IFUNC: %[[A:[^ ]*]] = call i64 asm "", "=r,0"([0 x i8]* @__hwasan_shadow)
; CHECK-IFUNC: add i64 %{{.*}}, %[[A]]
; CHECK-IFUNC: %[[A:[^ ]*]] = call i8* asm "", "=r,0"([0 x i8]* @__hwasan_shadow)
; CHECK-IFUNC: getelementptr i8, i8* %[[A]]

; CHECK-GLOBAL: load i64, i64* @__hwasan_shadow_memory_dynamic_address
; CHECK-GLOBAL: load i8*, i8** @__hwasan_shadow_memory_dynamic_address

; CHECK-TLS: %[[A:[^ ]*]] = call i8* @llvm.thread.pointer()
; CHECK-TLS: %[[B:[^ ]*]] = getelementptr i8, i8* %[[A]], i32 48
Expand Down