Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3bd6c0a
mm: VM_SHADOW_STACK definition for riscv
deepak0414 May 23, 2025
549372c
dt-bindings: riscv: zicfilp and zicfiss in dt-bindings (extensions.yaml)
deepak0414 May 23, 2025
f06a0a8
riscv: zicfiss / zicfilp enumeration
deepak0414 May 23, 2025
ee9ab40
riscv: zicfiss / zicfilp extension csr and bit definitions
deepak0414 May 23, 2025
3393be8
riscv: usercfi state for task and save/restore of CSR_SSP on trap ent…
deepak0414 May 23, 2025
94b348e
riscv/mm : ensure PROT_WRITE leads to VM_READ | VM_WRITE
deepak0414 May 23, 2025
b31cb50
riscv mm: manufacture shadow stack pte
deepak0414 May 23, 2025
51414fb
riscv mmu: teach pte_mkwrite to manufacture shadow stack PTEs
deepak0414 May 23, 2025
29659f5
riscv mmu: write protect and shadow stack
deepak0414 May 23, 2025
4d50b6c
riscv/mm: Implement map_shadow_stack() syscall
deepak0414 May 23, 2025
3dd12dd
riscv/shstk: If needed allocate a new shadow stack on clone
deepak0414 May 23, 2025
6b853c8
riscv: Implements arch agnostic shadow stack prctls
deepak0414 May 23, 2025
4d2282c
prctl: arch-agnostic prctl for indirect branch tracking
deepak0414 May 23, 2025
d62074d
riscv: Implements arch agnostic indirect branch tracking prctls
deepak0414 May 23, 2025
94e2bb7
riscv/traps: Introduce software check exception
deepak0414 May 23, 2025
5b9f0f7
riscv: signal: abstract header saving for setup_sigcontext
AndybnACT May 23, 2025
ca4fea1
riscv/signal: save and restore of shadow stack for signal
deepak0414 May 23, 2025
edb9f9f
riscv/kernel: update __show_regs to print shadow stack register
deepak0414 May 23, 2025
c7b4066
riscv/ptrace: riscv cfi status and state via ptrace and in core files
deepak0414 May 23, 2025
03a31f1
riscv/hwprobe: zicfilp / zicfiss enumeration in hwprobe
deepak0414 May 23, 2025
33ddd3b
riscv: kernel command line option to opt out of user cfi
deepak0414 May 23, 2025
b280809
riscv: enable kernel access to shadow stack memory via FWFT sbi call
deepak0414 May 23, 2025
99e1385
arch/riscv: compile vdso with landing pad
cwshu May 23, 2025
e208d15
riscv: create a config for shadow stack and landing pad instr support
deepak0414 May 23, 2025
d205afb
riscv: Documentation for landing pad / indirect branch tracking
deepak0414 May 23, 2025
24e554b
riscv: Documentation for shadow stack on riscv
deepak0414 May 23, 2025
dba46c4
kselftest/riscv: kselftest for user mode cfi
deepak0414 May 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Documentation/admin-guide/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6234,6 +6234,14 @@
replacement properties are not found. See the Kconfig
entry for RISCV_ISA_FALLBACK.

riscv_nousercfi=
all Disable user cfi ABI to userspace even if cpu extension
are available.
bcfi Disable user backward cfi ABI to userspace even if
shadow stack extension is available.
fcfi Disable user forward cfi ABI to userspace even if landing
pad extension is available.

ro [KNL] Mount root device read-only on boot

rodata= [KNL,EARLY]
Expand Down
2 changes: 2 additions & 0 deletions Documentation/arch/riscv/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ RISC-V architecture
uabi
vector
cmodx
zicfilp
zicfiss

features

Expand Down
115 changes: 115 additions & 0 deletions Documentation/arch/riscv/zicfilp.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
.. SPDX-License-Identifier: GPL-2.0

:Author: Deepak Gupta <debug@rivosinc.com>
:Date: 12 January 2024

====================================================
Tracking indirect control transfers on RISC-V Linux
====================================================

This document briefly describes the interface provided to userspace by Linux
to enable indirect branch tracking for user mode applications on RISC-V

1. Feature Overview
--------------------

Memory corruption issues usually result into crashes, however when in hands of
an adversary and if used creatively can result into a variety security issues.

One of those security issues can be code re-use attacks on program where adversary
can use corrupt function pointers and chain them together to perform jump oriented
programming (JOP) or call oriented programming (COP) and thus compromising control
flow integrity (CFI) of the program.

Function pointers live in read-write memory and thus are susceptible to corruption
and allows an adversary to reach any program counter (PC) in address space. On
RISC-V zicfilp extension enforces a restriction on such indirect control
transfers:

- indirect control transfers must land on a landing pad instruction ``lpad``.
There are two exception to this rule:

- rs1 = x1 or rs1 = x5, i.e. a return from a function and returns are
protected using shadow stack (see zicfiss.rst)

- rs1 = x7. On RISC-V compiler usually does below to reach function
which is beyond the offset possible J-type instruction::

auipc x7, <imm>
jalr (x7)

Such form of indirect control transfer are still immutable and don't rely
on memory and thus rs1=x7 is exempted from tracking and considered software
guarded jumps.

``lpad`` instruction is pseudo of ``auipc rd, <imm_20bit>`` with ``rd=x0`` and
is a HINT nop. ``lpad`` instruction must be aligned on 4 byte boundary and
compares 20 bit immediate with x7. If ``imm_20bit`` == 0, CPU doesn't perform
any comparision with ``x7``. If ``imm_20bit`` != 0, then ``imm_20bit`` must
match ``x7`` else CPU will raise ``software check exception`` (``cause=18``)
with ``*tval = 2``.

Compiler can generate a hash over function signatures and setup them (truncated
to 20bit) in x7 at callsites and function prologues can have ``lpad`` with same
function hash. This further reduces number of program counters a call site can
reach.

2. ELF and psABI
-----------------

Toolchain sets up :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_FCFI` for property
:c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_AND` in notes section of the object file.

3. Linux enabling
------------------

User space programs can have multiple shared objects loaded in its address space
and it's a difficult task to make sure all the dependencies have been compiled
with support of indirect branch. Thus it's left to dynamic loader to enable
indirect branch tracking for the program.

4. prctl() enabling
--------------------

:c:macro:`PR_SET_INDIR_BR_LP_STATUS` / :c:macro:`PR_GET_INDIR_BR_LP_STATUS` /
:c:macro:`PR_LOCK_INDIR_BR_LP_STATUS` are three prctls added to manage indirect
branch tracking. prctls are arch agnostic and returns -EINVAL on other arches.

* prctl(PR_SET_INDIR_BR_LP_STATUS, unsigned long arg)

If arg1 is :c:macro:`PR_INDIR_BR_LP_ENABLE` and if CPU supports ``zicfilp``
then kernel will enable indirect branch tracking for the task. Dynamic loader
can issue this :c:macro:`prctl` once it has determined that all the objects
loaded in address space support indirect branch tracking. Additionally if there
is a `dlopen` to an object which wasn't compiled with ``zicfilp``, dynamic
loader can issue this prctl with arg1 set to 0 (i.e.
:c:macro:`PR_INDIR_BR_LP_ENABLE` being clear)

* prctl(PR_GET_INDIR_BR_LP_STATUS, unsigned long arg)

Returns current status of indirect branch tracking. If enabled it'll return
:c:macro:`PR_INDIR_BR_LP_ENABLE`

* prctl(PR_LOCK_INDIR_BR_LP_STATUS, unsigned long arg)

Locks current status of indirect branch tracking on the task. User space may
want to run with strict security posture and wouldn't want loading of objects
without ``zicfilp`` support in it and thus would want to disallow disabling of
indirect branch tracking. In that case user space can use this prctl to lock
current settings.

5. violations related to indirect branch tracking
--------------------------------------------------

Pertaining to indirect branch tracking, CPU raises software check exception in
following conditions:

- missing ``lpad`` after indirect call / jmp
- ``lpad`` not on 4 byte boundary
- ``imm_20bit`` embedded in ``lpad`` instruction doesn't match with ``x7``

In all 3 cases, ``*tval = 2`` is captured and software check exception is
raised (``cause=18``)

Linux kernel will treat this as :c:macro:`SIGSEV`` with code =
:c:macro:`SEGV_CPERR` and follow normal course of signal delivery.
179 changes: 179 additions & 0 deletions Documentation/arch/riscv/zicfiss.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
.. SPDX-License-Identifier: GPL-2.0

:Author: Deepak Gupta <debug@rivosinc.com>
:Date: 12 January 2024

=========================================================
Shadow stack to protect function returns on RISC-V Linux
=========================================================

This document briefly describes the interface provided to userspace by Linux
to enable shadow stack for user mode applications on RISC-V

1. Feature Overview
--------------------

Memory corruption issues usually result into crashes, however when in hands of
an adversary and if used creatively can result into a variety security issues.

One of those security issues can be code re-use attacks on program where
adversary can use corrupt return addresses present on stack and chain them
together to perform return oriented programming (ROP) and thus compromising
control flow integrity (CFI) of the program.

Return addresses live on stack and thus in read-write memory and thus are
susceptible to corruption and which allows an adversary to reach any program
counter (PC) in address space. On RISC-V ``zicfiss`` extension provides an
alternate stack termed as shadow stack on which return addresses can be safely
placed in prolog of the function and retrieved in epilog. ``zicfiss`` extension
makes following changes:

- PTE encodings for shadow stack virtual memory
An earlier reserved encoding in first stage translation i.e.
PTE.R=0, PTE.W=1, PTE.X=0 becomes PTE encoding for shadow stack pages.

- ``sspush x1/x5`` instruction pushes (stores) ``x1/x5`` to shadow stack.

- ``sspopchk x1/x5`` instruction pops (loads) from shadow stack and compares
with ``x1/x5`` and if un-equal, CPU raises ``software check exception`` with
``*tval = 3``

Compiler toolchain makes sure that function prologue have ``sspush x1/x5`` to
save return address on shadow stack in addition to regular stack. Similarly
function epilogs have ``ld x5, offset(x2)`` followed by ``sspopchk x5`` to
ensure that popped value from regular stack matches with popped value from
shadow stack.

2. Shadow stack protections and linux memory manager
-----------------------------------------------------

As mentioned earlier, shadow stacks get new page table encodings and thus have
some special properties assigned to them and instructions that operate on them
as below:

- Regular stores to shadow stack memory raises access store faults. This way
shadow stack memory is protected from stray inadvertent writes.

- Regular loads to shadow stack memory are allowed. This allows stack trace
utilities or backtrace functions to read true callstack (not tampered).

- Only shadow stack instructions can generate shadow stack load or shadow stack
store.

- Shadow stack load / shadow stack store on read-only memory raises AMO/store
page fault. Thus both ``sspush x1/x5`` and ``sspopchk x1/x5`` will raise AMO/
store page fault. This simplies COW handling in kernel during fork, kernel
can convert shadow stack pages into read-only memory (as it does for regular
read-write memory) and as soon as subsequent ``sspush`` or ``sspopchk`` in
userspace is encountered, then kernel can perform COW.

- Shadow stack load / shadow stack store on read-write, read-write-execute
memory raises an access fault. This is a fatal condition because shadow stack
should never be operating on read-write, read-write-execute memory.

3. ELF and psABI
-----------------

Toolchain sets up :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_BCFI` for property
:c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_AND` in notes section of the object file.

4. Linux enabling
------------------

User space programs can have multiple shared objects loaded in its address space
and it's a difficult task to make sure all the dependencies have been compiled
with support of shadow stack. Thus it's left to dynamic loader to enable
shadow stack for the program.

5. prctl() enabling
--------------------

:c:macro:`PR_SET_SHADOW_STACK_STATUS` / :c:macro:`PR_GET_SHADOW_STACK_STATUS` /
:c:macro:`PR_LOCK_SHADOW_STACK_STATUS` are three prctls added to manage shadow
stack enabling for tasks. prctls are arch agnostic and returns -EINVAL on other
arches.

* prctl(PR_SET_SHADOW_STACK_STATUS, unsigned long arg)

If arg1 :c:macro:`PR_SHADOW_STACK_ENABLE` and if CPU supports ``zicfiss`` then
kernel will enable shadow stack for the task. Dynamic loader can issue this
:c:macro:`prctl` once it has determined that all the objects loaded in address
space have support for shadow stack. Additionally if there is a
:c:macro:`dlopen` to an object which wasn't compiled with ``zicfiss``, dynamic
loader can issue this prctl with arg1 set to 0 (i.e.
:c:macro:`PR_SHADOW_STACK_ENABLE` being clear)

* prctl(PR_GET_SHADOW_STACK_STATUS, unsigned long *arg)

Returns current status of indirect branch tracking. If enabled it'll return
:c:macro:`PR_SHADOW_STACK_ENABLE`.

* prctl(PR_LOCK_SHADOW_STACK_STATUS, unsigned long arg)

Locks current status of shadow stack enabling on the task. User space may want
to run with strict security posture and wouldn't want loading of objects
without ``zicfiss`` support in it and thus would want to disallow disabling of
shadow stack on current task. In that case user space can use this prctl to
lock current settings.

5. violations related to returns with shadow stack enabled
-----------------------------------------------------------

Pertaining to shadow stack, CPU raises software check exception in following
condition:

- On execution of ``sspopchk x1/x5``, ``x1/x5`` didn't match top of shadow
stack. If mismatch happens then cpu does ``*tval = 3`` and raise software
check exception.

Linux kernel will treat this as :c:macro:`SIGSEV`` with code =
:c:macro:`SEGV_CPERR` and follow normal course of signal delivery.

6. Shadow stack tokens
-----------------------
Regular stores on shadow stacks are not allowed and thus can't be tampered
with via arbitrary stray writes due to bugs. However method of pivoting /
switching to shadow stack is simply writing to csr ``CSR_SSP`` and that will
change active shadow stack for the program. Instances of writes to ``CSR_SSP``
in the address space of the program should be mostly limited to context
switching, stack unwind, longjmp or similar mechanisms (like context switching
of green threads) in languages like go, rust. This can be problematic because
an attacker can use memory corruption bugs and eventually use such context
switching routines to pivot to any shadow stack. Shadow stack tokens can help
mitigate this problem by making sure that:

- When software is switching away from a shadow stack, shadow stack pointer
should be saved on shadow stack itself and call it ``shadow stack token``

- When software is switching to a shadow stack, it should read the
``shadow stack token`` from shadow stack pointer and verify that
``shadow stack token`` itself is pointer to shadow stack itself.

- Once the token verification is done, software can perform the write to
``CSR_SSP`` to switch shadow stack.

Here software can be user mode task runtime itself which is managing various
contexts as part of single thread. Software can be kernel as well when kernel
has to deliver a signal to user task and must save shadow stack pointer. Kernel
can perform similar procedure by saving a token on user shadow stack itself.
This way whenever :c:macro:`sigreturn` happens, kernel can read the token and
verify the token and then switch to shadow stack. Using this mechanism, kernel
helps user task so that any corruption issue in user task is not exploited by
adversary by arbitrarily using :c:macro:`sigreturn`. Adversary will have to
make sure that there is a ``shadow stack token`` in addition to invoking
:c:macro:`sigreturn`

7. Signal shadow stack
-----------------------
Following structure has been added to sigcontext for RISC-V::

struct __sc_riscv_cfi_state {
unsigned long ss_ptr;
};

As part of signal delivery, shadow stack token is saved on current shadow stack
itself and updated pointer is saved away in :c:macro:`ss_ptr` field in
:c:macro:`__sc_riscv_cfi_state` under :c:macro:`sigcontext`. Existing shadow
stack allocation is used for signal delivery. During :c:macro:`sigreturn`,
kernel will obtain :c:macro:`ss_ptr` from :c:macro:`sigcontext` and verify the
saved token on shadow stack itself and switch shadow stack.
14 changes: 14 additions & 0 deletions Documentation/devicetree/bindings/riscv/extensions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,20 @@ properties:
The standard Zicboz extension for cache-block zeroing as ratified
in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.

- const: zicfilp
description: |
The standard Zicfilp extension for enforcing forward edge
control-flow integrity as ratified in commit 3f8e450 ("merge
pull request #227 from ved-rivos/0709") of riscv-cfi
github repo.

- const: zicfiss
description: |
The standard Zicfiss extension for enforcing backward edge
control-flow integrity as ratified in commit 3f8e450 ("merge
pull request #227 from ved-rivos/0709") of riscv-cfi
github repo.

- const: zicntr
description:
The standard Zicntr extension for base counters and timers, as
Expand Down
21 changes: 21 additions & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,27 @@ config ARCH_HAS_BROKEN_DWARF5
# https://github.com/llvm/llvm-project/commit/7ffabb61a5569444b5ac9322e22e5471cc5e4a77
depends on LD_IS_LLD && LLD_VERSION < 180000

config RISCV_USER_CFI
def_bool n
bool "riscv userspace control flow integrity"
depends on 64BIT && $(cc-option,-mabi=lp64 -march=rv64ima_zicfiss)
depends on RISCV_ALTERNATIVE
select RISCV_SBI
select ARCH_HAS_USER_SHADOW_STACK
select ARCH_USES_HIGH_VMA_FLAGS
select DYNAMIC_SIGFRAME
help
Provides CPU assisted control flow integrity to userspace tasks.
Control flow integrity is provided by implementing shadow stack for
backward edge and indirect branch tracking for forward edge in program.
Shadow stack protection is a hardware feature that detects function
return address corruption. This helps mitigate ROP attacks.
Indirect branch tracking enforces that all indirect branches must land
on a landing pad instruction else CPU will fault. This mitigates against
JOP / COP attacks. Applications must be enabled to use it, and old user-
space does not get protection "for free".
default y

config ARCH_MMAP_RND_BITS_MIN
default 18 if 64BIT
default 8
Expand Down
5 changes: 4 additions & 1 deletion arch/riscv/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,12 @@ riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZACAS) := $(riscv-march-y)_zacas
# Check if the toolchain supports Zabha
riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZABHA) := $(riscv-march-y)_zabha

KBUILD_BASE_ISA = -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/')
export KBUILD_BASE_ISA

# Remove F,D,V from isa string for all. Keep extensions between "fd" and "v" by
# matching non-v and non-multi-letter extensions out with the filter ([^v_]*)
KBUILD_CFLAGS += -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/')
KBUILD_CFLAGS += $(KBUILD_BASE_ISA)

KBUILD_AFLAGS += -march=$(riscv-march-y)

Expand Down
1 change: 1 addition & 0 deletions arch/riscv/include/asm/asm-prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ DECLARE_DO_ERROR_INFO(do_trap_ecall_u);
DECLARE_DO_ERROR_INFO(do_trap_ecall_s);
DECLARE_DO_ERROR_INFO(do_trap_ecall_m);
DECLARE_DO_ERROR_INFO(do_trap_break);
DECLARE_DO_ERROR_INFO(do_trap_software_check);

asmlinkage void handle_bad_stack(struct pt_regs *regs);
asmlinkage void do_page_fault(struct pt_regs *regs);
Expand Down
Loading
Loading