From da7ba3f774d5306a11b473c972ec0870555cc2c6 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Aug 2023 18:28:25 -0500 Subject: [PATCH 01/13] move Zicfilp CSRs to Zicfilp chapter --- cfi_forward.adoc | 304 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 283 insertions(+), 21 deletions(-) diff --git a/cfi_forward.adoc b/cfi_forward.adoc index 16b748a..0560494 100644 --- a/cfi_forward.adoc +++ b/cfi_forward.adoc @@ -17,7 +17,7 @@ indirect call or an indirect jump. The `ELP` state can be one of: The `ELP` state is initialized to `NO_LP_EXPECTED` by the hardware upon reset. -The Zicfilp extension, when active, determines if an indirect call or an +The Zicfilp extension, when enabled, determines if an indirect call or an indirect jump must land on a landing pad, as specified in <>. If `is_lp_expected` is 1, then the hart updates the `ELP` to `LP_EXPECTED`. @@ -103,38 +103,300 @@ not a valid landing pad or may use an alternate register allocation to prevent the accidental landing pad. ==== -[[FCIFIACT]] -=== Forward-edge-CFI-active state +== Zicfilp CSRs -The term `xFCFIE` is used to determine if forward-edge CFI provided by the -Zicfilp extension is active at privilege mode `x` and is defined as follows: +This chapter specifies the CSR state of the Zicfilp extension. -.`xFCFIE` determination -[listing] ----- -if ( privilege == M-mode ) - xFCFIE = mseccfg.MFCFIE -else if ( privilege == S-mode ) - xFCFIE = (V == 0) ? menvcfg.SFCFIE : henvcfg.SFCFIE -else - xFCFIE = (V == 0) ? mstatus.UFCFIE : vsstatus.UFCFIE ----- +=== Machine environment configuration registers (`menvcfg and menvcfgh`) + +.Machine environment configuration register (`menvcfg`) for MXLEN=64 +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'LPE'}, + {bits: 1, name: 'SSE'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, + {bits: 53, name: 'WPRI'}, + {bits: 1, name: 'HADE'}, + {bits: 1, name: 'PBMTE'}, + {bits: 1, name: 'STCE'}, +], config:{lanes: 4, hspace:1024}} +.... + +Zicfilp extension introduces the `LPE` field (bit 2) in `menvcfg`. When +`LPE` field is 1, the Zicfilp extension is enabled in S-mode. When `LPE` +field is 0, the Zicfilp extension is not enabled in S-mode and the following +rules apply to S-mode: + +* The hart does not update the expected landing pad (`ELP`) state and the `ELP` + state is always `NO_LP_EXPECTED`. +* The `lpad` instruction executes as a no-op. + +=== Supervisor environment configuration registers (`senvcfg`) + +.Supervisor environment configuration register (`senvcfg`) +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'LPE'}, + {bits: 1, name: 'SSE'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, +], config:{lanes: 1, hspace:1024}} +.... + +Zicfilp extension introduces the `LPE` field (bit 2) in `senvcfg`. When +`LPE` field is 1, the Zicfilp extension is enabled in VU/U-mode. When `LPE` +field is 0, the Zicfilp extension is not enabled in VU/U-mode and the +following rules apply to VU/U-mode: + +* The hart does not update the expected landing pad (`ELP`) state and the `ELP` + state is always `NO_LP_EXPECTED`. +* The `lpad` instruction executes as a no-op. + +=== Hypervisor environment configuration registers (`henvcfg and henvcfgh`) + +.Hypervisor environment configuration register (`henvcfg`) for MXLEN=64 +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'LPE'}, + {bits: 1, name: 'SSE'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, + {bits: 53, name: 'WPRI'}, + {bits: 1, name: 'HADE'}, + {bits: 1, name: 'PBMTE'}, + {bits: 1, name: 'STCE'}, +], config:{lanes: 4, hspace:1024}} +.... + +Zicfilp extension introduces the `LPE` field (bit 2) in `henvcfg`. When +`LPE` field is 1, the Zicfilp extension is enabled in VS-mode. When `LPE` +field is 0, the Zicfilp extension is not enabled in VS-mode and the following +rules apply to VS-mode: + +* The hart does not update the expected landing pad (`ELP`) state and the `ELP` + state is always `NO_LP_EXPECTED`. +* The `lpad` instruction executes as a no-op. + +=== Machine status registers (`mstatus`) + +.Machine-mode status register (`mstatus`) for RV64 +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SIE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'MIE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SPIE'}, + {bits: 1, name: 'UBE'}, + {bits: 1, name: 'MPIE'}, + {bits: 1, name: 'SPP'}, + {bits: 2, name: 'VS[1:0]'}, + {bits: 2, name: 'MPP[1:0]'}, + {bits: 2, name: 'FS[1:0]'}, + {bits: 2, name: 'XS[1:0]'}, + {bits: 1, name: 'MPRV'}, + {bits: 1, name: 'SUM'}, + {bits: 1, name: 'MXR'}, + {bits: 1, name: 'TVM'}, + {bits: 1, name: 'TW'}, + {bits: 1, name: 'TSR'}, + {bits: 1, name: 'SPELP'}, + {bits: 8, name: 'WPRI'}, + {bits: 2, name: 'UXL[1:0]'}, + {bits: 2, name: 'SXL[1:0]'}, + {bits: 1, name: 'SBE'}, + {bits: 1, name: 'MBE'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'MPELP'}, + {bits: 21, name: 'WPRI'}, + {bits: 1, name: 'SD'}, +], config:{lanes: 4, hspace:1024}} +.... + +The Zicfilp extension introduces the `SPELP` (bit 23) and `MPELP` (bit 41) +fields that hold the previous `ELP`, and are updated as specified in +<>. The `xPELP` fields are encoded as follows: + +* 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. +* 1 - `LP_EXPECTED` - a landing pad instruction is expected. + +=== Supervisor status registers (`sstatus`) + +.Supervisor-mode status register (`sstatus`) when `SXLEN=64` +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SIE'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPIE'}, + {bits: 1, name: 'UBE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SPP'}, + {bits: 2, name: 'VS[1:0]'}, + {bits: 2, name: 'WPRI'}, + {bits: 2, name: 'FS[1:0]'}, + {bits: 2, name: 'XS[1:0]'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SUM'}, + {bits: 1, name: 'MXR'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPELP'}, + {bits: 8, name: 'WPRI'}, + {bits: 2, name: 'UXL[1:0]'}, + {bits: 29, name: 'WPRI'}, + {bits: 1, name: 'SD'}, +], config:{lanes: 4, hspace:1024}} +.... + +Access to the `SPELP` field introducecd by Zicfilp accesses the homonymous +fields of `mstatus` when `V=0` and the homonymous fields of `vsstatus` +when `V=1`. + +=== Virtual supervisor status registers (`vsstatus`) + +.Virtual supervisor status register (`vsstatus`) when `VSXLEN=64` +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SIE'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPIE'}, + {bits: 1, name: 'UBE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SPP'}, + {bits: 2, name: 'VS[1:0]'}, + {bits: 2, name: 'WPRI'}, + {bits: 2, name: 'FS[1:0]'}, + {bits: 2, name: 'XS[1:0]'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SUM'}, + {bits: 1, name: 'MXR'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPELP'}, + {bits: 8, name: 'WPRI'}, + {bits: 2, name: 'UXL[1:0]'}, + {bits: 29, name: 'WPRI'}, + {bits: 1, name: 'SD'}, +], config:{lanes: 4, hspace:1024}} +.... + +The Zicfilp extension introduces the `SPELP` (bit 23) field that hold the +previous `ELP`, and is updated as specified in <>. +The `SPELP` field is encoded as follows: + +* 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. +* 1 - `LP_EXPECTED` - a landing pad instruction is expected. + +=== Machine Security Configuration (`mseccfg`) + +.Machine security configuration register (`mseccfg`) when `MXLEN=64` +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'MML'}, + {bits: 1, name: 'MMWP'}, + {bits: 1, name: 'RLB'}, + {bits: 5, name: 'WPRI'}, + {bits: 1, name: 'USEED'}, + {bits: 1, name: 'SSEED'}, + {bits: 1, name: 'MLPE'}, + {bits: 53, name: 'WPRI'}, +], config:{lanes: 4, hspace:1024}} +.... + +The Zicfilp extension introduces the `MLPE` (bit 10) field in `mseccfg`. When +`MLPE` field is 1, Zicfilp extension is enabled in M-mode. When `MLPE` field +is 0, the Zicfilp extension is not enabled in M-mode and the following rules +apply to M-mode. + +* The hart does not update the expected landing pad (`ELP`) state and the `ELP` + state is always `NO_LP_EXPECTED`. +* The `lpad` instruction executes as a no-op. + +=== Debug Control and Status (`dcsr`) + +.Debug Control and Status (`dcsr`) +[wavedrom, ,svg] +.... +{reg: [ + {bits: 2, name: 'prv'}, + {bits: 1, name: 'step'}, + {bits: 1, name: 'nmip'}, + {bits: 1, name: 'mprven'}, + {bits: 1, name: 'v'}, + {bits: 3, name: 'cause'}, + {bits: 1, name: 'stoptime'}, + {bits: 1, name: 'stopcount'}, + {bits: 1, name: 'stepie'}, + {bits: 1, name: 'ebreaku'}, + {bits: 1, name: 'ebreaks'}, + {bits: 1, name: '0'}, + {bits: 1, name: 'ebreakm'}, + {bits: 1, name: 'ebreakvu'}, + {bits: 1, name: 'ebreakvs'}, + {bits: 1, name: 'pelp'}, + {bits: 9, name: '0'}, + {bits: 4, name: 'debugver'}, +], config:{lanes: 4, hspace:1024}} +.... + +The Zicfilp extension introduces the `pelp` (bit 18) in `dcsr`. The `pelp` field +holds the previous `ELP`, and is updated as specified in <>. The +`pelp` field is encoded as follows: + +* 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. +* 1 - `LP_EXPECTED` - a landing pad instruction is expected. + +[[FCIFIACT]] +=== 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 and it is +determined as follows: + +.`xLPE` determination +[width=100%] +[%header, cols="^4,^12"] +|=== +|Privilege Mode| xLPE +| M | `mseccfg.MLPE` +| S or HS | `menvcfg.LPE` +| VS | `henvcfg.LPE` +| U or VU | `senvcfg.LPE` +|=== [NOTE] ==== -The Zicfilp must be explicitly activated for use at each privilege mode. +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 active. +implemented or is not enabled. ==== [[LP_INST]] === Landing pad instruction -When Zicfilp is active, `lpad` is the only instruction allowed to execute when -the `ELP` state is `LP_EXPECTED`. If Zicfilp is not active then the instruction -is a no-op. If Zicfilp is active, the `lpad` instruction causes an +When Zicfilp is enabled, `lpad` is the only instruction allowed to execute when +the `ELP` state is `LP_EXPECTED`. If Zicfilp is not enabled then the instruction +is a no-op. If Zicfilp is enabled, the `lpad` instruction causes an integrity-fault exception with *tval set to "landing pad fault (code=2)" if any of the following conditions are true: @@ -161,7 +423,7 @@ The operation of the `lpad` instruction is as follows: .`lpad` operation [listing] ---- -if (xFCFIE != 0) +if (xLPE != 0) // If PC not 4-byte aligned then integrity-fault if pc[1:0] != 0 Cause integrity-fault exception From fb2aa0dab73291001c46ac94ab92f284315fcb81 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Aug 2023 18:39:34 -0500 Subject: [PATCH 02/13] move Zicfiss CSRs to Zicfiss chapter --- cfi_backward.adoc | 189 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 154 insertions(+), 35 deletions(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index 9af9b5f..cfa65af 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -11,7 +11,7 @@ addresses to lower addresses. Each entry on the shadow stack is `XLEN` wide and holds the link register value. The `ssp` points to the top of the shadow stack, i.e. address of the last element stored on the shadow stack. -When backward-edge CFI is active, each function that needs to spill the link +When Zicfiss is enabled, each function that needs to spill the link register (e.g., non-leaf functions) stores the link register value to the regular stack and a shadow copy of the link register value to the shadow stack when the function is entered (the prologue). When such a function need to return (the @@ -48,21 +48,139 @@ the `c.mop.0` and `c.mop.2` encodings defined by the Zcmop extension. When a Zimop encoding is not used by the Zicfiss extension then the instruction follows its Zimop defined behavior. -=== Backward-edge-CFI-active state +== Zicfiss CSRs -The term `xBCFIE` is used to determine if backward-edge CFI provided by the -Zicfiss extension is active at a privilege mode `x` and is defined as follows: +This chapter specifies the CSR state of the Zicfiss extensions. -.`xBCFIE` determination -[listing] ----- -if ( privilege == M-mode ) - xBCFIE = 1 -else if ( privilege == S-mode ) - xBCFIE = (V == 0) ? menvcfg.SBCFIE : henvcfg.SBCFIE -else - xBCFIE = (V == 0) ? sstatus.UBCFIE : vsstatus.UBCFIE ----- +=== Machine environment configuration registers (`menvcfg and menvcfgh`) + +.Machine environment configuration register (`menvcfg`) for MXLEN=64 +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 2, name: 'WPRI'}, + {bits: 1, name: 'SSE'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, + {bits: 53, name: 'WPRI'}, + {bits: 1, name: 'HADE'}, + {bits: 1, name: 'PBMTE'}, + {bits: 1, name: 'STCE'}, +], config:{lanes: 4, hspace:1024}} +.... + +Zicfiss extension introduces the `SSE` field (bit 3) in `menvcfg`. When +`SSE` field is 1, the Zicfiss extension is enabled in S-mode. When `SSE` +field is 0, the Zicfiss extension is not enabled in S-mode and the following +rules apply to privilege modes less than M. + +* Attempts to access the `ssp` CSR raise an illegal-instruction exception. +* The 32-bit Zicfiss instructions revert to their Zimop defined behavior. +* The 16-bit Zicfiss instructions revert to their Zcmop defined behavior. +* The `pte.xwr=010b` encoding in S-stage page tables is reserved. +* The `henvcfg.SSE` and `senvcfg.SSE` fields are read-only zero. + +=== Supervisor environment configuration registers (`senvcfg`) + +.Supervisor environment configuration register (`senvcfg`) +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 2, name: 'WPRI'}, + {bits: 1, name: 'SSE'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, +], config:{lanes: 1, hspace:1024}} +.... + +Zicfiss extension introduces the `SSE` field (bit 2) in `senvcfg`. When +`SSE` field is 1, the Zicfiss extension is enabled in VU/U-mode. When `SSE` +field is 0, the Zicfiss extension is not enabled in VS/U-mode and the following +rules apply: + +* Attempts to access the `ssp` CSR raise an illegal-instruction exception. +* The 32-bit Zicfiss instructions revert to their Zimop defined behavior. +* The 16-bit Zicfiss instructions revert to their Zcmop defined behavior. + +=== Hypervisor environment configuration registers (`henvcfg and henvcfgh`) + +.Hypervisor environment configuration register (`henvcfg`) for MXLEN=64 +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 2, name: 'WPRI'}, + {bits: 1, name: 'SSE'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, + {bits: 53, name: 'WPRI'}, + {bits: 1, name: 'HADE'}, + {bits: 1, name: 'PBMTE'}, + {bits: 1, name: 'STCE'}, +], config:{lanes: 4, hspace:1024}} +.... + +Zicfiss extension introduces the `SSE` field (bit 2) in `henvcfg`. When +`SSE` field is 1, the Zicfiss extension is enabled in VS-mode. When `SSE` +field is 0, the Zicfiss extension is not enabled in VS-mode and the following +rules apply when `V=1`. + +* Attempts to access the `ssp` CSR raise an illegal-instruction exception. +* The 32-bit Zicfiss instructions revert to their Zimop defined behavior. +* The 16-bit Zicfiss instructions revert to their Zcmop defined behavior. +* The `pte.xwr=010b` encoding in VS-stage page tables is reserved. +* The `senvcfg.SSE` field is read-only zero. + +=== Shadow stack pointer (`ssp`) + +The `ssp` CSR is an unprivileged read-write (URW) CSR that reads and writes `XLEN` +low order bits of the shadow stack pointer (`ssp`). There is no high CSR defined +as the `ssp` is always as wide as the `XLEN` of the current privilege mode. + +=== Machine Security Configuration (`mseccfg`) + +.Machine security configuration register (`mseccfg`) when `MXLEN=64` +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'MML'}, + {bits: 1, name: 'MMWP'}, + {bits: 1, name: 'RLB'}, + {bits: 5, name: 'WPRI'}, + {bits: 1, name: 'USEED'}, + {bits: 1, name: 'SSEED'}, + {bits: 1, name: 'WPRI'}, + {bits: 6, name: 'SSPMP'}, + {bits: 47, name: 'WPRI'}, +], config:{lanes: 4, hspace:1024}} +.... + +The Zicfiss extension introduces the `SSPMP` WARL field in `mseccfg`. The +`SSPMP` field identifies a PMP entry as the shadow stack memory region for +M-mode use. The rules enforced by PMP for M-mode shadow stack memory accesses +are specified in <>. + +=== 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 and it is +determined as follows: + +.`xSSE` determination +[width=100%] +[%header, cols="^4,^12"] +|=== +|Privilege Mode| xLPE +| M | `1` +| S or HS | `menvcfg.SSE` +| VS | `henvcfg.SSE` +| U or VU | `senvcfg.SSE` +|=== [NOTE] ==== @@ -72,18 +190,18 @@ 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 active in S-mode it is benign to use an operating system that is +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 Zicfiss is implemented, the extension is always active in M-mode. However, +When Zicfiss is implemented, the extension is always enabled in M-mode. However, it is benign to use M-mode firmware that has not been compiled with Zicfiss instructions. Such M-mode firmware that does not use backward-edge CFI for M-mode execution may still enable the use of Zicfiss by lower privilege modes. When programs that use Zicfiss instructions are installed on a processor that -supports the Zicfiss extension but the extension is not active at the privilege +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. @@ -164,6 +282,7 @@ instructions using the Zcmop encodings. The `c.sspush x1` and the `c.sspopchk x5` instructions are encoded using the `C.LUI` major opcode and using the `c.mop.0` and `c.mop.2` encodings defined by the Zcmop extension. + The `c.sspush x1` expands to `sspush x1` and `c.sspopchk x5` expands to `sspopchk x5`. @@ -383,7 +502,7 @@ The operation of the `sspush` and `c.sspush` instructions is as follows: .`sspush` and `c.sspush` operation [listing] ---- -If (xBCFIE == 1) +If (xSSE == 1) mem[ssp - (XLEN/8)] = X(src) # Store src value to ssp - XLEN/8 ssp = ssp - (XLEN/8) # decrement ssp by XLEN/8 endif @@ -394,7 +513,7 @@ The operation of the `ssload` instruction is as follows: .`ssload` operation [listing] ---- -if (xBCFIE == 1) +if (xSSE == 1) X(dst) = mem[ssp] # Load dst from address in ssp # Only x1 and x5 may be used as dst else @@ -407,8 +526,8 @@ The operation of the `sspinc` instruction is as follows: .`sspinc` operation [listing] ---- -if (xBCFIE == 1) - ssp = ssp + (nzuimm * XLEN/8) +if (xSSE == 1) + ssp = ssp + XLEN/8 endif ---- @@ -417,7 +536,7 @@ The operation of the `sspopchk` and `c.sspopchk` instructions is as follows: .`sspopchk` and `c.sspopchk` operation [listing] ---- -if (xBCFIE == 1) +if (xSSE == 1) temp = mem[ssp] # Load temp from address in ssp and if temp != X(src) # Compare temp to value in src and # cause an integrity-fault exception @@ -560,7 +679,7 @@ The operation of the `ssprr` instructions is as follows: .`ssprr` operation [listing] ---- -If (xBCFIE == 1) +If (xSSE == 1) X(dst) = ssp else X(dst) = 0 @@ -570,21 +689,21 @@ endif [NOTE] ==== The property of Zimop writing 0 to the `rd` when the extension using Zimop is -not implemented, enabled for use, or not active may be used by to determine if -backward-edge CFI is active. For example, functions that unwind shadow stacks -may skip over the unwind actions by dynamically detecting if the backward-edge -CFI extension is active. +not implemented, enabled for use, or not enabled may be used by to determine if +Zicfiss extension is enabled. For example, functions that unwind shadow stacks +may skip over the unwind actions by dynamically detecting if the Zicfiss +extension is enabled. An example sequence such as the following may be used: [listing] ssprr t0 # mv ssp to t0 - beqz bcfi_not_active # zero is not a valid shadow stack + beqz zicfiss_not_enabled # zero is not a valid shadow stack # pointer by convention - # Backward-edge CFI is active + # Zicfiss is enabled : : -bcfi_not_active: +zicfiss_not_active: Operating systems and runtimes must not locate shadow stacks at address 0 to assist with the use of such code sequences. @@ -624,7 +743,7 @@ The operation of the `ssamoswap` instructions is as follows: .`ssamoswap` operation [listing] ---- -If (xBCFIE == 1) +If (xSSE == 1) Perform the following atomically with sequential consistency X(dst) = mem[X(addr)] mem[X(addr)] = X(src) @@ -701,8 +820,8 @@ Attempting to fetch an instruction from a shadow stack page raises an instruction page-fault exception. The encoding `R=0`, `W=1`, and `X=0`, is defined to represent a shadow stack -page. When `menvcfg.SBCFIE=0`, this encoding remains reserved. When `V=1` and -`henvcfg.SBCFIE=0`, this encoding remains reserved at `VS` and `VU`. +page. When `menvcfg.SSE=0`, this encoding remains reserved. When `V=1` and +`henvcfg.SSE=0`, this encoding remains reserved at `VS` and `VU`. The following faults may occur: @@ -752,8 +871,8 @@ follows: 3. If `pte.v = 0` or if any bits of encodings that are reserved for future standard use are set within `pte`, stop and raise a page-fault exception corresponding to the original access type. The encoding `pte.xwr = 010b` - is not reserved if `V=0` and `menvcfg.SBCFIE` is 1 or if `V=1` and - `henvcfg.SBCFIE` is 1. + is not reserved if `V=0` and `menvcfg.SSE` is 1 or if `V=1` and + `henvcfg.SSE` is 1. 4. Otherwise, the PTE is valid. If `pte.r = 1` or `pte.w = 1` or `pte.x = 1`, go to step 5. Otherwise, this PTE is a pointer to the next level of the page From b2e6dc2b850a51100ec87cd1da34b36639ecd7ee Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Aug 2023 19:02:29 -0500 Subject: [PATCH 03/13] rename sspinc to sspincp --- cfi_backward.adoc | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index cfa65af..570b601 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -30,7 +30,7 @@ The Zicfiss extension introduces the following instructions: ** `sspush x1`, `c.sspush x1`, and `sspush x5` ** `sspopchk x1`, `sspopchk x5`, and `c.sspopchk x5` ** `ssload x1` and `ssload x5` -** `sspinc` +** `ssincp` * Read the value of `ssp` into a register (See <>) ** `ssprr` @@ -245,7 +245,7 @@ current top of the shadow stack followed by an increment of the `ssp` by {bits: 5, name: '00000'}, {bits: 3, name: 'funct3', attr:['100']}, {bits: 5, name: 'nzuimm'}, - {bits: 12, name: '100000011101', attr:['sspinc']}, + {bits: 12, name: '100000011101', attr:['ssincp']}, ], config:{lanes: 1, hspace:1024}} .... @@ -275,7 +275,7 @@ current top of the shadow stack followed by an increment of the `ssp` by Only `x1` and `x5` encodings are supported as `rd` for `ssload`. Only `x1` and `x5` encodings are supported as `rs1` for `sspopchk`. Only `x1` and `x5` encodings are supported as `rs2` for `sspush`. -Only non-zero encodings of `nzuimm` are defined for `sspinc`. +Only non-zero encodings of `nzuimm` are defined for `ssincp`. Zicfiss provides 16-bit versions of the `sspush x1` and `sspopchk x5` instructions using the Zcmop encodings. The `c.sspush x1` and the @@ -305,7 +305,7 @@ integrity-fault exception with *tval set to "shadow stack fault (code=3)". The `ssload` instruction can be used to load a return address from the shadow stack into a link register. -The `sspinc` instruction adds the zero-extended non-zero immediate `nzuimm`, +The `ssincp` instruction adds the zero-extended non-zero immediate `nzuimm`, scaled by `XLEN/8`, to the `ssp`. This instruction may be used to pop up to 31 return addresses from the shadow stack. @@ -434,11 +434,11 @@ mode is as follows: : : ssload x1 # load return address from shadow stack - sspinc 1 # increment ssp by 1 * (XLEN/8) + ssincp 1 # increment ssp by 1 * (XLEN/8) # # ssload loads the value from location addressed by ssp into - # destination register. sspinc updates ssp to (ssp + XLEN/8) - # - does a pop. Following completion of sspinc the ssp value + # destination register. ssincp updates ssp to (ssp + XLEN/8) + # - does a pop. Following completion of ssincp the ssp value # is the new top of stack i.e. 0x19740428 # # 0x19740418:[ ] @@ -521,9 +521,9 @@ else endif ---- -The operation of the `sspinc` instruction is as follows: +The operation of the `ssincp` instruction is as follows: -.`sspinc` operation +.`ssincp` operation [listing] ---- if (xSSE == 1) @@ -628,23 +628,23 @@ longjmp() { // Unwind the frames in a loop while ( num_unwind > 0 ) { if ( num_unwind >= 31 ) { - asm("sspinc 31"); + asm("ssincp 31"); num_unwind -= 31; continue; } else if ( num_unwind >= 16 ) { - asm("sspinc 16"); + asm("ssincp 16"); num_unwind -= 16; continue; } else if ( num_unwind >= 8 ) { - asm("sspinc 8"); + asm("ssincp 8"); num_unwind -= 8; continue; } else if ( num_unwind >= 4 ) { - asm("sspinc 4"); + asm("ssincp 4"); num_unwind -= 4; continue; } else { - asm("sspinc 1"); + asm("ssincp 1"); num_unwind -= 1; } // Test if unwound past the shadow stack bounds From 9de6b60812a4def245c908d4b7d1bc053873c886 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Aug 2023 19:24:43 -0500 Subject: [PATCH 04/13] update sspinc to unwind only by 1 frame --- cfi_backward.adoc | 53 +++++++++++------------------------------------ 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index 570b601..269469a 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -231,21 +231,10 @@ current top of the shadow stack followed by an increment of the `ssp` by .... {reg: [ {bits: 7, name: 'opcode', attr:'SYSTEM'}, - {bits: 5, name: 'rd', attr:['00001','00101','00000','00000']}, + {bits: 5, name: 'rd', attr:[`00000`,'00001','00101','00000','00000']}, {bits: 3, name: 'funct3', attr:['100']}, - {bits: 5, name: 'rs1', attr:['00000','00000', '00001', '00101']}, - {bits: 12, name: '100000011100', attr:['ssload x1','ssload x5','sspopchk x1','sspopchk x5']}, -], config:{lanes: 1, hspace:1024}} -.... - -[wavedrom, ,svg] -.... -{reg: [ - {bits: 7, name: 'opcode', attr:'SYSTEM'}, - {bits: 5, name: '00000'}, - {bits: 3, name: 'funct3', attr:['100']}, - {bits: 5, name: 'nzuimm'}, - {bits: 12, name: '100000011101', attr:['ssincp']}, + {bits: 5, name: 'rs1', attr:[`00000`,'00000','00000', '00001', '00101']}, + {bits: 12, name: '100000011100', attr:[`ssincp`, 'ssload x1','ssload x5','sspopchk x1','sspopchk x5']}, ], config:{lanes: 1, hspace:1024}} .... @@ -275,7 +264,6 @@ current top of the shadow stack followed by an increment of the `ssp` by Only `x1` and `x5` encodings are supported as `rd` for `ssload`. Only `x1` and `x5` encodings are supported as `rs1` for `sspopchk`. Only `x1` and `x5` encodings are supported as `rs2` for `sspush`. -Only non-zero encodings of `nzuimm` are defined for `ssincp`. Zicfiss provides 16-bit versions of the `sspush x1` and `sspopchk x5` instructions using the Zcmop encodings. The `c.sspush x1` and the @@ -305,9 +293,8 @@ integrity-fault exception with *tval set to "shadow stack fault (code=3)". The `ssload` instruction can be used to load a return address from the shadow stack into a link register. -The `ssincp` instruction adds the zero-extended non-zero immediate `nzuimm`, -scaled by `XLEN/8`, to the `ssp`. This instruction may be used to pop up to 31 -return addresses from the shadow stack. +The `ssincp` instruction adds `XLEN/8` to the `ssp`. This instruction may be +used to pop a return address from the shadow stack. While any register may be used as link register, conventionally the `x1` or `x5` registers are used. The shadow stack instructions are designed to be most @@ -434,7 +421,7 @@ mode is as follows: : : ssload x1 # load return address from shadow stack - ssincp 1 # increment ssp by 1 * (XLEN/8) + ssincp # increment ssp by (XLEN/8) # # ssload loads the value from location addressed by ssp into # destination register. ssincp updates ssp to (ssp + XLEN/8) @@ -624,29 +611,13 @@ longjmp() { asm("ssprr %0" : "=r"(cur_ssp):); // Skip the unwind if backward-edge CFI not enabled asm("beqz %0, back_cfi_not_enabled" : "=r"(cur_ssp):); - num_unwind = jmp_buf->saved_ssp - cur_ssp; // Unwind the frames in a loop - while ( num_unwind > 0 ) { - if ( num_unwind >= 31 ) { - asm("ssincp 31"); - num_unwind -= 31; - continue; - } else if ( num_unwind >= 16 ) { - asm("ssincp 16"); - num_unwind -= 16; - continue; - } else if ( num_unwind >= 8 ) { - asm("ssincp 8"); - num_unwind -= 8; - continue; - } else if ( num_unwind >= 4 ) { - asm("ssincp 4"); - num_unwind -= 4; - continue; - } else { - asm("ssincp 1"); - num_unwind -= 1; - } + while ( jmp_buf->saved_ssp > cur_ssp ) { + // advance by a maximum of 4K at a time to avoid + // unwinding past bounds of the shadow stack + cur_ssp = ( (jmp_buf->saved_ssp - cur_ssp) >= 4096 ) ? + (cur_ssp + 4096) : jmp_buf->saved_ssp; + asm("csrw ssp, %0" : : "r" (cur_ssp)); // Test if unwound past the shadow stack bounds asm("ssload x5"); } From 238600e56a0308a24df7efe63c9acc5bc6c57999 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Aug 2023 19:28:12 -0500 Subject: [PATCH 05/13] introduce compressed ssincp --- cfi_backward.adoc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index 269469a..d100a78 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -29,8 +29,8 @@ The Zicfiss extension introduces the following instructions: * Push to and pop from the shadow stack (See <>) ** `sspush x1`, `c.sspush x1`, and `sspush x5` ** `sspopchk x1`, `sspopchk x5`, and `c.sspopchk x5` -** `ssload x1` and `ssload x5` -** `ssincp` +** `ssload x1`, and `ssload x5` +** `ssincp`, and `c.ssincp` * Read the value of `ssp` into a register (See <>) ** `ssprr` @@ -43,7 +43,7 @@ the `mop.r.0`, `mop.r.1`, and `mop.rr.0` encodings defined by the Zimop extension. The 16-bit instructions are encoded using the `C.LUI` major opcode and using -the `c.mop.0` and `c.mop.2` encodings defined by the Zcmop extension. +the `c.mop.0`, `c.mop.1` and `c.mop.2` encodings defined by the Zcmop extension. When a Zimop encoding is not used by the Zicfiss extension then the instruction follows its Zimop defined behavior. @@ -266,10 +266,9 @@ Only `x1` and `x5` encodings are supported as `rs1` for `sspopchk`. Only `x1` and `x5` encodings are supported as `rs2` for `sspush`. Zicfiss provides 16-bit versions of the `sspush x1` and `sspopchk x5` -instructions using the Zcmop encodings. The `c.sspush x1` and the +instructions using the Zcmop encodings. The `c.ssincp`, `c.sspush x1` and the `c.sspopchk x5` instructions are encoded using the `C.LUI` major opcode and -using the `c.mop.0` and `c.mop.2` encodings defined by the Zcmop extension. - +using the `c.mop.0`, `c.mop.1`, and `c.mop.2` encodings. The `c.sspush x1` expands to `sspush x1` and `c.sspopchk x5` expands to `sspopchk x5`. @@ -294,7 +293,7 @@ The `ssload` instruction can be used to load a return address from the shadow stack into a link register. The `ssincp` instruction adds `XLEN/8` to the `ssp`. This instruction may be -used to pop a return address from the shadow stack. +used to remove a shadow stack frame from the shadow stack. While any register may be used as link register, conventionally the `x1` or `x5` registers are used. The shadow stack instructions are designed to be most @@ -508,9 +507,9 @@ else endif ---- -The operation of the `ssincp` instruction is as follows: +The operation of the `ssincp` and `c.ssincp` instructions is as follows: -.`ssincp` operation +.`ssincp` and `c.ssincp` operation [listing] ---- if (xSSE == 1) From 6b1f7937c96f6a4a8e1de43ee0bbde3fc0b465e5 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Aug 2023 20:01:19 -0500 Subject: [PATCH 06/13] include two mnemonics for ssload - sslw and ssld --- cfi_backward.adoc | 92 +++++++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index d100a78..268c534 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -29,7 +29,8 @@ The Zicfiss extension introduces the following instructions: * Push to and pop from the shadow stack (See <>) ** `sspush x1`, `c.sspush x1`, and `sspush x5` ** `sspopchk x1`, `sspopchk x5`, and `c.sspopchk x5` -** `ssload x1`, and `ssload x5` +** `sslw x1`, and `sslw x5` when effective xlen is 32 +** `ssld x1`, and `ssld x5` when effective xlen is 64 ** `ssincp`, and `c.ssincp` * Read the value of `ssp` into a register (See <>) @@ -231,10 +232,10 @@ current top of the shadow stack followed by an increment of the `ssp` by .... {reg: [ {bits: 7, name: 'opcode', attr:'SYSTEM'}, - {bits: 5, name: 'rd', attr:[`00000`,'00001','00101','00000','00000']}, + {bits: 5, name: 'rd', attr:[`00000`,'00001','00101','00001','00101', '00000','00000']}, {bits: 3, name: 'funct3', attr:['100']}, - {bits: 5, name: 'rs1', attr:[`00000`,'00000','00000', '00001', '00101']}, - {bits: 12, name: '100000011100', attr:[`ssincp`, 'ssload x1','ssload x5','sspopchk x1','sspopchk x5']}, + {bits: 5, name: 'rs1', attr:[`00000`,'00000','00000', '00000','00000', '00001', '00101']}, + {bits: 12, name: '100000011100', attr:[`ssincp`, 'sslw x1','sslw x5',`ssld x1',`ssld x5, sspopchk x1','sspopchk x5']}, ], config:{lanes: 1, hspace:1024}} .... @@ -261,7 +262,7 @@ current top of the shadow stack followed by an increment of the `ssp` by ], config:{lanes: 1, hspace:1024}} .... -Only `x1` and `x5` encodings are supported as `rd` for `ssload`. +Only `x1` and `x5` encodings are supported as `rd` for `sslw` and `ssld`. Only `x1` and `x5` encodings are supported as `rs1` for `sspopchk`. Only `x1` and `x5` encodings are supported as `rs2` for `sspush`. @@ -289,8 +290,15 @@ pop the shadow return address value from the shadow stack and check that the value matches the contents of the link register and if not cause an integrity-fault exception with *tval set to "shadow stack fault (code=3)". -The `ssload` instruction can be used to load a return address from the shadow -stack into a link register. +The `sslw` instruction can be used, when effective xlen is 32, to load a return +address from the shadow stack into a link register. + +The `ssld` instruction can be used, when effective xlen is 64, to load a return +address from the shadow stack into a link register. + +The `sslw` and `ssld` are both encoded identically. They have different mnemonics +to illustrate that the instructions operates on a _word_ when XLEN is 32 and on +a _doubleword_ when XLEN is 64. The `ssincp` instruction adds `XLEN/8` to the `ssp`. This instruction may be used to remove a shadow stack frame from the shadow stack. @@ -419,10 +427,10 @@ mode is as follows: # : : - ssload x1 # load return address from shadow stack + ssld x1 # load return address from shadow stack ssincp # increment ssp by (XLEN/8) # - # ssload loads the value from location addressed by ssp into + # ssld loads the value from location addressed by ssp into # destination register. ssincp updates ssp to (ssp + XLEN/8) # - does a pop. Following completion of ssincp the ssp value # is the new top of stack i.e. 0x19740428 @@ -443,24 +451,32 @@ be held in the link register itself for the duration of the leaf function execution. ==== -The `ssload`, `c.sspopchk`, and `sspopchk` instructions perform a load +The `c.sspopchk`, and `sspopchk` instructions perform a load identically to the existing `LOAD` instruction, with the difference that the base is implicitly `ssp` and the width is implicitly `XLEN`. +The `sslw` instruction perform a load identically to the existing `LOAD` +instruction, with the difference that the base is implicitly `ssp` and the width +is implicitly a _word_. + +The `ssld` instruction perform a load identically to the existing `LOAD` +instruction, with the difference that the base is implicitly `ssp` and the width +is implicitly a _doubleword_. + The `sspush` and `c.sspush` instructions performs a store identically to the existing `STORE` instruction, with the difference that the base is implicitly `ssp` and the width is implicitly `XLEN`. -The `sspush`, `c.sspush`, `sspopchk`, `c.sspopchk`, and `ssload` require the +The `sspush`, `c.sspush`, `sspopchk`, `c.sspopchk`, `sslw`, and `ssld` require the virtual address in `ssp` to have a shadow stack attribute (see <>). -Correct execution of `sspush`, `c.sspush`, `sspopchk`, `c.sspopchk`, and `ssload` +Correct execution of `sspush`, `c.sspush`, `sspopchk`, `c.sspopchk`, `sslw`, and `ssld` require that `ssp` refers to idempotent memory. If the memory reference by `ssp` is not idempotent, then the `sspush`/`c.sspush` instructions cause a -store/AMO access-fault exception, and the `ssload`/`sspopchk`/`c.sspopchk` +store/AMO access-fault exception, and the `sslw`/`ssld`/`sspopchk`/`c.sspopchk` instructions cause a load access-fault exception. -If the virtual address in `ssp` is not `XLEN` aligned, then the `ssload`/ +If the virtual address in `ssp` is not `XLEN` aligned, then the `sslw`/`ssld`/ `sspopchk`/`c.sspopchk` instructions cause a load access-fault exception, and the `sspush`/`c.sspush` instructions cause a store/AMO access-fault exception. @@ -494,13 +510,26 @@ If (xSSE == 1) endif ---- -The operation of the `ssload` instruction is as follows: +The operation of the `sslw` instruction is as follows: -.`ssload` operation +.`sslw` operation [listing] ---- if (xSSE == 1) - X(dst) = mem[ssp] # Load dst from address in ssp + X(dst) = mem[ssp] # Load dst with 32-bits from address in ssp + # Only x1 and x5 may be used as dst +else + X(dst) = 0 +endif +---- + +The operation of the `ssld` instruction is as follows: + +.`ssld` operation +[listing] +---- +if (xSSE == 1) + X(dst) = mem[ssp] # Load dst with 64-bits from address in ssp # Only x1 and x5 may be used as dst else X(dst) = 0 @@ -571,11 +600,11 @@ jump to the return address. ==== Store-to-load forwarding is a common technique employed by high-performance processor implementations. Zicfiss implementations may prevent forwarding from -a non-shadow-stack store to `ssload`/`sspopchk`/`c.sspopchk` instructions. A +a non-shadow-stack store to `sslw`/`ssld`/`sspopchk`/`c.sspopchk` instructions. A non-shadow-stack store causes a fault if done to a page mapped as a shadow stack. However, such determination may be delayed till the PTE has been examined and thus may be used to transiently forward the data from such stores to a -`ssload`/`sspopchk`/`c.sspopchk`. +`sslw`/`ssld`/`sspopchk`/`c.sspopchk`. ==== [NOTE] @@ -618,7 +647,7 @@ longjmp() { (cur_ssp + 4096) : jmp_buf->saved_ssp; asm("csrw ssp, %0" : : "r" (cur_ssp)); // Test if unwound past the shadow stack bounds - asm("ssload x5"); + asm("ssld x5"); } back_cfi_not_enabled: : @@ -777,11 +806,11 @@ enhanced to support a shadow stack memory region for use by M-mode. The shadow stack memory is protected using page table attributes such that it cannot be stored to by instructions other than `sspush`, `c.sspush`, and -`ssamoswap`. The `ssload`, `sspopchk`, and `c.sspopchk` instructions can only +`ssamoswap`. The `sslw`, `ssld`, `sspopchk`, and `c.sspopchk` instructions can only load from shadow stack memory. The `sspush` and `c.sspush` instructions perform a store. The `ssamoswap` -instruction performs an AMO. The `ssload`, `sspopchk`, and `c.sspopchk` +instruction performs an AMO. The `sslw`, `ssld`, `sspopchk`, and `c.sspopchk` instructions perfom a load. The shadow stack can be read using all instructions that load from memory. @@ -801,7 +830,7 @@ The following faults may occur: . If the accessed page is not a shadow stack page or if the page is in non-idempotent memory: .. `ssamoswap`, `c.sspush`, and `sspush` cause a store/AMO access-fault. -.. `ssload`, `c.sspopchk`, and `sspopchk` cause a load access-fault. +.. `sslw`, `ssld`, `c.sspopchk`, and `sspopchk` cause a load access-fault. [NOTE] ==== @@ -859,8 +888,8 @@ follows: fields of the `mstatus` register, stop and raise a page-fault exception corresponding to the original access type. -The PMA checks are extended to require memory referenced by `sspush`, `ssload`, -`ssamoswap`, `c.sspush`, `c.sspopchk`, and `sspopchk` to be idempotent. +The PMA checks are extended to require memory referenced by `sspush`, `sslw`, +`ssld`, `ssamoswap`, `c.sspush`, `c.sspopchk`, and `sspopchk` to be idempotent. The `U` and `SUM` bit enforcement is performed normally for shadow stack instruction initiated memory accesses. The state of the `MXR` bit does not @@ -891,7 +920,7 @@ access-fault exception. ==== The G-stage address translation and protections remain unaffected by Zicfiss -extension. When G-stage page tables are active, the `ssamoswap`, `ssload`, +extension. When G-stage page tables are active, the `ssamoswap`, `sslw`, `ssld`, `c.sspopchk`, and `sspopchk` instructions require the G-stage page table to have read permission for the accessed memory, whereas the `ssamoswap`, `c.sspush`, and `sspush` instructions require write permission. The `xwr == 010b` encoding in @@ -909,15 +938,16 @@ its guests. When privilege mode is less than M, the PMP region accessed by `sspush`, `c.sspush`, and `ssamoswap` must provide write permission and the PMP region -accessed by `ssload`, `c.sspopchk`, and `sspopchk` must provide read permission. +accessed by `sslw`, `ssld`, `c.sspopchk`, and `sspopchk` must provide read +permission. The M-mode memory accesses by `sspush`, `c.sspush` and `ssamoswap` instructions test for write permission in the matching PMP entry when permission checking is required. -The M-mode memory accesses by `ssload`, `c.sspopchk`, and `sspopchk` instructions -test for read permission in the matching PMP entry when permission checking is -required. +The M-mode memory accesses by `sslw`, `ssld`, `c.sspopchk`, and `sspopchk` +instructions test for read permission in the matching PMP entry when permission +checking is required. A new WARL field `SSPMP` is defined in the `mseccfg` CSR to identify a PMP entry as the shadow stack memory region for M-mode accesses. @@ -927,7 +957,7 @@ When `mseccfg.MML` is 1, the `SSPMP` field is read-only else it may be written. When the `SSPMP` field is not zero, the following rules are additionally enforced for M-mode memory accesses: -* `sspush`, `c.sspush`, `ssload`, `sspopchk`, `c.sspopchk`, and `ssamoswap` +* `sspush`, `c.sspush`, `sslw`, `ssld`, `sspopchk`, `c.sspopchk`, and `ssamoswap` instructions must match the PMP entry identified by `SSPMP` else an access-fault exception corresponding to the access type occurs. From 1a75ad421d5ac086897ac2aa8280a44ce512cf7f Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Aug 2023 20:04:02 -0500 Subject: [PATCH 07/13] ssp bits 1:0 are always 0; if xlen can never be 32 then bit 2 is also read-only 0 --- cfi_backward.adoc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index 268c534..b940613 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -141,7 +141,9 @@ rules apply when `V=1`. The `ssp` CSR is an unprivileged read-write (URW) CSR that reads and writes `XLEN` low order bits of the shadow stack pointer (`ssp`). There is no high CSR defined -as the `ssp` is always as wide as the `XLEN` of the current privilege mode. +as the `ssp` is always as wide as the `XLEN` of the current privilege mode. The +bits 1:0 of `ssp` are read-only zero. If the UXLEN or SXLEN may never be 32, +then the bit 2 is also read-only zero. === Machine Security Configuration (`mseccfg`) From 36f6a5e1d4bfe8ac06884d44756a56fb6f7145c9 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Aug 2023 20:07:09 -0500 Subject: [PATCH 08/13] remove csrs chapter --- cfi_backward.adoc | 27 ++-- cfi_csrs.adoc | 314 ---------------------------------------------- cfi_forward.adoc | 31 ++--- cfi_header.adoc | 1 - 4 files changed, 30 insertions(+), 343 deletions(-) delete mode 100644 cfi_csrs.adoc diff --git a/cfi_backward.adoc b/cfi_backward.adoc index b940613..7921513 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -49,11 +49,11 @@ the `c.mop.0`, `c.mop.1` and `c.mop.2` encodings defined by the Zcmop extension. When a Zimop encoding is not used by the Zicfiss extension then the instruction follows its Zimop defined behavior. -== Zicfiss CSRs +=== Zicfiss CSRs This chapter specifies the CSR state of the Zicfiss extensions. -=== Machine environment configuration registers (`menvcfg and menvcfgh`) +==== Machine environment configuration registers (`menvcfg and menvcfgh`) .Machine environment configuration register (`menvcfg`) for MXLEN=64 [wavedrom, ,svg] @@ -83,9 +83,9 @@ rules apply to privilege modes less than M. * The `pte.xwr=010b` encoding in S-stage page tables is reserved. * The `henvcfg.SSE` and `senvcfg.SSE` fields are read-only zero. -=== Supervisor environment configuration registers (`senvcfg`) +==== Supervisor environment configuration registers (`senvcfg`) -.Supervisor environment configuration register (`senvcfg`) +.Supervisor environment configuration register (`senvcfg`) when `SXLEN=64` [wavedrom, ,svg] .... {reg: [ @@ -95,7 +95,8 @@ rules apply to privilege modes less than M. {bits: 2, name: 'CBIE'}, {bits: 1, name: 'CBCFE'}, {bits: 1, name: 'CBZE'}, -], config:{lanes: 1, hspace:1024}} + {bits: 56, name: 'WPRI'}, +], config:{lanes: 4, hspace:1024}} .... Zicfiss extension introduces the `SSE` field (bit 2) in `senvcfg`. When @@ -107,7 +108,7 @@ rules apply: * The 32-bit Zicfiss instructions revert to their Zimop defined behavior. * The 16-bit Zicfiss instructions revert to their Zcmop defined behavior. -=== Hypervisor environment configuration registers (`henvcfg and henvcfgh`) +==== Hypervisor environment configuration registers (`henvcfg and henvcfgh`) .Hypervisor environment configuration register (`henvcfg`) for MXLEN=64 [wavedrom, ,svg] @@ -137,7 +138,7 @@ rules apply when `V=1`. * The `pte.xwr=010b` encoding in VS-stage page tables is reserved. * The `senvcfg.SSE` field is read-only zero. -=== Shadow stack pointer (`ssp`) +==== Shadow stack pointer (`ssp`) The `ssp` CSR is an unprivileged read-write (URW) CSR that reads and writes `XLEN` low order bits of the shadow stack pointer (`ssp`). There is no high CSR defined @@ -145,7 +146,7 @@ as the `ssp` is always as wide as the `XLEN` of the current privilege mode. The bits 1:0 of `ssp` are read-only zero. If the UXLEN or SXLEN may never be 32, then the bit 2 is also read-only zero. -=== Machine Security Configuration (`mseccfg`) +==== Machine Security Configuration (`mseccfg`) .Machine security configuration register (`mseccfg`) when `MXLEN=64` [wavedrom, ,svg] @@ -160,7 +161,7 @@ then the bit 2 is also read-only zero. {bits: 1, name: 'WPRI'}, {bits: 6, name: 'SSPMP'}, {bits: 47, name: 'WPRI'}, -], config:{lanes: 4, hspace:1024}} +], config:{lanes: 1, hspace:1024}} .... The Zicfiss extension introduces the `SSPMP` WARL field in `mseccfg`. The @@ -178,7 +179,7 @@ determined as follows: [width=100%] [%header, cols="^4,^12"] |=== -|Privilege Mode| xLPE +|Privilege Mode| xSSE | M | `1` | S or HS | `menvcfg.SSE` | VS | `henvcfg.SSE` @@ -234,10 +235,10 @@ current top of the shadow stack followed by an increment of the `ssp` by .... {reg: [ {bits: 7, name: 'opcode', attr:'SYSTEM'}, - {bits: 5, name: 'rd', attr:[`00000`,'00001','00101','00001','00101', '00000','00000']}, + {bits: 5, name: 'rd', attr:['00000','00001','00101','00001','00101','00000','00000']}, {bits: 3, name: 'funct3', attr:['100']}, - {bits: 5, name: 'rs1', attr:[`00000`,'00000','00000', '00000','00000', '00001', '00101']}, - {bits: 12, name: '100000011100', attr:[`ssincp`, 'sslw x1','sslw x5',`ssld x1',`ssld x5, sspopchk x1','sspopchk x5']}, + {bits: 5, name: 'rs1', attr:['00000','00000','00000','00000','00000','00001','00101']}, + {bits: 12, name: '100000011100', attr:['ssincp','sslw x1','sslw x5','ssld x1','ssld x5','sspopchk x1','sspopchk x5']}, ], config:{lanes: 1, hspace:1024}} .... diff --git a/cfi_csrs.adoc b/cfi_csrs.adoc deleted file mode 100644 index 82204fc..0000000 --- a/cfi_csrs.adoc +++ /dev/null @@ -1,314 +0,0 @@ -[[CSRs]] -== Shadow Stack and Landing Pad CSRs - -This chapter specifies the CSR state of the Zicfiss and Zicfilp extensions. - -=== Machine environment configuration registers (`menvcfg and menvcfgh`) - -.Machine environment configuration register (`menvcfg`) for MXLEN=64 -[wavedrom, ,svg] -.... -{reg: [ - {bits: 1, name: 'FIOM'}, - {bits: 3, name: 'WPRI'}, - {bits: 2, name: 'CBIE'}, - {bits: 1, name: 'CBCFE'}, - {bits: 1, name: 'CBZE'}, - {bits: 51, name: 'WPRI'}, - {bits: 1, name: 'SFCFIE'}, - {bits: 1, name: 'SBCFIE'}, - {bits: 1, name: 'HADE'}, - {bits: 1, name: 'PBMTE'}, - {bits: 1, name: 'STCE'}, -], config:{lanes: 4, hspace:1024}} -.... - -Zicfiss extension introduces the `SBCFIE` field (bit 60) in `menvcfg`. When -`SBCFIE` field is 1, the Zicfiss extension is active in S-mode. When `SBCFIE` -field is 0, the Zicfiss extension is not active in S-mode and the following -rules apply to privilege modes less than M. - -* Attempts to access the `ssp` CSR raise an illegal-instruction exception. -* The 32-bit Zicfiss instructions revert to their Zimop defined behavior. -* The 16-bit Zicfiss instructions revert to their Zcmop defined behavior. -* The `pte.xwr=010b` encoding in S-stage page tables is reserved. -* The `henvcfg.SBCFIE` and `sstatus.UBCFIE` fields are read-only zero. - -Zicfilp extension introduces the `SFCFIE` field (bit 59) in `menvcfg`. When -`SFCFIE` field is 1, the Zicfilp extension is active in S-mode. When `SFCFIE` -field is 0, the Zicfilp extension is not active in S-mode and the following -rules apply to S-mode: - -* The hart does not update the expected landing pad (`ELP`) state and the `ELP` - state is always `NO_LP_EXPECTED`. -* The `lpad` instruction executes as a no-op. - -=== Hypervisor environment configuration registers (`henvcfg and henvcfgh`) - -.Hypervisor environment configuration register (`henvcfg`) for MXLEN=64 -[wavedrom, ,svg] -.... -{reg: [ - {bits: 1, name: 'FIOM'}, - {bits: 3, name: 'WPRI'}, - {bits: 2, name: 'CBIE'}, - {bits: 1, name: 'CBCFE'}, - {bits: 1, name: 'CBZE'}, - {bits: 51, name: 'WPRI'}, - {bits: 1, name: 'SFCFIE'}, - {bits: 1, name: 'SBCFIE'}, - {bits: 1, name: 'HADE'}, - {bits: 1, name: 'PBMTE'}, - {bits: 1, name: 'STCE'}, -], config:{lanes: 4, hspace:1024}} -.... - -Zicfiss extension introduces the `SBCFIE` field (bit 60) in `henvcfg`. When -`SBCFIE` field is 1, the Zicfiss extension is active in VS-mode. When `SBCFIE` -field is 0, the Zicfiss extension is not active in VS-mode and the following -rules apply when `V=1`. - -* Attempts to access the `ssp` CSR raise an illegal-instruction exception. -* The 32-bit Zicfiss instructions revert to their Zimop defined behavior. -* The 16-bit Zicfiss instructions revert to their Zcmop defined behavior. -* The `pte.xwr=010b` encoding in VS-stage page tables is reserved. -* The `sstatus.UBCFIE` (really `vsstatus.UBCFIE`) field is read-only zero. - -Zicfilp extension introduces the `SFCFIE` field (bit 59) in `henvcfg`. When -`SFCFIE` field is 1, the Zicfilp extension is active in VS-mode. When `SFCFIE` -field is 0, the Zicfilp extension is not active in VS-mode and the following -rules apply to VS-mode: - -* The hart does not update the expected landing pad (`ELP`) state and the `ELP` - state is always `NO_LP_EXPECTED`. -* The `lpad` instruction executes as a no-op. - -=== Machine status registers (`mstatus`) - -.Machine-mode status register (`mstatus`) for RV64 -[wavedrom, ,svg] -.... -{reg: [ - {bits: 1, name: 'WPRI'}, - {bits: 1, name: 'SIE'}, - {bits: 1, name: 'WPRI'}, - {bits: 1, name: 'MIE'}, - {bits: 1, name: 'WPRI'}, - {bits: 1, name: 'SPIE'}, - {bits: 1, name: 'UBE'}, - {bits: 1, name: 'MPIE'}, - {bits: 1, name: 'SPP'}, - {bits: 2, name: 'VS[1:0]'}, - {bits: 2, name: 'MPP[1:0]'}, - {bits: 2, name: 'FS[1:0]'}, - {bits: 2, name: 'XS[1:0]'}, - {bits: 1, name: 'MPRV'}, - {bits: 1, name: 'SUM'}, - {bits: 1, name: 'MXR'}, - {bits: 1, name: 'TVM'}, - {bits: 1, name: 'TW'}, - {bits: 1, name: 'TSR'}, - {bits: 1, name: 'UFCFIE'}, - {bits: 1, name: 'UBCFIE'}, - {bits: 1, name: 'SPELP'}, - {bits: 1, name: 'MPELP'}, - {bits: 5, name: 'WPRI'}, - {bits: 2, name: 'UXL[1:0]'}, - {bits: 2, name: 'SXL[1:0]'}, - {bits: 1, name: 'SBE'}, - {bits: 1, name: 'MBE'}, - {bits: 25, name: 'WPRI'}, - {bits: 1, name: 'SD'}, -], config:{lanes: 4, hspace:1024}} -.... - -The Zicfiss extension introduces the `UBCFIE` (bit 24) field in `mstatus`. When -`UBCFIE` field is 1, the Zicfiss extension is active in U-mode. When `UBCFIE` -field is 0, the Zicfiss extension is not active in U-mode and the following -rules apply to U-mode. - -* Attempts to access the `ssp` CSR raise an illegal-instruction exception. -* The 32-bit Zicfiss instructions revert to their Zimop defined behavior. -* The 16-bit Zicfiss instructions revert to their Zcmop defined behavior. - -The Zicfilp extension introduces the `UFCFIE` (bit 23), `SPELP` (bit 25), and -`MPELP` (bit 26) fields in `mstatus`. When `UFCFIE` field is 1, the Zicfilp -extension is active in U-mode. When `UFCFIE` field is 0, the Zicfilp extension -is not active in U-mode and the following rules apply to U-mode. - -* The hart does not update the expected landing pad (`ELP`) state and the `ELP` - state is always `NO_LP_EXPECTED`. -* The `lpad` instruction executes as a no-op. - -The `SPELP` (bit 25) and `MPELP` (bit 26) are fields that hold the previous -`ELP`, and are updated as specified in <>. The `xPELP` fields are -encoded as follows: - -* 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. -* 1 - `LP_EXPECTED` - a landing pad instruction is expected. - -=== Supervisor status registers (`sstatus`) - -.Supervisor-mode status register (`sstatus`) when `SXLEN=64` -[wavedrom, ,svg] -.... -{reg: [ - {bits: 1, name: 'WPRI'}, - {bits: 1, name: 'SIE'}, - {bits: 3, name: 'WPRI'}, - {bits: 1, name: 'SPIE'}, - {bits: 1, name: 'UBE'}, - {bits: 1, name: 'WPRI'}, - {bits: 1, name: 'SPP'}, - {bits: 2, name: 'VS[1:0]'}, - {bits: 2, name: 'WPRI'}, - {bits: 2, name: 'FS[1:0]'}, - {bits: 2, name: 'XS[1:0]'}, - {bits: 1, name: 'WPRI'}, - {bits: 1, name: 'SUM'}, - {bits: 1, name: 'MXR'}, - {bits: 3, name: 'WPRI'}, - {bits: 1, name: 'UFCFIE'}, - {bits: 1, name: 'UBCFIE'}, - {bits: 1, name: 'SPELP'}, - {bits: 6, name: 'WPRI'}, - {bits: 2, name: 'UXL[1:0]'}, - {bits: 29, name: 'WPRI'}, - {bits: 1, name: 'SD'}, -], config:{lanes: 4, hspace:1024}} -.... - -Access to `UBCFIE` (bit 24) field introduced by Zicfiss accesses the homonymous -field of `mstatus` when `V=0` and the homonymous field of `vsstatus` when `V=1`. - -Access to the following fields introducecd by Zicfilp accesses the homonymous -fields of `mstatus` when `V=0` and the homonymous fields of `vsstatus` when `V=1`. - -* `UFCFIE` (bit 23). -* `SPELP` (bit 25). - -=== Virtual supervisor status registers (`vsstatus`) - -.Virtual supervisor status register (`vsstatus`) when `VSXLEN=64` -[wavedrom, ,svg] -.... -{reg: [ - {bits: 1, name: 'WPRI'}, - {bits: 1, name: 'SIE'}, - {bits: 3, name: 'WPRI'}, - {bits: 1, name: 'SPIE'}, - {bits: 1, name: 'UBE'}, - {bits: 1, name: 'WPRI'}, - {bits: 1, name: 'SPP'}, - {bits: 2, name: 'VS[1:0]'}, - {bits: 2, name: 'WPRI'}, - {bits: 2, name: 'FS[1:0]'}, - {bits: 2, name: 'XS[1:0]'}, - {bits: 1, name: 'WPRI'}, - {bits: 1, name: 'SUM'}, - {bits: 1, name: 'MXR'}, - {bits: 3, name: 'WPRI'}, - {bits: 1, name: 'UFCFIE'}, - {bits: 1, name: 'UBCFIE'}, - {bits: 1, name: 'SPELP'}, - {bits: 6, name: 'WPRI'}, - {bits: 2, name: 'UXL[1:0]'}, - {bits: 29, name: 'WPRI'}, - {bits: 1, name: 'SD'}, -], config:{lanes: 4, hspace:1024}} -.... - -The Zicfiss extension introduces the `UBCFIE` (bit 24) field in `vsstatus`. When -`UBCFIE` field is 1, Zicfiss extension is active in VU-mode. When `UBCFIE` field -is 0, the following rules apply to VU-mode. - -* Attempts to access the `ssp` CSR raise an illegal-instruction exception. -* The 32-bit Zicfiss instructions revert to their Zimop defined behavior. -* The 16-bit Zicfiss instructions revert to their Zcmop defined behavior. - -The Zicfilp extension introduces the `UFCFIE` (bit 23) and the `SPELP` (bit 25) -fields in `mstatus`. When `UFCFIE` field is 1, the Zicfilp extension is active -in VU-mode. When `UFCFIE` field is 0, the Zicfilp extension is not active in -VU-mode and the following rules apply to VU-mode. - -* The hart does not update the expected landing pad (`ELP`) state and the `ELP` - state is always `NO_LP_EXPECTED`. -* The `lpad` instruction executes as a no-op. - -The `SPELP` (bit 25) field holds the previous `ELP`, and is updated as specified -in <>. The `SPELP` field is encoded as follows: - -* 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. -* 1 - `LP_EXPECTED` - a landing pad instruction is expected. - -=== Shadow stack pointer (`ssp`) - -The `ssp` CSR is an unprivileged read-write (URW) CSR that reads and writes `XLEN` -low order bits of the shadow stack pointer (`ssp`). There is no high CSR defined -as the `ssp` is always as wide as the `XLEN` of the current privilege mode. - -=== Machine Security Configuration (`mseccfg`) - -.Machine security configuration register (`mseccfg`) when `MXLEN=64` -[wavedrom, ,svg] -.... -{reg: [ - {bits: 1, name: 'MML'}, - {bits: 1, name: 'MMWP'}, - {bits: 1, name: 'RLB'}, - {bits: 5, name: 'WPRI'}, - {bits: 1, name: 'USEED'}, - {bits: 1, name: 'SSEED'}, - {bits: 1, name: 'MFCFIE'}, - {bits: 6, name: 'SSPMP'}, - {bits: 47, name: 'WPRI'}, -], config:{lanes: 4, hspace:1024}} -.... - -The Zicfiss extension introduces the `SSPMP` WARL field in `mseccfg`. The -`SSPMP` field identifies a PMP entry as the shadow stack memory region for -M-mode use. The rules enforced by PMP for M-mode shadow stack memory accesses -are specified in <>. - -The Zicfilp extension introduces the `MFCFIE` (bit 10) field in `mseccfg`. When -`MFCFIE` field is 1, Zicfilp extension is active in M-mode. When `MFCFIE` field -is 0, the Zicfilp extension is not active in M-mode and the following rules -apply to M-mode. - -* The hart does not update the expected landing pad (`ELP`) state and the `ELP` - state is always `NO_LP_EXPECTED`. -* The `lpad` instruction executes as a no-op. - -=== Debug Control and Status (`dcsr`) - -.Debug Control and Status (`dcsr`) -[wavedrom, ,svg] -.... -{reg: [ - {bits: 2, name: 'prv'}, - {bits: 1, name: 'step'}, - {bits: 1, name: 'nmip'}, - {bits: 1, name: 'mprven'}, - {bits: 1, name: 'v'}, - {bits: 3, name: 'cause'}, - {bits: 1, name: 'stoptime'}, - {bits: 1, name: 'stopcount'}, - {bits: 1, name: 'stepie'}, - {bits: 1, name: 'ebreaku'}, - {bits: 1, name: 'ebreaks'}, - {bits: 1, name: '0'}, - {bits: 1, name: 'ebreakm'}, - {bits: 1, name: 'ebreakvu'}, - {bits: 1, name: 'ebreakvs'}, - {bits: 1, name: 'pelp'}, - {bits: 9, name: '0'}, - {bits: 4, name: 'debugver'}, -], config:{lanes: 4, hspace:1024}} -.... - -The Zicfilp extension introduces the `pelp` (bit 18) in `dcsr`. The `pelp` field -holds the previous `ELP`, and is updated as specified in <>. The -`pelp` field is encoded as follows: - -* 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. -* 1 - `LP_EXPECTED` - a landing pad instruction is expected. diff --git a/cfi_forward.adoc b/cfi_forward.adoc index 0560494..991ec82 100644 --- a/cfi_forward.adoc +++ b/cfi_forward.adoc @@ -1,4 +1,4 @@ -[[forward]] +4[forward]] == Landing pad (Zicfilp) To enforce forward-edge control-flow integrity, the Zicfilp extension introduces @@ -103,11 +103,11 @@ not a valid landing pad or may use an alternate register allocation to prevent the accidental landing pad. ==== -== Zicfilp CSRs +=== Zicfilp CSRs This chapter specifies the CSR state of the Zicfilp extension. -=== Machine environment configuration registers (`menvcfg and menvcfgh`) +==== Machine environment configuration registers (`menvcfg and menvcfgh`) .Machine environment configuration register (`menvcfg`) for MXLEN=64 [wavedrom, ,svg] @@ -116,7 +116,7 @@ This chapter specifies the CSR state of the Zicfilp extension. {bits: 1, name: 'FIOM'}, {bits: 1, name: 'WPRI'}, {bits: 1, name: 'LPE'}, - {bits: 1, name: 'SSE'}, + {bits: 1, name: 'WPRI'}, {bits: 2, name: 'CBIE'}, {bits: 1, name: 'CBCFE'}, {bits: 1, name: 'CBZE'}, @@ -136,20 +136,21 @@ rules apply to S-mode: state is always `NO_LP_EXPECTED`. * The `lpad` instruction executes as a no-op. -=== Supervisor environment configuration registers (`senvcfg`) +==== Supervisor environment configuration registers (`senvcfg`) -.Supervisor environment configuration register (`senvcfg`) +.Supervisor environment configuration register (`senvcfg`) when `SXLEN=64` [wavedrom, ,svg] .... {reg: [ {bits: 1, name: 'FIOM'}, {bits: 1, name: 'WPRI'}, {bits: 1, name: 'LPE'}, - {bits: 1, name: 'SSE'}, + {bits: 1, name: 'WPRI'}, {bits: 2, name: 'CBIE'}, {bits: 1, name: 'CBCFE'}, {bits: 1, name: 'CBZE'}, -], config:{lanes: 1, hspace:1024}} + {bits: 56, name: 'WPRI'}, +], config:{lanes: 4, hspace:1024}} .... Zicfilp extension introduces the `LPE` field (bit 2) in `senvcfg`. When @@ -161,7 +162,7 @@ following rules apply to VU/U-mode: state is always `NO_LP_EXPECTED`. * The `lpad` instruction executes as a no-op. -=== Hypervisor environment configuration registers (`henvcfg and henvcfgh`) +==== Hypervisor environment configuration registers (`henvcfg and henvcfgh`) .Hypervisor environment configuration register (`henvcfg`) for MXLEN=64 [wavedrom, ,svg] @@ -170,7 +171,7 @@ following rules apply to VU/U-mode: {bits: 1, name: 'FIOM'}, {bits: 1, name: 'WPRI'}, {bits: 1, name: 'LPE'}, - {bits: 1, name: 'SSE'}, + {bits: 1, name: 'WPRI'}, {bits: 2, name: 'CBIE'}, {bits: 1, name: 'CBCFE'}, {bits: 1, name: 'CBZE'}, @@ -190,7 +191,7 @@ rules apply to VS-mode: state is always `NO_LP_EXPECTED`. * The `lpad` instruction executes as a no-op. -=== Machine status registers (`mstatus`) +==== Machine status registers (`mstatus`) .Machine-mode status register (`mstatus`) for RV64 [wavedrom, ,svg] @@ -235,7 +236,7 @@ fields that hold the previous `ELP`, and are updated as specified in * 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. * 1 - `LP_EXPECTED` - a landing pad instruction is expected. -=== Supervisor status registers (`sstatus`) +==== Supervisor status registers (`sstatus`) .Supervisor-mode status register (`sstatus`) when `SXLEN=64` [wavedrom, ,svg] @@ -268,7 +269,7 @@ Access to the `SPELP` field introducecd by Zicfilp accesses the homonymous fields of `mstatus` when `V=0` and the homonymous fields of `vsstatus` when `V=1`. -=== Virtual supervisor status registers (`vsstatus`) +==== Virtual supervisor status registers (`vsstatus`) .Virtual supervisor status register (`vsstatus`) when `VSXLEN=64` [wavedrom, ,svg] @@ -304,7 +305,7 @@ The `SPELP` field is encoded as follows: * 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. * 1 - `LP_EXPECTED` - a landing pad instruction is expected. -=== Machine Security Configuration (`mseccfg`) +==== Machine Security Configuration (`mseccfg`) .Machine security configuration register (`mseccfg`) when `MXLEN=64` [wavedrom, ,svg] @@ -330,7 +331,7 @@ apply to M-mode. state is always `NO_LP_EXPECTED`. * The `lpad` instruction executes as a no-op. -=== Debug Control and Status (`dcsr`) +==== Debug Control and Status (`dcsr`) .Debug Control and Status (`dcsr`) [wavedrom, ,svg] diff --git a/cfi_header.adoc b/cfi_header.adoc index 2d82e80..8aa880b 100644 --- a/cfi_header.adoc +++ b/cfi_header.adoc @@ -61,7 +61,6 @@ Copyright 2022 by RISC-V International. include::cfi_contributors.adoc[] include::cfi_intro.adoc[] -include::cfi_csrs.adoc[] include::cfi_backward.adoc[] include::cfi_forward.adoc[] //the index must precede the bibliography From 83cd32444725ab065247af622c51a2c62ea97646 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Aug 2023 20:19:50 -0500 Subject: [PATCH 09/13] rename ssprr to ssrdp --- cfi_backward.adoc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index 7921513..a9fa329 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -34,7 +34,7 @@ The Zicfiss extension introduces the following instructions: ** `ssincp`, and `c.ssincp` * Read the value of `ssp` into a register (See <>) -** `ssprr` +** `ssrdp` * Perform atomic swap from a shadow stack location (See <>) ** `ssamoswap` @@ -630,7 +630,7 @@ setjmp() { : : // read and save the shadow stack pointer to jmp_buf - asm("ssprr %0" : "=r"(cur_ssp):); + asm("ssrdp %0" : "=r"(cur_ssp):); jmp_buf->saved_ssp = cur_ssp; : : @@ -639,7 +639,7 @@ longjmp() { : // Read current shadow stack pointer and // compute number of call frames to unwind - asm("ssprr %0" : "=r"(cur_ssp):); + asm("ssrdp %0" : "=r"(cur_ssp):); // Skip the unwind if backward-edge CFI not enabled asm("beqz %0, back_cfi_not_enabled" : "=r"(cur_ssp):); // Unwind the frames in a loop @@ -660,7 +660,7 @@ back_cfi_not_enabled: [[SSP_READ]] === Read `ssp` into a register -The `ssprr` instruction is provided to move the contents of `ssp` to a destination +The `ssrdp` instruction is provided to move the contents of `ssp` to a destination register. [wavedrom, ,svg] @@ -670,15 +670,15 @@ register. {bits: 5, name: 'rd', attr:['dst']}, {bits: 3, name: 'funct3', attr:['100']}, {bits: 5, name: '00000'}, - {bits: 12, name: '100000011101', attr:['ssprr']}, + {bits: 12, name: '100000011101', attr:['ssrdp']}, ], config:{lanes: 1, hspace:1024}} .... -Encoding `rd` as `x0` is not supported for `ssprr`. +Encoding `rd` as `x0` is not supported for `ssrdp`. -The operation of the `ssprr` instructions is as follows: +The operation of the `ssrdp` instructions is as follows: -.`ssprr` operation +.`ssrdp` operation [listing] ---- If (xSSE == 1) @@ -699,7 +699,7 @@ extension is enabled. An example sequence such as the following may be used: [listing] - ssprr t0 # mv ssp to t0 + ssrdp t0 # mv ssp to t0 beqz zicfiss_not_enabled # zero is not a valid shadow stack # pointer by convention # Zicfiss is enabled @@ -775,7 +775,7 @@ An example sequence to store and restore the shadow stack pointer is as follows: # to switch to. The current ssp is first pushed on the current # shadow stack and the ssp is restored from new shadow stack save_shadow_stack_pointer: - ssprr x5 # read ssp and push value onto + ssrdp x5 # read ssp and push value onto sspush x5 # shadow stack. The [ssp] now addi x5, x5, -(XLEN/8) # holds ptr+XLEN/8. The [x5] now # holds ptr. Save away x5 From 8630c23e60f19c597a6f20cfec207fb1d30e60d8 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Fri, 11 Aug 2023 13:01:03 -0500 Subject: [PATCH 10/13] drop amossswap --- cfi_backward.adoc | 120 ++++++++++------------------------------------ 1 file changed, 25 insertions(+), 95 deletions(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index a9fa329..a75433f 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -36,9 +36,6 @@ The Zicfiss extension introduces the following instructions: * Read the value of `ssp` into a register (See <>) ** `ssrdp` -* Perform atomic swap from a shadow stack location (See <>) -** `ssamoswap` - The 32-bit instructions are encoded using the `SYSTEM` major opcode and using the `mop.r.0`, `mop.r.1`, and `mop.rr.0` encodings defined by the Zimop extension. @@ -711,49 +708,6 @@ Operating systems and runtimes must not locate shadow stacks at address 0 to assist with the use of such code sequences. ==== -[[SS_SWAP]] -=== Atomic Swap from a shadow stack location - -The `ssamoswap` instruction performs an atomic swap operation between the `XLEN` -bits of the `src` register and the `XLEN` bits located on the shadow stack at the -address specified in the `addr` register. The resulting value from the swap -operation is then stored into the register specified in the `dst` operand. - -[wavedrom, ,svg] -.... -{reg: [ - {bits: 7, name: 'opcode', attr:'SYSTEM'}, - {bits: 5, name: 'rd', attr:['dst']}, - {bits: 3, name: 'funct3', attr:['100']}, - {bits: 5, name: 'rs1', attr:['addr']}, - {bits: 5, name: 'rs2', attr:['src']}, - {bits: 7, name: '1000001', attr:['ssamoswap']}, -], config:{lanes: 1, hspace:1024}} -.... - -Encoding `rd` as `x0` is not supported for `ssamoswap`. - -The `ssamoswap` is always sequentially consistent and cannot be reordered with -earlier or later memory operations from the same hart. - -The `ssamoswap` causes a store/AMO access-fault exception if the address in -`addr` does not have a shadow stack attribute (see <>), of if the address - is not `XLEN` aligned, or if the memory reference by `ssp` is not idempotent. - -The operation of the `ssamoswap` instructions is as follows: - -.`ssamoswap` operation -[listing] ----- -If (xSSE == 1) - Perform the following atomically with sequential consistency - X(dst) = mem[X(addr)] - mem[X(addr)] = X(src) -else - X(dst) = 0 -endif ----- - [NOTE] ==== Stack switching is a common operation in user programs as well as supervisor @@ -771,29 +725,7 @@ checkpoint. An example sequence to store and restore the shadow stack pointer is as follows: [listing] -# The a0 register holds the pointer to top of new shadow -# to switch to. The current ssp is first pushed on the current -# shadow stack and the ssp is restored from new shadow stack -save_shadow_stack_pointer: - ssrdp x5 # read ssp and push value onto - sspush x5 # shadow stack. The [ssp] now - addi x5, x5, -(XLEN/8) # holds ptr+XLEN/8. The [x5] now - # holds ptr. Save away x5 - # into a context structure to - # restore later. -restore_shadow_stack_pointer: - ssamoswap t0, x0, (a0) # t0=*[a0] and *[a0]=0 - # The [a0] should hold ptr - # The [t0] should hold ptr+XLEN/8 - addi a0, a0, (XLEN/8) # a0+XLEN/8 must match to t0 - bne t0, a0, crash # if not crash program - csrw ssp, t0 # setup new ssp - -Further, the program may enforce an invariant that a shadow stack can be active -only on one hart by using the `ssamoswap` when performing the restore from the -checkpoint such that the checkpoint data is zeroed as part of the restore -sequence. If multiple hart attempt to restore the checkpoint data, only one -of them succeeds. + ==== [[SSMP]] @@ -808,13 +740,12 @@ enhanced to support a shadow stack memory region for use by M-mode. ==== Virtual-Memory system extension for Shadow Stack The shadow stack memory is protected using page table attributes such that it -cannot be stored to by instructions other than `sspush`, `c.sspush`, and -`ssamoswap`. The `sslw`, `ssld`, `sspopchk`, and `c.sspopchk` instructions can only -load from shadow stack memory. +cannot be stored to by instructions other than `sspush`, and `c.sspush`. The +`sslw`, `ssld`, `sspopchk`, and `c.sspopchk` instructions can only load from +shadow stack memory. -The `sspush` and `c.sspush` instructions perform a store. The `ssamoswap` -instruction performs an AMO. The `sslw`, `ssld`, `sspopchk`, and `c.sspopchk` -instructions perfom a load. +The `sspush` and `c.sspush` instructions perform a store. The `sslw`, `ssld`, +`sspopchk`, and `c.sspopchk` instructions perfom a load. The shadow stack can be read using all instructions that load from memory. @@ -828,26 +759,26 @@ page. When `menvcfg.SSE=0`, this encoding remains reserved. When `V=1` and The following faults may occur: . If the accessed page is a shadow stack page: -.. Stores other than `sspush` and `ssamoswap` cause store/AMO access-fault. +.. Stores other than `sspush` and `c.sspush` cause store/AMO access-fault. .. Instruction fetches cause an instruction page-fault. . If the accessed page is not a shadow stack page or if the page is in non-idempotent memory: -.. `ssamoswap`, `c.sspush`, and `sspush` cause a store/AMO access-fault. +.. `c.sspush`, and `sspush` cause a store/AMO access-fault. .. `sslw`, `ssld`, `c.sspopchk`, and `sspopchk` cause a load access-fault. [NOTE] ==== -Stores to shadow stack by instructions other than `sspush`, `c.sspush`, and -`ssamoswap` cause a store/AMO access-fault exception, rather than a store/AMO -page-fault exception, to indicate fatality. +Stores to shadow stack by instructions other than `sspush`, and `c.sspush` +cause a store/AMO access-fault exception, rather than a store/AMO page-fault +exception, to indicate fatality. If a store/AMO page-fault was triggered, it would suggest that the operating system should service that fault and correct the condition. Correcting the condition is not possible in this case. The page-fault handler would have to resort to decoding the opcode of the instruction that caused the page-fault to determine if it was caused by non-shadow-stack-stores to shadow stack pages -(which is a fatal condition) vs. a page fault caused by an `sspush`, `c.sspush`, -or `ssamoswap` to a non-resident page (which is a recoverable condition). Since +(which is a fatal condition) vs. a page fault caused by an `sspush` or +`c.sspush` to a non-resident page (which is a recoverable condition). Since the operating system page-fault handler is typically performance-critical, causing an access-fault instead of a page-fault enables the operating system to easily distinguish between the fatal/non-recoverable conditions and recoverable @@ -855,8 +786,8 @@ page-faults. On implementations where address-misaligned exception is prioritized higher than access-fault exception, a trap handler handler that emulates misaligned stores -must cause an access-fault exception if the store is not `sspush`, `c.sspush`, -or, `ssamoswap`, and the store is being made to a shadow stack page. +must cause an access-fault exception if the store is not `sspush` or `c.sspush`, +and the store is being made to a shadow stack page. Shadow stack instructions cause an access-fault if the accessed page is not a shadow stack page or if the page is in non-idempotent memory to similarly @@ -892,7 +823,7 @@ follows: corresponding to the original access type. The PMA checks are extended to require memory referenced by `sspush`, `sslw`, -`ssld`, `ssamoswap`, `c.sspush`, `c.sspopchk`, and `sspopchk` to be idempotent. +`ssld`, `c.sspush`, `c.sspopchk`, and `sspopchk` to be idempotent. The `U` and `SUM` bit enforcement is performed normally for shadow stack instruction initiated memory accesses. The state of the `MXR` bit does not @@ -923,9 +854,9 @@ access-fault exception. ==== The G-stage address translation and protections remain unaffected by Zicfiss -extension. When G-stage page tables are active, the `ssamoswap`, `sslw`, `ssld`, +extension. When G-stage page tables are active, the `sslw`, `ssld`, `c.sspopchk`, and `sspopchk` instructions require the G-stage page table to have -read permission for the accessed memory, whereas the `ssamoswap`, `c.sspush`, and +read permission for the accessed memory, whereas the `c.sspush`, and `sspush` instructions require write permission. The `xwr == 010b` encoding in the G-stage PTE remains reserved. @@ -939,13 +870,12 @@ its guests. [[PMP_SS]] ==== PMP extension for shadow stack -When privilege mode is less than M, the PMP region accessed by `sspush`, -`c.sspush`, and `ssamoswap` must provide write permission and the PMP region -accessed by `sslw`, `ssld`, `c.sspopchk`, and `sspopchk` must provide read -permission. +When privilege mode is less than M, the PMP region accessed by `sspush` and +`c.sspush` must provide write permission and the PMP region accessed by `sslw`, +`ssld`, `c.sspopchk`, and `sspopchk` must provide read permission. -The M-mode memory accesses by `sspush`, `c.sspush` and `ssamoswap` instructions -test for write permission in the matching PMP entry when permission checking is +The M-mode memory accesses by `sspush` and `c.sspush` instructions test for +write permission in the matching PMP entry when permission checking is required. The M-mode memory accesses by `sslw`, `ssld`, `c.sspopchk`, and `sspopchk` @@ -960,11 +890,11 @@ When `mseccfg.MML` is 1, the `SSPMP` field is read-only else it may be written. When the `SSPMP` field is not zero, the following rules are additionally enforced for M-mode memory accesses: -* `sspush`, `c.sspush`, `sslw`, `ssld`, `sspopchk`, `c.sspopchk`, and `ssamoswap` +* `sspush`, `c.sspush`, `sslw`, `ssld`, `sspopchk`, and `c.sspopchk` instructions must match the PMP entry identified by `SSPMP` else an access-fault exception corresponding to the access type occurs. -* Write by instructions other than `sspush`, `c.sspush`, and `ssamoswap` that +* Write by instructions other than `sspush` and `c.sspush` that match the PMP entry identified by `SSPMP` cause an store/AMO access-fault exception. From 999ef670b87f22feac0f7f8426f6e111ea6d1a6d Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Fri, 11 Aug 2023 14:05:40 -0500 Subject: [PATCH 11/13] add stack switch example sequence --- cfi_backward.adoc | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index a75433f..3ac76f0 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -696,9 +696,9 @@ extension is enabled. An example sequence such as the following may be used: [listing] - ssrdp t0 # mv ssp to t0 - beqz zicfiss_not_enabled # zero is not a valid shadow stack - # pointer by convention + ssrdp t0 # mv ssp to t0 + beqz t0, zicfiss_not_enabled # zero is not a valid shadow stack + # pointer by convention # Zicfiss is enabled : : @@ -722,10 +722,30 @@ memory corruption vulnerabilities. To protect the pointer value, the program may store it at the top of the deactivated shadow stack itself and thereby create a checkpoint. -An example sequence to store and restore the shadow stack pointer is as follows: +An example sequence to restore the shadow stack pointer from the new shadow +stack and save the old shadow stack pointer on the old shadow stack is as +follows: [listing] - +# The a0 register holds the pointer to checkpoint at the top of the new shadow +# stack. x5 at end of the sequence has the address of the checkpoint created on +# current shadow stack. This should be saved away in a context structure to +# restore later. +switch_shadow_stack: + ssrdp t1 # read current ssp + beqz t1, ss_not_enabled # skip if shadow stacks not enabled + csrw ssp, a0 # ssp = checkpoint address + mv t0, a0 # checkpoint value == checkpoint address + sspopchk t0 # pop and check the checkpoint value + li t0, 0 # clear the checkpoint + sspush t0 # by pushing zero in its place + addi a0, a0, (XLEN/8) # a0 now has the top of new shadow stack + mv t0, t1 # t1 holds the top of old shadow stack + csrw ssp, t0 # point ssp to old top of old shadow stack + addi x5, x5, -(XLEN/8) # checkpoint val = (ssp - XLEN/8) + sspush x5 # store checkpoint to (ssp - XLEN/8) + csrw ssp, a0 # setup new ssp +ss_not_enabled: ==== [[SSMP]] From ed6658c977e8f7954f5af045d0035c5253e32ad7 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Fri, 11 Aug 2023 14:11:35 -0500 Subject: [PATCH 12/13] restructure for better readability --- cfi_backward.adoc | 465 +++++++++++++++++++++++----------------------- cfi_forward.adoc | 2 +- 2 files changed, 230 insertions(+), 237 deletions(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index 3ac76f0..93d2903 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -26,12 +26,18 @@ exception. The Zicfiss extension introduces the following instructions: -* Push to and pop from the shadow stack (See <>) +* Push to the shadow stack (See <>) ** `sspush x1`, `c.sspush x1`, and `sspush x5` -** `sspopchk x1`, `sspopchk x5`, and `c.sspopchk x5` -** `sslw x1`, and `sslw x5` when effective xlen is 32 -** `ssld x1`, and `ssld x5` when effective xlen is 64 -** `ssincp`, and `c.ssincp` + +* Pop from the shadow stack (See <>) +** `sspopchk x1`, `c.sspopchk x5`, and `sspopchk x5` + +* Load from the shadow stack (See <>) +** `sslw x1` and `sslw x5` when effective xlen is 32 +** `ssld x1` and `ssld x5` when effective xlen is 64 + +* Increment the shadow stack pointer (See <>) +** `ssincp` and `c.ssincp` * Read the value of `ssp` into a register (See <>) ** `ssrdp` @@ -219,35 +225,77 @@ illegal-instruction exception. Execution of programs that use these instructions on such machines is not supported. ==== -[[SS_PUSH_POP]] -=== Push to and Pop from the shadow stack - +[[SS_PUSH]] +=== Push to shadow stack A shadow stack push operation is defined as decrement of the `ssp` by `XLEN` followed by a write of the link register at the new top of the shadow stack. -A shadow stack pop operation is defined as a `XLEN` wide read from the -current top of the shadow stack followed by an increment of the `ssp` by -`XLEN`. [wavedrom, ,svg] .... {reg: [ {bits: 7, name: 'opcode', attr:'SYSTEM'}, - {bits: 5, name: 'rd', attr:['00000','00001','00101','00001','00101','00000','00000']}, + {bits: 5, name: 'rd', attr:['00000']}, {bits: 3, name: 'funct3', attr:['100']}, - {bits: 5, name: 'rs1', attr:['00000','00000','00000','00000','00000','00001','00101']}, - {bits: 12, name: '100000011100', attr:['ssincp','sslw x1','sslw x5','ssld x1','ssld x5','sspopchk x1','sspopchk x5']}, + {bits: 5, name: 'rs1', attr:['00000']}, + {bits: 5, name: 'rs2', attr:['00001', '00101']}, + {bits: 7, name: '1000001', attr:['sspush x1','sspush x5']}, +], config:{lanes: 1, hspace:1024}} +.... + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 2, name: 'op', attr:'C1'}, + {bits: 5, name: '00000'}, + {bits: 5, name: 'rs1', attr:['00001']}, + {bits: 1, name: '0'}, + {bits: 3, name: '011', attr:['c.sspush x1']}, ], config:{lanes: 1, hspace:1024}} .... +Only `x1` and `x5` encodings are supported as `rs2` for `sspush`. Zicfiss +provides 16-bit versions of the `sspush x1` instruction using the Zcmop +defined `c.mop.1` encoding. The `c.sspush x1` expands to `sspush x1`. + +The `sspush` instruction and its compressed form `c.sspush` can be used, to push +a link register on the shadow stack. The `sspush` and `c.sspush` instructions +performs a store identically to the existing `STORE` instruction, with the +difference that the base is implicitly `ssp` and the width is implicitly `XLEN`. + +The `sspush` and `c.sspush` instructions require the virtual address in `ssp` to +have a shadow stack attribute (see <>). Correct execution of `sspush` and +`c.sspush` requires that `ssp` refers to idempotent memory. If the memory +referenced by `ssp` is not idempotent, then the `sspush`/`c.sspush` instructions +cause a store/AMO access-fault exception. If the virtual address in `ssp` is not +`XLEN` aligned, then the `sspush`/`c.sspush` instructions cause a store/AMO +access-fault exception. + +The operation of the `sspush` and `c.sspush` instructions is as follows: + +.`sspush` and `c.sspush` operation +[listing] +---- +If (xSSE == 1) + mem[ssp - (XLEN/8)] = X(src) # Store src value to ssp - XLEN/8 + ssp = ssp - (XLEN/8) # decrement ssp by XLEN/8 +endif +---- + +[[SS_POP]] +=== Pop from the shadow stack + +A shadow stack pop operation is defined as a `XLEN` wide read from the +current top of the shadow stack followed by an increment of the `ssp` by +`XLEN`. + [wavedrom, ,svg] .... {reg: [ {bits: 7, name: 'opcode', attr:'SYSTEM'}, - {bits: 5, name: 'rd', attr:['00000']}, + {bits: 5, name: 'rd', attr:['00000','00000']}, {bits: 3, name: 'funct3', attr:['100']}, - {bits: 5, name: 'rs1', attr:['00000']}, - {bits: 5, name: 'rs2', attr:['00001', '00101']}, - {bits: 7, name: '1000001', attr:['sspush x1','sspush x5']}, + {bits: 5, name: 'rs1', attr:['00001','00101']}, + {bits: 12, name: '100000011100', attr:['sspopchk x1','sspopchk x5']}, ], config:{lanes: 1, hspace:1024}} .... @@ -256,23 +304,15 @@ current top of the shadow stack followed by an increment of the `ssp` by {reg: [ {bits: 2, name: 'op', attr:'C1'}, {bits: 5, name: '00000'}, - {bits: 5, name: 'rs1', attr:['00001','00101']}, + {bits: 5, name: 'rs1', attr:['00101']}, {bits: 1, name: '0'}, - {bits: 3, name: '011', attr:['c.sspush x1', 'c.sspopchk x5']}, + {bits: 3, name: '011', attr:['c.sspopchk x5']}, ], config:{lanes: 1, hspace:1024}} .... -Only `x1` and `x5` encodings are supported as `rd` for `sslw` and `ssld`. -Only `x1` and `x5` encodings are supported as `rs1` for `sspopchk`. -Only `x1` and `x5` encodings are supported as `rs2` for `sspush`. - -Zicfiss provides 16-bit versions of the `sspush x1` and `sspopchk x5` -instructions using the Zcmop encodings. The `c.ssincp`, `c.sspush x1` and the -`c.sspopchk x5` instructions are encoded using the `C.LUI` major opcode and -using the `c.mop.0`, `c.mop.1`, and `c.mop.2` encodings. - -The `c.sspush x1` expands to `sspush x1` and `c.sspopchk x5` expands to -`sspopchk x5`. +Only `x1` and `x5` encodings are supported as `rs1` for `sspopchk`. Zicfiss +provides a 16-bit version of the `sspopchk x5` using Zcmop define `c.mop.2` +encoding. The `c.sspopchk x5` expands to `sspopchk x5`. Usually programs with a shadow stack push the return address onto the regular stack as well as the shadow stack in the function prologue of non-leaf @@ -282,27 +322,11 @@ the shadow stack. The two values are then compared. If the values do not match it is indicative of a corruption of the return address variable on the regular stack. -The `sspush` instruction and its compressed form `c.sspush` can be used, to push -a link register on the shadow stack. - The `sspopchk` instruction and its compressed form `c.sspopchk` can be used to pop the shadow return address value from the shadow stack and check that the value matches the contents of the link register and if not cause an integrity-fault exception with *tval set to "shadow stack fault (code=3)". -The `sslw` instruction can be used, when effective xlen is 32, to load a return -address from the shadow stack into a link register. - -The `ssld` instruction can be used, when effective xlen is 64, to load a return -address from the shadow stack into a link register. - -The `sslw` and `ssld` are both encoded identically. They have different mnemonics -to illustrate that the instructions operates on a _word_ when XLEN is 32 and on -a _doubleword_ when XLEN is 64. - -The `ssincp` instruction adds `XLEN/8` to the `ssp`. This instruction may be -used to remove a shadow stack frame from the shadow stack. - While any register may be used as link register, conventionally the `x1` or `x5` registers are used. The shadow stack instructions are designed to be most efficient when the `x1` and `x5` registers are used as the link register. @@ -358,88 +382,19 @@ The prologue and epilogue of a non-leaf function in shadow stack mode is as follows: [listing] +---- function_entry: addi sp,sp,-8 # push link register x1 sd x1,(sp) # on data stack - # - # Let the contents of ssp register be 0x0000000121679F8 and - # XLEN be 64 ssp register holds the address of the top of - # shadow stack. Let the contents of the link register x1 - # be 0x0000000010252000 - # - # 0x00000000121679E8:[ ] - # 0x00000000121679F0:[ ] - # 0x00000000121679F8:[0xrrrrrrrrrrrrrrrr] <- ssp - # sspush x1 # push link register x1 on shadow stack - # - # sspush store the source register value to address - # (ssp - XLEN/8) and updates ssp to (ssp - XLEN/8) - does - # a push. Following completion of # sspush the ssp value is - # the new top of stack i.e. 0x0000000121679F0 and the value - # in x1 is stored at this location - # - # 0x00000000121679E8:[ ] - # 0x00000000121679F0:[0x0000000010252000] <- ssp - # 0x00000000121679F8:[0xrrrrrrrrrrrrrrrr] - # : : ld x1,(sp) # pop link register x1 from data stack addi sp,sp,8 sspopchk x1 # compare link register x1 to shadow # return address; faults if not same - # - # sspopchk loads the value from location addressed by ssp and - # compares the loaded value to the value held in the register - # source and if the two are identical updates ssp to - # (ssp + XLEN/8) - does a pop and a check. Following - # completion of sspopchk the ssp value is the # new top of - # stack i.e. 0x00000000121679F8 - # - # 0x00000000121679E8:[ ] - # 0x00000000121679F0:[0x0000000010252000] - # 0x00000000121679F8:[0xrrrrrrrrrrrrrrrr] <- ssp - # - ret - -The prologue and epilogue of a non-leaf function when operating in control stack -mode is as follows: - -[listing] - function_entry: - # - # Let the contents of ssp register be 0x19740428 and XLEN be 32 - # ssp register holds the address of the top of shadow stack - # Let the contents of the link register x1 be 0x19791216 - # - # 0x19740418:[ ] - # 0x19740420:[ ] - # 0x19740428:[0xrrrrrrrr] <- ssp - # - sspush x1 # push link register x1 on shadow stack - # - # Following sspush the shadow stack and ssp are as follows: - # - # 0x19740418:[ ] - # 0x19740420:[0x19791216] <- ssp - # 0x19740428:[0xrrrrrrrr] - # - : - : - ssld x1 # load return address from shadow stack - ssincp # increment ssp by (XLEN/8) - # - # ssld loads the value from location addressed by ssp into - # destination register. ssincp updates ssp to (ssp + XLEN/8) - # - does a pop. Following completion of ssincp the ssp value - # is the new top of stack i.e. 0x19740428 - # - # 0x19740418:[ ] - # 0x19740420:[0x19791216] - # 0x19740428:[0xrrrrrrrr] <- ssp - # ret +---- These examples illustrate the use of `x1` register as the link register. Alternatively, the `x5` register may also be used as the link register. @@ -451,34 +406,17 @@ be held in the link register itself for the duration of the leaf function execution. ==== -The `c.sspopchk`, and `sspopchk` instructions perform a load -identically to the existing `LOAD` instruction, with the difference that the -base is implicitly `ssp` and the width is implicitly `XLEN`. - -The `sslw` instruction perform a load identically to the existing `LOAD` -instruction, with the difference that the base is implicitly `ssp` and the width -is implicitly a _word_. - -The `ssld` instruction perform a load identically to the existing `LOAD` -instruction, with the difference that the base is implicitly `ssp` and the width -is implicitly a _doubleword_. - -The `sspush` and `c.sspush` instructions performs a store identically to the -existing `STORE` instruction, with the difference that the base is implicitly +The `c.sspopchk`, and `sspopchk` instructions perform a load identically to the +existing `LOAD` instruction, with the difference that the base is implicitly `ssp` and the width is implicitly `XLEN`. -The `sspush`, `c.sspush`, `sspopchk`, `c.sspopchk`, `sslw`, and `ssld` require the -virtual address in `ssp` to have a shadow stack attribute (see <>). - -Correct execution of `sspush`, `c.sspush`, `sspopchk`, `c.sspopchk`, `sslw`, and `ssld` -require that `ssp` refers to idempotent memory. If the memory reference by -`ssp` is not idempotent, then the `sspush`/`c.sspush` instructions cause a -store/AMO access-fault exception, and the `sslw`/`ssld`/`sspopchk`/`c.sspopchk` -instructions cause a load access-fault exception. - -If the virtual address in `ssp` is not `XLEN` aligned, then the `sslw`/`ssld`/ -`sspopchk`/`c.sspopchk` instructions cause a load access-fault exception, and -the `sspush`/`c.sspush` instructions cause a store/AMO access-fault exception. +The `sspopchk` and `c.sspopchk` instructions require the virtual address in +`ssp` to have a shadow stack attribute (see <>). Correct execution of +`sspopchk` and `c.sspopchk` requires that `ssp` refers to idempotent memory. If +the memory reference by `ssp` is not idempotent, then the instructions cause a +load access-fault exception. If the virtual address in `ssp` is not `XLEN` +aligned, then `sspopchk` and `c.sspopchk` instructions cause a load access +fault exception [NOTE] ==== @@ -499,53 +437,6 @@ usage, and requiring memory referenced by `ssp` to be idempotent does not pose a significant restriction. ==== -The operation of the `sspush` and `c.sspush` instructions is as follows: - -.`sspush` and `c.sspush` operation -[listing] ----- -If (xSSE == 1) - mem[ssp - (XLEN/8)] = X(src) # Store src value to ssp - XLEN/8 - ssp = ssp - (XLEN/8) # decrement ssp by XLEN/8 -endif ----- - -The operation of the `sslw` instruction is as follows: - -.`sslw` operation -[listing] ----- -if (xSSE == 1) - X(dst) = mem[ssp] # Load dst with 32-bits from address in ssp - # Only x1 and x5 may be used as dst -else - X(dst) = 0 -endif ----- - -The operation of the `ssld` instruction is as follows: - -.`ssld` operation -[listing] ----- -if (xSSE == 1) - X(dst) = mem[ssp] # Load dst with 64-bits from address in ssp - # Only x1 and x5 may be used as dst -else - X(dst) = 0 -endif ----- - -The operation of the `ssincp` and `c.ssincp` instructions is as follows: - -.`ssincp` and `c.ssincp` operation -[listing] ----- -if (xSSE == 1) - ssp = ssp + XLEN/8 -endif ----- - The operation of the `sspopchk` and `c.sspopchk` instructions is as follows: .`sspopchk` and `c.sspopchk` operation @@ -596,6 +487,50 @@ jump to the return address. ==== + +[[SS_LOAD]] +=== Load from the shadow stack + +The `sslw` instruction can be used, when effective xlen is 32, to load a return +address from the shadow stack into a link register. The `ssld` instruction can +be used, when effective xlen is 64, to load a return address from the shadow +stack into a link register. + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 7, name: 'opcode', attr:'SYSTEM'}, + {bits: 5, name: 'rd', attr:['00001','00101','00001','00101']}, + {bits: 3, name: 'funct3', attr:['100']}, + {bits: 5, name: 'rs1', attr:[`00000','00000','00000','00000']}, + {bits: 12, name: '100000011100', attr:['sslw x1','sslw x5','ssld x1','ssld x5']}, +], config:{lanes: 1, hspace:1024}} +.... + +The `sslw` and `ssld` are both encoded identically. They have different +mnemonics to illustrate that the instructions operates on a _word_ when +XLEN is 32 and on a _doubleword_ when XLEN is 64. + +The `sslw` and `ssld` instructions require the virtual address in `ssp` to have +a shadow stack attribute (see <>). Correct execution of `sslw` and `ssld` +requires that `ssp` refers to idempotent memory. If the memory reference by +`ssp` is not idempotent, then the instructions cause a load access fault +exception. If the virtual address in `ssp` is not `XLEN` aligned, then `sslw` +and `ssld` instructions cause a load access fault exception + +The operation of the `sslw` and `ssld` instructions is as follows: + +.`sslw` and `ssld` operation +[listing] +---- +if (xSSE == 1) + X(dst) = mem[ssp] # Load dst with XLEN bits from address in ssp + # Only x1 and x5 may be used as dst +else + X(dst) = 0 +endif +---- + [NOTE] ==== Store-to-load forwarding is a common technique employed by high-performance @@ -607,51 +542,61 @@ and thus may be used to transiently forward the data from such stores to a `sslw`/`ssld`/`sspopchk`/`c.sspopchk`. ==== +[[SSP_INC]] +=== Increment the shadow stack pointer + +The `ssincp` instruction adds `XLEN/8` to the `ssp`. This instruction may be +used to remove a shadow stack frame from the shadow stack. Zicfiss provides a +16-bit version of the `ssincp` using Zcmop define `c.mop.0` encoding. The +`c.ssincp` expands to `ssincp`. + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 7, name: 'opcode', attr:'SYSTEM'}, + {bits: 5, name: 'rd', attr:['00000']}, + {bits: 3, name: 'funct3', attr:['100']}, + {bits: 5, name: 'rs1', attr:['00000']}, + {bits: 12, name: '100000011100', attr:['ssincp']}, +], config:{lanes: 1, hspace:1024}} +.... + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 2, name: 'op', attr:'C1'}, + {bits: 5, name: '00000'}, + {bits: 5, name: 'rs1', attr:['00000']}, + {bits: 1, name: '0'}, + {bits: 3, name: '011', attr:['c.ssincp']}, +], config:{lanes: 1, hspace:1024}} +.... + +The operation of the `ssincp` and `c.ssincp` instructions is as follows: + +.`ssincp` and `c.ssincp` operation +[listing] +---- +if (xSSE == 1) + ssp = ssp + XLEN/8 +endif +---- + [NOTE] ==== -A common operation performed on stacks is to unwind them to support constructs -like `setjmp`/`longjmp`, C++ exception handling, etc. A program that uses shadow -stacks must unwind the shadow stack in addition to the stack used to store data. -The unwind function must verify that it does not accidentally unwind past the -bounds of the shadow stack. Shadow stacks are expected to be bounded on each end -using guard pages, i.e. pages that do not have a shadow stack attribute. To -detect if the unwind occurs past the bounds of the shadow stack, the unwind may -be done in maximal increments of 4 KiB and testing for the `ssp` to be still -pointing to a shadow stack page or has unwound into the guard page. The -following examples illustrate the use of shadow stack instructions to -unwind a shadow stack. This example assumes that the `setjmp` function itself does -not push on to the shadow stack (being a leaf function, it is not required to). +The prologue and epilogue of a non-leaf function when operating in control stack +mode is as follows: [listing] -setjmp() { - : - : - // read and save the shadow stack pointer to jmp_buf - asm("ssrdp %0" : "=r"(cur_ssp):); - jmp_buf->saved_ssp = cur_ssp; - : - : -} -longjmp() { - : - // Read current shadow stack pointer and - // compute number of call frames to unwind - asm("ssrdp %0" : "=r"(cur_ssp):); - // Skip the unwind if backward-edge CFI not enabled - asm("beqz %0, back_cfi_not_enabled" : "=r"(cur_ssp):); - // Unwind the frames in a loop - while ( jmp_buf->saved_ssp > cur_ssp ) { - // advance by a maximum of 4K at a time to avoid - // unwinding past bounds of the shadow stack - cur_ssp = ( (jmp_buf->saved_ssp - cur_ssp) >= 4096 ) ? - (cur_ssp + 4096) : jmp_buf->saved_ssp; - asm("csrw ssp, %0" : : "r" (cur_ssp)); - // Test if unwound past the shadow stack bounds - asm("ssld x5"); - } -back_cfi_not_enabled: - : -} +---- + function_entry: + sspush x1 # push link register x1 on shadow stack + : + : + ssld x1 # load return address from shadow stack + ssincp # increment ssp by (XLEN/8) + ret +---- ==== [[SSP_READ]] @@ -708,6 +653,53 @@ Operating systems and runtimes must not locate shadow stacks at address 0 to assist with the use of such code sequences. ==== +[NOTE] +==== +A common operation performed on stacks is to unwind them to support constructs +like `setjmp`/`longjmp`, C++ exception handling, etc. A program that uses shadow +stacks must unwind the shadow stack in addition to the stack used to store data. +The unwind function must verify that it does not accidentally unwind past the +bounds of the shadow stack. Shadow stacks are expected to be bounded on each end +using guard pages, i.e. pages that do not have a shadow stack attribute. To +detect if the unwind occurs past the bounds of the shadow stack, the unwind may +be done in maximal increments of 4 KiB and testing for the `ssp` to be still +pointing to a shadow stack page or has unwound into the guard page. The +following examples illustrate the use of shadow stack instructions to +unwind a shadow stack. This example assumes that the `setjmp` function itself does +not push on to the shadow stack (being a leaf function, it is not required to). + +[listing] +setjmp() { + : + : + // read and save the shadow stack pointer to jmp_buf + asm("ssrdp %0" : "=r"(cur_ssp):); + jmp_buf->saved_ssp = cur_ssp; + : + : +} +longjmp() { + : + // Read current shadow stack pointer and + // compute number of call frames to unwind + asm("ssrdp %0" : "=r"(cur_ssp):); + // Skip the unwind if backward-edge CFI not enabled + asm("beqz %0, back_cfi_not_enabled" : "=r"(cur_ssp):); + // Unwind the frames in a loop + while ( jmp_buf->saved_ssp > cur_ssp ) { + // advance by a maximum of 4K at a time to avoid + // unwinding past bounds of the shadow stack + cur_ssp = ( (jmp_buf->saved_ssp - cur_ssp) >= 4096 ) ? + (cur_ssp + 4096) : jmp_buf->saved_ssp; + asm("csrw ssp, %0" : : "r" (cur_ssp)); + // Test if unwound past the shadow stack bounds + asm("ssld x5"); + } +back_cfi_not_enabled: + : +} +==== + [NOTE] ==== Stack switching is a common operation in user programs as well as supervisor @@ -727,10 +719,10 @@ stack and save the old shadow stack pointer on the old shadow stack is as follows: [listing] -# The a0 register holds the pointer to checkpoint at the top of the new shadow -# stack. x5 at end of the sequence has the address of the checkpoint created on -# current shadow stack. This should be saved away in a context structure to -# restore later. +# The a0 register holds the pointer to checkpoint at the top of the +# new shadow stack. x5 at end of the sequence has the address of the +# checkpoint created on current shadow stack. This should be saved +# away in a context structure to restore later. switch_shadow_stack: ssrdp t1 # read current ssp beqz t1, ss_not_enabled # skip if shadow stacks not enabled @@ -925,3 +917,4 @@ inaccessible for U-mode and S-mode read and write accesses. Allowing write access violates the integrity of the shadow stack, and allowing read access may lead to disclosure of M-mode return addresses. ==== + diff --git a/cfi_forward.adoc b/cfi_forward.adoc index 991ec82..07a66f6 100644 --- a/cfi_forward.adoc +++ b/cfi_forward.adoc @@ -1,4 +1,4 @@ -4[forward]] +[[forward]] == Landing pad (Zicfilp) To enforce forward-edge control-flow integrity, the Zicfilp extension introduces From b31742ff6047075476f40ee3501ce2b6f36fa0c5 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Fri, 11 Aug 2023 15:25:50 -0500 Subject: [PATCH 13/13] restructure for better readability --- cfi_backward.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfi_backward.adoc b/cfi_backward.adoc index 93d2903..3fd3405 100644 --- a/cfi_backward.adoc +++ b/cfi_backward.adoc @@ -502,7 +502,7 @@ stack into a link register. {bits: 7, name: 'opcode', attr:'SYSTEM'}, {bits: 5, name: 'rd', attr:['00001','00101','00001','00101']}, {bits: 3, name: 'funct3', attr:['100']}, - {bits: 5, name: 'rs1', attr:[`00000','00000','00000','00000']}, + {bits: 5, name: 'rs1', attr:['00000','00000','00000','00000']}, {bits: 12, name: '100000011100', attr:['sslw x1','sslw x5','ssld x1','ssld x5']}, ], config:{lanes: 1, hspace:1024}} ....