Auto merge of #58235 - jethrogb:jb/sgx-usercall-internals, r=alexcrichton
SGX target: simplify usercall internals
This moves logic from assembly to Rust and removes the special case for exit/panic handling, merging it with regular usercall handling.
Also, this fixes a bug in the exit usercall introduced in a75ae00
. The bug would make regular exits look like panics with high probability. It would also with some probability leak information through uncleared registers.
cc @VardhanThigle
r? @alexcrichton
This commit is contained in:
commit
ccd23b95e5
|
@ -341,9 +341,6 @@ extern crate backtrace_sys;
|
|||
#[cfg(test)] extern crate std as realstd;
|
||||
|
||||
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
|
||||
#[macro_use]
|
||||
#[allow(unused_imports)] // FIXME: without `#[macro_use]`, get error: “cannot
|
||||
// determine resolution for the macro `usercalls_asm`”
|
||||
extern crate fortanix_sgx_abi;
|
||||
|
||||
// The standard macros that are not built-in to the compiler.
|
||||
|
|
|
@ -69,10 +69,6 @@ IMAGE_BASE:
|
|||
.asciz "Re-entered aborted enclave!"
|
||||
.Lreentry_panic_msg_end:
|
||||
|
||||
.Lusercall_panic_msg:
|
||||
.asciz "Invalid usercall#!"
|
||||
.Lusercall_panic_msg_end:
|
||||
|
||||
.org .Lxsave_clear+512
|
||||
.Lxsave_header:
|
||||
.int 0, 0 /* XSTATE_BV */
|
||||
|
@ -219,13 +215,21 @@ sgx_entry:
|
|||
orq $8,%rsp
|
||||
jmp panic_msg
|
||||
|
||||
.Lusercall_panic:
|
||||
lea .Lusercall_panic_msg(%rip),%rdi
|
||||
mov $.Lusercall_panic_msg_end-.Lusercall_panic_msg,%esi
|
||||
orq $8,%rsp
|
||||
jmp panic_msg
|
||||
|
||||
.macro push_callee_saved_registers
|
||||
/* This *MUST* be called with 6 parameters, otherwise register information */
|
||||
/* might leak! */
|
||||
.global usercall
|
||||
usercall:
|
||||
test %rcx,%rcx /* check `abort` function argument */
|
||||
jnz .Lusercall_abort /* abort is set, jump to abort code (unlikely forward conditional) */
|
||||
jmp .Lusercall_save_state /* non-aborting usercall */
|
||||
.Lusercall_abort:
|
||||
/* set aborted bit */
|
||||
movb $1,.Laborted(%rip)
|
||||
/* save registers in DEBUG mode, so that debugger can reconstruct the stack */
|
||||
testb $0xff,DEBUG(%rip)
|
||||
jz .Lusercall_noreturn
|
||||
.Lusercall_save_state:
|
||||
/* save callee-saved state */
|
||||
push %r15
|
||||
push %r14
|
||||
push %r13
|
||||
|
@ -235,33 +239,8 @@ sgx_entry:
|
|||
sub $8, %rsp
|
||||
fstcw 4(%rsp)
|
||||
stmxcsr (%rsp)
|
||||
.endm
|
||||
|
||||
.global usercall_exit
|
||||
usercall_exit:
|
||||
/* save registers in DEBUG mode, so that debugger can reconstruct the stack */
|
||||
testb $0xff,DEBUG(%rip)
|
||||
jz .Lskip_save_registers
|
||||
push_callee_saved_registers
|
||||
movq %rsp,%gs:tcsls_panic_last_rsp
|
||||
.Lskip_save_registers:
|
||||
/* set aborted bit */
|
||||
movb $1,.Laborted(%rip)
|
||||
/* call usercall exit(true) */
|
||||
/* NOP: mov %rsi,%rsi */ /* RSI = usercall() argument: panic */
|
||||
xor %rdx,%rdx /* RDX cleared */
|
||||
movq $usercall_nr_exit,%rdi /* RDI = usercall exit */
|
||||
jmp .Lexit
|
||||
|
||||
/* This *MUST* be called with 6 parameters, otherwise register information */
|
||||
/* might leak! */
|
||||
.global usercall
|
||||
usercall:
|
||||
test %rdi,%rdi
|
||||
jle .Lusercall_panic
|
||||
/* save callee-saved state */
|
||||
push_callee_saved_registers
|
||||
movq %rsp,%gs:tcsls_last_rsp
|
||||
.Lusercall_noreturn:
|
||||
/* clear general purpose register state */
|
||||
/* RAX overwritten by ENCLU */
|
||||
/* RBX set by sgx_exit */
|
||||
|
@ -281,7 +260,7 @@ usercall:
|
|||
jmp .Lsgx_exit
|
||||
.Lusercall_ret:
|
||||
movq $0,%gs:tcsls_last_rsp
|
||||
/* restore callee-saved state, cf. push_callee_saved_registers */
|
||||
/* restore callee-saved state, cf. "save" above */
|
||||
mov %r11,%rsp
|
||||
ldmxcsr (%rsp)
|
||||
fldcw 4(%rsp)
|
||||
|
|
|
@ -12,7 +12,7 @@ pub mod tls;
|
|||
#[macro_use]
|
||||
pub mod usercalls;
|
||||
|
||||
global_asm!(concat!(usercalls_asm!(), include_str!("entry.S")));
|
||||
global_asm!(include_str!("entry.S"));
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn tcs_init(secondary: bool) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::usercalls::alloc::UserRef;
|
||||
use super::usercalls::{alloc::UserRef, self};
|
||||
use cmp;
|
||||
use io::{self, Write};
|
||||
use mem;
|
||||
|
@ -52,7 +52,5 @@ impl Write for SgxPanicOutput {
|
|||
#[no_mangle]
|
||||
pub extern "C" fn panic_msg(msg: &str) -> ! {
|
||||
let _ = SgxPanicOutput::new().map(|mut out| out.write(msg.as_bytes()));
|
||||
unsafe { usercall_exit(true); }
|
||||
usercalls::exit(true)
|
||||
}
|
||||
|
||||
extern "C" { pub fn usercall_exit(panic: bool) -> !; }
|
||||
|
|
|
@ -120,7 +120,7 @@ pub unsafe fn launch_thread() -> IoResult<()> {
|
|||
/// Usercall `exit`. See the ABI documentation for more information.
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
pub fn exit(panic: bool) -> ! {
|
||||
unsafe { super::panic::usercall_exit(panic) }
|
||||
unsafe { raw::exit(panic) }
|
||||
}
|
||||
|
||||
/// Usercall `wait`. See the ABI documentation for more information.
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
pub use fortanix_sgx_abi::*;
|
||||
|
||||
use ptr::NonNull;
|
||||
use num::NonZeroU64;
|
||||
|
||||
#[repr(C)]
|
||||
struct UsercallReturn(u64, u64);
|
||||
|
||||
extern "C" {
|
||||
fn usercall(nr: u64, p1: u64, p2: u64, _ignore: u64, p3: u64, p4: u64) -> UsercallReturn;
|
||||
fn usercall(nr: NonZeroU64, p1: u64, p2: u64, abort: u64, p3: u64, p4: u64) -> UsercallReturn;
|
||||
}
|
||||
|
||||
/// Performs the raw usercall operation as defined in the ABI calling convention.
|
||||
|
@ -23,9 +24,11 @@ extern "C" {
|
|||
///
|
||||
/// Panics if `nr` is `0`.
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
pub unsafe fn do_usercall(nr: u64, p1: u64, p2: u64, p3: u64, p4: u64) -> (u64, u64) {
|
||||
if nr==0 { panic!("Invalid usercall number {}",nr) }
|
||||
let UsercallReturn(a, b) = usercall(nr,p1,p2,0,p3,p4);
|
||||
#[inline]
|
||||
pub unsafe fn do_usercall(nr: NonZeroU64, p1: u64, p2: u64, p3: u64, p4: u64, abort: bool)
|
||||
-> (u64, u64)
|
||||
{
|
||||
let UsercallReturn(a, b) = usercall(nr, p1, p2, abort as _, p3, p4);
|
||||
(a, b)
|
||||
}
|
||||
|
||||
|
@ -41,7 +44,6 @@ trait ReturnValue {
|
|||
}
|
||||
|
||||
macro_rules! define_usercalls {
|
||||
// Using `$r:tt` because `$r:ty` doesn't match ! in `clobber_diverging`
|
||||
($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:tt)*; )*) => {
|
||||
/// Usercall numbers as per the ABI.
|
||||
#[repr(u64)]
|
||||
|
@ -59,22 +61,6 @@ macro_rules! define_usercalls {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! define_usercalls_asm {
|
||||
($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:ty)*; )*) => {
|
||||
macro_rules! usercalls_asm {
|
||||
() => {
|
||||
concat!(
|
||||
".equ usercall_nr_LAST, 0\n",
|
||||
$(
|
||||
".equ usercall_nr_", stringify!($f), ", usercall_nr_LAST+1\n",
|
||||
".equ usercall_nr_LAST, usercall_nr_", stringify!($f), "\n"
|
||||
),*
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_ra {
|
||||
(< $i:ident > $t:ty) => {
|
||||
impl<$i> RegisterArgument for $t {
|
||||
|
@ -173,74 +159,90 @@ impl<T: RegisterArgument, U: RegisterArgument> ReturnValue for (T, U) {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! return_type_is_abort {
|
||||
(!) => { true };
|
||||
($r:ty) => { false };
|
||||
}
|
||||
|
||||
// In this macro: using `$r:tt` because `$r:ty` doesn't match ! in `return_type_is_abort`
|
||||
macro_rules! enclave_usercalls_internal_define_usercalls {
|
||||
(def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty,
|
||||
$n3:ident: $t3:ty, $n4:ident: $t4:ty) -> $r:ty) => (
|
||||
$n3:ident: $t3:ty, $n4:ident: $t4:ty) -> $r:tt) => (
|
||||
/// This is the raw function definition, see the ABI documentation for
|
||||
/// more information.
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3, $n4: $t4) -> $r {
|
||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
||||
Usercalls::$f as Register,
|
||||
NonZeroU64::new(Usercalls::$f as Register)
|
||||
.expect("Usercall number must be non-zero"),
|
||||
RegisterArgument::into_register($n1),
|
||||
RegisterArgument::into_register($n2),
|
||||
RegisterArgument::into_register($n3),
|
||||
RegisterArgument::into_register($n4),
|
||||
return_type_is_abort!($r)
|
||||
))
|
||||
}
|
||||
);
|
||||
(def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty, $n3:ident: $t3:ty) -> $r:ty) => (
|
||||
(def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty, $n3:ident: $t3:ty) -> $r:tt) => (
|
||||
/// This is the raw function definition, see the ABI documentation for
|
||||
/// more information.
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r {
|
||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
||||
Usercalls::$f as Register,
|
||||
NonZeroU64::new(Usercalls::$f as Register)
|
||||
.expect("Usercall number must be non-zero"),
|
||||
RegisterArgument::into_register($n1),
|
||||
RegisterArgument::into_register($n2),
|
||||
RegisterArgument::into_register($n3),
|
||||
0
|
||||
0,
|
||||
return_type_is_abort!($r)
|
||||
))
|
||||
}
|
||||
);
|
||||
(def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty) -> $r:ty) => (
|
||||
(def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty) -> $r:tt) => (
|
||||
/// This is the raw function definition, see the ABI documentation for
|
||||
/// more information.
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r {
|
||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
||||
Usercalls::$f as Register,
|
||||
NonZeroU64::new(Usercalls::$f as Register)
|
||||
.expect("Usercall number must be non-zero"),
|
||||
RegisterArgument::into_register($n1),
|
||||
RegisterArgument::into_register($n2),
|
||||
0,0
|
||||
0,0,
|
||||
return_type_is_abort!($r)
|
||||
))
|
||||
}
|
||||
);
|
||||
(def fn $f:ident($n1:ident: $t1:ty) -> $r:ty) => (
|
||||
(def fn $f:ident($n1:ident: $t1:ty) -> $r:tt) => (
|
||||
/// This is the raw function definition, see the ABI documentation for
|
||||
/// more information.
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn $f($n1: $t1) -> $r {
|
||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
||||
Usercalls::$f as Register,
|
||||
NonZeroU64::new(Usercalls::$f as Register)
|
||||
.expect("Usercall number must be non-zero"),
|
||||
RegisterArgument::into_register($n1),
|
||||
0,0,0
|
||||
0,0,0,
|
||||
return_type_is_abort!($r)
|
||||
))
|
||||
}
|
||||
);
|
||||
(def fn $f:ident() -> $r:ty) => (
|
||||
(def fn $f:ident() -> $r:tt) => (
|
||||
/// This is the raw function definition, see the ABI documentation for
|
||||
/// more information.
|
||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||
#[inline(always)]
|
||||
pub unsafe fn $f() -> $r {
|
||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
||||
Usercalls::$f as Register,
|
||||
0,0,0,0
|
||||
NonZeroU64::new(Usercalls::$f as Register)
|
||||
.expect("Usercall number must be non-zero"),
|
||||
0,0,0,0,
|
||||
return_type_is_abort!($r)
|
||||
))
|
||||
}
|
||||
);
|
||||
|
@ -250,4 +252,3 @@ macro_rules! enclave_usercalls_internal_define_usercalls {
|
|||
}
|
||||
|
||||
invoke_with_usercalls!(define_usercalls);
|
||||
invoke_with_usercalls!(define_usercalls_asm);
|
||||
|
|
|
@ -125,7 +125,7 @@ pub unsafe fn strlen(mut s: *const c_char) -> usize {
|
|||
}
|
||||
|
||||
pub unsafe fn abort_internal() -> ! {
|
||||
abi::panic::usercall_exit(true)
|
||||
abi::usercalls::exit(true)
|
||||
}
|
||||
|
||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||
|
|
Loading…
Reference in New Issue