Control flow integrity extensions (`zicfilp` and `zicfiss`) [1] for risc-v
are ratified. `zicfilp` protects forward control flow i.e. indirect function
call sites while `zicfiss` protects return control flow i.e. return from
functions which have spilled return addresses on stack.
This patchset is based on recent master with patch series from LIU (alibaba)
[4] which enables `zimop` and `zcmop` support in qemu. `zicfiss` has a dependency
on `zimop` and `zcmop`.
zicfilp
-------
On a very high level `zicfilp` extends risc-v architecture by introducing a
landing pad instruction `lpad` which must be target for every indirect branch
except when
- rs1 == x1 || x5 (a return from a function)
- rs1 == x7 (sw guarded branch)
If `lpad` is not present, cpu will raise software check exception (cause=18)
, introduced in privileged spec version 1.13. To track `expected landing pad`
state on every indirect branch, a new state is introduced in cpu `elp` short
for expected landing pad status and can be spilled into *status register if
a trap occurs between indirect branch and target instruction. `lpad` instr
is carved out of HINT space `auipc rd` with `rd==x0` and can have 20 bit
immediate (label_20bit) encoded in it. After an indirect branch lands on
`lpad` instruction, cpu performs following checks:
-- If label_20bit == 0, no further check are performed.
-- If label_20bit != 0, cpu evaluates (label_20bit == x7_upper_20bit)
If above expression is evaluated to be false, cpu raises sw check
exception.
Software can implement more finer-grained control flow using label mechanism
defined in `zicfilp` by ensuring x7 has appropriate label setup prior to
indirect call and thus further restraining call sites target locations.
zicfiss
-------
On a very high level `zicfiss` extends risc-v architecture by providing a
separate stack (called shadow stack) to store return addresses. Shadow stack
can be protected using a reserved PTE encoding (PTE.W=1, PTE.R=0, PTE.X=0).
Only shadow stack instructions can perform stores on shadow stack memory while
regular stores on such memory result in access faults. Shadow stack memory can
be read by regular load instructions. A new unprivileged csr `CSR_SSP` is
introduced which holds pointer to shadow stack for the execution environment.
To store return addresses on shadow stack, `sspush x1/x5` instruction is added.
`sspopchk x1/x5` instruction pops from top of shadow stack and compares it with
`x1/x5` and if there is a mismatch, cpu raises a software check exception.
riscv-gnu-toolchain changes [2] are required to test these changes on qemu-user
or on full blown linux guest using qemu-system. In order to test under qemu-system,
one will require relevant linux changes as well. I've tested linux (6.8) based on
current patches [3] and booted a rootfs compiled with cfi enabled toolchain to shell.
There're still some work going on to make it work with VDSO and multi-label scheme.
But none of those work is gate for these patches.
I would like to express my sincere thanks to all the community members in helping out.
Special shout out and acknowledgement to kito cheng, andy chiu, jim shu and jesse huang
from SiFive.
[1] - https://github.com/riscv/riscv-cfi
[2] - https://github.com/sifive/riscv-gnu-toolchain/tree/cfi-dev
[3] - https://lore.kernel.org/all/20240403234054.2020347-1-debug@rivosinc.com/
[4] - https://lore.kernel.org/all/20240709113652.1239-1-zhiwei_liu@linux.alibaba.com/#t
Deepak Gupta (24):
target/riscv: Add zicfilp extension
target/riscv: Introduce elp state and enabling controls for zicfilp
target/riscv: save and restore elp state on priv transitions
target/riscv: additional code information for sw check
target/riscv: tracking indirect branches (fcfi) for zicfilp
target/riscv: zicfilp `lpad` impl and branch tracking
disas/riscv: enabled `lpad` disassembly
linux-user/syscall: introduce prctl for indirect branch tracking
linux-user/riscv: implement indirect branch tracking prctls
target/riscv: Add zicfiss extension
target/riscv: introduce ssp and enabling controls for zicfiss
target/riscv: tb flag for shadow stack instructions
target/riscv: implement zicfiss instructions
target/riscv: compressed encodings for sspush and sspopchk
target/riscv: mmu changes for zicfiss shadow stack protection
target/riscv: shadow stack mmu index for shadow stack instructions
linux-user/syscall: introduce prctl for shadow stack enable/disable
linux-user/riscv: setup/teardown zicfiss shadow stack for qemu-user
disas/riscv: enable disassembly for zicfiss instructions
disas/riscv: enable disassembly for compressed sspush/sspopchk
target/riscv: add trace-hooks for each case of sw-check exception
linux-user: permit RISC-V CFI dynamic entry in VDSO
linux-user: Add RISC-V zicfilp support in VDSO
linux-user/riscv: Adding zicfiss/lp extension in hwprobe syscall
disas/riscv.c | 71 +++++++++-
disas/riscv.h | 4 +
linux-user/gen-vdso-elfn.c.inc | 7 +
linux-user/riscv/cpu_loop.c | 50 +++++++
linux-user/riscv/target_cpu.h | 7 +
linux-user/riscv/target_prctl.h | 70 ++++++++++
linux-user/riscv/vdso-64.so | Bin 3944 -> 4128 bytes
linux-user/riscv/vdso.S | 50 +++++++
linux-user/syscall.c | 40 ++++++
target/riscv/cpu.c | 21 +++
target/riscv/cpu.h | 28 ++++
target/riscv/cpu_bits.h | 23 ++++
target/riscv/cpu_cfg.h | 2 +
target/riscv/cpu_helper.c | 166 +++++++++++++++++++++++-
target/riscv/cpu_user.h | 1 +
target/riscv/csr.c | 106 +++++++++++++++
target/riscv/helper.h | 6 +
target/riscv/insn16.decode | 4 +
target/riscv/insn32.decode | 23 +++-
target/riscv/insn_trans/trans_rva.c.inc | 55 ++++++++
target/riscv/insn_trans/trans_rvi.c.inc | 52 ++++++++
target/riscv/internals.h | 4 +
target/riscv/op_helper.c | 63 +++++++++
target/riscv/pmp.c | 5 +
target/riscv/pmp.h | 3 +-
target/riscv/tcg/tcg-cpu.c | 20 +++
target/riscv/trace-events | 6 +
target/riscv/translate.c | 80 ++++++++++++
28 files changed, 959 insertions(+), 8 deletions(-)
--
2.44.0