Skip to content

Commit 471a386

Browse files
TNorthovertstellar
authored andcommitted
StackProtector: ensure protection does not interfere with tail call frame.
The IR stack protector pass must insert stack checks before the call instead of between it and the return. Similarly, SDAG one should recognize that ADJCALLFRAME instructions could be part of the terminal sequence of a tail call. In this case because such call frames cannot be nested in LLVM the stack protection code must skip over the whole sequence (or risk clobbering argument registers). (cherry picked from commit 5e3d9fc)
1 parent 6baa5ce commit 471a386

File tree

5 files changed

+197
-8
lines changed

5 files changed

+197
-8
lines changed

llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,16 +1691,41 @@ static bool MIIsInTerminatorSequence(const MachineInstr &MI) {
16911691
/// terminator, but additionally the copies that move the vregs into the
16921692
/// physical registers.
16931693
static MachineBasicBlock::iterator
1694-
FindSplitPointForStackProtector(MachineBasicBlock *BB) {
1694+
FindSplitPointForStackProtector(MachineBasicBlock *BB,
1695+
const TargetInstrInfo &TII) {
16951696
MachineBasicBlock::iterator SplitPoint = BB->getFirstTerminator();
1696-
//
16971697
if (SplitPoint == BB->begin())
16981698
return SplitPoint;
16991699

17001700
MachineBasicBlock::iterator Start = BB->begin();
17011701
MachineBasicBlock::iterator Previous = SplitPoint;
17021702
--Previous;
17031703

1704+
if (TII.isTailCall(*SplitPoint) &&
1705+
Previous->getOpcode() == TII.getCallFrameDestroyOpcode()) {
1706+
// call itself, then we must insert before the sequence even starts. For
1707+
// example:
1708+
// <split point>
1709+
// ADJCALLSTACKDOWN ...
1710+
// <Moves>
1711+
// ADJCALLSTACKUP ...
1712+
// TAILJMP somewhere
1713+
// On the other hand, it could be an unrelated call in which case this tail call
1714+
// has to register moves of its own and should be the split point. For example:
1715+
// ADJCALLSTACKDOWN
1716+
// CALL something_else
1717+
// ADJCALLSTACKUP
1718+
// <split point>
1719+
// TAILJMP somewhere
1720+
do {
1721+
--Previous;
1722+
if (Previous->isCall())
1723+
return SplitPoint;
1724+
} while(Previous->getOpcode() != TII.getCallFrameSetupOpcode());
1725+
1726+
return Previous;
1727+
}
1728+
17041729
while (MIIsInTerminatorSequence(*Previous)) {
17051730
SplitPoint = Previous;
17061731
if (Previous == Start)
@@ -1740,7 +1765,7 @@ SelectionDAGISel::FinishBasicBlock() {
17401765
// Add load and check to the basicblock.
17411766
FuncInfo->MBB = ParentMBB;
17421767
FuncInfo->InsertPt =
1743-
FindSplitPointForStackProtector(ParentMBB);
1768+
FindSplitPointForStackProtector(ParentMBB, *TII);
17441769
SDB->visitSPDescriptorParent(SDB->SPDescriptor, ParentMBB);
17451770
CurDAG->setRoot(SDB->getRoot());
17461771
SDB->clear();
@@ -1759,7 +1784,7 @@ SelectionDAGISel::FinishBasicBlock() {
17591784
// register allocation issues caused by us splitting the parent mbb. The
17601785
// register allocator will clean up said virtual copies later on.
17611786
MachineBasicBlock::iterator SplitPoint =
1762-
FindSplitPointForStackProtector(ParentMBB);
1787+
FindSplitPointForStackProtector(ParentMBB, *TII);
17631788

17641789
// Splice the terminator of ParentMBB into SuccessMBB.
17651790
SuccessMBB->splice(SuccessMBB->end(), ParentMBB,

llvm/lib/CodeGen/StackProtector.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,21 +470,36 @@ bool StackProtector::InsertStackProtectors() {
470470
// instrumentation has already been generated.
471471
HasIRCheck = true;
472472

473+
// If we're instrumenting a block with a musttail call, the check has to be
474+
// inserted before the call rather than between it and the return. The
475+
// verifier guarantees that a musttail call is either directly before the
476+
// return or with a single correct bitcast of the return value in between so
477+
// we don't need to worry about many situations here.
478+
Instruction *CheckLoc = RI;
479+
Instruction *Prev = RI->getPrevNonDebugInstruction();
480+
if (Prev && isa<CallInst>(Prev) && cast<CallInst>(Prev)->isMustTailCall())
481+
CheckLoc = Prev;
482+
else if (Prev) {
483+
Prev = Prev->getPrevNonDebugInstruction();
484+
if (Prev && isa<CallInst>(Prev) && cast<CallInst>(Prev)->isMustTailCall())
485+
CheckLoc = Prev;
486+
}
487+
473488
// Generate epilogue instrumentation. The epilogue intrumentation can be
474489
// function-based or inlined depending on which mechanism the target is
475490
// providing.
476491
if (Function *GuardCheck = TLI->getSSPStackGuardCheck(*M)) {
477492
// Generate the function-based epilogue instrumentation.
478493
// The target provides a guard check function, generate a call to it.
479-
IRBuilder<> B(RI);
494+
IRBuilder<> B(CheckLoc);
480495
LoadInst *Guard = B.CreateLoad(B.getInt8PtrTy(), AI, true, "Guard");
481496
CallInst *Call = B.CreateCall(GuardCheck, {Guard});
482497
Call->setAttributes(GuardCheck->getAttributes());
483498
Call->setCallingConv(GuardCheck->getCallingConv());
484499
} else {
485500
// Generate the epilogue with inline instrumentation.
486-
// If we do not support SelectionDAG based tail calls, generate IR level
487-
// tail calls.
501+
// If we do not support SelectionDAG based calls, generate IR level
502+
// calls.
488503
//
489504
// For each block with a return instruction, convert this:
490505
//
@@ -514,7 +529,8 @@ bool StackProtector::InsertStackProtectors() {
514529
BasicBlock *FailBB = CreateFailBB();
515530

516531
// Split the basic block before the return instruction.
517-
BasicBlock *NewBB = BB->splitBasicBlock(RI->getIterator(), "SP_return");
532+
BasicBlock *NewBB =
533+
BB->splitBasicBlock(CheckLoc->getIterator(), "SP_return");
518534

519535
// Update the dominator tree if we need to.
520536
if (DT && DT->isReachableFromEntry(BB)) {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
; RUN: llc -mtriple=arm64-apple-macosx -fast-isel %s -o - -start-before=stack-protector -stop-after=stack-protector | FileCheck %s
2+
3+
@var = global [2 x i64]* null
4+
5+
declare void @callee()
6+
7+
define void @caller1() ssp {
8+
; CHECK-LABEL: define void @caller1()
9+
; Prologue:
10+
; CHECK: @llvm.stackguard
11+
12+
; CHECK: [[GUARD:%.*]] = call i8* @llvm.stackguard()
13+
; CHECK: [[TOKEN:%.*]] = load volatile i8*, i8** {{%.*}}
14+
; CHECK: [[TST:%.*]] = icmp eq i8* [[GUARD]], [[TOKEN]]
15+
; CHECK: br i1 [[TST]]
16+
17+
; CHECK: musttail call void @callee()
18+
; CHECK-NEXT: ret void
19+
%var = alloca [2 x i64]
20+
store [2 x i64]* %var, [2 x i64]** @var
21+
musttail call void @callee()
22+
ret void
23+
}
24+
25+
define void @justret() ssp {
26+
; CHECK-LABEL: define void @justret()
27+
; Prologue:
28+
; CHECK: @llvm.stackguard
29+
30+
; CHECK: [[GUARD:%.*]] = call i8* @llvm.stackguard()
31+
; CHECK: [[TOKEN:%.*]] = load volatile i8*, i8** {{%.*}}
32+
; CHECK: [[TST:%.*]] = icmp eq i8* [[GUARD]], [[TOKEN]]
33+
; CHECK: br i1 [[TST]]
34+
35+
; CHECK: ret void
36+
%var = alloca [2 x i64]
37+
store [2 x i64]* %var, [2 x i64]** @var
38+
br label %retblock
39+
40+
retblock:
41+
ret void
42+
}
43+
44+
45+
declare i64* @callee2()
46+
47+
define i8* @caller2() ssp {
48+
; CHECK-LABEL: define i8* @caller2()
49+
; Prologue:
50+
; CHECK: @llvm.stackguard
51+
52+
; CHECK: [[GUARD:%.*]] = call i8* @llvm.stackguard()
53+
; CHECK: [[TOKEN:%.*]] = load volatile i8*, i8** {{%.*}}
54+
; CHECK: [[TST:%.*]] = icmp eq i8* [[GUARD]], [[TOKEN]]
55+
; CHECK: br i1 [[TST]]
56+
57+
; CHECK: [[TMP:%.*]] = musttail call i64* @callee2()
58+
; CHECK-NEXT: [[RES:%.*]] = bitcast i64* [[TMP]] to i8*
59+
; CHECK-NEXT: ret i8* [[RES]]
60+
61+
%var = alloca [2 x i64]
62+
store [2 x i64]* %var, [2 x i64]** @var
63+
%tmp = musttail call i64* @callee2()
64+
%res = bitcast i64* %tmp to i8*
65+
ret i8* %res
66+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
; RUN: llc -mtriple=thumbv7-windows-msvc -fast-isel %s -o - -start-before=stack-protector -stop-after=stack-protector | FileCheck %s
2+
3+
@var = global [2 x i64]* null
4+
5+
declare void @callee()
6+
7+
define void @caller1() sspreq {
8+
; CHECK-LABEL: define void @caller1()
9+
; Prologue:
10+
11+
; CHECK: call void @__security_check_cookie
12+
13+
; CHECK: musttail call void @callee()
14+
; CHECK-NEXT: ret void
15+
%var = alloca [2 x i64]
16+
store [2 x i64]* %var, [2 x i64]** @var
17+
musttail call void @callee()
18+
ret void
19+
}
20+
21+
define void @justret() sspreq {
22+
; CHECK-LABEL: define void @justret()
23+
; Prologue:
24+
; CHECK: @llvm.stackguard
25+
26+
; CHECK: call void @__security_check_cookie
27+
28+
; CHECK: ret void
29+
%var = alloca [2 x i64]
30+
store [2 x i64]* %var, [2 x i64]** @var
31+
br label %retblock
32+
33+
retblock:
34+
ret void
35+
}
36+
37+
38+
declare i64* @callee2()
39+
40+
define i8* @caller2() sspreq {
41+
; CHECK-LABEL: define i8* @caller2()
42+
; Prologue:
43+
; CHECK: @llvm.stackguard
44+
45+
; CHECK: call void @__security_check_cookie
46+
47+
; CHECK: [[TMP:%.*]] = musttail call i64* @callee2()
48+
; CHECK-NEXT: [[RES:%.*]] = bitcast i64* [[TMP]] to i8*
49+
; CHECK-NEXT: ret i8* [[RES]]
50+
51+
%var = alloca [2 x i64]
52+
store [2 x i64]* %var, [2 x i64]** @var
53+
%tmp = musttail call i64* @callee2()
54+
%res = bitcast i64* %tmp to i8*
55+
ret i8* %res
56+
}

llvm/test/CodeGen/X86/tailcc-ssp.ll

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
; RUN: llc -mtriple=x86_64-windows-msvc %s -o - -verify-machineinstrs | FileCheck %s
2+
3+
declare void @h(i8*, i64, i8*)
4+
5+
define tailcc void @tailcall_frame(i8* %0, i64 %1) sspreq {
6+
; CHECK-LABEL: tailcall_frame:
7+
; CHECK: callq __security_check_cookie
8+
; CHECK: xorl %ecx, %ecx
9+
; CHECK: jmp h
10+
11+
tail call tailcc void @h(i8* null, i64 0, i8* null)
12+
ret void
13+
}
14+
15+
declare void @bar()
16+
define void @tailcall_unrelated_frame() sspreq {
17+
; CHECK-LABEL: tailcall_unrelated_frame:
18+
; CHECK: subq [[STACK:\$.*]], %rsp
19+
; CHECK: callq bar
20+
; CHECK: callq __security_check_cookie
21+
; CHECK: addq [[STACK]], %rsp
22+
; CHECK: jmp bar
23+
call void @bar()
24+
tail call void @bar()
25+
ret void
26+
}

0 commit comments

Comments
 (0)