Skip to content

Commit 59037c0

Browse files
authored
[RISCV] Add Zicfiss support to the shadow call stack implementation. (llvm#68075)
This patch enable hardware shadow stack with `Zicifss` and `mno-forced-sw-shadow-stack`. New feature forced-sw-shadow-stack disables hardware shadow stack even when `Zicfiss` enabled.
1 parent 9981f5a commit 59037c0

File tree

6 files changed

+187
-18
lines changed

6 files changed

+187
-18
lines changed

clang/docs/ShadowCallStack.rst

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,25 @@ compiled application or the operating system. Integrating the runtime into
5757
the operating system should be preferred since otherwise all thread creation
5858
and destruction would need to be intercepted by the application.
5959

60-
The instrumentation makes use of the platform register ``x18`` on AArch64 and
61-
``x3`` (``gp``) on RISC-V. For simplicity we will refer to this as the
62-
``SCSReg``. On some platforms, ``SCSReg`` is reserved, and on others, it is
63-
designated as a scratch register. This generally means that any code that may
64-
run on the same thread as code compiled with ShadowCallStack must either target
65-
one of the platforms whose ABI reserves ``SCSReg`` (currently Android, Darwin,
66-
Fuchsia and Windows) or be compiled with a flag to reserve that register (e.g.,
67-
``-ffixed-x18``). If absolutely necessary, code compiled without reserving the
68-
register may be run on the same thread as code that uses ShadowCallStack by
69-
saving the register value temporarily on the stack (`example in Android`_) but
70-
this should be done with care since it risks leaking the shadow call stack
71-
address.
72-
60+
The instrumentation makes use of the platform register ``x18`` on AArch64,
61+
``x3`` (``gp``) on RISC-V with software shadow stack and ``ssp`` on RISC-V with
62+
hardware shadow stack, which needs `Zicfiss`_ and ``-mno-forced-sw-shadow-stack``
63+
(default option). Note that with ``Zicfiss``_ the RISC-V backend will default to
64+
the hardware based shadow call stack. Users can force the RISC-V backend to
65+
generate the software shadow call stack with ``Zicfiss``_ by passing
66+
``-mforced-sw-shadow-stack``.
67+
For simplicity we will refer to this as the ``SCSReg``. On some platforms,
68+
``SCSReg`` is reserved, and on others, it is designated as a scratch register.
69+
This generally means that any code that may run on the same thread as code
70+
compiled with ShadowCallStack must either target one of the platforms whose ABI
71+
reserves ``SCSReg`` (currently Android, Darwin, Fuchsia and Windows) or be
72+
compiled with a flag to reserve that register (e.g., ``-ffixed-x18``). If
73+
absolutely necessary, code compiled without reserving the register may be run on
74+
the same thread as code that uses ShadowCallStack by saving the register value
75+
temporarily on the stack (`example in Android`_) but this should be done with
76+
care since it risks leaking the shadow call stack address.
77+
78+
.. _`Zicfiss`: https://github.com/riscv/riscv-cfi/blob/main/cfi_backward.adoc
7379
.. _`example in Android`: https://android-review.googlesource.com/c/platform/frameworks/base/+/803717
7480

7581
Because it requires a dedicated register, the ShadowCallStack feature is
@@ -151,9 +157,13 @@ Usage
151157

152158
To enable ShadowCallStack, just pass the ``-fsanitize=shadow-call-stack`` flag
153159
to both compile and link command lines. On aarch64, you also need to pass
154-
``-ffixed-x18`` unless your target already reserves ``x18``. On RISC-V, ``x3``
155-
(``gp``) is always reserved. It is, however, important to disable GP relaxation
156-
in the linker. This can be done with the ``--no-relax-gp`` flag in GNU ld.
160+
``-ffixed-x18`` unless your target already reserves ``x18``. No additional flags
161+
need to be passed on RISC-V because the software based shadow stack uses
162+
``x3`` (``gp``), which is always reserved, and the hardware based shadow call
163+
stack uses a dedicated register, ``ssp``.
164+
However, it is important to disable GP relaxation in the linker when using the
165+
software based shadow call stack on RISC-V. This can be done with the
166+
``--no-relax-gp`` flag in GNU ld, and is off by default in LLD.
157167

158168
Low-level API
159169
-------------

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4614,6 +4614,10 @@ def msave_restore : Flag<["-"], "msave-restore">, Group<m_riscv_Features_Group>,
46144614
HelpText<"Enable using library calls for save and restore">;
46154615
def mno_save_restore : Flag<["-"], "mno-save-restore">, Group<m_riscv_Features_Group>,
46164616
HelpText<"Disable using library calls for save and restore">;
4617+
def mforced_sw_shadow_stack : Flag<["-"], "mforced-sw-shadow-stack">, Group<m_riscv_Features_Group>,
4618+
HelpText<"Force using software shadow stack when shadow-stack enabled">;
4619+
def mno_forced_sw_shadow_stack : Flag<["-"], "mno-forced-sw-shadow-stack">, Group<m_riscv_Features_Group>,
4620+
HelpText<"Not force using software shadow stack when shadow-stack enabled">;
46174621
} // let Flags = [TargetSpecific]
46184622
let Flags = [TargetSpecific] in {
46194623
def menable_experimental_extensions : Flag<["-"], "menable-experimental-extensions">, Group<m_Group>,

clang/test/Driver/riscv-features.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
// DEFAULT-NOT: "-target-feature" "-save-restore"
2828
// DEFAULT-NOT: "-target-feature" "+save-restore"
2929

30+
// RUN: %clang --target=riscv32-unknown-elf -### %s -mforced-sw-shadow-stack 2>&1 | FileCheck %s -check-prefix=FORCE-SW-SCS
31+
// RUN: %clang --target=riscv32-unknown-elf -### %s -mno-forced-sw-shadow-stack 2>&1 | FileCheck %s -check-prefix=NO-FORCE-SW-SCS
32+
// FORCE-SW-SCS: "-target-feature" "+forced-sw-shadow-stack"
33+
// NO-FORCE-SW-SCS: "-target-feature" "-forced-sw-shadow-stack"
34+
// DEFAULT-NOT: "-target-feature" "+forced-sw-shadow-stack"
35+
3036
// RUN: %clang --target=riscv32-unknown-elf -### %s -munaligned-access 2>&1 | FileCheck %s -check-prefix=FAST-UNALIGNED-ACCESS
3137
// RUN: %clang --target=riscv32-unknown-elf -### %s -mno-unaligned-access 2>&1 | FileCheck %s -check-prefix=NO-FAST-UNALIGNED-ACCESS
3238
// RUN: %clang --target=riscv32-unknown-elf -### %s -mno-strict-align 2>&1 | FileCheck %s -check-prefix=FAST-UNALIGNED-ACCESS

llvm/lib/Target/RISCV/RISCVFeatures.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,3 +1227,8 @@ def FeatureTaggedGlobals : SubtargetFeature<"tagged-globals",
12271227
"AllowTaggedGlobals",
12281228
"true", "Use an instruction sequence for taking the address of a global "
12291229
"that allows a memory tag in the upper address bits">;
1230+
1231+
def FeatureForcedSWShadowStack : SubtargetFeature<
1232+
"forced-sw-shadow-stack", "HasForcedSWShadowStack", "true",
1233+
"Implement shadow stack with software.">;
1234+
def HasForcedSWShadowStack : Predicate<"Subtarget->hasForcedSWShadowStack()">;

llvm/lib/Target/RISCV/RISCVFrameLowering.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,14 @@ static void emitSCSPrologue(MachineFunction &MF, MachineBasicBlock &MBB,
6666
CSI, [&](CalleeSavedInfo &CSR) { return CSR.getReg() == RAReg; }))
6767
return;
6868

69+
const RISCVInstrInfo *TII = STI.getInstrInfo();
70+
if (!STI.hasForcedSWShadowStack() && STI.hasStdExtZicfiss()) {
71+
BuildMI(MBB, MI, DL, TII->get(RISCV::SSPUSH)).addReg(RAReg);
72+
return;
73+
}
74+
6975
Register SCSPReg = RISCVABI::getSCSPReg();
7076

71-
const RISCVInstrInfo *TII = STI.getInstrInfo();
7277
bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit);
7378
int64_t SlotSize = STI.getXLen() / 8;
7479
// Store return address to shadow call stack
@@ -121,9 +126,14 @@ static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB,
121126
CSI, [&](CalleeSavedInfo &CSR) { return CSR.getReg() == RAReg; }))
122127
return;
123128

129+
const RISCVInstrInfo *TII = STI.getInstrInfo();
130+
if (!STI.hasForcedSWShadowStack() && STI.hasStdExtZicfiss()) {
131+
BuildMI(MBB, MI, DL, TII->get(RISCV::SSPOPCHK)).addReg(RAReg);
132+
return;
133+
}
134+
124135
Register SCSPReg = RISCVABI::getSCSPReg();
125136

126-
const RISCVInstrInfo *TII = STI.getInstrInfo();
127137
bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit);
128138
int64_t SlotSize = STI.getXLen() / 8;
129139
// Load return address from shadow call stack

llvm/test/CodeGen/RISCV/shadowcallstack.ll

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
; RUN: | FileCheck %s --check-prefix=RV32
44
; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
55
; RUN: | FileCheck %s --check-prefix=RV64
6+
; RUN: llc -mtriple=riscv32 -mattr=+experimental-zicfiss < %s \
7+
; RUN: -verify-machineinstrs | FileCheck %s --check-prefix=RV32-ZICFISS
8+
; RUN: llc -mtriple=riscv64 -mattr=+experimental-zicfiss < %s \
9+
; RUN: -verify-machineinstrs | FileCheck %s --check-prefix=RV64-ZICFISS
10+
; RUN: llc -mtriple=riscv32 -mattr=+experimental-zicfiss,forced-sw-shadow-stack \
11+
; RUN: -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32
12+
; RUN: llc -mtriple=riscv64 -mattr=+experimental-zicfiss,forced-sw-shadow-stack \
13+
; RUN: -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64
614

715
define void @f1() shadowcallstack {
816
; RV32-LABEL: f1:
@@ -12,6 +20,14 @@ define void @f1() shadowcallstack {
1220
; RV64-LABEL: f1:
1321
; RV64: # %bb.0:
1422
; RV64-NEXT: ret
23+
;
24+
; RV32-ZICFISS-LABEL: f1:
25+
; RV32-ZICFISS: # %bb.0:
26+
; RV32-ZICFISS-NEXT: ret
27+
;
28+
; RV64-ZICFISS-LABEL: f1:
29+
; RV64-ZICFISS: # %bb.0:
30+
; RV64-ZICFISS-NEXT: ret
1531
ret void
1632
}
1733

@@ -25,6 +41,14 @@ define void @f2() shadowcallstack {
2541
; RV64-LABEL: f2:
2642
; RV64: # %bb.0:
2743
; RV64-NEXT: tail foo
44+
;
45+
; RV32-ZICFISS-LABEL: f2:
46+
; RV32-ZICFISS: # %bb.0:
47+
; RV32-ZICFISS-NEXT: tail foo
48+
;
49+
; RV64-ZICFISS-LABEL: f2:
50+
; RV64-ZICFISS: # %bb.0:
51+
; RV64-ZICFISS-NEXT: tail foo
2852
tail call void @foo()
2953
ret void
3054
}
@@ -65,6 +89,32 @@ define i32 @f3() shadowcallstack {
6589
; RV64-NEXT: addi gp, gp, -8
6690
; RV64-NEXT: .cfi_restore gp
6791
; RV64-NEXT: ret
92+
;
93+
; RV32-ZICFISS-LABEL: f3:
94+
; RV32-ZICFISS: # %bb.0:
95+
; RV32-ZICFISS-NEXT: sspush ra
96+
; RV32-ZICFISS-NEXT: addi sp, sp, -16
97+
; RV32-ZICFISS-NEXT: .cfi_def_cfa_offset 16
98+
; RV32-ZICFISS-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
99+
; RV32-ZICFISS-NEXT: .cfi_offset ra, -4
100+
; RV32-ZICFISS-NEXT: call bar
101+
; RV32-ZICFISS-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
102+
; RV32-ZICFISS-NEXT: addi sp, sp, 16
103+
; RV32-ZICFISS-NEXT: sspopchk ra
104+
; RV32-ZICFISS-NEXT: ret
105+
;
106+
; RV64-ZICFISS-LABEL: f3:
107+
; RV64-ZICFISS: # %bb.0:
108+
; RV64-ZICFISS-NEXT: sspush ra
109+
; RV64-ZICFISS-NEXT: addi sp, sp, -16
110+
; RV64-ZICFISS-NEXT: .cfi_def_cfa_offset 16
111+
; RV64-ZICFISS-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
112+
; RV64-ZICFISS-NEXT: .cfi_offset ra, -8
113+
; RV64-ZICFISS-NEXT: call bar
114+
; RV64-ZICFISS-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
115+
; RV64-ZICFISS-NEXT: addi sp, sp, 16
116+
; RV64-ZICFISS-NEXT: sspopchk ra
117+
; RV64-ZICFISS-NEXT: ret
68118
%res = call i32 @bar()
69119
%res1 = add i32 %res, 1
70120
ret i32 %res
@@ -140,6 +190,68 @@ define i32 @f4() shadowcallstack {
140190
; RV64-NEXT: addi gp, gp, -8
141191
; RV64-NEXT: .cfi_restore gp
142192
; RV64-NEXT: ret
193+
;
194+
; RV32-ZICFISS-LABEL: f4:
195+
; RV32-ZICFISS: # %bb.0:
196+
; RV32-ZICFISS-NEXT: sspush ra
197+
; RV32-ZICFISS-NEXT: addi sp, sp, -16
198+
; RV32-ZICFISS-NEXT: .cfi_def_cfa_offset 16
199+
; RV32-ZICFISS-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
200+
; RV32-ZICFISS-NEXT: sw s0, 8(sp) # 4-byte Folded Spill
201+
; RV32-ZICFISS-NEXT: sw s1, 4(sp) # 4-byte Folded Spill
202+
; RV32-ZICFISS-NEXT: sw s2, 0(sp) # 4-byte Folded Spill
203+
; RV32-ZICFISS-NEXT: .cfi_offset ra, -4
204+
; RV32-ZICFISS-NEXT: .cfi_offset s0, -8
205+
; RV32-ZICFISS-NEXT: .cfi_offset s1, -12
206+
; RV32-ZICFISS-NEXT: .cfi_offset s2, -16
207+
; RV32-ZICFISS-NEXT: call bar
208+
; RV32-ZICFISS-NEXT: mv s0, a0
209+
; RV32-ZICFISS-NEXT: call bar
210+
; RV32-ZICFISS-NEXT: mv s1, a0
211+
; RV32-ZICFISS-NEXT: call bar
212+
; RV32-ZICFISS-NEXT: mv s2, a0
213+
; RV32-ZICFISS-NEXT: call bar
214+
; RV32-ZICFISS-NEXT: add s0, s0, s1
215+
; RV32-ZICFISS-NEXT: add a0, s2, a0
216+
; RV32-ZICFISS-NEXT: add a0, s0, a0
217+
; RV32-ZICFISS-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
218+
; RV32-ZICFISS-NEXT: lw s0, 8(sp) # 4-byte Folded Reload
219+
; RV32-ZICFISS-NEXT: lw s1, 4(sp) # 4-byte Folded Reload
220+
; RV32-ZICFISS-NEXT: lw s2, 0(sp) # 4-byte Folded Reload
221+
; RV32-ZICFISS-NEXT: addi sp, sp, 16
222+
; RV32-ZICFISS-NEXT: sspopchk ra
223+
; RV32-ZICFISS-NEXT: ret
224+
;
225+
; RV64-ZICFISS-LABEL: f4:
226+
; RV64-ZICFISS: # %bb.0:
227+
; RV64-ZICFISS-NEXT: sspush ra
228+
; RV64-ZICFISS-NEXT: addi sp, sp, -32
229+
; RV64-ZICFISS-NEXT: .cfi_def_cfa_offset 32
230+
; RV64-ZICFISS-NEXT: sd ra, 24(sp) # 8-byte Folded Spill
231+
; RV64-ZICFISS-NEXT: sd s0, 16(sp) # 8-byte Folded Spill
232+
; RV64-ZICFISS-NEXT: sd s1, 8(sp) # 8-byte Folded Spill
233+
; RV64-ZICFISS-NEXT: sd s2, 0(sp) # 8-byte Folded Spill
234+
; RV64-ZICFISS-NEXT: .cfi_offset ra, -8
235+
; RV64-ZICFISS-NEXT: .cfi_offset s0, -16
236+
; RV64-ZICFISS-NEXT: .cfi_offset s1, -24
237+
; RV64-ZICFISS-NEXT: .cfi_offset s2, -32
238+
; RV64-ZICFISS-NEXT: call bar
239+
; RV64-ZICFISS-NEXT: mv s0, a0
240+
; RV64-ZICFISS-NEXT: call bar
241+
; RV64-ZICFISS-NEXT: mv s1, a0
242+
; RV64-ZICFISS-NEXT: call bar
243+
; RV64-ZICFISS-NEXT: mv s2, a0
244+
; RV64-ZICFISS-NEXT: call bar
245+
; RV64-ZICFISS-NEXT: add s0, s0, s1
246+
; RV64-ZICFISS-NEXT: add a0, s2, a0
247+
; RV64-ZICFISS-NEXT: addw a0, s0, a0
248+
; RV64-ZICFISS-NEXT: ld ra, 24(sp) # 8-byte Folded Reload
249+
; RV64-ZICFISS-NEXT: ld s0, 16(sp) # 8-byte Folded Reload
250+
; RV64-ZICFISS-NEXT: ld s1, 8(sp) # 8-byte Folded Reload
251+
; RV64-ZICFISS-NEXT: ld s2, 0(sp) # 8-byte Folded Reload
252+
; RV64-ZICFISS-NEXT: addi sp, sp, 32
253+
; RV64-ZICFISS-NEXT: sspopchk ra
254+
; RV64-ZICFISS-NEXT: ret
143255
%res1 = call i32 @bar()
144256
%res2 = call i32 @bar()
145257
%res3 = call i32 @bar()
@@ -176,6 +288,28 @@ define i32 @f5() shadowcallstack nounwind {
176288
; RV64-NEXT: ld ra, -8(gp)
177289
; RV64-NEXT: addi gp, gp, -8
178290
; RV64-NEXT: ret
291+
;
292+
; RV32-ZICFISS-LABEL: f5:
293+
; RV32-ZICFISS: # %bb.0:
294+
; RV32-ZICFISS-NEXT: sspush ra
295+
; RV32-ZICFISS-NEXT: addi sp, sp, -16
296+
; RV32-ZICFISS-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
297+
; RV32-ZICFISS-NEXT: call bar
298+
; RV32-ZICFISS-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
299+
; RV32-ZICFISS-NEXT: addi sp, sp, 16
300+
; RV32-ZICFISS-NEXT: sspopchk ra
301+
; RV32-ZICFISS-NEXT: ret
302+
;
303+
; RV64-ZICFISS-LABEL: f5:
304+
; RV64-ZICFISS: # %bb.0:
305+
; RV64-ZICFISS-NEXT: sspush ra
306+
; RV64-ZICFISS-NEXT: addi sp, sp, -16
307+
; RV64-ZICFISS-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
308+
; RV64-ZICFISS-NEXT: call bar
309+
; RV64-ZICFISS-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
310+
; RV64-ZICFISS-NEXT: addi sp, sp, 16
311+
; RV64-ZICFISS-NEXT: sspopchk ra
312+
; RV64-ZICFISS-NEXT: ret
179313
%res = call i32 @bar()
180314
%res1 = add i32 %res, 1
181315
ret i32 %res

0 commit comments

Comments
 (0)