The CFI instructions — LPAD landing pads (forward-edge, anti-JOP) and the shadow-stack ops (backward-edge, anti-ROP) — live in Volume I. This chapter is their privileged plumbing: who may enable them, and how the MMU protects shadow-stack memory.
Enabling, mode by mode
| Landing pads (xLPE) | Shadow stacks (xSSE) | |
|---|---|---|
| M-mode | mseccfg.MLPE | Hardwired 0 |
| S / HS | menvcfg.LPE | menvcfg.SSE |
| VS | henvcfg.LPE | henvcfg.SSE |
| U / VU | senvcfg.LPE | senvcfg.SSE |
Disabled modes degrade gracefully: LPAD becomes a no-op and the
shadow-stack ops fall back to their Zimop/Zcmop write-zero behavior —
so one binary (or shared library) runs on any combination of support
and policy. ssp CSR access follows the cascade with the usual trap
taxonomy: menvcfg.SSE=0 → illegal-instruction below M; senvcfg.SSE=0
→ illegal in U; henvcfg.SSE=0 → virtual-instruction in VS/VU. xSSE
changes take effect immediately — no fence required.
Preserving ELP across traps
A trap can strike after JALR retires but before its target decodes —
exactly when ELP=LP_EXPECTED is armed. Trap entry to mode x saves
ELP into xPELP (mstatus.MPELP, mstatus.SPELP via sstatus,
vsstatus.SPELP, dcsr.pelp for Debug Mode) and clears ELP so the
handler runs unarmed. On xRET to mode y: ELP ← xPELP if yLPE, else
NO_LP_EXPECTED; xPELP clears. The landing-pad software-check
(cause 18, stval=2) sits above illegal-instruction
but below instruction-access-fault in priority. Handlers making their
own indirect calls (or enabling interrupts) must first save xPELP and
x7 — the label register is state too.
Shadow-stack memory protection
The PTE encoding xwr = 010 — reserved since Sv32 — becomes the Shadow Stack page when the governing SSE bit is on. The fault matrix is the heart of the design — access-fault means fatal, page-fault means the OS can fix this:
| Result | |
|---|---|
| SSPUSH / C.SSPUSH / SSAMOSWAP → SS page | Allowed — the only writers. |
| Any load → SS page | Allowed — debuggers and profilers may read saved return addresses. MXR is irrelevant. |
| Ordinary store / CBO.* / instruction fetch → SS page | Store/AMO ACCESS-fault (fetch: fetch access-fault) — fatal by construction; the page-fault handler never needs to decode opcodes for this. |
| SS instruction → read-only page (xwr=001) | Store/AMO PAGE-fault — the recoverable case. |
| SS instruction → RW / RX / RWX / X page | Store/AMO ACCESS-fault — fatal; SS instructions may only touch SS or RO pages. |
| SS instruction with satp/vsatp = Bare (below M), misaligned ssp, or non-idempotent memory | Store/AMO ACCESS-fault — no Bare-mode shadow stacks, no misaligned emulation (deliberately non-emulable), no device-memory stacks (SSPOPCHK restart would double-load). |
U and SUM apply normally. Under H, the G-stage keeps xwr=010 reserved: a guest’s shadow-stack access needs plain read-write G-stage permission or it raises a store/AMO guest-page fault — shadow-stack-ness is purely a VS-stage property (for now). PMA checks extend to require idempotency, PMP to require read-write, and SSAMOSWAP needs AMOSwap-capable memory. Svpbmt and Svnapot compose fine with SS pages.
Hardware Designer Notes
Cost check: ELP machinery is ~5 flops and trap-path muxing; the SS page type is a handful of terms in the already-existing permission PLA plus a new op attribute. The verification burden is the fault matrix — six cases × (single-stage, two-stage, PMP, PMA) — not the datapath.
Minimal Linux-boot hart MUST
- Add the ELP/xPELP preservation flops to every trap-entry and xRET path (including MNRET and Debug entry/resume), with the yLPE-gated restore
- Extend the permission checker: xwr=010 ∧ SSE → SS page with the exact fault-type matrix (access = fatal, page = recoverable), keyed by an is-SS-instruction attribute on the memory op
- Report store/AMO fault types for ALL SS-instruction faults, SSPOPCHK included
MAY simplify / trap-and-emulate
- Omit both extensions for a Linux-boot v1 (senvcfg/menvcfg bits read zero, xwr=010 stays reserved) — RVA23 mandates them, so plan the PTE-decoder hook
- Reuse the AMO datapath for SSAMOSWAP; the SS-page qualifier is one extra permission input
Check yourself — privileged CFI
1.A timer interrupt fires between a JALR and the decode of its target. Why must trap entry save ELP, and where?
2.A normal SD instruction targets a shadow-stack page. Which exception, and why NOT a page fault?
3.Why does an SS instruction hitting a READ-ONLY page raise a store/AMO PAGE fault rather than an access fault?
4.Under the H extension, what must the G-stage grant for a guest's shadow-stack access?