Auto merge of #57119 - jethrogb:jb/sgx-os-mod2, r=joshtriplett

Add `io` and `arch` modules to `std::os::fortanix_sgx`

This PR adds two more (unstable) modules to `std::os::fortanix_sgx` for the `x86_64-fortanix-unknown-sgx` target.

### io
`io` allows conversion between raw file descriptors and Rust types, similar to `std::os::unix::io`.

### arch
`arch` exposes the `ENCLU[EREPORT]` and `ENCLU[EGETKEY]` instructions. The current functions are very likely not going to be the final form of these functions (see also https://github.com/fortanix/rust-sgx/issues/15), but this should be sufficient to enable experimentation in libraries. I tried using the actual types (from the [`sgx-isa` crate](https://crates.io/crates/sgx-isa)) instead of byte arrays, but that would make `std` dependent on the `bitflags` crate which I didn't want to do at this time.
This commit is contained in:
bors 2018-12-27 09:21:06 +00:00
commit d2986970ad
8 changed files with 307 additions and 11 deletions

View File

@ -304,7 +304,8 @@
#![feature(alloc_layout_extra)]
#![feature(maybe_uninit)]
#![cfg_attr(target_env = "sgx", feature(global_asm, range_contains, slice_index_methods,
decl_macro, coerce_unsized, sgx_platform))]
decl_macro, coerce_unsized, sgx_platform,
min_const_unsafe_fn))]
#![default_lib_allocator]

View File

@ -55,3 +55,5 @@ pub mod usercalls {
pub mod mem {
pub use sys::abi::mem::*;
}
pub use sys::ext::{io, arch};

View File

@ -0,0 +1,84 @@
// 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.
//! SGX-specific access to architectural features.
//!
//! The functionality in this module is further documented in the Intel
//! Software Developer's Manual, Volume 3, Chapter 40.
#![unstable(feature = "sgx_platform", issue = "56975")]
use mem::MaybeUninit;
/// Wrapper struct to force 16-byte alignment.
#[repr(align(16))]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct Align16<T>(pub T);
/// Wrapper struct to force 128-byte alignment.
#[repr(align(128))]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct Align128<T>(pub T);
/// Wrapper struct to force 512-byte alignment.
#[repr(align(512))]
#[unstable(feature = "sgx_platform", issue = "56975")]
pub struct Align512<T>(pub T);
const ENCLU_EREPORT: u32 = 0;
const ENCLU_EGETKEY: u32 = 1;
/// Call the `EGETKEY` instruction to obtain a 128-bit secret key.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn egetkey(request: &Align512<[u8; 512]>) -> Result<Align16<[u8; 16]>, u32> {
unsafe {
let mut out = MaybeUninit::uninitialized();
let error;
asm!(
"enclu"
: "={eax}"(error)
: "{eax}"(ENCLU_EGETKEY),
"{rbx}"(request),
"{rcx}"(out.get_mut())
: "flags"
);
match error {
0 => Ok(out.into_inner()),
err => Err(err),
}
}
}
/// Call the `EREPORT` instruction.
///
/// This creates a cryptographic report describing the contents of the current
/// enclave. The report may be verified by the enclave described in
/// `targetinfo`.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn ereport(
targetinfo: &Align512<[u8; 512]>,
reportdata: &Align128<[u8; 64]>,
) -> Align512<[u8; 432]> {
unsafe {
let mut report = MaybeUninit::uninitialized();
asm!(
"enclu"
: /* no output registers */
: "{eax}"(ENCLU_EREPORT),
"{rbx}"(targetinfo),
"{rcx}"(reportdata),
"{rdx}"(report.get_mut())
);
report.into_inner()
}
}

View File

@ -0,0 +1,119 @@
// 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.
//! SGX-specific extensions to general I/O primitives
//!
//! SGX file descriptors behave differently from Unix file descriptors. See the
//! description of [`TryIntoRawFd`](trait.TryIntoRawFd.html) for more details.
#![unstable(feature = "sgx_platform", issue = "56975")]
pub use sys::abi::usercalls::raw::Fd as RawFd;
use net;
use sys::{self, AsInner, FromInner, IntoInner, TryIntoInner};
/// A trait to extract the raw SGX file descriptor from an underlying
/// object.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub trait AsRawFd {
/// Extracts the raw file descriptor.
///
/// This method does **not** pass ownership of the raw file descriptor
/// to the caller. The descriptor is only guaranteed to be valid while
/// the original object has not yet been destroyed.
#[unstable(feature = "sgx_platform", issue = "56975")]
fn as_raw_fd(&self) -> RawFd;
}
/// A trait to express the ability to construct an object from a raw file
/// descriptor.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub trait FromRawFd {
/// Constructs a new instance of `Self` from the given raw file
/// descriptor.
///
/// This function **consumes ownership** of the specified file
/// descriptor. The returned object will take responsibility for closing
/// it when the object goes out of scope.
///
/// This function is also unsafe as the primitives currently returned
/// have the contract that they are the sole owner of the file
/// descriptor they are wrapping. Usage of this function could
/// accidentally allow violating this contract which can cause memory
/// unsafety in code that relies on it being true.
#[unstable(feature = "sgx_platform", issue = "56975")]
unsafe fn from_raw_fd(fd: RawFd) -> Self;
}
/// A trait to express the ability to consume an object and acquire ownership of
/// its raw file descriptor.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub trait TryIntoRawFd: Sized {
/// Consumes this object, returning the raw underlying file descriptor, if
/// this object is not cloned.
///
/// This function **transfers ownership** of the underlying file descriptor
/// to the caller. Callers are then the unique owners of the file descriptor
/// and must close the descriptor once it's no longer needed.
///
/// Unlike other platforms, on SGX, the file descriptor is shared between
/// all clones of an object. To avoid race conditions, this function will
/// only return `Ok` when called on the final clone.
#[unstable(feature = "sgx_platform", issue = "56975")]
fn try_into_raw_fd(self) -> Result<RawFd, Self>;
}
impl AsRawFd for net::TcpStream {
fn as_raw_fd(&self) -> RawFd { *self.as_inner().as_inner().as_inner().as_inner() }
}
impl AsRawFd for net::TcpListener {
fn as_raw_fd(&self) -> RawFd { *self.as_inner().as_inner().as_inner().as_inner() }
}
impl FromRawFd for net::TcpStream {
unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream {
let fd = sys::fd::FileDesc::from_inner(fd);
let socket = sys::net::Socket::from_inner(fd);
net::TcpStream::from_inner(sys::net::TcpStream::from_inner((socket, None)))
}
}
impl FromRawFd for net::TcpListener {
unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener {
let fd = sys::fd::FileDesc::from_inner(fd);
let socket = sys::net::Socket::from_inner(fd);
net::TcpListener::from_inner(sys::net::TcpListener::from_inner(socket))
}
}
impl TryIntoRawFd for net::TcpStream {
fn try_into_raw_fd(self) -> Result<RawFd, Self> {
let (socket, peer_addr) = self.into_inner().into_inner();
match socket.try_into_inner() {
Ok(fd) => Ok(fd.into_inner()),
Err(socket) => {
let sys = sys::net::TcpStream::from_inner((socket, peer_addr));
Err(net::TcpStream::from_inner(sys))
}
}
}
}
impl TryIntoRawFd for net::TcpListener {
fn try_into_raw_fd(self) -> Result<RawFd, Self> {
match self.into_inner().into_inner().try_into_inner() {
Ok(fd) => Ok(fd.into_inner()),
Err(socket) => {
let sys = sys::net::TcpListener::from_inner(socket);
Err(net::TcpListener::from_inner(sys))
}
}
}
}

View File

@ -0,0 +1,14 @@
// 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.
#![unstable(feature = "sgx_platform", issue = "56975")]
pub mod arch;
pub mod io;

View File

@ -2,7 +2,7 @@ use fortanix_sgx_abi::Fd;
use io;
use mem;
use sys_common::AsInner;
use sys::{AsInner, FromInner, IntoInner};
use super::abi::usercalls;
#[derive(Debug)]
@ -41,6 +41,20 @@ impl AsInner<Fd> for FileDesc {
fn as_inner(&self) -> &Fd { &self.fd }
}
impl IntoInner<Fd> for FileDesc {
fn into_inner(self) -> Fd {
let fd = self.fd;
mem::forget(self);
fd
}
}
impl FromInner<Fd> for FileDesc {
fn from_inner(fd: Fd) -> FileDesc {
FileDesc { fd }
}
}
impl Drop for FileDesc {
fn drop(&mut self) {
usercalls::close(self.fd)

View File

@ -17,6 +17,7 @@ pub mod backtrace;
pub mod cmath;
pub mod condvar;
pub mod env;
pub mod ext;
pub mod fd;
pub mod fs;
pub mod memchr;
@ -141,3 +142,9 @@ pub fn hashmap_random_keys() -> (u64, u64) {
}
(rdrand64(), rdrand64())
}
pub use sys_common::{AsInner, FromInner, IntoInner};
pub trait TryIntoInner<Inner>: Sized {
fn try_into_inner(self) -> Result<Inner, Self>;
}

View File

@ -2,7 +2,7 @@ use fmt;
use io;
use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr, ToSocketAddrs};
use time::Duration;
use sys::{unsupported, Void, sgx_ineffective};
use sys::{unsupported, Void, sgx_ineffective, AsInner, FromInner, IntoInner, TryIntoInner};
use sys::fd::FileDesc;
use convert::TryFrom;
use error;
@ -13,21 +13,38 @@ use super::abi::usercalls;
const DEFAULT_FAKE_TTL: u32 = 64;
#[derive(Debug, Clone)]
struct Socket {
pub struct Socket {
inner: Arc<FileDesc>,
local_addr: String,
local_addr: Option<String>,
}
impl Socket {
fn new(fd: usercalls::raw::Fd, local_addr: String) -> Socket {
Socket { inner: Arc::new(FileDesc::new(fd)), local_addr }
Socket { inner: Arc::new(FileDesc::new(fd)), local_addr: Some(local_addr) }
}
}
impl AsInner<FileDesc> for Socket {
fn as_inner(&self) -> &FileDesc { &self.inner }
}
impl TryIntoInner<FileDesc> for Socket {
fn try_into_inner(self) -> Result<FileDesc, Socket> {
let Socket { inner, local_addr } = self;
Arc::try_unwrap(inner).map_err(|inner| Socket { inner, local_addr } )
}
}
impl FromInner<FileDesc> for Socket {
fn from_inner(inner: FileDesc) -> Socket {
Socket { inner: Arc::new(inner), local_addr: None }
}
}
#[derive(Debug, Clone)]
pub struct TcpStream {
inner: Socket,
peer_addr: String,
peer_addr: Option<String>,
}
fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result<String> {
@ -43,16 +60,19 @@ fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result<String> {
}
}
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())
fn addr_to_sockaddr(addr: &Option<String>) -> io::Result<SocketAddr> {
addr.as_ref()
.ok_or(io::ErrorKind::AddrNotAvailable)?
.to_socket_addrs()
// unwrap OK: if an iterator is returned, we're guaranteed to get exactly one entry
.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 })
Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) })
}
pub fn connect_timeout(addr: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
@ -128,6 +148,24 @@ impl TcpStream {
}
}
impl AsInner<Socket> for TcpStream {
fn as_inner(&self) -> &Socket { &self.inner }
}
// `Inner` includes `peer_addr` so that a `TcpStream` maybe correctly
// reconstructed if `Socket::try_into_inner` fails.
impl IntoInner<(Socket, Option<String>)> for TcpStream {
fn into_inner(self) -> (Socket, Option<String>) {
(self.inner, self.peer_addr)
}
}
impl FromInner<(Socket, Option<String>)> for TcpStream {
fn from_inner((inner, peer_addr): (Socket, Option<String>)) -> TcpStream {
TcpStream { inner, peer_addr }
}
}
#[derive(Debug, Clone)]
pub struct TcpListener {
inner: Socket,
@ -146,6 +184,7 @@ impl TcpListener {
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
let (fd, local_addr, peer_addr) = usercalls::accept_stream(self.inner.inner.raw())?;
let peer_addr = Some(peer_addr);
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))
}
@ -179,6 +218,22 @@ impl TcpListener {
}
}
impl AsInner<Socket> for TcpListener {
fn as_inner(&self) -> &Socket { &self.inner }
}
impl IntoInner<Socket> for TcpListener {
fn into_inner(self) -> Socket {
self.inner
}
}
impl FromInner<Socket> for TcpListener {
fn from_inner(inner: Socket) -> TcpListener {
TcpListener { inner }
}
}
pub struct UdpSocket(Void);
impl UdpSocket {