diff --git a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h --- a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h +++ b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h @@ -78,8 +78,8 @@ // Whether we can prove that all accesses to this Alloca are in-range and // during its lifetime. bool isSafe(const AllocaInst &AI) const; - // Whether we can prove that an instruction only accesses a live alloca in - // range. + // Whether we can prove that all stack accesses in an instruction only access + // live alloca in range. bool accessIsSafe(const Instruction &I) const; void print(raw_ostream &O) const; void dump() const; diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -285,6 +285,7 @@ void instrumentMemAccessInline(Value *Ptr, bool IsWrite, unsigned AccessSizeIndex, Instruction *InsertBefore); + bool ignoreMemIntrinsic(MemIntrinsic *MI); void instrumentMemIntrinsic(MemIntrinsic *MI); bool instrumentMemAccess(InterestingMemoryOperand &O); bool ignoreAccess(Instruction *Inst, Value *Ptr); @@ -785,7 +786,7 @@ if (!InstrumentStack) { if (findAllocaForValue(Ptr)) return true; - } else if (SSI && SSI->accessIsSafe(*Inst)) { + } else if (SSI && SSI->accessIsSafe(*Inst) && findAllocaForValue(Ptr)) { return true; } return false; @@ -970,6 +971,16 @@ cast(CheckFailTerm)->setSuccessor(0, CheckTerm->getParent()); } +bool HWAddressSanitizer::ignoreMemIntrinsic(MemIntrinsic *MI) { + if (MemTransferInst *MTI = dyn_cast(MI)) { + return (!ClInstrumentWrites || ignoreAccess(MTI, MTI->getDest())) && + (!ClInstrumentReads || ignoreAccess(MTI, MTI->getSource())); + } + if (isa(MI)) + return !ClInstrumentWrites || ignoreAccess(MI, MI->getDest()); + return false; +} + void HWAddressSanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) { IRBuilder<> IRB(MI); if (isa(MI)) { @@ -1516,7 +1527,8 @@ getInterestingMemoryOperands(&Inst, OperandsToInstrument); if (MemIntrinsic *MI = dyn_cast(&Inst)) - IntrinToInstrument.push_back(MI); + if (!ignoreMemIntrinsic(MI)) + IntrinToInstrument.push_back(MI); } } diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/mem-intrinsics.ll b/llvm/test/Instrumentation/HWAddressSanitizer/mem-intrinsics.ll --- a/llvm/test/Instrumentation/HWAddressSanitizer/mem-intrinsics.ll +++ b/llvm/test/Instrumentation/HWAddressSanitizer/mem-intrinsics.ll @@ -1,4 +1,4 @@ -; RUN: opt -S -passes=hwasan %s | FileCheck %s +; RUN: opt -S -passes=hwasan -hwasan-use-stack-safety=0 %s | FileCheck %s target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll b/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll --- a/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll +++ b/llvm/test/Instrumentation/HWAddressSanitizer/stack-safety-analysis.ll @@ -78,6 +78,65 @@ ret i32 0 } +define i32 @test_in_range3(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_in_range3 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memset + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_memset + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memset + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + call void @llvm.memset.p0i8.i32(i8* %ptr, i8 0, i32 1, i1 true) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + ret i32 0 +} + +define i32 @test_in_range4(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_in_range4 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %ptr, i32 1, i1 true) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + ret i32 0 +} + +define i32 @test_in_range5(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_in_range5 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + %buf.sroa.1 = alloca [10 x i8], align 4 + %ptr1 = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %y = bitcast [10 x i8]* %buf.sroa.1 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %y) + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %ptr1, i32 1, i1 true) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %y) + ret i32 0 +} + ; Check an alloca with out of range GEP to ensure it gets a tag and check. define i32 @test_out_of_range(i32* %a) sanitize_hwaddress { entry: @@ -97,6 +156,65 @@ ret i32 0 } +define i32 @test_out_of_range3(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_out_of_range3 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memset + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_memset + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memset + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + call void @llvm.memset.p0i8.i32(i8* %ptr, i8 0, i32 2, i1 true) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + ret i32 0 +} + +define i32 @test_out_of_range4(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_out_of_range4 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %ptr, i32 2, i1 true) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + ret i32 0 +} + +define i32 @test_out_of_range5(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_out_of_range5 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + %buf.sroa.1 = alloca [10 x i8], align 4 + %ptr1 = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %y = bitcast [10 x i8]* %buf.sroa.1 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %y) + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %ptr1, i32 1, i1 true) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %y) + ret i32 0 +} + ; Check an alloca with potentially out of range GEP to ensure it gets a tag and ; check. define i32 @test_potentially_out_of_range(i32* %a) sanitize_hwaddress { @@ -117,6 +235,23 @@ ret i32 0 } +define i32 @test_potentially_out_of_range2(i8* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_potentially_out_of_range2 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %a, i32 1, i1 true) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + ret i32 0 +} ; Check an alloca with potentially out of range GEP to ensure it gets a tag and ; check. define i32 @test_unclear(i32* %a) sanitize_hwaddress { @@ -179,6 +314,10 @@ ; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.memset.p0i8.i32(i8*, i8, i32, i1) +declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1) +declare void @llvm.memmove.p0i8.p0i8.i32(i8*, i8*, i32, i1) + declare i1 @cond() declare void @use(i8* nocapture) declare i32 @getoffset()