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:
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user