Skip to content

cmse: clear padding when crossing the secure boundary#157397

Open
folkertdev wants to merge 4 commits into
rust-lang:mainfrom
folkertdev:cmse-clear-padding
Open

cmse: clear padding when crossing the secure boundary#157397
folkertdev wants to merge 4 commits into
rust-lang:mainfrom
folkertdev:cmse-clear-padding

Conversation

@folkertdev
Copy link
Copy Markdown
Contributor

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

@folkertdev folkertdev added F-cmse_nonsecure_entry `#![feature(cmse_nonsecure_entry)]` F-abi_cmse_nonsecure_call `#![feature(abi_cmse_nonsecure_call)]` labels Jun 3, 2026
@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 3, 2026
Comment thread compiler/rustc_abi/src/layout/ty.rs Outdated
Comment on lines +364 to +370
// 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)>);
Copy link
Copy Markdown
Contributor Author

@folkertdev folkertdev Jun 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RalfJung given the dependency graph, do you have objections to moving the rustc_const_eval into rustc_abi?

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.^^

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you mean moving this data structure -- if it gets moved it should move to rustc_data_structures.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can't due to the use of the Size type which is defined in rustc_abi.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should probably be generic over some traits then.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I've found something that kind of works, but rustc_const_eval uses a bunch of implementation details.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown
Member

@RalfJung RalfJung Jun 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How could you possibly zero uninit bytes? You have no way of knowing which bytes are uninit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, is "padding" the right word then? Basically I mean bytes that are not initialized by any valid value of the type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, but that's kind of a mouthful.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling them "padding bytes" is a good shorthand I think. The long form can then go in a doc comment.

Comment thread compiler/rustc_abi/src/layout/ty.rs Outdated
/// 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)
Copy link
Copy Markdown
Member

@RalfJung RalfJung Jun 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the text is not clear, but this function finds the union of all fields, not the intersection.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@rust-log-analyzer

This comment has been minimized.

@folkertdev folkertdev force-pushed the cmse-clear-padding branch from cb1d784 to 3be4bee Compare June 3, 2026 20:16
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Jun 3, 2026

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

Comment on lines +2 to +3
// 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).
Copy link
Copy Markdown
Member

@RalfJung RalfJung Jun 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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.

View changes since the review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

F-abi_cmse_nonsecure_call `#![feature(abi_cmse_nonsecure_call)]` F-cmse_nonsecure_entry `#![feature(cmse_nonsecure_entry)]` S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants