Auto merge of #56066 - jethrogb:jb/sgx-target, r=alexcrichton

Add SGX target to std and dependencies

This PR adds tier 3 `std` support for the `x86_64-fortanix-unknown-sgx` target.

### Background

Intel Software Guard Extensions (SGX) is an instruction set extension for x86 that allows executing code in fully-isolated *secure enclaves*. These enclaves reside in the address space of a regular user process, but access to the enclave's address space from outside (by e.g. the OS or a hypervisor) is blocked.

From within such enclaves, there is no access to the operating system or hardware peripherals. In order to communicate with the outside world, enclaves require an untrusted “helper” program that runs as a normal user process.

SGX is **not** a sandboxing technology: code inside SGX has full access to all memory belonging to the process it is running in.

### Overview

The Fortanix SGX ABI (compiler target `x86_64-fortanix-unknown-sgx`) is an interface for Intel SGX enclaves. It is a small yet functional interface suitable for writing larger enclaves. In contrast to other enclave interfaces, this interface is primarly designed for running entire applications in an enclave. The interface has been under development since early 2016 and builds on Fortanix's significant experience running enclaves in production.

Also unlike other enclave interfaces, this is the only implementation of an enclave interface that is nearly pure-Rust (except for the entry point code).

A description of the ABI may be found at https://docs.rs/fortanix-sgx-abi/ and https://github.com/fortanix/rust-sgx/blob/master/doc/FORTANIX-SGX-ABI.md.

The following parts of `std` are not supported and most operations will error when used:

* `std::fs`
* `std::process`
* `std::net::UdpSocket`

### Future plans

A separate PR (https://github.com/rust-lang/rust/pull/56067/) will add the SGX target to the rust compiler. In the very near future, I expect to upgrade this target to tier 2.

This PR is just the initial support to make things mostly work. There will be more work coming in the future, for example to add interfaces to the native SGX primitives, implement unwinding, optimize usercalls.

UDP and some form of filesystem support may be added in the future, but process support seems unlikely given the platform's constraints.

### Testing build

1. Install [Xargo](https://github.com/japaric/xargo): `cargo install xargo`
2. Create a new Cargo project, for example: `cargo new --bin sgxtest`.
3. Put the following in a file `Xargo.toml` next to your `Cargo.toml`:

```toml
[target.x86_64-fortanix-unknown-sgx.dependencies.std]
git = "https://github.com/jethrogb/rust"
branch = "jb/sgx-target"
```

NB. This can be quite slow. Instead, you can have a local checkout of that branch and use `path = "/path/to/rust/src/libstd"` instead. Don't forget to checkout the submodules too!

4. Build:

```sh
xargo build --target x86_64-fortanix-unknown-sgx
```

### Testing execution

Execution is currently only supported on x86-64 Linux, but support for Windows is planned.

1. Install pre-requisites. In order to test execution, you'll need to have a CPU with Intel SGX support. SGX support needs to be enabled in the BIOS. You'll also need to install the SGX driver and Platform Software (PSW) from [Intel](https://01.org/intel-software-guard-extensions).

2. Install toolchain, executor:
```sh
cargo install sgxs-tools --version 0.6.0-rc1
cargo install fortanix-sgx-tools --version 0.1.0-rc1
```

3. Start the enclave:

```sh
ftxsgx-elf2sgxs target/x86_64-fortanix-unknown-sgx/debug/sgxtest --heap-size 0x20000 --ssaframesize 1 --stack-size 0x20000 --threads 1 --debug
sgxs-append -i target/x86_64-fortanix-unknown-sgx/debug/sgxtest.sgxs
ftxsgx-runner target/x86_64-fortanix-unknown-sgx/debug/sgxtest.sgxs
```
This commit is contained in:
bors 2018-12-07 06:34:16 +00:00
commit 15a2607863
80 changed files with 4980 additions and 248 deletions

4
.gitmodules vendored
View File

@ -61,10 +61,12 @@
path = src/tools/clang
url = https://github.com/rust-lang-nursery/clang.git
branch = rust-release-80-v2
[submodule "src/doc/rustc-guide"]
path = src/doc/rustc-guide
url = https://github.com/rust-lang/rustc-guide.git
[submodule "src/doc/edition-guide"]
path = src/doc/edition-guide
url = https://github.com/rust-lang-nursery/edition-guide
[submodule "src/rust-sgx"]
path = src/rust-sgx
url = https://github.com/fortanix/rust-sgx

View File

@ -797,6 +797,14 @@ name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fortanix-sgx-abi"
version = "0.0.0"
dependencies = [
"compiler_builtins 0.0.0",
"core 0.0.0",
]
[[package]]
name = "fs2"
version = "0.4.3"
@ -2773,6 +2781,7 @@ dependencies = [
"compiler_builtins 0.0.0",
"core 0.0.0",
"dlmalloc 0.0.0",
"fortanix-sgx-abi 0.0.0",
"libc 0.0.0",
"panic_abort 0.0.0",
"panic_unwind 0.0.0",

View File

@ -874,6 +874,7 @@ fn run(self, builder: &Builder) -> PathBuf {
"src/rustc/compiler_builtins_shim",
"src/rustc/libc_shim",
"src/rustc/dlmalloc_shim",
"src/rustc/fortanix-sgx-abi_shim",
"src/libtest",
"src/libterm",
"src/libprofiler_builtins",

@ -1 +1 @@
Subproject commit c99638dc2ecfc750cc1656f6edb2bd062c1e0981
Subproject commit de99f4b0c886f5916cd1a146464276d65bef61b8

@ -1 +1 @@
Subproject commit fe74674f6e4be76d47b66f67d529ebf4186f4eb1
Subproject commit 10f4f35f9670bb29715a8c1ec01284852d47ed35

@ -1 +1 @@
Subproject commit c75ca6465a139704e00295be355b1f067af2f535
Subproject commit 5b403753da9ec8ff501adf34cb6d63b319b4a3ae

View File

@ -66,6 +66,12 @@ unsafe fn abort() -> ! {
unsafe fn abort() -> ! {
core::intrinsics::abort();
}
#[cfg(target_env="sgx")]
unsafe fn abort() -> ! {
extern "C" { pub fn panic_exit() -> !; }
panic_exit();
}
}
// This... is a bit of an oddity. The tl;dr; is that this is required to link

View File

@ -62,7 +62,7 @@
if #[cfg(target_os = "emscripten")] {
#[path = "emcc.rs"]
mod imp;
} else if #[cfg(target_arch = "wasm32")] {
} else if #[cfg(any(target_arch = "wasm32", target_env = "sgx"))] {
#[path = "dummy.rs"]
mod imp;
} else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {

View File

@ -35,9 +35,12 @@ rustc_lsan = { path = "../librustc_lsan" }
rustc_msan = { path = "../librustc_msan" }
rustc_tsan = { path = "../librustc_tsan" }
[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
[target.'cfg(any(all(target_arch = "wasm32", not(target_os = "emscripten")), target_env = "sgx"))'.dependencies]
dlmalloc = { path = '../rustc/dlmalloc_shim' }
[target.x86_64-fortanix-unknown-sgx.dependencies]
fortanix-sgx-abi = { path = "../rustc/fortanix-sgx-abi_shim" }
[build-dependencies]
cc = "1.0"
build_helper = { path = "../build_helper" }

View File

@ -184,7 +184,7 @@ pub enum ErrorKind {
}
impl ErrorKind {
fn as_str(&self) -> &'static str {
pub(crate) fn as_str(&self) -> &'static str {
match *self {
ErrorKind::NotFound => "entity not found",
ErrorKind::PermissionDenied => "permission denied",

View File

@ -26,6 +26,7 @@ const fn done<T>() -> *mut Arc<T> { 1_usize as *mut _ }
unsafe impl<T> Sync for Lazy<T> {}
impl<T> Lazy<T> {
#[unstable(feature = "sys_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new() -> Lazy<T> {
Lazy {
lock: Mutex::new(),

View File

@ -312,6 +312,8 @@
#![feature(non_exhaustive)]
#![feature(alloc_layout_extra)]
#![feature(maybe_uninit)]
#![cfg_attr(target_env = "sgx", feature(global_asm, range_contains, slice_index_methods,
decl_macro, coerce_unsized))]
#![default_lib_allocator]
@ -354,6 +356,12 @@
// testing gives test-std access to real-std lang items and globals. See #2912
#[cfg(test)] extern crate std as realstd;
#[cfg(target_env = "sgx")]
#[macro_use]
#[allow(unused_imports)] // FIXME: without `#[macro_use]`, get error: “cannot
// determine resolution for the macro `usercalls_asm`”
extern crate fortanix_sgx_abi;
// The standard macros that are not built-in to the compiler.
#[macro_use]
mod macros;

View File

@ -16,10 +16,11 @@
use option;
use sys::net::netc as c;
use sys_common::{FromInner, AsInner, IntoInner};
use sys_common::net::lookup_host;
use sys_common::net::LookupHost;
use vec;
use iter;
use slice;
use convert::TryInto;
/// An internet socket address, either IPv4 or IPv6.
///
@ -863,9 +864,9 @@ fn to_socket_addrs(&self) -> io::Result<option::IntoIter<SocketAddr>> {
}
}
fn resolve_socket_addr(s: &str, p: u16) -> io::Result<vec::IntoIter<SocketAddr>> {
let ips = lookup_host(s)?;
let v: Vec<_> = ips.map(|mut a| { a.set_port(p); a }).collect();
fn resolve_socket_addr(lh: LookupHost) -> io::Result<vec::IntoIter<SocketAddr>> {
let p = lh.port();
let v: Vec<_> = lh.map(|mut a| { a.set_port(p); a }).collect();
Ok(v.into_iter())
}
@ -885,7 +886,7 @@ fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
return Ok(vec![SocketAddr::V6(addr)].into_iter())
}
resolve_socket_addr(host, port)
resolve_socket_addr((host, port).try_into()?)
}
}
@ -899,22 +900,7 @@ fn to_socket_addrs(&self) -> io::Result<vec::IntoIter<SocketAddr>> {
return Ok(vec![addr].into_iter());
}
macro_rules! try_opt {
($e:expr, $msg:expr) => (
match $e {
Some(r) => r,
None => return Err(io::Error::new(io::ErrorKind::InvalidInput,
$msg)),
}
)
}
// split the string by ':' and convert the second part to u16
let mut parts_iter = self.rsplitn(2, ':');
let port_str = try_opt!(parts_iter.next(), "invalid socket address");
let host = try_opt!(parts_iter.next(), "invalid socket address");
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
resolve_socket_addr(host, port)
resolve_socket_addr(self.try_into()?)
}
}

View File

@ -112,11 +112,15 @@ fn hton<I: NetInt>(i: I) -> I { i.to_be() }
fn ntoh<I: NetInt>(i: I) -> I { I::from_be(i) }
fn each_addr<A: ToSocketAddrs, F, T>(addr: A, mut f: F) -> io::Result<T>
where F: FnMut(&SocketAddr) -> io::Result<T>
where F: FnMut(io::Result<&SocketAddr>) -> io::Result<T>
{
let addrs = match addr.to_socket_addrs() {
Ok(addrs) => addrs,
Err(e) => return f(Err(e))
};
let mut last_err = None;
for addr in addr.to_socket_addrs()? {
match f(&addr) {
for addr in addrs {
match f(Ok(&addr)) {
Ok(l) => return Ok(l),
Err(e) => last_err = Some(e),
}

View File

@ -29,7 +29,7 @@
use mem;
use ptr;
use raw;
use sys::stdio::{Stderr, stderr_prints_nothing};
use sys::stdio::panic_output;
use sys_common::rwlock::RWLock;
use sys_common::thread_info;
use sys_common::util;
@ -193,7 +193,6 @@ fn default_hook(info: &PanicInfo) {
None => "Box<Any>",
}
};
let mut err = Stderr::new().ok();
let thread = thread_info::current_thread();
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
@ -215,17 +214,14 @@ fn default_hook(info: &PanicInfo) {
}
};
let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());
match (prev, err.as_mut()) {
(Some(mut stderr), _) => {
write(&mut *stderr);
let mut s = Some(stderr);
LOCAL_STDERR.with(|slot| {
*slot.borrow_mut() = s.take();
});
}
(None, Some(ref mut err)) => { write(err) }
_ => {}
if let Some(mut local) = LOCAL_STDERR.with(|s| s.borrow_mut().take()) {
write(&mut *local);
let mut s = Some(local);
LOCAL_STDERR.with(|slot| {
*slot.borrow_mut() = s.take();
});
} else if let Some(mut out) = panic_output() {
write(&mut out);
}
}
@ -485,7 +481,7 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp,
// Some platforms know that printing to stderr won't ever actually
// print anything, and if that's the case we can skip the default
// hook.
Hook::Default if stderr_prints_nothing() => {}
Hook::Default if panic_output().is_none() => {}
Hook::Default => {
info.set_payload(payload.get());
default_hook(&info);
@ -494,7 +490,7 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp,
info.set_payload(payload.get());
(*ptr)(&info);
}
}
};
HOOK_LOCK.read_unlock();
}

View File

@ -13,13 +13,14 @@
use net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use time::Duration;
use sys::{unsupported, Void};
use convert::TryFrom;
pub extern crate libc as netc;
pub struct TcpStream(Void);
impl TcpStream {
pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> {
pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
unsupported()
}
@ -105,7 +106,7 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
pub struct TcpListener(Void);
impl TcpListener {
pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
unsupported()
}
@ -155,7 +156,7 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
pub struct UdpSocket(Void);
impl UdpSocket {
pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
unsupported()
}
@ -271,7 +272,7 @@ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn connect(&self, _: &SocketAddr) -> io::Result<()> {
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
match self.0 {}
}
}
@ -284,6 +285,12 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
pub struct LookupHost(Void);
impl LookupHost {
pub fn port(&self) -> u16 {
match self.0 {}
}
}
impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
@ -291,6 +298,18 @@ fn next(&mut self) -> Option<SocketAddr> {
}
}
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()
}
}

View File

@ -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()
}

View File

@ -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();

View File

@ -48,6 +48,9 @@
} else if #[cfg(target_arch = "wasm32")] {
mod wasm;
pub use self::wasm::*;
} else if #[cfg(target_env = "sgx")] {
mod sgx;
pub use self::sgx::*;
} else {
compile_error!("libstd doesn't compile for this platform yet");
}

View File

@ -9,7 +9,7 @@
// except according to those terms.
use fs::File;
use io::{Error, Result, Read};
use io::{Error, Read, self};
use iter::Iterator;
use net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use str::FromStr;
@ -17,6 +17,7 @@
use sys::syscall::EINVAL;
use time::{self, Duration};
use vec::{IntoIter, Vec};
use convert::{TryFrom, TryInto};
use self::dns::{Dns, DnsQuery};
@ -29,7 +30,13 @@
mod tcp;
mod udp;
pub struct LookupHost(IntoIter<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 @@ fn next(&mut self) -> Option<Self::Item> {
}
}
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();
impl<'a> TryFrom<&'a str> for LookupHost {
type Error = io::Error;
let mut dns_string = String::new();
File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?;
let dns: Vec<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)));
}
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))
}
}

View File

@ -24,8 +24,8 @@
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 @@ fn into_inner(self) -> File { self.0 }
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);

View File

@ -25,8 +25,8 @@
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 @@ fn get_conn(&self) -> &mut Option<SocketAddr> {
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(())
}

View File

@ -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()
}

View File

@ -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))?;

View File

@ -0,0 +1,327 @@
/* Copyright 2018 The Rust Project Developers. See the COPYRIGHT */
/* file at the top-level directory of this distribution and at */
/* http://rust-lang.org/COPYRIGHT. */
/* */
/* Licensed under the Apache License, Version 2.0 <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
.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

View File

@ -0,0 +1,49 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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_enclave_range(p: *const u8, len: usize) -> bool {
let start=p as u64;
let end=start + (len as u64);
start >= image_base() &&
end <= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant
}
pub fn is_user_range(p: *const u8, len: usize) -> bool {
let start=p as u64;
let end=start + (len as u64);
end <= image_base() ||
start >= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant
}

View File

@ -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);
}

View File

@ -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() -> !; }

View File

@ -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) };
}
}

View File

@ -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() }
}

View File

@ -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));
}
}
}

View File

@ -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()))
}
}
}

View File

@ -0,0 +1,197 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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 read_alloc(fd: Fd) -> IoResult<Vec<u8>> {
unsafe {
let mut userbuf = alloc::User::<ByteBuffer>::uninitialized();
raw::read_alloc(fd, userbuf.as_raw_mut_ptr()).from_sgx_result()?;
Ok(copy_user_buffer(&userbuf))
}
}
pub fn write(fd: Fd, buf: &[u8]) -> IoResult<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)))
}
}
}

View File

@ -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);

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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()
}

View File

@ -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;
}

View File

@ -0,0 +1,52 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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 {
#[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new() -> Condvar {
Condvar { inner: SpinMutex::new(WaitVariable::new(())) }
}
#[inline]
pub unsafe fn init(&mut self) {}
#[inline]
pub unsafe fn notify_one(&self) {
let _ = WaitQueue::notify_one(self.inner.lock());
}
#[inline]
pub unsafe fn notify_all(&self) {
let _ = WaitQueue::notify_all(self.inner.lock());
}
pub unsafe fn wait(&self, mutex: &Mutex) {
let guard = self.inner.lock();
mutex.unlock();
WaitQueue::wait(guard);
mutex.lock()
}
pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
panic!("timeout not supported in SGX");
}
#[inline]
pub unsafe fn destroy(&self) {}
}

19
src/libstd/sys/sgx/env.rs Normal file
View File

@ -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";
}

58
src/libstd/sys/sgx/fd.rs Normal file
View File

@ -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)
}
}

304
src/libstd/sys/sgx/fs.rs Normal file
View File

@ -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()
}

View File

@ -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};

153
src/libstd/sys/sgx/mod.rs Normal file
View File

@ -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())
}

152
src/libstd/sys/sgx/mutex.rs Normal file
View File

@ -0,0 +1,152 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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 {
#[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new() -> Mutex {
Mutex { inner: SpinMutex::new(WaitVariable::new(false)) }
}
#[inline]
pub unsafe fn init(&mut self) {}
#[inline]
pub unsafe fn lock(&self) {
let mut guard = self.inner.lock();
if *guard.lock_var() {
// Another thread has the lock, wait
WaitQueue::wait(guard)
// Another thread has passed the lock to us
} else {
// We are just now obtaining the lock
*guard.lock_var_mut() = true;
}
}
#[inline]
pub unsafe fn unlock(&self) {
let guard = self.inner.lock();
if let Err(mut guard) = WaitQueue::notify_one(guard) {
// No other waiters, unlock
*guard.lock_var_mut() = false;
} else {
// There was a thread waiting, just pass the lock
}
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
let mut guard = try_lock_or_false!(self.inner);
if *guard.lock_var() {
// Another thread has the lock
false
} else {
// We are just now obtaining the lock
*guard.lock_var_mut() = true;
true
}
}
#[inline]
pub unsafe fn destroy(&self) {}
}
struct ReentrantLock {
owner: Option<Tcs>,
count: usize
}
pub struct ReentrantMutex {
inner: SpinMutex<WaitVariable<ReentrantLock>>,
}
impl ReentrantMutex {
#[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn
pub const fn uninitialized() -> ReentrantMutex {
ReentrantMutex {
inner: SpinMutex::new(WaitVariable::new(ReentrantLock { owner: None, count: 0 }))
}
}
#[inline]
pub unsafe fn init(&mut self) {}
#[inline]
pub unsafe fn lock(&self) {
let mut guard = self.inner.lock();
match guard.lock_var().owner {
Some(tcs) if tcs != thread::current() => {
// Another thread has the lock, wait
WaitQueue::wait(guard);
// Another thread has passed the lock to us
},
_ => {
// We are just now obtaining the lock
guard.lock_var_mut().owner = Some(thread::current());
guard.lock_var_mut().count += 1;
},
}
}
#[inline]
pub unsafe fn unlock(&self) {
let mut guard = self.inner.lock();
if guard.lock_var().count > 1 {
guard.lock_var_mut().count -= 1;
} else {
match WaitQueue::notify_one(guard) {
Err(mut guard) => {
// No other waiters, unlock
guard.lock_var_mut().count = 0;
guard.lock_var_mut().owner = None;
},
Ok(mut guard) => {
// There was a thread waiting, just pass the lock
if let NotifiedTcs::Single(tcs) = guard.notified_tcs() {
guard.lock_var_mut().owner = Some(tcs)
} else {
unreachable!() // called notify_one
}
}
}
}
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
let mut guard = try_lock_or_false!(self.inner);
match guard.lock_var().owner {
Some(tcs) if tcs != thread::current() => {
// Another thread has the lock
false
},
_ => {
// We are just now obtaining the lock
guard.lock_var_mut().owner = Some(thread::current());
guard.lock_var_mut().count += 1;
true
},
}
}
#[inline]
pub unsafe fn destroy(&self) {}
}

415
src/libstd/sys/sgx/net.rs Normal file
View File

@ -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;
}

147
src/libstd/sys/sgx/os.rs Normal file
View File

@ -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")
}

View File

@ -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) }
}
}

View File

@ -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 = '/';

View File

@ -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 {}
}

View File

@ -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 {}
}
}

View File

@ -0,0 +1,134 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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 super::waitqueue::{WaitVariable, WaitQueue, SpinMutex, NotifiedTcs, try_lock_or_false};
pub struct RWLock {
readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
writer: SpinMutex<WaitVariable<bool>>,
}
//unsafe impl Send for RWLock {}
//unsafe impl Sync for RWLock {} // FIXME
impl RWLock {
#[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new() -> RWLock {
RWLock {
readers: SpinMutex::new(WaitVariable::new(None)),
writer: SpinMutex::new(WaitVariable::new(false))
}
}
#[inline]
pub unsafe fn read(&self) {
let mut rguard = self.readers.lock();
let wguard = self.writer.lock();
if *wguard.lock_var() || !wguard.queue_empty() {
// Another thread has or is waiting for the write lock, wait
drop(wguard);
WaitQueue::wait(rguard);
// Another thread has passed the lock to us
} else {
// No waiting writers, acquire the read lock
*rguard.lock_var_mut() =
NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
}
}
#[inline]
pub unsafe fn try_read(&self) -> bool {
let mut rguard = try_lock_or_false!(self.readers);
let wguard = try_lock_or_false!(self.writer);
if *wguard.lock_var() || !wguard.queue_empty() {
// Another thread has or is waiting for the write lock
false
} else {
// No waiting writers, acquire the read lock
*rguard.lock_var_mut() =
NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1);
true
}
}
#[inline]
pub unsafe fn write(&self) {
let rguard = self.readers.lock();
let mut wguard = self.writer.lock();
if *wguard.lock_var() || rguard.lock_var().is_some() {
// Another thread has the lock, wait
drop(rguard);
WaitQueue::wait(wguard);
// Another thread has passed the lock to us
} else {
// We are just now obtaining the lock
*wguard.lock_var_mut() = true;
}
}
#[inline]
pub unsafe fn try_write(&self) -> bool {
let rguard = try_lock_or_false!(self.readers);
let mut wguard = try_lock_or_false!(self.writer);
if *wguard.lock_var() || rguard.lock_var().is_some() {
// Another thread has the lock
false
} else {
// We are just now obtaining the lock
*wguard.lock_var_mut() = true;
true
}
}
#[inline]
pub unsafe fn read_unlock(&self) {
let mut rguard = self.readers.lock();
let wguard = self.writer.lock();
*rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1);
if rguard.lock_var().is_some() {
// There are other active readers
} else {
if let Ok(mut wguard) = WaitQueue::notify_one(wguard) {
// A writer was waiting, pass the lock
*wguard.lock_var_mut() = true;
} else {
// No writers were waiting, the lock is released
assert!(rguard.queue_empty());
}
}
}
#[inline]
pub unsafe fn write_unlock(&self) {
let rguard = self.readers.lock();
let wguard = self.writer.lock();
if let Err(mut wguard) = WaitQueue::notify_one(wguard) {
// No writers waiting, release the write lock
*wguard.lock_var_mut() = false;
if let Ok(mut rguard) = WaitQueue::notify_all(rguard) {
// One or more readers were waiting, pass the lock to them
if let NotifiedTcs::All { count } = rguard.notified_tcs() {
*rguard.lock_var_mut() = Some(count)
} else {
unreachable!() // called notify_all
}
} else {
// No readers waiting, the lock is released
}
} else {
// There was a thread waiting for write, just pass the lock
}
}
#[inline]
pub unsafe fn destroy(&self) {}
}

View File

@ -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() {
}

View File

@ -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()
}

View File

@ -0,0 +1,101 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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 }
pub unsafe fn deinit() {}
}

View File

@ -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
}

View File

@ -0,0 +1,61 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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 add_duration(&self, other: &Duration) -> Instant {
Instant(self.0 + *other)
}
pub fn sub_duration(&self, other: &Duration) -> Instant {
Instant(self.0 - *other)
}
}
impl SystemTime {
pub fn now() -> SystemTime {
SystemTime(usercalls::insecure_time())
}
pub fn sub_time(&self, other: &SystemTime)
-> Result<Duration, Duration> {
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))
}
pub fn sub_duration(&self, other: &Duration) -> SystemTime {
SystemTime(self.0 - *other)
}
}

View File

@ -0,0 +1,552 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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> {
#[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new(var: T) -> Self {
WaitVariable {
queue: WaitQueue::new(),
lock: var
}
}
pub fn queue_empty(&self) -> bool {
self.queue.is_empty()
}
pub fn lock_var(&self) -> &T {
&self.lock
}
pub fn lock_var_mut(&mut self) -> &mut T {
&mut self.lock
}
}
#[derive(Copy, Clone)]
pub enum NotifiedTcs {
Single(Tcs),
All { count: NonZeroUsize }
}
/// An RAII guard that will notify a set of target threads as well as unlock
/// a mutex on drop.
pub struct WaitGuard<'a, T: 'a> {
mutex_guard: Option<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 {
#[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new() -> Self {
WaitQueue {
inner: UnsafeList::new()
}
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
/// Add the calling thread to the WaitVariable's wait queue, then wait
/// until a wakeup event.
///
/// This function does not return until this thread has been awoken.
pub fn wait<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> {
#[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new() -> Self {
unsafe {
UnsafeList {
head_tail: NonNull::new_unchecked(1 as _),
head_tail_entry: None
}
}
}
unsafe fn init(&mut self) {
if self.head_tail_entry.is_none() {
self.head_tail_entry = Some(UnsafeListEntry::dummy());
self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap());
self.head_tail.as_mut().next = self.head_tail;
self.head_tail.as_mut().prev = self.head_tail;
}
}
pub fn is_empty(&self) -> bool {
unsafe {
if self.head_tail_entry.is_some() {
let first = self.head_tail.as_ref().next;
if first == self.head_tail {
// ,-------> /---------\ next ---,
// | |head_tail| |
// `--- prev \---------/ <-------`
assert_eq!(self.head_tail.as_ref().prev, first);
true
} else {
false
}
} else {
true
}
}
}
/// Pushes an entry onto the back of the list.
///
/// # Safety
///
/// The entry must remain allocated until the entry is removed from the
/// list AND the caller who popped is done using the entry.
pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry<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();
}
}

View File

@ -21,7 +21,7 @@ pub mod net {
use sys_common::{AsInner, FromInner, IntoInner};
use sys::fd::FileDesc;
use time::Duration;
use convert::TryFrom;
pub extern crate libc as netc;
@ -118,7 +118,7 @@ pub struct TcpStream {
}
impl TcpStream {
pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> {
pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
unimpl!();
}
@ -216,7 +216,7 @@ pub struct TcpListener {
}
impl TcpListener {
pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
unimpl!();
}
@ -278,7 +278,7 @@ pub struct UdpSocket {
}
impl UdpSocket {
pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
unimpl!();
}
@ -402,7 +402,7 @@ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
unimpl!();
}
pub fn connect(&self, _: &SocketAddr) -> io::Result<()> {
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
unimpl!();
}
}
@ -431,11 +431,30 @@ fn next(&mut self) -> Option<SocketAddr> {
}
}
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!();
}
}
}

View File

@ -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()
}

View File

@ -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();

View File

@ -8,85 +8,32 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[inline]
pub unsafe fn cbrtf(n: f32) -> f32 {
f64::cbrt(n as f64) as f32
}
#[inline]
pub unsafe fn expm1f(n: f32) -> f32 {
f64::exp_m1(n as f64) as f32
}
#[inline]
#[allow(deprecated)]
pub unsafe fn fdimf(a: f32, b: f32) -> f32 {
f64::abs_sub(a as f64, b as f64) as f32
}
#[inline]
pub unsafe fn log1pf(n: f32) -> f32 {
f64::ln_1p(n as f64) as f32
}
#[inline]
pub unsafe fn hypotf(x: f32, y: f32) -> f32 {
f64::hypot(x as f64, y as f64) as f32
}
#[inline]
pub unsafe fn acosf(n: f32) -> f32 {
f64::acos(n as f64) as f32
}
#[inline]
pub unsafe fn asinf(n: f32) -> f32 {
f64::asin(n as f64) as f32
}
#[inline]
pub unsafe fn atan2f(n: f32, b: f32) -> f32 {
f64::atan2(n as f64, b as f64) as f32
}
#[inline]
pub unsafe fn atanf(n: f32) -> f32 {
f64::atan(n as f64) as f32
}
#[inline]
pub unsafe fn coshf(n: f32) -> f32 {
f64::cosh(n as f64) as f32
}
#[inline]
pub unsafe fn sinhf(n: f32) -> f32 {
f64::sinh(n as f64) as f32
}
#[inline]
pub unsafe fn tanf(n: f32) -> f32 {
f64::tan(n as f64) as f32
}
#[inline]
pub unsafe fn tanhf(n: f32) -> f32 {
f64::tanh(n as f64) as f32
}
// These symbols are all defined in `compiler-builtins`
extern {
pub fn acos(n: f64) -> f64;
pub fn acosf(n: f32) -> f32;
pub fn asin(n: f64) -> f64;
pub fn asinf(n: f32) -> f32;
pub fn atan(n: f64) -> f64;
pub fn atan2(a: f64, b: f64) -> f64;
pub fn atan2f(a: f32, b: f32) -> f32;
pub fn atanf(n: f32) -> f32;
pub fn cbrt(n: f64) -> f64;
pub fn cbrtf(n: f32) -> f32;
pub fn cosh(n: f64) -> f64;
pub fn coshf(n: f32) -> f32;
pub fn expm1(n: f64) -> f64;
pub fn expm1f(n: f32) -> f32;
pub fn fdim(a: f64, b: f64) -> f64;
pub fn log1p(n: f64) -> f64;
pub fn sinh(n: f64) -> f64;
pub fn tan(n: f64) -> f64;
pub fn tanh(n: f64) -> f64;
pub fn fdimf(a: f32, b: f32) -> f32;
pub fn hypot(x: f64, y: f64) -> f64;
pub fn hypotf(x: f32, y: f32) -> f32;
pub fn log1p(n: f64) -> f64;
pub fn log1pf(n: f32) -> f32;
pub fn sinh(n: f64) -> f64;
pub fn sinhf(n: f32) -> f32;
pub fn tan(n: f64) -> f64;
pub fn tanf(n: f32) -> f32;
pub fn tanh(n: f64) -> f64;
pub fn tanhf(n: f32) -> f32;
}

View File

@ -13,11 +13,12 @@
use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr};
use time::Duration;
use sys::{unsupported, Void};
use convert::TryFrom;
pub struct TcpStream(Void);
impl TcpStream {
pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> {
pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
unsupported()
}
@ -103,7 +104,7 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
pub struct TcpListener(Void);
impl TcpListener {
pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
unsupported()
}
@ -153,7 +154,7 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
pub struct UdpSocket(Void);
impl UdpSocket {
pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
unsupported()
}
@ -273,7 +274,7 @@ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
match self.0 {}
}
pub fn connect(&self, _: &SocketAddr) -> io::Result<()> {
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
match self.0 {}
}
}
@ -286,6 +287,12 @@ fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
pub struct LookupHost(Void);
impl LookupHost {
pub fn port(&self) -> u16 {
match self.0 {}
}
}
impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
@ -293,8 +300,20 @@ fn next(&mut self) -> Option<SocketAddr> {
}
}
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)]

View File

@ -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
}
}

View File

@ -19,7 +19,8 @@
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()

View File

@ -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()
}

View File

@ -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;

View File

@ -25,6 +25,7 @@ impl Condvar {
///
/// Behavior is undefined if the condition variable is moved after it is
/// first used with any of the functions below.
#[unstable(feature = "sys_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new() -> Condvar { Condvar(imp::Condvar::new()) }
/// Prepares the condition variable for use.

View File

@ -57,9 +57,11 @@ macro_rules! rtassert {
pub mod process;
cfg_if! {
if #[cfg(any(target_os = "cloudabi", target_os = "l4re", target_os = "redox"))] {
pub use sys::net;
} else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] {
if #[cfg(any(target_os = "cloudabi",
target_os = "l4re",
target_os = "redox",
all(target_arch = "wasm32", not(target_os = "emscripten")),
target_env = "sgx"))] {
pub use sys::net;
} else {
pub mod net;

View File

@ -27,6 +27,7 @@ impl Mutex {
/// Also, until `init` is called, behavior is undefined if this
/// mutex is ever used reentrantly, i.e., `raw_lock` or `try_lock`
/// are called by the thread currently holding the lock.
#[unstable(feature = "sys_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new() -> Mutex { Mutex(imp::Mutex::new()) }
/// Prepare the mutex for use.

View File

@ -20,6 +20,7 @@
use sys::net::netc as c;
use sys_common::{AsInner, FromInner, IntoInner};
use time::Duration;
use convert::{TryFrom, TryInto};
#[cfg(any(target_os = "dragonfly", target_os = "freebsd",
target_os = "ios", target_os = "macos",
@ -129,6 +130,13 @@ fn to_ipv6mr_interface(value: u32) -> ::libc::c_uint {
pub struct LookupHost {
original: *mut c::addrinfo,
cur: *mut c::addrinfo,
port: u16
}
impl LookupHost {
pub fn port(&self) -> u16 {
self.port
}
}
impl Iterator for LookupHost {
@ -158,17 +166,45 @@ fn drop(&mut self) {
}
}
pub fn lookup_host(host: &str) -> io::Result<LookupHost> {
init();
impl<'a> TryFrom<&'a str> for LookupHost {
type Error = io::Error;
let c_host = CString::new(host)?;
let mut hints: c::addrinfo = unsafe { mem::zeroed() };
hints.ai_socktype = c::SOCK_STREAM;
let mut res = ptr::null_mut();
unsafe {
cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)).map(|_| {
LookupHost { original: res, cur: res }
})
fn try_from(s: &str) -> io::Result<LookupHost> {
macro_rules! try_opt {
($e:expr, $msg:expr) => (
match $e {
Some(r) => r,
None => return Err(io::Error::new(io::ErrorKind::InvalidInput,
$msg)),
}
)
}
// split the string by ':' and convert the second part to u16
let mut parts_iter = s.rsplitn(2, ':');
let port_str = try_opt!(parts_iter.next(), "invalid socket address");
let host = try_opt!(parts_iter.next(), "invalid socket address");
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
(host, port).try_into()
}
}
impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
type Error = io::Error;
fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> {
init();
let c_host = CString::new(host)?;
let mut hints: c::addrinfo = unsafe { mem::zeroed() };
hints.ai_socktype = c::SOCK_STREAM;
let mut res = ptr::null_mut();
unsafe {
cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)).map(|_| {
LookupHost { original: res, cur: res, port }
})
}
}
}
@ -181,7 +217,9 @@ pub struct TcpStream {
}
impl TcpStream {
pub fn connect(addr: &SocketAddr) -> io::Result<TcpStream> {
pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
let addr = addr?;
init();
let sock = Socket::new(addr, c::SOCK_STREAM)?;
@ -317,7 +355,9 @@ pub struct TcpListener {
}
impl TcpListener {
pub fn bind(addr: &SocketAddr) -> io::Result<TcpListener> {
pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
let addr = addr?;
init();
let sock = Socket::new(addr, c::SOCK_STREAM)?;
@ -418,7 +458,9 @@ pub struct UdpSocket {
}
impl UdpSocket {
pub fn bind(addr: &SocketAddr) -> io::Result<UdpSocket> {
pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
let addr = addr?;
init();
let sock = Socket::new(addr, c::SOCK_DGRAM)?;
@ -584,8 +626,8 @@ pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
Ok(ret as usize)
}
pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
let (addrp, len) = addr.into_inner();
pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> {
let (addrp, len) = addr?.into_inner();
cvt_r(|| unsafe { c::connect(*self.inner.as_inner(), addrp, len) }).map(|_| ())
}
}
@ -618,7 +660,7 @@ mod tests {
#[test]
fn no_lookup_host_duplicates() {
let mut addrs = HashMap::new();
let lh = match lookup_host("localhost") {
let lh = match LookupHost::try_from(("localhost", 0)) {
Ok(lh) => lh,
Err(e) => panic!("couldn't resolve `localhost': {}", e)
};

View File

@ -22,6 +22,7 @@ impl RWLock {
///
/// Behavior is undefined if the reader-writer lock is moved after it is
/// first used with any of the functions below.
#[unstable(feature = "sys_internals", issue = "0")] // FIXME: min_const_fn
pub const fn new() -> RWLock { RWLock(imp::RWLock::new()) }
/// Acquires shared access to the underlying lock, blocking the current

View File

@ -10,14 +10,13 @@
use fmt;
use io::prelude::*;
use sys::stdio::{Stderr, stderr_prints_nothing};
use sys::stdio::panic_output;
use thread;
pub fn dumb_print(args: fmt::Arguments) {
if stderr_prints_nothing() {
return
if let Some(mut out) = panic_output() {
let _ = out.write_fmt(args);
}
let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args));
}
// Other platforms should use the appropriate platform-specific mechanism for

View File

@ -167,10 +167,12 @@
#![stable(feature = "rust1", since = "1.0.0")]
use any::Any;
use boxed::FnBox;
use cell::UnsafeCell;
use ffi::{CStr, CString};
use fmt;
use io;
use mem;
use panic;
use panicking;
use str;
@ -452,8 +454,8 @@ pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>> where
/// [`io::Result`]: ../../std/io/type.Result.html
/// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html
#[unstable(feature = "thread_spawn_unchecked", issue = "55132")]
pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>> where
F: FnOnce() -> T, F: Send, T: Send
pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result<JoinHandle<T>> where
F: FnOnce() -> T, F: Send + 'a, T: Send + 'a
{
let Builder { name, stack_size } = self;
@ -482,7 +484,21 @@ pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>> whe
};
Ok(JoinHandle(JoinInner {
native: Some(imp::Thread::new(stack_size, Box::new(main))?),
// `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed
// through FFI or otherwise used with low-level threading primitives that have no
// notion of or way to enforce lifetimes.
//
// As mentioned in the `Safety` section of this function's documentation, the caller of
// this function needs to guarantee that the passed-in lifetime is sufficiently long
// for the lifetime of the thread.
//
// Similarly, the `sys` implementation must guarantee that no references to the closure
// exist after the thread has terminated, which is signaled by `Thread::join`
// returning.
native: Some(imp::Thread::new(
stack_size,
mem::transmute::<Box<dyn FnBox() + 'a>, Box<dyn FnBox() + 'static>>(Box::new(main))
)?),
thread: my_thread,
packet: Packet(my_packet),
}))

View File

@ -1018,10 +1018,12 @@ fn use_color(opts: &TestOpts) -> bool {
}
}
#[cfg(any(target_os = "cloudabi", target_os = "redox",
all(target_arch = "wasm32", not(target_os = "emscripten"))))]
#[cfg(any(target_os = "cloudabi",
target_os = "redox",
all(target_arch = "wasm32", not(target_os = "emscripten")),
target_env = "sgx"))]
fn stdout_isatty() -> bool {
// FIXME: Implement isatty on Redox
// FIXME: Implement isatty on Redox and SGX
false
}
#[cfg(unix)]
@ -1246,7 +1248,7 @@ fn num_cpus() -> usize {
1
}
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
#[cfg(any(all(target_arch = "wasm32", not(target_os = "emscripten")), target_env = "sgx"))]
fn num_cpus() -> usize {
1
}

View File

@ -26,7 +26,10 @@
cfg_if! {
if #[cfg(target_env = "msvc")] {
// no extra unwinder support needed
} else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] {
} else if #[cfg(any(
all(target_arch = "wasm32", not(target_os = "emscripten")),
target_env = "sgx"
))] {
// no unwinder on the system!
} else {
extern crate libc;

1
src/rust-sgx Submodule

@ -0,0 +1 @@
Subproject commit 9656260888095f44830641ca7bb3da609a793451

View File

@ -0,0 +1,14 @@
[package]
name = "fortanix-sgx-abi"
version = "0.0.0"
authors = ["The Rust Project Developers"]
[lib]
path = "../../rust-sgx/fortanix-sgx-abi/src/lib.rs"
test = false
bench = false
doc = false
[dependencies]
core = { path = "../../libcore" }
compiler_builtins = { path = "../../rustc/compiler_builtins_shim" }

@ -1 +1 @@
Subproject commit 0309be1ade6bf61066f2c69f77ac3567b7dc31b5
Subproject commit 5e628c5120c619a22799187371f057ec41e06f87

View File

@ -76,6 +76,7 @@ fn filter_dirs(path: &Path) -> bool {
"src/tools/lldb",
"src/target",
"src/stdsimd",
"src/rust-sgx",
"target",
"vendor",
];