rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.8k stars 12.66k forks source link

Tracking Issue for the #[cmse_nonsecure_entry] attribute #75835

Open hug-dev opened 4 years ago

hug-dev commented 4 years ago

This is a tracking issue for the PR #75810. It was deemed in that PR that a RFC was not necessary because the changes were small. The feature gate for the issue is #![feature(cmse_nonsecure_entry)].

Description

The cmse_nonsecure_entry attribute is a target-dependent attribute available for thumbv8m targets. It directly maps to the attribute of the same name in LLVM.

It is to be used under the TrustZone-M technology for Armv8-M architecture.

It will modify code generation of Secure entry functions:

See section 5.4 of ARMv8-M Security Extensions: Requirements on Development Tools - Engineering Specification for more details.

Example

Having a Secure entry function named entry_function:

#![feature(cmse_nonsecure_entry)]

#[no_mangle]
#[cmse_nonsecure_entry]
pub extern "C" fn entry_function(in: u32) -> u32 {
    in + 6
}

With those commands:

$ rustc --emit obj --crate-type lib --target thumbv8m.main-none-eabi function.rs
$ arm-none-eabi-objdump -D function.o

it will emit the following assembly:

00000000 <entry_function>:
   0:   b580            push    {r7, lr}
   2:   466f            mov     r7, sp
   4:   b082            sub     sp, #8
   6:   9001            str     r0, [sp, #4]
   8:   1d81            adds    r1, r0, #6
   a:   460a            mov     r2, r1
   c:   4281            cmp     r1, r0
   e:   9200            str     r2, [sp, #0]
  10:   d30b            bcc.n   2a <entry_function+0x2a>
  12:   e7ff            b.n     14 <entry_function+0x14>
  14:   9800            ldr     r0, [sp, #0]
  16:   b002            add     sp, #8
  18:   e8bd 4080       ldmia.w sp!, {r7, lr}
  1c:   4671            mov     r1, lr
  1e:   4672            mov     r2, lr
  20:   4673            mov     r3, lr
  22:   46f4            mov     ip, lr
  24:   f38e 8800       msr     CPSR_f, lr
  28:   4774            bxns    lr
  2a:   f240 0000       movw    r0, #0
  2e:   f2c0 0000       movt    r0, #0
  32:   f240 0200       movw    r2, #0
  36:   f2c0 0200       movt    r2, #0
  3a:   211c            movs    r1, #28
  3c:   f7ff fffe       bl      0 <_ZN4core9panicking5panic17h5c028258ca2fb3f5E>
  40:   defe            udf     #254    ; 0xfe

You can see from 1c to 24 the clearing of the registers and the BXNS instruction used on 28.

Steps

hug-dev commented 4 years ago

Note: the error produced when an entry function require arguments being passed on the stack is currently a LLVM error. It would be better to have this error being thrown out in Rust ABI, for better user experience.

nihalpasham commented 4 years ago

noticed the current implementation only allows FFI functions (or extern "C" functions) to be decorated with the attribute. I'm assuming this is only required because the implementation needs the function to use a C ABI and the function by itself is actually safe (i.e. no unsafe code here). Is my understanding correct?

hug-dev commented 4 years ago

The reason the C ABI is forced for entry function is because parameters must only be passed using registers and not using memory. See this comment for reference:

Ah, I think we should definitely forbid using this attribute with non-"C" ABI functions then. Since the Rust ABI is unstable, it can pass arguments either directly in registers, or in memory, and that would be a problem here.

This is because depending where the parameters would be in memory, there would be more work needed in LLVM to access them from Secure. For example if they were pushed on to the Non-Secure stack when NS calls the entry functions, LLVM would have to put them onto the Secure stack as a prelude before the actual function executes so that the parameters are where expected 👌

hug-dev commented 3 years ago

I am thinking now that this could also be implemented as an ABI, similarly than cmse-nonsecure-call. It could even be the same extern symbol let's say extern "C-cmse-nonsecure" that:

We already restrict the C ABI for entry functions anyway!

joshtriplett commented 2 years ago

We discussed this in today's @rust-lang/lang meeting. We felt like an extern "ABI" does make sense for most of the functionality of this. What's left sounds like the generation of a special symbol, and it's not clear if we should have that as a built-in attribute, a library attribute, or something in a crate.

StefanHri commented 1 year ago

What is the recommended work around to deal with entry functions that require many arguments, e.g., 5x i32 ? Currently, this is causes an error:

error: <unknown>:0:0: in function fkt i32 (i32, i32, i32, i32, i32): secure entry function requires arguments on stack
hug-dev commented 1 year ago

What is the recommended work around to deal with entry functions that require many arguments, e.g., 5x i32 ? Currently, this is causes an error:

error: <unknown>:0:0: in function fkt i32 (i32, i32, i32, i32, i32): secure entry function requires arguments on stack

Hey! It is possible for you to pass pointers to structures containing more data. The only drawback is that you must check in the Secure function that the address is accessible by the Non-Secure code. You can do that with the TestTarget functions.

tdittr commented 3 months ago

One caveat with these functions is that they should not be callable from the current binary as that is in the secure context. So calling a function marked extern "C-cmse-nonsecure-entry" or #[cmse_nonsecure_entry] would end with a BXNS instruction and return to wherever the non-secure sides stack indicates, instead of the function that actually called it.

I tried it out on godbolt and LLVM seems to just permit it. I still think rustc should give an error.

Edit: Reading the docs again, BXNS determines whether to switch to non-secure state based on the least significant bit of the address it will jump to. So maybe this would be ok?

Edit: I asked a developer who is using Trustzone from C and there it is not uncommon to call these entry functions even in your secure zone. So this should just work thanks to LLVM.

scottmcm commented 2 days ago

I think that some random target feature putting something in the global attribute namespace is a bad precedent.

To me, this should be namespaced in some way -- I don't know if that means #[arch::something_or_other] or what.