riscv / riscv-cheri

This repository contains the CHERI extension specification, adding hardware capabilities to RISC-V ISA to enable fine-grained memory protection and scalable compartmentalization.
https://jira.riscv.org/browse/RVG-148
Creative Commons Attribution 4.0 International
38 stars 24 forks source link

Forward compatibility of acperm #71

Open sorear opened 5 months ago

sorear commented 5 months ago

Imagine the simplest possible implementation of memory capabilities, where each capability has bounds and authorizes all primitive operations (lw, lc, sw, sc, execute) within its bounds. No non-memory resources exist and no permission bits are needed.

If we add the CHERI ISAv9 permission bits one at a time, they fall into two groups:

A compartment or sandbox manager which begins with a highly privileged capability should generally clear permissions in the first group that it does not understand to avoid surprising new capabilities of the restricted code; permissions in the second group should not be cleared to avoid failure of things architecturally expected to work.

My proposal is to rearrange the bit flags of the ACPERM/GCPERM image so that the permissions in the first group appear above bit N and the permissions in the second group appear below bit N, so software which intends to restrict capabilities should set unknown bits above N to 0 in the ACPERM mask and unknown bits below N to 1.

LawrenceEsswood commented 5 months ago

I don't think that software should clear permissions it doesn't understand. It should only clear permissions it doesn't understand if they somehow overlapped with the ones it did. This probably means don't ever include a new "alternate write" permission so software can be certain that clearing the write permission does stop writing, regardless of what other permissions are kept.

The proposed divide would certainly require rewriting code as new permissions arrived as there are things not in the original model that might need to be maintained by software for a system to function. Software defined permissions definitely fall into this bucket as the whole point of them is not to assume how they would be used. But, as for more concrete examples, if ASR were stripped by existing software I would need to rewrite parts of my kernel when it arrived. If Seal/Unseal were stripped, I would not be able to use sealing features within compartments without rewriting my sandbox logic. Software should try to maintain all permissions bits, and if there is a permission it should not have, that should be stripped by a lower layer that does understand it.

I still think that we could better future proof ourselves by defining the exact position (not just before/after some divide) of as many of the architectural permission bits (as we can) now, and say only a subset of them need be supported / respected. This gives software writers the greatest chance to write future compatible software.

sorear commented 5 months ago

I don't think that software should clear permissions it doesn't understand. It should only clear permissions it doesn't understand if they somehow overlapped with the ones it did. This probably means don't ever include a new "alternate write" permission so software can be certain that clearing the write permission does stop writing, regardless of what other permissions are kept.

I could get behind this if it could be made precise enough.

If Seal/Unseal were stripped, I would not be able to use sealing features within compartments without rewriting my sandbox logic.

I'm not sure if this is desirable - if compartments A and B are mutually distrusting, it might not be a good thing if B can unseal any capabilities from A because the compartment initializer wasn't aware of sealing rights.

I still think that we could better future proof ourselves by defining the exact position (not just before/after some divide) of as many of the architectural permission bits (as we can) now, and say only a subset of them need be supported / respected. This gives software writers the greatest chance to write future compatible software.

Strong agree; I merely think that we're still in the early days for CHERI and after we've had a cycle or two of real-world deployment there will be permissions invented that aren't currently thought of.

rwatson commented 5 months ago

Musings rather than a structured recommendation:

As an example of a permission we don’t currently have, but it’s easy to imagine adding, for fodder:

Currently we specify a single ‘execute’ permission. But there’s a reasonable argument that we should, in CHERI, differentiate ‘forward’ and ‘backward’ edges in the control-flow graph -- i.e., to avoid confusion between return addresses and function pointers -- a potentially desirable property in the event of an stack initialisation failure, or when control-flow pointers are passed between compartments. This might (hypothetically) be implemented as two permissions: ‘execute_forwards’ and ‘execute_return’, the former intended for use with jump-and-link, and the latter with jump (or something along those lines). I wonder whether we could fit something like that into this framework.

Another example to think about is use of software-defined permission bits -- e.g., using a software-defined bit to mark capabilities allocated by a special heap allocator, or to delegate the right to free() memory.

In the past, I’ve pondered whether it should actually be CNANDPERM, so that you could specify bits to clear rather than bits to retain. Otherwise you might find you are always doing CANDPERM with ~perms, which doesn’t lend itself to more compact use of immediate fields, etc.

LawrenceEsswood commented 4 months ago

@rwatson I have found that nowadays I nearly always write & ~(SET_OF_PERMISSIONS), so I agree andperm is probably the wrong way around for optimal code generation.

@sorear

I'm not sure if this is desirable - if compartments A and B are mutually distrusting, it might not be a good thing if B can unseal any capabilities from A because the compartment initializer wasn't aware of sealing rights.

I agree this is a case where it would have been preferable for a strip (for security). But of course doing so would mean neither compartment could do sealing at all, which they might have had valid use cases for even if the loader knew nothing of sealing. I think its very hard to guess what a good policy should be for unknown bits, which is why I am still pushing for defining the "seal" permission (and others for that matter), but just not define any instructions that actually use it in the base CHERI ISA (although we might offer some verbiage as to how it may be used in the future).

jrtc27 commented 4 months ago

which they might have had valid use cases for even if the loader knew nothing of sealing

It is completely reasonable, maybe even a good idea, to require the loader to know of significant architectural features like sealing. Sealing is not a great example to use for extensibility, because any otype-like mechanism should have a separate sealing root (or multiple as far as the application is concerned), not be conflated with memory capabilities.