diff --git a/Cargo.lock b/Cargo.lock index a1687cbd..2810adc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1022,6 +1022,7 @@ dependencies = [ "fnv", "fortanix-sgx-abi", "futures 0.3.17", + "insecure-time", "ipc-queue", "lazy_static", "libc", @@ -1216,7 +1217,7 @@ dependencies = [ [[package]] name = "fortanix-sgx-abi" -version = "0.5.0" +version = "0.6.0" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", diff --git a/intel-sgx/async-usercalls/Cargo.toml b/intel-sgx/async-usercalls/Cargo.toml index ea692ae3..9b8406b2 100644 --- a/intel-sgx/async-usercalls/Cargo.toml +++ b/intel-sgx/async-usercalls/Cargo.toml @@ -17,7 +17,7 @@ categories = ["asynchronous"] [dependencies] # Project dependencies ipc-queue = { version = "0.3", path = "../../ipc-queue" } -fortanix-sgx-abi = { version = "0.5.0", path = "../fortanix-sgx-abi" } +fortanix-sgx-abi = { version = "0.6.0", path = "../fortanix-sgx-abi" } # External dependencies lazy_static = "1.4.0" # MIT/Apache-2.0 diff --git a/intel-sgx/async-usercalls/src/callback.rs b/intel-sgx/async-usercalls/src/callback.rs index ea5d969b..3194fa3d 100644 --- a/intel-sgx/async-usercalls/src/callback.rs +++ b/intel-sgx/async-usercalls/src/callback.rs @@ -1,4 +1,4 @@ -use fortanix_sgx_abi::{invoke_with_usercalls, Fd, Result}; +use fortanix_sgx_abi::{invoke_with_usercalls, Fd, Result, InsecureTimeInfo}; use std::io; use std::os::fortanix_sgx::usercalls::raw::{Return, ReturnValue}; use std::os::fortanix_sgx::usercalls::FromSgxResult; @@ -21,21 +21,23 @@ impl From for CbFn } macro_rules! cbfn_type { - ( ) => { CbFn<()> }; - ( -> ! ) => { () }; - ( -> u64 ) => { CbFn }; - ( -> (Result, usize) ) => { CbFn> }; - ( -> (Result, u64) ) => { CbFn> }; - ( -> (Result, Fd) ) => { CbFn> }; - ( -> (Result, *mut u8) ) => { CbFn> }; - ( -> Result ) => { CbFn> }; + ( ) => { CbFn<()> }; + ( -> ! ) => { () }; + ( -> u64 ) => { CbFn }; + ( -> (u64, *const InsecureTimeInfo) ) => { CbFn<(u64, *const InsecureTimeInfo)> }; + ( -> (Result, usize) ) => { CbFn> }; + ( -> (Result, u64) ) => { CbFn> }; + ( -> (Result, Fd) ) => { CbFn> }; + ( -> (Result, *mut u8) ) => { CbFn> }; + ( -> Result ) => { CbFn> }; } macro_rules! call_cbfn { - ( $cb:ident, $rv:expr, ) => { let x: () = $rv; $cb.call(x); }; - ( $cb:ident, $rv:expr, -> ! ) => { let _: ! = $rv; }; - ( $cb:ident, $rv:expr, -> u64 ) => { let x: u64 = $rv; $cb.call(x); }; - ( $cb:ident, $rv:expr, -> $t:ty ) => { let x: $t = $rv; $cb.call(x.from_sgx_result()); }; + ( $cb:ident, $rv:expr, ) => { let x: () = $rv; $cb.call(x); }; + ( $cb:ident, $rv:expr, -> ! ) => { let _: ! = $rv; }; + ( $cb:ident, $rv:expr, -> u64 ) => { let x: u64 = $rv; $cb.call(x); }; + ( $cb:ident, $rv:expr, -> (u64, *const InsecureTimeInfo) ) => { let x: (u64, *const InsecureTimeInfo) = $rv; $cb.call(x); }; + ( $cb:ident, $rv:expr, -> $t:ty ) => { let x: $t = $rv; $cb.call(x.from_sgx_result()); }; } macro_rules! define_callback { diff --git a/intel-sgx/async-usercalls/src/provider_api.rs b/intel-sgx/async-usercalls/src/provider_api.rs index 304c4101..4c45ff48 100644 --- a/intel-sgx/async-usercalls/src/provider_api.rs +++ b/intel-sgx/async-usercalls/src/provider_api.rs @@ -3,7 +3,7 @@ use crate::io_bufs::UserBuf; use crate::raw::RawApi; use crate::utils::MakeSend; use crate::{AsyncUsercallProvider, CancelHandle}; -use fortanix_sgx_abi::Fd; +use fortanix_sgx_abi::{Fd, InsecureTimeInfo}; use std::io; use std::mem::{self, ManuallyDrop}; use std::net::{TcpListener, TcpStream}; @@ -251,13 +251,15 @@ impl AsyncUsercallProvider { /// callbacks. pub fn insecure_time(&self, callback: F) where - F: FnOnce(SystemTime) + Send + 'static, + F: FnOnce(SystemTime, *const InsecureTimeInfo) + Send + 'static, { - let cb = move |nanos_since_epoch| { + let cb = move |(nanos_since_epoch, insecure_time_info_ptr): (u64, *const InsecureTimeInfo)| { let t = UNIX_EPOCH + Duration::from_nanos(nanos_since_epoch); - callback(t); + callback(t, insecure_time_info_ptr); }; unsafe { + // TODO We could detect if we're able to handle insecure time within the enclave, and + // avoid the async call self.raw_insecure_time(Some(cb.into())); } } @@ -285,4 +287,4 @@ fn copy_user_buffer(buf: &UserRef) -> Vec { Vec::new() } } -} \ No newline at end of file +} diff --git a/intel-sgx/async-usercalls/src/raw.rs b/intel-sgx/async-usercalls/src/raw.rs index e66e5a35..ae511b16 100644 --- a/intel-sgx/async-usercalls/src/raw.rs +++ b/intel-sgx/async-usercalls/src/raw.rs @@ -1,6 +1,6 @@ use crate::callback::*; use crate::{AsyncUsercallProvider, CancelHandle}; -use fortanix_sgx_abi::Fd; +use fortanix_sgx_abi::{Fd, InsecureTimeInfo}; use std::io; use std::os::fortanix_sgx::usercalls::raw::ByteBuffer; use std::os::fortanix_sgx::usercalls::raw::{Usercall, UsercallNrs}; @@ -51,7 +51,7 @@ pub trait RawApi { callback: Option>>, ) -> CancelHandle; - unsafe fn raw_insecure_time(&self, callback: Option>); + unsafe fn raw_insecure_time(&self, callback: Option>); unsafe fn raw_alloc(&self, size: usize, alignment: usize, callback: Option>>); @@ -137,7 +137,7 @@ impl RawApi for AsyncUsercallProvider { self.send_usercall(u, callback.map(|cb| Callback::connect_stream(cb))) } - unsafe fn raw_insecure_time(&self, callback: Option>) { + unsafe fn raw_insecure_time(&self, callback: Option>) { let u = Usercall(UsercallNrs::insecure_time as _, 0, 0, 0, 0); self.send_usercall(u, callback.map(|cb| Callback::insecure_time(cb))); } @@ -172,7 +172,7 @@ mod tests { let (tx, rx) = mpmc::bounded(N); for _ in 0..N { let tx = tx.clone(); - let cb = move |d| { + let cb = move |(d, _)| { let system_time = UNIX_EPOCH + Duration::from_nanos(d); tx.send(system_time).unwrap(); }; diff --git a/intel-sgx/async-usercalls/src/test_support.rs b/intel-sgx/async-usercalls/src/test_support.rs index b73f50e6..59d78cb5 100644 --- a/intel-sgx/async-usercalls/src/test_support.rs +++ b/intel-sgx/async-usercalls/src/test_support.rs @@ -41,7 +41,7 @@ impl Drop for AutoPollingProvider { fn drop(&mut self) { self.shutdown.store(true, Ordering::Relaxed); // send a usercall to ensure thread wakes up - self.provider.insecure_time(|_| {}); + self.provider.insecure_time(|_, _| {}); self.join_handle.take().unwrap().join().unwrap(); } } \ No newline at end of file diff --git a/intel-sgx/enclave-runner/Cargo.toml b/intel-sgx/enclave-runner/Cargo.toml index 01d590df..bd344da1 100644 --- a/intel-sgx/enclave-runner/Cargo.toml +++ b/intel-sgx/enclave-runner/Cargo.toml @@ -21,8 +21,9 @@ exclude = ["fake-vdso/.gitignore", "fake-vdso/Makefile", "fake-vdso/main.S"] [dependencies] # Project dependencies sgxs = { version = "0.8.0", path = "../sgxs" } -fortanix-sgx-abi = { version = "0.5.0", path = "../fortanix-sgx-abi" } +fortanix-sgx-abi = { version = "0.6.0", path = "../fortanix-sgx-abi" } sgx-isa = { version = "0.4.0", path = "../sgx-isa" } +insecure-time = { version = "0.1", path = "../insecure-time", features = ["estimate_crystal_clock_freq"] } ipc-queue = { version = "0.3.0", path = "../../ipc-queue" } # External dependencies diff --git a/intel-sgx/enclave-runner/src/command.rs b/intel-sgx/enclave-runner/src/command.rs index 666ccb05..00f9717c 100644 --- a/intel-sgx/enclave-runner/src/command.rs +++ b/intel-sgx/enclave-runner/src/command.rs @@ -22,6 +22,7 @@ pub struct Command { size: usize, usercall_ext: Option>, forward_panics: bool, + force_time_usercalls: bool, cmd_args: Vec>, } @@ -44,6 +45,7 @@ impl Command { size: usize, usercall_ext: Option>, forward_panics: bool, + force_time_usercalls: bool, cmd_args: Vec>, ) -> Command { let main = tcss.remove(0); @@ -54,6 +56,7 @@ impl Command { size, usercall_ext, forward_panics, + force_time_usercalls, cmd_args, } } @@ -63,6 +66,6 @@ impl Command { } pub fn run(self) -> Result<(), Error> { - EnclaveState::main_entry(self.main, self.threads, self.usercall_ext, self.forward_panics, self.cmd_args) + EnclaveState::main_entry(self.main, self.threads, self.usercall_ext, self.forward_panics, self.force_time_usercalls, self.cmd_args) } } diff --git a/intel-sgx/enclave-runner/src/library.rs b/intel-sgx/enclave-runner/src/library.rs index 4ab75851..28ae33ee 100644 --- a/intel-sgx/enclave-runner/src/library.rs +++ b/intel-sgx/enclave-runner/src/library.rs @@ -48,9 +48,10 @@ impl Library { size: usize, usercall_ext: Option>, forward_panics: bool, + force_time_usercalls: bool, ) -> Library { Library { - enclave: EnclaveState::library(tcss, usercall_ext, forward_panics), + enclave: EnclaveState::library(tcss, usercall_ext, forward_panics, force_time_usercalls), address, size, } diff --git a/intel-sgx/enclave-runner/src/loader.rs b/intel-sgx/enclave-runner/src/loader.rs index daca85e3..88aa0016 100644 --- a/intel-sgx/enclave-runner/src/loader.rs +++ b/intel-sgx/enclave-runner/src/loader.rs @@ -68,6 +68,7 @@ pub struct EnclaveBuilder<'a> { load_and_sign: Option Result>>, hash_enclave: Option) -> Result>>, forward_panics: bool, + force_time_usercalls: bool, cmd_args: Option>>, } @@ -145,6 +146,7 @@ impl<'a> EnclaveBuilder<'a> { load_and_sign: None, hash_enclave: None, forward_panics: false, + force_time_usercalls: true, // By default, keep the old behavior of always doing a usercall on an insecure_time call cmd_args: None, }; @@ -266,6 +268,16 @@ impl<'a> EnclaveBuilder<'a> { self } + /// SGXv2 platforms allow enclaves to use the `rdtsc` instruction. This can speed up + /// performance significantly as enclave no longer need to call out to userspace to request the + /// current time. Unfortunately, older enclaves are not compatible with new enclave runners. + /// Also, sometimes the behavior of enclaves always calling out the userspace needs to be + /// simulated. This setting enforces the old behavior. + pub fn force_insecure_time_usercalls(&mut self, force_time_usercalls: bool) -> &mut Self { + self.force_time_usercalls = force_time_usercalls; + self + } + fn initialized_args_mut(&mut self) -> &mut Vec> { self.cmd_args.get_or_insert_with(|| vec![b"enclave".to_vec()]) } @@ -309,7 +321,7 @@ impl<'a> EnclaveBuilder<'a> { fn load( mut self, loader: &mut T, - ) -> Result<(Vec, *mut c_void, usize, bool), anyhow::Error> { + ) -> Result<(Vec, *mut c_void, usize, bool, bool), anyhow::Error> { let signature = match self.signature { Some(sig) => sig, None => self @@ -320,6 +332,7 @@ impl<'a> EnclaveBuilder<'a> { let miscselect = self.miscselect.unwrap_or(signature.miscselect); let mapping = loader.load(&mut self.enclave, &signature, attributes, miscselect)?; let forward_panics = self.forward_panics; + let force_time_usercalls = self.force_time_usercalls; if mapping.tcss.is_empty() { unimplemented!() } @@ -328,6 +341,7 @@ impl<'a> EnclaveBuilder<'a> { mapping.info.address(), mapping.info.size(), forward_panics, + force_time_usercalls, )) } @@ -336,7 +350,7 @@ impl<'a> EnclaveBuilder<'a> { let args = self.cmd_args.take().unwrap_or_default(); let c = self.usercall_ext.take(); self.load(loader) - .map(|(t, a, s, fp)| Command::internal_new(t, a, s, c, fp, args)) + .map(|(t, a, s, fp, dti)| Command::internal_new(t, a, s, c, fp, dti, args)) } /// Panics if you have previously called [`arg`] or [`args`]. @@ -349,6 +363,6 @@ impl<'a> EnclaveBuilder<'a> { } let c = self.usercall_ext.take(); self.load(loader) - .map(|(t, a, s, fp)| Library::internal_new(t, a, s, c, fp)) + .map(|(t, a, s, fp, dti)| Library::internal_new(t, a, s, c, fp, dti)) } } diff --git a/intel-sgx/enclave-runner/src/usercalls/interface.rs b/intel-sgx/enclave-runner/src/usercalls/interface.rs index 092d403d..b10c7e23 100644 --- a/intel-sgx/enclave-runner/src/usercalls/interface.rs +++ b/intel-sgx/enclave-runner/src/usercalls/interface.rs @@ -222,7 +222,7 @@ impl<'future, 'ioinput: 'future, 'tcs: 'ioinput> Usercalls<'future> for Handler< fn insecure_time( self, - ) -> std::pin::Pin)> + 'future>> { + ) -> std::pin::Pin)> + 'future>> { async move { let ret = Ok(self.0.insecure_time()); return (self, ret); diff --git a/intel-sgx/enclave-runner/src/usercalls/mod.rs b/intel-sgx/enclave-runner/src/usercalls/mod.rs index a1340ddc..7eced57e 100644 --- a/intel-sgx/enclave-runner/src/usercalls/mod.rs +++ b/intel-sgx/enclave-runner/src/usercalls/mod.rs @@ -9,9 +9,10 @@ use std::cell::RefCell; use std::collections::{HashMap, VecDeque}; use std::io::{self, ErrorKind as IoErrorKind, Read, Result as IoResult}; use std::pin::Pin; +use std::ptr; use std::result::Result as StdResult; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, LazyLock}; use std::task::{Context, Poll, Waker}; use std::thread::{self, JoinHandle}; use std::time::{self, Duration}; @@ -31,6 +32,7 @@ use tokio::runtime::Builder as RuntimeBuilder; use tokio::sync::{broadcast, mpsc as async_mpsc, oneshot, Semaphore}; use tokio::sync::broadcast::error::RecvError; use fortanix_sgx_abi::*; +use insecure_time::{Freq, Rdtscp}; use ipc_queue::{DescriptorGuard, Identified, QueueEvent}; use ipc_queue::position::WritePosition; use sgxs::loader::Tcs as SgxsTcs; @@ -50,6 +52,20 @@ lazy_static! { static ref DEBUGGER_TOGGLE_SYNC: Mutex<()> = Mutex::new(()); } +const NANOS_PER_SEC: u64 = 1_000_000_000; + +static mut TIME_INFO: LazyLock> = LazyLock::new(|| { + if Rdtscp::is_supported() { + if let Ok(frequency) = Freq::get() { + return Some(InsecureTimeInfo { + version: 0, + frequency: frequency.as_u64(), + }) + } + } + None +}); + // This is not an event in the sense that it could be passed to `send()` or // `wait()` usercalls in enclave code. However, it's easier for the enclave // runner implementation to lump it in with events. Also note that this constant @@ -645,6 +661,7 @@ pub(crate) struct EnclaveState { usercall_ext: Box, threads_queue: crossbeam::queue::SegQueue, forward_panics: bool, + force_time_usercalls: bool, // Once set to Some, the guards should not be dropped for the lifetime of the enclave. fifo_guards: Mutex>, return_queue_tx: Mutex>>, @@ -718,6 +735,7 @@ impl EnclaveState { usercall_ext: Option>, threads_vector: Vec, forward_panics: bool, + force_time_usercalls: bool, ) -> Arc { let mut fds = FnvHashMap::default(); @@ -758,6 +776,7 @@ impl EnclaveState { usercall_ext, threads_queue, forward_panics, + force_time_usercalls, fifo_guards: Mutex::new(None), return_queue_tx: Mutex::new(None), }) @@ -1085,6 +1104,7 @@ impl EnclaveState { threads: Vec, usercall_ext: Option>, forward_panics: bool, + force_time_usercalls: bool, cmd_args: Vec>, ) -> StdResult<(), anyhow::Error> { let mut event_queues = @@ -1117,7 +1137,7 @@ impl EnclaveState { other_reasons: vec![], }), }); - let enclave = EnclaveState::new(kind, event_queues, usercall_ext, threads, forward_panics); + let enclave = EnclaveState::new(kind, event_queues, usercall_ext, threads, forward_panics, force_time_usercalls); let main_result = EnclaveState::run(enclave.clone(), num_of_worker_threads, main_work); @@ -1168,12 +1188,13 @@ impl EnclaveState { threads: Vec, usercall_ext: Option>, forward_panics: bool, + force_time_usercalls: bool, ) -> Arc { let event_queues = FnvHashMap::with_capacity_and_hasher(threads.len(), Default::default()); let kind = EnclaveKind::Library(Library {}); - let enclave = EnclaveState::new(kind, event_queues, usercall_ext, threads, forward_panics); + let enclave = EnclaveState::new(kind, event_queues, usercall_ext, threads, forward_panics, force_time_usercalls); return enclave; } @@ -1633,11 +1654,20 @@ impl<'tcs> IOHandlerInput<'tcs> { } #[inline(always)] - fn insecure_time(&mut self) -> u64 { + fn insecure_time(&mut self) -> (u64, *const InsecureTimeInfo) { let time = time::SystemTime::now() .duration_since(time::UNIX_EPOCH) .unwrap(); - (time.subsec_nanos() as u64) + time.as_secs() * 1_000_000_000 + let t = (time.subsec_nanos() as u64) + time.as_secs() * NANOS_PER_SEC; + let info = unsafe { match (*TIME_INFO, self.enclave.force_time_usercalls) { + (Some(info), false) => { + &info + }, + _ => { + ptr::null() + } + } }; + (t, info) } #[inline(always)] diff --git a/intel-sgx/fortanix-sgx-abi/Cargo.toml b/intel-sgx/fortanix-sgx-abi/Cargo.toml index da9f8c8e..818cca61 100644 --- a/intel-sgx/fortanix-sgx-abi/Cargo.toml +++ b/intel-sgx/fortanix-sgx-abi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fortanix-sgx-abi" -version = "0.5.0" +version = "0.6.0" authors = ["Fortanix, Inc."] license = "MPL-2.0" description = """ diff --git a/intel-sgx/fortanix-sgx-abi/src/lib.rs b/intel-sgx/fortanix-sgx-abi/src/lib.rs index 17188e6c..e6a11d8e 100644 --- a/intel-sgx/fortanix-sgx-abi/src/lib.rs +++ b/intel-sgx/fortanix-sgx-abi/src/lib.rs @@ -75,6 +75,7 @@ //! synchronously or asynchronously. #![allow(unused)] #![no_std] +#![cfg_attr(feature = "rustc-dep-of-std", allow(internal_features))] #![cfg_attr(feature = "rustc-dep-of-std", feature(staged_api))] #![cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] #![doc(html_logo_url = "https://edp.fortanix.com/img/docs/edp-logo.svg", @@ -566,13 +567,21 @@ impl Usercalls { pub fn send(event_set: u64, tcs: Option) -> Result { unimplemented!() } } +#[repr(C)] +#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))] +#[derive(Copy, Clone, Debug, Default)] +pub struct InsecureTimeInfo { + pub version: u64, + pub frequency: u64, +} + /// # Miscellaneous impl Usercalls { /// This returns the number of nanoseconds since midnight UTC on January 1, /// 1970\. The enclave must not rely on the accuracy of this time for /// security purposes, such as checking credential expiry or preventing /// rollback. - pub fn insecure_time() -> u64 { unimplemented!() } + pub fn insecure_time() -> (u64, *const InsecureTimeInfo) { unimplemented!() } } /// # Memory diff --git a/ipc-queue/Cargo.toml b/ipc-queue/Cargo.toml index 62624c8d..e96d7776 100644 --- a/ipc-queue/Cargo.toml +++ b/ipc-queue/Cargo.toml @@ -14,7 +14,7 @@ keywords = ["sgx", "fifo", "queue", "ipc"] categories = ["asynchronous"] [dependencies] -fortanix-sgx-abi = { version = "0.5.0", path = "../intel-sgx/fortanix-sgx-abi" } +fortanix-sgx-abi = { version = "0.6.0", path = "../intel-sgx/fortanix-sgx-abi" } [dev-dependencies] static_assertions = "1.1.0"