mc_sgx_io_untrusted/
lib.rs1#![doc = include_str!("../README.md")]
3#![deny(missing_docs, missing_debug_implementations)]
4
5use once_cell::sync::Lazy;
12use std::ffi::c_void;
13use std::io::Write;
14use std::slice;
15use std::sync::Mutex;
16
17pub type WriteAll = dyn Fn(&[u8]);
21
22pub 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
31struct Stream {
34 write_all: &'static dyn Fn(&[u8]),
35}
36
37unsafe impl Send for Stream {}
40
41static STDERR: Lazy<Mutex<Stream>> = Lazy::new(|| {
43 Mutex::new(Stream {
44 write_all: &default_stderr_write_all,
45 })
46});
47
48fn 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]
56extern "C" fn ocall_stderr(input: *const c_void, len: usize) {
58 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}