Skip to content

Commit b8a8ef3

Browse files
committed
[SafeStack] Make sure SafeStack does not break musttail call contract
SafeStack instrumentation should not insert anything inbetween musttail call and return instruction. For every ReturnInst that needs to be instrumented, we adjust the insertion point to the musttail call if exists. Differential Revision: https://reviews.llvm.org/D90702
1 parent 7dcc889 commit b8a8ef3

File tree

2 files changed

+62
-16
lines changed

2 files changed

+62
-16
lines changed

llvm/lib/CodeGen/SafeStack.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ class SafeStack {
151151
Value *getStackGuard(IRBuilder<> &IRB, Function &F);
152152

153153
/// Load stack guard from the frame and check if it has changed.
154-
void checkStackGuard(IRBuilder<> &IRB, Function &F, ReturnInst &RI,
154+
void checkStackGuard(IRBuilder<> &IRB, Function &F, Instruction &RI,
155155
AllocaInst *StackGuardSlot, Value *StackGuard);
156156

157157
/// Find all static allocas, dynamic allocas, return instructions and
@@ -160,23 +160,21 @@ class SafeStack {
160160
void findInsts(Function &F, SmallVectorImpl<AllocaInst *> &StaticAllocas,
161161
SmallVectorImpl<AllocaInst *> &DynamicAllocas,
162162
SmallVectorImpl<Argument *> &ByValArguments,
163-
SmallVectorImpl<ReturnInst *> &Returns,
163+
SmallVectorImpl<Instruction *> &Returns,
164164
SmallVectorImpl<Instruction *> &StackRestorePoints);
165165

166166
/// Calculate the allocation size of a given alloca. Returns 0 if the
167167
/// size can not be statically determined.
168168
uint64_t getStaticAllocaAllocationSize(const AllocaInst* AI);
169169

170170
/// Allocate space for all static allocas in \p StaticAllocas,
171-
/// replace allocas with pointers into the unsafe stack and generate code to
172-
/// restore the stack pointer before all return instructions in \p Returns.
171+
/// replace allocas with pointers into the unsafe stack.
173172
///
174173
/// \returns A pointer to the top of the unsafe stack after all unsafe static
175174
/// allocas are allocated.
176175
Value *moveStaticAllocasToUnsafeStack(IRBuilder<> &IRB, Function &F,
177176
ArrayRef<AllocaInst *> StaticAllocas,
178177
ArrayRef<Argument *> ByValArguments,
179-
ArrayRef<ReturnInst *> Returns,
180178
Instruction *BasePointer,
181179
AllocaInst *StackGuardSlot);
182180

@@ -383,7 +381,7 @@ void SafeStack::findInsts(Function &F,
383381
SmallVectorImpl<AllocaInst *> &StaticAllocas,
384382
SmallVectorImpl<AllocaInst *> &DynamicAllocas,
385383
SmallVectorImpl<Argument *> &ByValArguments,
386-
SmallVectorImpl<ReturnInst *> &Returns,
384+
SmallVectorImpl<Instruction *> &Returns,
387385
SmallVectorImpl<Instruction *> &StackRestorePoints) {
388386
for (Instruction &I : instructions(&F)) {
389387
if (auto AI = dyn_cast<AllocaInst>(&I)) {
@@ -401,7 +399,10 @@ void SafeStack::findInsts(Function &F,
401399
DynamicAllocas.push_back(AI);
402400
}
403401
} else if (auto RI = dyn_cast<ReturnInst>(&I)) {
404-
Returns.push_back(RI);
402+
if (CallInst *CI = I.getParent()->getTerminatingMustTailCall())
403+
Returns.push_back(CI);
404+
else
405+
Returns.push_back(RI);
405406
} else if (auto CI = dyn_cast<CallInst>(&I)) {
406407
// setjmps require stack restore.
407408
if (CI->getCalledFunction() && CI->canReturnTwice())
@@ -465,7 +466,7 @@ SafeStack::createStackRestorePoints(IRBuilder<> &IRB, Function &F,
465466
return DynamicTop;
466467
}
467468

468-
void SafeStack::checkStackGuard(IRBuilder<> &IRB, Function &F, ReturnInst &RI,
469+
void SafeStack::checkStackGuard(IRBuilder<> &IRB, Function &F, Instruction &RI,
469470
AllocaInst *StackGuardSlot, Value *StackGuard) {
470471
Value *V = IRB.CreateLoad(StackPtrTy, StackGuardSlot);
471472
Value *Cmp = IRB.CreateICmpNE(StackGuard, V);
@@ -490,8 +491,8 @@ void SafeStack::checkStackGuard(IRBuilder<> &IRB, Function &F, ReturnInst &RI,
490491
/// prologue into a local variable and restore it in the epilogue.
491492
Value *SafeStack::moveStaticAllocasToUnsafeStack(
492493
IRBuilder<> &IRB, Function &F, ArrayRef<AllocaInst *> StaticAllocas,
493-
ArrayRef<Argument *> ByValArguments, ArrayRef<ReturnInst *> Returns,
494-
Instruction *BasePointer, AllocaInst *StackGuardSlot) {
494+
ArrayRef<Argument *> ByValArguments, Instruction *BasePointer,
495+
AllocaInst *StackGuardSlot) {
495496
if (StaticAllocas.empty() && ByValArguments.empty())
496497
return BasePointer;
497498

@@ -759,7 +760,7 @@ bool SafeStack::run() {
759760
SmallVector<AllocaInst *, 16> StaticAllocas;
760761
SmallVector<AllocaInst *, 4> DynamicAllocas;
761762
SmallVector<Argument *, 4> ByValArguments;
762-
SmallVector<ReturnInst *, 4> Returns;
763+
SmallVector<Instruction *, 4> Returns;
763764

764765
// Collect all points where stack gets unwound and needs to be restored
765766
// This is only necessary because the runtime (setjmp and unwind code) is
@@ -812,17 +813,16 @@ bool SafeStack::run() {
812813
StackGuardSlot = IRB.CreateAlloca(StackPtrTy, nullptr);
813814
IRB.CreateStore(StackGuard, StackGuardSlot);
814815

815-
for (ReturnInst *RI : Returns) {
816+
for (Instruction *RI : Returns) {
816817
IRBuilder<> IRBRet(RI);
817818
checkStackGuard(IRBRet, F, *RI, StackGuardSlot, StackGuard);
818819
}
819820
}
820821

821822
// The top of the unsafe stack after all unsafe static allocas are
822823
// allocated.
823-
Value *StaticTop =
824-
moveStaticAllocasToUnsafeStack(IRB, F, StaticAllocas, ByValArguments,
825-
Returns, BasePointer, StackGuardSlot);
824+
Value *StaticTop = moveStaticAllocasToUnsafeStack(
825+
IRB, F, StaticAllocas, ByValArguments, BasePointer, StackGuardSlot);
826826

827827
// Safe stack object that stores the current unsafe stack top. It is updated
828828
// as unsafe dynamic (non-constant-sized) allocas are allocated and freed.
@@ -838,7 +838,7 @@ bool SafeStack::run() {
838838
DynamicAllocas);
839839

840840
// Restore the unsafe stack pointer before each return.
841-
for (ReturnInst *RI : Returns) {
841+
for (Instruction *RI : Returns) {
842842
IRB.SetInsertPoint(RI);
843843
IRB.CreateStore(BasePointer, UnsafeStackPtr);
844844
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
; To test that safestack does not break the musttail call contract.
2+
;
3+
; RUN: opt < %s --safe-stack -S | FileCheck %s
4+
5+
target triple = "x86_64-unknown-linux-gnu"
6+
7+
declare i32 @foo(i32* %p)
8+
declare void @alloca_test_use([10 x i8]*)
9+
10+
define i32 @call_foo(i32* %a) safestack {
11+
; CHECK-LABEL: @call_foo(
12+
; CHECK-NEXT: [[UNSAFE_STACK_PTR:%.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr, align 8
13+
; CHECK-NEXT: [[UNSAFE_STACK_STATIC_TOP:%.*]] = getelementptr i8, i8* [[UNSAFE_STACK_PTR]], i32 -16
14+
; CHECK-NEXT: store i8* [[UNSAFE_STACK_STATIC_TOP]], i8** @__safestack_unsafe_stack_ptr, align 8
15+
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, i8* [[UNSAFE_STACK_PTR]], i32 -10
16+
; CHECK-NEXT: [[X_UNSAFE:%.*]] = bitcast i8* [[TMP1]] to [10 x i8]*
17+
; CHECK-NEXT: call void @alloca_test_use([10 x i8]* [[X_UNSAFE]])
18+
; CHECK-NEXT: store i8* [[UNSAFE_STACK_PTR]], i8** @__safestack_unsafe_stack_ptr, align 8
19+
; CHECK-NEXT: [[R:%.*]] = musttail call i32 @foo(i32* [[A:%.*]])
20+
; CHECK-NEXT: ret i32 [[R]]
21+
;
22+
%x = alloca [10 x i8], align 1
23+
call void @alloca_test_use([10 x i8]* %x)
24+
%r = musttail call i32 @foo(i32* %a)
25+
ret i32 %r
26+
}
27+
28+
define i32 @call_foo_cast(i32* %a) safestack {
29+
; CHECK-LABEL: @call_foo_cast(
30+
; CHECK-NEXT: [[UNSAFE_STACK_PTR:%.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr, align 8
31+
; CHECK-NEXT: [[UNSAFE_STACK_STATIC_TOP:%.*]] = getelementptr i8, i8* [[UNSAFE_STACK_PTR]], i32 -16
32+
; CHECK-NEXT: store i8* [[UNSAFE_STACK_STATIC_TOP]], i8** @__safestack_unsafe_stack_ptr, align 8
33+
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i8, i8* [[UNSAFE_STACK_PTR]], i32 -10
34+
; CHECK-NEXT: [[X_UNSAFE:%.*]] = bitcast i8* [[TMP1]] to [10 x i8]*
35+
; CHECK-NEXT: call void @alloca_test_use([10 x i8]* [[X_UNSAFE]])
36+
; CHECK-NEXT: store i8* [[UNSAFE_STACK_PTR]], i8** @__safestack_unsafe_stack_ptr, align 8
37+
; CHECK-NEXT: [[R:%.*]] = musttail call i32 @foo(i32* [[A:%.*]])
38+
; CHECK-NEXT: [[T:%.*]] = bitcast i32 [[R]] to i32
39+
; CHECK-NEXT: ret i32 [[T]]
40+
;
41+
%x = alloca [10 x i8], align 1
42+
call void @alloca_test_use([10 x i8]* %x)
43+
%r = musttail call i32 @foo(i32* %a)
44+
%t = bitcast i32 %r to i32
45+
ret i32 %t
46+
}

0 commit comments

Comments
 (0)