[compiler-rt] abf8ed8 - [hwasan] Support more complicated lifetimes.
Florian Mayer via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 3 02:30:05 PDT 2021
Author: Florian Mayer
Date: 2021-09-03T10:29:50+01:00
New Revision: abf8ed8a823fea2b716d9c643bb809c46006a5c3
URL: https://github.com/llvm/llvm-project/commit/abf8ed8a823fea2b716d9c643bb809c46006a5c3
DIFF: https://github.com/llvm/llvm-project/commit/abf8ed8a823fea2b716d9c643bb809c46006a5c3.diff
LOG: [hwasan] Support more complicated lifetimes.
This is important as with exceptions enabled, non-POD allocas often have
two lifetime ends: the exception handler, and the normal one.
Reviewed By: eugenis
Differential Revision: https://reviews.llvm.org/D108365
Added:
llvm/test/Instrumentation/HWAddressSanitizer/exception-lifetime.ll
Modified:
compiler-rt/test/hwasan/TestCases/use-after-scope-types.cpp
llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerCommon.h
llvm/lib/Target/AArch64/AArch64StackTagging.cpp
llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
Removed:
################################################################################
diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-types.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-types.cpp
index 90897272331af..66fb339e477ac 100644
--- a/compiler-rt/test/hwasan/TestCases/use-after-scope-types.cpp
+++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-types.cpp
@@ -7,8 +7,7 @@
// RUN: not %run %t 3 2>&1 | FileCheck %s
// RUN: not %run %t 4 2>&1 | FileCheck %s
// RUN: not %run %t 5 2>&1 | FileCheck %s
-// The std::vector case is broken because of limited lifetime tracking.
-// TODO(fmayer): Fix and enable.
+// RUN: not %run %t 6 2>&1 | FileCheck %s
// RUN: not %run %t 7 2>&1 | FileCheck %s
// RUN: not %run %t 8 2>&1 | FileCheck %s
// RUN: not %run %t 9 2>&1 | FileCheck %s
diff --git a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerCommon.h b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerCommon.h
index 86c6ce4bc7c5e..5a0fb835606a1 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerCommon.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerCommon.h
@@ -17,6 +17,7 @@
#include "llvm/Analysis/PostDominators.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Instruction.h"
+#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
namespace llvm {
@@ -46,43 +47,47 @@ class InterestingMemoryOperand {
Value *getPtr() { return PtrUse->get(); }
};
-// For an alloca valid between lifetime markers Start and End, call the
+// For an alloca valid between lifetime markers Start and Ends, call the
// Callback for all possible exits out of the lifetime in the containing
// function, which can return from the instructions in RetVec.
//
-// Returns whether End was the only possible exit. If it wasn't, the caller
-// should remove End to ensure that work done at the other exits does not
-// happen outside of the lifetime.
+// Returns whether Ends covered all possible exits. If they did not,
+// the caller should remove Ends to ensure that work done at the other
+// exits does not happen outside of the lifetime.
template <typename F>
bool forAllReachableExits(const DominatorTree &DT, const PostDominatorTree &PDT,
- const Instruction *Start, Instruction *End,
+ const Instruction *Start,
+ const SmallVectorImpl<IntrinsicInst *> &Ends,
const SmallVectorImpl<Instruction *> &RetVec,
F Callback) {
- // We need to ensure that if we tag some object, we certainly untag it
- // before the function exits.
- if (PDT.dominates(End, Start)) {
- Callback(End);
- } else {
- SmallVector<Instruction *, 8> ReachableRetVec;
- unsigned NumCoveredExits = 0;
- for (auto &RI : RetVec) {
- if (!isPotentiallyReachable(Start, RI, nullptr, &DT))
- continue;
- ReachableRetVec.push_back(RI);
- if (DT.dominates(End, RI))
- ++NumCoveredExits;
- }
- // If there's a mix of covered and non-covered exits, just put the untag
- // on exits, so we avoid the redundancy of untagging twice.
- if (NumCoveredExits == ReachableRetVec.size()) {
+ if (Ends.size() == 1 && PDT.dominates(Ends[0], Start)) {
+ Callback(Ends[0]);
+ return true;
+ }
+ SmallVector<Instruction *, 8> ReachableRetVec;
+ unsigned NumCoveredExits = 0;
+ for (auto &RI : RetVec) {
+ if (!isPotentiallyReachable(Start, RI, nullptr, &DT))
+ continue;
+ ReachableRetVec.push_back(RI);
+ // TODO(fmayer): We don't support diamond shapes, where multiple lifetime
+ // ends together dominate the RI, but none of them does by itself.
+ // Check how often this happens and decide whether to support this here.
+ if (std::any_of(Ends.begin(), Ends.end(),
+ [&](Instruction *End) { return DT.dominates(End, RI); }))
+ ++NumCoveredExits;
+ }
+ // If there's a mix of covered and non-covered exits, just put the untag
+ // on exits, so we avoid the redundancy of untagging twice.
+ if (NumCoveredExits == ReachableRetVec.size()) {
+ for (auto *End : Ends)
Callback(End);
- } else {
- for (auto &RI : ReachableRetVec)
- Callback(RI);
- // We may have inserted untag outside of the lifetime interval.
- // Signal the caller to remove the lifetime end call for this alloca.
- return false;
- }
+ } else {
+ for (auto &RI : ReachableRetVec)
+ Callback(RI);
+ // We may have inserted untag outside of the lifetime interval.
+ // Signal the caller to remove the lifetime end call for this alloca.
+ return false;
}
return true;
}
diff --git a/llvm/lib/Target/AArch64/AArch64StackTagging.cpp b/llvm/lib/Target/AArch64/AArch64StackTagging.cpp
index 8732baa8f9780..5cec4cb663396 100644
--- a/llvm/lib/Target/AArch64/AArch64StackTagging.cpp
+++ b/llvm/lib/Target/AArch64/AArch64StackTagging.cpp
@@ -651,7 +651,8 @@ bool AArch64StackTagging::runOnFunction(Function &Fn) {
auto TagEnd = [&](Instruction *Node) { untagAlloca(AI, Node, Size); };
if (!DT || !PDT ||
- !forAllReachableExits(*DT, *PDT, Start, End, RetVec, TagEnd))
+ !forAllReachableExits(*DT, *PDT, Start, Info.LifetimeEnd, RetVec,
+ TagEnd))
End->eraseFromParent();
} else {
uint64_t Size = Info.AI->getAllocationSizeInBits(*DL).getValue() / 8;
diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
index aa791eb286c8a..e51bc2a7f015e 100644
--- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
@@ -17,6 +17,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/CFG.h"
#include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/StackSafetyAnalysis.h"
#include "llvm/Analysis/ValueTracking.h"
@@ -119,6 +120,12 @@ static cl::opt<bool>
cl::Hidden, cl::desc("Use Stack Safety analysis results"),
cl::Optional);
+static cl::opt<size_t> ClMaxLifetimes(
+ "hwasan-max-lifetimes-for-alloca", cl::Hidden, cl::init(3),
+ cl::ReallyHidden,
+ cl::desc("How many lifetime ends to handle for a single alloca."),
+ cl::Optional);
+
static cl::opt<bool>
ClUseAfterScope("hwasan-use-after-scope",
cl::desc("detect use after scope within function"),
@@ -288,6 +295,8 @@ class HWAddressSanitizer {
void tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag, size_t Size);
Value *tagPointer(IRBuilder<> &IRB, Type *Ty, Value *PtrLong, Value *Tag);
Value *untagPointer(IRBuilder<> &IRB, Value *PtrLong);
+ static bool isStandardLifetime(const AllocaInfo &AllocaInfo,
+ const DominatorTree &DT);
bool instrumentStack(
MapVector<AllocaInst *, AllocaInfo> &AllocasToInstrument,
SmallVector<Instruction *, 4> &UnrecognizedLifetimes,
@@ -1277,6 +1286,35 @@ bool HWAddressSanitizer::instrumentLandingPads(
return true;
}
+static bool
+maybeReachableFromEachOther(const SmallVectorImpl<IntrinsicInst *> &Insts,
+ const DominatorTree &DT) {
+ // If we have too many lifetime ends, give up, as the algorithm below is N^2.
+ if (Insts.size() > ClMaxLifetimes)
+ return true;
+ for (size_t I = 0; I < Insts.size(); ++I) {
+ for (size_t J = 0; J < Insts.size(); ++J) {
+ if (I == J)
+ continue;
+ if (isPotentiallyReachable(Insts[I], Insts[J], nullptr, &DT))
+ return true;
+ }
+ }
+ return false;
+}
+
+// static
+bool HWAddressSanitizer::isStandardLifetime(const AllocaInfo &AllocaInfo,
+ const DominatorTree &DT) {
+ // An alloca that has exactly one start and end in every possible execution.
+ // If it has multiple ends, they have to be unreachable from each other, so
+ // at most one of them is actually used for each execution of the function.
+ return AllocaInfo.LifetimeStart.size() == 1 &&
+ (AllocaInfo.LifetimeEnd.size() == 1 ||
+ (AllocaInfo.LifetimeEnd.size() > 0 &&
+ !maybeReachableFromEachOther(AllocaInfo.LifetimeEnd, DT)));
+}
+
bool HWAddressSanitizer::instrumentStack(
MapVector<AllocaInst *, AllocaInfo> &AllocasToInstrument,
SmallVector<Instruction *, 4> &UnrecognizedLifetimes,
@@ -1322,12 +1360,10 @@ bool HWAddressSanitizer::instrumentStack(
size_t Size = getAllocaSizeInBytes(*AI);
size_t AlignedSize = alignTo(Size, Mapping.getObjectAlignment());
- bool StandardLifetime = UnrecognizedLifetimes.empty() &&
- Info.LifetimeStart.size() == 1 &&
- Info.LifetimeEnd.size() == 1;
+ bool StandardLifetime =
+ UnrecognizedLifetimes.empty() && isStandardLifetime(Info, GetDT());
if (DetectUseAfterScope && StandardLifetime) {
IntrinsicInst *Start = Info.LifetimeStart[0];
- IntrinsicInst *End = Info.LifetimeEnd[0];
IRB.SetInsertPoint(Start->getNextNode());
auto TagEnd = [&](Instruction *Node) {
IRB.SetInsertPoint(Node);
@@ -1335,8 +1371,11 @@ bool HWAddressSanitizer::instrumentStack(
tagAlloca(IRB, AI, UARTag, AlignedSize);
};
tagAlloca(IRB, AI, Tag, Size);
- if (!forAllReachableExits(GetDT(), GetPDT(), Start, End, RetVec, TagEnd))
- End->eraseFromParent();
+ if (!forAllReachableExits(GetDT(), GetPDT(), Start, Info.LifetimeEnd,
+ RetVec, TagEnd)) {
+ for (auto *End : Info.LifetimeEnd)
+ End->eraseFromParent();
+ }
} else {
tagAlloca(IRB, AI, Tag, Size);
for (auto *RI : RetVec) {
diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/exception-lifetime.ll b/llvm/test/Instrumentation/HWAddressSanitizer/exception-lifetime.ll
new file mode 100644
index 0000000000000..3b656ce591dac
--- /dev/null
+++ b/llvm/test/Instrumentation/HWAddressSanitizer/exception-lifetime.ll
@@ -0,0 +1,59 @@
+; Test allocas with multiple lifetime ends, as frequently seen for exception
+; handling.
+;
+; RUN: opt -hwasan -hwasan-use-after-scope -S -o - %s | FileCheck %s --check-prefix=CHECK
+
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64--linux-android"
+
+declare void @mayFail(i32* %x) sanitize_hwaddress
+declare void @onExcept(i32* %x) sanitize_hwaddress
+
+declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) nounwind
+declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) nounwind
+declare i32 @__gxx_personality_v0(...)
+
+define void @test() sanitize_hwaddress personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
+entry:
+ %x = alloca i32, align 8
+ %exn.slot = alloca i8*, align 8
+ %ehselector.slot = alloca i32, align 4
+ %0 = bitcast i32* %x to i8*
+ call void @llvm.lifetime.start.p0i8(i64 8, i8* %0)
+ invoke void @mayFail(i32* %x) to label %invoke.cont unwind label %lpad
+
+invoke.cont: ; preds = %entry
+; CHECK: invoke.cont:
+; CHECK: call void @llvm.memset.p0i8.i64(i8* align 1 %31, i8 0, i64 1, i1 false)
+; CHECK: call void @llvm.lifetime.end.p0i8(i64 8, i8* %28)
+; CHECK: ret void
+
+ %1 = bitcast i32* %x to i8*
+ call void @llvm.lifetime.end.p0i8(i64 8, i8* %1)
+ ret void
+
+lpad: ; preds = %entry
+; CHECK: lpad
+; CHECK: %41 = getelementptr i8, i8* %17, i64 %40
+; CHECK: call void @llvm.memset.p0i8.i64(i8* align 1 %41, i8 0, i64 1, i1 false)
+; CHECK: call void @llvm.lifetime.end.p0i8(i64 8, i8* %38)
+; CHECK: br label %eh.resume
+
+ %2 = landingpad { i8*, i32 }
+ cleanup
+ %3 = extractvalue { i8*, i32 } %2, 0
+ store i8* %3, i8** %exn.slot, align 8
+ %4 = extractvalue { i8*, i32 } %2, 1
+ store i32 %4, i32* %ehselector.slot, align 4
+ call void @onExcept(i32* %x) #18
+ %5 = bitcast i32* %x to i8*
+ call void @llvm.lifetime.end.p0i8(i64 8, i8* %5)
+ br label %eh.resume
+
+eh.resume: ; preds = %lpad
+ %exn = load i8*, i8** %exn.slot, align 8
+ %sel = load i32, i32* %ehselector.slot, align 4
+ %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn, 0
+ %lpad.val1 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1
+ resume { i8*, i32 } %lpad.val1
+}
More information about the llvm-commits
mailing list