mirror of
https://github.com/intel/llvm.git
synced 2026-01-26 03:56:16 +08:00
[RISCV] Add Zicfiss support to the shadow call stack implementation. (#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.
This commit is contained in:
@@ -57,19 +57,25 @@ compiled application or the operating system. Integrating the runtime into
|
||||
the operating system should be preferred since otherwise all thread creation
|
||||
and destruction would need to be intercepted by the application.
|
||||
|
||||
The instrumentation makes use of the platform register ``x18`` on AArch64 and
|
||||
``x3`` (``gp``) on RISC-V. For simplicity we will refer to this as the
|
||||
``SCSReg``. On some platforms, ``SCSReg`` is reserved, and on others, it is
|
||||
designated as a scratch register. This generally means that any code that may
|
||||
run on the same thread as code compiled with ShadowCallStack must either target
|
||||
one of the platforms whose ABI reserves ``SCSReg`` (currently Android, Darwin,
|
||||
Fuchsia and Windows) or be compiled with a flag to reserve that register (e.g.,
|
||||
``-ffixed-x18``). If absolutely necessary, code compiled without reserving the
|
||||
register may be run on the same thread as code that uses ShadowCallStack by
|
||||
saving the register value temporarily on the stack (`example in Android`_) but
|
||||
this should be done with care since it risks leaking the shadow call stack
|
||||
address.
|
||||
The instrumentation makes use of the platform register ``x18`` on AArch64,
|
||||
``x3`` (``gp``) on RISC-V with software shadow stack and ``ssp`` on RISC-V with
|
||||
hardware shadow stack, which needs `Zicfiss`_ and ``-mno-forced-sw-shadow-stack``
|
||||
(default option). Note that with ``Zicfiss``_ the RISC-V backend will default to
|
||||
the hardware based shadow call stack. Users can force the RISC-V backend to
|
||||
generate the software shadow call stack with ``Zicfiss``_ by passing
|
||||
``-mforced-sw-shadow-stack``.
|
||||
For simplicity we will refer to this as the ``SCSReg``. On some platforms,
|
||||
``SCSReg`` is reserved, and on others, it is designated as a scratch register.
|
||||
This generally means that any code that may run on the same thread as code
|
||||
compiled with ShadowCallStack must either target one of the platforms whose ABI
|
||||
reserves ``SCSReg`` (currently Android, Darwin, Fuchsia and Windows) or be
|
||||
compiled with a flag to reserve that register (e.g., ``-ffixed-x18``). If
|
||||
absolutely necessary, code compiled without reserving the register may be run on
|
||||
the same thread as code that uses ShadowCallStack by saving the register value
|
||||
temporarily on the stack (`example in Android`_) but this should be done with
|
||||
care since it risks leaking the shadow call stack address.
|
||||
|
||||
.. _`Zicfiss`: https://github.com/riscv/riscv-cfi/blob/main/cfi_backward.adoc
|
||||
.. _`example in Android`: https://android-review.googlesource.com/c/platform/frameworks/base/+/803717
|
||||
|
||||
Because it requires a dedicated register, the ShadowCallStack feature is
|
||||
@@ -151,9 +157,13 @@ Usage
|
||||
|
||||
To enable ShadowCallStack, just pass the ``-fsanitize=shadow-call-stack`` flag
|
||||
to both compile and link command lines. On aarch64, you also need to pass
|
||||
``-ffixed-x18`` unless your target already reserves ``x18``. On RISC-V, ``x3``
|
||||
(``gp``) is always reserved. It is, however, important to disable GP relaxation
|
||||
in the linker. This can be done with the ``--no-relax-gp`` flag in GNU ld.
|
||||
``-ffixed-x18`` unless your target already reserves ``x18``. No additional flags
|
||||
need to be passed on RISC-V because the software based shadow stack uses
|
||||
``x3`` (``gp``), which is always reserved, and the hardware based shadow call
|
||||
stack uses a dedicated register, ``ssp``.
|
||||
However, it is important to disable GP relaxation in the linker when using the
|
||||
software based shadow call stack on RISC-V. This can be done with the
|
||||
``--no-relax-gp`` flag in GNU ld, and is off by default in LLD.
|
||||
|
||||
Low-level API
|
||||
-------------
|
||||
|
||||
@@ -4614,6 +4614,10 @@ def msave_restore : Flag<["-"], "msave-restore">, Group<m_riscv_Features_Group>,
|
||||
HelpText<"Enable using library calls for save and restore">;
|
||||
def mno_save_restore : Flag<["-"], "mno-save-restore">, Group<m_riscv_Features_Group>,
|
||||
HelpText<"Disable using library calls for save and restore">;
|
||||
def mforced_sw_shadow_stack : Flag<["-"], "mforced-sw-shadow-stack">, Group<m_riscv_Features_Group>,
|
||||
HelpText<"Force using software shadow stack when shadow-stack enabled">;
|
||||
def mno_forced_sw_shadow_stack : Flag<["-"], "mno-forced-sw-shadow-stack">, Group<m_riscv_Features_Group>,
|
||||
HelpText<"Not force using software shadow stack when shadow-stack enabled">;
|
||||
} // let Flags = [TargetSpecific]
|
||||
let Flags = [TargetSpecific] in {
|
||||
def menable_experimental_extensions : Flag<["-"], "menable-experimental-extensions">, Group<m_Group>,
|
||||
|
||||
@@ -27,6 +27,12 @@
|
||||
// DEFAULT-NOT: "-target-feature" "-save-restore"
|
||||
// DEFAULT-NOT: "-target-feature" "+save-restore"
|
||||
|
||||
// RUN: %clang --target=riscv32-unknown-elf -### %s -mforced-sw-shadow-stack 2>&1 | FileCheck %s -check-prefix=FORCE-SW-SCS
|
||||
// RUN: %clang --target=riscv32-unknown-elf -### %s -mno-forced-sw-shadow-stack 2>&1 | FileCheck %s -check-prefix=NO-FORCE-SW-SCS
|
||||
// FORCE-SW-SCS: "-target-feature" "+forced-sw-shadow-stack"
|
||||
// NO-FORCE-SW-SCS: "-target-feature" "-forced-sw-shadow-stack"
|
||||
// DEFAULT-NOT: "-target-feature" "+forced-sw-shadow-stack"
|
||||
|
||||
// RUN: %clang --target=riscv32-unknown-elf -### %s -munaligned-access 2>&1 | FileCheck %s -check-prefix=FAST-UNALIGNED-ACCESS
|
||||
// RUN: %clang --target=riscv32-unknown-elf -### %s -mno-unaligned-access 2>&1 | FileCheck %s -check-prefix=NO-FAST-UNALIGNED-ACCESS
|
||||
// RUN: %clang --target=riscv32-unknown-elf -### %s -mno-strict-align 2>&1 | FileCheck %s -check-prefix=FAST-UNALIGNED-ACCESS
|
||||
|
||||
@@ -1227,3 +1227,8 @@ def FeatureTaggedGlobals : SubtargetFeature<"tagged-globals",
|
||||
"AllowTaggedGlobals",
|
||||
"true", "Use an instruction sequence for taking the address of a global "
|
||||
"that allows a memory tag in the upper address bits">;
|
||||
|
||||
def FeatureForcedSWShadowStack : SubtargetFeature<
|
||||
"forced-sw-shadow-stack", "HasForcedSWShadowStack", "true",
|
||||
"Implement shadow stack with software.">;
|
||||
def HasForcedSWShadowStack : Predicate<"Subtarget->hasForcedSWShadowStack()">;
|
||||
|
||||
@@ -66,9 +66,14 @@ static void emitSCSPrologue(MachineFunction &MF, MachineBasicBlock &MBB,
|
||||
CSI, [&](CalleeSavedInfo &CSR) { return CSR.getReg() == RAReg; }))
|
||||
return;
|
||||
|
||||
const RISCVInstrInfo *TII = STI.getInstrInfo();
|
||||
if (!STI.hasForcedSWShadowStack() && STI.hasStdExtZicfiss()) {
|
||||
BuildMI(MBB, MI, DL, TII->get(RISCV::SSPUSH)).addReg(RAReg);
|
||||
return;
|
||||
}
|
||||
|
||||
Register SCSPReg = RISCVABI::getSCSPReg();
|
||||
|
||||
const RISCVInstrInfo *TII = STI.getInstrInfo();
|
||||
bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit);
|
||||
int64_t SlotSize = STI.getXLen() / 8;
|
||||
// Store return address to shadow call stack
|
||||
@@ -121,9 +126,14 @@ static void emitSCSEpilogue(MachineFunction &MF, MachineBasicBlock &MBB,
|
||||
CSI, [&](CalleeSavedInfo &CSR) { return CSR.getReg() == RAReg; }))
|
||||
return;
|
||||
|
||||
const RISCVInstrInfo *TII = STI.getInstrInfo();
|
||||
if (!STI.hasForcedSWShadowStack() && STI.hasStdExtZicfiss()) {
|
||||
BuildMI(MBB, MI, DL, TII->get(RISCV::SSPOPCHK)).addReg(RAReg);
|
||||
return;
|
||||
}
|
||||
|
||||
Register SCSPReg = RISCVABI::getSCSPReg();
|
||||
|
||||
const RISCVInstrInfo *TII = STI.getInstrInfo();
|
||||
bool IsRV64 = STI.hasFeature(RISCV::Feature64Bit);
|
||||
int64_t SlotSize = STI.getXLen() / 8;
|
||||
// Load return address from shadow call stack
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
; RUN: | FileCheck %s --check-prefix=RV32
|
||||
; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
|
||||
; RUN: | FileCheck %s --check-prefix=RV64
|
||||
; RUN: llc -mtriple=riscv32 -mattr=+experimental-zicfiss < %s \
|
||||
; RUN: -verify-machineinstrs | FileCheck %s --check-prefix=RV32-ZICFISS
|
||||
; RUN: llc -mtriple=riscv64 -mattr=+experimental-zicfiss < %s \
|
||||
; RUN: -verify-machineinstrs | FileCheck %s --check-prefix=RV64-ZICFISS
|
||||
; RUN: llc -mtriple=riscv32 -mattr=+experimental-zicfiss,forced-sw-shadow-stack \
|
||||
; RUN: -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV32
|
||||
; RUN: llc -mtriple=riscv64 -mattr=+experimental-zicfiss,forced-sw-shadow-stack \
|
||||
; RUN: -verify-machineinstrs < %s | FileCheck %s --check-prefix=RV64
|
||||
|
||||
define void @f1() shadowcallstack {
|
||||
; RV32-LABEL: f1:
|
||||
@@ -12,6 +20,14 @@ define void @f1() shadowcallstack {
|
||||
; RV64-LABEL: f1:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: ret
|
||||
;
|
||||
; RV32-ZICFISS-LABEL: f1:
|
||||
; RV32-ZICFISS: # %bb.0:
|
||||
; RV32-ZICFISS-NEXT: ret
|
||||
;
|
||||
; RV64-ZICFISS-LABEL: f1:
|
||||
; RV64-ZICFISS: # %bb.0:
|
||||
; RV64-ZICFISS-NEXT: ret
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -25,6 +41,14 @@ define void @f2() shadowcallstack {
|
||||
; RV64-LABEL: f2:
|
||||
; RV64: # %bb.0:
|
||||
; RV64-NEXT: tail foo
|
||||
;
|
||||
; RV32-ZICFISS-LABEL: f2:
|
||||
; RV32-ZICFISS: # %bb.0:
|
||||
; RV32-ZICFISS-NEXT: tail foo
|
||||
;
|
||||
; RV64-ZICFISS-LABEL: f2:
|
||||
; RV64-ZICFISS: # %bb.0:
|
||||
; RV64-ZICFISS-NEXT: tail foo
|
||||
tail call void @foo()
|
||||
ret void
|
||||
}
|
||||
@@ -65,6 +89,32 @@ define i32 @f3() shadowcallstack {
|
||||
; RV64-NEXT: addi gp, gp, -8
|
||||
; RV64-NEXT: .cfi_restore gp
|
||||
; RV64-NEXT: ret
|
||||
;
|
||||
; RV32-ZICFISS-LABEL: f3:
|
||||
; RV32-ZICFISS: # %bb.0:
|
||||
; RV32-ZICFISS-NEXT: sspush ra
|
||||
; RV32-ZICFISS-NEXT: addi sp, sp, -16
|
||||
; RV32-ZICFISS-NEXT: .cfi_def_cfa_offset 16
|
||||
; RV32-ZICFISS-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
|
||||
; RV32-ZICFISS-NEXT: .cfi_offset ra, -4
|
||||
; RV32-ZICFISS-NEXT: call bar
|
||||
; RV32-ZICFISS-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
|
||||
; RV32-ZICFISS-NEXT: addi sp, sp, 16
|
||||
; RV32-ZICFISS-NEXT: sspopchk ra
|
||||
; RV32-ZICFISS-NEXT: ret
|
||||
;
|
||||
; RV64-ZICFISS-LABEL: f3:
|
||||
; RV64-ZICFISS: # %bb.0:
|
||||
; RV64-ZICFISS-NEXT: sspush ra
|
||||
; RV64-ZICFISS-NEXT: addi sp, sp, -16
|
||||
; RV64-ZICFISS-NEXT: .cfi_def_cfa_offset 16
|
||||
; RV64-ZICFISS-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
|
||||
; RV64-ZICFISS-NEXT: .cfi_offset ra, -8
|
||||
; RV64-ZICFISS-NEXT: call bar
|
||||
; RV64-ZICFISS-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
|
||||
; RV64-ZICFISS-NEXT: addi sp, sp, 16
|
||||
; RV64-ZICFISS-NEXT: sspopchk ra
|
||||
; RV64-ZICFISS-NEXT: ret
|
||||
%res = call i32 @bar()
|
||||
%res1 = add i32 %res, 1
|
||||
ret i32 %res
|
||||
@@ -140,6 +190,68 @@ define i32 @f4() shadowcallstack {
|
||||
; RV64-NEXT: addi gp, gp, -8
|
||||
; RV64-NEXT: .cfi_restore gp
|
||||
; RV64-NEXT: ret
|
||||
;
|
||||
; RV32-ZICFISS-LABEL: f4:
|
||||
; RV32-ZICFISS: # %bb.0:
|
||||
; RV32-ZICFISS-NEXT: sspush ra
|
||||
; RV32-ZICFISS-NEXT: addi sp, sp, -16
|
||||
; RV32-ZICFISS-NEXT: .cfi_def_cfa_offset 16
|
||||
; RV32-ZICFISS-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
|
||||
; RV32-ZICFISS-NEXT: sw s0, 8(sp) # 4-byte Folded Spill
|
||||
; RV32-ZICFISS-NEXT: sw s1, 4(sp) # 4-byte Folded Spill
|
||||
; RV32-ZICFISS-NEXT: sw s2, 0(sp) # 4-byte Folded Spill
|
||||
; RV32-ZICFISS-NEXT: .cfi_offset ra, -4
|
||||
; RV32-ZICFISS-NEXT: .cfi_offset s0, -8
|
||||
; RV32-ZICFISS-NEXT: .cfi_offset s1, -12
|
||||
; RV32-ZICFISS-NEXT: .cfi_offset s2, -16
|
||||
; RV32-ZICFISS-NEXT: call bar
|
||||
; RV32-ZICFISS-NEXT: mv s0, a0
|
||||
; RV32-ZICFISS-NEXT: call bar
|
||||
; RV32-ZICFISS-NEXT: mv s1, a0
|
||||
; RV32-ZICFISS-NEXT: call bar
|
||||
; RV32-ZICFISS-NEXT: mv s2, a0
|
||||
; RV32-ZICFISS-NEXT: call bar
|
||||
; RV32-ZICFISS-NEXT: add s0, s0, s1
|
||||
; RV32-ZICFISS-NEXT: add a0, s2, a0
|
||||
; RV32-ZICFISS-NEXT: add a0, s0, a0
|
||||
; RV32-ZICFISS-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
|
||||
; RV32-ZICFISS-NEXT: lw s0, 8(sp) # 4-byte Folded Reload
|
||||
; RV32-ZICFISS-NEXT: lw s1, 4(sp) # 4-byte Folded Reload
|
||||
; RV32-ZICFISS-NEXT: lw s2, 0(sp) # 4-byte Folded Reload
|
||||
; RV32-ZICFISS-NEXT: addi sp, sp, 16
|
||||
; RV32-ZICFISS-NEXT: sspopchk ra
|
||||
; RV32-ZICFISS-NEXT: ret
|
||||
;
|
||||
; RV64-ZICFISS-LABEL: f4:
|
||||
; RV64-ZICFISS: # %bb.0:
|
||||
; RV64-ZICFISS-NEXT: sspush ra
|
||||
; RV64-ZICFISS-NEXT: addi sp, sp, -32
|
||||
; RV64-ZICFISS-NEXT: .cfi_def_cfa_offset 32
|
||||
; RV64-ZICFISS-NEXT: sd ra, 24(sp) # 8-byte Folded Spill
|
||||
; RV64-ZICFISS-NEXT: sd s0, 16(sp) # 8-byte Folded Spill
|
||||
; RV64-ZICFISS-NEXT: sd s1, 8(sp) # 8-byte Folded Spill
|
||||
; RV64-ZICFISS-NEXT: sd s2, 0(sp) # 8-byte Folded Spill
|
||||
; RV64-ZICFISS-NEXT: .cfi_offset ra, -8
|
||||
; RV64-ZICFISS-NEXT: .cfi_offset s0, -16
|
||||
; RV64-ZICFISS-NEXT: .cfi_offset s1, -24
|
||||
; RV64-ZICFISS-NEXT: .cfi_offset s2, -32
|
||||
; RV64-ZICFISS-NEXT: call bar
|
||||
; RV64-ZICFISS-NEXT: mv s0, a0
|
||||
; RV64-ZICFISS-NEXT: call bar
|
||||
; RV64-ZICFISS-NEXT: mv s1, a0
|
||||
; RV64-ZICFISS-NEXT: call bar
|
||||
; RV64-ZICFISS-NEXT: mv s2, a0
|
||||
; RV64-ZICFISS-NEXT: call bar
|
||||
; RV64-ZICFISS-NEXT: add s0, s0, s1
|
||||
; RV64-ZICFISS-NEXT: add a0, s2, a0
|
||||
; RV64-ZICFISS-NEXT: addw a0, s0, a0
|
||||
; RV64-ZICFISS-NEXT: ld ra, 24(sp) # 8-byte Folded Reload
|
||||
; RV64-ZICFISS-NEXT: ld s0, 16(sp) # 8-byte Folded Reload
|
||||
; RV64-ZICFISS-NEXT: ld s1, 8(sp) # 8-byte Folded Reload
|
||||
; RV64-ZICFISS-NEXT: ld s2, 0(sp) # 8-byte Folded Reload
|
||||
; RV64-ZICFISS-NEXT: addi sp, sp, 32
|
||||
; RV64-ZICFISS-NEXT: sspopchk ra
|
||||
; RV64-ZICFISS-NEXT: ret
|
||||
%res1 = call i32 @bar()
|
||||
%res2 = call i32 @bar()
|
||||
%res3 = call i32 @bar()
|
||||
@@ -176,6 +288,28 @@ define i32 @f5() shadowcallstack nounwind {
|
||||
; RV64-NEXT: ld ra, -8(gp)
|
||||
; RV64-NEXT: addi gp, gp, -8
|
||||
; RV64-NEXT: ret
|
||||
;
|
||||
; RV32-ZICFISS-LABEL: f5:
|
||||
; RV32-ZICFISS: # %bb.0:
|
||||
; RV32-ZICFISS-NEXT: sspush ra
|
||||
; RV32-ZICFISS-NEXT: addi sp, sp, -16
|
||||
; RV32-ZICFISS-NEXT: sw ra, 12(sp) # 4-byte Folded Spill
|
||||
; RV32-ZICFISS-NEXT: call bar
|
||||
; RV32-ZICFISS-NEXT: lw ra, 12(sp) # 4-byte Folded Reload
|
||||
; RV32-ZICFISS-NEXT: addi sp, sp, 16
|
||||
; RV32-ZICFISS-NEXT: sspopchk ra
|
||||
; RV32-ZICFISS-NEXT: ret
|
||||
;
|
||||
; RV64-ZICFISS-LABEL: f5:
|
||||
; RV64-ZICFISS: # %bb.0:
|
||||
; RV64-ZICFISS-NEXT: sspush ra
|
||||
; RV64-ZICFISS-NEXT: addi sp, sp, -16
|
||||
; RV64-ZICFISS-NEXT: sd ra, 8(sp) # 8-byte Folded Spill
|
||||
; RV64-ZICFISS-NEXT: call bar
|
||||
; RV64-ZICFISS-NEXT: ld ra, 8(sp) # 8-byte Folded Reload
|
||||
; RV64-ZICFISS-NEXT: addi sp, sp, 16
|
||||
; RV64-ZICFISS-NEXT: sspopchk ra
|
||||
; RV64-ZICFISS-NEXT: ret
|
||||
%res = call i32 @bar()
|
||||
%res1 = add i32 %res, 1
|
||||
ret i32 %res
|
||||
|
||||
Reference in New Issue
Block a user