Skip to content

Commit 604ddd9

Browse files
committed
Optimize security cookie check with New Fixup Pass
This patch adds the AArch64WinFixupBufferSecurityCheckPass to optimize the handling of buffer security checks on AArch64 Windows targets. The pass selectively replaces __security_check_cookie calls with inline comparisons, transferring control to the runtime library only on failure. A similar implementation for X86 Windows target was implemented by llvm#95904
1 parent 783a846 commit 604ddd9

File tree

7 files changed

+291
-2
lines changed

7 files changed

+291
-2
lines changed

llvm/lib/Target/AArch64/AArch64.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ FunctionPass *createAArch64PostLegalizerLowering();
7272
FunctionPass *createAArch64PostSelectOptimize();
7373
FunctionPass *createAArch64StackTaggingPass(bool IsOptNone);
7474
FunctionPass *createAArch64StackTaggingPreRAPass();
75+
FunctionPass *createAArch64WinFixupBufferSecurityCheckPass();
7576
ModulePass *createAArch64Arm64ECCallLoweringPass();
7677

7778
void initializeAArch64A53Fix835769Pass(PassRegistry&);
@@ -105,6 +106,7 @@ void initializeAArch64SpeculationHardeningPass(PassRegistry &);
105106
void initializeAArch64StackTaggingPass(PassRegistry &);
106107
void initializeAArch64StackTaggingPreRAPass(PassRegistry &);
107108
void initializeAArch64StorePairSuppressPass(PassRegistry&);
109+
void initializeAArch64WinFixupBufferSecurityCheckPassPass(PassRegistry &);
108110
void initializeFalkorHWPFFixPass(PassRegistry&);
109111
void initializeFalkorMarkStridedAccessesLegacyPass(PassRegistry&);
110112
void initializeLDTLSCleanupPass(PassRegistry&);

llvm/lib/Target/AArch64/AArch64TargetMachine.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,7 @@ void AArch64PassConfig::addPreRegAlloc() {
813813
}
814814
if (TM->getOptLevel() != CodeGenOptLevel::None && EnableMachinePipeliner)
815815
addPass(&MachinePipelinerID);
816+
addPass(createAArch64WinFixupBufferSecurityCheckPass());
816817
}
817818

818819
void AArch64PassConfig::addPostRegAlloc() {
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
//===- AArch64WinFixupBufferSecurityCheck.cpp Fix Buffer Security Check Call
2+
//-===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
// Buffer Security Check implementation inserts windows specific callback into
10+
// code. On windows, __security_check_cookie call gets call everytime function
11+
// is return without fixup. Since this function is defined in runtime library,
12+
// it incures cost of call in dll which simply does comparison and returns most
13+
// time. With Fixup, We selective move to call in DLL only if comparison fails.
14+
//===----------------------------------------------------------------------===//
15+
16+
#include "llvm/CodeGen/LivePhysRegs.h"
17+
#include "llvm/CodeGen/MachineFunctionPass.h"
18+
#include "llvm/CodeGen/MachineInstrBuilder.h"
19+
#include "llvm/CodeGen/MachineRegisterInfo.h"
20+
#include "llvm/IR/Module.h"
21+
22+
#include "AArch64.h"
23+
#include "AArch64InstrInfo.h"
24+
#include "AArch64Subtarget.h"
25+
26+
using namespace llvm;
27+
28+
#define DEBUG_TYPE "aarch64-win-fixup-bscheck"
29+
30+
namespace {
31+
32+
class AArch64WinFixupBufferSecurityCheckPass : public MachineFunctionPass {
33+
public:
34+
static char ID;
35+
36+
AArch64WinFixupBufferSecurityCheckPass() : MachineFunctionPass(ID) {}
37+
38+
StringRef getPassName() const override {
39+
return "AArch64 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+
MachineInstr *cloneLoadStackGuard(MachineBasicBlock *CurMBB,
48+
MachineInstr *CheckCall);
49+
50+
void getGuardCheckSequence(MachineBasicBlock *CurMBB, MachineInstr *CheckCall,
51+
MachineInstr *SeqMI[5]);
52+
53+
void SplitBasicBlock(MachineBasicBlock *CurMBB, MachineBasicBlock *NewRetMBB,
54+
MachineBasicBlock::iterator SplitIt);
55+
56+
void FinishBlock(MachineBasicBlock *MBB);
57+
58+
void FinishFunction(MachineBasicBlock *FailMBB, MachineBasicBlock *NewRetMBB);
59+
};
60+
} // end anonymous namespace
61+
62+
char AArch64WinFixupBufferSecurityCheckPass::ID = 0;
63+
64+
INITIALIZE_PASS(AArch64WinFixupBufferSecurityCheckPass, DEBUG_TYPE, DEBUG_TYPE,
65+
false, false)
66+
67+
FunctionPass *llvm::createAArch64WinFixupBufferSecurityCheckPass() {
68+
return new AArch64WinFixupBufferSecurityCheckPass();
69+
}
70+
71+
void AArch64WinFixupBufferSecurityCheckPass::SplitBasicBlock(
72+
MachineBasicBlock *CurMBB, MachineBasicBlock *NewRetMBB,
73+
MachineBasicBlock::iterator SplitIt) {
74+
NewRetMBB->splice(NewRetMBB->end(), CurMBB, SplitIt, CurMBB->end());
75+
}
76+
77+
std::pair<MachineBasicBlock *, MachineInstr *>
78+
AArch64WinFixupBufferSecurityCheckPass::getSecurityCheckerBasicBlock(
79+
MachineFunction &MF) {
80+
for (auto &MBB : MF) {
81+
for (auto &MI : MBB) {
82+
if (MI.getOpcode() == AArch64::BL && MI.getNumExplicitOperands() == 1) {
83+
auto MO = MI.getOperand(0);
84+
if (MO.isGlobal()) {
85+
auto Callee = dyn_cast<Function>(MO.getGlobal());
86+
if (Callee && Callee->getName() == "__security_check_cookie") {
87+
return std::make_pair(&MBB, &MI);
88+
break;
89+
}
90+
}
91+
}
92+
}
93+
}
94+
return std::make_pair(nullptr, nullptr);
95+
}
96+
97+
MachineInstr *AArch64WinFixupBufferSecurityCheckPass::cloneLoadStackGuard(
98+
MachineBasicBlock *CurMBB, MachineInstr *CheckCall) {
99+
// Ensure that we have a valid MachineBasicBlock and CheckCall
100+
if (!CurMBB || !CheckCall)
101+
return nullptr;
102+
103+
MachineFunction &MF = *CurMBB->getParent();
104+
MachineRegisterInfo &MRI = MF.getRegInfo();
105+
106+
// Initialize reverse iterator starting just before CheckCall
107+
MachineBasicBlock::reverse_iterator DIt(CheckCall);
108+
MachineBasicBlock::reverse_iterator DEnd = CurMBB->rend();
109+
110+
// Reverse iterate from CheckCall to find LOAD_STACK_GUARD
111+
for (; DIt != DEnd; ++DIt) {
112+
MachineInstr &MI = *DIt;
113+
if (MI.getOpcode() == TargetOpcode::LOAD_STACK_GUARD) {
114+
// Clone the LOAD_STACK_GUARD instruction
115+
MachineInstr *ClonedInstr = MF.CloneMachineInstr(&MI);
116+
117+
// Get the register class of the original destination register
118+
Register OrigReg = MI.getOperand(0).getReg();
119+
const TargetRegisterClass *RegClass = MRI.getRegClass(OrigReg);
120+
121+
// Create a new virtual register in the same register class
122+
Register NewReg = MRI.createVirtualRegister(RegClass);
123+
124+
// Update operand 0 (destination) of the cloned instruction
125+
MachineOperand &DestOperand = ClonedInstr->getOperand(0);
126+
if (DestOperand.isReg() && DestOperand.isDef()) {
127+
DestOperand.setReg(NewReg); // Set the new virtual register
128+
}
129+
130+
// Return the modified cloned instruction
131+
return ClonedInstr;
132+
}
133+
}
134+
135+
// If no LOAD_STACK_GUARD instruction was found, return nullptr
136+
return nullptr;
137+
}
138+
139+
void AArch64WinFixupBufferSecurityCheckPass::getGuardCheckSequence(
140+
MachineBasicBlock *CurMBB, MachineInstr *CheckCall,
141+
MachineInstr *SeqMI[5]) {
142+
143+
MachineBasicBlock::iterator UIt(CheckCall);
144+
MachineBasicBlock::reverse_iterator DIt(CheckCall);
145+
146+
// Move forward to find the stack adjustment after the call
147+
// to __security_check_cookie
148+
++UIt;
149+
SeqMI[4] = &*UIt;
150+
151+
// Assign the BL instruction (call to __security_check_cookie)
152+
SeqMI[3] = CheckCall;
153+
154+
// COPY function slot cookie
155+
++DIt;
156+
SeqMI[2] = &*DIt;
157+
158+
// Move backward to find the instruction that loads the security cookie from
159+
// the stack
160+
++DIt;
161+
SeqMI[1] = &*DIt;
162+
163+
++DIt; // Find ADJCALLSTACKDOWN
164+
SeqMI[0] = &*DIt;
165+
}
166+
167+
void AArch64WinFixupBufferSecurityCheckPass::FinishBlock(
168+
MachineBasicBlock *MBB) {
169+
LivePhysRegs LiveRegs;
170+
computeAndAddLiveIns(LiveRegs, *MBB);
171+
}
172+
173+
void AArch64WinFixupBufferSecurityCheckPass::FinishFunction(
174+
MachineBasicBlock *FailMBB, MachineBasicBlock *NewRetMBB) {
175+
FailMBB->getParent()->RenumberBlocks();
176+
// FailMBB includes call to MSCV RT where __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 AArch64WinFixupBufferSecurityCheckPass::runOnMachineFunction(
186+
MachineFunction &MF) {
187+
bool Changed = false;
188+
const AArch64Subtarget &STI = MF.getSubtarget<AArch64Subtarget>();
189+
190+
if (!STI.getTargetTriple().isWindowsMSVCEnvironment())
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 call was installed or not
202+
auto [CurMBB, CheckCall] = getSecurityCheckerBasicBlock(MF);
203+
if (!CheckCall)
204+
return Changed;
205+
206+
// Get sequence of instruction in CurMBB responsible for calling
207+
// __security_check_cookie
208+
MachineInstr *SeqMI[5];
209+
getGuardCheckSequence(CurMBB, CheckCall, SeqMI);
210+
211+
// Find LOAD_STACK_GUARD in CurrMBB and build a new LOAD_STACK_GUARD
212+
// instruction with new destination register
213+
MachineInstr *ClonedInstr = cloneLoadStackGuard(CurMBB, CheckCall);
214+
if (!ClonedInstr)
215+
return Changed;
216+
217+
// Insert cloned LOAD_STACK_GUARD right before the call to
218+
// __security_check_cookie
219+
MachineBasicBlock::iterator InsertPt(SeqMI[0]);
220+
CurMBB->insert(InsertPt, ClonedInstr);
221+
222+
auto CookieLoadReg = SeqMI[1]->getOperand(0).getReg();
223+
auto GlobalCookieReg = ClonedInstr->getOperand(0).getReg();
224+
225+
// Move LDRXui that loads __security_cookie from stack, right after
226+
// the cloned LOAD_STACK_GUARD
227+
CurMBB->splice(InsertPt, CurMBB, std::next(InsertPt));
228+
229+
// Create a new virtual register for the CMP instruction result
230+
Register DiscardReg =
231+
MF.getRegInfo().createVirtualRegister(&AArch64::GPR64RegClass);
232+
233+
// Emit the CMP instruction to compare stack cookie with global cookie
234+
BuildMI(*CurMBB, InsertPt, DebugLoc(), TII->get(AArch64::SUBSXrr))
235+
.addReg(DiscardReg, RegState::Define | RegState::Dead) // Result discarded
236+
.addReg(CookieLoadReg) // First operand: stack cookie
237+
.addReg(GlobalCookieReg); // Second operand: global cookie
238+
239+
// Create FailMBB basic block to call __security_check_cookie
240+
MachineBasicBlock *FailMBB = MF.CreateMachineBasicBlock();
241+
MF.insert(MF.end(), FailMBB);
242+
243+
// Create NewRetMBB basic block to skip call to __security_check_cookie
244+
MachineBasicBlock *NewRetMBB = MF.CreateMachineBasicBlock();
245+
MF.insert(MF.end(), NewRetMBB);
246+
247+
// Conditional branch to FailMBB if cookies do not match
248+
BuildMI(*CurMBB, InsertPt, DebugLoc(), TII->get(AArch64::Bcc))
249+
.addImm(AArch64CC::NE) // Condition: Not Equal
250+
.addMBB(FailMBB); // Failure block
251+
252+
// Add an unconditional branch to NewRetMBB.
253+
BuildMI(*CurMBB, InsertPt, DebugLoc(), TII->get(AArch64::B))
254+
.addMBB(NewRetMBB);
255+
256+
// Move fail check squence from CurMBB to FailMBB
257+
MachineBasicBlock::iterator U2It(SeqMI[4]);
258+
++U2It;
259+
FailMBB->splice(FailMBB->end(), CurMBB, InsertPt, U2It);
260+
261+
// Insert a BRK instruction at the end of the FailMBB
262+
BuildMI(*FailMBB, FailMBB->end(), DebugLoc(), TII->get(AArch64::BRK))
263+
.addImm(0); // Immediate value for BRK
264+
265+
// Move remaining instructions after CheckCall to NewRetMBB.
266+
NewRetMBB->splice(NewRetMBB->end(), CurMBB, U2It, CurMBB->end());
267+
268+
// Restructure Basic Blocks
269+
CurMBB->addSuccessor(NewRetMBB);
270+
CurMBB->addSuccessor(FailMBB);
271+
272+
FinishFunction(FailMBB, NewRetMBB);
273+
274+
return !Changed;
275+
}

llvm/lib/Target/AArch64/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ add_llvm_target(AArch64CodeGen
8585
AArch64TargetMachine.cpp
8686
AArch64TargetObjectFile.cpp
8787
AArch64TargetTransformInfo.cpp
88+
AArch64WinFixupBufferSecurityCheck.cpp
8889
SMEABIPass.cpp
8990
SMEPeepholeOpt.cpp
9091
SVEIntrinsicOpts.cpp

llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-stack-protector-windows.ll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,22 @@ define void @caller() sspreq {
1717
; CHECK-NEXT: ldr x8, [x8, :lo12:__security_cookie]
1818
; CHECK-NEXT: str x8, [sp, #8]
1919
; CHECK-NEXT: bl callee
20+
; CHECK-NEXT: adrp x8, __security_cookie
2021
; CHECK-NEXT: ldr x0, [sp, #8]
21-
; CHECK-NEXT: bl __security_check_cookie
22+
; CHECK-NEXT: ldr x8, [x8, :lo12:__security_cookie]
23+
; CHECK-NEXT: subs x8, x0, x8
24+
; CHECK-NEXT: b.ne .LBB0_2
25+
; CHECK: // %bb.1:
2226
; CHECK-NEXT: .seh_startepilogue
2327
; CHECK-NEXT: ldr x30, [sp, #16] // 8-byte Folded Reload
2428
; CHECK-NEXT: .seh_save_reg x30, 16
2529
; CHECK-NEXT: add sp, sp, #32
2630
; CHECK-NEXT: .seh_stackalloc 32
2731
; CHECK-NEXT: .seh_endepilogue
2832
; CHECK-NEXT: ret
33+
; CHECK: .LBB0_2:
34+
; CHECK-NEXT: bl __security_check_cookie
35+
; CHECK-NEXT: brk #0
2936
; CHECK-NEXT: .seh_endfunclet
3037
; CHECK-NEXT: .seh_endproc
3138
entry:

llvm/test/CodeGen/AArch64/O0-pipeline.ll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
; CHECK-NEXT: AArch64 Instruction Selection
5555
; CHECK-NEXT: Finalize ISel and expand pseudo-instructions
5656
; CHECK-NEXT: Local Stack Slot Allocation
57+
; CHECK-NEXT: AArch64 Windows Fixup Buffer Security Check
5758
; CHECK-NEXT: Eliminate PHI nodes for register allocation
5859
; CHECK-NEXT: Two-Address instruction pass
5960
; CHECK-NEXT: Fast Register Allocator

llvm/test/CodeGen/AArch64/O3-pipeline.ll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,16 @@
162162
; CHECK-NEXT: Remove dead machine instructions
163163
; CHECK-NEXT: AArch64 MI Peephole Optimization pass
164164
; CHECK-NEXT: AArch64 Dead register definitions
165+
; CHECK-NEXT: AArch64 Windows Fixup Buffer Security Check
165166
; CHECK-NEXT: Detect Dead Lanes
166167
; CHECK-NEXT: Init Undef Pass
167168
; CHECK-NEXT: Process Implicit Definitions
168169
; CHECK-NEXT: Remove unreachable machine basic blocks
169170
; CHECK-NEXT: Live Variable Analysis
171+
; CHECK-NEXT: MachineDominator Tree Construction
172+
; CHECK-NEXT: Machine Natural Loop Construction
170173
; CHECK-NEXT: Eliminate PHI nodes for register allocation
171174
; CHECK-NEXT: Two-Address instruction pass
172-
; CHECK-NEXT: MachineDominator Tree Construction
173175
; CHECK-NEXT: Slot index numbering
174176
; CHECK-NEXT: Live Interval Analysis
175177
; CHECK-NEXT: Register Coalescer

0 commit comments

Comments
 (0)