diff --git a/.gitmodules b/.gitmodules index 4a136cff1cd..3fc6e45db3e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -61,10 +61,12 @@ path = src/tools/clang url = https://github.com/rust-lang-nursery/clang.git branch = rust-release-80-v2 - [submodule "src/doc/rustc-guide"] path = src/doc/rustc-guide url = https://github.com/rust-lang/rustc-guide.git [submodule "src/doc/edition-guide"] path = src/doc/edition-guide url = https://github.com/rust-lang-nursery/edition-guide +[submodule "src/rust-sgx"] + path = src/rust-sgx + url = https://github.com/fortanix/rust-sgx diff --git a/Cargo.lock b/Cargo.lock index 02c63200a28..316724ca651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -797,6 +797,14 @@ name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "fortanix-sgx-abi" +version = "0.0.0" +dependencies = [ + "compiler_builtins 0.0.0", + "core 0.0.0", +] + [[package]] name = "fs2" version = "0.4.3" @@ -2773,6 +2781,7 @@ dependencies = [ "compiler_builtins 0.0.0", "core 0.0.0", "dlmalloc 0.0.0", + "fortanix-sgx-abi 0.0.0", "libc 0.0.0", "panic_abort 0.0.0", "panic_unwind 0.0.0", diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index cd8d5642b25..6108692e43c 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -874,6 +874,7 @@ fn run(self, builder: &Builder) -> PathBuf { "src/rustc/compiler_builtins_shim", "src/rustc/libc_shim", "src/rustc/dlmalloc_shim", + "src/rustc/fortanix-sgx-abi_shim", "src/libtest", "src/libterm", "src/libprofiler_builtins", diff --git a/src/dlmalloc b/src/dlmalloc index c99638dc2ec..de99f4b0c88 160000 --- a/src/dlmalloc +++ b/src/dlmalloc @@ -1 +1 @@ -Subproject commit c99638dc2ecfc750cc1656f6edb2bd062c1e0981 +Subproject commit de99f4b0c886f5916cd1a146464276d65bef61b8 diff --git a/src/libcompiler_builtins b/src/libcompiler_builtins index fe74674f6e4..10f4f35f967 160000 --- a/src/libcompiler_builtins +++ b/src/libcompiler_builtins @@ -1 +1 @@ -Subproject commit fe74674f6e4be76d47b66f67d529ebf4186f4eb1 +Subproject commit 10f4f35f9670bb29715a8c1ec01284852d47ed35 diff --git a/src/liblibc b/src/liblibc index c75ca6465a1..5b403753da9 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit c75ca6465a139704e00295be355b1f067af2f535 +Subproject commit 5b403753da9ec8ff501adf34cb6d63b319b4a3ae diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index 9235f8e7660..95c3514185e 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -66,6 +66,12 @@ unsafe fn abort() -> ! { unsafe fn abort() -> ! { core::intrinsics::abort(); } + + #[cfg(target_env="sgx")] + unsafe fn abort() -> ! { + extern "C" { pub fn panic_exit() -> !; } + panic_exit(); + } } // This... is a bit of an oddity. The tl;dr; is that this is required to link diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index cfe671c626b..49f8a429126 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -62,7 +62,7 @@ if #[cfg(target_os = "emscripten")] { #[path = "emcc.rs"] mod imp; - } else if #[cfg(target_arch = "wasm32")] { + } else if #[cfg(any(target_arch = "wasm32", target_env = "sgx"))] { #[path = "dummy.rs"] mod imp; } else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] { diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index cae2f405318..274d5bec662 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -35,9 +35,12 @@ rustc_lsan = { path = "../librustc_lsan" } rustc_msan = { path = "../librustc_msan" } rustc_tsan = { path = "../librustc_tsan" } -[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] +[target.'cfg(any(all(target_arch = "wasm32", not(target_os = "emscripten")), target_env = "sgx"))'.dependencies] dlmalloc = { path = '../rustc/dlmalloc_shim' } +[target.x86_64-fortanix-unknown-sgx.dependencies] +fortanix-sgx-abi = { path = "../rustc/fortanix-sgx-abi_shim" } + [build-dependencies] cc = "1.0" build_helper = { path = "../build_helper" } diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs index 386de080b85..32e29962760 100644 --- a/src/libstd/io/error.rs +++ b/src/libstd/io/error.rs @@ -184,7 +184,7 @@ pub enum ErrorKind { } impl ErrorKind { - fn as_str(&self) -> &'static str { + pub(crate) fn as_str(&self) -> &'static str { match *self { ErrorKind::NotFound => "entity not found", ErrorKind::PermissionDenied => "permission denied", diff --git a/src/libstd/io/lazy.rs b/src/libstd/io/lazy.rs index 24965ff6931..c2aaeb98907 100644 --- a/src/libstd/io/lazy.rs +++ b/src/libstd/io/lazy.rs @@ -26,6 +26,7 @@ const fn done() -> *mut Arc { 1_usize as *mut _ } unsafe impl Sync for Lazy {} impl Lazy { + #[unstable(feature = "sys_internals", issue = "0")] // FIXME: min_const_fn pub const fn new() -> Lazy { Lazy { lock: Mutex::new(), diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 78109a1a690..66771b3c4a2 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -312,6 +312,8 @@ #![feature(non_exhaustive)] #![feature(alloc_layout_extra)] #![feature(maybe_uninit)] +#![cfg_attr(target_env = "sgx", feature(global_asm, range_contains, slice_index_methods, + decl_macro, coerce_unsized))] #![default_lib_allocator] @@ -354,6 +356,12 @@ // testing gives test-std access to real-std lang items and globals. See #2912 #[cfg(test)] extern crate std as realstd; +#[cfg(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. #[macro_use] mod macros; diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index ff35325ab4f..1ac0bdf922f 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -16,10 +16,11 @@ use option; use sys::net::netc as c; use sys_common::{FromInner, AsInner, IntoInner}; -use sys_common::net::lookup_host; +use sys_common::net::LookupHost; use vec; use iter; use slice; +use convert::TryInto; /// An internet socket address, either IPv4 or IPv6. /// @@ -863,9 +864,9 @@ fn to_socket_addrs(&self) -> io::Result> { } } -fn resolve_socket_addr(s: &str, p: u16) -> io::Result> { - let ips = lookup_host(s)?; - let v: Vec<_> = ips.map(|mut a| { a.set_port(p); a }).collect(); +fn resolve_socket_addr(lh: LookupHost) -> io::Result> { + let p = lh.port(); + let v: Vec<_> = lh.map(|mut a| { a.set_port(p); a }).collect(); Ok(v.into_iter()) } @@ -885,7 +886,7 @@ fn to_socket_addrs(&self) -> io::Result> { return Ok(vec![SocketAddr::V6(addr)].into_iter()) } - resolve_socket_addr(host, port) + resolve_socket_addr((host, port).try_into()?) } } @@ -899,22 +900,7 @@ fn to_socket_addrs(&self) -> io::Result> { return Ok(vec![addr].into_iter()); } - macro_rules! try_opt { - ($e:expr, $msg:expr) => ( - match $e { - Some(r) => r, - None => return Err(io::Error::new(io::ErrorKind::InvalidInput, - $msg)), - } - ) - } - - // split the string by ':' and convert the second part to u16 - let mut parts_iter = self.rsplitn(2, ':'); - let port_str = try_opt!(parts_iter.next(), "invalid socket address"); - let host = try_opt!(parts_iter.next(), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - resolve_socket_addr(host, port) + resolve_socket_addr(self.try_into()?) } } diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index be4bcee8a68..ff579a5feb1 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -112,11 +112,15 @@ fn hton(i: I) -> I { i.to_be() } fn ntoh(i: I) -> I { I::from_be(i) } fn each_addr(addr: A, mut f: F) -> io::Result - where F: FnMut(&SocketAddr) -> io::Result + where F: FnMut(io::Result<&SocketAddr>) -> io::Result { + let addrs = match addr.to_socket_addrs() { + Ok(addrs) => addrs, + Err(e) => return f(Err(e)) + }; let mut last_err = None; - for addr in addr.to_socket_addrs()? { - match f(&addr) { + for addr in addrs { + match f(Ok(&addr)) { Ok(l) => return Ok(l), Err(e) => last_err = Some(e), } diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 4930d356608..b70d56f9e59 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -29,7 +29,7 @@ use mem; use ptr; use raw; -use sys::stdio::{Stderr, stderr_prints_nothing}; +use sys::stdio::panic_output; use sys_common::rwlock::RWLock; use sys_common::thread_info; use sys_common::util; @@ -193,7 +193,6 @@ fn default_hook(info: &PanicInfo) { None => "Box", } }; - let mut err = Stderr::new().ok(); let thread = thread_info::current_thread(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or(""); @@ -215,17 +214,14 @@ fn default_hook(info: &PanicInfo) { } }; - let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take()); - match (prev, err.as_mut()) { - (Some(mut stderr), _) => { - write(&mut *stderr); - let mut s = Some(stderr); - LOCAL_STDERR.with(|slot| { - *slot.borrow_mut() = s.take(); - }); - } - (None, Some(ref mut err)) => { write(err) } - _ => {} + if let Some(mut local) = LOCAL_STDERR.with(|s| s.borrow_mut().take()) { + write(&mut *local); + let mut s = Some(local); + LOCAL_STDERR.with(|slot| { + *slot.borrow_mut() = s.take(); + }); + } else if let Some(mut out) = panic_output() { + write(&mut out); } } @@ -485,7 +481,7 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp, // Some platforms know that printing to stderr won't ever actually // print anything, and if that's the case we can skip the default // hook. - Hook::Default if stderr_prints_nothing() => {} + Hook::Default if panic_output().is_none() => {} Hook::Default => { info.set_payload(payload.get()); default_hook(&info); @@ -494,7 +490,7 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp, info.set_payload(payload.get()); (*ptr)(&info); } - } + }; HOOK_LOCK.read_unlock(); } diff --git a/src/libstd/sys/cloudabi/shims/net.rs b/src/libstd/sys/cloudabi/shims/net.rs index 93eaf6a9e7d..7229e71d175 100644 --- a/src/libstd/sys/cloudabi/shims/net.rs +++ b/src/libstd/sys/cloudabi/shims/net.rs @@ -13,13 +13,14 @@ use net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use time::Duration; use sys::{unsupported, Void}; +use convert::TryFrom; pub extern crate libc as netc; pub struct TcpStream(Void); impl TcpStream { - pub fn connect(_: &SocketAddr) -> io::Result { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { unsupported() } @@ -105,7 +106,7 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { pub struct TcpListener(Void); impl TcpListener { - pub fn bind(_: &SocketAddr) -> io::Result { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { unsupported() } @@ -155,7 +156,7 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { pub struct UdpSocket(Void); impl UdpSocket { - pub fn bind(_: &SocketAddr) -> io::Result { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { unsupported() } @@ -271,7 +272,7 @@ pub fn send(&self, _: &[u8]) -> io::Result { match self.0 {} } - pub fn connect(&self, _: &SocketAddr) -> io::Result<()> { + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { match self.0 {} } } @@ -284,6 +285,12 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { pub struct LookupHost(Void); +impl LookupHost { + pub fn port(&self) -> u16 { + match self.0 {} + } +} + impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -291,6 +298,18 @@ fn next(&mut self) -> Option { } } -pub fn lookup_host(_: &str) -> io::Result { - unsupported() +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unsupported() + } } diff --git a/src/libstd/sys/cloudabi/stdio.rs b/src/libstd/sys/cloudabi/stdio.rs index 1d7344f921c..c90dbd8beab 100644 --- a/src/libstd/sys/cloudabi/stdio.rs +++ b/src/libstd/sys/cloudabi/stdio.rs @@ -78,6 +78,6 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; -pub fn stderr_prints_nothing() -> bool { - false +pub fn panic_output() -> Option { + Stderr::new().ok() } diff --git a/src/libstd/sys/cloudabi/thread.rs b/src/libstd/sys/cloudabi/thread.rs index 8cca47efd22..a76e1fa3345 100644 --- a/src/libstd/sys/cloudabi/thread.rs +++ b/src/libstd/sys/cloudabi/thread.rs @@ -32,7 +32,8 @@ unsafe impl Send for Thread {} unsafe impl Sync for Thread {} impl Thread { - pub unsafe fn new<'a>(stack: usize, p: Box) -> io::Result { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box) -> io::Result { let p = box p; let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs index 61e4ce66eec..04c47aeb827 100644 --- a/src/libstd/sys/mod.rs +++ b/src/libstd/sys/mod.rs @@ -48,6 +48,9 @@ } else if #[cfg(target_arch = "wasm32")] { mod wasm; pub use self::wasm::*; + } else if #[cfg(target_env = "sgx")] { + mod sgx; + pub use self::sgx::*; } else { compile_error!("libstd doesn't compile for this platform yet"); } diff --git a/src/libstd/sys/redox/net/mod.rs b/src/libstd/sys/redox/net/mod.rs index 67f22231d5f..04a183f2417 100644 --- a/src/libstd/sys/redox/net/mod.rs +++ b/src/libstd/sys/redox/net/mod.rs @@ -9,7 +9,7 @@ // except according to those terms. use fs::File; -use io::{Error, Result, Read}; +use io::{Error, Read, self}; use iter::Iterator; use net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use str::FromStr; @@ -17,6 +17,7 @@ use sys::syscall::EINVAL; use time::{self, Duration}; use vec::{IntoIter, Vec}; +use convert::{TryFrom, TryInto}; use self::dns::{Dns, DnsQuery}; @@ -29,7 +30,13 @@ mod tcp; mod udp; -pub struct LookupHost(IntoIter); +pub struct LookupHost(IntoIter, u16); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.1 + } +} impl Iterator for LookupHost { type Item = SocketAddr; @@ -38,65 +45,93 @@ fn next(&mut self) -> Option { } } -pub fn lookup_host(host: &str) -> Result { - let mut ip_string = String::new(); - File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?; - let ip: Vec = ip_string.trim().split('.').map(|part| part.parse::() - .unwrap_or(0)).collect(); +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; - let mut dns_string = String::new(); - File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?; - let dns: Vec = dns_string.trim().split('.').map(|part| part.parse::() - .unwrap_or(0)).collect(); - - if ip.len() == 4 && dns.len() == 4 { - let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap(); - let tid = (time.subsec_nanos() >> 16) as u16; - - let packet = Dns { - transaction_id: tid, - flags: 0x0100, - queries: vec![DnsQuery { - name: host.to_string(), - q_type: 0x0001, - q_class: 0x0001, - }], - answers: vec![] - }; - - let packet_data = packet.compile(); - - let my_ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]); - let dns_ip = Ipv4Addr::new(dns[0], dns[1], dns[2], dns[3]); - let socket = UdpSocket::bind(&SocketAddr::V4(SocketAddrV4::new(my_ip, 0)))?; - socket.set_read_timeout(Some(Duration::new(5, 0)))?; - socket.set_write_timeout(Some(Duration::new(5, 0)))?; - socket.connect(&SocketAddr::V4(SocketAddrV4::new(dns_ip, 53)))?; - socket.send(&packet_data)?; - - let mut buf = [0; 65536]; - let count = socket.recv(&mut buf)?; - - match Dns::parse(&buf[.. count]) { - Ok(response) => { - let mut addrs = vec![]; - for answer in response.answers.iter() { - if answer.a_type == 0x0001 && answer.a_class == 0x0001 - && answer.data.len() == 4 - { - let answer_ip = Ipv4Addr::new(answer.data[0], - answer.data[1], - answer.data[2], - answer.data[3]); - addrs.push(SocketAddr::V4(SocketAddrV4::new(answer_ip, 0))); - } + fn try_from(s: &str) -> io::Result { + macro_rules! try_opt { + ($e:expr, $msg:expr) => ( + match $e { + Some(r) => r, + None => return Err(io::Error::new(io::ErrorKind::InvalidInput, + $msg)), } - Ok(LookupHost(addrs.into_iter())) - }, - Err(_err) => Err(Error::from_raw_os_error(EINVAL)) + ) + } + + // split the string by ':' and convert the second part to u16 + let mut parts_iter = s.rsplitn(2, ':'); + let port_str = try_opt!(parts_iter.next(), "invalid socket address"); + let host = try_opt!(parts_iter.next(), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + + (host, port).try_into() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result { + let mut ip_string = String::new(); + File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?; + let ip: Vec = ip_string.trim().split('.').map(|part| part.parse::() + .unwrap_or(0)).collect(); + + let mut dns_string = String::new(); + File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?; + let dns: Vec = dns_string.trim().split('.').map(|part| part.parse::() + .unwrap_or(0)).collect(); + + if ip.len() == 4 && dns.len() == 4 { + let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap(); + let tid = (time.subsec_nanos() >> 16) as u16; + + let packet = Dns { + transaction_id: tid, + flags: 0x0100, + queries: vec![DnsQuery { + name: host.to_string(), + q_type: 0x0001, + q_class: 0x0001, + }], + answers: vec![] + }; + + let packet_data = packet.compile(); + + let my_ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]); + let dns_ip = Ipv4Addr::new(dns[0], dns[1], dns[2], dns[3]); + let socket = UdpSocket::bind(Ok(&SocketAddr::V4(SocketAddrV4::new(my_ip, 0))))?; + socket.set_read_timeout(Some(Duration::new(5, 0)))?; + socket.set_write_timeout(Some(Duration::new(5, 0)))?; + socket.connect(Ok(&SocketAddr::V4(SocketAddrV4::new(dns_ip, 53))))?; + socket.send(&packet_data)?; + + let mut buf = [0; 65536]; + let count = socket.recv(&mut buf)?; + + match Dns::parse(&buf[.. count]) { + Ok(response) => { + let mut addrs = vec![]; + for answer in response.answers.iter() { + if answer.a_type == 0x0001 && answer.a_class == 0x0001 + && answer.data.len() == 4 + { + let answer_ip = Ipv4Addr::new(answer.data[0], + answer.data[1], + answer.data[2], + answer.data[3]); + addrs.push(SocketAddr::V4(SocketAddrV4::new(answer_ip, 0))); + } + } + Ok(LookupHost(addrs.into_iter(), port)) + }, + Err(_err) => Err(Error::from_raw_os_error(EINVAL)) + } + } else { + Err(Error::from_raw_os_error(EINVAL)) } - } else { - Err(Error::from_raw_os_error(EINVAL)) } } diff --git a/src/libstd/sys/redox/net/tcp.rs b/src/libstd/sys/redox/net/tcp.rs index b5664908479..37457d87f33 100644 --- a/src/libstd/sys/redox/net/tcp.rs +++ b/src/libstd/sys/redox/net/tcp.rs @@ -24,8 +24,8 @@ pub struct TcpStream(File); impl TcpStream { - pub fn connect(addr: &SocketAddr) -> Result { - let path = format!("tcp:{}", addr); + pub fn connect(addr: Result<&SocketAddr>) -> Result { + let path = format!("tcp:{}", addr?); let mut options = OpenOptions::new(); options.read(true); options.write(true); @@ -180,8 +180,8 @@ fn into_inner(self) -> File { self.0 } pub struct TcpListener(File); impl TcpListener { - pub fn bind(addr: &SocketAddr) -> Result { - let path = format!("tcp:/{}", addr); + pub fn bind(addr: Result<&SocketAddr>) -> Result { + let path = format!("tcp:/{}", addr?); let mut options = OpenOptions::new(); options.read(true); options.write(true); diff --git a/src/libstd/sys/redox/net/udp.rs b/src/libstd/sys/redox/net/udp.rs index 22af02079e7..85bfd425924 100644 --- a/src/libstd/sys/redox/net/udp.rs +++ b/src/libstd/sys/redox/net/udp.rs @@ -25,8 +25,8 @@ pub struct UdpSocket(File, UnsafeCell>); impl UdpSocket { - pub fn bind(addr: &SocketAddr) -> Result { - let path = format!("udp:/{}", addr); + pub fn bind(addr: Result<&SocketAddr>) -> Result { + let path = format!("udp:/{}", addr?); let mut options = OpenOptions::new(); options.read(true); options.write(true); @@ -37,8 +37,8 @@ fn get_conn(&self) -> &mut Option { unsafe { &mut *(self.1.get()) } } - pub fn connect(&self, addr: &SocketAddr) -> Result<()> { - unsafe { *self.1.get() = Some(*addr) }; + pub fn connect(&self, addr: Result<&SocketAddr>) -> Result<()> { + unsafe { *self.1.get() = Some(*addr?) }; Ok(()) } diff --git a/src/libstd/sys/redox/stdio.rs b/src/libstd/sys/redox/stdio.rs index 7a4d11b0ecb..52cd9334ffb 100644 --- a/src/libstd/sys/redox/stdio.rs +++ b/src/libstd/sys/redox/stdio.rs @@ -76,6 +76,6 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; -pub fn stderr_prints_nothing() -> bool { - false +pub fn panic_output() -> Option { + Stderr::new().ok() } diff --git a/src/libstd/sys/redox/thread.rs b/src/libstd/sys/redox/thread.rs index bab91b16e6c..ff861805382 100644 --- a/src/libstd/sys/redox/thread.rs +++ b/src/libstd/sys/redox/thread.rs @@ -28,7 +28,8 @@ unsafe impl Send for Thread {} unsafe impl Sync for Thread {} impl Thread { - pub unsafe fn new<'a>(_stack: usize, p: Box) -> io::Result { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, p: Box) -> io::Result { let p = box p; let id = cvt(syscall::clone(syscall::CLONE_VM | syscall::CLONE_FS | syscall::CLONE_FILES))?; diff --git a/src/libstd/sys/sgx/abi/entry.S b/src/libstd/sys/sgx/abi/entry.S new file mode 100644 index 00000000000..4d5cc02e11e --- /dev/null +++ b/src/libstd/sys/sgx/abi/entry.S @@ -0,0 +1,327 @@ +/* Copyright 2018 The Rust Project Developers. See the COPYRIGHT */ +/* file at the top-level directory of this distribution and at */ +/* http://rust-lang.org/COPYRIGHT. */ +/* */ +/* Licensed under the Apache License, Version 2.0 or the MIT license */ +/* , at your */ +/* option. This file may not be copied, modified, or distributed */ +/* except according to those terms. */ + +/* This symbol is used at runtime to figure out the virtual address that the */ +/* enclave is loaded at. */ +.section absolute +.global IMAGE_BASE +IMAGE_BASE: + +.section .rodata +/* The XSAVE area needs to be a large chunk of readable memory, but since we are */ +/* going to restore everything to its initial state (XSTATE_BV=0), only certain */ +/* parts need to have a defined value. In particular: */ +/* */ +/* * MXCSR in the legacy area. This register is always restored if RFBM[1] or */ +/* RFBM[2] is set, regardless of the value of XSTATE_BV */ +/* * XSAVE header */ +.align 64 +.Lxsave_clear: +.org .+24 +.Lxsave_mxcsr: + .int 0 + +/* We can store a bunch of data in the gap between MXCSR and the XSAVE header */ + +/* The following symbols point at read-only data that will be filled in by the */ +/* post-linker. */ + +/* When using this macro, don't forget to adjust the linker version script! */ +.macro globvar name:req size:req + .global \name + .protected \name + .align \size + .size \name , \size + \name : + .org .+\size +.endm + /* The base address (relative to enclave start) of the heap area */ + globvar HEAP_BASE 8 + /* The heap size in bytes */ + globvar HEAP_SIZE 8 + /* Value of the RELA entry in the dynamic table */ + globvar RELA 8 + /* Value of the RELACOUNT entry in the dynamic table */ + globvar RELACOUNT 8 + /* The enclave size in bytes */ + globvar ENCLAVE_SIZE 8 + /* The base address (relative to enclave start) of the enclave configuration area */ + globvar CFGDATA_BASE 8 + /* Non-zero if debugging is enabled, zero otherwise */ + globvar DEBUG 1 + +.Lreentry_panic_msg: + .asciz "Re-entered panicked 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 */ + .int 0, 0 /* XCOMP_BV */ + .org .+48 /* reserved bits */ + +.data +.Lpanicked: + .byte 0 + +/* TCS local storage section */ +.equ tcsls_tos, 0x00 /* initialized by loader to *offset* from image base to TOS */ +.equ tcsls_flags, 0x08 /* initialized by loader */ +.equ tcsls_flag_secondary, 0 /* initialized by loader; 0 = standard TCS, 1 = secondary TCS */ +.equ tcsls_flag_init_once, 1 /* initialized by loader to 0 */ +/* 14 unused bits */ +.equ tcsls_user_fcw, 0x0a +.equ tcsls_user_mxcsr, 0x0c +.equ tcsls_last_rsp, 0x10 /* initialized by loader to 0 */ +.equ tcsls_panic_last_rsp, 0x18 /* initialized by loader to 0 */ +.equ tcsls_debug_panic_buf_ptr, 0x20 /* initialized by loader to 0 */ +.equ tcsls_user_rsp, 0x28 +.equ tcsls_user_retip, 0x30 +.equ tcsls_user_rbp, 0x38 +.equ tcsls_user_r12, 0x40 +.equ tcsls_user_r13, 0x48 +.equ tcsls_user_r14, 0x50 +.equ tcsls_user_r15, 0x58 +.equ tcsls_tls_ptr, 0x60 +.equ tcsls_tcs_addr, 0x68 + +.macro load_tcsls_flag_secondary_bool reg:req comments:vararg + .ifne tcsls_flag_secondary /* to convert to a bool, must be the first bit */ + .abort + .endif + mov $(1< ! { + panic!(msg) +} + +// Called once when a TCS is first entered +extern "C" fn tcs_init(secondary: bool); + +// Standard TCS entrypoint +extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64); +``` +*/ + +.global get_tcs_addr +get_tcs_addr: + mov %gs:tcsls_tcs_addr,%rax + ret + +.global get_tls_ptr +get_tls_ptr: + mov %gs:tcsls_tls_ptr,%rax + ret + +.global set_tls_ptr +set_tls_ptr: + mov %rdi,%gs:tcsls_tls_ptr + ret + +.global take_debug_panic_buf_ptr +take_debug_panic_buf_ptr: + xor %rax,%rax + xchg %gs:tcsls_debug_panic_buf_ptr,%rax + ret diff --git a/src/libstd/sys/sgx/abi/mem.rs b/src/libstd/sys/sgx/abi/mem.rs new file mode 100644 index 00000000000..508f2ff4d4f --- /dev/null +++ b/src/libstd/sys/sgx/abi/mem.rs @@ -0,0 +1,49 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Do not remove inline: will result in relocation failure +#[inline(always)] +pub unsafe fn rel_ptr(offset: u64) -> *const T { + (image_base() + offset) as *const T +} + +// Do not remove inline: will result in relocation failure +#[inline(always)] +pub unsafe fn rel_ptr_mut(offset: u64) -> *mut T { + (image_base() + offset) as *mut T +} + +extern { + static ENCLAVE_SIZE: usize; +} + +// Do not remove inline: will result in relocation failure +// For the same reason we use inline ASM here instead of an extern static to +// locate the base +#[inline(always)] +fn image_base() -> u64 { + let base; + unsafe { asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) }; + base +} + +pub fn is_enclave_range(p: *const u8, len: usize) -> bool { + let start=p as u64; + let end=start + (len as u64); + start >= image_base() && + end <= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant +} + +pub fn is_user_range(p: *const u8, len: usize) -> bool { + let start=p as u64; + let end=start + (len as u64); + end <= image_base() || + start >= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant +} diff --git a/src/libstd/sys/sgx/abi/mod.rs b/src/libstd/sys/sgx/abi/mod.rs new file mode 100644 index 00000000000..069cca3b98e --- /dev/null +++ b/src/libstd/sys/sgx/abi/mod.rs @@ -0,0 +1,99 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::sync::atomic::{AtomicUsize, Ordering}; +use io::Write; + +// runtime features +mod reloc; +mod mem; +pub(super) mod panic; + +// library features +pub mod thread; +pub mod tls; +#[macro_use] +pub mod usercalls; + +global_asm!(concat!(usercalls_asm!(), include_str!("entry.S"))); + +#[no_mangle] +unsafe extern "C" fn tcs_init(secondary: bool) { + // Be very careful when changing this code: it runs before the binary has been + // relocated. Any indirect accesses to symbols will likely fail. + const UNINIT: usize = 0; + const BUSY: usize = 1; + const DONE: usize = 2; + // Three-state spin-lock + static RELOC_STATE: AtomicUsize = AtomicUsize::new(UNINIT); + + if secondary && RELOC_STATE.load(Ordering::Relaxed) != DONE { + panic::panic_msg("Entered secondary TCS before main TCS!") + } + + // Try to atomically swap UNINIT with BUSY. The returned state can be: + match RELOC_STATE.compare_and_swap(UNINIT, BUSY, Ordering::Acquire) { + // This thread just obtained the lock and other threads will observe BUSY + UNINIT => { + reloc::relocate_elf_rela(); + RELOC_STATE.store(DONE, Ordering::Release); + }, + // We need to wait until the initialization is done. + BUSY => while RELOC_STATE.load(Ordering::Acquire) == BUSY { + ::core::arch::x86_64::_mm_pause() + }, + // Initialization is done. + DONE => {}, + _ => unreachable!() + } +} + +// FIXME: this item should only exist if this is linked into an executable +// (main function exists). If this is a library, the crate author should be +// able to specify this +#[no_mangle] +extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) { + // FIXME: how to support TLS in library mode? + let tls = Box::new(tls::Tls::new()); + let _tls_guard = unsafe { tls.activate() }; + + if secondary { + super::thread::Thread::entry(); + + (0, 0) + } else { + extern "C" { + fn main(argc: isize, argv: *const *const u8) -> isize; + } + + // check entry is being called according to ABI + assert_eq!(p3, 0); + assert_eq!(p4, 0); + assert_eq!(p5, 0); + + unsafe { + // The actual types of these arguments are `p1: *const Arg, p2: + // usize`. We can't currently customize the argument list of Rust's + // main function, so we pass these in as the standard pointer-sized + // values in `argc` and `argv`. + let ret = main(p2 as _, p1 as _); + exit_with_code(ret) + } + } +} + +pub(super) fn exit_with_code(code: isize) -> ! { + if code != 0 { + if let Some(mut out) = panic::SgxPanicOutput::new() { + let _ = write!(out, "Exited with status code {}", code); + } + } + usercalls::exit(code != 0); +} diff --git a/src/libstd/sys/sgx/abi/panic.rs b/src/libstd/sys/sgx/abi/panic.rs new file mode 100644 index 00000000000..dd9159b9fe2 --- /dev/null +++ b/src/libstd/sys/sgx/abi/panic.rs @@ -0,0 +1,58 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io::{self, Write}; +use slice::from_raw_parts_mut; + +extern "C" { + fn take_debug_panic_buf_ptr() -> *mut u8; + static DEBUG: u8; +} + +pub(crate) struct SgxPanicOutput(Option<&'static mut [u8]>); + +impl SgxPanicOutput { + pub(crate) fn new() -> Option { + if unsafe { DEBUG == 0 } { + None + } else { + Some(SgxPanicOutput(None)) + } + } + + fn init(&mut self) -> &mut &'static mut [u8] { + self.0.get_or_insert_with(|| unsafe { + let ptr = take_debug_panic_buf_ptr(); + if ptr.is_null() { + &mut [] + } else { + from_raw_parts_mut(ptr, 1024) + } + }) + } +} + +impl Write for SgxPanicOutput { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.init().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.init().flush() + } +} + +#[no_mangle] +pub extern "C" fn panic_msg(msg: &str) -> ! { + let _ = SgxPanicOutput::new().map(|mut out| out.write(msg.as_bytes())); + unsafe { panic_exit(); } +} + +extern "C" { pub fn panic_exit() -> !; } diff --git a/src/libstd/sys/sgx/abi/reloc.rs b/src/libstd/sys/sgx/abi/reloc.rs new file mode 100644 index 00000000000..2d5e14d6ad1 --- /dev/null +++ b/src/libstd/sys/sgx/abi/reloc.rs @@ -0,0 +1,40 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use slice::from_raw_parts; +use super::mem; + +const R_X86_64_RELATIVE: u32 = 8; + +#[repr(packed)] +struct Rela { + offset: T, + info: T, + addend: T, +} + +pub fn relocate_elf_rela() { + extern { + static RELA: u64; + static RELACOUNT: usize; + } + + if unsafe { RELACOUNT } == 0 { return } // unsafe ok: link-time constant + + let relas = unsafe { + from_raw_parts::>(mem::rel_ptr(RELA), RELACOUNT) // unsafe ok: link-time constant + }; + for rela in relas { + if rela.info != (/*0 << 32 |*/ R_X86_64_RELATIVE as u64) { + panic!("Invalid relocation"); + } + unsafe { *mem::rel_ptr_mut::<*const ()>(rela.offset) = mem::rel_ptr(rela.addend) }; + } +} diff --git a/src/libstd/sys/sgx/abi/thread.rs b/src/libstd/sys/sgx/abi/thread.rs new file mode 100644 index 00000000000..4640b812fea --- /dev/null +++ b/src/libstd/sys/sgx/abi/thread.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi::Tcs; + +/// Get the ID for the current thread. The ID is guaranteed to be unique among +/// all currently running threads in the enclave, and it is guaranteed to be +/// constant for the lifetime of the thread. More specifically for SGX, there +/// is a one-to-one correspondence of the ID to the address of the TCS. +pub fn current() -> Tcs { + extern "C" { fn get_tcs_addr() -> Tcs; } + unsafe { get_tcs_addr() } +} diff --git a/src/libstd/sys/sgx/abi/tls.rs b/src/libstd/sys/sgx/abi/tls.rs new file mode 100644 index 00000000000..ab7822182a5 --- /dev/null +++ b/src/libstd/sys/sgx/abi/tls.rs @@ -0,0 +1,246 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; +use ptr; +use mem; +use cell::Cell; +use num::NonZeroUsize; +use self::sync_bitset::*; + +#[cfg(target_pointer_width="64")] +const USIZE_BITS: usize = 64; +const TLS_KEYS: usize = 128; // Same as POSIX minimum +const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; + +static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; +macro_rules! dup { + ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); + (() $($val:tt)*) => ([$($val),*]) +} +static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) ATOMIC_USIZE_INIT); + +extern "C" { + fn get_tls_ptr() -> *const u8; + fn set_tls_ptr(tls: *const u8); +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Key(NonZeroUsize); + +impl Key { + fn to_index(self) -> usize { + self.0.get() - 1 + } + + fn from_index(index: usize) -> Self { + Key(NonZeroUsize::new(index + 1).unwrap()) + } + + pub fn as_usize(self) -> usize { + self.0.get() + } + + pub fn from_usize(index: usize) -> Self { + Key(NonZeroUsize::new(index).unwrap()) + } +} + +#[repr(C)] +pub struct Tls { + data: [Cell<*mut u8>; TLS_KEYS] +} + +pub struct ActiveTls<'a> { + tls: &'a Tls +} + +impl<'a> Drop for ActiveTls<'a> { + fn drop(&mut self) { + let value_with_destructor = |key: usize| { + let ptr = TLS_DESTRUCTOR[key].load(Ordering::Relaxed); + unsafe { mem::transmute::<_,Option>(ptr) } + .map(|dtor| (&self.tls.data[key], dtor)) + }; + + let mut any_non_null_dtor = true; + while any_non_null_dtor { + any_non_null_dtor = false; + for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) { + let value = value.replace(ptr::null_mut()); + if value != ptr::null_mut() { + any_non_null_dtor = true; + unsafe { dtor(value) } + } + } + } + } +} + +impl Tls { + pub fn new() -> Tls { + Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) } + } + + pub unsafe fn activate(&self) -> ActiveTls { + set_tls_ptr(self as *const Tls as _); + ActiveTls { tls: self } + } + + #[allow(unused)] + pub unsafe fn activate_persistent(self: Box) { + set_tls_ptr((&*self) as *const Tls as _); + mem::forget(self); + } + + unsafe fn current<'a>() -> &'a Tls { + &*(get_tls_ptr() as *const Tls) + } + + pub fn create(dtor: Option) -> Key { + let index = TLS_KEY_IN_USE.set().expect("TLS limit exceeded"); + TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed); + Key::from_index(index) + } + + pub fn set(key: Key, value: *mut u8) { + let index = key.to_index(); + assert!(TLS_KEY_IN_USE.get(index)); + unsafe { Self::current() }.data[index].set(value); + } + + pub fn get(key: Key) -> *mut u8 { + let index = key.to_index(); + assert!(TLS_KEY_IN_USE.get(index)); + unsafe { Self::current() }.data[index].get() + } + + pub fn destroy(key: Key) { + TLS_KEY_IN_USE.clear(key.to_index()); + } +} + +mod sync_bitset { + use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + use iter::{Enumerate, Peekable}; + use slice::Iter; + use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS}; + + /// A bitset that can be used synchronously. + pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]); + + pub(super) const SYNC_BITSET_INIT: SyncBitset = + SyncBitset([ATOMIC_USIZE_INIT, ATOMIC_USIZE_INIT]); + + impl SyncBitset { + pub fn get(&self, index: usize) -> bool { + let (hi, lo) = Self::split(index); + (self.0[hi].load(Ordering::Relaxed) & lo) != 0 + } + + /// Not atomic. + pub fn iter(&self) -> SyncBitsetIter { + SyncBitsetIter { + iter: self.0.iter().enumerate().peekable(), + elem_idx: 0, + } + } + + pub fn clear(&self, index: usize) { + let (hi, lo) = Self::split(index); + self.0[hi].fetch_and(!lo, Ordering::Relaxed); + } + + /// Set any unset bit. Not atomic. Returns `None` if all bits were + /// observed to be set. + pub fn set(&self) -> Option { + 'elems: for (idx, elem) in self.0.iter().enumerate() { + let mut current = elem.load(Ordering::Relaxed); + loop { + if 0 == !current { + continue 'elems; + } + let trailing_ones = (!current).trailing_zeros() as usize; + match elem.compare_exchange( + current, + current | (1 << trailing_ones), + Ordering::AcqRel, + Ordering::Relaxed + ) { + Ok(_) => return Some(idx * USIZE_BITS + trailing_ones), + Err(previous) => current = previous, + } + } + } + None + } + + fn split(index: usize) -> (usize, usize) { + (index / USIZE_BITS, 1 << (index % USIZE_BITS)) + } + } + + pub(super) struct SyncBitsetIter<'a> { + iter: Peekable>>, + elem_idx: usize, + } + + impl<'a> Iterator for SyncBitsetIter<'a> { + type Item = usize; + + fn next(&mut self) -> Option { + self.iter.peek().cloned().and_then(|(idx, elem)| { + let elem = elem.load(Ordering::Relaxed); + let low_mask = (1 << self.elem_idx) - 1; + let next = elem & !low_mask; + let next_idx = next.trailing_zeros() as usize; + self.elem_idx = next_idx + 1; + if self.elem_idx >= 64 { + self.elem_idx = 0; + self.iter.next(); + } + match next_idx { + 64 => self.next(), + _ => Some(idx * USIZE_BITS + next_idx), + } + }) + } + } + + #[cfg(test)] + mod tests { + use super::*; + + fn test_data(bitset: [usize; 2], bit_indices: &[usize]) { + let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]); + assert_eq!(set.iter().collect::>(), bit_indices); + for &i in bit_indices { + assert!(set.get(i)); + } + } + + #[test] + fn iter() { + test_data([0b0110_1001, 0], &[0, 3, 5, 6]); + test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]); + test_data([0, 0], &[]); + } + + #[test] + fn set_get_clear() { + let set = SYNC_BITSET_INIT; + let key = set.set().unwrap(); + assert!(set.get(key)); + set.clear(key); + assert!(!set.get(key)); + } + } +} diff --git a/src/libstd/sys/sgx/abi/usercalls/alloc.rs b/src/libstd/sys/sgx/abi/usercalls/alloc.rs new file mode 100644 index 00000000000..64968a9970c --- /dev/null +++ b/src/libstd/sys/sgx/abi/usercalls/alloc.rs @@ -0,0 +1,404 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] + +use ptr; +use mem; +use cell::UnsafeCell; +use slice; +use ops::{Deref, DerefMut, Index, IndexMut, CoerceUnsized}; +use slice::SliceIndex; + +use fortanix_sgx_abi::*; +use super::super::mem::is_user_range; + +/// A type that can be safely read from or written to userspace. +/// +/// Non-exhaustive list of specific requirements for reading and writing: +/// * **Type is `Copy`** (and therefore also not `Drop`). Copies will be +/// created when copying from/to userspace. Destructors will not be called. +/// * **No references or Rust-style owned pointers** (`Vec`, `Arc`, etc.). When +/// reading from userspace, references into enclave memory must not be +/// created. Also, only enclave memory is considered managed by the Rust +/// compiler's static analysis. When reading from userspace, there can be no +/// guarantee that the value correctly adheres to the expectations of the +/// type. When writing to userspace, memory addresses of data in enclave +/// memory must not be leaked for confidentiality reasons. `User` and +/// `UserRef` are also not allowed for the same reasons. +/// * **No fat pointers.** When reading from userspace, the size or vtable +/// pointer could be automatically interpreted and used by the code. When +/// writing to userspace, memory addresses of data in enclave memory (such +/// as vtable pointers) must not be leaked for confidentiality reasons. +/// +/// Non-exhaustive list of specific requirements for reading from userspace: +/// * Any bit pattern is valid for this type (no `enum`s). There can be no +/// guarantee that the value correctly adheres to the expectations of the +/// type, so any value must be valid for this type. +/// +/// Non-exhaustive list of specific requirements for writing to userspace: +/// * No pointers to enclave memory. Memory addresses of data in enclave memory +/// must not be leaked for confidentiality reasons. +/// * No internal padding. Padding might contain previously-initialized secret +/// data stored at that memory location and must not be leaked for +/// confidentiality reasons. +pub unsafe trait UserSafeSized: Copy + Sized {} + +unsafe impl UserSafeSized for u8 {} +unsafe impl UserSafeSized for FifoDescriptor {} +unsafe impl UserSafeSized for ByteBuffer {} +unsafe impl UserSafeSized for Usercall {} +unsafe impl UserSafeSized for Return {} +unsafe impl UserSafeSized for [T; 2] {} + +/// A type that can be represented in memory as one or more `UserSafeSized`s. +pub unsafe trait UserSafe { + unsafe fn align_of() -> usize; + + /// NB. This takes a size, not a length! + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self; + + /// NB. This takes a size, not a length! + unsafe fn from_raw_sized(ptr: *const u8, size: usize) -> *const Self { + let ret = Self::from_raw_sized_unchecked(ptr, size); + Self::check_ptr(ret); + ret + } + + unsafe fn check_ptr(ptr: *const Self) { + let is_aligned = |p| -> bool { + 0 == (p as usize) & (Self::align_of() - 1) + }; + + assert!(is_aligned(ptr as *const u8)); + assert!(is_user_range(ptr as _, mem::size_of_val(&*ptr))); + assert!(!ptr.is_null()); + } +} + +unsafe impl UserSafe for T { + unsafe fn align_of() -> usize { + mem::align_of::() + } + + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self { + assert_eq!(size, mem::size_of::()); + ptr as _ + } +} + +unsafe impl UserSafe for [T] { + unsafe fn align_of() -> usize { + mem::align_of::() + } + + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self { + let elem_size = mem::size_of::(); + assert_eq!(size % elem_size, 0); + let len = size / elem_size; + slice::from_raw_parts(ptr as _, len) + } +} + +/// A reference to some type in userspace memory. `&UserRef` is equivalent +/// to `&T` in enclave memory. Access to the memory is only allowed by copying +/// to avoid TOCTTOU issues. After copying, code should make sure to completely +/// check the value before use. +pub struct UserRef(UnsafeCell); +/// An owned type in userspace memory. `User` is equivalent to `Box` in +/// enclave memory. Access to the memory is only allowed by copying to avoid +/// TOCTTOU issues. The user memory will be freed when the value is dropped. +/// After copying, code should make sure to completely check the value before +/// use. +pub struct User(*mut UserRef); + +impl User where T: UserSafe { + // This function returns memory that is practically uninitialized, but is + // not considered "unspecified" or "undefined" for purposes of an + // optimizing compiler. This is achieved by returning a pointer from + // from outside as obtained by `super::alloc`. + fn new_uninit_bytes(size: usize) -> Self { + unsafe { + let ptr = super::alloc(size, T::align_of()).expect("User memory allocation failed"); + User(T::from_raw_sized(ptr as _, size) as _) + } + } + + pub fn new_from_enclave(val: &T) -> Self { + unsafe { + let ret = Self::new_uninit_bytes(mem::size_of_val(val)); + ptr::copy( + val as *const T as *const u8, + ret.0 as *mut T as *mut u8, + mem::size_of_val(val) + ); + ret + } + } + + /// Create an owned `User` from a raw pointer. The pointer should be + /// freeable with the `free` usercall and the alignment of `T`. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_raw(ptr: *mut T) -> Self { + T::check_ptr(ptr); + User(ptr as _) + } + + /// Convert this value into a raw pointer. The value will no longer be + /// automatically freed. + pub fn into_raw(self) -> *mut T { + let ret = self.0; + mem::forget(self); + ret as _ + } +} + +impl User where T: UserSafe { + pub fn uninitialized() -> Self { + Self::new_uninit_bytes(mem::size_of::()) + } +} + +impl User<[T]> where [T]: UserSafe { + pub fn uninitialized(n: usize) -> Self { + Self::new_uninit_bytes(n * mem::size_of::()) + } + + /// Create an owned `User<[T]>` from a raw thin pointer and a slice length. + /// The pointer should be freeable with the `free` usercall and the + /// alignment of `T`. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self { + User(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()) as _) + } +} + +impl UserRef where T: UserSafe { + /// Create a `&UserRef<[T]>` from a raw pointer. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_ptr<'a>(ptr: *const T) -> &'a Self { + T::check_ptr(ptr); + &*(ptr as *const Self) + } + + /// Create a `&mut UserRef<[T]>` from a raw pointer. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_mut_ptr<'a>(ptr: *mut T) -> &'a mut Self { + T::check_ptr(ptr); + &mut*(ptr as *mut Self) + } + + /// # Panics + /// This function panics if the destination doesn't have the same size as + /// the source. This can happen for dynamically-sized types such as slices. + pub fn copy_from_enclave(&mut self, val: &T) { + unsafe { + assert_eq!(mem::size_of_val(val), mem::size_of_val( &*self.0.get() )); + ptr::copy( + val as *const T as *const u8, + self.0.get() as *mut T as *mut u8, + mem::size_of_val(val) + ); + } + } + + /// # Panics + /// This function panics if the destination doesn't have the same size as + /// the source. This can happen for dynamically-sized types such as slices. + pub fn copy_to_enclave(&self, dest: &mut T) { + unsafe { + assert_eq!(mem::size_of_val(dest), mem::size_of_val( &*self.0.get() )); + ptr::copy( + self.0.get() as *const T as *const u8, + dest as *mut T as *mut u8, + mem::size_of_val(dest) + ); + } + } + + pub fn as_raw_ptr(&self) -> *const T { + self as *const _ as _ + } + + pub fn as_raw_mut_ptr(&mut self) -> *mut T { + self as *mut _ as _ + } +} + +impl UserRef where T: UserSafe { + pub fn to_enclave(&self) -> T { + unsafe { ptr::read(self.0.get()) } + } +} + +impl UserRef<[T]> where [T]: UserSafe { + /// Create a `&UserRef<[T]>` from a raw thin pointer and a slice length. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_raw_parts<'a>(ptr: *const T, len: usize) -> &'a Self { + &*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()) as *const Self) + } + + /// Create a `&mut UserRef<[T]>` from a raw thin pointer and a slice length. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut T, len: usize) -> &'a mut Self { + &mut*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()) as *mut Self) + } + + pub fn as_ptr(&self) -> *const T { + self.0.get() as _ + } + + pub fn as_mut_ptr(&mut self) -> *mut T { + self.0.get() as _ + } + + pub fn len(&self) -> usize { + unsafe { (*self.0.get()).len() } + } + + pub fn copy_to_enclave_vec(&self, dest: &mut Vec) { + unsafe { + if let Some(missing) = self.len().checked_sub(dest.capacity()) { + dest.reserve(missing) + } + dest.set_len(self.len()); + self.copy_to_enclave(&mut dest[..]); + } + } + + pub fn to_enclave(&self) -> Vec { + let mut ret = Vec::with_capacity(self.len()); + self.copy_to_enclave_vec(&mut ret); + ret + } + + pub fn iter(&self) -> Iter + where T: UserSafe // FIXME: should be implied by [T]: UserSafe? + { + unsafe { + Iter((&*self.as_raw_ptr()).iter()) + } + } + + pub fn iter_mut(&mut self) -> IterMut + where T: UserSafe // FIXME: should be implied by [T]: UserSafe? + { + unsafe { + IterMut((&mut*self.as_raw_mut_ptr()).iter_mut()) + } + } +} + +pub struct Iter<'a, T: 'a + UserSafe>(slice::Iter<'a, T>); + +impl<'a, T: UserSafe> Iterator for Iter<'a, T> { + type Item = &'a UserRef; + + #[inline] + fn next(&mut self) -> Option { + unsafe { + self.0.next().map(|e| UserRef::from_ptr(e)) + } + } +} + +pub struct IterMut<'a, T: 'a + UserSafe>(slice::IterMut<'a, T>); + +impl<'a, T: UserSafe> Iterator for IterMut<'a, T> { + type Item = &'a mut UserRef; + + #[inline] + fn next(&mut self) -> Option { + unsafe { + self.0.next().map(|e| UserRef::from_mut_ptr(e)) + } + } +} + +impl Deref for User where T: UserSafe { + type Target = UserRef; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} + +impl DerefMut for User where T: UserSafe { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut*self.0 } + } +} + +impl Drop for User where T: UserSafe { + fn drop(&mut self) { + unsafe { + let ptr = (*self.0).0.get(); + super::free(ptr as _, mem::size_of_val(&mut*ptr), T::align_of()); + } + } +} + +impl, U> CoerceUnsized> for UserRef {} + +impl> Index for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe { + type Output = UserRef; + + #[inline] + fn index(&self, index: I) -> &UserRef { + unsafe { + UserRef::from_ptr(index.index(&*self.as_raw_ptr())) + } + } +} + +impl> IndexMut for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe { + #[inline] + fn index_mut(&mut self, index: I) -> &mut UserRef { + unsafe { + UserRef::from_mut_ptr(index.index_mut(&mut*self.as_raw_mut_ptr())) + } + } +} diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs new file mode 100644 index 00000000000..2bc32c9fefb --- /dev/null +++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs @@ -0,0 +1,197 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use fortanix_sgx_abi::*; + +use io::{Error as IoError, Result as IoResult}; +use time::Duration; + +pub mod alloc; +#[macro_use] +mod raw; + +pub(crate) fn copy_user_buffer(buf: &alloc::UserRef) -> Vec { + unsafe { + let buf = buf.to_enclave(); + alloc::User::from_raw_parts(buf.data as _, buf.len).to_enclave() + } +} + +pub fn read(fd: Fd, buf: &mut [u8]) -> IoResult { + unsafe { + let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.len()); + let len = raw::read(fd, userbuf.as_mut_ptr(), userbuf.len()).from_sgx_result()?; + userbuf[..len].copy_to_enclave(&mut buf[..len]); + Ok(len) + } +} + +pub fn read_alloc(fd: Fd) -> IoResult> { + unsafe { + let mut userbuf = alloc::User::::uninitialized(); + raw::read_alloc(fd, userbuf.as_raw_mut_ptr()).from_sgx_result()?; + Ok(copy_user_buffer(&userbuf)) + } +} + +pub fn write(fd: Fd, buf: &[u8]) -> IoResult { + unsafe { + let userbuf = alloc::User::new_from_enclave(buf); + raw::write(fd, userbuf.as_ptr(), userbuf.len()).from_sgx_result() + } +} + +pub fn flush(fd: Fd) -> IoResult<()> { + unsafe { raw::flush(fd).from_sgx_result() } +} + +pub fn close(fd: Fd) { + unsafe { raw::close(fd) } +} + +fn string_from_bytebuffer(buf: &alloc::UserRef, usercall: &str, arg: &str) -> String { + String::from_utf8(copy_user_buffer(buf)) + .unwrap_or_else(|_| panic!("Usercall {}: expected {} to be valid UTF-8", usercall, arg)) +} + +pub fn bind_stream(addr: &str) -> IoResult<(Fd, String)> { + unsafe { + let addr_user = alloc::User::new_from_enclave(addr.as_bytes()); + let mut local = alloc::User::::uninitialized(); + let fd = raw::bind_stream( + addr_user.as_ptr(), + addr_user.len(), + local.as_raw_mut_ptr() + ).from_sgx_result()?; + let local = string_from_bytebuffer(&local, "bind_stream", "local_addr"); + Ok((fd, local)) + } +} + +pub fn accept_stream(fd: Fd) -> IoResult<(Fd, String, String)> { + unsafe { + let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized(); + let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done + // without forcing coercion? + let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap()); + let fd = raw::accept_stream( + fd, + local.as_raw_mut_ptr(), + peer.as_raw_mut_ptr() + ).from_sgx_result()?; + let local = string_from_bytebuffer(&local, "accept_stream", "local_addr"); + let peer = string_from_bytebuffer(&peer, "accept_stream", "peer_addr"); + Ok((fd, local, peer)) + } +} + +pub fn connect_stream(addr: &str) -> IoResult<(Fd, String, String)> { + unsafe { + let addr_user = alloc::User::new_from_enclave(addr.as_bytes()); + let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized(); + let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done + // without forcing coercion? + let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap()); + let fd = raw::connect_stream( + addr_user.as_ptr(), + addr_user.len(), + local.as_raw_mut_ptr(), + peer.as_raw_mut_ptr() + ).from_sgx_result()?; + let local = string_from_bytebuffer(&local, "connect_stream", "local_addr"); + let peer = string_from_bytebuffer(&peer, "connect_stream", "peer_addr"); + Ok((fd, local, peer)) + } +} + +pub fn launch_thread() -> IoResult<()> { + unsafe { raw::launch_thread().from_sgx_result() } +} + +pub fn exit(panic: bool) -> ! { + unsafe { raw::exit(panic) } +} + +pub fn wait(event_mask: u64, timeout: u64) -> IoResult { + unsafe { raw::wait(event_mask, timeout).from_sgx_result() } +} + +pub fn send(event_set: u64, tcs: Option) -> IoResult<()> { + unsafe { raw::send(event_set, tcs).from_sgx_result() } +} + +pub fn insecure_time() -> Duration { + let t = unsafe { raw::insecure_time() }; + Duration::new(t / 1_000_000_000, (t % 1_000_000_000) as _) +} + +pub fn alloc(size: usize, alignment: usize) -> IoResult<*mut u8> { + unsafe { raw::alloc(size, alignment).from_sgx_result() } +} + +pub use self::raw::free; + +fn check_os_error(err: Result) -> i32 { + // FIXME: not sure how to make sure all variants of Error are covered + if err == Error::NotFound as _ || + err == Error::PermissionDenied as _ || + err == Error::ConnectionRefused as _ || + err == Error::ConnectionReset as _ || + err == Error::ConnectionAborted as _ || + err == Error::NotConnected as _ || + err == Error::AddrInUse as _ || + err == Error::AddrNotAvailable as _ || + err == Error::BrokenPipe as _ || + err == Error::AlreadyExists as _ || + err == Error::WouldBlock as _ || + err == Error::InvalidInput as _ || + err == Error::InvalidData as _ || + err == Error::TimedOut as _ || + err == Error::WriteZero as _ || + err == Error::Interrupted as _ || + err == Error::Other as _ || + err == Error::UnexpectedEof as _ || + ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&err) + { + err + } else { + panic!("Usercall: returned invalid error value {}", err) + } +} + +trait FromSgxResult { + type Return; + + fn from_sgx_result(self) -> IoResult; +} + +impl FromSgxResult for (Result, T) { + type Return = T; + + fn from_sgx_result(self) -> IoResult { + if self.0 == RESULT_SUCCESS { + Ok(self.1) + } else { + Err(IoError::from_raw_os_error(check_os_error(self.0))) + } + } +} + +impl FromSgxResult for Result { + type Return = (); + + fn from_sgx_result(self) -> IoResult { + if self == RESULT_SUCCESS { + Ok(()) + } else { + Err(IoError::from_raw_os_error(check_os_error(self))) + } + } +} diff --git a/src/libstd/sys/sgx/abi/usercalls/raw.rs b/src/libstd/sys/sgx/abi/usercalls/raw.rs new file mode 100644 index 00000000000..a28d41c1b74 --- /dev/null +++ b/src/libstd/sys/sgx/abi/usercalls/raw.rs @@ -0,0 +1,231 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] + +use fortanix_sgx_abi::*; + +use ptr::NonNull; + +#[repr(C)] +struct UsercallReturn(u64, u64); + +extern "C" { + fn usercall(nr: u64, p1: u64, p2: u64, _ignore: u64, p3: u64, p4: u64) -> UsercallReturn; +} + +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); + (a, b) +} + +type Register = u64; + +trait RegisterArgument { + fn from_register(Register) -> Self; + fn into_register(self) -> Register; +} + +trait ReturnValue { + fn from_registers(call: &'static str, regs: (Register, Register)) -> Self; +} + +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)*; )*) => { + #[repr(C)] + #[allow(non_camel_case_types)] + enum Usercalls { + __enclave_usercalls_invalid, + $($f,)* + } + + $(enclave_usercalls_internal_define_usercalls!(def fn $f($($n: $t),*) $(-> $r)*);)* + }; +} + +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 { + fn from_register(a: Register) -> Self { + a as _ + } + fn into_register(self) -> Register { + self as _ + } + } + }; + ($i:ty as $t:ty) => { + impl RegisterArgument for $t { + fn from_register(a: Register) -> Self { + a as $i as _ + } + fn into_register(self) -> Register { + self as $i as _ + } + } + }; + ($t:ty) => { + impl RegisterArgument for $t { + fn from_register(a: Register) -> Self { + a as _ + } + fn into_register(self) -> Register { + self as _ + } + } + }; +} + +define_ra!(Register); +define_ra!(i64); +define_ra!(u32); +define_ra!(u32 as i32); +define_ra!(u16); +define_ra!(u16 as i16); +define_ra!(u8); +define_ra!(u8 as i8); +define_ra!(usize); +define_ra!(usize as isize); +define_ra!( *const T); +define_ra!( *mut T); + +impl RegisterArgument for bool { + fn from_register(a: Register) -> bool { + if a != 0 { + true + } else { + false + } + } + fn into_register(self) -> Register { + self as _ + } +} + +impl RegisterArgument for Option> { + fn from_register(a: Register) -> Option> { + NonNull::new(a as _) + } + fn into_register(self) -> Register { + self.map_or(0 as _, NonNull::as_ptr) as _ + } +} + +impl ReturnValue for ! { + fn from_registers(call: &'static str, _regs: (Register, Register)) -> Self { + panic!("Usercall {}: did not expect to be re-entered", call); + } +} + +impl ReturnValue for () { + fn from_registers(call: &'static str, regs: (Register, Register)) -> Self { + assert_eq!(regs.0, 0, "Usercall {}: expected {} return value to be 0", call, "1st"); + assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd"); + () + } +} + +impl ReturnValue for T { + fn from_registers(call: &'static str, regs: (Register, Register)) -> Self { + assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd"); + T::from_register(regs.0) + } +} + +impl ReturnValue for (T, U) { + fn from_registers(_call: &'static str, regs: (Register, Register)) -> Self { + ( + T::from_register(regs.0), + U::from_register(regs.1) + ) + } +} + +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) => ( + #[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, + RegisterArgument::into_register($n1), + RegisterArgument::into_register($n2), + RegisterArgument::into_register($n3), + RegisterArgument::into_register($n4), + )) + } + ); + (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty, $n3:ident: $t3:ty) -> $r:ty) => ( + #[inline(always)] + pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r { + ReturnValue::from_registers(stringify!($f), do_usercall( + Usercalls::$f as Register, + RegisterArgument::into_register($n1), + RegisterArgument::into_register($n2), + RegisterArgument::into_register($n3), + 0 + )) + } + ); + (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty) -> $r:ty) => ( + #[inline(always)] + pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r { + ReturnValue::from_registers(stringify!($f), do_usercall( + Usercalls::$f as Register, + RegisterArgument::into_register($n1), + RegisterArgument::into_register($n2), + 0,0 + )) + } + ); + (def fn $f:ident($n1:ident: $t1:ty) -> $r:ty) => ( + #[inline(always)] + pub unsafe fn $f($n1: $t1) -> $r { + ReturnValue::from_registers(stringify!($f), do_usercall( + Usercalls::$f as Register, + RegisterArgument::into_register($n1), + 0,0,0 + )) + } + ); + (def fn $f:ident() -> $r:ty) => ( + #[inline(always)] + pub unsafe fn $f() -> $r { + ReturnValue::from_registers(stringify!($f), do_usercall( + Usercalls::$f as Register, + 0,0,0,0 + )) + } + ); + (def fn $f:ident($($n:ident: $t:ty),*)) => ( + enclave_usercalls_internal_define_usercalls!(def fn $f($($n: $t),*) -> ()); + ); +} + +invoke_with_usercalls!(define_usercalls); +invoke_with_usercalls!(define_usercalls_asm); diff --git a/src/libstd/sys/sgx/alloc.rs b/src/libstd/sys/sgx/alloc.rs new file mode 100644 index 00000000000..83c20ace89b --- /dev/null +++ b/src/libstd/sys/sgx/alloc.rs @@ -0,0 +1,42 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate dlmalloc; + +use alloc::{GlobalAlloc, Layout, System}; + +use super::waitqueue::SpinMutex; + +// Using a SpinMutex because we never want to exit the enclave waiting for the +// allocator. +static DLMALLOC: SpinMutex = SpinMutex::new(dlmalloc::DLMALLOC_INIT); + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + DLMALLOC.lock().malloc(layout.size(), layout.align()) + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + DLMALLOC.lock().calloc(layout.size(), layout.align()) + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + DLMALLOC.lock().free(ptr, layout.size(), layout.align()) + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) + } +} diff --git a/src/libstd/sys/sgx/args.rs b/src/libstd/sys/sgx/args.rs new file mode 100644 index 00000000000..8fb35d7ef98 --- /dev/null +++ b/src/libstd/sys/sgx/args.rs @@ -0,0 +1,75 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsString; +use super::abi::usercalls::{copy_user_buffer, alloc, ByteBuffer}; +use sync::atomic::{AtomicUsize, Ordering}; +use sys::os_str::Buf; +use sys_common::FromInner; +use slice; + +static ARGS: AtomicUsize = AtomicUsize::new(0); +type ArgsStore = Vec; + +pub unsafe fn init(argc: isize, argv: *const *const u8) { + if argc != 0 { + let args = alloc::User::<[ByteBuffer]>::from_raw_parts(argv as _, argc as _); + let args = args.iter() + .map( |a| OsString::from_inner(Buf { inner: copy_user_buffer(a) }) ) + .collect::(); + ARGS.store(Box::into_raw(Box::new(args)) as _, Ordering::Relaxed); + } +} + +pub unsafe fn cleanup() { + let args = ARGS.swap(0, Ordering::Relaxed); + if args != 0 { + drop(Box::::from_raw(args as _)) + } +} + +pub fn args() -> Args { + let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() }; + if let Some(args) = args { + Args(args.iter()) + } else { + Args([].iter()) + } +} + +pub struct Args(slice::Iter<'static, OsString>); + +impl Args { + pub fn inner_debug(&self) -> &[OsString] { + self.0.as_slice() + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { + self.0.next().cloned() + } + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.0.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { + self.0.next_back().cloned() + } +} diff --git a/src/libstd/sys/sgx/backtrace.rs b/src/libstd/sys/sgx/backtrace.rs new file mode 100644 index 00000000000..ca4a7c9561c --- /dev/null +++ b/src/libstd/sys/sgx/backtrace.rs @@ -0,0 +1,37 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::unsupported; +use sys_common::backtrace::Frame; + +pub struct BacktraceContext; + +pub fn unwind_backtrace(_frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + unsupported() +} + +pub fn resolve_symname(_frame: Frame, + _callback: F, + _: &BacktraceContext) -> io::Result<()> + where F: FnOnce(Option<&str>) -> io::Result<()> +{ + unsupported() +} + +pub fn foreach_symbol_fileline(_: Frame, + _: F, + _: &BacktraceContext) -> io::Result + where F: FnMut(&[u8], u32) -> io::Result<()> +{ + unsupported() +} diff --git a/src/libstd/sys/sgx/cmath.rs b/src/libstd/sys/sgx/cmath.rs new file mode 100644 index 00000000000..0c1300f61f8 --- /dev/null +++ b/src/libstd/sys/sgx/cmath.rs @@ -0,0 +1,41 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(not(test))] + +// These symbols are all defined in `compiler-builtins` +extern { + pub fn acos(n: f64) -> f64; + pub fn acosf(n: f32) -> f32; + pub fn asin(n: f64) -> f64; + pub fn asinf(n: f32) -> f32; + pub fn atan(n: f64) -> f64; + pub fn atan2(a: f64, b: f64) -> f64; + pub fn atan2f(a: f32, b: f32) -> f32; + pub fn atanf(n: f32) -> f32; + pub fn cbrt(n: f64) -> f64; + pub fn cbrtf(n: f32) -> f32; + pub fn cosh(n: f64) -> f64; + pub fn coshf(n: f32) -> f32; + pub fn expm1(n: f64) -> f64; + pub fn expm1f(n: f32) -> f32; + pub fn fdim(a: f64, b: f64) -> f64; + pub fn fdimf(a: f32, b: f32) -> f32; + pub fn hypot(x: f64, y: f64) -> f64; + pub fn hypotf(x: f32, y: f32) -> f32; + pub fn log1p(n: f64) -> f64; + pub fn log1pf(n: f32) -> f32; + pub fn sinh(n: f64) -> f64; + pub fn sinhf(n: f32) -> f32; + pub fn tan(n: f64) -> f64; + pub fn tanf(n: f32) -> f32; + pub fn tanh(n: f64) -> f64; + pub fn tanhf(n: f32) -> f32; +} diff --git a/src/libstd/sys/sgx/condvar.rs b/src/libstd/sys/sgx/condvar.rs new file mode 100644 index 00000000000..d3e8165f3df --- /dev/null +++ b/src/libstd/sys/sgx/condvar.rs @@ -0,0 +1,52 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use sys::mutex::Mutex; +use time::Duration; + +use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex}; + +pub struct Condvar { + inner: SpinMutex>, +} + +impl Condvar { + #[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn + pub const fn new() -> Condvar { + Condvar { inner: SpinMutex::new(WaitVariable::new(())) } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn notify_one(&self) { + let _ = WaitQueue::notify_one(self.inner.lock()); + } + + #[inline] + pub unsafe fn notify_all(&self) { + let _ = WaitQueue::notify_all(self.inner.lock()); + } + + pub unsafe fn wait(&self, mutex: &Mutex) { + let guard = self.inner.lock(); + mutex.unlock(); + WaitQueue::wait(guard); + mutex.lock() + } + + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + panic!("timeout not supported in SGX"); + } + + #[inline] + pub unsafe fn destroy(&self) {} +} diff --git a/src/libstd/sys/sgx/env.rs b/src/libstd/sys/sgx/env.rs new file mode 100644 index 00000000000..146ce02754b --- /dev/null +++ b/src/libstd/sys/sgx/env.rs @@ -0,0 +1,19 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod os { + pub const FAMILY: &'static str = ""; + pub const OS: &'static str = ""; + pub const DLL_PREFIX: &'static str = ""; + pub const DLL_SUFFIX: &'static str = ".sgxs"; + pub const DLL_EXTENSION: &'static str = "sgxs"; + pub const EXE_SUFFIX: &'static str = ".sgxs"; + pub const EXE_EXTENSION: &'static str = "sgxs"; +} diff --git a/src/libstd/sys/sgx/fd.rs b/src/libstd/sys/sgx/fd.rs new file mode 100644 index 00000000000..31c4199c6cd --- /dev/null +++ b/src/libstd/sys/sgx/fd.rs @@ -0,0 +1,58 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi::Fd; + +use io; +use mem; +use sys_common::AsInner; +use super::abi::usercalls; + +#[derive(Debug)] +pub struct FileDesc { + fd: Fd, +} + +impl FileDesc { + pub fn new(fd: Fd) -> FileDesc { + FileDesc { fd: fd } + } + + pub fn raw(&self) -> Fd { self.fd } + + /// Extracts the actual filedescriptor without closing it. + pub fn into_raw(self) -> Fd { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + usercalls::read(self.fd, buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + usercalls::write(self.fd, buf) + } + + pub fn flush(&self) -> io::Result<()> { + usercalls::flush(self.fd) + } +} + +impl AsInner for FileDesc { + fn as_inner(&self) -> &Fd { &self.fd } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + usercalls::close(self.fd) + } +} diff --git a/src/libstd/sys/sgx/fs.rs b/src/libstd/sys/sgx/fs.rs new file mode 100644 index 00000000000..1dcea3e8eac --- /dev/null +++ b/src/libstd/sys/sgx/fs.rs @@ -0,0 +1,304 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsString; +use fmt; +use hash::{Hash, Hasher}; +use io::{self, SeekFrom}; +use path::{Path, PathBuf}; +use sys::time::SystemTime; +use sys::{unsupported, Void}; + +pub struct File(Void); + +pub struct FileAttr(Void); + +pub struct ReadDir(Void); + +pub struct DirEntry(Void); + +#[derive(Clone, Debug)] +pub struct OpenOptions { } + +pub struct FilePermissions(Void); + +pub struct FileType(Void); + +#[derive(Debug)] +pub struct DirBuilder { } + +impl FileAttr { + pub fn size(&self) -> u64 { + match self.0 {} + } + + pub fn perm(&self) -> FilePermissions { + match self.0 {} + } + + pub fn file_type(&self) -> FileType { + match self.0 {} + } + + pub fn modified(&self) -> io::Result { + match self.0 {} + } + + pub fn accessed(&self) -> io::Result { + match self.0 {} + } + + pub fn created(&self) -> io::Result { + match self.0 {} + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + match self.0 {} + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + match self.0 {} + } + + pub fn set_readonly(&mut self, _readonly: bool) { + match self.0 {} + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + match self.0 {} + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + match self.0 {} + } +} + +impl Eq for FilePermissions { +} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + match self.0 {} + } + + pub fn is_file(&self) -> bool { + match self.0 {} + } + + pub fn is_symlink(&self) -> bool { + match self.0 {} + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + match self.0 {} + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + match self.0 {} + } +} + +impl Eq for FileType { +} + +impl Hash for FileType { + fn hash(&self, _h: &mut H) { + match self.0 {} + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + match self.0 {} + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + match self.0 {} + } + + pub fn file_name(&self) -> OsString { + match self.0 {} + } + + pub fn metadata(&self) -> io::Result { + match self.0 {} + } + + pub fn file_type(&self) -> io::Result { + match self.0 {} + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { } + } + + pub fn read(&mut self, _read: bool) { } + pub fn write(&mut self, _write: bool) { } + pub fn append(&mut self, _append: bool) { } + pub fn truncate(&mut self, _truncate: bool) { } + pub fn create(&mut self, _create: bool) { } + pub fn create_new(&mut self, _create_new: bool) { } +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + match self.0 {} + } + + pub fn fsync(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn datasync(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + match self.0 {} + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn flush(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { } + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} diff --git a/src/libstd/sys/sgx/memchr.rs b/src/libstd/sys/sgx/memchr.rs new file mode 100644 index 00000000000..0998bc5db4c --- /dev/null +++ b/src/libstd/sys/sgx/memchr.rs @@ -0,0 +1,11 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use core::slice::memchr::{memchr, memrchr}; diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs new file mode 100644 index 00000000000..dd6862e908e --- /dev/null +++ b/src/libstd/sys/sgx/mod.rs @@ -0,0 +1,153 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! System bindings for the Fortanix SGX platform +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for Fortanix SGX. + +use io; +use os::raw::c_char; +use sync::atomic::{AtomicBool, Ordering}; + +pub mod abi; +mod waitqueue; + +pub mod alloc; +pub mod args; +#[cfg(feature = "backtrace")] +pub mod backtrace; +pub mod cmath; +pub mod condvar; +pub mod env; +pub mod fd; +pub mod fs; +pub mod memchr; +pub mod mutex; +pub mod net; +pub mod os; +pub mod os_str; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rwlock; +pub mod stack_overflow; +pub mod thread; +pub mod thread_local; +pub mod time; +pub mod stdio; + +#[cfg(not(test))] +pub fn init() { +} + +/// This function is used to implement functionality that simply doesn't exist. +/// Programs relying on this functionality will need to deal with the error. +pub fn unsupported() -> io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> io::Error { + io::Error::new(io::ErrorKind::Other, + "operation not supported on SGX yet") +} + +/// This function is used to implement various functions that doesn't exist, +/// but the lack of which might not be reason for error. If no error is +/// returned, the program might very well be able to function normally. This is +/// what happens when `SGX_INEFFECTIVE_ERROR` is set to `true`. If it is +/// `false`, the behavior is the same as `unsupported`. +pub fn sgx_ineffective(v: T) -> io::Result { + static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false); + if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) { + Err(io::Error::new(io::ErrorKind::Other, + "operation can't be trusted to have any effect on SGX")) + } else { + Ok(v) + } +} + +pub fn decode_error_kind(code: i32) -> io::ErrorKind { + use fortanix_sgx_abi::Error; + + // FIXME: not sure how to make sure all variants of Error are covered + if code == Error::NotFound as _ { + io::ErrorKind::NotFound + } else if code == Error::PermissionDenied as _ { + io::ErrorKind::PermissionDenied + } else if code == Error::ConnectionRefused as _ { + io::ErrorKind::ConnectionRefused + } else if code == Error::ConnectionReset as _ { + io::ErrorKind::ConnectionReset + } else if code == Error::ConnectionAborted as _ { + io::ErrorKind::ConnectionAborted + } else if code == Error::NotConnected as _ { + io::ErrorKind::NotConnected + } else if code == Error::AddrInUse as _ { + io::ErrorKind::AddrInUse + } else if code == Error::AddrNotAvailable as _ { + io::ErrorKind::AddrNotAvailable + } else if code == Error::BrokenPipe as _ { + io::ErrorKind::BrokenPipe + } else if code == Error::AlreadyExists as _ { + io::ErrorKind::AlreadyExists + } else if code == Error::WouldBlock as _ { + io::ErrorKind::WouldBlock + } else if code == Error::InvalidInput as _ { + io::ErrorKind::InvalidInput + } else if code == Error::InvalidData as _ { + io::ErrorKind::InvalidData + } else if code == Error::TimedOut as _ { + io::ErrorKind::TimedOut + } else if code == Error::WriteZero as _ { + io::ErrorKind::WriteZero + } else if code == Error::Interrupted as _ { + io::ErrorKind::Interrupted + } else if code == Error::Other as _ { + io::ErrorKind::Other + } else if code == Error::UnexpectedEof as _ { + io::ErrorKind::UnexpectedEof + } else { + io::ErrorKind::Other + } +} + +// This enum is used as the storage for a bunch of types which can't actually +// exist. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Void {} + +pub unsafe fn strlen(mut s: *const c_char) -> usize { + let mut n = 0; + while *s != 0 { + n += 1; + s = s.offset(1); + } + return n +} + +pub unsafe fn abort_internal() -> ! { + abi::panic::panic_exit() +} + +pub fn hashmap_random_keys() -> (u64, u64) { + fn rdrand64() -> u64 { + unsafe { + let mut ret: u64 = ::mem::uninitialized(); + for _ in 0..10 { + if ::arch::x86_64::_rdrand64_step(&mut ret) == 1 { + return ret; + } + } + panic!("Failed to obtain random data"); + } + } + (rdrand64(), rdrand64()) +} diff --git a/src/libstd/sys/sgx/mutex.rs b/src/libstd/sys/sgx/mutex.rs new file mode 100644 index 00000000000..663361162bc --- /dev/null +++ b/src/libstd/sys/sgx/mutex.rs @@ -0,0 +1,152 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi::Tcs; + +use super::abi::thread; + +use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex, NotifiedTcs, try_lock_or_false}; + +pub struct Mutex { + inner: SpinMutex>, +} + +// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 +impl Mutex { + #[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn + pub const fn new() -> Mutex { + Mutex { inner: SpinMutex::new(WaitVariable::new(false)) } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn lock(&self) { + let mut guard = self.inner.lock(); + if *guard.lock_var() { + // Another thread has the lock, wait + WaitQueue::wait(guard) + // Another thread has passed the lock to us + } else { + // We are just now obtaining the lock + *guard.lock_var_mut() = true; + } + } + + #[inline] + pub unsafe fn unlock(&self) { + let guard = self.inner.lock(); + if let Err(mut guard) = WaitQueue::notify_one(guard) { + // No other waiters, unlock + *guard.lock_var_mut() = false; + } else { + // There was a thread waiting, just pass the lock + } + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + let mut guard = try_lock_or_false!(self.inner); + if *guard.lock_var() { + // Another thread has the lock + false + } else { + // We are just now obtaining the lock + *guard.lock_var_mut() = true; + true + } + } + + #[inline] + pub unsafe fn destroy(&self) {} +} + +struct ReentrantLock { + owner: Option, + count: usize +} + +pub struct ReentrantMutex { + inner: SpinMutex>, +} + +impl ReentrantMutex { + #[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn + pub const fn uninitialized() -> ReentrantMutex { + ReentrantMutex { + inner: SpinMutex::new(WaitVariable::new(ReentrantLock { owner: None, count: 0 })) + } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn lock(&self) { + let mut guard = self.inner.lock(); + match guard.lock_var().owner { + Some(tcs) if tcs != thread::current() => { + // Another thread has the lock, wait + WaitQueue::wait(guard); + // Another thread has passed the lock to us + }, + _ => { + // We are just now obtaining the lock + guard.lock_var_mut().owner = Some(thread::current()); + guard.lock_var_mut().count += 1; + }, + } + } + + #[inline] + pub unsafe fn unlock(&self) { + let mut guard = self.inner.lock(); + if guard.lock_var().count > 1 { + guard.lock_var_mut().count -= 1; + } else { + match WaitQueue::notify_one(guard) { + Err(mut guard) => { + // No other waiters, unlock + guard.lock_var_mut().count = 0; + guard.lock_var_mut().owner = None; + }, + Ok(mut guard) => { + // There was a thread waiting, just pass the lock + if let NotifiedTcs::Single(tcs) = guard.notified_tcs() { + guard.lock_var_mut().owner = Some(tcs) + } else { + unreachable!() // called notify_one + } + } + } + } + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + let mut guard = try_lock_or_false!(self.inner); + match guard.lock_var().owner { + Some(tcs) if tcs != thread::current() => { + // Another thread has the lock + false + }, + _ => { + // We are just now obtaining the lock + guard.lock_var_mut().owner = Some(thread::current()); + guard.lock_var_mut().count += 1; + true + }, + } + } + + #[inline] + pub unsafe fn destroy(&self) {} +} diff --git a/src/libstd/sys/sgx/net.rs b/src/libstd/sys/sgx/net.rs new file mode 100644 index 00000000000..176d230846d --- /dev/null +++ b/src/libstd/sys/sgx/net.rs @@ -0,0 +1,415 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fmt; +use io; +use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr, ToSocketAddrs}; +use time::Duration; +use sys::{unsupported, Void, sgx_ineffective}; +use sys::fd::FileDesc; +use convert::TryFrom; +use error; +use sync::Arc; + +use super::abi::usercalls; + +const DEFAULT_FAKE_TTL: u32 = 64; + +#[derive(Debug, Clone)] +struct Socket { + inner: Arc, + local_addr: String, +} + +impl Socket { + fn new(fd: usercalls::Fd, local_addr: String) -> Socket { + Socket { inner: Arc::new(FileDesc::new(fd)), local_addr } + } +} + +#[derive(Debug, Clone)] +pub struct TcpStream { + inner: Socket, + peer_addr: String, +} + +fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result { + match result { + Ok(saddr) => Ok(saddr.to_string()), + // need to downcast twice because io::Error::into_inner doesn't return the original + // value if the conversion fails + Err(e) => if e.get_ref().and_then(|e| e.downcast_ref::()).is_some() { + Ok(e.into_inner().unwrap().downcast::().unwrap().host) + } else { + Err(e) + } + } +} + +fn addr_to_sockaddr(addr: &str) -> io::Result { + // unwrap OK: if an iterator is returned, we're guaranteed to get exactly one entry + addr.to_socket_addrs().map(|mut it| it.next().unwrap()) +} + +impl TcpStream { + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = io_err_to_addr(addr)?; + let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?; + Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr }) + } + + pub fn connect_timeout(addr: &SocketAddr, _: Duration) -> io::Result { + Self::connect(Ok(addr)) // FIXME: ignoring timeout + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn read_timeout(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn write_timeout(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + Ok(0) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.inner.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.inner.inner.write(buf) + } + + pub fn peer_addr(&self) -> io::Result { + addr_to_sockaddr(&self.peer_addr) + } + + pub fn socket_addr(&self) -> io::Result { + addr_to_sockaddr(&self.inner.local_addr) + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn duplicate(&self) -> io::Result { + Ok(self.clone()) + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn nodelay(&self) -> io::Result { + sgx_ineffective(false) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn take_error(&self) -> io::Result> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +#[derive(Debug, Clone)] +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = io_err_to_addr(addr)?; + let (fd, local_addr) = usercalls::bind_stream(&addr)?; + Ok(TcpListener { inner: Socket::new(fd, local_addr) }) + } + + pub fn socket_addr(&self) -> io::Result { + addr_to_sockaddr(&self.inner.local_addr) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let (fd, local_addr, peer_addr) = usercalls::accept_stream(self.inner.inner.raw())?; + let ret_peer = addr_to_sockaddr(&peer_addr).unwrap_or_else(|_| ([0; 4], 0).into()); + Ok((TcpStream { inner: Socket::new(fd, local_addr), peer_addr }, ret_peer)) + } + + pub fn duplicate(&self) -> io::Result { + Ok(self.clone()) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn only_v6(&self) -> io::Result { + sgx_ineffective(false) + } + + pub fn take_error(&self) -> io::Result> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +pub struct UdpSocket(Void); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + match self.0 {} + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + match self.0 {} + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + match self.0 {} + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn read_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn write_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn broadcast(&self) -> io::Result { + match self.0 {} + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v4(&self) -> io::Result { + match self.0 {} + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + match self.0 {} + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v6(&self) -> io::Result { + match self.0 {} + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + match self.0 {} + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + match self.0 {} + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + match self.0 {} + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + match self.0 {} + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn ttl(&self) -> io::Result { + match self.0 {} + } + + pub fn take_error(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn send(&self, _: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + match self.0 {} + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +#[derive(Debug)] +pub struct NonIpSockAddr { + host: String +} + +impl error::Error for NonIpSockAddr { + fn description(&self) -> &str { + "Failed to convert address to SocketAddr" + } +} + +impl fmt::Display for NonIpSockAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Failed to convert address to SocketAddr: {}", self.host) + } +} + +pub struct LookupHost(Void); + +impl LookupHost { + fn new(host: String) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, NonIpSockAddr { host })) + } + + pub fn port(&self) -> u16 { + match self.0 {} + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + match self.0 {} + } +} + +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(v: &'a str) -> io::Result { + LookupHost::new(v.to_owned()) + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result { + LookupHost::new(format!("{}:{}", host, port)) + } +} + +#[allow(bad_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr { + } + + pub type socklen_t = usize; +} diff --git a/src/libstd/sys/sgx/os.rs b/src/libstd/sys/sgx/os.rs new file mode 100644 index 00000000000..79ebafe73f9 --- /dev/null +++ b/src/libstd/sys/sgx/os.rs @@ -0,0 +1,147 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; + +use error::Error as StdError; +use ffi::{OsString, OsStr}; +use fmt; +use io; +use path::{self, PathBuf}; +use str; +use sys::{unsupported, Void, sgx_ineffective, decode_error_kind}; +use collections::HashMap; +use vec; +use sync::Mutex; +use sync::atomic::{AtomicUsize, Ordering}; +use sync::Once; + +pub fn errno() -> i32 { + RESULT_SUCCESS +} + +pub fn error_string(errno: i32) -> String { + if errno == RESULT_SUCCESS { + "operation succesful".into() + } else if ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&errno) { + format!("user-specified error {:08x}", errno) + } else { + decode_error_kind(errno).as_str().into() + } +} + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + sgx_ineffective(()) +} + +pub struct SplitPaths<'a>(&'a Void); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { + match *self.0 {} + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result + where I: Iterator, T: AsRef +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "not supported in SGX yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + fn description(&self) -> &str { + "not supported in SGX yet" + } +} + +pub fn current_exe() -> io::Result { + unsupported() +} + +static ENV: AtomicUsize = AtomicUsize::new(0); +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex>; + +fn get_env_store() -> Option<&'static EnvStore> { + unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() } +} + +fn create_env_store() -> &'static EnvStore { + ENV_INIT.call_once(|| { + ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed) + }); + unsafe { + &*(ENV.load(Ordering::Relaxed) as *const EnvStore) + } +} + +pub type Env = vec::IntoIter<(OsString, OsString)>; + +pub fn env() -> Env { + let clone_to_vec = |map: &HashMap| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone()) ).collect() + }; + + get_env_store() + .map(|env| clone_to_vec(&env.lock().unwrap()) ) + .unwrap_or_default() + .into_iter() +} + +pub fn getenv(k: &OsStr) -> io::Result> { + Ok(get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned() )) +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + create_env_store().lock().unwrap().insert(k, v); + Ok(()) +} + +pub fn unsetenv(k: &OsStr) -> io::Result<()> { + if let Some(env) = get_env_store() { + env.lock().unwrap().remove(k); + } + Ok(()) +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem in SGX") +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(code: i32) -> ! { + super::abi::exit_with_code(code as _) +} + +pub fn getpid() -> u32 { + panic!("no pids in SGX") +} diff --git a/src/libstd/sys/sgx/os_str.rs b/src/libstd/sys/sgx/os_str.rs new file mode 100644 index 00000000000..9bfb84db209 --- /dev/null +++ b/src/libstd/sys/sgx/os_str.rs @@ -0,0 +1,189 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Unix systems: just +/// a `Vec`/`[u8]`. + +use borrow::Cow; +use fmt; +use str; +use mem; +use rc::Rc; +use sync::Arc; +use sys_common::{AsInner, IntoInner}; +use sys_common::bytestring::debug_fmt_bytestring; +use core::str::lossy::Utf8Lossy; + +#[derive(Clone, Hash)] +pub struct Buf { + pub inner: Vec +} + +pub struct Slice { + pub inner: [u8] +} + +impl fmt::Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + debug_fmt_bytestring(&self.inner, formatter) + } +} + +impl fmt::Display for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) + } +} + +impl fmt::Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self.as_slice(), formatter) + } +} + +impl fmt::Display for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self.as_slice(), formatter) + } +} + +impl IntoInner> for Buf { + fn into_inner(self) -> Vec { + self.inner + } +} + +impl AsInner<[u8]> for Buf { + fn as_inner(&self) -> &[u8] { + &self.inner + } +} + + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: s.into_bytes() } + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Buf { + Buf { + inner: Vec::with_capacity(capacity) + } + } + + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } + + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + #[inline] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(&*self.inner) } + } + + pub fn into_string(self) -> Result { + String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.extend_from_slice(&s.inner) + } + + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } + + #[inline] + pub fn from_box(boxed: Box) -> Buf { + let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; + Buf { inner: inner.into_vec() } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } +} + +impl Slice { + fn from_u8_slice(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + pub fn from_str(s: &str) -> &Slice { + Slice::from_u8_slice(s.as_bytes()) + } + + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(&self.inner).ok() + } + + pub fn to_string_lossy(&self) -> Cow { + String::from_utf8_lossy(&self.inner) + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_vec() } + } + + #[inline] + pub fn into_box(&self) -> Box { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc: Arc<[u8]> = Arc::from(&self.inner); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc: Rc<[u8]> = Rc::from(&self.inner); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } +} diff --git a/src/libstd/sys/sgx/path.rs b/src/libstd/sys/sgx/path.rs new file mode 100644 index 00000000000..afe0c490426 --- /dev/null +++ b/src/libstd/sys/sgx/path.rs @@ -0,0 +1,29 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use path::Prefix; +use ffi::OsStr; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' +} + +pub fn parse_prefix(_: &OsStr) -> Option { + None +} + +pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/sgx/pipe.rs b/src/libstd/sys/sgx/pipe.rs new file mode 100644 index 00000000000..6c6cbc14a8a --- /dev/null +++ b/src/libstd/sys/sgx/pipe.rs @@ -0,0 +1,35 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::Void; + +pub struct AnonPipe(Void); + +impl AnonPipe { + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +pub fn read2(p1: AnonPipe, + _v1: &mut Vec, + _p2: AnonPipe, + _v2: &mut Vec) -> io::Result<()> { + match p1.0 {} +} diff --git a/src/libstd/sys/sgx/process.rs b/src/libstd/sys/sgx/process.rs new file mode 100644 index 00000000000..01a12fba043 --- /dev/null +++ b/src/libstd/sys/sgx/process.rs @@ -0,0 +1,162 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsStr; +use fmt; +use io; +use sys::fs::File; +use sys::pipe::AnonPipe; +use sys::{unsupported, Void}; +use sys_common::process::{CommandEnv, DefaultEnvKey}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + env: CommandEnv +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(_program: &OsStr) -> Command { + Command { + env: Default::default() + } + } + + pub fn arg(&mut self, _arg: &OsStr) { + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn cwd(&mut self, _dir: &OsStr) { + } + + pub fn stdin(&mut self, _stdin: Stdio) { + } + + pub fn stdout(&mut self, _stdout: Stdio) { + } + + pub fn stderr(&mut self, _stderr: Stdio) { + } + + pub fn spawn(&mut self, _default: Stdio, _needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + unsupported() + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + file.diverge() + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + Ok(()) + } +} + +pub struct ExitStatus(Void); + +impl ExitStatus { + pub fn success(&self) -> bool { + match self.0 {} + } + + pub fn code(&self) -> Option { + match self.0 {} + } +} + +impl Clone for ExitStatus { + fn clone(&self) -> ExitStatus { + match self.0 {} + } +} + +impl Copy for ExitStatus {} + +impl PartialEq for ExitStatus { + fn eq(&self, _other: &ExitStatus) -> bool { + match self.0 {} + } +} + +impl Eq for ExitStatus { +} + +impl fmt::Debug for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(bool); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(false); + pub const FAILURE: ExitCode = ExitCode(true); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +pub struct Process(Void); + +impl Process { + pub fn id(&self) -> u32 { + match self.0 {} + } + + pub fn kill(&mut self) -> io::Result<()> { + match self.0 {} + } + + pub fn wait(&mut self) -> io::Result { + match self.0 {} + } + + pub fn try_wait(&mut self) -> io::Result> { + match self.0 {} + } +} diff --git a/src/libstd/sys/sgx/rwlock.rs b/src/libstd/sys/sgx/rwlock.rs new file mode 100644 index 00000000000..7b6970b825f --- /dev/null +++ b/src/libstd/sys/sgx/rwlock.rs @@ -0,0 +1,134 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use num::NonZeroUsize; + +use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex, NotifiedTcs, try_lock_or_false}; + +pub struct RWLock { + readers: SpinMutex>>, + writer: SpinMutex>, +} + +//unsafe impl Send for RWLock {} +//unsafe impl Sync for RWLock {} // FIXME + +impl RWLock { + #[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn + pub const fn new() -> RWLock { + RWLock { + readers: SpinMutex::new(WaitVariable::new(None)), + writer: SpinMutex::new(WaitVariable::new(false)) + } + } + + #[inline] + pub unsafe fn read(&self) { + let mut rguard = self.readers.lock(); + let wguard = self.writer.lock(); + if *wguard.lock_var() || !wguard.queue_empty() { + // Another thread has or is waiting for the write lock, wait + drop(wguard); + WaitQueue::wait(rguard); + // Another thread has passed the lock to us + } else { + // No waiting writers, acquire the read lock + *rguard.lock_var_mut() = + NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); + } + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + let mut rguard = try_lock_or_false!(self.readers); + let wguard = try_lock_or_false!(self.writer); + if *wguard.lock_var() || !wguard.queue_empty() { + // Another thread has or is waiting for the write lock + false + } else { + // No waiting writers, acquire the read lock + *rguard.lock_var_mut() = + NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); + true + } + } + + #[inline] + pub unsafe fn write(&self) { + let rguard = self.readers.lock(); + let mut wguard = self.writer.lock(); + if *wguard.lock_var() || rguard.lock_var().is_some() { + // Another thread has the lock, wait + drop(rguard); + WaitQueue::wait(wguard); + // Another thread has passed the lock to us + } else { + // We are just now obtaining the lock + *wguard.lock_var_mut() = true; + } + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + let rguard = try_lock_or_false!(self.readers); + let mut wguard = try_lock_or_false!(self.writer); + if *wguard.lock_var() || rguard.lock_var().is_some() { + // Another thread has the lock + false + } else { + // We are just now obtaining the lock + *wguard.lock_var_mut() = true; + true + } + } + + #[inline] + pub unsafe fn read_unlock(&self) { + let mut rguard = self.readers.lock(); + let wguard = self.writer.lock(); + *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1); + if rguard.lock_var().is_some() { + // There are other active readers + } else { + if let Ok(mut wguard) = WaitQueue::notify_one(wguard) { + // A writer was waiting, pass the lock + *wguard.lock_var_mut() = true; + } else { + // No writers were waiting, the lock is released + assert!(rguard.queue_empty()); + } + } + } + + #[inline] + pub unsafe fn write_unlock(&self) { + let rguard = self.readers.lock(); + let wguard = self.writer.lock(); + if let Err(mut wguard) = WaitQueue::notify_one(wguard) { + // No writers waiting, release the write lock + *wguard.lock_var_mut() = false; + if let Ok(mut rguard) = WaitQueue::notify_all(rguard) { + // One or more readers were waiting, pass the lock to them + if let NotifiedTcs::All { count } = rguard.notified_tcs() { + *rguard.lock_var_mut() = Some(count) + } else { + unreachable!() // called notify_all + } + } else { + // No readers waiting, the lock is released + } + } else { + // There was a thread waiting for write, just pass the lock + } + } + + #[inline] + pub unsafe fn destroy(&self) {} +} diff --git a/src/libstd/sys/sgx/stack_overflow.rs b/src/libstd/sys/sgx/stack_overflow.rs new file mode 100644 index 00000000000..0176b748a87 --- /dev/null +++ b/src/libstd/sys/sgx/stack_overflow.rs @@ -0,0 +1,23 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct Handler; + +impl Handler { + pub unsafe fn new() -> Handler { + Handler + } +} + +pub unsafe fn init() { +} + +pub unsafe fn cleanup() { +} diff --git a/src/libstd/sys/sgx/stdio.rs b/src/libstd/sys/sgx/stdio.rs new file mode 100644 index 00000000000..13c91195569 --- /dev/null +++ b/src/libstd/sys/sgx/stdio.rs @@ -0,0 +1,81 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi as abi; + +use io; +use sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +fn with_std_fd R, R>(fd: abi::Fd, f: F) -> R { + let fd = FileDesc::new(fd); + let ret = f(&fd); + fd.into_raw(); + ret +} + +impl Stdin { + pub fn new() -> io::Result { Ok(Stdin(())) } + + pub fn read(&self, data: &mut [u8]) -> io::Result { + with_std_fd(abi::FD_STDIN, |fd| fd.read(data)) + } +} + +impl Stdout { + pub fn new() -> io::Result { Ok(Stdout(())) } + + pub fn write(&self, data: &[u8]) -> io::Result { + with_std_fd(abi::FD_STDOUT, |fd| fd.write(data)) + } + + pub fn flush(&self) -> io::Result<()> { + with_std_fd(abi::FD_STDOUT, |fd| fd.flush()) + } +} + +impl Stderr { + pub fn new() -> io::Result { Ok(Stderr(())) } + + pub fn write(&self, data: &[u8]) -> io::Result { + with_std_fd(abi::FD_STDERR, |fd| fd.write(data)) + } + + pub fn flush(&self) -> io::Result<()> { + with_std_fd(abi::FD_STDERR, |fd| fd.flush()) + } +} + +// FIXME: right now this raw stderr handle is used in a few places because +// std::io::stderr_raw isn't exposed, but once that's exposed this impl +// should go away +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result { + Stderr::write(self, data) + } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } +} + +pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(err: &io::Error) -> bool { + // FIXME: Rust normally maps Unix EBADF to `Other` + err.raw_os_error() == Some(abi::Error::BrokenPipe as _) +} + +pub fn panic_output() -> Option { + super::abi::panic::SgxPanicOutput::new() +} diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs new file mode 100644 index 00000000000..9de12a5e6f1 --- /dev/null +++ b/src/libstd/sys/sgx/thread.rs @@ -0,0 +1,101 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use boxed::FnBox; +use ffi::CStr; +use io; +use time::Duration; + +use super::abi::usercalls; + +pub struct Thread(task_queue::JoinHandle); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + +mod task_queue { + use sync::{Mutex, MutexGuard, Once}; + use sync::mpsc; + use boxed::FnBox; + + pub type JoinHandle = mpsc::Receiver<()>; + + pub(super) struct Task { + p: Box, + done: mpsc::Sender<()>, + } + + impl Task { + pub(super) fn new(p: Box) -> (Task, JoinHandle) { + let (done, recv) = mpsc::channel(); + (Task { p, done }, recv) + } + + pub(super) fn run(self) { + (self.p)(); + let _ = self.done.send(()); + } + } + + static TASK_QUEUE_INIT: Once = Once::new(); + static mut TASK_QUEUE: Option>> = None; + + pub(super) fn lock() -> MutexGuard<'static, Vec> { + unsafe { + TASK_QUEUE_INIT.call_once(|| TASK_QUEUE = Some(Default::default()) ); + TASK_QUEUE.as_ref().unwrap().lock().unwrap() + } + } +} + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, p: Box) + -> io::Result + { + let mut queue_lock = task_queue::lock(); + usercalls::launch_thread()?; + let (task, handle) = task_queue::Task::new(p); + queue_lock.push(task); + Ok(Thread(handle)) + } + + pub(super) fn entry() { + let mut guard = task_queue::lock(); + let task = guard.pop().expect("Thread started but no tasks pending"); + drop(guard); // make sure to not hold the task queue lock longer than necessary + task.run() + } + + pub fn yield_now() { + assert_eq!( + usercalls::wait(0, usercalls::WAIT_NO).unwrap_err().kind(), + io::ErrorKind::WouldBlock + ); + } + + pub fn set_name(_name: &CStr) { + // FIXME: could store this pointer in TLS somewhere + } + + pub fn sleep(_dur: Duration) { + panic!("can't sleep"); // FIXME + } + + pub fn join(self) { + let _ = self.0.recv(); + } +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { None } + pub unsafe fn init() -> Option { None } + pub unsafe fn deinit() {} +} diff --git a/src/libstd/sys/sgx/thread_local.rs b/src/libstd/sys/sgx/thread_local.rs new file mode 100644 index 00000000000..3b628bae4fb --- /dev/null +++ b/src/libstd/sys/sgx/thread_local.rs @@ -0,0 +1,38 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::abi::tls::{Tls, Key as AbiKey}; + +pub type Key = usize; + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + Tls::create(dtor).as_usize() +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + Tls::set(AbiKey::from_usize(key), value) +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + Tls::get(AbiKey::from_usize(key)) +} + +#[inline] +pub unsafe fn destroy(key: Key) { + Tls::destroy(AbiKey::from_usize(key)) +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/src/libstd/sys/sgx/time.rs b/src/libstd/sys/sgx/time.rs new file mode 100644 index 00000000000..b01c992768e --- /dev/null +++ b/src/libstd/sys/sgx/time.rs @@ -0,0 +1,61 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use time::Duration; +use super::abi::usercalls; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +impl Instant { + pub fn now() -> Instant { + Instant(usercalls::insecure_time()) + } + + pub fn sub_instant(&self, other: &Instant) -> Duration { + self.0 - other.0 + } + + pub fn add_duration(&self, other: &Duration) -> Instant { + Instant(self.0 + *other) + } + + pub fn sub_duration(&self, other: &Duration) -> Instant { + Instant(self.0 - *other) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + SystemTime(usercalls::insecure_time()) + } + + pub fn sub_time(&self, other: &SystemTime) + -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn add_duration(&self, other: &Duration) -> SystemTime { + SystemTime(self.0 + *other) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option { + self.0.checked_add(*other).map(|d| SystemTime(d)) + } + + pub fn sub_duration(&self, other: &Duration) -> SystemTime { + SystemTime(self.0 - *other) + } +} diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs new file mode 100644 index 00000000000..ec1135ba30c --- /dev/null +++ b/src/libstd/sys/sgx/waitqueue.rs @@ -0,0 +1,552 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// A simple queue implementation for synchronization primitives. +/// +/// This queue is used to implement condition variable and mutexes. +/// +/// Users of this API are expected to use the `WaitVariable` type. Since +/// that type is not `Sync`, it needs to be protected by e.g. a `SpinMutex` to +/// allow shared access. +/// +/// Since userspace may send spurious wake-ups, the wakeup event state is +/// recorded in the enclave. The wakeup event state is protected by a spinlock. +/// The queue and associated wait state are stored in a `WaitVariable`. + +use ops::{Deref, DerefMut}; +use num::NonZeroUsize; + +use fortanix_sgx_abi::{Tcs, EV_UNPARK, WAIT_INDEFINITE}; +use super::abi::usercalls; +use super::abi::thread; + +use self::unsafe_list::{UnsafeList, UnsafeListEntry}; +pub use self::spin_mutex::{SpinMutex, SpinMutexGuard, try_lock_or_false}; + +/// An queue entry in a `WaitQueue`. +struct WaitEntry { + /// TCS address of the thread that is waiting + tcs: Tcs, + /// Whether this thread has been notified to be awoken + wake: bool +} + +/// Data stored with a `WaitQueue` alongside it. This ensures accesses to the +/// queue and the data are synchronized, since the type itself is not `Sync`. +/// +/// Consumers of this API should use a synchronization primitive for shared +/// access, such as `SpinMutex`. +#[derive(Default)] +pub struct WaitVariable { + queue: WaitQueue, + lock: T +} + +impl WaitVariable { + #[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn + pub const fn new(var: T) -> Self { + WaitVariable { + queue: WaitQueue::new(), + lock: var + } + } + + pub fn queue_empty(&self) -> bool { + self.queue.is_empty() + } + + pub fn lock_var(&self) -> &T { + &self.lock + } + + pub fn lock_var_mut(&mut self) -> &mut T { + &mut self.lock + } +} + +#[derive(Copy, Clone)] +pub enum NotifiedTcs { + Single(Tcs), + All { count: NonZeroUsize } +} + +/// An RAII guard that will notify a set of target threads as well as unlock +/// a mutex on drop. +pub struct WaitGuard<'a, T: 'a> { + mutex_guard: Option>>, + notified_tcs: NotifiedTcs +} + +/// A queue of threads that are waiting on some synchronization primitive. +/// +/// `UnsafeList` entries are allocated on the waiting thread's stack. This +/// avoids any global locking that might happen in the heap allocator. This is +/// safe because the waiting thread will not return from that stack frame until +/// after it is notified. The notifying thread ensures to clean up any +/// references to the list entries before sending the wakeup event. +pub struct WaitQueue { + // We use an inner Mutex here to protect the data in the face of spurious + // wakeups. + inner: UnsafeList>, +} +unsafe impl Send for WaitQueue {} + +impl Default for WaitQueue { + fn default() -> Self { + Self::new() + } +} + +impl<'a, T> WaitGuard<'a, T> { + /// Returns which TCSes will be notified when this guard drops. + pub fn notified_tcs(&self) -> NotifiedTcs { + self.notified_tcs + } +} + +impl<'a, T> Deref for WaitGuard<'a, T> { + type Target = SpinMutexGuard<'a, WaitVariable>; + + fn deref(&self) -> &Self::Target { + self.mutex_guard.as_ref().unwrap() + } +} + +impl<'a, T> DerefMut for WaitGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.mutex_guard.as_mut().unwrap() + } +} + +impl<'a, T> Drop for WaitGuard<'a, T> { + fn drop(&mut self) { + drop(self.mutex_guard.take()); + let target_tcs = match self.notified_tcs { + NotifiedTcs::Single(tcs) => Some(tcs), + NotifiedTcs::All { .. } => None + }; + usercalls::send(EV_UNPARK, target_tcs).unwrap(); + } +} + +impl WaitQueue { + #[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn + pub const fn new() -> Self { + WaitQueue { + inner: UnsafeList::new() + } + } + + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Add the calling thread to the WaitVariable's wait queue, then wait + /// until a wakeup event. + /// + /// This function does not return until this thread has been awoken. + pub fn wait(mut guard: SpinMutexGuard>) { + unsafe { + let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry { + tcs: thread::current(), + wake: false + })); + let entry = guard.queue.inner.push(&mut entry); + drop(guard); + while !entry.lock().wake { + assert_eq!( + usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap() & EV_UNPARK, + EV_UNPARK + ); + } + } + } + + /// Either find the next waiter on the wait queue, or return the mutex + /// guard unchanged. + /// + /// If a waiter is found, a `WaitGuard` is returned which will notify the + /// waiter when it is dropped. + pub fn notify_one(mut guard: SpinMutexGuard>) + -> Result, SpinMutexGuard>> + { + unsafe { + if let Some(entry) = guard.queue.inner.pop() { + let mut entry_guard = entry.lock(); + let tcs = entry_guard.tcs; + entry_guard.wake = true; + drop(entry); + Ok(WaitGuard { + mutex_guard: Some(guard), + notified_tcs: NotifiedTcs::Single(tcs) + }) + } else { + Err(guard) + } + } + } + + /// Either find any and all waiters on the wait queue, or return the mutex + /// guard unchanged. + /// + /// If at least one waiter is found, a `WaitGuard` is returned which will + /// notify all waiters when it is dropped. + pub fn notify_all(mut guard: SpinMutexGuard>) + -> Result, SpinMutexGuard>> + { + unsafe { + let mut count = 0; + while let Some(entry) = guard.queue.inner.pop() { + count += 1; + let mut entry_guard = entry.lock(); + entry_guard.wake = true; + } + if let Some(count) = NonZeroUsize::new(count) { + Ok(WaitGuard { + mutex_guard: Some(guard), + notified_tcs: NotifiedTcs::All { count } + }) + } else { + Err(guard) + } + } + } +} + +/// A doubly-linked list where callers are in charge of memory allocation +/// of the nodes in the list. +mod unsafe_list { + use ptr::NonNull; + use mem; + + pub struct UnsafeListEntry { + next: NonNull>, + prev: NonNull>, + value: Option + } + + impl UnsafeListEntry { + fn dummy() -> Self { + UnsafeListEntry { + next: NonNull::dangling(), + prev: NonNull::dangling(), + value: None + } + } + + pub fn new(value: T) -> Self { + UnsafeListEntry { + value: Some(value), + ..Self::dummy() + } + } + } + + pub struct UnsafeList { + head_tail: NonNull>, + head_tail_entry: Option>, + } + + impl UnsafeList { + #[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn + pub const fn new() -> Self { + unsafe { + UnsafeList { + head_tail: NonNull::new_unchecked(1 as _), + head_tail_entry: None + } + } + } + + unsafe fn init(&mut self) { + if self.head_tail_entry.is_none() { + self.head_tail_entry = Some(UnsafeListEntry::dummy()); + self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap()); + self.head_tail.as_mut().next = self.head_tail; + self.head_tail.as_mut().prev = self.head_tail; + } + } + + pub fn is_empty(&self) -> bool { + unsafe { + if self.head_tail_entry.is_some() { + let first = self.head_tail.as_ref().next; + if first == self.head_tail { + // ,-------> /---------\ next ---, + // | |head_tail| | + // `--- prev \---------/ <-------` + assert_eq!(self.head_tail.as_ref().prev, first); + true + } else { + false + } + } else { + true + } + } + } + + /// Pushes an entry onto the back of the list. + /// + /// # Safety + /// + /// The entry must remain allocated until the entry is removed from the + /// list AND the caller who popped is done using the entry. + pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry) -> &'a T { + self.init(); + + // BEFORE: + // /---------\ next ---> /---------\ + // ... |prev_tail| |head_tail| ... + // \---------/ <--- prev \---------/ + // + // AFTER: + // /---------\ next ---> /-----\ next ---> /---------\ + // ... |prev_tail| |entry| |head_tail| ... + // \---------/ <--- prev \-----/ <--- prev \---------/ + let mut entry = NonNull::new_unchecked(entry); + let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry); + entry.as_mut().prev = prev_tail; + entry.as_mut().next = self.head_tail; + prev_tail.as_mut().next = entry; + (*entry.as_ptr()).value.as_ref().unwrap() + } + + /// Pops an entry from the front of the list. + /// + /// # Safety + /// + /// The caller must make sure to synchronize ending the borrow of the + /// return value and deallocation of the containing entry. + pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> { + self.init(); + + if self.is_empty() { + None + } else { + // BEFORE: + // /---------\ next ---> /-----\ next ---> /------\ + // ... |head_tail| |first| |second| ... + // \---------/ <--- prev \-----/ <--- prev \------/ + // + // AFTER: + // /---------\ next ---> /------\ + // ... |head_tail| |second| ... + // \---------/ <--- prev \------/ + let mut first = self.head_tail.as_mut().next; + let mut second = first.as_mut().next; + self.head_tail.as_mut().next = second; + second.as_mut().prev = self.head_tail; + first.as_mut().next = NonNull::dangling(); + first.as_mut().prev = NonNull::dangling(); + Some((*first.as_ptr()).value.as_ref().unwrap()) + } + } + } + + #[cfg(test)] + mod tests { + use super::*; + use cell::Cell; + + unsafe fn assert_empty(list: &mut UnsafeList) { + assert!(list.pop().is_none(), "assertion failed: list is not empty"); + } + + #[test] + fn init_empty() { + unsafe { + assert_empty(&mut UnsafeList::::new()); + } + } + + #[test] + fn push_pop() { + unsafe { + let mut node = UnsafeListEntry::new(1234); + let mut list = UnsafeList::new(); + assert_eq!(list.push(&mut node), &1234); + assert_eq!(list.pop().unwrap(), &1234); + assert_empty(&mut list); + } + } + + #[test] + fn complex_pushes_pops() { + unsafe { + let mut node1 = UnsafeListEntry::new(1234); + let mut node2 = UnsafeListEntry::new(4567); + let mut node3 = UnsafeListEntry::new(9999); + let mut node4 = UnsafeListEntry::new(8642); + let mut list = UnsafeList::new(); + list.push(&mut node1); + list.push(&mut node2); + assert_eq!(list.pop().unwrap(), &1234); + list.push(&mut node3); + assert_eq!(list.pop().unwrap(), &4567); + assert_eq!(list.pop().unwrap(), &9999); + assert_empty(&mut list); + list.push(&mut node4); + assert_eq!(list.pop().unwrap(), &8642); + assert_empty(&mut list); + } + } + + #[test] + fn cell() { + unsafe { + let mut node = UnsafeListEntry::new(Cell::new(0)); + let mut list = UnsafeList::new(); + let noderef = list.push(&mut node); + assert_eq!(noderef.get(), 0); + list.pop().unwrap().set(1); + assert_empty(&mut list); + assert_eq!(noderef.get(), 1); + } + } + } +} + +/// Trivial spinlock-based implementation of `sync::Mutex`. +// FIXME: Perhaps use Intel TSX to avoid locking? +mod spin_mutex { + use cell::UnsafeCell; + use sync::atomic::{AtomicBool, Ordering, spin_loop_hint}; + use ops::{Deref, DerefMut}; + + #[derive(Default)] + pub struct SpinMutex { + value: UnsafeCell, + lock: AtomicBool, + } + + unsafe impl Send for SpinMutex {} + unsafe impl Sync for SpinMutex {} + + pub struct SpinMutexGuard<'a, T: 'a> { + mutex: &'a SpinMutex, + } + + impl<'a, T> !Send for SpinMutexGuard<'a, T> {} + unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {} + + impl SpinMutex { + pub const fn new(value: T) -> Self { + SpinMutex { + value: UnsafeCell::new(value), + lock: AtomicBool::new(false) + } + } + + #[inline(always)] + pub fn lock(&self) -> SpinMutexGuard { + loop { + match self.try_lock() { + None => while self.lock.load(Ordering::Relaxed) { + spin_loop_hint() + }, + Some(guard) => return guard + } + } + } + + #[inline(always)] + pub fn try_lock(&self) -> Option> { + if !self.lock.compare_and_swap(false, true, Ordering::Acquire) { + Some(SpinMutexGuard { + mutex: self, + }) + } else { + None + } + } + } + + pub macro try_lock_or_false { + ($e:expr) => { + if let Some(v) = $e.try_lock() { + v + } else { + return false + } + } + } + + impl<'a, T> Deref for SpinMutexGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { + &*self.mutex.value.get() + } + } + } + + impl<'a, T> DerefMut for SpinMutexGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { + &mut*self.mutex.value.get() + } + } + } + + impl<'a, T> Drop for SpinMutexGuard<'a, T> { + fn drop(&mut self) { + self.mutex.lock.store(false, Ordering::Release) + } + } + + #[cfg(test)] + mod tests { + #![allow(deprecated)] + + use super::*; + use sync::Arc; + use thread; + + #[test] + fn sleep() { + let mutex = Arc::new(SpinMutex::::default()); + let mutex2 = mutex.clone(); + let guard = mutex.lock(); + let t1 = thread::spawn(move || { + *mutex2.lock() = 1; + }); + thread::sleep_ms(50); + assert_eq!(*guard, 0); + drop(guard); + t1.join().unwrap(); + assert_eq!(*mutex.lock(), 1); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sync::Arc; + use thread; + + #[test] + fn queue() { + let wq = Arc::new(SpinMutex::>::default()); + let wq2 = wq.clone(); + + let locked = wq.lock(); + + let t1 = thread::spawn(move || { + assert!(WaitQueue::notify_one(wq2.lock()).is_none()) + }); + + WaitQueue::wait(locked); + + t1.join().unwrap(); + } +} diff --git a/src/libstd/sys/unix/l4re.rs b/src/libstd/sys/unix/l4re.rs index 21218489679..bbb0fd45ba3 100644 --- a/src/libstd/sys/unix/l4re.rs +++ b/src/libstd/sys/unix/l4re.rs @@ -21,7 +21,7 @@ pub mod net { use sys_common::{AsInner, FromInner, IntoInner}; use sys::fd::FileDesc; use time::Duration; - + use convert::TryFrom; pub extern crate libc as netc; @@ -118,7 +118,7 @@ pub struct TcpStream { } impl TcpStream { - pub fn connect(_: &SocketAddr) -> io::Result { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { unimpl!(); } @@ -216,7 +216,7 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(_: &SocketAddr) -> io::Result { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { unimpl!(); } @@ -278,7 +278,7 @@ pub struct UdpSocket { } impl UdpSocket { - pub fn bind(_: &SocketAddr) -> io::Result { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { unimpl!(); } @@ -402,7 +402,7 @@ pub fn send(&self, _: &[u8]) -> io::Result { unimpl!(); } - pub fn connect(&self, _: &SocketAddr) -> io::Result<()> { + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { unimpl!(); } } @@ -431,11 +431,30 @@ fn next(&mut self) -> Option { } } + impl LookupHost { + pub fn port(&self) -> u16 { + unimpl!(); + } + } + unsafe impl Sync for LookupHost {} unsafe impl Send for LookupHost {} - pub fn lookup_host(_: &str) -> io::Result { - unimpl!(); + + impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result { + unimpl!(); + } + } + + impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unimpl!(); + } } } diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index 87ba2aef4f1..63e341abb2c 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -76,6 +76,6 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; -pub fn stderr_prints_nothing() -> bool { - false +pub fn panic_output() -> Option { + Stderr::new().ok() } diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index f3a45d24657..4ff060018ae 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -49,7 +49,8 @@ unsafe fn pthread_attr_setstacksize(_attr: *mut libc::pthread_attr_t, } impl Thread { - pub unsafe fn new<'a>(stack: usize, p: Box) + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box) -> io::Result { let p = box p; let mut native: libc::pthread_t = mem::zeroed(); diff --git a/src/libstd/sys/wasm/cmath.rs b/src/libstd/sys/wasm/cmath.rs index 64fc14d42d9..d4f8d66ee3e 100644 --- a/src/libstd/sys/wasm/cmath.rs +++ b/src/libstd/sys/wasm/cmath.rs @@ -8,85 +8,32 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[inline] -pub unsafe fn cbrtf(n: f32) -> f32 { - f64::cbrt(n as f64) as f32 -} - -#[inline] -pub unsafe fn expm1f(n: f32) -> f32 { - f64::exp_m1(n as f64) as f32 -} - -#[inline] -#[allow(deprecated)] -pub unsafe fn fdimf(a: f32, b: f32) -> f32 { - f64::abs_sub(a as f64, b as f64) as f32 -} - -#[inline] -pub unsafe fn log1pf(n: f32) -> f32 { - f64::ln_1p(n as f64) as f32 -} - -#[inline] -pub unsafe fn hypotf(x: f32, y: f32) -> f32 { - f64::hypot(x as f64, y as f64) as f32 -} - -#[inline] -pub unsafe fn acosf(n: f32) -> f32 { - f64::acos(n as f64) as f32 -} - -#[inline] -pub unsafe fn asinf(n: f32) -> f32 { - f64::asin(n as f64) as f32 -} - -#[inline] -pub unsafe fn atan2f(n: f32, b: f32) -> f32 { - f64::atan2(n as f64, b as f64) as f32 -} - -#[inline] -pub unsafe fn atanf(n: f32) -> f32 { - f64::atan(n as f64) as f32 -} - -#[inline] -pub unsafe fn coshf(n: f32) -> f32 { - f64::cosh(n as f64) as f32 -} - -#[inline] -pub unsafe fn sinhf(n: f32) -> f32 { - f64::sinh(n as f64) as f32 -} - -#[inline] -pub unsafe fn tanf(n: f32) -> f32 { - f64::tan(n as f64) as f32 -} - -#[inline] -pub unsafe fn tanhf(n: f32) -> f32 { - f64::tanh(n as f64) as f32 -} - // These symbols are all defined in `compiler-builtins` extern { pub fn acos(n: f64) -> f64; + pub fn acosf(n: f32) -> f32; pub fn asin(n: f64) -> f64; + pub fn asinf(n: f32) -> f32; pub fn atan(n: f64) -> f64; pub fn atan2(a: f64, b: f64) -> f64; + pub fn atan2f(a: f32, b: f32) -> f32; + pub fn atanf(n: f32) -> f32; pub fn cbrt(n: f64) -> f64; + pub fn cbrtf(n: f32) -> f32; pub fn cosh(n: f64) -> f64; + pub fn coshf(n: f32) -> f32; pub fn expm1(n: f64) -> f64; + pub fn expm1f(n: f32) -> f32; pub fn fdim(a: f64, b: f64) -> f64; - pub fn log1p(n: f64) -> f64; - pub fn sinh(n: f64) -> f64; - pub fn tan(n: f64) -> f64; - pub fn tanh(n: f64) -> f64; + pub fn fdimf(a: f32, b: f32) -> f32; pub fn hypot(x: f64, y: f64) -> f64; + pub fn hypotf(x: f32, y: f32) -> f32; + pub fn log1p(n: f64) -> f64; + pub fn log1pf(n: f32) -> f32; + pub fn sinh(n: f64) -> f64; + pub fn sinhf(n: f32) -> f32; + pub fn tan(n: f64) -> f64; + pub fn tanf(n: f32) -> f32; + pub fn tanh(n: f64) -> f64; + pub fn tanhf(n: f32) -> f32; } diff --git a/src/libstd/sys/wasm/net.rs b/src/libstd/sys/wasm/net.rs index 03a5b2d779e..e1c33b09cb4 100644 --- a/src/libstd/sys/wasm/net.rs +++ b/src/libstd/sys/wasm/net.rs @@ -13,11 +13,12 @@ use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr}; use time::Duration; use sys::{unsupported, Void}; +use convert::TryFrom; pub struct TcpStream(Void); impl TcpStream { - pub fn connect(_: &SocketAddr) -> io::Result { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { unsupported() } @@ -103,7 +104,7 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { pub struct TcpListener(Void); impl TcpListener { - pub fn bind(_: &SocketAddr) -> io::Result { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { unsupported() } @@ -153,7 +154,7 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { pub struct UdpSocket(Void); impl UdpSocket { - pub fn bind(_: &SocketAddr) -> io::Result { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { unsupported() } @@ -273,7 +274,7 @@ pub fn send(&self, _: &[u8]) -> io::Result { match self.0 {} } - pub fn connect(&self, _: &SocketAddr) -> io::Result<()> { + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { match self.0 {} } } @@ -286,6 +287,12 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { pub struct LookupHost(Void); +impl LookupHost { + pub fn port(&self) -> u16 { + match self.0 {} + } +} + impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -293,8 +300,20 @@ fn next(&mut self) -> Option { } } -pub fn lookup_host(_: &str) -> io::Result { - unsupported() +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unsupported() + } } #[allow(nonstandard_style)] diff --git a/src/libstd/sys/wasm/stdio.rs b/src/libstd/sys/wasm/stdio.rs index 023f29576a2..e51aba75333 100644 --- a/src/libstd/sys/wasm/stdio.rs +++ b/src/libstd/sys/wasm/stdio.rs @@ -70,6 +70,10 @@ pub fn is_ebadf(_err: &io::Error) -> bool { true } -pub fn stderr_prints_nothing() -> bool { - !cfg!(feature = "wasm_syscall") +pub fn panic_output() -> Option { + if cfg!(feature = "wasm_syscall") { + Stderr::new().ok() + } else { + None + } } diff --git a/src/libstd/sys/wasm/thread.rs b/src/libstd/sys/wasm/thread.rs index 4ad89c42b92..3d74ffdc14a 100644 --- a/src/libstd/sys/wasm/thread.rs +++ b/src/libstd/sys/wasm/thread.rs @@ -19,7 +19,8 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; impl Thread { - pub unsafe fn new<'a>(_stack: usize, _p: Box) + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { unsupported() diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index c3a94698a0f..61e0db87ebe 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -228,6 +228,6 @@ pub fn is_ebadf(err: &io::Error) -> bool { // been seen to be acceptable. pub const STDIN_BUF_SIZE: usize = 8 * 1024; -pub fn stderr_prints_nothing() -> bool { - false +pub fn panic_output() -> Option { + Stderr::new().ok() } diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index 85588cc6c8e..1a97dd10ced 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -28,7 +28,8 @@ pub struct Thread { } impl Thread { - pub unsafe fn new<'a>(stack: usize, p: Box) + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box) -> io::Result { let p = box p; diff --git a/src/libstd/sys_common/condvar.rs b/src/libstd/sys_common/condvar.rs index b6f29dd5fc3..16bf0803a8d 100644 --- a/src/libstd/sys_common/condvar.rs +++ b/src/libstd/sys_common/condvar.rs @@ -25,6 +25,7 @@ impl Condvar { /// /// Behavior is undefined if the condition variable is moved after it is /// first used with any of the functions below. + #[unstable(feature = "sys_internals", issue = "0")] // FIXME: min_const_fn pub const fn new() -> Condvar { Condvar(imp::Condvar::new()) } /// Prepares the condition variable for use. diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index 4b8cde3d1f4..881794d9f16 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -57,9 +57,11 @@ macro_rules! rtassert { pub mod process; cfg_if! { - if #[cfg(any(target_os = "cloudabi", target_os = "l4re", target_os = "redox"))] { - pub use sys::net; - } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { + if #[cfg(any(target_os = "cloudabi", + target_os = "l4re", + target_os = "redox", + all(target_arch = "wasm32", not(target_os = "emscripten")), + target_env = "sgx"))] { pub use sys::net; } else { pub mod net; diff --git a/src/libstd/sys_common/mutex.rs b/src/libstd/sys_common/mutex.rs index c6d531c7a1a..87684237638 100644 --- a/src/libstd/sys_common/mutex.rs +++ b/src/libstd/sys_common/mutex.rs @@ -27,6 +27,7 @@ impl Mutex { /// Also, until `init` is called, behavior is undefined if this /// mutex is ever used reentrantly, i.e., `raw_lock` or `try_lock` /// are called by the thread currently holding the lock. + #[unstable(feature = "sys_internals", issue = "0")] // FIXME: min_const_fn pub const fn new() -> Mutex { Mutex(imp::Mutex::new()) } /// Prepare the mutex for use. diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs index d09a233ed89..dce2bf71cec 100644 --- a/src/libstd/sys_common/net.rs +++ b/src/libstd/sys_common/net.rs @@ -20,6 +20,7 @@ use sys::net::netc as c; use sys_common::{AsInner, FromInner, IntoInner}; use time::Duration; +use convert::{TryFrom, TryInto}; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", @@ -129,6 +130,13 @@ fn to_ipv6mr_interface(value: u32) -> ::libc::c_uint { pub struct LookupHost { original: *mut c::addrinfo, cur: *mut c::addrinfo, + port: u16 +} + +impl LookupHost { + pub fn port(&self) -> u16 { + self.port + } } impl Iterator for LookupHost { @@ -158,17 +166,45 @@ fn drop(&mut self) { } } -pub fn lookup_host(host: &str) -> io::Result { - init(); +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; - let c_host = CString::new(host)?; - let mut hints: c::addrinfo = unsafe { mem::zeroed() }; - hints.ai_socktype = c::SOCK_STREAM; - let mut res = ptr::null_mut(); - unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)).map(|_| { - LookupHost { original: res, cur: res } - }) + fn try_from(s: &str) -> io::Result { + macro_rules! try_opt { + ($e:expr, $msg:expr) => ( + match $e { + Some(r) => r, + None => return Err(io::Error::new(io::ErrorKind::InvalidInput, + $msg)), + } + ) + } + + // split the string by ':' and convert the second part to u16 + let mut parts_iter = s.rsplitn(2, ':'); + let port_str = try_opt!(parts_iter.next(), "invalid socket address"); + let host = try_opt!(parts_iter.next(), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + + (host, port).try_into() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result { + init(); + + let c_host = CString::new(host)?; + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + let mut res = ptr::null_mut(); + unsafe { + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)).map(|_| { + LookupHost { original: res, cur: res, port } + }) + } } } @@ -181,7 +217,9 @@ pub struct TcpStream { } impl TcpStream { - pub fn connect(addr: &SocketAddr) -> io::Result { + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + init(); let sock = Socket::new(addr, c::SOCK_STREAM)?; @@ -317,7 +355,9 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(addr: &SocketAddr) -> io::Result { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + init(); let sock = Socket::new(addr, c::SOCK_STREAM)?; @@ -418,7 +458,9 @@ pub struct UdpSocket { } impl UdpSocket { - pub fn bind(addr: &SocketAddr) -> io::Result { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + init(); let sock = Socket::new(addr, c::SOCK_DGRAM)?; @@ -584,8 +626,8 @@ pub fn send(&self, buf: &[u8]) -> io::Result { Ok(ret as usize) } - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addrp, len) = addr.into_inner(); + pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { + let (addrp, len) = addr?.into_inner(); cvt_r(|| unsafe { c::connect(*self.inner.as_inner(), addrp, len) }).map(|_| ()) } } @@ -618,7 +660,7 @@ mod tests { #[test] fn no_lookup_host_duplicates() { let mut addrs = HashMap::new(); - let lh = match lookup_host("localhost") { + let lh = match LookupHost::try_from(("localhost", 0)) { Ok(lh) => lh, Err(e) => panic!("couldn't resolve `localhost': {}", e) }; diff --git a/src/libstd/sys_common/rwlock.rs b/src/libstd/sys_common/rwlock.rs index 71a4f01ec4c..a430c254d3c 100644 --- a/src/libstd/sys_common/rwlock.rs +++ b/src/libstd/sys_common/rwlock.rs @@ -22,6 +22,7 @@ impl RWLock { /// /// Behavior is undefined if the reader-writer lock is moved after it is /// first used with any of the functions below. + #[unstable(feature = "sys_internals", issue = "0")] // FIXME: min_const_fn pub const fn new() -> RWLock { RWLock(imp::RWLock::new()) } /// Acquires shared access to the underlying lock, blocking the current diff --git a/src/libstd/sys_common/util.rs b/src/libstd/sys_common/util.rs index a373e980b97..fc86a59d17f 100644 --- a/src/libstd/sys_common/util.rs +++ b/src/libstd/sys_common/util.rs @@ -10,14 +10,13 @@ use fmt; use io::prelude::*; -use sys::stdio::{Stderr, stderr_prints_nothing}; +use sys::stdio::panic_output; use thread; pub fn dumb_print(args: fmt::Arguments) { - if stderr_prints_nothing() { - return + if let Some(mut out) = panic_output() { + let _ = out.write_fmt(args); } - let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args)); } // Other platforms should use the appropriate platform-specific mechanism for diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 3a9f3ec5c6f..d15b4902412 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -167,10 +167,12 @@ #![stable(feature = "rust1", since = "1.0.0")] use any::Any; +use boxed::FnBox; use cell::UnsafeCell; use ffi::{CStr, CString}; use fmt; use io; +use mem; use panic; use panicking; use str; @@ -452,8 +454,8 @@ pub fn spawn(self, f: F) -> io::Result> where /// [`io::Result`]: ../../std/io/type.Result.html /// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html #[unstable(feature = "thread_spawn_unchecked", issue = "55132")] - pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> where - F: FnOnce() -> T, F: Send, T: Send + pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result> where + F: FnOnce() -> T, F: Send + 'a, T: Send + 'a { let Builder { name, stack_size } = self; @@ -482,7 +484,21 @@ pub unsafe fn spawn_unchecked(self, f: F) -> io::Result> whe }; Ok(JoinHandle(JoinInner { - native: Some(imp::Thread::new(stack_size, Box::new(main))?), + // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed + // through FFI or otherwise used with low-level threading primitives that have no + // notion of or way to enforce lifetimes. + // + // As mentioned in the `Safety` section of this function's documentation, the caller of + // this function needs to guarantee that the passed-in lifetime is sufficiently long + // for the lifetime of the thread. + // + // Similarly, the `sys` implementation must guarantee that no references to the closure + // exist after the thread has terminated, which is signaled by `Thread::join` + // returning. + native: Some(imp::Thread::new( + stack_size, + mem::transmute::, Box>(Box::new(main)) + )?), thread: my_thread, packet: Packet(my_packet), })) diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 7c26d042a7c..b8711a69147 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -1018,10 +1018,12 @@ fn use_color(opts: &TestOpts) -> bool { } } -#[cfg(any(target_os = "cloudabi", target_os = "redox", - all(target_arch = "wasm32", not(target_os = "emscripten"))))] +#[cfg(any(target_os = "cloudabi", + target_os = "redox", + all(target_arch = "wasm32", not(target_os = "emscripten")), + target_env = "sgx"))] fn stdout_isatty() -> bool { - // FIXME: Implement isatty on Redox + // FIXME: Implement isatty on Redox and SGX false } #[cfg(unix)] @@ -1246,7 +1248,7 @@ fn num_cpus() -> usize { 1 } - #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + #[cfg(any(all(target_arch = "wasm32", not(target_os = "emscripten")), target_env = "sgx"))] fn num_cpus() -> usize { 1 } diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs index 954eb9d6d03..eb53332ab33 100644 --- a/src/libunwind/lib.rs +++ b/src/libunwind/lib.rs @@ -26,7 +26,10 @@ cfg_if! { if #[cfg(target_env = "msvc")] { // no extra unwinder support needed - } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { + } else if #[cfg(any( + all(target_arch = "wasm32", not(target_os = "emscripten")), + target_env = "sgx" + ))] { // no unwinder on the system! } else { extern crate libc; diff --git a/src/rust-sgx b/src/rust-sgx new file mode 160000 index 00000000000..96562608880 --- /dev/null +++ b/src/rust-sgx @@ -0,0 +1 @@ +Subproject commit 9656260888095f44830641ca7bb3da609a793451 diff --git a/src/rustc/fortanix-sgx-abi_shim/Cargo.toml b/src/rustc/fortanix-sgx-abi_shim/Cargo.toml new file mode 100644 index 00000000000..fd81d3db3a7 --- /dev/null +++ b/src/rustc/fortanix-sgx-abi_shim/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "fortanix-sgx-abi" +version = "0.0.0" +authors = ["The Rust Project Developers"] + +[lib] +path = "../../rust-sgx/fortanix-sgx-abi/src/lib.rs" +test = false +bench = false +doc = false + +[dependencies] +core = { path = "../../libcore" } +compiler_builtins = { path = "../../rustc/compiler_builtins_shim" } diff --git a/src/stdsimd b/src/stdsimd index 0309be1ade6..5e628c5120c 160000 --- a/src/stdsimd +++ b/src/stdsimd @@ -1 +1 @@ -Subproject commit 0309be1ade6bf61066f2c69f77ac3567b7dc31b5 +Subproject commit 5e628c5120c619a22799187371f057ec41e06f87 diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 700103d35d8..53db589beaf 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -76,6 +76,7 @@ fn filter_dirs(path: &Path) -> bool { "src/tools/lldb", "src/target", "src/stdsimd", + "src/rust-sgx", "target", "vendor", ];