Skip to content

Commit f6eeb67

Browse files
deepak0414Paul Walmsley
authored andcommitted
riscv: add documentation for landing pad / indirect branch tracking
Add documentation on landing pad aka indirect branch tracking on riscv and the kernel interfaces exposed for user tasks to enable it. Reviewed-by: Zong Li <zong.li@sifive.com> Signed-off-by: Deepak Gupta <debug@rivosinc.com> Link: https://patch.msgid.link/20251112-v5_user_cfi_series-v23-26-b55691eacf4f@rivosinc.com [pjw@kernel.org: cleaned up the documentation] Signed-off-by: Paul Walmsley <pjw@kernel.org>
1 parent 22c1e26 commit f6eeb67

2 files changed

Lines changed: 123 additions & 0 deletions

File tree

Documentation/arch/riscv/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ RISC-V architecture
1414
uabi
1515
vector
1616
cmodx
17+
zicfilp
1718

1819
features
1920

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
:Author: Deepak Gupta <debug@rivosinc.com>
4+
:Date: 12 January 2024
5+
6+
====================================================
7+
Tracking indirect control transfers on RISC-V Linux
8+
====================================================
9+
10+
This document briefly describes the interface provided to userspace by Linux
11+
to enable indirect branch tracking for user mode applications on RISC-V.
12+
13+
1. Feature Overview
14+
--------------------
15+
16+
Memory corruption issues usually result in crashes. However, in the
17+
hands of a creative adversary, these can result in a variety of
18+
security issues.
19+
20+
Some of those security issues can be code re-use attacks, where an
21+
adversary can use corrupt function pointers, chaining them together to
22+
perform jump oriented programming (JOP) or call oriented programming
23+
(COP) and thus compromise control flow integrity (CFI) of the program.
24+
25+
Function pointers live in read-write memory and thus are susceptible
26+
to corruption. This can allow an adversary to control the program
27+
counter (PC) value. On RISC-V, the zicfilp extension enforces a
28+
restriction on such indirect control transfers:
29+
30+
- Indirect control transfers must land on a landing pad instruction ``lpad``.
31+
There are two exceptions to this rule:
32+
33+
- rs1 = x1 or rs1 = x5, i.e. a return from a function and returns are
34+
protected using shadow stack (see zicfiss.rst)
35+
36+
- rs1 = x7. On RISC-V, the compiler usually does the following to reach a
37+
function which is beyond the offset of possible J-type instruction::
38+
39+
auipc x7, <imm>
40+
jalr (x7)
41+
42+
This form of indirect control transfer is immutable and doesn't
43+
rely on memory. Thus rs1=x7 is exempted from tracking and
44+
these are considered software guarded jumps.
45+
46+
The ``lpad`` instruction is a pseudo-op of ``auipc rd, <imm_20bit>``
47+
with ``rd=x0``. This is a HINT op. The ``lpad`` instruction must be
48+
aligned on a 4 byte boundary. It compares the 20 bit immediate with
49+
x7. If ``imm_20bit`` == 0, the CPU doesn't perform any comparison with
50+
``x7``. If ``imm_20bit`` != 0, then ``imm_20bit`` must match ``x7``
51+
else CPU will raise ``software check exception`` (``cause=18``) with
52+
``*tval = 2``.
53+
54+
The compiler can generate a hash over function signatures and set them
55+
up (truncated to 20 bits) in x7 at callsites. Function prologues can
56+
have ``lpad`` instructions encoded with the same function hash. This
57+
further reduces the number of valid program counter addresses a call
58+
site can reach.
59+
60+
2. ELF and psABI
61+
-----------------
62+
63+
The toolchain sets up :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_FCFI` for
64+
property :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_AND` in the notes
65+
section of the object file.
66+
67+
3. Linux enabling
68+
------------------
69+
70+
User space programs can have multiple shared objects loaded in their
71+
address spaces. It's a difficult task to make sure all the
72+
dependencies have been compiled with indirect branch support. Thus
73+
it's left to the dynamic loader to enable indirect branch tracking for
74+
the program.
75+
76+
4. prctl() enabling
77+
--------------------
78+
79+
:c:macro:`PR_SET_INDIR_BR_LP_STATUS` / :c:macro:`PR_GET_INDIR_BR_LP_STATUS` /
80+
:c:macro:`PR_LOCK_INDIR_BR_LP_STATUS` are three prctls added to manage indirect
81+
branch tracking. These prctls are architecture-agnostic and return -EINVAL if
82+
the underlying functionality is not supported.
83+
84+
* prctl(PR_SET_INDIR_BR_LP_STATUS, unsigned long arg)
85+
86+
If arg1 is :c:macro:`PR_INDIR_BR_LP_ENABLE` and if CPU supports
87+
``zicfilp`` then the kernel will enable indirect branch tracking for the
88+
task. The dynamic loader can issue this :c:macro:`prctl` once it has
89+
determined that all the objects loaded in the address space support
90+
indirect branch tracking. Additionally, if there is a `dlopen` to an
91+
object which wasn't compiled with ``zicfilp``, the dynamic loader can
92+
issue this prctl with arg1 set to 0 (i.e. :c:macro:`PR_INDIR_BR_LP_ENABLE`
93+
cleared).
94+
95+
* prctl(PR_GET_INDIR_BR_LP_STATUS, unsigned long * arg)
96+
97+
Returns the current status of indirect branch tracking. If enabled
98+
it'll return :c:macro:`PR_INDIR_BR_LP_ENABLE`
99+
100+
* prctl(PR_LOCK_INDIR_BR_LP_STATUS, unsigned long arg)
101+
102+
Locks the current status of indirect branch tracking on the task. User
103+
space may want to run with a strict security posture and wouldn't want
104+
loading of objects without ``zicfilp`` support in them, to disallow
105+
disabling of indirect branch tracking. In this case, user space can
106+
use this prctl to lock the current settings.
107+
108+
5. violations related to indirect branch tracking
109+
--------------------------------------------------
110+
111+
Pertaining to indirect branch tracking, the CPU raises a software
112+
check exception in the following conditions:
113+
114+
- missing ``lpad`` after indirect call / jmp
115+
- ``lpad`` not on 4 byte boundary
116+
- ``imm_20bit`` embedded in ``lpad`` instruction doesn't match with ``x7``
117+
118+
In all 3 cases, ``*tval = 2`` is captured and software check exception is
119+
raised (``cause=18``).
120+
121+
The kernel will treat this as :c:macro:`SIGSEGV` with code =
122+
:c:macro:`SEGV_CPERR` and follow the normal course of signal delivery.

0 commit comments

Comments
 (0)