Skip to content

Commit 9320a32

Browse files
committed
[MTE] [HWASan] Use LoopInfo for reachability queries.
The reachability queries default to "reachable" after exploring too many basic blocks. LoopInfo helps it skip over the whole loop. Reviewed By: eugenis Differential Revision: https://reviews.llvm.org/D127917
1 parent 46be5fa commit 9320a32

File tree

5 files changed

+93
-16
lines changed

5 files changed

+93
-16
lines changed

llvm/include/llvm/Transforms/Utils/MemoryTaggingSupport.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/ADT/MapVector.h"
1616
#include "llvm/ADT/STLFunctionalExtras.h"
1717
#include "llvm/ADT/SmallVector.h"
18+
#include "llvm/Analysis/LoopInfo.h"
1819
#include "llvm/Support/Alignment.h"
1920

2021
namespace llvm {
@@ -33,14 +34,15 @@ namespace memtag {
3334
// the caller should remove Ends to ensure that work done at the other
3435
// exits does not happen outside of the lifetime.
3536
bool forAllReachableExits(const DominatorTree &DT, const PostDominatorTree &PDT,
36-
const Instruction *Start,
37+
const LoopInfo &LI, const Instruction *Start,
3738
const SmallVectorImpl<IntrinsicInst *> &Ends,
3839
const SmallVectorImpl<Instruction *> &RetVec,
3940
llvm::function_ref<void(Instruction *)> Callback);
4041

4142
bool isStandardLifetime(const SmallVectorImpl<IntrinsicInst *> &LifetimeStart,
4243
const SmallVectorImpl<IntrinsicInst *> &LifetimeEnd,
43-
const DominatorTree *DT, size_t MaxLifetimes);
44+
const DominatorTree *DT, const LoopInfo *LI,
45+
size_t MaxLifetimes);
4446

4547
Instruction *getUntagLocationIfFunctionExit(Instruction &Inst);
4648

llvm/lib/Target/AArch64/AArch64StackTagging.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "llvm/Transforms/Utils/MemoryTaggingSupport.h"
5959
#include <cassert>
6060
#include <iterator>
61+
#include <memory>
6162
#include <utility>
6263

6364
using namespace llvm;
@@ -523,6 +524,15 @@ bool AArch64StackTagging::runOnFunction(Function &Fn) {
523524
PDT = DeletePDT.get();
524525
}
525526

527+
std::unique_ptr<LoopInfo> DeleteLI;
528+
LoopInfo *LI = nullptr;
529+
if (auto *LIWP = getAnalysisIfAvailable<LoopInfoWrapperPass>()) {
530+
LI = &LIWP->getLoopInfo();
531+
} else {
532+
DeleteLI = std::make_unique<LoopInfo>(*DT);
533+
LI = DeleteLI.get();
534+
}
535+
526536
SetTagFunc =
527537
Intrinsic::getDeclaration(F->getParent(), Intrinsic::aarch64_settag);
528538

@@ -555,7 +565,7 @@ bool AArch64StackTagging::runOnFunction(Function &Fn) {
555565
// statement if return_twice functions are called.
556566
bool StandardLifetime =
557567
SInfo.UnrecognizedLifetimes.empty() &&
558-
memtag::isStandardLifetime(Info.LifetimeStart, Info.LifetimeEnd, DT,
568+
memtag::isStandardLifetime(Info.LifetimeStart, Info.LifetimeEnd, DT, LI,
559569
ClMaxLifetimes) &&
560570
!SInfo.CallsReturnTwice;
561571
if (StandardLifetime) {
@@ -567,7 +577,7 @@ bool AArch64StackTagging::runOnFunction(Function &Fn) {
567577

568578
auto TagEnd = [&](Instruction *Node) { untagAlloca(AI, Node, Size); };
569579
if (!DT || !PDT ||
570-
!memtag::forAllReachableExits(*DT, *PDT, Start, Info.LifetimeEnd,
580+
!memtag::forAllReachableExits(*DT, *PDT, *LI, Start, Info.LifetimeEnd,
571581
SInfo.RetVec, TagEnd)) {
572582
for (auto *End : Info.LifetimeEnd)
573583
End->eraseFromParent();

llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ class HWAddressSanitizer {
292292
Value *tagPointer(IRBuilder<> &IRB, Type *Ty, Value *PtrLong, Value *Tag);
293293
Value *untagPointer(IRBuilder<> &IRB, Value *PtrLong);
294294
bool instrumentStack(memtag::StackInfo &Info, Value *StackTag,
295-
const DominatorTree &DT, const PostDominatorTree &PDT);
295+
const DominatorTree &DT, const PostDominatorTree &PDT,
296+
const LoopInfo &LI);
296297
Value *readRegister(IRBuilder<> &IRB, StringRef Name);
297298
bool instrumentLandingPads(SmallVectorImpl<Instruction *> &RetVec);
298299
Value *getNextTagWithCall(IRBuilder<> &IRB);
@@ -1217,7 +1218,8 @@ static bool isLifetimeIntrinsic(Value *V) {
12171218
bool HWAddressSanitizer::instrumentStack(memtag::StackInfo &SInfo,
12181219
Value *StackTag,
12191220
const DominatorTree &DT,
1220-
const PostDominatorTree &PDT) {
1221+
const PostDominatorTree &PDT,
1222+
const LoopInfo &LI) {
12211223
// Ideally, we want to calculate tagged stack base pointer, and rewrite all
12221224
// alloca addresses using that. Unfortunately, offsets are not known yet
12231225
// (unless we use ASan-style mega-alloca). Instead we keep the base tag in a
@@ -1294,13 +1296,13 @@ bool HWAddressSanitizer::instrumentStack(memtag::StackInfo &SInfo,
12941296
bool StandardLifetime =
12951297
SInfo.UnrecognizedLifetimes.empty() &&
12961298
memtag::isStandardLifetime(Info.LifetimeStart, Info.LifetimeEnd, &DT,
1297-
ClMaxLifetimes) &&
1299+
&LI, ClMaxLifetimes) &&
12981300
!SInfo.CallsReturnTwice;
12991301
if (DetectUseAfterScope && StandardLifetime) {
13001302
IntrinsicInst *Start = Info.LifetimeStart[0];
13011303
IRB.SetInsertPoint(Start->getNextNode());
13021304
tagAlloca(IRB, AI, Tag, Size);
1303-
if (!memtag::forAllReachableExits(DT, PDT, Start, Info.LifetimeEnd,
1305+
if (!memtag::forAllReachableExits(DT, PDT, LI, Start, Info.LifetimeEnd,
13041306
SInfo.RetVec, TagEnd)) {
13051307
for (auto *End : Info.LifetimeEnd)
13061308
End->eraseFromParent();
@@ -1405,9 +1407,10 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F,
14051407
if (!SInfo.AllocasToInstrument.empty()) {
14061408
const DominatorTree &DT = FAM.getResult<DominatorTreeAnalysis>(F);
14071409
const PostDominatorTree &PDT = FAM.getResult<PostDominatorTreeAnalysis>(F);
1410+
const LoopInfo &LI = FAM.getResult<LoopAnalysis>(F);
14081411
Value *StackTag =
14091412
ClGenerateTagsWithCalls ? nullptr : getStackBaseTag(EntryIRB);
1410-
instrumentStack(SInfo, StackTag, DT, PDT);
1413+
instrumentStack(SInfo, StackTag, DT, PDT, LI);
14111414
}
14121415

14131416
// If we split the entry block, move any allocas that were originally in the

llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ namespace llvm {
2222
namespace memtag {
2323
namespace {
2424
bool maybeReachableFromEachOther(const SmallVectorImpl<IntrinsicInst *> &Insts,
25-
const DominatorTree *DT, size_t MaxLifetimes) {
25+
const DominatorTree *DT, const LoopInfo *LI,
26+
size_t MaxLifetimes) {
2627
// If we have too many lifetime ends, give up, as the algorithm below is N^2.
2728
if (Insts.size() > MaxLifetimes)
2829
return true;
2930
for (size_t I = 0; I < Insts.size(); ++I) {
3031
for (size_t J = 0; J < Insts.size(); ++J) {
3132
if (I == J)
3233
continue;
33-
if (isPotentiallyReachable(Insts[I], Insts[J], nullptr, DT))
34+
if (isPotentiallyReachable(Insts[I], Insts[J], nullptr, DT, LI))
3435
return true;
3536
}
3637
}
@@ -39,7 +40,7 @@ bool maybeReachableFromEachOther(const SmallVectorImpl<IntrinsicInst *> &Insts,
3940
} // namespace
4041

4142
bool forAllReachableExits(const DominatorTree &DT, const PostDominatorTree &PDT,
42-
const Instruction *Start,
43+
const LoopInfo &LI, const Instruction *Start,
4344
const SmallVectorImpl<IntrinsicInst *> &Ends,
4445
const SmallVectorImpl<Instruction *> &RetVec,
4546
llvm::function_ref<void(Instruction *)> Callback) {
@@ -54,15 +55,15 @@ bool forAllReachableExits(const DominatorTree &DT, const PostDominatorTree &PDT,
5455
SmallVector<Instruction *, 8> ReachableRetVec;
5556
unsigned NumCoveredExits = 0;
5657
for (auto *RI : RetVec) {
57-
if (!isPotentiallyReachable(Start, RI, nullptr, &DT))
58+
if (!isPotentiallyReachable(Start, RI, nullptr, &DT, &LI))
5859
continue;
5960
ReachableRetVec.push_back(RI);
6061
// If there is an end in the same basic block as the return, we know for
6162
// sure that the return is covered. Otherwise, we can check whether there
6263
// is a way to reach the RI from the start of the lifetime without passing
6364
// through an end.
6465
if (EndBlocks.count(RI->getParent()) > 0 ||
65-
!isPotentiallyReachable(Start, RI, &EndBlocks, &DT)) {
66+
!isPotentiallyReachable(Start, RI, &EndBlocks, &DT, &LI)) {
6667
++NumCoveredExits;
6768
}
6869
}
@@ -83,14 +84,15 @@ bool forAllReachableExits(const DominatorTree &DT, const PostDominatorTree &PDT,
8384

8485
bool isStandardLifetime(const SmallVectorImpl<IntrinsicInst *> &LifetimeStart,
8586
const SmallVectorImpl<IntrinsicInst *> &LifetimeEnd,
86-
const DominatorTree *DT, size_t MaxLifetimes) {
87+
const DominatorTree *DT, const LoopInfo *LI,
88+
size_t MaxLifetimes) {
8789
// An alloca that has exactly one start and end in every possible execution.
8890
// If it has multiple ends, they have to be unreachable from each other, so
8991
// at most one of them is actually used for each execution of the function.
9092
return LifetimeStart.size() == 1 &&
9193
(LifetimeEnd.size() == 1 ||
9294
(LifetimeEnd.size() > 0 &&
93-
!maybeReachableFromEachOther(LifetimeEnd, DT, MaxLifetimes)));
95+
!maybeReachableFromEachOther(LifetimeEnd, DT, LI, MaxLifetimes)));
9496
}
9597

9698
Instruction *getUntagLocationIfFunctionExit(Instruction &Inst) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
; We set a low dom-tree-reachability-max-bbs-to-explore to check whether the
2+
; loop analysis is working. Without skipping over the loop, we would need more
3+
; than 4 BB to reach end from entry.
4+
5+
; RUN: opt -S -dom-tree-reachability-max-bbs-to-explore=4 -aarch64-stack-tagging %s -o - | FileCheck %s
6+
7+
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
8+
target triple = "aarch64"
9+
10+
define dso_local void @foo(i1 %x, i32 %n) sanitize_memtag {
11+
entry:
12+
%c = alloca [1024 x i8], align 1
13+
call void @llvm.lifetime.start.p0(i64 1024, ptr nonnull %c)
14+
%cmp2.not = icmp eq i32 %n, 0
15+
br i1 %x, label %entry2, label %noloop
16+
17+
entry2:
18+
br i1 %cmp2.not, label %for.cond.cleanup, label %for.body
19+
20+
for.cond.cleanup: ; preds = %for.body, %entry
21+
; CHECK-LABEL: for.cond.cleanup:
22+
; CHECK: call{{.*}}settag
23+
; CHECK: call{{.*}}lifetime.end
24+
call void @llvm.lifetime.end.p0(i64 1024, ptr nonnull %c)
25+
call void @bar(ptr noundef nonnull inttoptr (i64 120 to ptr))
26+
br label %end
27+
28+
for.body: ; preds = %entry, %for.body
29+
%i.03 = phi i32 [ %inc, %for.body2 ], [ 0, %entry2 ]
30+
call void @bar(ptr noundef nonnull %c) #3
31+
br label %for.body2
32+
33+
for.body2:
34+
%inc = add nuw nsw i32 %i.03, 1
35+
%cmp = icmp ult i32 %inc, %n
36+
br i1 %cmp, label %for.body, label %for.cond.cleanup, !llvm.loop !13
37+
38+
noloop:
39+
; CHECK-LABEL: noloop:
40+
; CHECK: call{{.*}}settag
41+
; CHECK: call{{.*}}lifetime.end
42+
call void @llvm.lifetime.end.p0(i64 1024, ptr nonnull %c)
43+
br label %end
44+
45+
end:
46+
; CHECK-LABEL: end:
47+
; CHECK-NOT: call{{.*}}settag
48+
ret void
49+
}
50+
51+
; Function Attrs: argmemonly mustprogress nocallback nofree nosync nounwind willreturn
52+
declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0
53+
declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #0
54+
55+
declare dso_local void @bar(ptr noundef)
56+
57+
attributes #0 = { argmemonly mustprogress nocallback nofree nosync nounwind willreturn }
58+
59+
!13 = distinct !{!13, !14}
60+
!14 = !{!"llvm.loop.mustprogress"}

0 commit comments

Comments
 (0)