xref: /linux/Documentation/arch/riscv/zicfilp.rst (revision cee73b1e840c154f64ace682cb477c1ae2e29cc4)
1.. SPDX-License-Identifier: GPL-2.0
2
3:Author: Deepak Gupta <debug@rivosinc.com>
4:Date:   12 January 2024
5
6====================================================
7Tracking indirect control transfers on RISC-V Linux
8====================================================
9
10This document briefly describes the interface provided to userspace by Linux
11to enable indirect branch tracking for user mode applications on RISC-V.
12
131. Feature Overview
14--------------------
15
16Memory corruption issues usually result in crashes.  However, in the
17hands of a creative adversary, these can result in a variety of
18security issues.
19
20Some of those security issues can be code re-use attacks, where an
21adversary can use corrupt function pointers, chaining them together to
22perform jump oriented programming (JOP) or call oriented programming
23(COP) and thus compromise control flow integrity (CFI) of the program.
24
25Function pointers live in read-write memory and thus are susceptible
26to corruption.  This can allow an adversary to control the program
27counter (PC) value.  On RISC-V, the zicfilp extension enforces a
28restriction 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
46The ``lpad`` instruction is a pseudo-op of ``auipc rd, <imm_20bit>``
47with ``rd=x0``.  This is a HINT op.  The ``lpad`` instruction must be
48aligned on a 4 byte boundary.  It compares the 20 bit immediate with
49x7. If ``imm_20bit`` == 0, the CPU doesn't perform any comparison with
50``x7``. If ``imm_20bit`` != 0, then ``imm_20bit`` must match ``x7``
51else CPU will raise ``software check exception`` (``cause=18``) with
52``*tval = 2``.
53
54The compiler can generate a hash over function signatures and set them
55up (truncated to 20 bits) in x7 at callsites.  Function prologues can
56have ``lpad`` instructions encoded with the same function hash. This
57further reduces the number of valid program counter addresses a call
58site can reach.
59
602. ELF and psABI
61-----------------
62
63The toolchain sets up :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_FCFI` for
64property :c:macro:`GNU_PROPERTY_RISCV_FEATURE_1_AND` in the notes
65section of the object file.
66
673. Linux enabling
68------------------
69
70User space programs can have multiple shared objects loaded in their
71address spaces.  It's a difficult task to make sure all the
72dependencies have been compiled with indirect branch support. Thus
73it's left to the dynamic loader to enable indirect branch tracking for
74the program.
75
764. 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
81branch tracking.  These prctls are architecture-agnostic and return -EINVAL if
82the underlying functionality is not supported.
83
84* prctl(PR_SET_INDIR_BR_LP_STATUS, unsigned long arg)
85
86If 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
88task.  The dynamic loader can issue this :c:macro:`prctl` once it has
89determined that all the objects loaded in the address space support
90indirect branch tracking.  Additionally, if there is a `dlopen` to an
91object which wasn't compiled with ``zicfilp``, the dynamic loader can
92issue this prctl with arg1 set to 0 (i.e. :c:macro:`PR_INDIR_BR_LP_ENABLE`
93cleared).
94
95* prctl(PR_GET_INDIR_BR_LP_STATUS, unsigned long * arg)
96
97Returns the current status of indirect branch tracking. If enabled
98it'll return :c:macro:`PR_INDIR_BR_LP_ENABLE`
99
100* prctl(PR_LOCK_INDIR_BR_LP_STATUS, unsigned long arg)
101
102Locks the current status of indirect branch tracking on the task. User
103space may want to run with a strict security posture and wouldn't want
104loading of objects without ``zicfilp`` support in them, to disallow
105disabling of indirect branch tracking. In this case, user space can
106use this prctl to lock the current settings.
107
1085. violations related to indirect branch tracking
109--------------------------------------------------
110
111Pertaining to indirect branch tracking, the CPU raises a software
112check 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
118In all 3 cases, ``*tval = 2`` is captured and software check exception is
119raised (``cause=18``).
120
121The kernel will treat this as :c:macro:`SIGSEGV` with code =
122:c:macro:`SEGV_CPERR` and follow the normal course of signal delivery.
123