Skip to content

Commit

Permalink
editorial updates
Browse files Browse the repository at this point in the history
  • Loading branch information
ved-rivos committed Oct 31, 2023
1 parent 7068ff2 commit 421d704
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 114 deletions.
140 changes: 68 additions & 72 deletions cfi_backward.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
== Shadow Stack (Zicfiss)

The Zicfiss extension introduces a shadow stack to enforce backward-edge
control-flow integrity, A shadow stack is a second stack used to store a
control-flow integrity. A shadow stack is a second stack used to store a
shadow copy of the return address in the link register if it needs to be
spilled.

Expand All @@ -18,10 +18,10 @@ if required, by non-leaf functions. An additional register, shadow-stack-pointer
(`ssp`), is introduced in the architecture to hold the address of the top of the
active shadow stack.

The shadow stack, similar to the regular stack, grows downwards, i.e. from higher
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.
The shadow stack, similar to the regular stack, grows downwards, i.e., from
higher 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.

The shadow stack is architecturally protected from inadvertent corruptions and
modifications, as detailed later (See <<SSMP>>).
Expand All @@ -36,44 +36,45 @@ When Zicfiss is enabled, each function that needs to spill the link register
and a shadow copy of the link register value to the shadow stack when the
function is entered (the prologue). When such a function returns (the
epilogue), the function loads the link register from the regular stack and
the shadow copy of the link register from the shadow stack. Then, the link register
value from the regular stack and the shadow link register value from the shadow
stack are compared. A mismatch of the two values is indicative of a subversion
of the return address control variable and causes a software-check exception.
the shadow copy of the link register from the shadow stack. Then, the link
register value from the regular stack and the shadow link register value from
the shadow stack are compared. A mismatch of the two values is indicative of a
subversion of the return address control variable and causes a software-check
exception.

The Zicfiss instructions are encoded using a subset of "May be op" instructions
defined by the Zimop and Zcmop extensions cite:[ZIMOP]. This subset of
instructions revert to their Zimop/Zcmop defined behavior when the Zicfiss
extension is not implemented or if the extension has not been activated at a
privilege mode. A program that is built with Zicfiss instructions can thus
continue to operate correctly, but without backward-edge control-flow integrity,
on processors that do not support the Zicfiss extension or if the Zicfiss
extension is not active. The Zicfiss extensions may be activated for use
individually and independently for each privilege mode.

Compilers should flag each object file (for example, using flags in the elf
extension is not implemented or if the extension has not been activated. A
program that is built with Zicfiss instructions can thus continue to operate
correctly, but without backward-edge control-flow integrity, on processors that
do not support the Zicfiss extension or if the Zicfiss extension is not active.
The Zicfiss extension may be activated for use individually and independently
for each privilege mode.

Compilers should flag each object file (for example, using flags in the ELF
attributes) to indicate if the object file has been compiled with the Zicfiss
instructions. The linker should flag (for example, using flags in the elf
instructions. The linker should flag (for example, using flags in the ELF
attributes) the binary/executable generated by linking objects as being
compiled with the Zicfiss instructions only if all the object files that are
linked have the same Zicfiss attributes.
linked, have the same Zicfiss attributes.

The dynamic loader should activate the use of Zicfiss extension for an
application only if all executables (the application and the dependent
dynamically linked libraries) used by that application use the Zicfiss
dynamically-linked libraries) used by that application use the Zicfiss
extension.

An application that has the Zicfiss extension active may request the dynamic
loader at runtime to load a new dynamic shared object (using dlopen() for
example). If the requested object does not have the Zicfiss attribute then
the dynamic loader, based on its policy (e.g, established by the operating
the dynamic loader, based on its policy (e.g., established by the operating
system or the administrator) configuration, could either deny the request or
deactivate the Zicfiss extension for the application. It is strongly recommended
that the policy enforces a strict security posture and denies the request.

When the Zicfiss extension is not active or not implemented, the Zicfiss
instructions revert to their Zimop/Zcmop defined behavior. This allows a
compiled with Zicfiss instructions to operate correctly but without
program compiled with Zicfiss instructions to operate correctly but without
backward-edge control-flow integrity.

The Zicfiss extension has dependencies on the following extensions: A, Zicsr,
Expand Down Expand Up @@ -129,8 +130,7 @@ This section specifies the CSR state of the Zicfiss extensions.
....

The Zicfiss extension adds the `SSE` field (bit 3) to `menvcfg`. When the `SSE`
field is set to 1 and S-mode is implemented, the Zicfiss extension is enabled in
S-mode.
field is set to 1 the Zicfiss extension is enabled in S-mode.

When `SSE` field is 0, the following rules apply to privilege modes that are
less than M:
Expand Down Expand Up @@ -164,6 +164,8 @@ the following rules apply:
* 32-bit Zicfiss instructions will revert to their behavior as defined by Zimop.
* 16-bit Zicfiss instructions will revert to their behavior as defined by Zcmop.

<<<

==== Hypervisor environment configuration register (`henvcfg`)

.Hypervisor environment configuration register (`henvcfg`)
Expand Down Expand Up @@ -228,7 +230,7 @@ When S-mode is implemented, it is determined as follows:
[width=100%]
[%header, cols="^4,^12"]
|===
|Privilege Mode| xSSE
|Privilege Mode| `xSSE`
| M | `0`
| S or HS | `menvcfg.SSE`
| VS | `henvcfg.SSE`
Expand Down Expand Up @@ -268,17 +270,17 @@ illegal-instruction exception. Execution of programs that use these
instructions on such machines is not supported.
Activating Zicfiss in M-mode is currently not supported. Additionally, when
S-mode is not implemented, activation in U-mode activation is also not
supported. These functionalities may be introduced in a future standard
extension.
S-mode is not implemented, activation in U-mode is also not supported. These
functionalities may be introduced in a future standard extension.
====

<<<

[[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 push operation is defined as decrement of the `ssp` by `XLEN/8`
followed by a store of the value in the link register to memory at the new top
of the shadow stack.

[wavedrom, ,svg]
....
Expand All @@ -305,7 +307,7 @@ followed by a write of the link register at the new top of the shadow stack.
], config:{lanes: 1, hspace:1024}}
....

Only `x1` and `x5` encodings are supported as `rs2` for `SSPUSH`. Zicfiss
Only `x1` and `x5` registers 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`.

Expand Down Expand Up @@ -339,9 +341,9 @@ shadow stack completes successfully.
[[SS_POP]]
=== Pop from the shadow stack

A shadow stack pop operation is defined as a `XLEN` wide read from the
A shadow stack pop operation is defined as an `XLEN` wide read from the
current top of the shadow stack followed by an increment of the `ssp` by
`XLEN`.
`XLEN/8`.

[wavedrom, ,svg]
....
Expand All @@ -367,17 +369,16 @@ 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 `rs1` for `SSPOPCHK`. Zicfiss
Only `x1` and `x5` registers are supported as `rs1` for `SSPOPCHK`. Zicfiss
provides a 16-bit version of the `SSPOPCHK x5` using Zcmop define `C.MOP.5`
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
functions. Such programs when returning from the non-leaf function pop the link
register from the regular stack and pop a shadow copy of the link register from
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.
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 functions. Such
programs when returning from the non-leaf function pop the link register from
the regular stack and pop a shadow copy of the link register from 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 `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
Expand All @@ -404,15 +405,12 @@ using any other register as a link register would incur the cost of additional
register movements.
Compilers when generating code with backward-edge CFI must protect the link
register, e.g. `x1` and/or `x5`, from arbitrary modification by not emitting
register, e.g., `x1` and/or `x5`, from arbitrary modification by not emitting
unsafe code sequences.
====

[NOTE]
====
When shadow stack is active, programs store the return addresses on both the
regular stack and the shadow stack in the function prologue, and then pop them
them from both stacks and compare the values before returning from the function.
Storing the return address on both stacks preserves the call stack layout and
the ABI, while also allowing for the detection of corruption of the return
address on the regular stack.
Expand Down Expand Up @@ -574,18 +572,17 @@ endif
[NOTE]
====
The property of Zimop writing 0 to the `rd` when the extension using Zimop is
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.
not implemented or not active may be used by to determine if Zicfiss extension
is active. For example, functions that unwind shadow stacks may skip over the
unwind actions by dynamically detecting if the Zicfiss extension is active.
An example sequence such as the following may be used:
[listing]
ssrdp t0 # mv ssp to t0
beqz t0, zicfiss_not_enabled # zero is not a valid shadow stack
beqz t0, zicfiss_not_active # zero is not a valid shadow stack
# pointer by convention
# Zicfiss is enabled
# Zicfiss is active
:
:
zicfiss_not_active:
Expand All @@ -603,7 +600,7 @@ 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
be done in maximal increments of 4 KiB, testing whether the `ssp` is 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
Expand All @@ -624,8 +621,8 @@ 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):);
// Skip the unwind if backward-edge CFI not active
asm("beqz %0, back_cfi_not_active" : "=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
Expand All @@ -637,16 +634,16 @@ longjmp() {
asm("sspush x5");
asm("sspopchk x5");
}
back_cfi_not_enabled:
back_cfi_not_active:
:
}
====

[[SSAMOSWAP]]
=== 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
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.

Expand Down Expand Up @@ -694,11 +691,10 @@ aligned for __quadwords__, eight-byte aligned for __doublewords__, and four-byte
aligned for __words__). And the same exception options apply if the address is
not naturally aligned.

Just as for AMOs in the A extension, the `SSAMOSWAP` optionally provide release
Just as for AMOs in the A extension, the `SSAMOSWAP` optionally provides release
consistency semantics, using the `aq` and `rl` bits, to help implement
multiprocessor synchronization. The memory operation performed by an
`SSAMOSWAP`, has acquire semantics if `aq` bit is 1 and has release semantics if
`rl` bit is 1.
`SSAMOSWAP`, has acquire semantics if `aq=1` and has release semantics if `rl=1`.

[NOTE]
====
Expand Down Expand Up @@ -769,6 +765,7 @@ The `SSAMOSWAP`, `SSPUSH`, and `C.SSPUSH` instructions perform a store. The
When the value of `satp.MODE` (or `vsatp.MODE` when `V=1`) is set to `Bare`
and the effective privilege mode is less than M, shadow stack memory accesses
are disallowed. Under these conditions:

* The `SSPUSH`, `SSAMOSWAP`, and `C.SSPUSH` instructions will result in a
store/AMO access-fault exception.
* The `SSPOPCHK` and `C.SSPOPCHK` instructions will result in a load
Expand All @@ -784,7 +781,7 @@ to the original access type.
The shadow stack can be read using all instructions that load from memory.

The encoding `R=0`, `W=1`, and `X=0`, is defined to represent a shadow stack
page. When `menvcfg.SSE=0`, this encoding remains reserved. When `V=1` and
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:
Expand All @@ -810,17 +807,16 @@ 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 `SSAMOSWAP, `SSPUSH`
(which is a fatal condition) vs. a page fault caused by an `SSAMOSWAP`, `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
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 store is being made to a shadow stack
page.
access-fault exception, a trap handler that emulates misaligned stores must
cause an access-fault exception if 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
Expand All @@ -835,8 +831,8 @@ either single-stage or VS-stage address translation.
====

To support these rules, the virtual address translation process specified in
section 4.3.2 of the Privileged Specification cite:[PRIV] is modified as
follows:
section "Virtual Address Translation Process" of the Privileged Specification
cite:[PRIV] is modified as follows:
[start=3]
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
Expand Down Expand Up @@ -891,7 +887,7 @@ because shadow stack loads and stores to non-shadow-stack pages cause an
access-fault exception.
====

The G-stage address translation and protections remain unaffected by Zicfiss
The G-stage address translation and protections remain unaffected by the Zicfiss
extension. When G-stage page tables are active, the `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
Expand All @@ -907,8 +903,8 @@ its guests.

==== PMP extension for shadow stack

Attempts by `SSAMOSWAP`, `SSPUSH` and `C.SSPUSH` to a PMP region that does not
have write permission raises a store access-fault exception. Attempts by
`C.SSPOPCHK` and `SSPOPCHK` to access a PMP region that does not have read
permission raises a load access-fault exception.
Attempts by `SSAMOSWAP`, `SSPUSH` and `C.SSPUSH` to access a PMP region that
does not provide write permission raises a store access-fault exception.
Attempts by `C.SSPOPCHK` and `SSPOPCHK` to access a PMP region that does not
provide read permission raises a load access-fault exception.

Loading

0 comments on commit 421d704

Please sign in to comment.