On Tue, Feb 11, 2020 at 8:25 PM John Hauser <jh.riscv@...
I've been studying the TEE Task Group's "PMP Enhancements" proposal
with great interest off-and-on for several weeks. I definitely agree
with the intention of the proposal, but I see several issues. For
presentation, I've numbered them 1 through 4 below.
Currently, when a memory access is prevented by physical memory
attributes (PMAs) or by PMP, an access fault trap is taken. The
proposal defines a new "security exception" and requires that some
blocked memory accesses take a security exception trap instead of the
usual access fault. The document says
A new exception will help distinguish the exceptions we get with
the current PMP spec when the access type doesn’t match R/W/X flags
on the matching rule, from the exception we get when violating the
access controls of the new mechanisms in place.
I request that some explanation be provided for how this distinction
is expected be helpful; i.e., why "denied" accesses need a different
exception code than "enforced" accesses. If the reason is supposed
to be obvious, it was not so to me. Just saying it "will help
distinguish" isn't sufficient. Why distinguish? What good does it do?
For me, it seems obvious that these should all be access faults.
As Jonathan Behrens has already noted, some systems depend on being
able to set mstatus.MXR = 1 temporarily to read S/U-executable
instructions, for emulation purposes. The proposal should be modified
to say that any S/U-mode-only PMP region that grants execute permission
to S/U modes (bit X is set), implicitly grants read permission to
M mode when MXR = 1.
I'm concerned that the use of the reserved combination W = 1, R = 0
for shared memory regions may be incompatible with a future use for
this encoding in page tables. For example, one possible allocation of
the reserved W/R encoding in page tables could be:
X W R
0 1 0 uncached read-only page
1 1 0 uncached read-write page
If so defined, the same _uncached_ property might also be sensible
for PMP entries, yet we would no longer be able to encode it the same
way, because we have allocated the reserved W/R combination for shared
memory regions instead.
To be clear, I know of no current plans to use the reserved W/R
encoding for an _uncached_ property this way, or for any other purpose.
I am merely giving an example of the sort of inconsistency that could
arise because of our choices today.
I understand that the reserved W/R encoding ended up in use because
there is opposition to touching the two reserved bits that still exist
in a PMP configuration byte, and there are few options for encoding
everything in just the four bits we already have: L, X, W, R. My own
choice would be to go ahead and consume a reserved bit to avoid the
risk of creating a mess of the encoding down the road.
The biggest concern I have with the proposal is that the effort to
fully lock down the executable regions for M mode, while correct for
maximizing security in principle, doesn't leave enough flexibility
for some systems. Tariq Kurd has given an example of a system that,
during booting, progressively expands the regions accessible to M mode,
which the current proposal prohibits. I'd like to give a couple other
examples that are more specifically about execute permission, but
still revolve around the need to edit M-mode-only PMP entries even when
enhanced security is enabled.
Consider a complex operating system, running in M mode, that supports
loadable "kernel modules", which are components that can be brought
into memory or evicted in response to the varying needs of user-level
tasks. With the current PMP proposal, when MML = 1, this M-mode OS
cannot dynamically adjust the regions of memory that are executable for
loadable modules. Instead, the OS authors must make a choice: either
pre-allocate the maximal amount of memory that could ever be needed for
loaded kernel modules, possibly wasting memory, or entirely forgo using
the security enhancement. If they choose the latter because memory
really is scarce, how has security been improved?
Or consider the situation where there is more than one independent
stage of boot-time software that could benefit from enhanced PMP
security. U-Boot, for example, is a complex piece of software in its
own right. If a bootloader like U-Boot is used in an M/U-only system,
it's easy to imagine that enhanced security could help guard against
attacks. But with the current proposal, U-Boot cannot set MML = 1,
because it would have to configure the executable regions not only
for itself but also for the operating system it subsequently loads,
something outside its knowledge or authority. Because all current and
future executable regions must be known and configured before MML can
be enabled, a U-Boot-like loader must run with MML = 0. Again, this
seems like a loss for security in this instance.
I have no argument with anyone who needs all the restrictions the
current proposal provides; we should be able to offer that. But if we
require always that all executable regions be locked down in advance,
we're not providing sufficient flexibility for all systems at all
times, instead sometimes forcing an awkward "maximal security or none"
(To help his particular system, Mr. Kurd has proposed a DPL bit, Delay
PMP Lock. However, this bit would conflict with one of the intended
purposes of PMP locking as I understand it, which is to permit earlier
initialization software to protect some regions from access by later,
less trusted, M-mode code. By itself, the DPL solution is too simple
because, in general, we need to be able to set some PMP entries that
stay locked, while at the same time other entries remain unlocked for
editing but are nonetheless enforced.)
To bridge the gap between "maximal security" and "none", I've developed
a modified proposal with four security levels rather than just the
current two (MML = 0 or 1). Unfortunately, I see no good way to
provide all the needed flexibility without also taking one of the two
reserved PMP configuration bits. While having four security levels may
sound more complex, actually it's not, because the extra configuration
bit allows some encoding complexity to be reduced at the same time.
The only significant cost is the allocation of the reserved bit. I'll
be sending my modified proposal in a follow-up message.
A while ago, I expressed opposition to consuming one of the remaining pmpcfg bits because it seemed, at the time, that the goals of this proposal could be accomplished without doing so. If that is not in fact the case, I withdraw my objection.
- John Hauser