forked from OSchip/llvm-project
[AddressSanitizer] Allow prefixing memintrinsic calls in kernel mode
Allow receiving memcpy/memset/memmove instrumentation by using __asan or __hwasan prefixed versions for AddressSanitizer and HWAddressSanitizer respectively when compiling in kernel mode, by passing params -asan-kernel-mem-intrinsic-prefix or -hwasan-kernel-mem-intrinsic-prefix. By default the kernel-specialized versions of both passes drop the prefixes for calls generated by memintrinsics. This assumes that all locations that can lower the intrinsics to libcalls can safely be instrumented. This unfortunately is not the case when implicit calls to memintrinsics are inserted by the compiler in no_sanitize functions [1]. To solve the issue, normal memcpy/memset/memmove need to be uninstrumented, and instrumented code should instead use the prefixed versions. This also aligns with ASan behaviour in user space. [1] https://lore.kernel.org/lkml/Yj2yYFloadFobRPx@lakrids/ Reviewed By: glider Differential Revision: https://reviews.llvm.org/D122724
This commit is contained in:
parent
8de84198ce
commit
b8e49fdcb1
|
@ -331,6 +331,11 @@ static cl::opt<std::string> ClMemoryAccessCallbackPrefix(
|
|||
cl::desc("Prefix for memory access callbacks"), cl::Hidden,
|
||||
cl::init("__asan_"));
|
||||
|
||||
static cl::opt<bool> ClKasanMemIntrinCallbackPrefix(
|
||||
"asan-kernel-mem-intrinsic-prefix",
|
||||
cl::desc("Use prefix for memory intrinsics in KASAN mode"), cl::Hidden,
|
||||
cl::init(false));
|
||||
|
||||
static cl::opt<bool>
|
||||
ClInstrumentDynamicAllocas("asan-instrument-dynamic-allocas",
|
||||
cl::desc("instrument dynamic allocas"),
|
||||
|
@ -2729,7 +2734,9 @@ void AddressSanitizer::initializeCallbacks(Module &M) {
|
|||
}
|
||||
|
||||
const std::string MemIntrinCallbackPrefix =
|
||||
CompileKernel ? std::string("") : ClMemoryAccessCallbackPrefix;
|
||||
(CompileKernel && !ClKasanMemIntrinCallbackPrefix)
|
||||
? std::string("")
|
||||
: ClMemoryAccessCallbackPrefix;
|
||||
AsanMemmove = M.getOrInsertFunction(MemIntrinCallbackPrefix + "memmove",
|
||||
IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
|
||||
IRB.getInt8PtrTy(), IntptrTy);
|
||||
|
|
|
@ -83,6 +83,11 @@ static cl::opt<std::string>
|
|||
cl::desc("Prefix for memory access callbacks"),
|
||||
cl::Hidden, cl::init("__hwasan_"));
|
||||
|
||||
static cl::opt<bool> ClKasanMemIntrinCallbackPrefix(
|
||||
"hwasan-kernel-mem-intrinsic-prefix",
|
||||
cl::desc("Use prefix for memory intrinsics in KASAN mode"), cl::Hidden,
|
||||
cl::init(false));
|
||||
|
||||
static cl::opt<bool> ClInstrumentWithCalls(
|
||||
"hwasan-instrument-with-calls",
|
||||
cl::desc("instrument reads and writes with callbacks"), cl::Hidden,
|
||||
|
@ -723,7 +728,9 @@ void HWAddressSanitizer::initializeCallbacks(Module &M) {
|
|||
ArrayType::get(IRB.getInt8Ty(), 0));
|
||||
|
||||
const std::string MemIntrinCallbackPrefix =
|
||||
CompileKernel ? std::string("") : ClMemoryAccessCallbackPrefix;
|
||||
(CompileKernel && !ClKasanMemIntrinCallbackPrefix)
|
||||
? std::string("")
|
||||
: ClMemoryAccessCallbackPrefix;
|
||||
HWAsanMemmove = M.getOrInsertFunction(MemIntrinCallbackPrefix + "memmove",
|
||||
IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
|
||||
IRB.getInt8PtrTy(), IntptrTy);
|
||||
|
|
|
@ -160,43 +160,6 @@ entry:
|
|||
; CHECK-NOT: __asan_report
|
||||
; CHECK: ret i32
|
||||
|
||||
declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i1) nounwind
|
||||
declare void @llvm.memmove.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i1) nounwind
|
||||
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i1) nounwind
|
||||
|
||||
define void @memintr_test(i8* %a, i8* %b) nounwind uwtable sanitize_address {
|
||||
entry:
|
||||
tail call void @llvm.memset.p0i8.i64(i8* %a, i8 0, i64 100, i1 false)
|
||||
tail call void @llvm.memmove.p0i8.p0i8.i64(i8* %a, i8* %b, i64 100, i1 false)
|
||||
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %b, i64 100, i1 false)
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: memintr_test
|
||||
; CHECK: __asan_memset
|
||||
; CHECK: __asan_memmove
|
||||
; CHECK: __asan_memcpy
|
||||
; CHECK: ret void
|
||||
|
||||
declare void @llvm.memset.element.unordered.atomic.p0i8.i64(i8* nocapture writeonly, i8, i64, i32) nounwind
|
||||
declare void @llvm.memmove.element.unordered.atomic.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32) nounwind
|
||||
declare void @llvm.memcpy.element.unordered.atomic.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32) nounwind
|
||||
|
||||
define void @memintr_element_atomic_test(i8* %a, i8* %b) nounwind uwtable sanitize_address {
|
||||
; This is a canary test to make sure that these don't get lowered into calls that don't
|
||||
; have the element-atomic property. Eventually, asan will have to be enhanced to lower
|
||||
; these properly.
|
||||
; CHECK-LABEL: memintr_element_atomic_test
|
||||
; CHECK-NEXT: tail call void @llvm.memset.element.unordered.atomic.p0i8.i64(i8* align 1 %a, i8 0, i64 100, i32 1)
|
||||
; CHECK-NEXT: tail call void @llvm.memmove.element.unordered.atomic.p0i8.p0i8.i64(i8* align 1 %a, i8* align 1 %b, i64 100, i32 1)
|
||||
; CHECK-NEXT: tail call void @llvm.memcpy.element.unordered.atomic.p0i8.p0i8.i64(i8* align 1 %a, i8* align 1 %b, i64 100, i32 1)
|
||||
; CHECK-NEXT: ret void
|
||||
tail call void @llvm.memset.element.unordered.atomic.p0i8.i64(i8* align 1 %a, i8 0, i64 100, i32 1)
|
||||
tail call void @llvm.memmove.element.unordered.atomic.p0i8.p0i8.i64(i8* align 1 %a, i8* align 1 %b, i64 100, i32 1)
|
||||
tail call void @llvm.memcpy.element.unordered.atomic.p0i8.p0i8.i64(i8* align 1 %a, i8* align 1 %b, i64 100, i32 1)
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
; CHECK-LABEL: @test_swifterror
|
||||
; CHECK-NOT: __asan_report_load
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
; Test memory intrinsics instrumentation
|
||||
|
||||
; RUN: opt < %s -passes='asan-pipeline' -S | FileCheck --check-prefixes=CHECK,CHECK-PREFIX %s
|
||||
; RUN: opt < %s -passes='asan-pipeline' -asan-kernel -S | FileCheck --check-prefixes=CHECK,CHECK-NOPREFIX %s
|
||||
; RUN: opt < %s -passes='asan-pipeline' -asan-kernel -asan-kernel-mem-intrinsic-prefix -S | FileCheck --check-prefixes=CHECK,CHECK-PREFIX %s
|
||||
|
||||
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i1) nounwind
|
||||
declare void @llvm.memmove.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i1) nounwind
|
||||
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i1) nounwind
|
||||
|
||||
define void @memintr_test(i8* %a, i8* %b) nounwind uwtable sanitize_address {
|
||||
entry:
|
||||
tail call void @llvm.memset.p0i8.i64(i8* %a, i8 0, i64 100, i1 false)
|
||||
tail call void @llvm.memmove.p0i8.p0i8.i64(i8* %a, i8* %b, i64 100, i1 false)
|
||||
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %b, i64 100, i1 false)
|
||||
ret void
|
||||
}
|
||||
; CHECK-LABEL: memintr_test
|
||||
; CHECK-PREFIX: @__asan_memset
|
||||
; CHECK-PREFIX: @__asan_memmove
|
||||
; CHECK-PREFIX: @__asan_memcpy
|
||||
; CHECK-NOPREFIX: @memset
|
||||
; CHECK-NOPREFIX: @memmove
|
||||
; CHECK-NOPREFIX: @memcpy
|
||||
; CHECK: ret void
|
||||
|
||||
define void @memintr_test_nosanitize(i8* %a, i8* %b) nounwind uwtable {
|
||||
entry:
|
||||
tail call void @llvm.memset.p0i8.i64(i8* %a, i8 0, i64 100, i1 false)
|
||||
tail call void @llvm.memmove.p0i8.p0i8.i64(i8* %a, i8* %b, i64 100, i1 false)
|
||||
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %b, i64 100, i1 false)
|
||||
ret void
|
||||
}
|
||||
; CHECK-LABEL: memintr_test_nosanitize
|
||||
; CHECK: @llvm.memset
|
||||
; CHECK: @llvm.memmove
|
||||
; CHECK: @llvm.memcpy
|
||||
; CHECK: ret void
|
||||
|
||||
declare void @llvm.memset.element.unordered.atomic.p0i8.i64(i8* nocapture writeonly, i8, i64, i32) nounwind
|
||||
declare void @llvm.memmove.element.unordered.atomic.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32) nounwind
|
||||
declare void @llvm.memcpy.element.unordered.atomic.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32) nounwind
|
||||
|
||||
define void @memintr_element_atomic_test(i8* %a, i8* %b) nounwind uwtable sanitize_address {
|
||||
; This is a canary test to make sure that these don't get lowered into calls that don't
|
||||
; have the element-atomic property. Eventually, asan will have to be enhanced to lower
|
||||
; these properly.
|
||||
; CHECK-LABEL: memintr_element_atomic_test
|
||||
; CHECK-NEXT: tail call void @llvm.memset.element.unordered.atomic.p0i8.i64(i8* align 1 %a, i8 0, i64 100, i32 1)
|
||||
; CHECK-NEXT: tail call void @llvm.memmove.element.unordered.atomic.p0i8.p0i8.i64(i8* align 1 %a, i8* align 1 %b, i64 100, i32 1)
|
||||
; CHECK-NEXT: tail call void @llvm.memcpy.element.unordered.atomic.p0i8.p0i8.i64(i8* align 1 %a, i8* align 1 %b, i64 100, i32 1)
|
||||
; CHECK-NEXT: ret void
|
||||
tail call void @llvm.memset.element.unordered.atomic.p0i8.i64(i8* align 1 %a, i8 0, i64 100, i32 1)
|
||||
tail call void @llvm.memmove.element.unordered.atomic.p0i8.p0i8.i64(i8* align 1 %a, i8* align 1 %b, i64 100, i32 1)
|
||||
tail call void @llvm.memcpy.element.unordered.atomic.p0i8.p0i8.i64(i8* align 1 %a, i8* align 1 %b, i64 100, i32 1)
|
||||
ret void
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
; RUN: opt -S -passes=hwasan -hwasan-use-stack-safety=0 %s | FileCheck %s
|
||||
; RUN: opt -S -passes=hwasan -hwasan-use-stack-safety=0 %s | FileCheck --check-prefixes=CHECK,CHECK-PREFIX %s
|
||||
; RUN: opt -S -passes=hwasan -hwasan-kernel -hwasan-use-stack-safety=0 %s | FileCheck --check-prefixes=CHECK,CHECK-NOPREFIX %s
|
||||
; RUN: opt -S -passes=hwasan -hwasan-kernel -hwasan-kernel-mem-intrinsic-prefix -hwasan-use-stack-safety=0 %s | FileCheck --check-prefixes=CHECK,CHECK-PREFIX %s
|
||||
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
; Function Attrs: noinline nounwind optnone uwtable
|
||||
define dso_local i32 @main() sanitize_hwaddress {
|
||||
; CHECK-LABEL: main
|
||||
entry:
|
||||
%retval = alloca i32, align 4
|
||||
%Q = alloca [10 x i8], align 1
|
||||
|
@ -13,20 +16,23 @@ entry:
|
|||
%arraydecay = getelementptr inbounds [10 x i8], [10 x i8]* %Q, i32 0, i32 0
|
||||
|
||||
call void @llvm.memset.p0i8.i64(i8* align 1 %arraydecay, i8 0, i64 10, i1 false)
|
||||
; CHECK: call i8* @__hwasan_memset
|
||||
; CHECK-PREFIX: call i8* @__hwasan_memset
|
||||
; CHECK-NOPREFIX: call i8* @memset
|
||||
|
||||
%arraydecay1 = getelementptr inbounds [10 x i8], [10 x i8]* %Q, i32 0, i32 0
|
||||
%arraydecay2 = getelementptr inbounds [10 x i8], [10 x i8]* %Q, i32 0, i32 0
|
||||
%add.ptr = getelementptr inbounds i8, i8* %arraydecay2, i64 5
|
||||
|
||||
call void @llvm.memmove.p0i8.p0i8.i64(i8* align 1 %arraydecay1, i8* align 1 %add.ptr, i64 5, i1 false)
|
||||
; CHECK: call i8* @__hwasan_memmove
|
||||
; CHECK-PREFIX: call i8* @__hwasan_memmove
|
||||
; CHECK-NOPREFIX: call i8* @memmove
|
||||
|
||||
%arraydecay3 = getelementptr inbounds [10 x i8], [10 x i8]* %P, i32 0, i32 0
|
||||
%arraydecay4 = getelementptr inbounds [10 x i8], [10 x i8]* %Q, i32 0, i32 0
|
||||
|
||||
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %arraydecay3, i8* align 1 %arraydecay4, i64 10, i1 false)
|
||||
; CHECK: call i8* @__hwasan_memcpy
|
||||
; CHECK-PREFIX: call i8* @__hwasan_memcpy
|
||||
; CHECK-NOPREFIX: call i8* @memcpy
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
|
@ -38,3 +44,16 @@ declare void @llvm.memmove.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly,
|
|||
|
||||
; Function Attrs: argmemonly nounwind
|
||||
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) #1
|
||||
|
||||
define void @memintr_test_nosanitize(i8* %a, i8* %b) nounwind uwtable {
|
||||
entry:
|
||||
tail call void @llvm.memset.p0i8.i64(i8* %a, i8 0, i64 100, i1 false)
|
||||
tail call void @llvm.memmove.p0i8.p0i8.i64(i8* %a, i8* %b, i64 100, i1 false)
|
||||
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %b, i64 100, i1 false)
|
||||
ret void
|
||||
}
|
||||
; CHECK-LABEL: memintr_test_nosanitize
|
||||
; CHECK: @llvm.memset
|
||||
; CHECK: @llvm.memmove
|
||||
; CHECK: @llvm.memcpy
|
||||
; CHECK: ret void
|
||||
|
|
Loading…
Reference in New Issue