mc_sgx_io_untrusted/
lib.rs

1// Copyright (c) 2022 The MobileCoin Foundation
2#![doc = include_str!("../README.md")]
3#![deny(missing_docs, missing_debug_implementations)]
4
5//! Module for providing stderr IO from an enclave.
6//!
7//! By default stderr from an enclave will be directed to the untrusted (host)
8//! stderr. Consumers can redirect this stream by providing a [`WriteAll`]
9//! function via [`stderr_sink`].
10
11use once_cell::sync::Lazy;
12use std::ffi::c_void;
13use std::io::Write;
14use std::slice;
15use std::sync::Mutex;
16
17/// A function that that writes the entire contents of the provided buffer.
18///
19/// This is meant be a stand alone version of [`std::io::Write::write_all`].
20pub type WriteAll = dyn Fn(&[u8]);
21
22/// Specify the function to use for stderr messages
23///
24/// # Arguments
25/// * `write_all` - The function to use for writing stderr from the enclave
26pub fn stderr_sink(write_all: &'static WriteAll) {
27    let mut stderr = STDERR.lock().expect("Mutex has been poisoned");
28    stderr.write_all = write_all;
29}
30
31/// Wraps the `write_all` function in a struct so that we can implement the
32/// `Send` trait.
33struct Stream {
34    write_all: &'static dyn Fn(&[u8]),
35}
36
37/// SAFETY: The [`Stream`] is local to this crate and will be enclosed in a
38/// Mutex so is safe to make `Send`.
39unsafe impl Send for Stream {}
40
41/// The stderr stream to use for the `ocall_stderr`
42static STDERR: Lazy<Mutex<Stream>> = Lazy::new(|| {
43    Mutex::new(Stream {
44        write_all: &default_stderr_write_all,
45    })
46});
47
48/// A ['WriteAll] function that directs to [`std::io::stderr`]
49fn default_stderr_write_all(buf: &[u8]) {
50    std::io::stderr()
51        .write_all(buf)
52        .expect("Failed writing to stderr");
53}
54
55#[no_mangle]
56/// The ocall that will take in stderr messages from the enclave.
57extern "C" fn ocall_stderr(input: *const c_void, len: usize) {
58    // SAFETY: Converting from C interface to Rust. We must rely on the enclave
59    // side of the implementation to provide the correct length for the input
60    // buffer
61    let bytes = unsafe { slice::from_raw_parts(input as *const u8, len) };
62    let stderr = STDERR.lock().expect("Mutex has been poisoned");
63    (stderr.write_all)(bytes)
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use mc_sgx_urts::EnclaveBuilder;
70    use mc_sgx_util::ResultInto;
71    use serial_test::serial;
72    use test_enclave::{ecall_round_trip_to_stderr, ENCLAVE};
73
74    static TEST_STREAM: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
75    fn test_stream_write_all(message: &[u8]) {
76        let mut error_string = TEST_STREAM.lock().expect("Mutex has been poisoned");
77        error_string.clear();
78        error_string.push_str(std::str::from_utf8(message).unwrap());
79    }
80
81    fn test_stream_contents() -> String {
82        TEST_STREAM.lock().expect("Mutex has been poisoned").clone()
83    }
84
85    #[test]
86    #[serial]
87    fn one_line_error_message() {
88        stderr_sink(&test_stream_write_all);
89        let enclave = EnclaveBuilder::from(ENCLAVE).create().unwrap();
90        let id = enclave.id();
91
92        let message = b"a one liner";
93        unsafe {
94            ecall_round_trip_to_stderr(*id, message.as_ptr() as *const c_void, message.len())
95        }
96        .into_result()
97        .unwrap();
98        let output = test_stream_contents();
99        assert_eq!(output.as_str(), "a one liner");
100    }
101
102    #[test]
103    #[serial]
104    fn multi_line_error_message() {
105        stderr_sink(&test_stream_write_all);
106        let enclave = EnclaveBuilder::from(ENCLAVE).create().unwrap();
107        let id = enclave.id();
108
109        let message = b"this is\nmulti line\n";
110        unsafe {
111            ecall_round_trip_to_stderr(*id, message.as_ptr() as *const c_void, message.len())
112        }
113        .into_result()
114        .unwrap();
115        let output = test_stream_contents();
116        assert_eq!(output.as_str(), "this is\nmulti line\n");
117    }
118}