Control-flow Integrity (CFI)

Control-flow Integrity (CFI) capabilities help defend against Return-Oriented Programming (ROP) and Call/Jump-Oriented Programming (COP/JOP) style control-flow subversion attacks. The Zicfiss and Zicfilp extensions provide backward-edge and forward-edge control flow integrity respectively. Please see the Control-flow Integrity chapter of the Unprivileged ISA specification for further details on these CFI capabilities and the associated Unprivileged ISA.

Landing Pad (Zicfilp)

This section specifies the Privileged ISA for the Zicfilp extension.

Landing-Pad-Enabled (LPE) State

The term xLPE is used to determine if forward-edge CFI using landing pads provided by the Zicfilp extension is enabled at a privilege mode.

When S-mode is implemented, it is determined as follows:

Table 1. xLPE determination when S-mode is implemented
Privilege Mode xLPE

M

mseccfg.MLPE

S or HS

menvcfg.LPE

VS

henvcfg.LPE

U or VU

senvcfg.LPE

When S-mode is not implemented, it is determined as follows:

Table 2. xLPE determination when S-mode is not implemented
Privilege Mode xLPE

M

mseccfg.MLPE

U

menvcfg.LPE

The Zicfilp must be explicitly enabled for use at each privilege mode.

Programs compiled with the LPAD instruction continue to function correctly, but without forward-edge CFI protection, when the Zicfilp extension is not implemented or is not enabled.

Preserving Expected Landing Pad State on Traps

A trap may need to be delivered to the same or to a higher privilege mode upon completion of JALR/C.JALR/C.JR, but before the instruction at the target of indirect call/jump was decoded, due to:

The software-check exception caused by Zicfilp has higher priority than an illegal-instruction exception but lower priority than instruction access-fault.

The software-check exception due to the instruction not being an LPAD instruction when ELP is LP_EXPECTED or a software-check exception caused by the LPAD instruction itself leads to a trap being delivered to the same or to a higher privilege mode.

In such cases, the ELP prior to the trap, the previous ELP, must be preserved by the trap delivery such that it can be restored on a return from the trap. To store the previous ELP state on trap delivery to M-mode, an MPELP bit is provided in the mstatus CSR. To store the previous ELP state on trap delivery to S/HS-mode, an SPELP bit is provided in the mstatus CSR. The SPELP bit in mstatus can be accessed through the sstatus CSR. To store the previous ELP state on traps to VS-mode, a SPELP bit is defined in the vsstatus (VS-modes version of sstatus). To store the previous ELP state on transition to Debug Mode, a pelp bit is defined in the dcsr register.

When a trap is taken into privilege mode x, the xPELP is set to ELP and ELP is set to NO_LP_EXPECTED.

An MRET or SRET instruction is used to return from a trap in M-mode or S-mode, respectively. When executing an xRET instruction, if the new privilege mode is y, then ELP is set to the value of xPELP if yLPE (see Landing-Pad-Enabled (LPE) State) is 1; otherwise, it is set to NO_LP_EXPECTED; xPELP is set to NO_LP_EXPECTED.

Upon entry into Debug Mode, the pelp bit in dcsr is updated with the ELP at the privilege level the hart was previously in, and the ELP is set to NO_LP_EXPECTED. When a hart resumes from Debug Mode, if the new privilege mode is y, then ELP is set to the value of pelp if yLPE (see Landing-Pad-Enabled (LPE) State) is 1; otherwise, it is set to NO_LP_EXPECTED.

See also "Smrnmi" Extension for Resumable Non-Maskable Interrupts for semantics added to the RNMI trap and the MNRET instruction when this extension is implemented.

The trap handler in privilege mode x must save the xPELP bit and the x7 register before performing an indirect call/jump if xLPE=1. If the privilege mode x can respond to interrupts and xLPE=1, then the trap handler should also save these values before enabling interrupts.

The trap handler in privilege mode x must restore the saved xPELP bit and the x7 register before executing the xRET instruction to return from a trap.

Shadow Stack (Zicfiss)

This section specifies the Privileged ISA for the Zicfiss extension.

Shadow Stack Pointer (ssp) CSR access control

Attempts to access the ssp CSR may result in either an illegal-instruction exception or a virtual-instruction exception, contingent upon the state of the xenvcfg.SSE fields. The conditions are specified as follows:

  • If the privilege mode is less than M and menvcfg.SSE is 0, an illegal-instruction exception is raised.

  • Otherwise, if in U-mode and senvcfg.SSE is 0, an illegal-instruction exception is raised.

  • Otherwise, if in VS-mode and henvcfg.SSE is 0, a virtual-instruction exception is raised.

  • Otherwise, if in VU-mode and either henvcfg.SSE or senvcfg.SSE is 0, a virtual-instruction exception is raised.

  • Otherwise, the access is allowed.

Shadow-Stack-Enabled (SSE) State

The term xSSE is used to determine if backward-edge CFI using shadow stacks provided by the Zicfiss extension is enabled at a privilege mode.

When S-mode is implemented, it is determined as follows:

Table 3. xSSE determination when S-mode is implemented
Privilege Mode xSSE

M

0

S or HS

menvcfg.SSE

VS

henvcfg.SSE

U or VU

senvcfg.SSE

When S-mode is not implemented, then xSSE is 0 at both M and U privilege modes.

Activating Zicfiss in U-mode must be done explicitly per process. Not activating Zicfiss at U-mode for a process when that application is not compiled with Zicfiss allows it to invoke shared libraries that may contain Zicfiss instructions. The Zicfiss instructions in the shared library revert to their Zimop/Zcmop-defined behavior in this case.

When Zicfiss is enabled in S-mode it is benign to use an operating system that is not compiled with Zicfiss instructions. Such an operating system that does not use backward-edge CFI for S-mode execution may still activate Zicfiss for U-mode applications.

When programs that use Zicfiss instructions are installed on a processor that supports the Zicfiss extension but the extension is not enabled at the privilege mode where the program executes, the program continues to function correctly but without backward-edge CFI protection as the Zicfiss instructions will revert to their Zimop/Zcmop-defined behavior.

When programs that use Zicfiss instructions are installed on a processor that does not support the Zicfiss extension but supports the Zimop and Zcmop extensions, the programs continues to function correctly but without backward-edge CFI protection as the Zicfiss instructions will revert to their Zimop/Zcmop-defined behavior.

On processors that do not support Zimop/Zcmop extensions, all Zimop/Zcmop code points including those used for Zicfiss instructions may cause an illegal-instruction exception. Execution of programs that use these instructions on such machines is not supported.

Activating Zicfiss in M-mode is currently not supported. Additionally, when S-mode is not implemented, activation in U-mode is also not supported. These functionalities may be introduced in a future standard extension.

Changes to xSSE take effect immediately; address-translation caches need not be synchronized with SFENCE.VMA, HFENCE.GVMA, or HFENCE.VVMA instructions.

Shadow Stack Memory Protection

To protect shadow stack memory, the memory is associated with a new page type – the Shadow Stack (SS) page – in the single-stage and VS-stage page tables. The encoding R=0, W=1, and X=0, is defined to represent an SS page. When menvcfg.SSE=0, this encoding remains reserved. Similarly, when V=1 and henvcfg.SSE=0, this encoding remains reserved at VS and VU levels.

If satp.MODE (or vsatp.MODE when V=1) is set to Bare and the effective privilege mode is below M, shadow stack memory accesses are prohibited, and shadow stack instructions will raise a store/AMO access-fault exception. When the effective privilege mode is M, any memory access by an SSAMOSWAP.W/D instruction will result in a store/AMO access-fault exception.

Memory mapped as an SS page cannot be written to by instructions other than SSAMOSWAP.W/D, SSPUSH, and C.SSPUSH. Attempts will raise a store/AMO access-fault exception. Access to a SS page using cache-block operation (CBO.*) instructions is not permitted. Such accesses will raise a store/AMO access-fault exception. Implicit accesses, including instruction fetches to an SS page, are not permitted. Such accesses will raise an access-fault exception appropriate to the access type. However, the shadow stack is readable by all instructions that only load from memory.

Stores to shadow stack pages by instructions other than SSAMOSWAP, SSPUSH, and C.SSPUSH will trigger a store/AMO access-fault exception, not a store/AMO page-fault exception, signaling a fatal error. A store/AMO page-fault suggests that the operating system could address and rectify the fault, which is not feasible in this scenario. Hence, the page-fault handler must decode the opcode of the faulting instruction to discern whether the fault was caused by a non-shadow-stack instruction writing to an SS page (a fatal condition) or by a shadow stack instruction to a non-resident page (a recoverable condition). The performance-critical nature of operating system page fault handlers necessitates triggering an access fault instead of a page fault, allowing for a straightforward distinction between fatal conditions and recoverable faults.

Operating systems must ensure that no writable, non-shadow-stack alias virtual address mappings exist for the physical memory backing the shadow stack. Furthermore, in systems where an address-misaligned exception supersedes the access-fault exception, handlers emulating misaligned stores must be designed to cause an access-fault exception when the store is directed to a shadow stack page.

All instructions that perform load operations are allowed to read from the shadow stack. This feature facilitates debugging and performance profiling by allowing examination of the link register values backed up in the shadow stack.

As of the drafting of this specification, instruction fetches are the sole type of implicit access subjected to single- or VS-stage address translation.

If a shadow stack (SS) instruction raises an access-fault, page-fault, or guest-page-fault exception that is supposed to indicate the original instruction type (load or store/AMO), then the reported exception cause is respectively a store/AMO access fault (code 7), a store/AMO page fault (code 15), or a store/AMO guest-page fault (code 23). For shadow stack instructions, the reported instruction type is always as though it were a store or AMO, even for instructions SSPOPCHK and C.SSPOPCHK that only read from memory and do not write to it.

When Zicfiss is implemented, the existing "store/AMO" exceptions can be thought of as "store/AMO/SS" exceptions, indicating that the trapping instruction is either a store, an AMO, or a shadow stack instruction.

Shadow stack instructions are restricted to accessing shadow stack (pte.xwr=010b) pages. Should a shadow stack instruction access a page that is not designated as a shadow stack page and is not marked as read-only (pte.xwr=001), a store/AMO access-fault exception will be invoked. Conversely, if the page being accessed by a shadow stack instruction is a read-only page, a store/AMO page-fault exception will be triggered.

Shadow stack loads and stores will trigger a store/AMO page-fault if the accessed page is read-only, to support copy-on-write (COW) of a shadow stack page. If the page has been marked read-only for COW tracking, the page-fault handler responds by creating a copy of the page and updates the pte.xwr to 010b, thereby designating each copy as a shadow stack page. Conversely, if the access targets a genuinely read-only page, the fault being reported as a store/AMO page-fault signals to the operating system that the fault is fatal and non-recoverable. Reporting the fault as a store/AMO page-fault, even for SSPOPCHK initiated memory access, aids in the determination of fatality; if these were reported as load page-faults, access to a truly read-only page might be mistakenly treated as a recoverable fault, leading to the faulting instruction being retried indefinitely. The PTE does not provide a read-only shadow stack encoding.

Attempts by shadow stack instructions to access pages marked as read-write, read-write-execute, read-execute, or execute-only result in a store/AMO access-fault exception, similarly indicating a fatal condition.

Shadow stacks should be bounded at each end by guard pages to prevent accidental underflows or overflows from one shadow stack into another. Conventionally, a guard page for a stack is a page that is not accessible by the process that owns the stack.

If the virtual address in ssp is not XLEN aligned, then the SSPUSH/ C.SSPUSH/SSPOPCHK/C.SSPOPCHK instructions cause a store/AMO access-fault exception.

Misaligned accesses to shadow stack are not required and enforcing alignment is more secure to detect errors in the program. An access-fault exception is raised instead of address-misaligned exception in such cases to indicate fatality and that the instruction must not be emulated by a trap handler.

Correct execution of shadow stack instructions that access memory requires the the accessed memory to be idempotent. If the memory referenced by SSPUSH/C.SSPUSH/SSPOPCHK/C.SSPOPCHK/SSAMOSWAP.W/D instructions is not idempotent, then the instructions cause a store/AMO access-fault exception.

The SSPOPCHK instruction performs a load followed by a check of the loaded data value with the link register as source. If the check against the link register faults, and the instruction is restarted by the trap handler, then the instruction will perform a load again. If the memory from which the load is performed is non-idempotent, then the second load may cause unexpected side effects. Shadow stack instructions that access the shadow stack require the memory referenced by ssp to be idempotent to avoid such concerns. Locating shadow stacks in non-idempotent memory, such as non-idempotent device memory, is not an expected usage, and requiring memory referenced to be idempotent does not pose a significant restriction.

The U and SUM bit enforcement is performed normally for shadow stack instruction initiated memory accesses. The state of the MXR bit does not affect read access to a shadow stack page as the shadow stack page is always readable by all instructions that load from memory.

The G-stage address translation and protections remain unaffected by the Zicfiss extension. The xwr == 010b encoding in the G-stage PTE remains reserved. When G-stage page tables are active, the shadow stack instructions that access memory require the G-stage page table to have read-write permission for the accessed memory; else a store/AMO guest-page-fault exception is raised.

A future extension may define a shadow stack encoding in the G-stage page table to support use cases such as a hypervisor enforcing shadow stack protections for its guests.

Svpbmt and Svnapot extensions are supported for shadow stack pages.

The PMA checks are extended to require memory referenced by shadow stack instructions to be idempotent. The PMP checks are extended to require read-write permission for memory accessed by shadow stack instructions. If the PMP does not provide read-write permissions or if the accessed memory is not idempotent then a store/AMO access-fault exception is raised.

The SSAMOSWAP.W/D instructions require the PMA of the accessed memory range to provide AMOSwap level support.