diff options
| author | Konrad Borowski <konrad@borowski.pw> | 2018-12-23 16:47:11 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-12-23 16:47:11 +0100 |
| commit | 8ac5380ea0204dbdcbc8108d259928b67d5f8ebb (patch) | |
| tree | 174d912756fc2678af50d46ff457f7504750a975 /src/libstd/sys | |
| parent | b4a306c1e648c84f289c63e984941b7faad10af1 (diff) | |
| parent | ddab10a692aab2e2984b5c826ed9d78a57e94851 (diff) | |
| download | rust-8ac5380ea0204dbdcbc8108d259928b67d5f8ebb.tar.gz rust-8ac5380ea0204dbdcbc8108d259928b67d5f8ebb.zip | |
Merge branch 'master' into copied
Diffstat (limited to 'src/libstd/sys')
84 files changed, 5461 insertions, 596 deletions
diff --git a/src/libstd/sys/cloudabi/condvar.rs b/src/libstd/sys/cloudabi/condvar.rs index ccf848a9be4..3229d98624e 100644 --- a/src/libstd/sys/cloudabi/condvar.rs +++ b/src/libstd/sys/cloudabi/condvar.rs @@ -13,7 +13,7 @@ use mem; use sync::atomic::{AtomicU32, Ordering}; use sys::cloudabi::abi; use sys::mutex::{self, Mutex}; -use sys::time::dur2intervals; +use sys::time::checked_dur2intervals; use time::Duration; extern "C" { @@ -114,6 +114,8 @@ impl Condvar { // Call into the kernel to wait on the condition variable. let condvar = self.condvar.get(); + let timeout = checked_dur2intervals(&dur) + .expect("overflow converting duration to nanoseconds"); let subscriptions = [ abi::subscription { type_: abi::eventtype::CONDVAR, @@ -132,7 +134,7 @@ impl Condvar { union: abi::subscription_union { clock: abi::subscription_clock { clock_id: abi::clockid::MONOTONIC, - timeout: dur2intervals(&dur), + timeout, ..mem::zeroed() }, }, diff --git a/src/libstd/sys/cloudabi/shims/env.rs b/src/libstd/sys/cloudabi/shims/env.rs index 31777aa94bc..c7691e3b2df 100644 --- a/src/libstd/sys/cloudabi/shims/env.rs +++ b/src/libstd/sys/cloudabi/shims/env.rs @@ -9,11 +9,11 @@ // except according to those terms. pub mod os { - pub const FAMILY: &'static str = "cloudabi"; - pub const OS: &'static str = "cloudabi"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "cloudabi"; + pub const OS: &str = "cloudabi"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } 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 io; 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<TcpStream> { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { unsupported() } @@ -105,7 +106,7 @@ impl fmt::Debug for TcpStream { pub struct TcpListener(Void); impl TcpListener { - pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { unsupported() } @@ -155,7 +156,7 @@ impl fmt::Debug for TcpListener { pub struct UdpSocket(Void); impl UdpSocket { - pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { unsupported() } @@ -271,7 +272,7 @@ impl UdpSocket { 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 @@ impl fmt::Debug for UdpSocket { 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<SocketAddr> { @@ -291,6 +298,18 @@ impl Iterator for LookupHost { } } -pub fn lookup_host(_: &str) -> io::Result<LookupHost> { - unsupported() +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result<LookupHost> { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { + 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<impl io::Write> { + Stderr::new().ok() } diff --git a/src/libstd/sys/cloudabi/thread.rs b/src/libstd/sys/cloudabi/thread.rs index 8cca47efd22..a64e0f06849 100644 --- a/src/libstd/sys/cloudabi/thread.rs +++ b/src/libstd/sys/cloudabi/thread.rs @@ -16,7 +16,7 @@ use libc; use mem; use ptr; use sys::cloudabi::abi; -use sys::time::dur2intervals; +use sys::time::checked_dur2intervals; use sys_common::thread::*; use time::Duration; @@ -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<dyn FnBox() + 'a>) -> io::Result<Thread> { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { let p = box p; let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); @@ -69,13 +70,15 @@ impl Thread { } pub fn sleep(dur: Duration) { + let timeout = checked_dur2intervals(&dur) + .expect("overflow converting duration to nanoseconds"); unsafe { let subscription = abi::subscription { type_: abi::eventtype::CLOCK, union: abi::subscription_union { clock: abi::subscription_clock { clock_id: abi::clockid::MONOTONIC, - timeout: dur2intervals(&dur), + timeout, ..mem::zeroed() }, }, @@ -118,7 +121,6 @@ pub mod guard { pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { diff --git a/src/libstd/sys/cloudabi/time.rs b/src/libstd/sys/cloudabi/time.rs index a442d1e4ad7..c9fea18fda6 100644 --- a/src/libstd/sys/cloudabi/time.rs +++ b/src/libstd/sys/cloudabi/time.rs @@ -19,15 +19,10 @@ pub struct Instant { t: abi::timestamp, } -fn checked_dur2intervals(dur: &Duration) -> Option<abi::timestamp> { +pub fn checked_dur2intervals(dur: &Duration) -> Option<abi::timestamp> { dur.as_secs() - .checked_mul(NSEC_PER_SEC) - .and_then(|nanos| nanos.checked_add(dur.subsec_nanos() as abi::timestamp)) -} - -pub fn dur2intervals(dur: &Duration) -> abi::timestamp { - checked_dur2intervals(dur) - .expect("overflow converting duration to nanoseconds") + .checked_mul(NSEC_PER_SEC)? + .checked_add(dur.subsec_nanos() as abi::timestamp) } impl Instant { @@ -47,20 +42,16 @@ impl Instant { Duration::new(diff / NSEC_PER_SEC, (diff % NSEC_PER_SEC) as u32) } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant { - t: self.t - .checked_add(dur2intervals(other)) - .expect("overflow when adding duration to instant"), - } + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { + t: self.t.checked_add(checked_dur2intervals(other)?)?, + }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant { - t: self.t - .checked_sub(dur2intervals(other)) - .expect("overflow when subtracting duration from instant"), - } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { + t: self.t.checked_sub(checked_dur2intervals(other)?)?, + }) } } @@ -95,23 +86,16 @@ impl SystemTime { } } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - self.checked_add_duration(other) - .expect("overflow when adding duration to instant") - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - checked_dur2intervals(other) - .and_then(|d| self.t.checked_add(d)) - .map(|t| SystemTime {t}) + Some(SystemTime { + t: self.t.checked_add(checked_dur2intervals(other)?)?, + }) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime { - t: self.t - .checked_sub(dur2intervals(other)) - .expect("overflow when subtracting duration from instant"), - } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { + t: self.t.checked_sub(checked_dur2intervals(other)?)?, + }) } } 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 @@ cfg_if! { } 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/env.rs b/src/libstd/sys/redox/env.rs index 669b7520df8..75e35046fb7 100644 --- a/src/libstd/sys/redox/env.rs +++ b/src/libstd/sys/redox/env.rs @@ -9,11 +9,11 @@ // except according to those terms. pub mod os { - pub const FAMILY: &'static str = "redox"; - pub const OS: &'static str = "redox"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "redox"; + pub const OS: &str = "redox"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } 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 string::{String, ToString}; 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 dns; mod tcp; mod udp; -pub struct LookupHost(IntoIter<SocketAddr>); +pub struct LookupHost(IntoIter<SocketAddr>, u16); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.1 + } +} impl Iterator for LookupHost { type Item = SocketAddr; @@ -38,65 +45,93 @@ impl Iterator for LookupHost { } } -pub fn lookup_host(host: &str) -> Result<LookupHost> { - let mut ip_string = String::new(); - File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?; - let ip: Vec<u8> = ip_string.trim().split('.').map(|part| part.parse::<u8>() - .unwrap_or(0)).collect(); - - let mut dns_string = String::new(); - File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?; - let dns: Vec<u8> = dns_string.trim().split('.').map(|part| part.parse::<u8>() - .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))); - } +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(s: &str) -> io::Result<LookupHost> { + 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<LookupHost> { + let mut ip_string = String::new(); + File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?; + let ip: Vec<u8> = ip_string.trim().split('.').map(|part| part.parse::<u8>() + .unwrap_or(0)).collect(); + + let mut dns_string = String::new(); + File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?; + let dns: Vec<u8> = dns_string.trim().split('.').map(|part| part.parse::<u8>() + .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 @@ use super::{path_to_peer_addr, path_to_local_addr}; pub struct TcpStream(File); impl TcpStream { - pub fn connect(addr: &SocketAddr) -> Result<TcpStream> { - let path = format!("tcp:{}", addr); + pub fn connect(addr: Result<&SocketAddr>) -> Result<TcpStream> { + let path = format!("tcp:{}", addr?); let mut options = OpenOptions::new(); options.read(true); options.write(true); @@ -180,8 +180,8 @@ impl IntoInner<File> for TcpStream { pub struct TcpListener(File); impl TcpListener { - pub fn bind(addr: &SocketAddr) -> Result<TcpListener> { - let path = format!("tcp:/{}", addr); + pub fn bind(addr: Result<&SocketAddr>) -> Result<TcpListener> { + 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 @@ use super::{path_to_peer_addr, path_to_local_addr}; pub struct UdpSocket(File, UnsafeCell<Option<SocketAddr>>); impl UdpSocket { - pub fn bind(addr: &SocketAddr) -> Result<UdpSocket> { - let path = format!("udp:/{}", addr); + pub fn bind(addr: Result<&SocketAddr>) -> Result<UdpSocket> { + let path = format!("udp:/{}", addr?); let mut options = OpenOptions::new(); options.read(true); options.write(true); @@ -37,8 +37,8 @@ impl UdpSocket { 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/path.rs b/src/libstd/sys/redox/path.rs index e6a267dd5d9..b1a4ed30404 100644 --- a/src/libstd/sys/redox/path.rs +++ b/src/libstd/sys/redox/path.rs @@ -35,5 +35,5 @@ pub fn parse_prefix(path: &OsStr) -> Option<Prefix> { } } -pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/redox/process.rs b/src/libstd/sys/redox/process.rs index 4370c1e0502..8f6d83c544a 100644 --- a/src/libstd/sys/redox/process.rs +++ b/src/libstd/sys/redox/process.rs @@ -143,7 +143,7 @@ impl Command { pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) -> io::Result<(Process, StdioPipes)> { - const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + const CLOEXEC_MSG_FOOTER: &[u8] = b"NOEX"; if self.saw_nul { return Err(io::Error::new(ErrorKind::InvalidInput, 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<impl io::Write> { + Stderr::new().ok() } diff --git a/src/libstd/sys/redox/thread.rs b/src/libstd/sys/redox/thread.rs index bab91b16e6c..ca014fd576b 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<dyn FnBox() + 'a>) -> io::Result<Thread> { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { let p = box p; let id = cvt(syscall::clone(syscall::CLONE_VM | syscall::CLONE_FS | syscall::CLONE_FILES))?; @@ -91,5 +92,4 @@ pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option<Guard> { None } pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } diff --git a/src/libstd/sys/redox/time.rs b/src/libstd/sys/redox/time.rs index beff8d287e7..cb2eab52211 100644 --- a/src/libstd/sys/redox/time.rs +++ b/src/libstd/sys/redox/time.rs @@ -41,10 +41,6 @@ impl Timespec { } } - fn add_duration(&self, other: &Duration) -> Timespec { - self.checked_add_duration(other).expect("overflow when adding duration to time") - } - fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() @@ -67,27 +63,25 @@ impl Timespec { }) } - fn sub_duration(&self, other: &Duration) -> Timespec { + fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() .try_into() // <- target type would be `i64` .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs)) - .expect("overflow when subtracting duration from time"); + .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; // Similar to above, nanos can't overflow. let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; - secs = secs.checked_sub(1).expect("overflow when subtracting \ - duration from time"); + secs = secs.checked_sub(1)?; } - Timespec { + Some(Timespec { t: syscall::TimeSpec { tv_sec: secs, tv_nsec: nsec as i32, }, - } + }) } } @@ -150,12 +144,12 @@ impl Instant { }) } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant { t: self.t.add_duration(other) } + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_sub_duration(other)? }) } } @@ -178,16 +172,12 @@ impl SystemTime { self.t.sub_timespec(&other.t) } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.add_duration(other) } - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - self.t.checked_add_duration(other).map(|t| SystemTime { t }) + Some(SystemTime { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) } } diff --git a/src/libstd/sys/sgx/abi/entry.S b/src/libstd/sys/sgx/abi/entry.S new file mode 100644 index 00000000000..49ede0674ce --- /dev/null +++ b/src/libstd/sys/sgx/abi/entry.S @@ -0,0 +1,335 @@ +/* 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 <LICENSE-APACHE or */ +/* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license */ +/* <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 + /* The base address (relative to enclave start) of the enclave text section */ + globvar TEXT_BASE 8 + /* The size in bytes of enclacve text section */ + globvar TEXT_SIZE 8 + /* The base address (relative to enclave start) of the enclave EH_FRM_HDR section */ + globvar EH_FRM_HDR_BASE 8 + /* The size in bytes of enclacve EH_FRM_HDR section */ + globvar EH_FRM_HDR_SIZE 8 + +.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<<tcsls_flag_secondary),%e\reg + and %gs:tcsls_flags,%\reg +.endm + +.text +.global sgx_entry +.type sgx_entry,function +sgx_entry: +/* save user registers */ + mov %rcx,%gs:tcsls_user_retip + mov %rsp,%gs:tcsls_user_rsp + mov %rbp,%gs:tcsls_user_rbp + mov %r12,%gs:tcsls_user_r12 + mov %r13,%gs:tcsls_user_r13 + mov %r14,%gs:tcsls_user_r14 + mov %r15,%gs:tcsls_user_r15 + mov %rbx,%gs:tcsls_tcs_addr + stmxcsr %gs:tcsls_user_mxcsr + fnstcw %gs:tcsls_user_fcw +/* reset user state */ + cld /* x86-64 ABI requires DF to be unset at function entry/exit */ +/* check for debug buffer pointer */ + testb $0xff,DEBUG(%rip) + jz .Lskip_debug_init + mov %r10,%gs:tcsls_debug_panic_buf_ptr +.Lskip_debug_init: +/* check if returning from usercall */ + mov %gs:tcsls_last_rsp,%r11 + test %r11,%r11 + jnz .Lusercall_ret +/* setup stack */ + mov %gs:tcsls_tos,%rsp /* initially, RSP is not set to the correct value */ + /* here. This is fixed below under "adjust stack". */ +/* check for thread init */ + bts $tcsls_flag_init_once,%gs:tcsls_flags + jc .Lskip_init +/* adjust stack */ + lea IMAGE_BASE(%rip),%rax + add %rax,%rsp + mov %rsp,%gs:tcsls_tos +/* call tcs_init */ +/* store caller-saved registers in callee-saved registers */ + mov %rdi,%rbx + mov %rsi,%r12 + mov %rdx,%r13 + mov %r8,%r14 + mov %r9,%r15 + load_tcsls_flag_secondary_bool di /* RDI = tcs_init() argument: secondary: bool */ + call tcs_init +/* reload caller-saved registers */ + mov %rbx,%rdi + mov %r12,%rsi + mov %r13,%rdx + mov %r14,%r8 + mov %r15,%r9 +.Lskip_init: +/* check for panic */ + bt $0,.Lpanicked(%rip) + jc .Lreentry_panic +/* call into main entry point */ + load_tcsls_flag_secondary_bool cx /* RCX = entry() argument: secondary: bool */ + call entry /* RDI, RSI, RDX, R8, R9 passed in from userspace */ + mov %rax,%rsi /* RSI = return value */ + /* NOP: mov %rdx,%rdx */ /* RDX = return value */ + xor %rdi,%rdi /* RDI = normal exit */ +.Lexit: +/* clear general purpose register state */ + /* RAX overwritten by ENCLU */ + /* RBX set later */ + /* RCX overwritten by ENCLU */ + /* RDX contains return value */ + /* RSP set later */ + /* RBP set later */ + /* RDI contains exit mode */ + /* RSI contains return value */ + xor %r8,%r8 + xor %r9,%r9 + xor %r10,%r10 + xor %r11,%r11 + /* R12 ~ R15 set by sgx_exit */ +.Lsgx_exit: +/* clear extended register state */ + mov %rdx, %rcx /* save RDX */ + mov $-1, %rax + mov %rax, %rdx + xrstor .Lxsave_clear(%rip) + mov %rcx, %rdx /* restore RDX */ +/* clear flags */ + pushq $0 + popfq +/* restore user registers */ + mov %gs:tcsls_user_r12,%r12 + mov %gs:tcsls_user_r13,%r13 + mov %gs:tcsls_user_r14,%r14 + mov %gs:tcsls_user_r15,%r15 + mov %gs:tcsls_user_retip,%rbx + mov %gs:tcsls_user_rsp,%rsp + mov %gs:tcsls_user_rbp,%rbp + fldcw %gs:tcsls_user_fcw + ldmxcsr %gs:tcsls_user_mxcsr +/* exit enclave */ + mov $0x4,%eax /* EEXIT */ + enclu +/* end sgx_entry */ + +.Lreentry_panic: + lea .Lreentry_panic_msg(%rip),%rdi + mov $.Lreentry_panic_msg_end-.Lreentry_panic_msg,%esi + orq $8,%rsp + jmp panic_msg + +.Lusercall_panic: + lea .Lusercall_panic_msg(%rip),%rdi + mov $.Lusercall_panic_msg_end-.Lusercall_panic_msg,%esi + orq $8,%rsp + jmp panic_msg + +.macro push_callee_saved_registers + push %r15 + push %r14 + push %r13 + push %r12 + push %rbp + push %rbx + sub $8, %rsp + fstcw 4(%rsp) + stmxcsr (%rsp) +.endm + +.global panic_exit +panic_exit: +/* save registers in DEBUG mode, so that debugger can reconstruct the stack */ + testb $0xff,DEBUG(%rip) + jz .Lskip_save_registers + push_callee_saved_registers + movq %rsp,%gs:tcsls_panic_last_rsp +.Lskip_save_registers: +/* set panicked bit */ + movb $1,.Lpanicked(%rip) +/* call usercall exit(true) */ + mov $1,%esi /* RSI = usercall() argument: panic = true */ + xor %rdx,%rdx /* RDX cleared */ + movq $usercall_nr_exit,%rdi /* RDI = usercall exit */ + jmp .Lexit + +/* This *MUST* be called with 6 parameters, otherwise register information */ +/* might leak! */ +.global usercall +usercall: + test %rdi,%rdi + jle .Lusercall_panic +/* save callee-saved state */ + push_callee_saved_registers + movq %rsp,%gs:tcsls_last_rsp +/* clear general purpose register state */ + /* RAX overwritten by ENCLU */ + /* RBX set by sgx_exit */ + /* RCX overwritten by ENCLU */ + /* RDX contains parameter */ + /* RSP set by sgx_exit */ + /* RBP set by sgx_exit */ + /* RDI contains parameter */ + /* RSI contains parameter */ + /* R8 contains parameter */ + /* R9 contains parameter */ + xor %r10,%r10 + xor %r11,%r11 + /* R12 ~ R15 set by sgx_exit */ +/* extended registers/flags cleared by sgx_exit */ +/* exit */ + jmp .Lsgx_exit +.Lusercall_ret: + movq $0,%gs:tcsls_last_rsp +/* restore callee-saved state, cf. push_callee_saved_registers */ + mov %r11,%rsp + ldmxcsr (%rsp) + fldcw 4(%rsp) + add $8, %rsp + pop %rbx + pop %rbp + pop %r12 + pop %r13 + pop %r14 + pop %r15 +/* return */ + mov %rsi,%rax /* RAX = return value */ + /* NOP: mov %rdx,%rdx */ /* RDX = return value */ + ret + +/* +The following functions need to be defined externally: +``` +// Called by entry code when it needs to panic +extern "C" fn panic_msg(msg: &'static str) -> ! { + 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..bf32c712216 --- /dev/null +++ b/src/libstd/sys/sgx/abi/mem.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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T>(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<T>(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_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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Self> { + 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<usize> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> { + 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::<Rela<u64>>(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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<unsafe extern fn(*mut u8)>>(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<Self>) { + 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<unsafe extern fn(*mut u8)>) -> 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<usize> { + '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<Enumerate<Iter<'a, AtomicUsize>>>, + elem_idx: usize, + } + + impl<'a> Iterator for SyncBitsetIter<'a> { + type Item = usize; + + fn next(&mut self) -> Option<usize> { + 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::<Vec<_>>(), 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> UserSafeSized for FifoDescriptor<T> {} +unsafe impl UserSafeSized for ByteBuffer {} +unsafe impl UserSafeSized for Usercall {} +unsafe impl UserSafeSized for Return {} +unsafe impl<T: UserSafeSized> 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<T: UserSafeSized> UserSafe for T { + unsafe fn align_of() -> usize { + mem::align_of::<T>() + } + + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self { + assert_eq!(size, mem::size_of::<T>()); + ptr as _ + } +} + +unsafe impl<T: UserSafeSized> UserSafe for [T] { + unsafe fn align_of() -> usize { + mem::align_of::<T>() + } + + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self { + let elem_size = mem::size_of::<T>(); + 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<T>` 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<T: ?Sized>(UnsafeCell<T>); +/// An owned type in userspace memory. `User<T>` is equivalent to `Box<T>` 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<T: UserSafe + ?Sized>(*mut UserRef<T>); + +impl<T: ?Sized> User<T> 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<T>` 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<T> User<T> where T: UserSafe { + pub fn uninitialized() -> Self { + Self::new_uninit_bytes(mem::size_of::<T>()) + } +} + +impl<T> User<[T]> where [T]: UserSafe { + pub fn uninitialized(n: usize) -> Self { + Self::new_uninit_bytes(n * mem::size_of::<T>()) + } + + /// 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::<T>()) as _) + } +} + +impl<T: ?Sized> UserRef<T> 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<T> UserRef<T> where T: UserSafe { + pub fn to_enclave(&self) -> T { + unsafe { ptr::read(self.0.get()) } + } +} + +impl<T> 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::<T>()) 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::<T>()) 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<T>) { + 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<T> { + let mut ret = Vec::with_capacity(self.len()); + self.copy_to_enclave_vec(&mut ret); + ret + } + + pub fn iter(&self) -> Iter<T> + where T: UserSafe // FIXME: should be implied by [T]: UserSafe? + { + unsafe { + Iter((&*self.as_raw_ptr()).iter()) + } + } + + pub fn iter_mut(&mut self) -> IterMut<T> + 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<T>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + 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<T>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + unsafe { + self.0.next().map(|e| UserRef::from_mut_ptr(e)) + } + } +} + +impl<T: ?Sized> Deref for User<T> where T: UserSafe { + type Target = UserRef<T>; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} + +impl<T: ?Sized> DerefMut for User<T> where T: UserSafe { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut*self.0 } + } +} + +impl<T: ?Sized> Drop for User<T> 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<T: CoerceUnsized<U>, U> CoerceUnsized<UserRef<U>> for UserRef<T> {} + +impl<T, I: SliceIndex<[T]>> Index<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe { + type Output = UserRef<I::Output>; + + #[inline] + fn index(&self, index: I) -> &UserRef<I::Output> { + unsafe { + UserRef::from_ptr(index.index(&*self.as_raw_ptr())) + } + } +} + +impl<T, I: SliceIndex<[T]>> IndexMut<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe { + #[inline] + fn index_mut(&mut self, index: I) -> &mut UserRef<I::Output> { + 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..d1d180e4825 --- /dev/null +++ b/src/libstd/sys/sgx/abi/usercalls/mod.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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<ByteBuffer>) -> Vec<u8> { + 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<usize> { + 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 write(fd: Fd, buf: &[u8]) -> IoResult<usize> { + 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<ByteBuffer>, 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::<ByteBuffer>::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<u64> { + unsafe { raw::wait(event_mask, timeout).from_sgx_result() } +} + +pub fn send(event_set: u64, tcs: Option<Tcs>) -> 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<Self::Return>; +} + +impl<T> FromSgxResult for (Result, T) { + type Return = T; + + fn from_sgx_result(self) -> IoResult<Self::Return> { + 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<Self::Return> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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!(<T> *const T); +define_ra!(<T> *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<T: RegisterArgument> RegisterArgument for Option<NonNull<T>> { + fn from_register(a: Register) -> Option<NonNull<T>> { + 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<T: RegisterArgument> 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<T: RegisterArgument, U: RegisterArgument> 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<dlmalloc::Dlmalloc> = 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<OsString>; + +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::<ArgsStore>(); + 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::<ArgsStore>::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<OsString> { + self.0.next().cloned() + } + fn size_hint(&self) -> (usize, Option<usize>) { + 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<OsString> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<F>(_frame: Frame, + _callback: F, + _: &BacktraceContext) -> io::Result<()> + where F: FnOnce(Option<&str>) -> io::Result<()> +{ + unsupported() +} + +pub fn foreach_symbol_fileline<F>(_: Frame, + _: F, + _: &BacktraceContext) -> io::Result<bool> + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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..940f50f25b8 --- /dev/null +++ b/src/libstd/sys/sgx/condvar.rs @@ -0,0 +1,51 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<WaitVariable<()>>, +} + +impl Condvar { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<usize> { + usercalls::read(self.fd, buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + usercalls::write(self.fd, buf) + } + + pub fn flush(&self) -> io::Result<()> { + usercalls::flush(self.fd) + } +} + +impl AsInner<Fd> 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<SystemTime> { + match self.0 {} + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + match self.0 {} + } + + pub fn created(&self) -> io::Result<SystemTime> { + 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<H: Hasher>(&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<DirEntry>; + + fn next(&mut self) -> Option<io::Result<DirEntry>> { + 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<FileAttr> { + match self.0 {} + } + + pub fn file_type(&self) -> io::Result<FileType> { + 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<File> { + unsupported() + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + 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<usize> { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn flush(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result<File> { + 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<ReadDir> { + 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<PathBuf> { + 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<FileAttr> { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result<FileAttr> { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T>() -> io::Result<T> { + 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<T>(v: T) -> io::Result<T> { + 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..994cf91eea0 --- /dev/null +++ b/src/libstd/sys/sgx/mutex.rs @@ -0,0 +1,150 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<WaitVariable<bool>>, +} + +// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 +impl Mutex { + 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<Tcs>, + count: usize +} + +pub struct ReentrantMutex { + inner: SpinMutex<WaitVariable<ReentrantLock>>, +} + +impl ReentrantMutex { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<FileDesc>, + 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<String> { + 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::<NonIpSockAddr>()).is_some() { + Ok(e.into_inner().unwrap().downcast::<NonIpSockAddr>().unwrap().host) + } else { + Err(e) + } + } +} + +fn addr_to_sockaddr(addr: &str) -> io::Result<SocketAddr> { + // 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<TcpStream> { + 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<TcpStream> { + Self::connect(Ok(addr)) // FIXME: ignoring timeout + } + + pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + sgx_ineffective(None) + } + + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + sgx_ineffective(None) + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { + Ok(0) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.inner.inner.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.inner.inner.write(buf) + } + + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + addr_to_sockaddr(&self.peer_addr) + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + addr_to_sockaddr(&self.inner.local_addr) + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn duplicate(&self) -> io::Result<TcpStream> { + Ok(self.clone()) + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn nodelay(&self) -> io::Result<bool> { + sgx_ineffective(false) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result<u32> { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + 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<TcpListener> { + 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<SocketAddr> { + 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<TcpListener> { + Ok(self.clone()) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result<u32> { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn only_v6(&self) -> io::Result<bool> { + sgx_ineffective(false) + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + 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<UdpSocket> { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + 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<usize> { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result<UdpSocket> { + match self.0 {} + } + + pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { + match self.0 {} + } + + pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { + match self.0 {} + } + + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + match self.0 {} + } + + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + match self.0 {} + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn broadcast(&self) -> io::Result<bool> { + match self.0 {} + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v4(&self) -> io::Result<bool> { + match self.0 {} + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_ttl_v4(&self) -> io::Result<u32> { + match self.0 {} + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v6(&self) -> io::Result<bool> { + 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<u32> { + match self.0 {} + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + match self.0 {} + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn send(&self, _: &[u8]) -> io::Result<usize> { + 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<LookupHost> { + 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<SocketAddr> { + match self.0 {} + } +} + +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(v: &'a str) -> io::Result<LookupHost> { + 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> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<PathBuf> { + 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<PathBuf> { + match *self.0 {} + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError> + where I: Iterator<Item=T>, T: AsRef<OsStr> +{ + 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<PathBuf> { + unsupported() +} + +static ENV: AtomicUsize = AtomicUsize::new(0); +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex<HashMap<OsString, OsString>>; + +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<OsString, OsString>| -> 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<Option<OsString>> { + 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<PathBuf> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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>`/`[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<u8> +} + +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<Vec<u8>> for Buf { + fn into_inner(self) -> Vec<u8> { + 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, Buf> { + 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<Slice> { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } + + #[inline] + pub fn from_box(boxed: Box<Slice>) -> Buf { + let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; + Buf { inner: inner.into_vec() } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc<Slice> { + 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<str> { + 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<Slice> { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box<Slice> { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + 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<Slice> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Prefix> { + 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<usize> { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +pub fn read2(p1: AnonPipe, + _v1: &mut Vec<u8>, + _p2: AnonPipe, + _v2: &mut Vec<u8>) -> 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<DefaultEnvKey> +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option<AnonPipe>, + pub stdout: Option<AnonPipe>, + pub stderr: Option<AnonPipe>, +} + +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<DefaultEnvKey> { + &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<AnonPipe> for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From<File> 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<i32> { + 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<ExitStatus> { + match self.0 {} + } + + pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { + 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..d1af98bd4f5 --- /dev/null +++ b/src/libstd/sys/sgx/rwlock.rs @@ -0,0 +1,258 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use num::NonZeroUsize; +use slice; +use str; + +use super::waitqueue::{ + try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable, +}; +use mem; + +pub struct RWLock { + readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>, + writer: SpinMutex<WaitVariable<bool>>, +} + +// Below is to check at compile time, that RWLock has size of 128 bytes. +#[allow(dead_code)] +unsafe fn rw_lock_size_assert(r: RWLock) { + mem::transmute::<RWLock, [u8; 128]>(r); +} + +//unsafe impl Send for RWLock {} +//unsafe impl Sync for RWLock {} // FIXME + +impl RWLock { + 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] + unsafe fn __read_unlock( + &self, + mut rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>, + wguard: SpinMutexGuard<WaitVariable<bool>>, + ) { + *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 read_unlock(&self) { + let rguard = self.readers.lock(); + let wguard = self.writer.lock(); + self.__read_unlock(rguard, wguard); + } + + #[inline] + unsafe fn __write_unlock( + &self, + rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>, + wguard: SpinMutexGuard<WaitVariable<bool>>, + ) { + 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 write_unlock(&self) { + let rguard = self.readers.lock(); + let wguard = self.writer.lock(); + self.__write_unlock(rguard, wguard); + } + + #[inline] + unsafe fn unlock(&self) { + let rguard = self.readers.lock(); + let wguard = self.writer.lock(); + if *wguard.lock_var() == true { + self.__write_unlock(rguard, wguard); + } else { + self.__read_unlock(rguard, wguard); + } + } + + #[inline] + pub unsafe fn destroy(&self) {} +} + +const EINVAL: i32 = 22; + +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 { + if p.is_null() { + return EINVAL; + } + (*p).read(); + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 { + if p.is_null() { + return EINVAL; + } + (*p).write(); + return 0; +} +#[no_mangle] +pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 { + if p.is_null() { + return EINVAL; + } + (*p).unlock(); + return 0; +} + +#[no_mangle] +pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { + if s < 0 { + return; + } + let buf = slice::from_raw_parts(m as *const u8, s as _); + if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) { + eprint!("{}", s); + } +} + +#[no_mangle] +pub unsafe extern "C" fn __rust_abort() { + ::sys::abort_internal(); +} + +#[cfg(test)] +mod tests { + + use super::*; + use core::array::FixedSizeArray; + use mem::MaybeUninit; + use {mem, ptr}; + + // The below test verifies that the bytes of initialized RWLock are the ones + // we use in libunwind. + // If they change we need to update src/UnwindRustSgx.h in libunwind. + #[test] + fn test_c_rwlock_initializer() { + const RWLOCK_INIT: &[u8] = &[ + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + ]; + + let mut init = MaybeUninit::<RWLock>::zeroed(); + init.set(RWLock::new()); + assert_eq!( + mem::transmute::<_, [u8; 128]>(init.into_inner()).as_slice(), + RWLOCK_INIT + ); + } +} 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<F: FnOnce(&FileDesc) -> 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<Stdin> { Ok(Stdin(())) } + + pub fn read(&self, data: &mut [u8]) -> io::Result<usize> { + with_std_fd(abi::FD_STDIN, |fd| fd.read(data)) + } +} + +impl Stdout { + pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + 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<Stderr> { Ok(Stderr(())) } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + 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<usize> { + 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<impl io::Write> { + 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..9f3c4536cb5 --- /dev/null +++ b/src/libstd/sys/sgx/thread.rs @@ -0,0 +1,100 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<dyn FnBox()>, + done: mpsc::Sender<()>, + } + + impl Task { + pub(super) fn new(p: Box<dyn FnBox()>) -> (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<Mutex<Vec<Task>>> = None; + + pub(super) fn lock() -> MutexGuard<'static, Vec<Task>> { + 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<dyn FnBox()>) + -> io::Result<Thread> + { + 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<Guard> { None } + pub unsafe fn init() -> Option<Guard> { None } +} 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<unsafe extern fn(*mut u8)>) -> 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..196e1a97fc4 --- /dev/null +++ b/src/libstd/sys/sgx/time.rs @@ -0,0 +1,57 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_sub(*other)?)) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + SystemTime(usercalls::insecure_time()) + } + + pub fn sub_time(&self, other: &SystemTime) + -> Result<Duration, Duration> { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs new file mode 100644 index 00000000000..ef0def13eee --- /dev/null +++ b/src/libstd/sys/sgx/waitqueue.rs @@ -0,0 +1,549 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T>` 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<T> { + queue: WaitQueue, + lock: T +} + +impl<T> WaitVariable<T> { + 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<SpinMutexGuard<'a, WaitVariable<T>>>, + 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<SpinMutex<WaitEntry>>, +} +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<T>>; + + 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 { + 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<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) { + 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<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) + -> Result<WaitGuard<T>, SpinMutexGuard<WaitVariable<T>>> + { + 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<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) + -> Result<WaitGuard<T>, SpinMutexGuard<WaitVariable<T>>> + { + 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<T> { + next: NonNull<UnsafeListEntry<T>>, + prev: NonNull<UnsafeListEntry<T>>, + value: Option<T> + } + + impl<T> UnsafeListEntry<T> { + 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<T> { + head_tail: NonNull<UnsafeListEntry<T>>, + head_tail_entry: Option<UnsafeListEntry<T>>, + } + + impl<T> UnsafeList<T> { + 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<T>) -> &'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<T>(list: &mut UnsafeList<T>) { + assert!(list.pop().is_none(), "assertion failed: list is not empty"); + } + + #[test] + fn init_empty() { + unsafe { + assert_empty(&mut UnsafeList::<i32>::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<T> { + value: UnsafeCell<T>, + lock: AtomicBool, + } + + unsafe impl<T: Send> Send for SpinMutex<T> {} + unsafe impl<T: Send> Sync for SpinMutex<T> {} + + pub struct SpinMutexGuard<'a, T: 'a> { + mutex: &'a SpinMutex<T>, + } + + impl<'a, T> !Send for SpinMutexGuard<'a, T> {} + unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {} + + impl<T> SpinMutex<T> { + pub const fn new(value: T) -> Self { + SpinMutex { + value: UnsafeCell::new(value), + lock: AtomicBool::new(false) + } + } + + #[inline(always)] + pub fn lock(&self) -> SpinMutexGuard<T> { + 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<SpinMutexGuard<T>> { + 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::<i32>::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::<WaitVariable<()>>::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/android.rs b/src/libstd/sys/unix/android.rs index 10436723a81..462eab56664 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -15,7 +15,7 @@ //! always work with the most recent version of Android, but we also want to //! work with older versions of Android for whenever projects need to. //! -//! Our current minimum supported Android version is `android-9`, e.g. Android +//! Our current minimum supported Android version is `android-9`, e.g., Android //! with API level 9. We then in theory want to work on that and all future //! versions of Android! //! diff --git a/src/libstd/sys/unix/env.rs b/src/libstd/sys/unix/env.rs index ad116c57f55..1b6838f0295 100644 --- a/src/libstd/sys/unix/env.rs +++ b/src/libstd/sys/unix/env.rs @@ -10,176 +10,176 @@ #[cfg(target_os = "linux")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "linux"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "linux"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "macos")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "macos"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".dylib"; - pub const DLL_EXTENSION: &'static str = "dylib"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "macos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "ios")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "ios"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".dylib"; - pub const DLL_EXTENSION: &'static str = "dylib"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "ios"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "freebsd")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "freebsd"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "freebsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "dragonfly")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "dragonfly"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "dragonfly"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "bitrig")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "bitrig"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "bitrig"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "netbsd")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "netbsd"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "netbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "openbsd")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "openbsd"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "openbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "android")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "android"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "android"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "solaris")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "solaris"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "solaris"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "haiku")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "haiku"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "haiku"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(all(target_os = "emscripten", target_arch = "asmjs"))] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "emscripten"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ".js"; - pub const EXE_EXTENSION: &'static str = "js"; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "emscripten"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".js"; + pub const EXE_EXTENSION: &str = "js"; } #[cfg(all(target_os = "emscripten", target_arch = "wasm32"))] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "emscripten"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ".js"; - pub const EXE_EXTENSION: &'static str = "js"; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "emscripten"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".js"; + pub const EXE_EXTENSION: &str = "js"; } #[cfg(target_os = "fuchsia")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "fuchsia"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "fuchsia"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "l4re")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "l4re"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "l4re"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "hermit")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "hermit"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "hermit"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 7e65bbdef2a..af2f1998415 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -348,7 +348,7 @@ pub trait OpenOptionsExt { /// # Examples /// /// ```no_run - /// # #![feature(libc)] + /// # #![feature(rustc_private)] /// extern crate libc; /// use std::fs::OpenOptions; /// use std::os::unix::fs::OpenOptionsExt; diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 737437c76b7..bcf0d440eba 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -132,7 +132,7 @@ impl SocketAddr { if len == 0 { // When there is a datagram from unnamed unix socket // linux returns zero bytes of address - len = sun_path_offset() as libc::socklen_t; // i.e. zero-length address + len = sun_path_offset() as libc::socklen_t; // i.e., zero-length address } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { return Err(io::Error::new(io::ErrorKind::InvalidInput, "file descriptor did not correspond to a Unix socket")); 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 mod net { } impl TcpStream { - pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { unimpl!(); } @@ -216,7 +216,7 @@ pub mod net { } impl TcpListener { - pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { unimpl!(); } @@ -278,7 +278,7 @@ pub mod net { } impl UdpSocket { - pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { unimpl!(); } @@ -402,7 +402,7 @@ pub mod net { unimpl!(); } - pub fn connect(&self, _: &SocketAddr) -> io::Result<()> { + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { unimpl!(); } } @@ -431,11 +431,30 @@ pub mod net { } } + 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<LookupHost> { - unimpl!(); + + impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result<LookupHost> { + unimpl!(); + } + } + + impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { + unimpl!(); + } } } diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 2d10541752c..f30817e69ab 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -26,7 +26,7 @@ pub extern crate libc as netc; pub type wrlen_t = size_t; // See below for the usage of SOCK_CLOEXEC, but this constant is only defined on -// Linux currently (e.g. support doesn't exist on other platforms). In order to +// Linux currently (e.g., support doesn't exist on other platforms). In order to // get name resolution to work and things to compile we just define a dummy // SOCK_CLOEXEC here for other platforms. Note that the dummy constant isn't // actually ever used (the blocks below are wrapped in `if cfg!` as well. @@ -203,18 +203,21 @@ impl Socket { // Linux. This was added in 2.6.28, however, and because we support // 2.6.18 we must detect this support dynamically. if cfg!(target_os = "linux") { - weak! { - fn accept4(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int + syscall! { + fn accept4( + fd: c_int, + addr: *mut sockaddr, + addr_len: *mut socklen_t, + flags: c_int + ) -> c_int } - if let Some(accept) = accept4.get() { - let res = cvt_r(|| unsafe { - accept(self.0.raw(), storage, len, SOCK_CLOEXEC) - }); - match res { - Ok(fd) => return Ok(Socket(FileDesc::new(fd))), - Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {} - Err(e) => return Err(e), - } + let res = cvt_r(|| unsafe { + accept4(self.0.raw(), storage, len, SOCK_CLOEXEC) + }); + match res { + Ok(fd) => return Ok(Socket(FileDesc::new(fd))), + Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {} + Err(e) => return Err(e), } } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index b387a8d59a5..6e8ee445994 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -67,7 +67,8 @@ pub fn errno() -> i32 { } /// Sets the platform-specific value of errno -#[cfg(any(target_os = "solaris", target_os = "fuchsia"))] // only needed for readdir so far +#[cfg(all(not(target_os = "linux"), + not(target_os = "dragonfly")))] // needed for readdir and syscall! pub fn set_errno(e: i32) { unsafe { *errno_location() = e as c_int @@ -84,6 +85,18 @@ pub fn errno() -> i32 { unsafe { errno as i32 } } +#[cfg(target_os = "dragonfly")] +pub fn set_errno(e: i32) { + extern { + #[thread_local] + static mut errno: c_int; + } + + unsafe { + errno = e; + } +} + /// Gets a detailed string description for the given error number. pub fn error_string(errno: i32) -> String { extern { @@ -283,11 +296,14 @@ pub fn current_exe() -> io::Result<PathBuf> { #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] pub fn current_exe() -> io::Result<PathBuf> { - let selfexe = PathBuf::from("/proc/self/exe"); - if selfexe.exists() { - ::fs::read_link(selfexe) - } else { - Err(io::Error::new(io::ErrorKind::Other, "no /proc/self/exe available. Is /proc mounted?")) + match ::fs::read_link("/proc/self/exe") { + Err(ref e) if e.kind() == io::ErrorKind::NotFound => { + Err(io::Error::new( + io::ErrorKind::Other, + "no /proc/self/exe available. Is /proc mounted?" + )) + }, + other => other, } } diff --git a/src/libstd/sys/unix/path.rs b/src/libstd/sys/unix/path.rs index bf9af7a4353..834b4b448dc 100644 --- a/src/libstd/sys/unix/path.rs +++ b/src/libstd/sys/unix/path.rs @@ -25,5 +25,5 @@ pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { None } -pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index 0a5dccdddda..24b2959a3fa 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -22,7 +22,7 @@ use sys::{cvt, cvt_r}; pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - weak! { fn pipe2(*mut c_int, c_int) -> c_int } + syscall! { fn pipe2(fds: *mut c_int, flags: c_int) -> c_int } static INVALID: AtomicBool = ATOMIC_BOOL_INIT; let mut fds = [0; 2]; @@ -39,22 +39,20 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { !INVALID.load(Ordering::SeqCst) { - if let Some(pipe) = pipe2.get() { - // Note that despite calling a glibc function here we may still - // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to - // emulate on older kernels, so if you happen to be running on - // an older kernel you may see `pipe2` as a symbol but still not - // see the syscall. - match cvt(unsafe { pipe(fds.as_mut_ptr(), libc::O_CLOEXEC) }) { - Ok(_) => { - return Ok((AnonPipe(FileDesc::new(fds[0])), - AnonPipe(FileDesc::new(fds[1])))); - } - Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => { - INVALID.store(true, Ordering::SeqCst); - } - Err(e) => return Err(e), + // Note that despite calling a glibc function here we may still + // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to + // emulate on older kernels, so if you happen to be running on + // an older kernel you may see `pipe2` as a symbol but still not + // see the syscall. + match cvt(unsafe { pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }) { + Ok(_) => { + return Ok((AnonPipe(FileDesc::new(fds[0])), + AnonPipe(FileDesc::new(fds[1])))); + } + Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => { + INVALID.store(true, Ordering::SeqCst); } + Err(e) => return Err(e), } } cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index bfbf12f34ee..3248f424460 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -22,7 +22,7 @@ use sys; impl Command { pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) -> io::Result<(Process, StdioPipes)> { - const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + const CLOEXEC_MSG_FOOTER: &[u8] = b"NOEX"; let envp = self.capture_env(); diff --git a/src/libstd/sys/unix/process/zircon.rs b/src/libstd/sys/unix/process/zircon.rs index a06c73ee263..0335c1e914c 100644 --- a/src/libstd/sys/unix/process/zircon.rs +++ b/src/libstd/sys/unix/process/zircon.rs @@ -215,7 +215,7 @@ pub const FDIO_SPAWN_ACTION_TRANSFER_FD: u32 = 0x0002; // and has a closed remote end will return ERR_REMOTE_CLOSED. #[allow(unused)] pub const ERR_SHOULD_WAIT: zx_status_t = -22; -// ERR_CANCELED: The in-progress operation (e.g. a wait) has been +// ERR_CANCELED: The in-progress operation (e.g., a wait) has been // // canceled. #[allow(unused)] pub const ERR_CANCELED: zx_status_t = -23; 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<impl io::Write> { + Stderr::new().ok() } diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index f3a45d24657..e0d2c620498 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<dyn FnBox() + 'a>) + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { let p = box p; let mut native: libc::pthread_t = mem::zeroed(); @@ -210,7 +211,6 @@ pub mod guard { pub type Guard = Range<usize>; pub unsafe fn current() -> Option<Guard> { None } pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } @@ -354,26 +354,6 @@ pub mod guard { } } - pub unsafe fn deinit() { - if !cfg!(target_os = "linux") { - if let Some(stackaddr) = get_stack_start_aligned() { - // Remove the protection on the guard page. - // FIXME: we cannot unmap the page, because when we mmap() - // above it may be already mapped by the OS, which we can't - // detect from mmap()'s return value. If we unmap this page, - // it will lead to failure growing stack size on platforms like - // macOS. Instead, just restore the page to a writable state. - // This ain't Linux, so we probably don't need to care about - // execstack. - let result = mprotect(stackaddr, PAGE_SIZE, PROT_READ | PROT_WRITE); - - if result != 0 { - panic!("unable to reset the guard page"); - } - } - } - } - #[cfg(any(target_os = "macos", target_os = "bitrig", target_os = "openbsd", diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index 1f9539c36e0..8f8aaa88b22 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -42,10 +42,6 @@ impl Timespec { } } - fn add_duration(&self, other: &Duration) -> Timespec { - self.checked_add_duration(other).expect("overflow when adding duration to time") - } - fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() @@ -68,27 +64,25 @@ impl Timespec { }) } - fn sub_duration(&self, other: &Duration) -> Timespec { + fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() .try_into() // <- target type would be `libc::time_t` .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs)) - .expect("overflow when subtracting duration from time"); + .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; // Similar to above, nanos can't overflow. let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; - secs = secs.checked_sub(1).expect("overflow when subtracting \ - duration from time"); + secs = secs.checked_sub(1)?; } - Timespec { + Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _, }, - } + }) } } @@ -165,18 +159,16 @@ mod inner { Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32) } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant { - t: self.t.checked_add(dur2intervals(other)) - .expect("overflow when adding duration to instant"), - } + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { + t: self.t.checked_add(checked_dur2intervals(other)?)?, + }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant { - t: self.t.checked_sub(dur2intervals(other)) - .expect("overflow when subtracting duration from instant"), - } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { + t: self.t.checked_sub(checked_dur2intervals(other)?)?, + }) } } @@ -199,16 +191,12 @@ mod inner { self.t.sub_timespec(&other.t) } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.add_duration(other) } - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - self.t.checked_add_duration(other).map(|t| SystemTime { t }) + Some(SystemTime { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) } } @@ -236,12 +224,12 @@ mod inner { } } - fn dur2intervals(dur: &Duration) -> u64 { + fn checked_dur2intervals(dur: &Duration) -> Option<u64> { + let nanos = dur.as_secs() + .checked_mul(NSEC_PER_SEC)? + .checked_add(dur.subsec_nanos() as u64)?; let info = info(); - let nanos = dur.as_secs().checked_mul(NSEC_PER_SEC).and_then(|nanos| { - nanos.checked_add(dur.subsec_nanos() as u64) - }).expect("overflow converting duration to nanoseconds"); - mul_div_u64(nanos, info.denom as u64, info.numer as u64) + Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) } fn info() -> &'static libc::mach_timebase_info { @@ -299,12 +287,12 @@ mod inner { }) } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant { t: self.t.add_duration(other) } + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_sub_duration(other)? }) } } @@ -327,16 +315,12 @@ mod inner { self.t.sub_timespec(&other.t) } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.add_duration(other) } - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - self.t.checked_add_duration(other).map(|t| SystemTime { t }) + Some(SystemTime { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) } } diff --git a/src/libstd/sys/unix/weak.rs b/src/libstd/sys/unix/weak.rs index 18944be58ee..7d293f1c47a 100644 --- a/src/libstd/sys/unix/weak.rs +++ b/src/libstd/sys/unix/weak.rs @@ -77,3 +77,38 @@ unsafe fn fetch(name: &str) -> usize { }; libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize } + +#[cfg(not(target_os = "linux"))] +macro_rules! syscall { + (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( + unsafe fn $name($($arg_name: $t),*) -> $ret { + use libc; + use super::os; + + weak! { fn $name($($t),*) -> $ret } + + if let Some(fun) = $name.get() { + fun($($arg_name),*) + } else { + os::set_errno(libc::ENOSYS); + -1 + } + } + ) +} + +#[cfg(target_os = "linux")] +macro_rules! syscall { + (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( + unsafe fn $name($($arg_name:$t),*) -> $ret { + // This looks like a hack, but concat_idents only accepts idents + // (not paths). + use libc::*; + + syscall( + concat_idents!(SYS_, $name), + $($arg_name as c_long),* + ) as $ret + } + ) +} 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 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 hypot(x: f64, y: f64) -> f64; + pub fn tanhf(n: f32) -> f32; } diff --git a/src/libstd/sys/wasm/env.rs b/src/libstd/sys/wasm/env.rs index 1422042bd02..09235a944ee 100644 --- a/src/libstd/sys/wasm/env.rs +++ b/src/libstd/sys/wasm/env.rs @@ -9,11 +9,11 @@ // 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 = ".wasm"; - pub const DLL_EXTENSION: &'static str = "wasm"; - pub const EXE_SUFFIX: &'static str = ".wasm"; - pub const EXE_EXTENSION: &'static str = "wasm"; + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".wasm"; + pub const DLL_EXTENSION: &str = "wasm"; + pub const EXE_SUFFIX: &str = ".wasm"; + pub const EXE_EXTENSION: &str = "wasm"; } 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 io; 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<TcpStream> { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { unsupported() } @@ -103,7 +104,7 @@ impl fmt::Debug for TcpStream { pub struct TcpListener(Void); impl TcpListener { - pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { unsupported() } @@ -153,7 +154,7 @@ impl fmt::Debug for TcpListener { pub struct UdpSocket(Void); impl UdpSocket { - pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { unsupported() } @@ -273,7 +274,7 @@ impl UdpSocket { 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 @@ impl fmt::Debug for UdpSocket { 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<SocketAddr> { @@ -293,8 +300,20 @@ impl Iterator for LookupHost { } } -pub fn lookup_host(_: &str) -> io::Result<LookupHost> { - unsupported() +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result<LookupHost> { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { + unsupported() + } } #[allow(nonstandard_style)] diff --git a/src/libstd/sys/wasm/path.rs b/src/libstd/sys/wasm/path.rs index 395b8c1e40e..fcc9d617a87 100644 --- a/src/libstd/sys/wasm/path.rs +++ b/src/libstd/sys/wasm/path.rs @@ -25,5 +25,5 @@ pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { None } -pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; 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<impl io::Write> { + 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..f9abb0b825a 100644 --- a/src/libstd/sys/wasm/thread.rs +++ b/src/libstd/sys/wasm/thread.rs @@ -19,7 +19,8 @@ pub struct Thread(Void); pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; impl Thread { - pub unsafe fn new<'a>(_stack: usize, _p: Box<dyn FnBox() + 'a>) + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, _p: Box<dyn FnBox()>) -> io::Result<Thread> { unsupported() @@ -67,7 +68,6 @@ pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option<Guard> { None } pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } cfg_if! { diff --git a/src/libstd/sys/wasm/time.rs b/src/libstd/sys/wasm/time.rs index 991e8176edf..cc56773e0ea 100644 --- a/src/libstd/sys/wasm/time.rs +++ b/src/libstd/sys/wasm/time.rs @@ -28,12 +28,12 @@ impl Instant { self.0 - other.0 } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant(self.0 + *other) + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_add(*other)?)) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant(self.0 - *other) + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_sub(*other)?)) } } @@ -47,15 +47,11 @@ impl SystemTime { 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<SystemTime> { - self.0.checked_add(*other).map(|d| SystemTime(d)) + Some(SystemTime(self.0.checked_add(*other)?)) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime(self.0 - *other) + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_sub(*other)?)) } } diff --git a/src/libstd/sys/windows/args.rs b/src/libstd/sys/windows/args.rs index 4784633edc1..9e9198e05ee 100644 --- a/src/libstd/sys/windows/args.rs +++ b/src/libstd/sys/windows/args.rs @@ -11,12 +11,14 @@ #![allow(dead_code)] // runtime init functions not used during testing use os::windows::prelude::*; +use sys::windows::os::current_exe; use sys::c; -use slice; -use ops::Range; use ffi::OsString; -use libc::{c_int, c_void}; use fmt; +use vec; +use core::iter; +use slice; +use path::PathBuf; pub unsafe fn init(_argc: isize, _argv: *const *const u8) { } @@ -24,20 +26,146 @@ pub unsafe fn cleanup() { } pub fn args() -> Args { unsafe { - let mut nArgs: c_int = 0; - let lpCmdLine = c::GetCommandLineW(); - let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs); - - // szArcList can be NULL if CommandLinToArgvW failed, - // but in that case nArgs is 0 so we won't actually - // try to read a null pointer - Args { cur: szArgList, range: 0..(nArgs as isize) } + let lp_cmd_line = c::GetCommandLineW(); + let parsed_args_list = parse_lp_cmd_line( + lp_cmd_line as *const u16, + || current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new())); + + Args { parsed_args_list: parsed_args_list.into_iter() } } } +/// Implements the Windows command-line argument parsing algorithm. +/// +/// Microsoft's documentation for the Windows CLI argument format can be found at +/// <https://docs.microsoft.com/en-us/previous-versions//17w5ykft(v=vs.85)>. +/// +/// Windows includes a function to do this in shell32.dll, +/// but linking with that DLL causes the process to be registered as a GUI application. +/// GUI applications add a bunch of overhead, even if no windows are drawn. See +/// <https://randomascii.wordpress.com/2018/12/03/a-not-called-function-can-cause-a-5x-slowdown/>. +/// +/// This function was tested for equivalence to the shell32.dll implementation in +/// Windows 10 Pro v1803, using an exhaustive test suite available at +/// <https://gist.github.com/notriddle/dde431930c392e428055b2dc22e638f5> or +/// <https://paste.gg/p/anonymous/47d6ed5f5bd549168b1c69c799825223>. +unsafe fn parse_lp_cmd_line<F: Fn() -> OsString>(lp_cmd_line: *const u16, exe_name: F) + -> Vec<OsString> { + const BACKSLASH: u16 = '\\' as u16; + const QUOTE: u16 = '"' as u16; + const TAB: u16 = '\t' as u16; + const SPACE: u16 = ' ' as u16; + let mut ret_val = Vec::new(); + if lp_cmd_line.is_null() || *lp_cmd_line == 0 { + ret_val.push(exe_name()); + return ret_val; + } + let mut cmd_line = { + let mut end = 0; + while *lp_cmd_line.offset(end) != 0 { + end += 1; + } + slice::from_raw_parts(lp_cmd_line, end as usize) + }; + // The executable name at the beginning is special. + cmd_line = match cmd_line[0] { + // The executable name ends at the next quote mark, + // no matter what. + QUOTE => { + let args = { + let mut cut = cmd_line[1..].splitn(2, |&c| c == QUOTE); + if let Some(exe) = cut.next() { + ret_val.push(OsString::from_wide(exe)); + } + cut.next() + }; + if let Some(args) = args { + args + } else { + return ret_val; + } + } + // Implement quirk: when they say whitespace here, + // they include the entire ASCII control plane: + // "However, if lpCmdLine starts with any amount of whitespace, CommandLineToArgvW + // will consider the first argument to be an empty string. Excess whitespace at the + // end of lpCmdLine is ignored." + 0...SPACE => { + ret_val.push(OsString::new()); + &cmd_line[1..] + }, + // The executable name ends at the next whitespace, + // no matter what. + _ => { + let args = { + let mut cut = cmd_line.splitn(2, |&c| c > 0 && c <= SPACE); + if let Some(exe) = cut.next() { + ret_val.push(OsString::from_wide(exe)); + } + cut.next() + }; + if let Some(args) = args { + args + } else { + return ret_val; + } + } + }; + let mut cur = Vec::new(); + let mut in_quotes = false; + let mut was_in_quotes = false; + let mut backslash_count: usize = 0; + for &c in cmd_line { + match c { + // backslash + BACKSLASH => { + backslash_count += 1; + was_in_quotes = false; + }, + QUOTE if backslash_count % 2 == 0 => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); + backslash_count = 0; + if was_in_quotes { + cur.push('"' as u16); + was_in_quotes = false; + } else { + was_in_quotes = in_quotes; + in_quotes = !in_quotes; + } + } + QUOTE if backslash_count % 2 != 0 => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); + backslash_count = 0; + was_in_quotes = false; + cur.push(b'"' as u16); + } + SPACE | TAB if !in_quotes => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); + if !cur.is_empty() || was_in_quotes { + ret_val.push(OsString::from_wide(&cur[..])); + cur.truncate(0); + } + backslash_count = 0; + was_in_quotes = false; + } + _ => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); + backslash_count = 0; + was_in_quotes = false; + cur.push(c); + } + } + } + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); + // include empty quoted strings at the end of the arguments list + if !cur.is_empty() || was_in_quotes || in_quotes { + ret_val.push(OsString::from_wide(&cur[..])); + } + ret_val +} + pub struct Args { - range: Range<isize>, - cur: *mut *mut u16, + parsed_args_list: vec::IntoIter<OsString>, } pub struct ArgsInnerDebug<'a> { @@ -46,19 +174,7 @@ pub struct ArgsInnerDebug<'a> { impl<'a> fmt::Debug for ArgsInnerDebug<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("[")?; - let mut first = true; - for i in self.args.range.clone() { - if !first { - f.write_str(", ")?; - } - first = false; - - // Here we do allocation which could be avoided. - fmt::Debug::fmt(&unsafe { os_string_from_ptr(*self.args.cur.offset(i)) }, f)?; - } - f.write_str("]")?; - Ok(()) + self.args.parsed_args_list.as_slice().fmt(f) } } @@ -70,38 +186,82 @@ impl Args { } } -unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString { - let mut len = 0; - while *ptr.offset(len) != 0 { len += 1; } - - // Push it onto the list. - let ptr = ptr as *const u16; - let buf = slice::from_raw_parts(ptr, len as usize); - OsStringExt::from_wide(buf) -} - impl Iterator for Args { type Item = OsString; - fn next(&mut self) -> Option<OsString> { - self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) - } - fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() } + fn next(&mut self) -> Option<OsString> { self.parsed_args_list.next() } + fn size_hint(&self) -> (usize, Option<usize>) { self.parsed_args_list.size_hint() } } impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option<OsString> { - self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) - } + fn next_back(&mut self) -> Option<OsString> { self.parsed_args_list.next_back() } } impl ExactSizeIterator for Args { - fn len(&self) -> usize { self.range.len() } + fn len(&self) -> usize { self.parsed_args_list.len() } } -impl Drop for Args { - fn drop(&mut self) { - // self.cur can be null if CommandLineToArgvW previously failed, - // but LocalFree ignores NULL pointers - unsafe { c::LocalFree(self.cur as *mut c_void); } +#[cfg(test)] +mod tests { + use sys::windows::args::*; + use ffi::OsString; + + fn chk(string: &str, parts: &[&str]) { + let mut wide: Vec<u16> = OsString::from(string).encode_wide().collect(); + wide.push(0); + let parsed = unsafe { + parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE")) + }; + let expected: Vec<OsString> = parts.iter().map(|k| OsString::from(k)).collect(); + assert_eq!(parsed.as_slice(), expected.as_slice()); + } + + #[test] + fn empty() { + chk("", &["TEST.EXE"]); + chk("\0", &["TEST.EXE"]); + } + + #[test] + fn single_words() { + chk("EXE one_word", &["EXE", "one_word"]); + chk("EXE a", &["EXE", "a"]); + chk("EXE 😅", &["EXE", "😅"]); + chk("EXE 😅🤦", &["EXE", "😅🤦"]); + } + + #[test] + fn official_examples() { + chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]); + chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]); + chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]); + chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]); + } + + #[test] + fn whitespace_behavior() { + chk(r#" test"#, &["", "test"]); + chk(r#" test"#, &["", "test"]); + chk(r#" test test2"#, &["", "test", "test2"]); + chk(r#" test test2"#, &["", "test", "test2"]); + chk(r#"test test2 "#, &["test", "test2"]); + chk(r#"test test2 "#, &["test", "test2"]); + chk(r#"test "#, &["test"]); + } + + #[test] + fn genius_quotes() { + chk(r#"EXE "" """#, &["EXE", "", ""]); + chk(r#"EXE "" """"#, &["EXE", "", "\""]); + chk( + r#"EXE "this is """all""" in the same argument""#, + &["EXE", "this is \"all\" in the same argument"] + ); + chk(r#"EXE "a"""#, &["EXE", "a\""]); + chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]); + // quotes cannot be escaped in command names + chk(r#""EXE" check"#, &["EXE", "check"]); + chk(r#""EXE check""#, &["EXE check"]); + chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]); + chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]); } } diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index c84874a3e88..fa21f459a8a 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -1035,9 +1035,6 @@ extern "system" { pub fn SetLastError(dwErrCode: DWORD); pub fn GetCommandLineW() -> *mut LPCWSTR; - pub fn LocalFree(ptr: *mut c_void); - pub fn CommandLineToArgvW(lpCmdLine: *mut LPCWSTR, - pNumArgs: *mut c_int) -> *mut *mut u16; pub fn GetTempPathW(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD; pub fn OpenProcessToken(ProcessHandle: HANDLE, diff --git a/src/libstd/sys/windows/env.rs b/src/libstd/sys/windows/env.rs index e6d74895774..4523df04f24 100644 --- a/src/libstd/sys/windows/env.rs +++ b/src/libstd/sys/windows/env.rs @@ -9,11 +9,11 @@ // except according to those terms. pub mod os { - pub const FAMILY: &'static str = "windows"; - pub const OS: &'static str = "windows"; - pub const DLL_PREFIX: &'static str = ""; - pub const DLL_SUFFIX: &'static str = ".dll"; - pub const DLL_EXTENSION: &'static str = "dll"; - pub const EXE_SUFFIX: &'static str = ".exe"; - pub const EXE_EXTENSION: &'static str = "exe"; + pub const FAMILY: &str = "windows"; + pub const OS: &str = "windows"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".dll"; + pub const DLL_EXTENSION: &str = "dll"; + pub const EXE_SUFFIX: &str = ".exe"; + pub const EXE_EXTENSION: &str = "exe"; } diff --git a/src/libstd/sys/windows/ext/ffi.rs b/src/libstd/sys/windows/ext/ffi.rs index bae0d02786a..0a13aeabe84 100644 --- a/src/libstd/sys/windows/ext/ffi.rs +++ b/src/libstd/sys/windows/ext/ffi.rs @@ -117,7 +117,7 @@ impl OsStringExt for OsString { /// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html #[stable(feature = "rust1", since = "1.0.0")] pub trait OsStrExt { - /// Re-encodes an `OsStr` as a wide character sequence, i.e. potentially + /// Re-encodes an `OsStr` as a wide character sequence, i.e., potentially /// ill-formed UTF-16. /// /// This is lossless: calling [`OsString::from_wide`] and then diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 082d4689c7b..949060b34dd 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -443,7 +443,7 @@ impl FromInner<c::HANDLE> for File { impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // FIXME(#24570): add more info here (e.g. mode) + // FIXME(#24570): add more info here (e.g., mode) let mut b = f.debug_struct("File"); b.field("handle", &self.handle.raw()); if let Ok(path) = get_path(&self) { diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index 29ea82c2053..84ef62e5fe9 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -48,8 +48,8 @@ pub fn error_string(mut errnum: i32) -> String { // `[MS-ERREF]`: https://msdn.microsoft.com/en-us/library/cc231198.aspx if (errnum & c::FACILITY_NT_BIT as i32) != 0 { // format according to https://support.microsoft.com/en-us/help/259693 - const NTDLL_DLL: &'static [u16] = &['N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, - '.' as _, 'D' as _, 'L' as _, 'L' as _, 0]; + const NTDLL_DLL: &[u16] = &['N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, + '.' as _, 'D' as _, 'L' as _, 'L' as _, 0]; module = c::GetModuleHandleW(NTDLL_DLL.as_ptr()); if module != ptr::null_mut() { @@ -67,7 +67,7 @@ pub fn error_string(mut errnum: i32) -> String { buf.len() as c::DWORD, ptr::null()) as usize; if res == 0 { - // Sometimes FormatMessageW can fail e.g. system doesn't like langId, + // Sometimes FormatMessageW can fail e.g., system doesn't like langId, let fm_err = errno(); return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err); @@ -76,7 +76,7 @@ pub fn error_string(mut errnum: i32) -> String { match String::from_utf16(&buf[..res]) { Ok(mut msg) => { // Trim trailing CRLF inserted by FormatMessageW - let len = msg.trim_right().len(); + let len = msg.trim_end().len(); msg.truncate(len); msg }, diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs index 98d62a0c953..385ea8e0531 100644 --- a/src/libstd/sys/windows/path.rs +++ b/src/libstd/sys/windows/path.rs @@ -91,10 +91,7 @@ pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> { } fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> { - let first = match path.iter().position(|x| f(*x)) { - None => return None, - Some(x) => &path[..x], - }; + let first = &path[..path.iter().position(|x| f(*x))?]; path = &path[(first.len() + 1)..]; let idx = path.iter().position(|x| f(*x)); let second = &path[..idx.unwrap_or(path.len())]; @@ -102,5 +99,5 @@ pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> { } } -pub const MAIN_SEP_STR: &'static str = "\\"; +pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index 4b19519a57a..f9eed31f0e0 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -100,23 +100,23 @@ pub fn anon_pipe(ours_readable: bool) -> io::Result<Pipes> { 0, ptr::null_mut()); - // We pass the FILE_FLAG_FIRST_PIPE_INSTANCE flag above, and we're + // We pass the `FILE_FLAG_FIRST_PIPE_INSTANCE` flag above, and we're // also just doing a best effort at selecting a unique name. If - // ERROR_ACCESS_DENIED is returned then it could mean that we + // `ERROR_ACCESS_DENIED` is returned then it could mean that we // accidentally conflicted with an already existing pipe, so we try // again. // // Don't try again too much though as this could also perhaps be a // legit error. - // If ERROR_INVALID_PARAMETER is returned, this probably means we're - // running on pre-Vista version where PIPE_REJECT_REMOTE_CLIENTS is + // If `ERROR_INVALID_PARAMETER` is returned, this probably means we're + // running on pre-Vista version where `PIPE_REJECT_REMOTE_CLIENTS` is // not supported, so we continue retrying without it. This implies // reduced security on Windows versions older than Vista by allowing // connections to this pipe from remote machines. // Proper fix would increase the number of FFI imports and introduce // significant amount of Windows XP specific code with no clean // testing strategy - // for more info see https://github.com/rust-lang/rust/pull/37677 + // For more info, see https://github.com/rust-lang/rust/pull/37677. if handle == c::INVALID_HANDLE_VALUE { let err = io::Error::last_os_error(); let raw_os_err = err.raw_os_error(); diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index ff1ee0d26fe..03c1bb54af8 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -487,7 +487,7 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> { } else { if x == '"' as u16 { // Add n+1 backslashes to total 2n+1 before internal '"'. - cmd.extend((0..(backslashes + 1)).map(|_| '\\' as u16)); + cmd.extend((0..=backslashes).map(|_| '\\' as u16)); } backslashes = 0; } 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<impl io::Write> { + Stderr::new().ok() } diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index 85588cc6c8e..621ae2fda58 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<dyn FnBox() + 'a>) + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { let p = box p; @@ -97,5 +98,4 @@ pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option<Guard> { None } pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs index c809a0b98ac..bb2c97ea149 100644 --- a/src/libstd/sys/windows/time.rs +++ b/src/libstd/sys/windows/time.rs @@ -68,30 +68,27 @@ impl Instant { Duration::new(nanos / NANOS_PER_SEC, (nanos % NANOS_PER_SEC) as u32) } - pub fn add_duration(&self, other: &Duration) -> Instant { + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { let freq = frequency() as u64; - let t = other.as_secs().checked_mul(freq).and_then(|i| { - (self.t as u64).checked_add(i) - }).and_then(|i| { - i.checked_add(mul_div_u64(other.subsec_nanos() as u64, freq, - NANOS_PER_SEC)) - }).expect("overflow when adding duration to time"); - Instant { + let t = other.as_secs() + .checked_mul(freq)? + .checked_add(mul_div_u64(other.subsec_nanos() as u64, freq, NANOS_PER_SEC))? + .checked_add(self.t as u64)?; + Some(Instant { t: t as c::LARGE_INTEGER, - } + }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { let freq = frequency() as u64; let t = other.as_secs().checked_mul(freq).and_then(|i| { (self.t as u64).checked_sub(i) }).and_then(|i| { - i.checked_sub(mul_div_u64(other.subsec_nanos() as u64, freq, - NANOS_PER_SEC)) - }).expect("overflow when subtracting duration from time"); - Instant { + i.checked_sub(mul_div_u64(other.subsec_nanos() as u64, freq, NANOS_PER_SEC)) + })?; + Some(Instant { t: t as c::LARGE_INTEGER, - } + }) } } @@ -127,20 +124,14 @@ impl SystemTime { } } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - self.checked_add_duration(other).expect("overflow when adding duration to time") - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - checked_dur2intervals(other) - .and_then(|d| self.intervals().checked_add(d)) - .map(|i| SystemTime::from_intervals(i)) + let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?; + Some(SystemTime::from_intervals(intervals)) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - let intervals = self.intervals().checked_sub(dur2intervals(other)) - .expect("overflow when subtracting from time"); - SystemTime::from_intervals(intervals) + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + let intervals = self.intervals().checked_sub(checked_dur2intervals(other)?)?; + Some(SystemTime::from_intervals(intervals)) } } @@ -184,16 +175,12 @@ impl Hash for SystemTime { } } -fn checked_dur2intervals(d: &Duration) -> Option<i64> { - d.as_secs() - .checked_mul(INTERVALS_PER_SEC) - .and_then(|i| i.checked_add(d.subsec_nanos() as u64 / 100)) - .and_then(|i| i.try_into().ok()) -} - -fn dur2intervals(d: &Duration) -> i64 { - checked_dur2intervals(d) - .expect("overflow when converting duration to intervals") +fn checked_dur2intervals(dur: &Duration) -> Option<i64> { + dur.as_secs() + .checked_mul(INTERVALS_PER_SEC)? + .checked_add(dur.subsec_nanos() as u64 / 100)? + .try_into() + .ok() } fn intervals2dur(intervals: u64) -> Duration { |
