New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Kernel Memory Protection #3599
Comments
Wow, that is a nasty bug!
Yeah, that's fair. Maybe a priority argument could help boards fix this up
That's a hard problem to get right though. The hope is that a board can work around those. In theory the current API could work, as it could probe existing locked regions and then not configure regions already covered by them.
That must just be some bad documentation, because the kernel regions should not be changed at run time. The documentation for Overall I think the interface can be improved. One interesting thing would be to update it to support improvements to the ARM MPU so the interface could be used on ARM as well. Obviously fixing the bug you mentioned is critical as well. Another interesting goal would be to try and enable it more by default. I can't remember if the ARM MPU can support this, but it would be nice to enable at least some basic W^X protections for all boards. |
As discussed in issue tock#3599 [1] and PR tock#3597 [2], the `KernelMPU` trait is not a particularly good abstraction for implementing a memory protection mechanism also affecting the kernel. Some of its issues are: - It does not account for the complex interplay between regions affecting userspace, the kernel, or (depending on the hardware) both. Rule precedence is implicit and implementation dependent in this API: for example, to set up a read-only flash region with a read-execute kernel .text section, on RISC-V you would first need to configure the larger flash region, and then the .text section. This assumes that the implementation will set up the PMP entries in reverse-order, such that entries added later precede earlier ones. - The interface does not adequately account for pre-locked regions in the memory-protection implementation. These regions may alias some of the memory to be protected, hence entry placement relative to those locked regions is important. - Looking at the allocate_kernel_region method documentation, statements such as "note that kernel level permissions also apply to apps" are, for instance, simply not true with the ePMP MML mode. - The current interface seems to suggest that the kernel is able to re-configure (or at least add regions) to the kernel MPU at runtime. This collides with the semantics exposed by the RISC-V PMP (non-ePMP) which enforces all locked entries for both the kernel and user-space. It requires a special "deny-all" user-mode entry after all other user-mode regions to properly limit user-mode access. With the introduction of the `KernelProtectionPMP` and `EarlGreyEPMP`, which both implement some form of kernel-mode memory protection, but take their memory regions as arguments in their constructor, we do not need the `KernelMPU` trait any longer. At some point it might make sense to resurrect this trait with a clearer and portable set of API semantics. [1]: tock#3599 [2]: tock#3597
As discussed in issue tock#3599 [1] and PR tock#3597 [2], the `KernelMPU` trait is not a particularly good abstraction for implementing a memory protection mechanism also affecting the kernel. Some of its issues are: - It does not account for the complex interplay between regions affecting userspace, the kernel, or (depending on the hardware) both. Rule precedence is implicit and implementation dependent in this API: for example, to set up a read-only flash region with a read-execute kernel .text section, on RISC-V you would first need to configure the larger flash region, and then the .text section. This assumes that the implementation will set up the PMP entries in reverse-order, such that entries added later precede earlier ones. - The interface does not adequately account for pre-locked regions in the memory-protection implementation. These regions may alias some of the memory to be protected, hence entry placement relative to those locked regions is important. - Looking at the allocate_kernel_region method documentation, statements such as "note that kernel level permissions also apply to apps" are, for instance, simply not true with the ePMP MML mode. - The current interface seems to suggest that the kernel is able to re-configure (or at least add regions) to the kernel MPU at runtime. This collides with the semantics exposed by the RISC-V PMP (non-ePMP) which enforces all locked entries for both the kernel and user-space. It requires a special "deny-all" user-mode entry after all other user-mode regions to properly limit user-mode access. With the introduction of the `KernelProtectionPMP` and `EarlGreyEPMP`, which both implement some form of kernel-mode memory protection, but take their memory regions as arguments in their constructor, we do not need the `KernelMPU` trait any longer. At some point it might make sense to resurrect this trait with a clearer and portable set of API semantics. [1]: tock#3599 [2]: tock#3597
As discussed in issue tock#3599 [1] and PR tock#3597 [2], the `KernelMPU` trait is not a particularly good abstraction for implementing a memory protection mechanism also affecting the kernel. Some of its issues are: - It does not account for the complex interplay between regions affecting userspace, the kernel, or (depending on the hardware) both. Rule precedence is implicit and implementation dependent in this API: for example, to set up a read-only flash region with a read-execute kernel .text section, on RISC-V you would first need to configure the larger flash region, and then the .text section. This assumes that the implementation will set up the PMP entries in reverse-order, such that entries added later precede earlier ones. - The interface does not adequately account for pre-locked regions in the memory-protection implementation. These regions may alias some of the memory to be protected, hence entry placement relative to those locked regions is important. - Looking at the allocate_kernel_region method documentation, statements such as "note that kernel level permissions also apply to apps" are, for instance, simply not true with the ePMP MML mode. - The current interface seems to suggest that the kernel is able to re-configure (or at least add regions) to the kernel MPU at runtime. This collides with the semantics exposed by the RISC-V PMP (non-ePMP) which enforces all locked entries for both the kernel and user-space. It requires a special "deny-all" user-mode entry after all other user-mode regions to properly limit user-mode access. With the introduction of the `KernelProtectionPMP` and `EarlGreyEPMP`, which both implement some form of kernel-mode memory protection, but take their memory regions as arguments in their constructor, we do not need the `KernelMPU` trait any longer. At some point it might make sense to resurrect this trait with a clearer and portable set of API semantics. [1]: tock#3599 [2]: tock#3597
As discussed in issue tock#3599 [1] and PR tock#3597 [2], the `KernelMPU` trait is not a particularly good abstraction for implementing a memory protection mechanism also affecting the kernel. Some of its issues are: - It does not account for the complex interplay between regions affecting userspace, the kernel, or (depending on the hardware) both. Rule precedence is implicit and implementation dependent in this API: for example, to set up a read-only flash region with a read-execute kernel .text section, on RISC-V you would first need to configure the larger flash region, and then the .text section. This assumes that the implementation will set up the PMP entries in reverse-order, such that entries added later precede earlier ones. - The interface does not adequately account for pre-locked regions in the memory-protection implementation. These regions may alias some of the memory to be protected, hence entry placement relative to those locked regions is important. - Looking at the allocate_kernel_region method documentation, statements such as "note that kernel level permissions also apply to apps" are, for instance, simply not true with the ePMP MML mode. - The current interface seems to suggest that the kernel is able to re-configure (or at least add regions) to the kernel MPU at runtime. This collides with the semantics exposed by the RISC-V PMP (non-ePMP) which enforces all locked entries for both the kernel and user-space. It requires a special "deny-all" user-mode entry after all other user-mode regions to properly limit user-mode access. With the introduction of the `KernelProtectionPMP` and `EarlGreyEPMP`, which both implement some form of kernel-mode memory protection, but take their memory regions as arguments in their constructor, we do not need the `KernelMPU` trait any longer. At some point it might make sense to resurrect this trait with a clearer and portable set of API semantics. [1]: tock#3599 [2]: tock#3597
An increasing number of microcontrollers feature mechanisms to limit memory accesses not only within an unprivileged user-mode, but also in their privileged machine-mode. Examples for these memory protection units include the RISC-V ePMP, (up to a certain extend) the RISC-V PMP, and potentially the MPU in ARM Cortex-M systems with TrustZone-M.
Tock's security and threat-model relies in large parts on the compile-time guarantees of the Rust programming language. However, this does not mean that the kernel is immune to attacks from user-space. Even assuming a correct Rust compiler, the kernel's unsafe code or low-level assembly can introduce vulnerabilities where userspace may cause the kernel to overwrite arbitrary memory locations, or execute arbitrary userspace code in machine-mode. Thus, to reduce these attack surfaces, security-oriented chips such as the OpenTitan EarlGrey SoC utilize their ePMP to limit their machine-mode accessible memory sections and permissions (see #3597).
Issues with the current interface
Tock currently features the
kernel::platform::mpu::KernelMPU
interface to configure such kernel memory protection regions. However, this interface has a host of issues, for example:allocate_kernel_region
method documentation, statements such as "note that kernel level permissions also apply to apps" are, for instance, simply not true with the ePMP MML mode.Example of a
KernelMPU
mis-configurationThese unclear semantics result in behavior such as the following. When running the following
libtock-c
application on a system without kernel memory protection enabled (for example, the LiteX sim board), the application faults:However, when we now copy the kernel MPU setup code from the OpenTitan EarlGrey board initialization and apply it to the LiteX sim platform, we see the following:
The above example demonstrates that enabling the
KernelMPU
actually allows access to all of the configured kernel protection regions from user-mode. This is because locked PMP regions apply to user-mode and kernel mode. The PMP fails to add a "deny-all" fallback user-mode region with a higher priority than all kernel protection regions. Doing so would prevent adding additional kernel-mode regions, which is something the API does not account for. I would argue that this is not expected behavior.For reference, here is the
KernelMPU
Initialization Code on the LiteX sim board:The need for new interface(s)
To properly support kernel memory protection implementations, we need to re-design the
KernelMPU
interface. As even our already supported hardware demonstrates, it fails to account for the plethora of different hardware configurations and their associated semantics. As the kernel MPU would be an integral part to the system's security, we need APIs which configure the MPU in a predictable manner, without risking to accidentally weaken the security by exposing spurious memory regions to unprivileged applications.I hope that we can discuss the set of requirements and different hardware semantics within this issue, and define a set of interfaces which adequately capture these constraints.
The text was updated successfully, but these errors were encountered: