Skip to content

Commit

Permalink
Add mc-sgx-io
Browse files Browse the repository at this point in the history
Add `mc-sgx-io` which provides functionality for io streams from an SGX
enclave
  • Loading branch information
nick-mobilecoin committed Jan 13, 2023
1 parent b6c5142 commit d75d963
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 4 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"alloc",
"io",
"io/untrusted",
]
exclude = [
Expand Down
21 changes: 21 additions & 0 deletions io/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "mc-sgx-io"
version = "0.1.0"
edition = "2021"
authors = ["MobileCoin"]
rust-version = "1.62.1"
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/mobilecoinfoundation/sgx-std"
description = "IO implementation for use inside of SGX enclaves"
categories = ["hardware-support", "no-std"]
keywords = ["sgx", "no-std"]

[dependencies]
mc-sgx-core-sys-types = "0.4.0"
mc-sgx-core-types = "0.4.0"
mc-sgx-util = "0.4.0"

[dev-dependencies]
once_cell = "1.16.0"
serial_test = "0.9.0"
21 changes: 21 additions & 0 deletions io/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# MobileCoin SGX: IO in Enclave

[![Project Chat][chat-image]][chat-link]<!--
-->![License][license-image]<!--
-->![Target][target-image]<!--
-->[![Crates.io][crate-image]][crate-link]<!--
-->[![Docs Status][docs-image]][docs-link]<!--
-->[![Dependency Status][deps-image]][deps-link]

Provide IO streams for use in an SGX enclave

[chat-image]: https://img.shields.io/discord/844353360348971068?style=flat-square
[chat-link]: https://mobilecoin.chat
[license-image]: https://img.shields.io/crates/l/mc-sgx-io?style=flat-square
[target-image]: https://img.shields.io/badge/target-sgx-red?style=flat-square
[crate-image]: https://img.shields.io/crates/v/mc-sgx-io.svg?style=flat-square
[crate-link]: https://crates.io/crates/mc-sgx-io
[docs-image]: https://img.shields.io/docsrs/mc-sgx-io?style=flat-square
[docs-link]: https://docs.rs/crate/mc-sgx-io
[deps-image]: https://deps.rs/crate/mc-sgx-io/0.1.0/status.svg?style=flat-square
[deps-link]: https://deps.rs/crate/mc-sgx-io/0.1.0
109 changes: 109 additions & 0 deletions io/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Copyright (c) 2022 The MobileCoin Foundation

#![doc = include_str!("../README.md")]
#![deny(missing_docs, missing_debug_implementations)]
#![no_std]

use core::ffi::c_void;
use mc_sgx_core_sys_types::sgx_status_t;
use mc_sgx_core_types::Error;
use mc_sgx_util::ResultInto;

/// Attempts to write the entire buffer into the hosts stderr sink.
///
/// # Arguments
/// * `buffer` - The buffer to write.
///
/// # Errors
/// If there is any error writing all of `buffer` to the sink. No
/// assumptions should be made about the amount that was written to the sink
/// when an error occurs.
pub fn stderr_write_all(buffer: &[u8]) -> Result<(), Error> {
unsafe { ocall_stderr(buffer.as_ptr() as *const c_void, buffer.len()) }.into_result()
}

extern "C" {
/// The ocall to send stderr messages to
///
/// # Arguments
/// * `input` - The input buffer/stream. Should be ui8/bytes
/// * `len` - The byte length of `input`
///
/// # Returns
/// `sgx_status_t::SGX_SUCCESS` when all of input was successfully written
/// to the untrusted stderr sink.
/// An error status if not all of the data could be written to the sink. No
/// assumptions are made about how much data was written on error.
fn ocall_stderr(input: *const c_void, len: usize) -> sgx_status_t;
}

// Done out here so that `serial_test` works, since it uses "::std" in the macro
#[cfg(test)]
extern crate std;

#[cfg(test)]
mod tests {
use super::*;
use core::slice;
use once_cell::sync::Lazy;
use serial_test::serial;
use std::string::String;
use std::sync::Mutex;

static TEST_STREAM: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
static TEST_STREAM_RESULT: Lazy<Mutex<sgx_status_t>> =
Lazy::new(|| Mutex::new(sgx_status_t::SGX_SUCCESS));

fn reset_test_stream() {
let mut stream = TEST_STREAM.lock().expect("Mutex has been poisoned");
stream.clear();
let mut status = TEST_STREAM_RESULT.lock().expect("Mutex has been poisoned");
*status = sgx_status_t::SGX_SUCCESS;
}

#[no_mangle]
extern "C" fn ocall_stderr(input: *const c_void, len: usize) -> sgx_status_t {
let bytes = unsafe { slice::from_raw_parts(input as *const u8, len) };
let message =
std::str::from_utf8(bytes).expect("Expected valid UTF8 from stderr in enclave");
let mut stream = TEST_STREAM.lock().expect("Mutex has been poisoned");
stream.clear();
stream.push_str(message);
let status = TEST_STREAM_RESULT.lock().expect("Mutex has been poisoned");
*status
}

#[test]
#[serial]
fn single_line_output_to_stderr() {
reset_test_stream();
stderr_write_all(b"what").expect("Expected the write to succeed");

let written = TEST_STREAM.lock().expect("Mutex has been poisoned");
assert_eq!(written.as_str(), "what");
}

#[test]
#[serial]
fn multi_line_output_to_stderr() {
reset_test_stream();
stderr_write_all(b"this\nhas\nmultiple\nlines").expect("Expected the write to succeed");

let written = TEST_STREAM.lock().expect("Mutex has been poisoned");
assert_eq!(written.as_str(), "this\nhas\nmultiple\nlines");
}

#[test]
#[serial]
fn error_when_outputting_to_stderr() {
reset_test_stream();
let expected_error = sgx_status_t::SGX_ERROR_FILE_BAD_STATUS;
{
let mut status = TEST_STREAM_RESULT.lock().expect("Mutex has been poisoned");
*status = expected_error;
}

let error = stderr_write_all(b"what").unwrap_err();
assert_eq!(error, Error::FileBadStatus);
}
}
1 change: 1 addition & 0 deletions io/untrusted/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ mc-sgx-util = "0.4.0"
once_cell = "1.16.0"

[dev-dependencies]
serial_test = "0.9.0"
test_enclave = { path = "../../test_enclave" }
11 changes: 7 additions & 4 deletions io/untrusted/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//!
//! By default stderr from an enclave will be directed to the untrusted (host)
//! stderr. Consumers can redirect this stream by providing a [`WriteAll`]
//! function via [`stderr_write_all`].
//! function via [`stderr_sink`].
use once_cell::sync::Lazy;
use std::ffi::c_void;
Expand All @@ -23,7 +23,7 @@ pub type WriteAll = dyn Fn(&[u8]);
///
/// # Arguments
/// * `write_all` - The function to use for writing stderr from the enclave
pub fn stderr_write_all(write_all: &'static WriteAll) {
pub fn stderr_sink(write_all: &'static WriteAll) {
let mut stderr = STDERR.lock().expect("Mutex has been poisoned");
stderr.write_all = write_all;
}
Expand Down Expand Up @@ -68,6 +68,7 @@ mod tests {
use super::*;
use mc_sgx_urts::EnclaveBuilder;
use mc_sgx_util::ResultInto;
use serial_test::serial;
use test_enclave::{ecall_round_trip_to_stderr, ENCLAVE};

static TEST_STREAM: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
Expand All @@ -82,8 +83,9 @@ mod tests {
}

#[test]
#[serial]
fn one_line_error_message() {
stderr_write_all(&test_stream_write_all);
stderr_sink(&test_stream_write_all);
let enclave = EnclaveBuilder::from(ENCLAVE).create().unwrap();
let id = enclave.id();

Expand All @@ -98,8 +100,9 @@ mod tests {
}

#[test]
#[serial]
fn multi_line_error_message() {
stderr_write_all(&test_stream_write_all);
stderr_sink(&test_stream_write_all);
let enclave = EnclaveBuilder::from(ENCLAVE).create().unwrap();
let id = enclave.id();

Expand Down

0 comments on commit d75d963

Please sign in to comment.