Skip to content

Commit df140b2

Browse files
committed
[RISCV] Add support for calling convention hardening
This patch changes the shadow call stack implementation to strengthen the hardening it provides, based on platform-specific assumptions. Both the return address and the stack pointer are now saved to the shadow call stack during the function prologue: ```asm s[w|d] ra, 0(s2) s[w|d] sp, [4|8](s2) addi s2, s2, [4|8]*2 ``` The hardened shadow call stack epilogue implements the following logic, without using branches: ```c if (sp == shadow_sp && ra == shadow_ra) { return; } else { __abi_shutdown$(); } ``` The new epilogue instruction sequence is: ```asm l[w|d] t0, -[4|8](s2) l[w|d] t1, -[4|8]*2(s2) addi s2, s2, -[4|8] xor t0, t0, sp xor t1, t1, ra or t0, t0, t1 snez t1, t0 addi t1, t1, -1 not t0, t1 lla t2, __abi_shutdown$ and t0, t0, t2 and t1, t1, ra or ra, t0, t1 The shadow call stack attribute is ignored in interrupt handler functions. ```
1 parent f82b487 commit df140b2

File tree

2 files changed

+271
-33
lines changed

2 files changed

+271
-33
lines changed

llvm/lib/Target/RISCV/RISCVFrameLowering.cpp

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,19 @@
2727

2828
using namespace llvm;
2929

30+
// Returns the register used to hold the frame pointer.
31+
static Register getFPReg(const RISCVSubtarget &STI) { return RISCV::X8; }
32+
33+
// Returns the register used to hold the stack pointer.
34+
static Register getSPReg(const RISCVSubtarget &STI) { return RISCV::X2; }
35+
3036
// For now we use x18, a.k.a s2, as pointer to shadow call stack.
3137
// User should explicitly set -ffixed-x18 and not use x18 in their asm.
3238
static void emitSCSPrologue(MachineFunction &MF, MachineBasicBlock &MBB,
3339
MachineBasicBlock::iterator MI,
3440
const DebugLoc &DL) {
35-
if (!MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack))
41+
if (!MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack) ||
42+
MF.getFunction().hasFnAttribute("interrupt"))
3643
return;
3744

3845
const auto &STI = MF.getSubtarget<RISCVSubtarget>();
@@ -65,25 +72,32 @@ static void emitSCSPrologue(MachineFunction &MF, MachineBasicBlock &MBB,
6572
const RISCVInstrInfo *TII = STI.getInstrInfo();
6673
bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit);
6774
int64_t SlotSize = STI.getXLen() / 8;
68-
// Store return address to shadow call stack
75+
// Store return address and the stack pointer to shadow call stack.
6976
// s[w|d] ra, 0(s2)
70-
// addi s2, s2, [4|8]
77+
// s[w|d] sp, [4|8](s2)
78+
// addi s2, s2, [4|8]*2
7179
BuildMI(MBB, MI, DL, TII->get(IsRV64 ? RISCV::SD : RISCV::SW))
7280
.addReg(RAReg)
7381
.addReg(SCSPReg)
7482
.addImm(0)
7583
.setMIFlag(MachineInstr::FrameSetup);
84+
BuildMI(MBB, MI, DL, TII->get(IsRV64 ? RISCV::SD : RISCV::SW))
85+
.addReg(getSPReg(STI))
86+
.addReg(SCSPReg)
87+
.addImm(SlotSize)
88+
.setMIFlag(MachineInstr::FrameSetup);
7689
BuildMI(MBB, MI, DL, TII->get(RISCV::ADDI))
7790
.addReg(SCSPReg, RegState::Define)
7891
.addReg(SCSPReg)
79-
.addImm(SlotSize)
92+
.addImm(SlotSize * 2)
8093
.setMIFlag(MachineInstr::FrameSetup);
8194
}
8295

8396
static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB,
8497
MachineBasicBlock::iterator MI,
8598
const DebugLoc &DL) {
86-
if (!MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack))
99+
if (!MF.getFunction().hasFnAttribute(Attribute::ShadowCallStack) ||
100+
MF.getFunction().hasFnAttribute("interrupt"))
87101
return;
88102

89103
const auto &STI = MF.getSubtarget<RISCVSubtarget>();
@@ -115,18 +129,92 @@ static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB,
115129
const RISCVInstrInfo *TII = STI.getInstrInfo();
116130
bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit);
117131
int64_t SlotSize = STI.getXLen() / 8;
118-
// Load return address from shadow call stack
119-
// l[w|d] ra, -[4|8](s2)
132+
// The assembly code below loads the return address and stack pointer from
133+
// the shadow call stack and executes the following pseudo-code without using
134+
// branches:
135+
//
136+
// if (sp == shadow_sp && ra == shadow_ra) {
137+
// return;
138+
// } else {
139+
// __abi_shutdown$();
140+
// }
141+
//
142+
// l[w|d] t0, -[4|8](s2)
143+
// l[w|d] t1, -[4|8]*2(s2)
120144
// addi s2, s2, -[4|8]
145+
// xor t0, t0, sp
146+
// xor t1, t1, ra
147+
// or t0, t0, t1
148+
// snez t1, t0
149+
// addi t1, t1, -1
150+
// not t0, t1
151+
// la t2, __abi_shutdown$
152+
// and t0, t0, t2
153+
// and t1, t1, ra
154+
// or ra, t0, t1
121155
BuildMI(MBB, MI, DL, TII->get(IsRV64 ? RISCV::LD : RISCV::LW))
122-
.addReg(RAReg, RegState::Define)
156+
.addReg(RISCV::X5, RegState::Define)
123157
.addReg(SCSPReg)
124158
.addImm(-SlotSize)
125159
.setMIFlag(MachineInstr::FrameDestroy);
160+
BuildMI(MBB, MI, DL, TII->get(IsRV64 ? RISCV::LD : RISCV::LW))
161+
.addReg(RISCV::X6, RegState::Define)
162+
.addReg(SCSPReg)
163+
.addImm(-SlotSize * 2)
164+
.setMIFlag(MachineInstr::FrameDestroy);
126165
BuildMI(MBB, MI, DL, TII->get(RISCV::ADDI))
127166
.addReg(SCSPReg, RegState::Define)
128167
.addReg(SCSPReg)
129-
.addImm(-SlotSize)
168+
.addImm(-SlotSize * 2)
169+
.setMIFlag(MachineInstr::FrameDestroy);
170+
BuildMI(MBB, MI, DL, TII->get(RISCV::XOR))
171+
.addReg(RISCV::X5, RegState::Define)
172+
.addReg(RISCV::X5)
173+
.addReg(getSPReg(STI))
174+
.setMIFlag(MachineInstr::FrameDestroy);
175+
BuildMI(MBB, MI, DL, TII->get(RISCV::XOR))
176+
.addReg(RISCV::X6, RegState::Define)
177+
.addReg(RISCV::X6)
178+
.addReg(RAReg)
179+
.setMIFlag(MachineInstr::FrameDestroy);
180+
BuildMI(MBB, MI, DL, TII->get(RISCV::OR))
181+
.addReg(RISCV::X5, RegState::Define)
182+
.addReg(RISCV::X5)
183+
.addReg(RISCV::X6)
184+
.setMIFlag(MachineInstr::FrameDestroy);
185+
BuildMI(MBB, MI, DL, TII->get(RISCV::SLTU))
186+
.addReg(RISCV::X6, RegState::Define)
187+
.addReg(RISCV::X0)
188+
.addReg(RISCV::X5)
189+
.setMIFlag(MachineInstr::FrameDestroy);
190+
BuildMI(MBB, MI, DL, TII->get(RISCV::ADDI))
191+
.addReg(RISCV::X6, RegState::Define)
192+
.addReg(RISCV::X6)
193+
.addImm(-1)
194+
.setMIFlag(MachineInstr::FrameDestroy);
195+
BuildMI(MBB, MI, DL, TII->get(RISCV::XORI))
196+
.addReg(RISCV::X5, RegState::Define)
197+
.addReg(RISCV::X6)
198+
.addImm(-1)
199+
.setMIFlag(MachineInstr::FrameDestroy);
200+
BuildMI(MBB, MI, DL, TII->get(RISCV::PseudoLLA))
201+
.addReg(RISCV::X7, RegState::Define)
202+
.addExternalSymbol("__abi_shutdown$", RISCVII::MO_CALL)
203+
.setMIFlag(MachineInstr::FrameDestroy);
204+
BuildMI(MBB, MI, DL, TII->get(RISCV::AND))
205+
.addReg(RISCV::X5, RegState::Define)
206+
.addReg(RISCV::X5)
207+
.addReg(RISCV::X7, RegState::Kill)
208+
.setMIFlag(MachineInstr::FrameDestroy);
209+
BuildMI(MBB, MI, DL, TII->get(RISCV::AND))
210+
.addReg(RISCV::X6, RegState::Define)
211+
.addReg(RISCV::X6)
212+
.addReg(RAReg)
213+
.setMIFlag(MachineInstr::FrameDestroy);
214+
BuildMI(MBB, MI, DL, TII->get(RISCV::OR))
215+
.addReg(RAReg, RegState::Define)
216+
.addReg(RISCV::X5, RegState::Kill)
217+
.addReg(RISCV::X6, RegState::Kill)
130218
.setMIFlag(MachineInstr::FrameDestroy);
131219
}
132220

@@ -291,12 +379,6 @@ uint64_t RISCVFrameLowering::getStackSizeWithRVVPadding(
291379
return alignTo(MFI.getStackSize() + RVFI->getRVVPadding(), getStackAlign());
292380
}
293381

294-
// Returns the register used to hold the frame pointer.
295-
static Register getFPReg(const RISCVSubtarget &STI) { return RISCV::X8; }
296-
297-
// Returns the register used to hold the stack pointer.
298-
static Register getSPReg(const RISCVSubtarget &STI) { return RISCV::X2; }
299-
300382
static SmallVector<CalleeSavedInfo, 8>
301383
getNonLibcallCSI(const MachineFunction &MF,
302384
const std::vector<CalleeSavedInfo> &CSI) {

0 commit comments

Comments
 (0)