Skip to content

Commit e7356fb

Browse files
committed
[nfc] [hwasan] factor out logic to collect info about stack
this is the first step in unifying some of the logic between hwasan and mte stack tagging. this only moves around code, changes to converge different implementations of the same logic follow later. Reviewed By: eugenis Differential Revision: https://reviews.llvm.org/D118947
1 parent ce5588f commit e7356fb

File tree

4 files changed

+111
-88
lines changed

4 files changed

+111
-88
lines changed

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/IR/Module.h"
2222

2323
namespace llvm {
24+
namespace memtag {
2425
// For an alloca valid between lifetime markers Start and Ends, call the
2526
// Callback for all possible exits out of the lifetime in the containing
2627
// function, which can return from the instructions in RetVec.
@@ -71,6 +72,34 @@ bool isStandardLifetime(const SmallVectorImpl<IntrinsicInst *> &LifetimeStart,
7172

7273
Instruction *getUntagLocationIfFunctionExit(Instruction &Inst);
7374

75+
struct AllocaInfo {
76+
AllocaInst *AI;
77+
SmallVector<IntrinsicInst *, 2> LifetimeStart;
78+
SmallVector<IntrinsicInst *, 2> LifetimeEnd;
79+
};
80+
81+
struct StackInfo {
82+
MapVector<AllocaInst *, AllocaInfo> AllocasToInstrument;
83+
SmallVector<Instruction *, 4> UnrecognizedLifetimes;
84+
DenseMap<AllocaInst *, std::vector<DbgVariableIntrinsic *>> AllocaDbgMap;
85+
SmallVector<Instruction *, 8> RetVec;
86+
bool CallsReturnTwice = false;
87+
};
88+
89+
class StackInfoBuilder {
90+
public:
91+
StackInfoBuilder(std::function<bool(const AllocaInst &)> IsInterestingAlloca)
92+
: IsInterestingAlloca(IsInterestingAlloca) {}
93+
94+
void visit(Instruction &Inst);
95+
StackInfo &get() { return Info; };
96+
97+
private:
98+
StackInfo Info;
99+
std::function<bool(const AllocaInst &)> IsInterestingAlloca;
100+
};
101+
102+
} // namespace memtag
74103
} // namespace llvm
75104

76105
#endif

llvm/lib/Target/AArch64/AArch64StackTagging.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ bool AArch64StackTagging::runOnFunction(Function &Fn) {
583583
continue;
584584
}
585585

586-
Instruction *ExitUntag = getUntagLocationIfFunctionExit(I);
586+
Instruction *ExitUntag = memtag::getUntagLocationIfFunctionExit(I);
587587
if (ExitUntag)
588588
RetVec.push_back(ExitUntag);
589589
}
@@ -643,8 +643,8 @@ bool AArch64StackTagging::runOnFunction(Function &Fn) {
643643

644644
bool StandardLifetime =
645645
UnrecognizedLifetimes.empty() &&
646-
isStandardLifetime(Info.LifetimeStart, Info.LifetimeEnd, DT,
647-
ClMaxLifetimes);
646+
memtag::isStandardLifetime(Info.LifetimeStart, Info.LifetimeEnd, DT,
647+
ClMaxLifetimes);
648648
// Calls to functions that may return twice (e.g. setjmp) confuse the
649649
// postdominator analysis, and will leave us to keep memory tagged after
650650
// function return. Work around this by always untagging at every return
@@ -659,8 +659,8 @@ bool AArch64StackTagging::runOnFunction(Function &Fn) {
659659

660660
auto TagEnd = [&](Instruction *Node) { untagAlloca(AI, Node, Size); };
661661
if (!DT || !PDT ||
662-
!forAllReachableExits(*DT, *PDT, Start, Info.LifetimeEnd, RetVec,
663-
TagEnd)) {
662+
!memtag::forAllReachableExits(*DT, *PDT, Start, Info.LifetimeEnd,
663+
RetVec, TagEnd)) {
664664
for (auto *End : Info.LifetimeEnd)
665665
End->eraseFromParent();
666666
}

llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp

Lines changed: 32 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,6 @@ bool shouldDetectUseAfterScope(const Triple &TargetTriple) {
247247
/// An instrumentation pass implementing detection of addressability bugs
248248
/// using tagged pointers.
249249
class HWAddressSanitizer {
250-
private:
251-
struct AllocaInfo {
252-
AllocaInst *AI;
253-
SmallVector<IntrinsicInst *, 2> LifetimeStart;
254-
SmallVector<IntrinsicInst *, 2> LifetimeEnd;
255-
};
256-
257250
public:
258251
HWAddressSanitizer(Module &M, bool CompileKernel, bool Recover,
259252
const StackSafetyGlobalInfo *SSI)
@@ -269,7 +262,7 @@ class HWAddressSanitizer {
269262
void setSSI(const StackSafetyGlobalInfo *S) { SSI = S; }
270263

271264
DenseMap<AllocaInst *, AllocaInst *> padInterestingAllocas(
272-
const MapVector<AllocaInst *, AllocaInfo> &AllocasToInstrument);
265+
const MapVector<AllocaInst *, memtag::AllocaInfo> &AllocasToInstrument);
273266
bool sanitizeFunction(Function &F,
274267
llvm::function_ref<const DominatorTree &()> GetDT,
275268
llvm::function_ref<const PostDominatorTree &()> GetPDT);
@@ -304,14 +297,10 @@ class HWAddressSanitizer {
304297
void tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag, size_t Size);
305298
Value *tagPointer(IRBuilder<> &IRB, Type *Ty, Value *PtrLong, Value *Tag);
306299
Value *untagPointer(IRBuilder<> &IRB, Value *PtrLong);
307-
bool instrumentStack(
308-
bool ShouldDetectUseAfterScope,
309-
MapVector<AllocaInst *, AllocaInfo> &AllocasToInstrument,
310-
SmallVector<Instruction *, 4> &UnrecognizedLifetimes,
311-
DenseMap<AllocaInst *, std::vector<DbgVariableIntrinsic *>> &AllocaDbgMap,
312-
SmallVectorImpl<Instruction *> &RetVec, Value *StackTag,
313-
llvm::function_ref<const DominatorTree &()> GetDT,
314-
llvm::function_ref<const PostDominatorTree &()> GetPDT);
300+
bool instrumentStack(bool ShouldDetectUseAfterScope, memtag::StackInfo &Info,
301+
Value *StackTag,
302+
llvm::function_ref<const DominatorTree &()> GetDT,
303+
llvm::function_ref<const PostDominatorTree &()> GetPDT);
315304
Value *readRegister(IRBuilder<> &IRB, StringRef Name);
316305
bool instrumentLandingPads(SmallVectorImpl<Instruction *> &RetVec);
317306
Value *getNextTagWithCall(IRBuilder<> &IRB);
@@ -1325,11 +1314,7 @@ bool HWAddressSanitizer::instrumentLandingPads(
13251314
}
13261315

13271316
bool HWAddressSanitizer::instrumentStack(
1328-
bool ShouldDetectUseAfterScope,
1329-
MapVector<AllocaInst *, AllocaInfo> &AllocasToInstrument,
1330-
SmallVector<Instruction *, 4> &UnrecognizedLifetimes,
1331-
DenseMap<AllocaInst *, std::vector<DbgVariableIntrinsic *>> &AllocaDbgMap,
1332-
SmallVectorImpl<Instruction *> &RetVec, Value *StackTag,
1317+
bool ShouldDetectUseAfterScope, memtag::StackInfo &SInfo, Value *StackTag,
13331318
llvm::function_ref<const DominatorTree &()> GetDT,
13341319
llvm::function_ref<const PostDominatorTree &()> GetPDT) {
13351320
// Ideally, we want to calculate tagged stack base pointer, and rewrite all
@@ -1339,10 +1324,10 @@ bool HWAddressSanitizer::instrumentStack(
13391324
// This generates one extra instruction per alloca use.
13401325
unsigned int I = 0;
13411326

1342-
for (auto &KV : AllocasToInstrument) {
1327+
for (auto &KV : SInfo.AllocasToInstrument) {
13431328
auto N = I++;
13441329
auto *AI = KV.first;
1345-
AllocaInfo &Info = KV.second;
1330+
memtag::AllocaInfo &Info = KV.second;
13461331
IRBuilder<> IRB(AI->getNextNode());
13471332

13481333
// Replace uses of the alloca with tagged address.
@@ -1356,7 +1341,7 @@ bool HWAddressSanitizer::instrumentStack(
13561341
AI->replaceUsesWithIf(Replacement,
13571342
[AILong](Use &U) { return U.getUser() != AILong; });
13581343

1359-
for (auto *DDI : AllocaDbgMap.lookup(AI)) {
1344+
for (auto *DDI : SInfo.AllocaDbgMap.lookup(AI)) {
13601345
// Prepend "tag_offset, N" to the dwarf expression.
13611346
// Tag offset logically applies to the alloca pointer, and it makes sense
13621347
// to put it at the beginning of the expression.
@@ -1376,21 +1361,22 @@ bool HWAddressSanitizer::instrumentStack(
13761361
tagAlloca(IRB, AI, UARTag, AlignedSize);
13771362
};
13781363
bool StandardLifetime =
1379-
UnrecognizedLifetimes.empty() &&
1380-
isStandardLifetime(Info.LifetimeStart, Info.LifetimeEnd, &GetDT(),
1381-
ClMaxLifetimes);
1364+
SInfo.UnrecognizedLifetimes.empty() &&
1365+
memtag::isStandardLifetime(Info.LifetimeStart, Info.LifetimeEnd,
1366+
&GetDT(), ClMaxLifetimes);
13821367
if (ShouldDetectUseAfterScope && StandardLifetime) {
13831368
IntrinsicInst *Start = Info.LifetimeStart[0];
13841369
IRB.SetInsertPoint(Start->getNextNode());
13851370
tagAlloca(IRB, AI, Tag, Size);
1386-
if (!forAllReachableExits(GetDT(), GetPDT(), Start, Info.LifetimeEnd,
1387-
RetVec, TagEnd)) {
1371+
if (!memtag::forAllReachableExits(GetDT(), GetPDT(), Start,
1372+
Info.LifetimeEnd, SInfo.RetVec,
1373+
TagEnd)) {
13881374
for (auto *End : Info.LifetimeEnd)
13891375
End->eraseFromParent();
13901376
}
13911377
} else {
13921378
tagAlloca(IRB, AI, Tag, Size);
1393-
for (auto *RI : RetVec)
1379+
for (auto *RI : SInfo.RetVec)
13941380
TagEnd(RI);
13951381
if (!StandardLifetime) {
13961382
for (auto &II : Info.LifetimeStart)
@@ -1400,7 +1386,7 @@ bool HWAddressSanitizer::instrumentStack(
14001386
}
14011387
}
14021388
}
1403-
for (auto &I : UnrecognizedLifetimes)
1389+
for (auto &I : SInfo.UnrecognizedLifetimes)
14041390
I->eraseFromParent();
14051391
return true;
14061392
}
@@ -1424,7 +1410,7 @@ bool HWAddressSanitizer::isInterestingAlloca(const AllocaInst &AI) {
14241410
}
14251411

14261412
DenseMap<AllocaInst *, AllocaInst *> HWAddressSanitizer::padInterestingAllocas(
1427-
const MapVector<AllocaInst *, AllocaInfo> &AllocasToInstrument) {
1413+
const MapVector<AllocaInst *, memtag::AllocaInfo> &AllocasToInstrument) {
14281414
DenseMap<AllocaInst *, AllocaInst *> AllocaToPaddedAllocaMap;
14291415
for (auto &KV : AllocasToInstrument) {
14301416
AllocaInst *AI = KV.first;
@@ -1469,52 +1455,13 @@ bool HWAddressSanitizer::sanitizeFunction(
14691455

14701456
SmallVector<InterestingMemoryOperand, 16> OperandsToInstrument;
14711457
SmallVector<MemIntrinsic *, 16> IntrinToInstrument;
1472-
MapVector<AllocaInst *, AllocaInfo> AllocasToInstrument;
1473-
SmallVector<Instruction *, 8> RetVec;
14741458
SmallVector<Instruction *, 8> LandingPadVec;
1475-
SmallVector<Instruction *, 4> UnrecognizedLifetimes;
1476-
DenseMap<AllocaInst *, std::vector<DbgVariableIntrinsic *>> AllocaDbgMap;
1477-
bool CallsReturnTwice = false;
1459+
1460+
memtag::StackInfoBuilder SIB(
1461+
[this](const AllocaInst &AI) { return isInterestingAlloca(AI); });
14781462
for (auto &Inst : instructions(F)) {
1479-
if (CallInst *CI = dyn_cast<CallInst>(&Inst)) {
1480-
if (CI->canReturnTwice()) {
1481-
CallsReturnTwice = true;
1482-
}
1483-
}
14841463
if (InstrumentStack) {
1485-
if (AllocaInst *AI = dyn_cast<AllocaInst>(&Inst)) {
1486-
if (isInterestingAlloca(*AI))
1487-
AllocasToInstrument.insert({AI, {}});
1488-
continue;
1489-
}
1490-
auto *II = dyn_cast<IntrinsicInst>(&Inst);
1491-
if (II && (II->getIntrinsicID() == Intrinsic::lifetime_start ||
1492-
II->getIntrinsicID() == Intrinsic::lifetime_end)) {
1493-
AllocaInst *AI = findAllocaForValue(II->getArgOperand(1));
1494-
if (!AI) {
1495-
UnrecognizedLifetimes.push_back(&Inst);
1496-
continue;
1497-
}
1498-
if (!isInterestingAlloca(*AI))
1499-
continue;
1500-
if (II->getIntrinsicID() == Intrinsic::lifetime_start)
1501-
AllocasToInstrument[AI].LifetimeStart.push_back(II);
1502-
else
1503-
AllocasToInstrument[AI].LifetimeEnd.push_back(II);
1504-
continue;
1505-
}
1506-
}
1507-
1508-
Instruction *ExitUntag = getUntagLocationIfFunctionExit(Inst);
1509-
if (ExitUntag)
1510-
RetVec.push_back(ExitUntag);
1511-
1512-
if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&Inst)) {
1513-
for (Value *V : DVI->location_ops()) {
1514-
if (auto *Alloca = dyn_cast_or_null<AllocaInst>(V))
1515-
if (!AllocaDbgMap.count(Alloca) || AllocaDbgMap[Alloca].back() != DVI)
1516-
AllocaDbgMap[Alloca].push_back(DVI);
1517-
}
1464+
SIB.visit(Inst);
15181465
}
15191466

15201467
if (InstrumentLandingPads && isa<LandingPadInst>(Inst))
@@ -1527,22 +1474,24 @@ bool HWAddressSanitizer::sanitizeFunction(
15271474
IntrinToInstrument.push_back(MI);
15281475
}
15291476

1477+
memtag::StackInfo &SInfo = SIB.get();
1478+
15301479
initializeCallbacks(*F.getParent());
15311480

15321481
bool Changed = false;
15331482

15341483
if (!LandingPadVec.empty())
15351484
Changed |= instrumentLandingPads(LandingPadVec);
15361485

1537-
if (AllocasToInstrument.empty() && F.hasPersonalityFn() &&
1486+
if (SInfo.AllocasToInstrument.empty() && F.hasPersonalityFn() &&
15381487
F.getPersonalityFn()->getName() == kHwasanPersonalityThunkName) {
15391488
// __hwasan_personality_thunk is a no-op for functions without an
15401489
// instrumented stack, so we can drop it.
15411490
F.setPersonalityFn(nullptr);
15421491
Changed = true;
15431492
}
15441493

1545-
if (AllocasToInstrument.empty() && OperandsToInstrument.empty() &&
1494+
if (SInfo.AllocasToInstrument.empty() && OperandsToInstrument.empty() &&
15461495
IntrinToInstrument.empty())
15471496
return Changed;
15481497

@@ -1552,24 +1501,24 @@ bool HWAddressSanitizer::sanitizeFunction(
15521501
IRBuilder<> EntryIRB(InsertPt);
15531502
emitPrologue(EntryIRB,
15541503
/*WithFrameRecord*/ ClRecordStackHistory &&
1555-
Mapping.WithFrameRecord && !AllocasToInstrument.empty());
1504+
Mapping.WithFrameRecord &&
1505+
!SInfo.AllocasToInstrument.empty());
15561506

1557-
if (!AllocasToInstrument.empty()) {
1507+
if (!SInfo.AllocasToInstrument.empty()) {
15581508
Value *StackTag =
15591509
ClGenerateTagsWithCalls ? nullptr : getStackBaseTag(EntryIRB);
15601510
// Calls to functions that may return twice (e.g. setjmp) confuse the
15611511
// postdominator analysis, and will leave us to keep memory tagged after
15621512
// function return. Work around this by always untagging at every return
15631513
// statement if return_twice functions are called.
1564-
instrumentStack(DetectUseAfterScope && !CallsReturnTwice,
1565-
AllocasToInstrument, UnrecognizedLifetimes, AllocaDbgMap,
1566-
RetVec, StackTag, GetDT, GetPDT);
1514+
instrumentStack(DetectUseAfterScope && !SInfo.CallsReturnTwice, SIB.get(),
1515+
StackTag, GetDT, GetPDT);
15671516
}
15681517
// Pad and align each of the allocas that we instrumented to stop small
15691518
// uninteresting allocas from hiding in instrumented alloca's padding and so
15701519
// that we have enough space to store real tags for short granules.
15711520
DenseMap<AllocaInst *, AllocaInst *> AllocaToPaddedAllocaMap =
1572-
padInterestingAllocas(AllocasToInstrument);
1521+
padInterestingAllocas(SInfo.AllocasToInstrument);
15731522

15741523
if (!AllocaToPaddedAllocaMap.empty()) {
15751524
for (auto &Inst : instructions(F)) {

llvm/lib/Transforms/Utils/MemoryTaggingSupport.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212

1313
#include "llvm/Transforms/Utils/MemoryTaggingSupport.h"
1414

15+
#include "llvm/Analysis/ValueTracking.h"
16+
1517
namespace llvm {
18+
namespace memtag {
1619
namespace {
1720
bool maybeReachableFromEachOther(const SmallVectorImpl<IntrinsicInst *> &Insts,
1821
const DominatorTree *DT, size_t MaxLifetimes) {
@@ -54,4 +57,46 @@ Instruction *getUntagLocationIfFunctionExit(Instruction &Inst) {
5457
}
5558
return nullptr;
5659
}
60+
61+
void StackInfoBuilder::visit(Instruction &Inst) {
62+
if (CallInst *CI = dyn_cast<CallInst>(&Inst)) {
63+
if (CI->canReturnTwice()) {
64+
Info.CallsReturnTwice = true;
65+
}
66+
}
67+
if (AllocaInst *AI = dyn_cast<AllocaInst>(&Inst)) {
68+
if (IsInterestingAlloca(*AI))
69+
Info.AllocasToInstrument.insert({AI, {}});
70+
return;
71+
}
72+
auto *II = dyn_cast<IntrinsicInst>(&Inst);
73+
if (II && (II->getIntrinsicID() == Intrinsic::lifetime_start ||
74+
II->getIntrinsicID() == Intrinsic::lifetime_end)) {
75+
AllocaInst *AI = findAllocaForValue(II->getArgOperand(1));
76+
if (!AI) {
77+
Info.UnrecognizedLifetimes.push_back(&Inst);
78+
return;
79+
}
80+
if (!IsInterestingAlloca(*AI))
81+
return;
82+
if (II->getIntrinsicID() == Intrinsic::lifetime_start)
83+
Info.AllocasToInstrument[AI].LifetimeStart.push_back(II);
84+
else
85+
Info.AllocasToInstrument[AI].LifetimeEnd.push_back(II);
86+
return;
87+
}
88+
if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&Inst)) {
89+
for (Value *V : DVI->location_ops()) {
90+
if (auto *Alloca = dyn_cast_or_null<AllocaInst>(V))
91+
if (!Info.AllocaDbgMap.count(Alloca) ||
92+
Info.AllocaDbgMap[Alloca].back() != DVI)
93+
Info.AllocaDbgMap[Alloca].push_back(DVI);
94+
}
95+
}
96+
Instruction *ExitUntag = getUntagLocationIfFunctionExit(Inst);
97+
if (ExitUntag)
98+
Info.RetVec.push_back(ExitUntag);
99+
}
100+
101+
} // namespace memtag
57102
} // namespace llvm

0 commit comments

Comments
 (0)