You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: text/0000-cmse-calling-conventions.md
+12-10Lines changed: 12 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,11 +27,11 @@ Trustzone is growing in availability and use. More and more of the new medium an
27
27
28
28
The cmse calling conventions are part of the *Cortex-M Security Extension* that are available on thumbv8 systems. They are used together with Trustzone (hardware isolation) to create more secure embedded applications. Arm defines the toolchain requirements in [ARMv8-M Security Extensions: Requirements on Development Tools - Engineering Specification](https://developer.arm.com/documentation/ecm0359818/latest/), but of course this specification needs to be interpreted in a Rust context.
29
29
30
-
The main idea of Trustzone is to split an embedded application into two executables. The secure executable has access to secrets (e.g. encryption keys), and must be careful not to leak those secrets. The non-secure executable cannot access these secrets or any memory that is marked as secure: the system will hardfault if it tries to dereference a pointer to memory that it does not have access to. In this way a whole class of security issues is simply impossible in the non-secure app.
30
+
The main idea of Trustzone is to split an embedded application into two executables. The secure executable has access to secrets (e.g. encryption keys), and must be careful not to leak those secrets. The non-secure executable cannot access these secrets or any memory that is marked as secure: the system will raise a SecureFault when a program dereferences a pointer to memory that it does not have access to. In this way a whole class of security issues is simply impossible in the non-secure app.
31
31
32
32
The cmse calling conventions facilitate interactions between the secure and non-secure executables. To ensure that secrets do not leak, these calling conventions impose some custom restrictions on top of the system's standard AAPCS calling convention.
33
33
34
-
The `cmse-nonsecure-entry` calling convention is used in the secure executable to define entry points that the non-secure executable can call. The use of this calling convention hooks into the tooling (LLVM and the linker) to generate a shim that switches the security mode, and an import library (an object file with only declarations, not actual instructions) that can be linked into the non-secure executable.
34
+
The `cmse-nonsecure-entry` calling convention is used in the secure executable to define entry points that the non-secure executable can call. The use of this calling convention hooks into the tooling (LLVM and the linker) to generate a shim (what rust calls a shim, arm calls a *veneer*) that switches the security mode, and an import library (an object file with only declarations, not actual instructions) that can be linked into the non-secure executable.
35
35
36
36
The `cmse-nonsecure-call` calling convention is used in the other direction, when the secure executable wants to call into the non-secure executable. This calling convention can only occur on function pointers, not on definitions or extern blocks. The secure executable can acquire a non-secure function pointer via shared memory or a non-secure callback can be passed to an entry function.
37
37
@@ -44,9 +44,9 @@ The `cmse-nonsecure-call` and `cmse-nonsecure-entry` ABIs are only accepted on `
44
44
45
45
The foundation of the cmse ABIs is the platform's standard AAPCS calling convention. On `thumbv8m` targets `extern "aapcs"` is the default C ABI and equivalent to `extern "C"`.
46
46
47
-
The `cmse-nonsecure-call` ABI can only be used on function pointers. Using it in for a function definition or extern block emits an error. It is sound to cast such a function to `extern "aapcs"`, but calling the function will cause a HardFault. Casting an `extern "aapcs"` function pointer to a `cmse-nonsecure-call` is valid, but will cause a HardFault if the function's definition is not in non-secure memory.
47
+
The `cmse-nonsecure-call` ABI can only be used on function pointers. Using it in for a function definition or extern block emits an error. It is sound to cast such a function to `extern "aapcs"`, but calling the function will cause a SecureFault. Casting an `extern "aapcs"` function pointer to a `cmse-nonsecure-call` is valid, but will cause a SecureFault if the function's definition is not in non-secure memory.
48
48
49
-
The `cmse-nonsecure-entry` ABI is allowed on function definitions, extern blocks and function pointers. It is sound and valid (in some cases even encouraged) to cast such a function to `extern "aapcs"`. Calling the function is valid and will behave as expected. Casting an `extern "aapcs"` function pointer to `cmse-nonsecure-entry` is valid, but will not change the security mode.
49
+
The `cmse-nonsecure-entry` ABI is allowed on function definitions, extern blocks and function pointers. It is sound and valid (in some cases even encouraged) to cast such a function to `extern "aapcs"`. Calling the function is valid and will behave as expected in both the secure and non-secure applications. Casting an `extern "aapcs"` function pointer to `cmse-nonsecure-entry` is valid, but will not change the security mode, so calling such a function that was defined in the secure application will SecureFault when called in the non-secure application.
The `cmse-nonsecure-call` calling convention can only be used on function pointers, which already disallows generics. For `cmse-nonsecure-entry`, it is standard to add a `#[no_mangle]` or similar attribute, which also disallows generics. Explicitly disallowing generics enables the layout calculation that is required for good error messages for signatures that use too many registers.
103
103
### No C-variadics (currently)
104
104
105
-
Currently both ABIs disallow the use of c-variadics. For `cmse-nonsecure-entry`, the toolchain actually does not support c-variadic signatures (likely because of how they interact with veneers, though the specification does not say that explicitly).
105
+
Currently both ABIs disallow the use of c-variadics. For `cmse-nonsecure-entry`, the toolchain actually does not support c-variadic signatures (likely because of how they interact with shim that switches to secure mode), though the specification does not say that explicitly).
- but accepts c-variadic nonsecure calls: https://godbolt.org/z/5rdK58ar4
109
109
110
-
For `cmse-nonsecure-call`, we may stabilize c-variadics at some point in the future.
110
+
For `cmse-nonsecure-call`, we may support and stabilize c-variadics at some point in the future.
111
111
112
112
### Support for `const fn`
113
113
114
114
No special support for calling cmse functions is needed.
115
115
116
-
Evaluating entry functions during constant evaluation is valid. The context switch from non-secure to secure mode is handled by the veneer, which is not visible to rust code. Clearing of registers is not relevant for constant evaluation.
116
+
Evaluating entry functions during constant evaluation is valid. The context switch from non-secure to secure mode is handled by the shim that switches to secure mode, which is not visible to rust code. Clearing of registers is not relevant for constant evaluation.
117
117
118
118
The `cmse-nonsecure-call` calling convention can only be used on function pointers, which cannot be evaluated during constant evaluation.
119
119
120
-
Miri is not a register machine, so the clearing of registers is not relevant. The context switching also does not need to be considered. Miri will continue to execute where actual hardware hits a hardfault, but this is similar to a standard rust process trying to read memory that the kernel has protected.
120
+
Miri is not a register machine, so the clearing of registers is not relevant. The context switching also does not need to be considered, because a Miri input program cannot use FFI and therefore cannot cross the secure boundary. Any attempt to do so would rely on a transmute or similar and would for that reason be unsound.
121
121
### Warn on partially uninitialized values crossing the secure boundary
122
122
123
123
Unions and types with padding or niches can contain uninitialized memory, and this uninitialized memory can contain stale secure information. Clang warns when union values cross the security boundary (see https://godbolt.org/z/vq9xnrnEs), and rust does the same.
Like clang, the lint is emitted at the use site. That means that in the case where passing such a value is deliberate, each use site can be annotated with `#[allow(cmse_uninitialized_leak)]`. In most cases this lint should be considered an error, and an alternative way of returning/passing the value should be found that does not run the risk of leaking secure information.
136
136
137
+
Unlike clang, the rust lint also warns on other instances where a value may be (partially) uninitialized based on its type. For instance, clang does not warn when a struct crossing the secure boundary contains padding (e.g. https://godbolt.org/z/rcM65YG1s).
138
+
137
139
The lint is implemented in https://github.com/rust-lang/rust/pull/147697, and checks whether transmuting a type `T` to `[u8; size_of::<T>]` is valid. The transmute is only valid when all bytes of `T` are guaranteed to be initialized.
138
140
139
141
```rust
@@ -291,9 +293,9 @@ One potential method is to extend the`repr` attribute with an option that adds f
0 commit comments