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