diff --git a/src/images/smepmp-visual-representation.png b/src/images/smepmp-visual-representation.png index 950227136..53564d9b8 100644 Binary files a/src/images/smepmp-visual-representation.png and b/src/images/smepmp-visual-representation.png differ diff --git a/src/images/wavedrom/mseccfg.edn b/src/images/wavedrom/mseccfg.edn index 7343fb328..b5c36287c 100644 --- a/src/images/wavedrom/mseccfg.edn +++ b/src/images/wavedrom/mseccfg.edn @@ -3,7 +3,7 @@ .... {reg: [ {bits: 1, name: 'MML'}, - {bits: 1, name: 'MMWP'}, + {bits: 1, name: 'MMAP'}, {bits: 1, name: 'RLB'}, {bits: 5, name: 'WPRI'}, {bits: 1, name: 'USEED'}, diff --git a/src/machine.adoc b/src/machine.adoc index 90af1459a..83100c6bd 100644 --- a/src/machine.adoc +++ b/src/machine.adoc @@ -2241,10 +2241,9 @@ include::images/wavedrom/mseccfg.edn[] The definitions of the SSEED and USEED fields are furnished by the entropy-source extension, Zkr. -The definitions of the RLB, MMWP, and MML fields are furnished by the -PMP-enhancement extension, Smepmp. +The RLB, MMWP, and MML fields are defined in <>. -The definition of the PMM field is furnished by the Smmpm extension. +The PMM field is defined in <>. The Zicfilp extension adds the `MLPE` field in `mseccfg`. When `MLPE` field is 1, Zicfilp extension is enabled in M-mode. When the `MLPE` field is 0, the @@ -2519,9 +2518,9 @@ as described in <>. For implementations with the "A" standard extension, there is no valid load reservation. The `pc` is set to an implementation-defined reset vector. The `mcause` register is set to a value indicating the cause of -the reset. Writable PMP registers’ A and L fields are set to 0, unless -the platform mandates a different reset value for some PMP registers’ A -and L fields. If the hypervisor extension is implemented, the +the reset. Writable PMP registers’ A and L fields, and `mseccfg`.MML/MMAP/RLB +fields are set to 0, unless the platform mandates a pre-defined PMP ruleset. +If the hypervisor extension is implemented, the `hgatp`.MODE and `vsatp`.MODE fields are reset to 0. If the Smrnmi extension is implemented, the `mnstatus`.NMIE field is reset to 0. No *WARL* field contains an illegal value. If the Zicfilp extension is @@ -3221,6 +3220,7 @@ If the current XLEN is greater than MXLEN, the PMP address registers are zero-extended from MXLEN to XLEN bits for the purposes of address matching. +[[pmp-locking]] ===== Locking and Privilege Mode The L bit indicates that the PMP entry is locked, i.e., writes to the diff --git a/src/riscv-privileged.adoc b/src/riscv-privileged.adoc index b8e67dc44..39daf2bf2 100644 --- a/src/riscv-privileged.adoc +++ b/src/riscv-privileged.adoc @@ -61,11 +61,11 @@ editors to suggest corrections): Krste Asanović, Peter Ashenden, Rimas Avižienis, Jacob Bachmeyer, Allen J. Baum, Jonathan Behrens, Paolo Bonzini, Ruslan Bukin, Christopher Celio, Chuanhua Chang, David Chisnall, Anthony Coulter, Palmer Dabbelt, Monte Dalrymple, Paul Donahue, Greg Favor, Dennis Ferguson, Marc Gauthier, Andy Glew, -Gary Guo, Mike Frysinger, John Hauser, David Horner, Olof -Johansson, David Kruckemyer, Yunsup Lee, Daniel Lustig, Andrew Lutomirski, Martin Maas, Prashanth Mundkur, Jonathan Neuschäfer, Rishiyur -Nikhil, Stefan O'Rear, Albert Ou, John Ousterhout, David Patterson, Dmitri +Gary Guo, Mike Frysinger, John Hauser, David Horner, Olof Johansson, Nick Kossifidis, David Kruckemyer, +Yunsup Lee, Daniel Lustig, Andrew Lutomirski, Martin Maas, Prashanth Mundkur, +Jonathan Neuschäfer, Rishiyur Nikhil, Stefan O'Rear, Albert Ou, John Ousterhout, David Patterson, Dmitri Pavlov, Kade Phillips, Josh Scheid, Colin Schmidt, Michael Taylor, Wesley Terpstra, Matt Thomas, Tommy Thorn, Ray -VanDeWalker, Megan Wachs, Steve Wallach, Andrew Waterman, Claire Wolf, Adam Zabrocki, +VanDeWalker, Megan Wachs, Steve Wallach, Andrew Waterman, Claire Wolf, Joe Xie, Adam Zabrocki, and Reinoud Zandijk.._ _This document is released under a Creative Commons Attribution 4.0 International License._ diff --git a/src/smepmp.adoc b/src/smepmp.adoc index 45e109dae..9b1945ccc 100644 --- a/src/smepmp.adoc +++ b/src/smepmp.adoc @@ -2,73 +2,36 @@ == "Smepmp" Extension for PMP Enhancements for memory access and execution prevention in Machine mode, Version 1.0 === Introduction -Being able to access the memory of a process running at a high privileged execution mode, such as the Supervisor or Machine mode, from a lower privileged mode such as the User mode, introduces an obvious attack vector since it allows for an attacker to perform privilege escalation, and tamper with the code and/or data of that process. A less obvious attack vector exists when the reverse happens, in which case an attacker instead of tampering with code and/or data that belong to a high-privileged process, can tamper with the memory of an unprivileged / less-privileged process and trick the high-privileged process to use or execute it. +The Smepmp extension enhances the xref:pmp[xrefstyle=basic] mechanism to improve Machine mode's security, by providing stronger memory isolation and privilege separation. It enables preventing the execution of code from memory regions not explicitly marked as executable by Machine mode, and restricts Machine mode's access to memory regions designated for Supervisor and User modes. These capabilities help to mitigate control-flow subversion attacks that exploit execution of unauthorized code with Machine mode privileges, and provide security guarantees consistent with the mechanism described in <> for memory access and execution prevention between Supervisor and User mode. -To prevent this attack vector, two mechanisms known as Supervisor Memory Access Prevention (SMAP) and Supervisor Memory Execution Prevention (SMEP) were introduced in recent systems. The first one prevents the OS from accessing the memory of an unprivileged process unless a specific code path is followed, and the second one prevents the OS from executing the memory of an unprivileged process at all times. RISC-V already includes support for SMAP, through the ``sstatus.SUM`` bit, and for SMEP by always denying execution of virtual memory pages marked with the U bit, with Supervisor mode (OS) privileges, as mandated on the Privilege Spec. +Additionally, this extension provides a set of mechanisms that allow for more flexible and efficient use of the PMP configuration registers, especially during early boot. +All mechanisms defined in this extension utilize the xref:mseccfg[xrefstyle=basic], and the existing PMP registers. No new CSRs are defined. All mseccfg fields defined here are WARL. + +For a more thorough discussion on the subject, please refer to the link:https://github.com/riscvarchive/riscv-tee/blob/main/Smepmp/Smepmp.pdf[ePMP proposal document]. [NOTE] +.Terms used in this specification for clarity ==== -Terms: - * *PMP Entry*: A pair of ``pmpcfg[i]`` / ``pmpaddr[i]`` registers. -* *PMP Rule*: The contents of a pmpcfg register and its associated pmpaddr register(s), that encode a valid protected physical memory region, where ``pmpcfg[i].A != OFF``, and if ``pmpcfg[i].A == TOR``, ``pmpaddr[i-1] < pmpaddr[i]``. -* *Ignored*: Any permissions set by a matching PMP rule are ignored, and _all_ accesses to the requested address range are allowed. -* *Enforced*: Only access types configured in the PMP rule matching the requested address range are allowed; failures will cause an access-fault exception. -* *Denied*: Any permissions set by a matching PMP rule are ignored, and _no_ accesses to the requested address range are allowed.; failures will cause an access-fault exception. -* *Locked*: A PMP rule/entry where the ``pmpcfg.L`` bit is set. -* *PMP reset*: A reset process where all PMP settings of the hart, including locked rules/settings, are re-initialized to a set of safe defaults, before releasing the hart (back) to the firmware / OS / application. -==== - -==== Threat model - -However, there are no such mechanisms available on Machine mode in the current (v1.11) Privileged Spec. It is not possible for a PMP rule to be *enforced* only on non-Machine modes and *denied* on Machine mode, to only allow access to a memory region by less-privileged modes. It is only possible to have a *locked* rule that will be *enforced* on all modes, or a rule that will be *enforced* on non-Machine modes and be *ignored* by Machine mode. So for any physical memory region which is not protected with a Locked rule, Machine mode has unlimited access, including the ability to execute it. - -Without being able to protect less-privileged modes from Machine mode, it is not possible to prevent the mentioned attack vector. This becomes even more important for RISC-V than on other architectures, since implementations are allowed where a hart only has Machine and User modes available, so the whole OS will run on Machine mode instead of the non-existent Supervisor mode. In such implementations the attack surface is greatly increased, and the same kind of attacks performed on Supervisor mode and mitigated through SMAP/SMEP, can be performed on Machine mode without any available mitigations. Even on implementations with Supervisor mode present attacks are still possible against the Firmware and/or the Secure Monitor running on Machine mode. - -[[proposal]] -=== Proposal - -. *Machine Security Configuration (mseccfg)* is a new RW Machine mode CSR, used for configuring various security mechanisms present on the hart, and only accessible to Machine mode. It is 64 bits wide, and is at address *0x747 on RV64* and *0x747 (low 32bits), 0x757 (high 32bits) on RV32*. All mseccfg fields defined on this proposal are WARL, and the remaining bits are reserved for future standard use and should always read zero. The reset value of mseccfg is implementation-specific, otherwise if backwards compatibility is a requirement it should reset to zero on hard reset. - -. On ``mseccfg`` we introduce a field on bit 2 called *Rule Locking Bypass (mseccfg.RLB)* with the following functionality: -+ -.. When ``mseccfg.RLB`` is 1 *locked* PMP rules may be removed/modified and *locked* PMP entries may be edited. - -.. When ``mseccfg.RLB`` is 0 and ``pmpcfg.L`` is 1 in any rule or entry (including disabled entries), then ``mseccfg.RLB`` remains 0 and any further modifications to ``mseccfg.RLB`` are ignored until a *PMP reset*. -+ -[CAUTION] +* *PMP Rule*: The contents of a pmpcfg register and its associated pmpaddr register(s), that encode a valid protected physical memory region, according to <>, where ``pmpcfg[i].A != OFF``, and if ``pmpcfg[i].A == TOR``, ``pmpaddr[i-1] < pmpaddr[i]``. +* *Locked*: A PMP rule/entry where the ``pmpcfg.L`` bit is set and the locking behavior mandated in <> applies. ==== -Note that this feature is intended to be used as a debug mechanism, or as a temporary workaround during the boot process for simplifying software, and optimizing the allocation of memory and PMP rules. Using this functionality under normal operation, after the boot process is completed, should be avoided since it weakens the protection of _M-mode-only_ rules. Vendors who don’t need this functionality may hardwire this field to 0. -==== - -. On ``mseccfg`` we introduce a field in bit 1 called *Machine-Mode Allowlist Policy (mseccfg.MMWP)*. This is a sticky bit, meaning that once set it cannot be unset until a *PMP reset*. When set it changes the default PMP policy for M-mode when accessing memory regions that don’t have a matching PMP rule, to *denied* instead of *ignored*. - -. On ``mseccfg`` we introduce a field in bit 0 called *Machine Mode Lockdown (mseccfg.MML)*. This is a sticky bit, meaning that once set it cannot be unset until a *PMP reset*. When ``mseccfg.MML`` is set the system's behavior changes in the following way: -.. The meaning of ``pmpcfg.L`` changes: Instead of marking a rule as *locked* and *enforced* in all modes, it now marks a rule as *M-mode-only* when set and *S/U-mode-only* when unset. The formerly reserved encoding of ``pmpcfg.RW=01``, and the encoding ``pmpcfg.LRWX=1111``, now encode a *Shared-Region*. -+ -An _M-mode-only_ rule is *enforced* on Machine mode and *denied* in Supervisor or User mode. It also remains *locked* so that any further modifications to its associated configuration or address registers are ignored until a *PMP reset*, unless ``mseccfg.RLB`` is set. -+ -An _S/U-mode-only_ rule is *enforced* on Supervisor and User modes and *denied* on Machine mode. -+ -A _Shared-Region_ rule is *enforced* on all modes, with restrictions depending on the ``pmpcfg.L`` and ``pmpcfg.X`` bits: -+ -* A _Shared-Region_ rule where ``pmpcfg.L`` is not set can be used for sharing data between M-mode and S/U-mode, so is not executable. M-mode has read/write access to that region, and S/U-mode has read access if ``pmpcfg.X`` is not set, or read/write access if ``pmpcfg.X`` is set. -+ -* A _Shared-Region_ rule where ``pmpcfg.L`` is set can be used for sharing code between M-mode and S/U-mode, so is not writeable. Both M-mode and S/U-mode have execute access on the region, and M-mode also has read access if ``pmpcfg.X`` is set. The rule remains *locked* so that any further modifications to its associated configuration or address registers are ignored until a *PMP reset*, unless ``mseccfg.RLB`` is set. -+ -* The encoding ``pmpcfg.LRWX=1111`` can be used for sharing data between M-mode and S/U mode, where both modes only have read-only access to the region. The rule remains *locked* so that any further modifications to its associated configuration or address registers are ignored until a *PMP reset*, unless ``mseccfg.RLB`` is set. +[[smepmp-mml]] +=== Machine Mode Lockdown (MML) +The ``mseccfg.MML`` bit changes the interpretation of the ``pmpcfg.L`` bit defined in <>. It is a sticky bit: once set, it cannot be unset until xref:reset[xrefstyle=basic]. -.. Adding a rule with executable privileges that either is *M-mode-only* or a *locked* *Shared-Region* is not possible and such ``pmpcfg`` writes are ignored, leaving ``pmpcfg`` unchanged. This restriction can be temporarily lifted by setting ``mseccfg.RLB`` e.g. during the boot process. +* When ``mseccfg.MML=0``, PMP operates according to the original specification, where the ``pmpcfg.L`` bit marks a rule as *Locked* and applicable in all privilege modes. -.. Executing code with Machine mode privileges is only possible from memory regions with a matching *M-mode-only* rule or a *locked* *Shared-Region* rule with executable privileges. Executing code from a region without a matching rule or with a matching _S/U-mode-only_ rule is *denied*. +* When ``mseccfg.MML=1``, the semantics of PMP rules change as follows: -.. If ``mseccfg.MML`` is not set, the combination of ``pmpcfg.RW=01`` remains reserved for future standard use. +** ``pmpcfg.L`` denotes an M-mode-only rule when set (applies only to accesses from M-mode), or an S/U-mode-only rule when clear (applies only to accesses from S/U-mode). When ``pmpcfg.L`` is set the rule is also *Locked*, unless ``mseccfg.RLB`` is also set. +** The formerly reserved encoding `pmpcfg.RW=01` (with various `pmpcfg.L` and `pmpcfg.X` settings) now encodes Shared-Region rules (that apply to accesses from all privilege modes) with varying permissions. If ``mseccfg.MML`` is not set, the encoding remains reserved for future standard use. -==== Truth table when mseccfg.MML is set +The following truth table shows the updated behavior in detail: [cols="^1,^1,^1,^1,^3,^3",stripes=even,options="header"] |=== @@ -77,94 +40,87 @@ A _Shared-Region_ rule is *enforced* on all modes, with restrictions depending o |{set:cellbgcolor:!} 0|0|0|0 2+|Inaccessible region (Access Exception) |0|0|0|1|Access Exception|Execute-only region |0|0|1|0 2+|Shared data region: Read/write on M mode, read-only on S/U mode -|0|0|1|1 2+|Shared data region: Read/write for both M and S/U mode +|0|0|1|1 2+|Shared data region: Read/write for both M and S/U modes |0|1|0|0|Access Exception|Read-only region |0|1|0|1|Access Exception|Read/Execute region |0|1|1|0|Access Exception|Read/Write region |0|1|1|1|Access Exception|Read/Write/Execute region -|1|0|0|0 2+|Locked inaccessible region* (Access Exception) -|1|0|0|1|Locked Execute-only region*|Access Exception -|1|0|1|0 2+|Locked Shared code region: Execute only on both M and S/U mode.* -|1|0|1|1 2+|Locked Shared code region: Execute only on S/U mode, read/execute on M mode.* -|1|1|0|0|Locked Read-only region*|Access Exception -|1|1|0|1|Locked Read/Execute region*|Access Exception -|1|1|1|0|Locked Read/Write region*|Access Exception -|1|1|1|1 2+|Locked Shared data region: Read only on both M and S/U mode.* +|1|0|0|0 2+|*Locked* inaccessible region (Access Exception) +|1|0|0|1|*Locked* Execute-only region|Access Exception +|1|0|1|0 2+|*Locked* Shared code region: Execute only on both M and S/U mode. +|1|0|1|1 2+|*Locked* Shared code region: Execute only on S/U mode, read/execute on M mode. +|1|1|0|0|*Locked* Read-only region|Access Exception +|1|1|0|1|*Locked* Read/Execute region|Access Exception +|1|1|1|0|*Locked* Read/Write region|Access Exception +|1|1|1|1 2+|*Locked* Shared data region: Read only on both M and S/U mode. |=== -*: *Locked* rules cannot be removed or modified until a *PMP reset*, unless ``mseccfg.RLB`` is set. - -==== Visual representation of the proposal - -image::smepmp-visual-representation.png[] +[TIP] +==== +Shared data regions may be used to expose static platform-specific information (e.g. ACPI tables or the device tree), used by both Machine and Supervisor modes, or exchange data between them without the overhead of copying them. -=== Smepmp software discovery +Shared code regions may be used for exposing vendor-specific functionality (e.g. with custom instructions) to Supervisor/User modes, without exposing that code, or risking the posibility of modifications (no write access). +==== -Since all fields defined on ``mseccfg`` as part of this proposal are locked when set (``MMWP``/``MML``) or locked when cleared (``RLB``), software can't poll them for determining the presence of Smepmp. It is expected that BootROM will set ``mseccfg.MMWP`` and/or ``mseccfg.MML`` during early boot, before jumping to the firmware, so that the firmware will be able to determine the presence of Smepmp by reading ``mseccfg`` and checking the state of ``mseccfg.MMWP`` and ``mseccfg.MML``. +Additionally, when `mseccfg.MML=1`: -[[rationale]] -=== Rationale +* Machine-mode code execution is only permitted from memory regions with a matching M-mode-only rule or a *Locked* Shared-Region rule with executable privileges. Attempting to execute from a region without a matching rule or with a matching S/U-mode-only rule raises an access exception. -. Since a CSR for security and / or global PMP behavior settings is not available with the current spec, we needed to define a new one. This new CSR will allow us to add further security configuration options in the future and also allow developers to verify the existence of the new mechanisms defined on this proposal. +* Adding a rule with executable privileges that is either M-mode-only or a *Locked* Shared-Region is not possible; such pmpcfg writes are ignored, leaving pmpcfg unchanged, unless ``mseccfg.RLB`` is also set. -. There are use cases where developers want to enforce PMP rules in M-mode during the boot process, that are also able to modify, merge, and / or remove later on. Since a rule that is enforced in M-mode also needs to be locked (or else badly written or malicious M-mode software can remove it at any time), the only way for developers to approach this is to keep adding PMP rules to the chain and rely on rule priority. This is a waste of PMP rules and since it’s only needed during boot, ``mseccfg.RLB`` is a simple workaround that can be used temporarily and then disabled and locked down. -+ -Also when ``mseccfg.MML`` is set, according to 4b it’s not possible to add a _Shared-Region_ rule with executable privileges. So RLB can be set temporarily during the boot process to register such regions. Note that it’s still possible to register executable _Shared-Region_ rules using initial register settings (that may include ``mseccfg.MML`` being set and the rule being set on PMP registers) on *PMP reset*, without using RLB. -+ -[WARNING] -==== -*Be aware that RLB introduces a security vulnerability if left set after the boot process is over and in general it should be used with caution, even when used temporarily.* Having editable PMP rules in M-mode gives a false sense of security since it only takes a few malicious instructions to lift any PMP restrictions this way. It doesn’t make sense to have a security control in place and leave it unprotected. Rule Locking Bypass is only meant as a way to optimize the allocation of PMP rules, catch errors during debugging, and allow the bootrom/firmware to register executable _Shared-Region_ rules. If developers / vendors have no use for such functionality, they should never set ``mseccfg.RLB`` and if possible hard-wire it to 0. In any case *RLB should be disabled and locked as soon as possible*. +[TIP] ==== -+ -[NOTE] -==== -If ``mseccfg.RLB`` is not used and left unset, it will be locked as soon as a PMP rule/entry with the ``pmpcfg.L`` bit set is configured. -==== -+ -[IMPORTANT] -==== -Since PMP rules with a higher priority override rules with a lower priority, locked rules must precede non-locked rules. +The first restriction ensures that execution prevention is always in effect after the MML bit is set, and the lockdown is enabled. + +The second restriction is a proactive measure to ensure that no new code can be injected (e.g. by a third-party malicious firmware component or some other vulnerability) after the firmware is initialized. ==== -. With the current spec M-mode can access any memory region unless restricted by a PMP rule with the ``pmpcfg.L`` bit set. There are cases where this approach is overly permissive, and although it’s possible to restrict M-mode by adding PMP rules during the boot process, this can also be seen as a waste of PMP rules. Having the option to block anything by default, and use PMP as an allowlist for M-mode is considered a safer approach. This functionality may be used during the boot process or upon *PMP reset*, using initial register settings. + +=== Machine-Mode Allowlist Policy (MMAP) -. The current dual meaning of the ``pmpcfg.L`` bit that marks a rule as Locked and *enforced* on all modes is neither flexible nor clean. With the introduction of _Machine Mode Lock-down_ the ``pmpcfg.L`` bit distinguishes between rules that are *enforced* *only* in M-mode (_M-mode-only_) or *only* in S/U-modes (_S/U-mode-only_). The rule locking becomes part of the definition of an _M-mode-only_ rule, since when a rule is added in M mode, if not locked, can be modified or removed in a few instructions. On the other hand, S/U modes can’t modify PMP rules anyway so locking them doesn’t make sense. +The ``msec.MMAP`` bit changes the default PMP policy for Machine mode when accessing memory regions that don't have a matching PMP rule. It is a sticky bit: once set, it cannot be unset until xref:reset[xrefstyle=basic]. -.. This separation between _M-mode-only_ and _S/U-mode-only_ rules also allows us to distinguish which regions are to be used by processes in Machine mode (``pmpcfg.L == 1``) and which by Supervisor or User mode processes (``pmpcfg.L == 0``), in the same way the U bit on the Virtual Memory’s PTEs marks which Virtual Memory pages are to be used by User mode applications (U=1) and which by the Supervisor / OS (U=0). With this distinction in place we are able to implement memory access and execution prevention in M-mode for any physical memory region that is not _M-mode-only_. -+ -An attacker that manages to tamper with a memory region used by S/U mode, even after successfully tricking a process running in M-mode to use or execute that region, will fail to perform a successful attack since that region will be _S/U-mode-only_ hence any access when in M-mode will trigger an access exception. -+ -[NOTE] -==== -In order to support zero-copy transfers between M-mode and S/U-mode we need to either allow shared memory regions, or introduce a mechanism similar to the ``sstatus.SUM`` bit to temporary allow the high-privileged mode (in this case M-mode) to be able to perform loads and stores on the region of a less-privileged process (in this case S/U-mode). In our case after discussion within the group it seemed a better idea to follow the first approach and have this functionality encoded on a per-rule basis to avoid the risk of leaving a temporary, global bypass active when exiting M-mode, hence rendering memory access prevention useless. -==== -+ +* When mseccfg.MMAP=0, Machine mode can access memory regions without a matching PMP rule (default behavior). -[NOTE] -==== -Although it’s possible to use ``mstatus.MPRV`` in M-mode to read/write data on an _S/U-mode-only_ region using general purpose registers for copying, this will happen with S/U-mode permissions, honoring any MMU restrictions put in place by S-mode. Of course it’s still possible for M-mode to tamper with the page tables and / or add _S/U-mode-only_ rules and bypass the protections put in place by S-mode but if an attacker has managed to compromise M-mode to such extent, no security guarantees are possible in any way. *Also note that the threat model we present here assumes buggy software in M-mode, not compromised software*. We considered disabling ``mstatus.MPRV`` but it seemed too much and out of scope. -==== -+ -_Shared-region_ rules can be used both for zero-copy data transfers and for sharing code segments. The latter may be used for example to allow S/U-mode to execute code by the vendor, that makes use of some vendor-specific ISA extension, without having to go through the firmware with an ecall. This is similar to the vDSO approach followed on Linux, that allows userspace code to execute kernel code without having to perform a system call. -+ -To make sure that shared data regions can’t be executed and shared code regions can’t be modified, the encoding changes the meaning of the ``pmpcfg.X bit``. In case of shared data regions, with the exception of the ``pmpcfg.LRWX=1111`` encoding, the ``pmpcfg.X`` bit marks the capability of S/U-mode to write to that region, so it’s not possible to encode an executable shared data region. In case of shared code regions, the ``pmpcfg.X`` bit marks the capability of M-mode to read from that region, and since ``pmpcfg.RW=01`` is used for encoding the shared region, it’s not possible to encode a shared writable code region. -+ -[NOTE] +* When mseccfg.MMAP=1, Machine mode cannot access memory regions without a matching PMP rule. + +[TIP] ==== -For adding _Shared-region_ rules with executable privileges to share code segments between M-mode and S/U-mode, ``mseccfg.RLB`` needs to be implemented, or else such rules can only be added together with ``mseccfg.MML`` being set on *PMP Reset*. That's because the reserved encoding ``pmpcfg.RW=01`` being used for _Shared-region_ rules is only defined when ``mseccfg.MML`` is set, and 4b prevents the addition of rules with executable privileges on M-mode after ``mseccfg.MML`` is set unless ``mseccfg.RLB`` is also set. +This mechanism not only provides a more strict access policy for Machine mode which otherwise has access everywhere unless restricted, it also allows for more efficient use of the available PMP registers, since there is no need to waste a rule for every region that should be blocked, such as regions used by peripherals / processors on the system, or e.g. the boot ROM after the handoff to firmware. ==== -+ -[NOTE] + +=== Rule Locking Bypass (RLB) + +The ``mseccfg.RLB`` bit provides a mechanism to temporarily modify *Locked* PMP rules: + +* When mseccfg.RLB=1, *Locked* PMP rules may be modified and *locked* PMP entries may be edited. + +* When mseccfg.RLB=0 and pmpcfg.L=1 in any rule or entry (including disabled entries), then mseccfg.RLB remains 0 and any further modifications to mseccfg.RLB are ignored until xref:reset[xrefstyle=basic]. + +[CAUTION] ==== -Using the ``pmpcfg.LRWX=1111`` encoding for a locked shared read-only data region was decided later on, its initial meaning was an M-mode-only read/write/execute region. The reason for that change was that the already defined shared data regions were not locked, so r/w access to M-mode couldn’t be restricted. In the same way we have execute-only shared code regions for both modes, it was decided to also be able to allow a least-privileged shared data region for both modes. This approach allows for example to share the .text section of an ELF with a shared code region and the .rodata section with a locked shared data region, without allowing M-mode to modify .rodata. We also decided that having a locked read/write/execute region in M-mode doesn’t make much sense and could be dangerous, since M-mode won’t be able to add further restrictions there (as in the case of S/U-mode where S-mode can further limit access to an ``pmpcfg.LWRX=0111`` region through the MMU), leaving the possibility of modifying an executable region in M-mode open. +*Be aware that RLB introduces a security vulnerability if left set after the boot process is over and in general it should be used with caution, even when used temporarily.* This feature is intended to be used as a debug mechanism, or as a temporary workaround during the boot process for simplifying software, and optimizing the allocation of memory and PMP rules. If developers / vendors have no use for such functionality, they should never set ``mseccfg.RLB`` and if possible hard-wire it to 0. In any case *RLB should be disabled and locked as soon as possible*. ==== -+ + [NOTE] ==== -For encoding Shared-region rules initially we used one of the two reserved bits on pmpcfg (bit 5) but in order to avoid allocating an extra bit, since those bits are a very limited resource, it was decided to use the reserved R=0,W=1 combination. +If ``mseccfg.RLB`` is not used and left unset, it will be locked as soon as a PMP rule/entry with the ``pmpcfg.L`` bit set is configured. ==== -.. The idea with this restriction is that after the Firmware or the OS running in M-mode is initialized and ``mseccfg.MML`` is set, no new code regions are expected to be added since nothing else is expected to run in M-mode (everything else will run in S/U mode). Since we want to limit the attack surface of the system as much as possible, it makes sense to disallow any new code regions which may include malicious code, to be added/executed in M-mode. -.. In case ``mseccfg.MMWP`` is not set, M-mode can still access and execute any region not covered by a PMP rule. Since we try to prevent M-mode from executing malicious code and since an attacker may manage to place code on some region not covered by PMP (e.g. a directly-addressable flash memory), we need to ensure that M-mode can only execute the code segments initialized during firmware / OS initialization. +=== Smepmp software discovery + +Since all fields defined on ``mseccfg`` under this proposal are either locked when set (``MMAP``/``MML``) or locked when cleared (``RLB``), software cannot dynamically query them to reliably determine the presence of Smepmp. It is expected that implementation-specific boot code will set ``mseccfg.MMAP`` and/or ``mseccfg.MML`` during early boot stages, before transferring control to the generic firmware implementation (e.g. OpenSBI). The firmware can then determine the presence of Smepmp by reading ``mseccfg`` and verifying the state of ``mseccfg.MMAP`` and ``mseccfg.MML``. Alternatively a software-defined discovery mechanism may be used. + + +=== Notable implementation considerations -.. We are only using the encoding ``pmpcfg.RW=01`` together with ``mseccfg.MML``, if ``mseccfg.MML`` is not set the encoding remains usable for future use. +* For adding shared code regions, ``mseccfg.RLB`` needs to be implemented, or else such rules can only be added together with ``mseccfg.MML`` being set, as part of a pre-defined ruleset on reset. That's because the reserved encoding ``pmpcfg.RW=01`` being used for shared region rules is only defined when ``mseccfg.MML`` is set, and <> prevents the addition of rules with executable privileges on M-mode after ``mseccfg.MML`` is set unless ``mseccfg.RLB`` is also set. + +* The RLB feature should be used with caution, primarily during debugging or early boot, and should be disabled as soon as possible in production environments. + +* Since PMP rules with a higher priority override rules with a lower priority, *Locked* rules must precede non-Locked rules. Otherwise the unlocked rules with a higher priority may be modified afterwards and compromise the ruleset. + +* For optimal security, systems should initialize both mseccfg.MMAP and mseccfg.MML to 1 as early as possible during boot. + +=== Visual representation of Smepmp + +image::smepmp-visual-representation.png[]