cmse: clear padding when crossing the secure boundary#157397
cmse: clear padding when crossing the secure boundary#157397folkertdev wants to merge 4 commits into
Conversation
| // FIXME: dedup with the one in | ||
| // `compiler/rustc_const_eval/src/interpret/validity.rs` | ||
| /// Represents a set of `Size` values as a sorted list of ranges. | ||
| // These are (offset, length) pairs, and they are sorted and mutually disjoint, | ||
| // and never adjacent (i.e. there's always a gap between two of them). | ||
| #[derive(Debug, Clone)] | ||
| struct RangeSet(Vec<(Size, Size)>); |
There was a problem hiding this comment.
@RalfJung given the dependency graph, do you have objections to moving the rustc_const_eval into rustc_abi?
There was a problem hiding this comment.
moving the rustc_const_eval into rustc_abi?
... you want to move the entire crate into rustc_abi? I doubt that's what you mean. I am confused.^^
There was a problem hiding this comment.
If you mean moving this data structure -- if it gets moved it should move to rustc_data_structures.
There was a problem hiding this comment.
It can't due to the use of the Size type which is defined in rustc_abi.
There was a problem hiding this comment.
It should probably be generic over some traits then.
There was a problem hiding this comment.
Hmm, I've found something that kind of works, but rustc_const_eval uses a bunch of implementation details.
There was a problem hiding this comment.
rustc_const_eval uses a bunch of implementation details.
Is that why the field is pub?
|
|
||
| if self.fn_abi.conv == CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) { | ||
| // The return value of an `extern "cmse-nonsecure-entry"` function crosses the secure | ||
| // boundary. Zero padding and uninitialized bytes so information does not leak. |
There was a problem hiding this comment.
How could you possibly zero uninit bytes? You have no way of knowing which bytes are uninit.
There was a problem hiding this comment.
Hmm, is "padding" the right word then? Basically I mean bytes that are not initialized by any valid value of the type.
There was a problem hiding this comment.
bytes that are not initialized by any valid value of the type.
That's not quite correct either. It's totally allowed for padding to be initialized.
What you are looking for is bytes that are always ignored by the representation relation of the type. I.e., for any sequence of bytes, if we reset the padding bytes to uninit, then these two sequences of bytes represent the same value (or they are both invalid).
There was a problem hiding this comment.
Exactly, but that's kind of a mouthful.
There was a problem hiding this comment.
Calling them "padding bytes" is a good shorthand I think. The long form can then go in a doc comment.
| /// Ranges of bytes that are initialized for some valid value of this type. In particular for | ||
| /// enums and unions there are offsets that are initialized for some variants but not for | ||
| /// others. | ||
| fn add_data_ranges<C>(self, cx: &C, base_offset: Size, out: &mut RangeSet) |
There was a problem hiding this comment.
This sounds very strange. You can't just zero all bytes that would be allowed to be uninit -- that can introduce UB as it might zero something where the active enum variant says it must be non-zero.
There was a problem hiding this comment.
Maybe the text is not clear, but this function finds the union of all fields, not the intersection.
There was a problem hiding this comment.
Specifically for e.g.
// size_of::<X>() == 8
#[repr(C, align(4))]
enum X {
A(u8),
B(u16),
}
I believe the discriminant is 32 bits here, then one variant uses 1 byte, the other 2, and this function will count the first 6 bytes as data bytes. The remaining 2 bytes are padding and should be zeroed.
There was a problem hiding this comment.
I see, makes sense.
Well, kinda. Obviously the enum might be in variant A and then there's still a byte of junk being passed around here. So I hope you don't intend for this to be used for anything critical.
The entire concept of "clearing padding for security" sounds pretty backwards to me. Similar to these crates that try to "erase" secrets from memory. It's trying to shove a square into a round hole.
There was a problem hiding this comment.
Well, kinda. Obviously the enum might be in variant A and then there's still a byte of junk being passed around here. So I hope you don't intend for this to be used for anything critical.
The lint in #147697 will warn on this sort of case. That PR will need some updating though after this one is merged.
There was a problem hiding this comment.
That entire approach files like piling hacks on top of hacks. I guess that's what we have to do if we want to copy what C does.
This comment has been minimized.
This comment has been minimized.
cb1d784 to
3be4bee
Compare
| // These are (offset, length) pairs, and they are sorted and mutually disjoint, | ||
| // and never adjacent (i.e. there's always a gap between two of them). |
There was a problem hiding this comment.
| // These are (offset, length) pairs, and they are sorted and mutually disjoint, | |
| // and never adjacent (i.e. there's always a gap between two of them). | |
| /// These are (offset, length) pairs, and they are sorted and mutually disjoint, | |
| /// and never adjacent (i.e. there's always a gap between two of them). |
Given that the field is public, the comment also should be public.
tracking issue: #81391
tracking issue: #75835
RFC: rust-lang/rfcs#3884
related: #147697
quick context: cmse creates a distinction between code running in secure mode and non-secure mode (think kernel space versus user space). Secure mode has access to data (e.g. encryption keys) that must not leak to non-secure mode. They use a special calling convention that clears unused registers, but padding in arguments/return values can contain stale secure data.
This PR clears the padding bytes (and similar, e.g. space not used in any variant of a union/enum) when values are passed over the secure boundary.
Separately we'll have a lint to warn on enums and unions being passed across the boundary: for them we can't statically know whether the variant that is passed contains padding.
This is conceptually modeled after a similar feature in
clang(implementation).cc @Jules-Bertholet
r? @davidtwco