Skip to content

Commit 2d69068

Browse files
committed
Recommit DwarfEHPrepare: insert extra unwind paths for stack protector to instrument
This is a mitigation patch for https://bugs.chromium.org/p/llvm/issues/detail?id=30, where existing stack protection is skipped if a function is returned through by an unwinder rather than the normal call/return path. The recent patch D139254 added the ability to instrument a visible unwind path, at least in the IR case (I'm working on the SelectionDAG instrumentation too) but there are still invisible unwinds it can't reach. So this patch adds logic to DwarfEHPrepare that goes through a function, converting any call that might throw into an invoke to a simple resume cleanup, and adding cleanup clauses to existing landingpads that lack them. Obviously we don't really want to do this if it's wasted effort, so I also exposed requiresStackProtector from the actual StackProtector code to skip the extra paths if they won't be used. Changes: * Move test to AArch64 directory as it relies on target presence. * Re-add Dominator-tree maintenance. Accidentally cherry-picked wrong patch. * Skip adding paths on Windows EH functions. https://reviews.llvm.org/D143637
1 parent 60bbf27 commit 2d69068

File tree

2 files changed

+272
-0
lines changed

2 files changed

+272
-0
lines changed

llvm/lib/CodeGen/DwarfEHPrepare.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/Analysis/DomTreeUpdater.h"
1919
#include "llvm/Analysis/TargetTransformInfo.h"
2020
#include "llvm/CodeGen/RuntimeLibcalls.h"
21+
#include "llvm/CodeGen/StackProtector.h"
2122
#include "llvm/CodeGen/TargetLowering.h"
2223
#include "llvm/CodeGen/TargetPassConfig.h"
2324
#include "llvm/CodeGen/TargetSubtargetInfo.h"
@@ -28,6 +29,7 @@
2829
#include "llvm/IR/Dominators.h"
2930
#include "llvm/IR/EHPersonalities.h"
3031
#include "llvm/IR/Function.h"
32+
#include "llvm/IR/IRBuilder.h"
3133
#include "llvm/IR/Instructions.h"
3234
#include "llvm/IR/Module.h"
3335
#include "llvm/IR/Type.h"
@@ -37,6 +39,7 @@
3739
#include "llvm/Target/TargetMachine.h"
3840
#include "llvm/TargetParser/Triple.h"
3941
#include "llvm/Transforms/Utils/Local.h"
42+
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
4043
#include <cstddef>
4144

4245
using namespace llvm;
@@ -166,7 +169,136 @@ size_t DwarfEHPrepare::pruneUnreachableResumes(
166169
return ResumesLeft;
167170
}
168171

172+
/// If a landingpad block doesn't already have a cleanup case, add one
173+
/// that feeds directly into a resume instruction.
174+
static void addCleanupResumeToLandingPad(BasicBlock &BB, DomTreeUpdater *DTU) {
175+
LandingPadInst *LP = BB.getLandingPadInst();
176+
if (LP->isCleanup())
177+
return;
178+
179+
// There will usually be code testing for the other kinds of exception
180+
// immediately after the landingpad. Working out the far end of that chain is
181+
// tricky, so put our test for the new cleanup case (i.e. selector == 0) at
182+
// the beginning.
183+
BasicBlock *ContBB = SplitBlock(&BB, LP->getNextNode(), DTU);
184+
BB.getTerminator()->eraseFromParent();
185+
186+
LP->setCleanup(true);
187+
IRBuilder<> B(&BB);
188+
Value *Selector = B.CreateExtractValue(LP, 1);
189+
Value *Cmp = B.CreateICmpEQ(Selector, ConstantInt::get(Selector->getType(), 0));
190+
191+
Function *F = BB.getParent();
192+
LLVMContext &Ctx = F->getContext();
193+
BasicBlock *ResumeBB = BasicBlock::Create(Ctx, "resume", F);
194+
ResumeInst::Create(LP, ResumeBB);
195+
196+
B.CreateCondBr(Cmp, ResumeBB, ContBB);
197+
if (DTU) {
198+
SmallVector<DominatorTree::UpdateType> Updates;
199+
Updates.push_back({DominatorTree::Insert, &BB, ResumeBB});
200+
DTU->applyUpdates(Updates);
201+
}
202+
}
203+
204+
/// Create a basic block that has a `landingpad` instruction feeding
205+
/// directly into a `resume`. Will be set to the unwind destination of a new
206+
/// invoke.
207+
static BasicBlock *createCleanupResumeBB(Function &F, Type *LandingPadTy) {
208+
LLVMContext &Ctx = F.getContext();
209+
BasicBlock *BB = BasicBlock::Create(Ctx, "cleanup_resume", &F);
210+
IRBuilder<> B(BB);
211+
212+
// If this is going to be the only landingpad in the function, synthesize the
213+
// standard type all ABIs use, which is essentially `{ ptr, i32 }`.
214+
if (!LandingPadTy)
215+
LandingPadTy =
216+
StructType::get(Type::getInt8PtrTy(Ctx), IntegerType::get(Ctx, 32));
217+
218+
LandingPadInst *Except = B.CreateLandingPad(LandingPadTy, 0);
219+
Except->setCleanup(true);
220+
B.CreateResume(Except);
221+
return BB;
222+
}
223+
224+
/// Convert a call that might throw into an invoke that unwinds to the specified
225+
/// simple landingpad/resume block.
226+
static void changeCallToInvokeResume(CallInst &CI, BasicBlock *CleanupResumeBB,
227+
DomTreeUpdater *DTU) {
228+
BasicBlock *BB = CI.getParent();
229+
BasicBlock *ContBB = SplitBlock(BB, &CI, DTU);
230+
BB->getTerminator()->eraseFromParent();
231+
232+
IRBuilder<> B(BB);
233+
SmallVector<Value *> Args(CI.args());
234+
SmallVector<OperandBundleDef> Bundles;
235+
CI.getOperandBundlesAsDefs(Bundles);
236+
InvokeInst *NewCall =
237+
B.CreateInvoke(CI.getFunctionType(), CI.getCalledOperand(), ContBB,
238+
CleanupResumeBB, Args, Bundles, CI.getName());
239+
NewCall->setAttributes(CI.getAttributes());
240+
NewCall->setCallingConv(CI.getCallingConv());
241+
NewCall->copyMetadata(CI);
242+
243+
if (DTU) {
244+
SmallVector<DominatorTree::UpdateType> Updates;
245+
Updates.push_back({DominatorTree::Insert, BB, CleanupResumeBB});
246+
DTU->applyUpdates(Updates);
247+
}
248+
CI.replaceAllUsesWith(NewCall);
249+
CI.eraseFromParent();
250+
}
251+
252+
/// Ensure that any call in this function that might throw has an associated
253+
/// cleanup/resume that the stack protector can instrument later. Existing
254+
/// invokes will get an added `cleanup` clause if needed, calls will be
255+
/// converted to an invoke with trivial unwind followup.
256+
static void addCleanupPathsForStackProtector(Function &F, DomTreeUpdater *DTU) {
257+
// First add cleanup -> resume paths to all existing landingpads, noting what
258+
// type landingpads in this function actually have along the way.
259+
Type *LandingPadTy = nullptr;
260+
for (Function::iterator FI = F.begin(); FI != F.end(); ++FI) {
261+
BasicBlock &BB = *FI;
262+
if (LandingPadInst *LP = BB.getLandingPadInst()) {
263+
// We can assume the type is broadly compatible with { ptr, i32 } since
264+
// other parts of this pass already try to extract values from it.
265+
LandingPadTy = LP->getType();
266+
addCleanupResumeToLandingPad(BB, DTU);
267+
}
268+
}
269+
270+
// Next convert any call that might throw into an invoke to a resume
271+
// instruction for later instrumentation.
272+
BasicBlock *CleanupResumeBB = nullptr;
273+
for (Function::iterator FI = F.begin(); FI != F.end(); ++FI) {
274+
BasicBlock &BB = *FI;
275+
for (Instruction &I : BB) {
276+
CallInst *CI = dyn_cast<CallInst>(&I);
277+
if (!CI || CI->doesNotThrow())
278+
continue;
279+
280+
// Tail calls cannot use our stack so no need to check whether it was
281+
// corrupted.
282+
if (CI->isTailCall())
283+
continue;
284+
285+
if (!CleanupResumeBB)
286+
CleanupResumeBB = createCleanupResumeBB(F, LandingPadTy);
287+
288+
changeCallToInvokeResume(*CI, CleanupResumeBB, DTU);
289+
290+
// This block has been split, start again on its continuation.
291+
break;
292+
}
293+
}
294+
}
295+
169296
bool DwarfEHPrepare::InsertUnwindResumeCalls() {
297+
if (F.hasPersonalityFn() &&
298+
!isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn())) &&
299+
StackProtector::requiresStackProtector(&F, nullptr))
300+
addCleanupPathsForStackProtector(F, DTU);
301+
170302
SmallVector<ResumeInst *, 16> Resumes;
171303
SmallVector<LandingPadInst *, 16> CleanupLPads;
172304
if (F.doesNotThrow())
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
; RUN: opt --dwarfehprepare %s -S -o - -mtriple=aarch64-linux-gnu | FileCheck %s
2+
3+
define void @call() sspreq personality ptr @__gxx_personality_v0 {
4+
; CHECK-LABEL: define void @call()
5+
; CHECK: invoke void @bar()
6+
; CHECK: to label %[[CONT:.*]] unwind label %[[CLEANUP:.*]]
7+
; CHECK: [[CONT]]:
8+
; CHECK: ret void
9+
; CHECK: [[CLEANUP]]:
10+
; CHECK: [[LP:%.*]] = landingpad { ptr, i32 }
11+
; CHECK: cleanup
12+
; CHECK: [[EXN:%.*]] = extractvalue { ptr, i32 } [[LP]], 0
13+
; CHECK: call void @_Unwind_Resume(ptr [[EXN]])
14+
; CHECK: unreachable
15+
call void @bar()
16+
ret void
17+
}
18+
19+
define void @invoke_no_cleanup() sspreq personality ptr @__gxx_personality_v0 {
20+
; CHECK-LABEL: define void @invoke_no_cleanup
21+
; CHECK: invoke void @bar()
22+
; CHECK: to label %done unwind label %catch
23+
24+
; CHECK: catch:
25+
; CHECK: [[LP:%.*]] = landingpad { ptr, i32 }
26+
; CHECK: cleanup
27+
; CHECK: catch ptr null
28+
; CHECK: [[SEL:%.*]] = extractvalue { ptr, i32 } [[LP]], 1
29+
; CHECK: [[CMP:%.*]] = icmp eq i32 [[SEL]], 0
30+
; CHECK: br i1 [[CMP]], label %[[RESUME:.*]], label %[[SPLIT:.*]]
31+
32+
; CHECK: [[SPLIT]]:
33+
; CHECK: br label %done
34+
35+
; CHECK: done:
36+
; CHECK: ret void
37+
38+
; CHECK: [[RESUME]]:
39+
; CHECK: [[EXN:%.*]] = extractvalue { ptr, i32 } [[LP]], 0
40+
; CHECK: call void @_Unwind_Resume(ptr [[EXN]])
41+
; CHECK: unreachable
42+
invoke void @bar() to label %done unwind label %catch
43+
44+
catch:
45+
%lp = landingpad { ptr, i32 }
46+
catch ptr null
47+
br label %done
48+
49+
done:
50+
ret void
51+
}
52+
53+
define void @invoke_no_cleanup_catches() sspreq personality ptr @__gxx_personality_v0 {
54+
; CHECK-LABEL: define void @invoke_no_cleanup_catches
55+
; CHECK: invoke void @bar()
56+
; CHECK: to label %done unwind label %catch
57+
58+
; CHECK: catch:
59+
; CHECK: [[LP:%.*]] = landingpad { ptr, i32 }
60+
; CHECK: cleanup
61+
; CHECK: catch ptr null
62+
; CHECK: [[SEL:%.*]] = extractvalue { ptr, i32 } [[LP]], 1
63+
; CEHCK: [[CMP:%.*]] = icmp eq i32 [[SEL]], 0
64+
; CEHCK: br i1 [[CMP]], label %[[RESUME:.*]], label %[[SPLIT:.*]]
65+
66+
; CHECK: [[SPLIT]]:
67+
; CHECK: %exn = extractvalue { ptr, i32 } %lp, 0
68+
; CHECK: invoke ptr @__cxa_begin_catch(ptr %exn)
69+
; CHECK: to label %[[SPLIT2:.*]] unwind label %[[CLEANUP_RESUME:.*]]
70+
71+
; CHECK: [[SPLIT2]]:
72+
; CHECK: invoke void @__cxa_end_catch()
73+
; CHECK: to label %[[SPLIT3:.*]] unwind label %[[CLEANUP_RESUME:.*]]
74+
75+
; CHECK: [[SPLIT3]]:
76+
; CHECK: br label %done
77+
78+
; CHECK: done:
79+
; CHECK: ret void
80+
81+
; CHECK: [[RESUME]]:
82+
; CHECK: [[EXN1:%.*]] = extractvalue { ptr, i32 } [[LP]], 0
83+
; CHECK: br label %[[RESUME_MERGE:.*]]
84+
85+
; CHECK: [[CLEANUP_RESUME]]:
86+
; CHECK: [[LP:%.*]] = landingpad { ptr, i32 }
87+
; CHECK: cleanup
88+
; CHECK: [[EXN2:%.*]] = extractvalue { ptr, i32 } [[LP]], 0
89+
; CHECK: br label %[[RESUME_MERGE]]
90+
91+
; CHECK: [[RESUME_MERGE]]:
92+
; CHECK: [[EXN_PHI:%.*]] = phi ptr [ [[EXN1]], %[[RESUME]] ], [ [[EXN2]], %[[CLEANUP_RESUME]] ]
93+
; CHECK: call void @_Unwind_Resume(ptr [[EXN_PHI]])
94+
; CHECK: unreachable
95+
invoke void @bar() to label %done unwind label %catch
96+
97+
catch:
98+
%lp = landingpad { ptr, i32 }
99+
catch ptr null
100+
%exn = extractvalue { ptr, i32 } %lp, 0
101+
call ptr @__cxa_begin_catch(ptr %exn)
102+
call void @__cxa_end_catch()
103+
br label %done
104+
105+
done:
106+
ret void
107+
}
108+
109+
; Don't try to invoke any intrinsics.
110+
define ptr @call_intrinsic() sspreq personality ptr @__gxx_personality_v0 {
111+
; CHECK-LABEL: define ptr @call_intrinsic
112+
; CHECK: call ptr @llvm.frameaddress.p0(i32 0)
113+
%res = call ptr @llvm.frameaddress.p0(i32 0)
114+
ret ptr %res
115+
}
116+
117+
; Check we go along with the existing landingpad type, even if it's a bit
118+
; outside the normal.
119+
define void @weird_landingpad() sspreq personality ptr @__gxx_personality_v0 {
120+
; CHECK-LABEL: define void @weird_landingpad
121+
; CHECK: landingpad { ptr, i64 }
122+
; CHECK: landingpad { ptr, i64 }
123+
invoke void @bar() to label %done unwind label %catch
124+
125+
catch:
126+
%lp = landingpad { ptr, i64 }
127+
catch ptr null
128+
resume { ptr, i64 } %lp
129+
; br label %done
130+
131+
done:
132+
call void @bar()
133+
ret void
134+
}
135+
136+
declare void @bar()
137+
declare i32 @__gxx_personality_v0(...)
138+
declare ptr @__cxa_begin_catch(ptr)
139+
declare void @__cxa_end_catch()
140+
declare ptr @llvm.frameaddress.p0(i32)

0 commit comments

Comments
 (0)