Skip to content

Commit 854bbc5

Browse files
[X86][CodeGen] security check cookie execute only when needed (#95904)
For windows __security_check_cookie call gets call everytime function is return without fixup. Since this function is defined in runtime library, it incures cost of call in dll which simply does comparison and returns most time. With Fixup, We selective move to call in DLL only if comparison fails.
1 parent 2f2b931 commit 854bbc5

File tree

7 files changed

+291
-10
lines changed

7 files changed

+291
-10
lines changed

llvm/lib/Target/X86/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ set(sources
8383
X86TargetTransformInfo.cpp
8484
X86VZeroUpper.cpp
8585
X86WinEHState.cpp
86+
X86WinFixupBufferSecurityCheck.cpp
8687
X86InsertWait.cpp
8788
GISel/X86CallLowering.cpp
8889
GISel/X86InstructionSelector.cpp

llvm/lib/Target/X86/X86.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ FunctionPass *createX86OptimizeLEAs();
7373
/// Return a pass that transforms setcc + movzx pairs into xor + setcc.
7474
FunctionPass *createX86FixupSetCC();
7575

76+
/// Return a pass that transform inline buffer security check into seperate bb
77+
FunctionPass *createX86WinFixupBufferSecurityCheckPass();
78+
7679
/// Return a pass that avoids creating store forward block issues in the hardware.
7780
FunctionPass *createX86AvoidStoreForwardingBlocks();
7881

@@ -186,6 +189,7 @@ void initializeX86ExpandPseudoPass(PassRegistry &);
186189
void initializeX86FastPreTileConfigPass(PassRegistry &);
187190
void initializeX86FastTileConfigPass(PassRegistry &);
188191
void initializeX86FixupSetCCPassPass(PassRegistry &);
192+
void initializeX86WinFixupBufferSecurityCheckPassPass(PassRegistry &);
189193
void initializeX86FlagsCopyLoweringPassPass(PassRegistry &);
190194
void initializeX86LoadValueInjectionLoadHardeningPassPass(PassRegistry &);
191195
void initializeX86LoadValueInjectionRetHardeningPassPass(PassRegistry &);

llvm/lib/Target/X86/X86TargetMachine.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ bool X86PassConfig::addPreISel() {
550550
void X86PassConfig::addPreRegAlloc() {
551551
if (getOptLevel() != CodeGenOptLevel::None) {
552552
addPass(&LiveRangeShrinkID);
553+
addPass(createX86WinFixupBufferSecurityCheckPass());
553554
addPass(createX86FixupSetCC());
554555
addPass(createX86OptimizeLEAs());
555556
addPass(createX86CallFrameOptimization());
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
//===- X86WinFixupBufferSecurityCheck.cpp Fix Buffer Security Check Call -===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// Buffer Security Check implementation inserts windows specific callback into
9+
// code. On windows, __security_check_cookie call gets call everytime function
10+
// is return without fixup. Since this function is defined in runtime library,
11+
// it incures cost of call in dll which simply does comparison and returns most
12+
// time. With Fixup, We selective move to call in DLL only if comparison fails.
13+
//===----------------------------------------------------------------------===//
14+
15+
#include "X86.h"
16+
#include "X86FrameLowering.h"
17+
#include "X86InstrInfo.h"
18+
#include "X86Subtarget.h"
19+
#include "llvm/CodeGen/LivePhysRegs.h"
20+
#include "llvm/CodeGen/MachineFunctionPass.h"
21+
#include "llvm/CodeGen/MachineInstrBuilder.h"
22+
#include "llvm/CodeGen/MachineRegisterInfo.h"
23+
#include "llvm/IR/Module.h"
24+
#include <iterator>
25+
26+
using namespace llvm;
27+
28+
#define DEBUG_TYPE "x86-win-fixup-bscheck"
29+
30+
namespace {
31+
32+
class X86WinFixupBufferSecurityCheckPass : public MachineFunctionPass {
33+
public:
34+
static char ID;
35+
36+
X86WinFixupBufferSecurityCheckPass() : MachineFunctionPass(ID) {}
37+
38+
StringRef getPassName() const override {
39+
return "X86 Windows Fixup Buffer Security Check";
40+
}
41+
42+
bool runOnMachineFunction(MachineFunction &MF) override;
43+
44+
std::pair<MachineBasicBlock *, MachineInstr *>
45+
getSecurityCheckerBasicBlock(MachineFunction &MF);
46+
47+
void getGuardCheckSequence(MachineBasicBlock *CurMBB, MachineInstr *CheckCall,
48+
MachineInstr *SeqMI[5]);
49+
50+
void SplitBasicBlock(MachineBasicBlock *CurMBB, MachineBasicBlock *NewRetMBB,
51+
MachineBasicBlock::iterator SplitIt);
52+
53+
void FinishBlock(MachineBasicBlock *MBB);
54+
55+
void FinishFunction(MachineBasicBlock *FailMBB, MachineBasicBlock *NewRetMBB);
56+
57+
std::pair<MachineInstr *, MachineInstr *>
58+
CreateFailCheckSequence(MachineBasicBlock *CurMBB, MachineBasicBlock *FailMBB,
59+
MachineInstr *SeqMI[5]);
60+
};
61+
} // end anonymous namespace
62+
63+
char X86WinFixupBufferSecurityCheckPass::ID = 0;
64+
65+
INITIALIZE_PASS(X86WinFixupBufferSecurityCheckPass, DEBUG_TYPE, DEBUG_TYPE,
66+
false, false)
67+
68+
FunctionPass *llvm::createX86WinFixupBufferSecurityCheckPass() {
69+
return new X86WinFixupBufferSecurityCheckPass();
70+
}
71+
72+
void X86WinFixupBufferSecurityCheckPass::SplitBasicBlock(
73+
MachineBasicBlock *CurMBB, MachineBasicBlock *NewRetMBB,
74+
MachineBasicBlock::iterator SplitIt) {
75+
NewRetMBB->splice(NewRetMBB->end(), CurMBB, SplitIt, CurMBB->end());
76+
}
77+
78+
std::pair<MachineBasicBlock *, MachineInstr *>
79+
X86WinFixupBufferSecurityCheckPass::getSecurityCheckerBasicBlock(
80+
MachineFunction &MF) {
81+
MachineBasicBlock::reverse_iterator RBegin, REnd;
82+
83+
for (auto &MBB : llvm::reverse(MF)) {
84+
for (RBegin = MBB.rbegin(), REnd = MBB.rend(); RBegin != REnd; ++RBegin) {
85+
auto &MI = *RBegin;
86+
if (MI.getOpcode() == X86::CALL64pcrel32 &&
87+
MI.getNumExplicitOperands() == 1) {
88+
auto MO = MI.getOperand(0);
89+
if (MO.isGlobal()) {
90+
auto Callee = dyn_cast<Function>(MO.getGlobal());
91+
if (Callee && Callee->getName() == "__security_check_cookie") {
92+
return std::make_pair(&MBB, &MI);
93+
break;
94+
}
95+
}
96+
}
97+
}
98+
}
99+
return std::make_pair(nullptr, nullptr);
100+
}
101+
102+
void X86WinFixupBufferSecurityCheckPass::getGuardCheckSequence(
103+
MachineBasicBlock *CurMBB, MachineInstr *CheckCall,
104+
MachineInstr *SeqMI[5]) {
105+
106+
MachineBasicBlock::iterator UIt(CheckCall);
107+
MachineBasicBlock::reverse_iterator DIt(CheckCall);
108+
// Seq From StackUp to Stack Down Is fixed.
109+
// ADJCALLSTACKUP64
110+
++UIt;
111+
SeqMI[4] = &*UIt;
112+
113+
// CALL __security_check_cookie
114+
SeqMI[3] = CheckCall;
115+
116+
// COPY function slot cookie
117+
++DIt;
118+
SeqMI[2] = &*DIt;
119+
120+
// ADJCALLSTACKDOWN64
121+
++DIt;
122+
SeqMI[1] = &*DIt;
123+
124+
MachineBasicBlock::reverse_iterator XIt(SeqMI[1]);
125+
for (; XIt != CurMBB->rbegin(); ++XIt) {
126+
auto &CI = *XIt;
127+
if ((CI.getOpcode() == X86::XOR64_FP) || (CI.getOpcode() == X86::XOR32_FP))
128+
break;
129+
}
130+
SeqMI[0] = &*XIt;
131+
}
132+
133+
std::pair<MachineInstr *, MachineInstr *>
134+
X86WinFixupBufferSecurityCheckPass::CreateFailCheckSequence(
135+
MachineBasicBlock *CurMBB, MachineBasicBlock *FailMBB,
136+
MachineInstr *SeqMI[5]) {
137+
138+
auto MF = CurMBB->getParent();
139+
140+
Module &M = *MF->getFunction().getParent();
141+
GlobalVariable *GV = M.getGlobalVariable("__security_cookie");
142+
assert(GV && " Security Cookie was not installed!");
143+
144+
const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo();
145+
146+
MachineInstr *GuardXor = SeqMI[0];
147+
MachineBasicBlock::iterator InsertPt(GuardXor);
148+
++InsertPt;
149+
150+
// Compare security_Cookie with XOR_Val, if not same, we have violation
151+
auto CMI = BuildMI(*CurMBB, InsertPt, DebugLoc(), TII->get(X86::CMP64rm))
152+
.addReg(GuardXor->getOperand(0).getReg())
153+
.addReg(X86::RIP)
154+
.addImm(1)
155+
.addReg(X86::NoRegister)
156+
.addGlobalAddress(GV)
157+
.addReg(X86::NoRegister);
158+
159+
BuildMI(*CurMBB, InsertPt, DebugLoc(), TII->get(X86::JCC_1))
160+
.addMBB(FailMBB)
161+
.addImm(X86::COND_NE);
162+
163+
auto JMI = BuildMI(*CurMBB, InsertPt, DebugLoc(), TII->get(X86::JMP_1));
164+
165+
return std::make_pair(CMI.getInstr(), JMI.getInstr());
166+
}
167+
168+
void X86WinFixupBufferSecurityCheckPass::FinishBlock(MachineBasicBlock *MBB) {
169+
LivePhysRegs LiveRegs;
170+
computeAndAddLiveIns(LiveRegs, *MBB);
171+
}
172+
173+
void X86WinFixupBufferSecurityCheckPass::FinishFunction(
174+
MachineBasicBlock *FailMBB, MachineBasicBlock *NewRetMBB) {
175+
FailMBB->getParent()->RenumberBlocks();
176+
// FailMBB includes call to MSCV RT where is __security_check_cookie
177+
// function is called. This function uses regcall and it expects cookie
178+
// value from stack slot.( even if this is modified)
179+
// Before going further we compute back livein for this block to make sure
180+
// it is live and provided.
181+
FinishBlock(FailMBB);
182+
FinishBlock(NewRetMBB);
183+
}
184+
185+
bool X86WinFixupBufferSecurityCheckPass::runOnMachineFunction(
186+
MachineFunction &MF) {
187+
bool Changed = false;
188+
const X86Subtarget &STI = MF.getSubtarget<X86Subtarget>();
189+
190+
if (!(STI.isTargetWindowsItanium() || STI.isTargetWindowsMSVC()))
191+
return Changed;
192+
193+
// Check if security cookie was installed or not
194+
Module &M = *MF.getFunction().getParent();
195+
GlobalVariable *GV = M.getGlobalVariable("__security_cookie");
196+
if (!GV)
197+
return Changed;
198+
199+
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
200+
201+
// Check if security check cookie was installed or not
202+
auto [CurMBB, CheckCall] = getSecurityCheckerBasicBlock(MF);
203+
204+
if (!CheckCall)
205+
return Changed;
206+
207+
MachineBasicBlock *FailMBB = MF.CreateMachineBasicBlock();
208+
MachineBasicBlock *NewRetMBB = MF.CreateMachineBasicBlock();
209+
210+
MF.insert(MF.end(), NewRetMBB);
211+
MF.insert(MF.end(), FailMBB);
212+
213+
MachineInstr *SeqMI[5];
214+
getGuardCheckSequence(CurMBB, CheckCall, SeqMI);
215+
// MachineInstr * GuardXor = SeqMI[0];
216+
217+
auto FailSeqRange = CreateFailCheckSequence(CurMBB, FailMBB, SeqMI);
218+
MachineInstrBuilder JMI(MF, FailSeqRange.second);
219+
220+
// After Inserting JMP_1, we can not have two terminators
221+
// in same block, split CurrentMBB after JMP_1
222+
MachineBasicBlock::iterator SplitIt(SeqMI[4]);
223+
++SplitIt;
224+
SplitBasicBlock(CurMBB, NewRetMBB, SplitIt);
225+
226+
// Fill up Failure Routine, move Fail Check Squence from CurMBB to FailMBB
227+
MachineBasicBlock::iterator U1It(SeqMI[1]);
228+
MachineBasicBlock::iterator U2It(SeqMI[4]);
229+
++U2It;
230+
FailMBB->splice(FailMBB->end(), CurMBB, U1It, U2It);
231+
BuildMI(*FailMBB, FailMBB->end(), DebugLoc(), TII->get(X86::INT3));
232+
233+
// Move left over instruction after StackUp
234+
// from Current Basic BLocks into New Return Block
235+
JMI.addMBB(NewRetMBB);
236+
MachineBasicBlock::iterator SplicePt(JMI.getInstr());
237+
++SplicePt;
238+
if (SplicePt != CurMBB->end())
239+
NewRetMBB->splice(NewRetMBB->end(), CurMBB, SplicePt);
240+
241+
// Restructure Basic Blocks
242+
CurMBB->addSuccessor(NewRetMBB);
243+
CurMBB->addSuccessor(FailMBB);
244+
245+
FinishFunction(FailMBB, NewRetMBB);
246+
return !Changed;
247+
}

llvm/test/CodeGen/X86/opt-pipeline.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
; CHECK-NEXT: Peephole Optimizations
120120
; CHECK-NEXT: Remove dead machine instructions
121121
; CHECK-NEXT: Live Range Shrink
122+
; CHECK-NEXT: X86 Windows Fixup Buffer Security Check
122123
; CHECK-NEXT: X86 Fixup SetCC
123124
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
124125
; CHECK-NEXT: X86 LEA Optimize

llvm/test/CodeGen/X86/stack-protector-msvc.ll

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,15 @@ define void @test(ptr %a) nounwind ssp {
4949
; MSVC-X64-NEXT: callq printf
5050
; MSVC-X64-NEXT: movq {{[0-9]+}}(%rsp), %rcx
5151
; MSVC-X64-NEXT: xorq %rsp, %rcx
52-
; MSVC-X64-NEXT: callq __security_check_cookie
52+
; MSVC-X64-NEXT: cmpq __security_cookie(%rip), %rcx
53+
; MSVC-X64-NEXT: jne .LBB0_2
54+
; MSVC-X64-NEXT: # %bb.1:
5355
; MSVC-X64-NEXT: addq $64, %rsp
5456
; MSVC-X64-NEXT: popq %rsi
5557
; MSVC-X64-NEXT: retq
58+
; MSVC-X64-NEXT: .LBB0_2:
59+
; MSVC-X64-NEXT: callq __security_check_cookie
60+
; MSVC-X64-NEXT: int3
5661
;
5762
; MSVC-X86-O0-LABEL: test:
5863
; MSVC-X86-O0: # %bb.0: # %entry
@@ -155,11 +160,17 @@ define void @test_vla(i32 %n) nounwind ssp {
155160
; MSVC-X64-NEXT: addq $32, %rsp
156161
; MSVC-X64-NEXT: movq -8(%rbp), %rcx
157162
; MSVC-X64-NEXT: xorq %rbp, %rcx
158-
; MSVC-X64-NEXT: subq $32, %rsp
159-
; MSVC-X64-NEXT: callq __security_check_cookie
163+
; MSVC-X64-NEXT: cmpq __security_cookie(%rip), %rcx
164+
; MSVC-X64-NEXT: jne .LBB1_2
165+
; MSVC-X64-NEXT: # %bb.1:
160166
; MSVC-X64-NEXT: movq %rbp, %rsp
161167
; MSVC-X64-NEXT: popq %rbp
162168
; MSVC-X64-NEXT: retq
169+
; MSVC-X64-NEXT: .LBB1_2:
170+
; MSVC-X64-NEXT: subq $32, %rsp
171+
; MSVC-X64-NEXT: callq __security_check_cookie
172+
; MSVC-X64-NEXT: addq $32, %rsp
173+
; MSVC-X64-NEXT: int3
163174
;
164175
; MSVC-X86-O0-LABEL: test_vla:
165176
; MSVC-X86-O0: # %bb.0:
@@ -277,13 +288,19 @@ define void @test_vla_realign(i32 %n) nounwind ssp {
277288
; MSVC-X64-NEXT: addq $32, %rsp
278289
; MSVC-X64-NEXT: movq 24(%rbx), %rcx
279290
; MSVC-X64-NEXT: xorq %rbp, %rcx
280-
; MSVC-X64-NEXT: subq $32, %rsp
281-
; MSVC-X64-NEXT: callq __security_check_cookie
291+
; MSVC-X64-NEXT: cmpq __security_cookie(%rip), %rcx
292+
; MSVC-X64-NEXT: jne .LBB2_2
293+
; MSVC-X64-NEXT: # %bb.1:
282294
; MSVC-X64-NEXT: movq %rbp, %rsp
283295
; MSVC-X64-NEXT: popq %rbx
284296
; MSVC-X64-NEXT: popq %rsi
285297
; MSVC-X64-NEXT: popq %rbp
286298
; MSVC-X64-NEXT: retq
299+
; MSVC-X64-NEXT: .LBB2_2:
300+
; MSVC-X64-NEXT: subq $32, %rsp
301+
; MSVC-X64-NEXT: callq __security_check_cookie
302+
; MSVC-X64-NEXT: addq $32, %rsp
303+
; MSVC-X64-NEXT: int3
287304
;
288305
; MSVC-X86-O0-LABEL: test_vla_realign:
289306
; MSVC-X86-O0: # %bb.0:
@@ -360,4 +377,3 @@ define void @test_vla_realign(i32 %n) nounwind ssp {
360377
declare ptr @strcpy(ptr, ptr) nounwind
361378

362379
declare i32 @printf(ptr, ...) nounwind
363-

0 commit comments

Comments
 (0)