Hardware-assisted AddressSanitizer (llvm part).
Summary:
This is LLVM instrumentation for the new HWASan tool. It is basically
a stripped down copy of ASan at this point, w/o stack or global
support. Instrumenation adds a global constructor + runtime callbacks
for every load and store.
HWASan comes with its own IR attribute.
A brief design document can be found in
clang/docs/HardwareAssistedAddressSanitizerDesign.rst (submitted earlier).
Reviewers: kcc, pcc, alekseyshl
Subscribers: srhines, mehdi_amini, mgorny, javed.absar, eraman, llvm-commits, hiraditya
Differential Revision: https://reviews.llvm.org/D40932
llvm-svn: 320217
diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index 18aa12c..8c74b3f 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -204,7 +204,7 @@
; CHECK: define void @f34()
{
call void @nobuiltin() nobuiltin
-; CHECK: call void @nobuiltin() #34
+; CHECK: call void @nobuiltin() #35
ret void;
}
@@ -339,6 +339,12 @@
ret void
}
+; CHECK: define void @f58() #34
+define void @f58() sanitize_hwaddress
+{
+ ret void;
+}
+
; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone }
@@ -373,4 +379,5 @@
; CHECK: attributes #31 = { allocsize(0,1) }
; CHECK: attributes #32 = { writeonly }
; CHECK: attributes #33 = { speculatable }
-; CHECK: attributes #34 = { nobuiltin }
+; CHECK: attributes #34 = { sanitize_hwaddress }
+; CHECK: attributes #35 = { nobuiltin }
diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/atomic.ll b/llvm/test/Instrumentation/HWAddressSanitizer/atomic.ll
new file mode 100644
index 0000000..67ddac7
--- /dev/null
+++ b/llvm/test/Instrumentation/HWAddressSanitizer/atomic.ll
@@ -0,0 +1,26 @@
+; Test basic address sanitizer instrumentation.
+;
+; RUN: opt < %s -hwasan -S | FileCheck %s
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64--linux-android"
+
+define void @atomicrmw(i64* %ptr) sanitize_hwaddress {
+; CHECK-LABEL: @atomicrmw(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64
+; CHECK: call void @__hwasan_store8(i64 %[[A]])
+
+entry:
+ %0 = atomicrmw add i64* %ptr, i64 1 seq_cst
+ ret void
+}
+
+define void @cmpxchg(i64* %ptr, i64 %compare_to, i64 %new_value) sanitize_hwaddress {
+; CHECK-LABEL: @cmpxchg(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64
+; CHECK: call void @__hwasan_store8(i64 %[[A]])
+
+entry:
+ %0 = cmpxchg i64* %ptr, i64 %compare_to, i64 %new_value seq_cst seq_cst
+ ret void
+}
diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll b/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll
new file mode 100644
index 0000000..93129a5
--- /dev/null
+++ b/llvm/test/Instrumentation/HWAddressSanitizer/basic.ll
@@ -0,0 +1,190 @@
+; Test basic address sanitizer instrumentation.
+;
+; RUN: opt < %s -hwasan -S | FileCheck %s
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64--linux-android"
+
+define i8 @test_load8(i8* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load8(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
+; CHECK: call void @__hwasan_load1(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i8, i8* %a
+; CHECK: ret i8 %[[B]]
+
+entry:
+ %b = load i8, i8* %a, align 4
+ ret i8 %b
+}
+
+define i16 @test_load16(i16* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load16(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64
+; CHECK: call void @__hwasan_load2(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i16, i16* %a
+; CHECK: ret i16 %[[B]]
+
+entry:
+ %b = load i16, i16* %a, align 4
+ ret i16 %b
+}
+
+define i32 @test_load32(i32* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load32(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64
+; CHECK: call void @__hwasan_load4(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i32, i32* %a
+; CHECK: ret i32 %[[B]]
+
+entry:
+ %b = load i32, i32* %a, align 4
+ ret i32 %b
+}
+
+define i64 @test_load64(i64* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load64(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64
+; CHECK: call void @__hwasan_load8(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i64, i64* %a
+; CHECK: ret i64 %[[B]]
+
+entry:
+ %b = load i64, i64* %a, align 8
+ ret i64 %b
+}
+
+define i128 @test_load128(i128* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load128(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64
+; CHECK: call void @__hwasan_load16(i64 %[[A]])
+; CHECK: %[[B:[^ ]*]] = load i128, i128* %a
+; CHECK: ret i128 %[[B]]
+
+entry:
+ %b = load i128, i128* %a, align 16
+ ret i128 %b
+}
+
+define i40 @test_load40(i40* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load40(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
+; CHECK: call void @__hwasan_load(i64 %[[A]], i64 5)
+; CHECK: %[[B:[^ ]*]] = load i40, i40* %a
+; CHECK: ret i40 %[[B]]
+
+entry:
+ %b = load i40, i40* %a, align 4
+ ret i40 %b
+}
+
+define void @test_store8(i8* %a, i8 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store8(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
+; CHECK: call void @__hwasan_store1(i64 %[[A]])
+; CHECK: store i8 %b, i8* %a
+; CHECK: ret void
+
+entry:
+ store i8 %b, i8* %a, align 4
+ ret void
+}
+
+define void @test_store16(i16* %a, i16 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store16(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64
+; CHECK: call void @__hwasan_store2(i64 %[[A]])
+; CHECK: store i16 %b, i16* %a
+; CHECK: ret void
+
+entry:
+ store i16 %b, i16* %a, align 4
+ ret void
+}
+
+define void @test_store32(i32* %a, i32 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store32(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64
+; CHECK: call void @__hwasan_store4(i64 %[[A]])
+; CHECK: store i32 %b, i32* %a
+; CHECK: ret void
+
+entry:
+ store i32 %b, i32* %a, align 4
+ ret void
+}
+
+define void @test_store64(i64* %a, i64 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store64(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64
+; CHECK: call void @__hwasan_store8(i64 %[[A]])
+; CHECK: store i64 %b, i64* %a
+; CHECK: ret void
+
+entry:
+ store i64 %b, i64* %a, align 4
+ ret void
+}
+
+define void @test_store128(i128* %a, i128 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store128(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64
+; CHECK: call void @__hwasan_store16(i64 %[[A]])
+; CHECK: store i128 %b, i128* %a
+; CHECK: ret void
+
+entry:
+ store i128 %b, i128* %a, align 4
+ ret void
+}
+
+define void @test_store40(i40* %a, i40 %b) sanitize_hwaddress {
+; CHECK-LABEL: @test_store40(
+; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
+; CHECK: call void @__hwasan_store(i64 %[[A]], i64 5)
+; CHECK: store i40 %b, i40* %a
+; CHECK: ret void
+
+entry:
+ store i40 %b, i40* %a, align 4
+ ret void
+}
+
+define i8 @test_load_noattr(i8* %a) {
+; CHECK-LABEL: @test_load_noattr(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a
+; CHECK-NEXT: ret i8 %[[B]]
+
+entry:
+ %b = load i8, i8* %a, align 4
+ ret i8 %b
+}
+
+define i8 @test_load_notmyattr(i8* %a) sanitize_address {
+; CHECK-LABEL: @test_load_notmyattr(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a
+; CHECK-NEXT: ret i8 %[[B]]
+
+entry:
+ %b = load i8, i8* %a, align 4
+ ret i8 %b
+}
+
+define i8 @test_load_addrspace(i8 addrspace(256)* %a) sanitize_hwaddress {
+; CHECK-LABEL: @test_load_addrspace(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8 addrspace(256)* %a
+; CHECK-NEXT: ret i8 %[[B]]
+
+entry:
+ %b = load i8, i8 addrspace(256)* %a, align 4
+ ret i8 %b
+}
+
+; CHECK: declare void @__hwasan_init()
+
+; CHECK: define internal void @hwasan.module_ctor() {
+; CHECK-NEXT: call void @__hwasan_init()
+; CHECK-NEXT: ret void
+; CHECK-NEXT: }
diff --git a/llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll b/llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll
index a83d7b6..72e0b4e 100644
--- a/llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll
+++ b/llvm/test/Transforms/GVN/no_speculative_loads_with_asan.ll
@@ -53,3 +53,30 @@
; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
; CHECK: {{.*}} = phi
+
+define i32 @TestHWAsan() sanitize_hwaddress {
+ %1 = tail call noalias i8* @_Znam(i64 2)
+ %2 = getelementptr inbounds i8, i8* %1, i64 1
+ store i8 0, i8* %2, align 1
+ store i8 0, i8* %1, align 1
+ %3 = bitcast i8* %1 to i16*
+ %4 = load i16, i16* %3, align 4
+ %5 = icmp eq i16 %4, 0
+ br i1 %5, label %11, label %6
+
+; <label>:6 ; preds = %0
+ %7 = getelementptr inbounds i8, i8* %1, i64 2
+ %8 = bitcast i8* %7 to i16*
+ %9 = load i16, i16* %8, align 2
+ %10 = sext i16 %9 to i32
+ br label %11
+
+; <label>:11 ; preds = %0, %6
+ %12 = phi i32 [ %10, %6 ], [ 0, %0 ]
+ ret i32 %12
+}
+
+; CHECK-LABEL: @TestHWAsan
+; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
+; CHECK: {{.*}} = phi
+
diff --git a/llvm/test/Transforms/Inline/attributes.ll b/llvm/test/Transforms/Inline/attributes.ll
index 1cc64b7..0df3cfa 100644
--- a/llvm/test/Transforms/Inline/attributes.ll
+++ b/llvm/test/Transforms/Inline/attributes.ll
@@ -10,6 +10,10 @@
ret i32 %i
}
+define i32 @sanitize_hwaddress_callee(i32 %i) sanitize_hwaddress {
+ ret i32 %i
+}
+
define i32 @sanitize_thread_callee(i32 %i) sanitize_thread {
ret i32 %i
}
@@ -30,6 +34,10 @@
ret i32 %i
}
+define i32 @alwaysinline_sanitize_hwaddress_callee(i32 %i) alwaysinline sanitize_hwaddress {
+ ret i32 %i
+}
+
define i32 @alwaysinline_sanitize_thread_callee(i32 %i) alwaysinline sanitize_thread {
ret i32 %i
}
@@ -59,6 +67,17 @@
; CHECK-NEXT: ret i32
}
+define i32 @test_no_sanitize_hwaddress(i32 %arg) {
+ %x1 = call i32 @noattr_callee(i32 %arg)
+ %x2 = call i32 @sanitize_hwaddress_callee(i32 %x1)
+ %x3 = call i32 @alwaysinline_callee(i32 %x2)
+ %x4 = call i32 @alwaysinline_sanitize_hwaddress_callee(i32 %x3)
+ ret i32 %x4
+; CHECK-LABEL: @test_no_sanitize_hwaddress(
+; CHECK-NEXT: @sanitize_hwaddress_callee
+; CHECK-NEXT: ret i32
+}
+
define i32 @test_no_sanitize_memory(i32 %arg) {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_memory_callee(i32 %x1)
@@ -98,6 +117,17 @@
; CHECK-NEXT: ret i32
}
+define i32 @test_sanitize_hwaddress(i32 %arg) sanitize_hwaddress {
+ %x1 = call i32 @noattr_callee(i32 %arg)
+ %x2 = call i32 @sanitize_hwaddress_callee(i32 %x1)
+ %x3 = call i32 @alwaysinline_callee(i32 %x2)
+ %x4 = call i32 @alwaysinline_sanitize_hwaddress_callee(i32 %x3)
+ ret i32 %x4
+; CHECK-LABEL: @test_sanitize_hwaddress(
+; CHECK-NEXT: @noattr_callee
+; CHECK-NEXT: ret i32
+}
+
define i32 @test_sanitize_memory(i32 %arg) sanitize_memory {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_memory_callee(i32 %x1)
diff --git a/llvm/test/Transforms/InstCombine/lifetime-asan.ll b/llvm/test/Transforms/InstCombine/lifetime-asan.ll
index 7fdc1fc..e7b996d 100644
--- a/llvm/test/Transforms/InstCombine/lifetime-asan.ll
+++ b/llvm/test/Transforms/InstCombine/lifetime-asan.ll
@@ -19,6 +19,20 @@
ret void
}
+define void @hwasan() sanitize_hwaddress {
+entry:
+ ; CHECK-LABEL: @hwasan(
+ %text = alloca i8, align 1
+
+ call void @llvm.lifetime.start.p0i8(i64 1, i8* %text)
+ call void @llvm.lifetime.end.p0i8(i64 1, i8* %text)
+ ; CHECK: call void @llvm.lifetime.start
+ ; CHECK-NEXT: call void @llvm.lifetime.end
+
+ call void @foo(i8* %text) ; Keep alloca alive
+
+ ret void
+}
define void @no_asan() {
entry:
diff --git a/llvm/test/Transforms/NewGVN/no_speculative_loads_with_asan.ll b/llvm/test/Transforms/NewGVN/no_speculative_loads_with_asan.ll
index a83d7b6..e1e5e4a 100644
--- a/llvm/test/Transforms/NewGVN/no_speculative_loads_with_asan.ll
+++ b/llvm/test/Transforms/NewGVN/no_speculative_loads_with_asan.ll
@@ -53,3 +53,29 @@
; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
; CHECK: {{.*}} = phi
+define i32 @TestHWAsan() sanitize_hwaddress {
+ %1 = tail call noalias i8* @_Znam(i64 2)
+ %2 = getelementptr inbounds i8, i8* %1, i64 1
+ store i8 0, i8* %2, align 1
+ store i8 0, i8* %1, align 1
+ %3 = bitcast i8* %1 to i16*
+ %4 = load i16, i16* %3, align 4
+ %5 = icmp eq i16 %4, 0
+ br i1 %5, label %11, label %6
+
+; <label>:6 ; preds = %0
+ %7 = getelementptr inbounds i8, i8* %1, i64 2
+ %8 = bitcast i8* %7 to i16*
+ %9 = load i16, i16* %8, align 2
+ %10 = sext i16 %9 to i32
+ br label %11
+
+; <label>:11 ; preds = %0, %6
+ %12 = phi i32 [ %10, %6 ], [ 0, %0 ]
+ ret i32 %12
+}
+
+; CHECK-LABEL: @TestHWAsan
+; CHECK-NOT: %[[LOAD:[^ ]+]] = load i32
+; CHECK: {{.*}} = phi
+
diff --git a/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll b/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll
index 063bde8..dfd0d71 100644
--- a/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll
+++ b/llvm/test/Transforms/SimplifyCFG/no_speculative_loads_with_asan.ll
@@ -38,3 +38,22 @@
; CHECK: br label
; CHECK: ret i32
}
+
+define i32 @TestHWAsan(i32 %cond) nounwind readonly uwtable sanitize_hwaddress {
+entry:
+ %tobool = icmp eq i32 %cond, 0
+ br i1 %tobool, label %return, label %if.then
+
+if.then: ; preds = %entry
+ %0 = load i32, i32* @g, align 4
+ br label %return
+
+return: ; preds = %entry, %if.then
+ %retval = phi i32 [ %0, %if.then ], [ 0, %entry ]
+ ret i32 %retval
+; CHECK-LABEL: @TestHWAsan
+; CHECK: br i1
+; CHECK: load i32, i32* @g
+; CHECK: br label
+; CHECK: ret i32
+}