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(test)] extern crate std as realstd;
|
||||||
|
|
||||||
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
|
#[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;
|
extern crate fortanix_sgx_abi;
|
||||||
|
|
||||||
// The standard macros that are not built-in to the compiler.
|
// The standard macros that are not built-in to the compiler.
|
||||||
|
|
|
@ -69,10 +69,6 @@ IMAGE_BASE:
|
||||||
.asciz "Re-entered aborted enclave!"
|
.asciz "Re-entered aborted enclave!"
|
||||||
.Lreentry_panic_msg_end:
|
.Lreentry_panic_msg_end:
|
||||||
|
|
||||||
.Lusercall_panic_msg:
|
|
||||||
.asciz "Invalid usercall#!"
|
|
||||||
.Lusercall_panic_msg_end:
|
|
||||||
|
|
||||||
.org .Lxsave_clear+512
|
.org .Lxsave_clear+512
|
||||||
.Lxsave_header:
|
.Lxsave_header:
|
||||||
.int 0, 0 /* XSTATE_BV */
|
.int 0, 0 /* XSTATE_BV */
|
||||||
|
@ -219,13 +215,21 @@ sgx_entry:
|
||||||
orq $8,%rsp
|
orq $8,%rsp
|
||||||
jmp panic_msg
|
jmp panic_msg
|
||||||
|
|
||||||
.Lusercall_panic:
|
/* This *MUST* be called with 6 parameters, otherwise register information */
|
||||||
lea .Lusercall_panic_msg(%rip),%rdi
|
/* might leak! */
|
||||||
mov $.Lusercall_panic_msg_end-.Lusercall_panic_msg,%esi
|
.global usercall
|
||||||
orq $8,%rsp
|
usercall:
|
||||||
jmp panic_msg
|
test %rcx,%rcx /* check `abort` function argument */
|
||||||
|
jnz .Lusercall_abort /* abort is set, jump to abort code (unlikely forward conditional) */
|
||||||
.macro push_callee_saved_registers
|
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 %r15
|
||||||
push %r14
|
push %r14
|
||||||
push %r13
|
push %r13
|
||||||
|
@ -235,33 +239,8 @@ sgx_entry:
|
||||||
sub $8, %rsp
|
sub $8, %rsp
|
||||||
fstcw 4(%rsp)
|
fstcw 4(%rsp)
|
||||||
stmxcsr (%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
|
movq %rsp,%gs:tcsls_last_rsp
|
||||||
|
.Lusercall_noreturn:
|
||||||
/* clear general purpose register state */
|
/* clear general purpose register state */
|
||||||
/* RAX overwritten by ENCLU */
|
/* RAX overwritten by ENCLU */
|
||||||
/* RBX set by sgx_exit */
|
/* RBX set by sgx_exit */
|
||||||
|
@ -281,7 +260,7 @@ usercall:
|
||||||
jmp .Lsgx_exit
|
jmp .Lsgx_exit
|
||||||
.Lusercall_ret:
|
.Lusercall_ret:
|
||||||
movq $0,%gs:tcsls_last_rsp
|
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
|
mov %r11,%rsp
|
||||||
ldmxcsr (%rsp)
|
ldmxcsr (%rsp)
|
||||||
fldcw 4(%rsp)
|
fldcw 4(%rsp)
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub mod tls;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod usercalls;
|
pub mod usercalls;
|
||||||
|
|
||||||
global_asm!(concat!(usercalls_asm!(), include_str!("entry.S")));
|
global_asm!(include_str!("entry.S"));
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn tcs_init(secondary: bool) {
|
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 cmp;
|
||||||
use io::{self, Write};
|
use io::{self, Write};
|
||||||
use mem;
|
use mem;
|
||||||
|
@ -52,7 +52,5 @@ impl Write for SgxPanicOutput {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn panic_msg(msg: &str) -> ! {
|
pub extern "C" fn panic_msg(msg: &str) -> ! {
|
||||||
let _ = SgxPanicOutput::new().map(|mut out| out.write(msg.as_bytes()));
|
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.
|
/// Usercall `exit`. See the ABI documentation for more information.
|
||||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||||
pub fn exit(panic: bool) -> ! {
|
pub fn exit(panic: bool) -> ! {
|
||||||
unsafe { super::panic::usercall_exit(panic) }
|
unsafe { raw::exit(panic) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Usercall `wait`. See the ABI documentation for more information.
|
/// Usercall `wait`. See the ABI documentation for more information.
|
||||||
|
|
|
@ -4,12 +4,13 @@
|
||||||
pub use fortanix_sgx_abi::*;
|
pub use fortanix_sgx_abi::*;
|
||||||
|
|
||||||
use ptr::NonNull;
|
use ptr::NonNull;
|
||||||
|
use num::NonZeroU64;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct UsercallReturn(u64, u64);
|
struct UsercallReturn(u64, u64);
|
||||||
|
|
||||||
extern "C" {
|
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.
|
/// Performs the raw usercall operation as defined in the ABI calling convention.
|
||||||
|
@ -23,9 +24,11 @@ extern "C" {
|
||||||
///
|
///
|
||||||
/// Panics if `nr` is `0`.
|
/// Panics if `nr` is `0`.
|
||||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||||
pub unsafe fn do_usercall(nr: u64, p1: u64, p2: u64, p3: u64, p4: u64) -> (u64, u64) {
|
#[inline]
|
||||||
if nr==0 { panic!("Invalid usercall number {}",nr) }
|
pub unsafe fn do_usercall(nr: NonZeroU64, p1: u64, p2: u64, p3: u64, p4: u64, abort: bool)
|
||||||
let UsercallReturn(a, b) = usercall(nr,p1,p2,0,p3,p4);
|
-> (u64, u64)
|
||||||
|
{
|
||||||
|
let UsercallReturn(a, b) = usercall(nr, p1, p2, abort as _, p3, p4);
|
||||||
(a, b)
|
(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +44,6 @@ trait ReturnValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! define_usercalls {
|
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)*; )*) => {
|
($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:tt)*; )*) => {
|
||||||
/// Usercall numbers as per the ABI.
|
/// Usercall numbers as per the ABI.
|
||||||
#[repr(u64)]
|
#[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 {
|
macro_rules! define_ra {
|
||||||
(< $i:ident > $t:ty) => {
|
(< $i:ident > $t:ty) => {
|
||||||
impl<$i> RegisterArgument for $t {
|
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 {
|
macro_rules! enclave_usercalls_internal_define_usercalls {
|
||||||
(def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty,
|
(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
|
/// This is the raw function definition, see the ABI documentation for
|
||||||
/// more information.
|
/// more information.
|
||||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3, $n4: $t4) -> $r {
|
pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3, $n4: $t4) -> $r {
|
||||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
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($n1),
|
||||||
RegisterArgument::into_register($n2),
|
RegisterArgument::into_register($n2),
|
||||||
RegisterArgument::into_register($n3),
|
RegisterArgument::into_register($n3),
|
||||||
RegisterArgument::into_register($n4),
|
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
|
/// This is the raw function definition, see the ABI documentation for
|
||||||
/// more information.
|
/// more information.
|
||||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r {
|
pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r {
|
||||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
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($n1),
|
||||||
RegisterArgument::into_register($n2),
|
RegisterArgument::into_register($n2),
|
||||||
RegisterArgument::into_register($n3),
|
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
|
/// This is the raw function definition, see the ABI documentation for
|
||||||
/// more information.
|
/// more information.
|
||||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r {
|
pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r {
|
||||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
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($n1),
|
||||||
RegisterArgument::into_register($n2),
|
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
|
/// This is the raw function definition, see the ABI documentation for
|
||||||
/// more information.
|
/// more information.
|
||||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn $f($n1: $t1) -> $r {
|
pub unsafe fn $f($n1: $t1) -> $r {
|
||||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
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($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
|
/// This is the raw function definition, see the ABI documentation for
|
||||||
/// more information.
|
/// more information.
|
||||||
#[unstable(feature = "sgx_platform", issue = "56975")]
|
#[unstable(feature = "sgx_platform", issue = "56975")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn $f() -> $r {
|
pub unsafe fn $f() -> $r {
|
||||||
ReturnValue::from_registers(stringify!($f), do_usercall(
|
ReturnValue::from_registers(stringify!($f), do_usercall(
|
||||||
Usercalls::$f as Register,
|
NonZeroU64::new(Usercalls::$f as Register)
|
||||||
0,0,0,0
|
.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);
|
||||||
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() -> ! {
|
pub unsafe fn abort_internal() -> ! {
|
||||||
abi::panic::usercall_exit(true)
|
abi::usercalls::exit(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hashmap_random_keys() -> (u64, u64) {
|
pub fn hashmap_random_keys() -> (u64, u64) {
|
||||||
|
|
Loading…
Reference in New Issue