blob: 50cc3fcdc74a6423a5ae6ccf9c14e474449cb8b9 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#[cfg(feature = "mesalock_sgx")]
use std::prelude::v1::*;
use std::cell::RefCell;
use std::slice;
use std::thread_local;
use sgx_types::{c_char, c_int, c_uchar, c_uint, size_t};
use std::collections::HashMap;
use std::format;
use teaclave_types::TeaclaveRuntime;
const FFI_OK: c_uint = 0;
const FFI_FILE_ERROR: c_uint = 1;
pub struct Context {
runtime: Box<dyn TeaclaveRuntime + Send + Sync>,
seq: Sequence,
read_handles: HandleRegistry<Box<dyn std::io::Read>>,
write_handles: HandleRegistry<Box<dyn std::io::Write>>,
}
impl Context {
pub fn new(runtime: Box<dyn TeaclaveRuntime + Send + Sync>) -> Context {
Context {
runtime,
seq: Sequence::new(1, 1024),
read_handles: HandleRegistry::default(),
write_handles: HandleRegistry::default(),
}
}
fn open_input(&mut self, fid: &str) -> anyhow::Result<FileHandle> {
let file = self.runtime.open_input(fid)?;
let handle = self.seq.next()?.into_read_handle();
self.read_handles.add(handle, file)?;
Ok(handle)
}
fn create_output(&mut self, fid: &str) -> anyhow::Result<FileHandle> {
let file = self.runtime.create_output(fid)?;
let handle = self.seq.next()?.into_write_handle();
self.write_handles.add(handle, file)?;
Ok(handle)
}
fn read_handle(&mut self, handle: FileHandle, buf: &mut [u8]) -> anyhow::Result<usize> {
let file = self.read_handles.get_mut(handle)?;
let size = file.read(buf)?;
Ok(size)
}
fn write_handle(&mut self, handle: FileHandle, buf: &[u8]) -> anyhow::Result<usize> {
let file = self.write_handles.get_mut(handle)?;
let size = file.write(buf)?;
Ok(size)
}
fn close_handle(&mut self, handle: FileHandle) -> anyhow::Result<()> {
if handle.is_read_handle() {
self.read_handles.remove(handle)?;
} else {
self.write_handles.remove(handle)?;
}
Ok(())
}
}
trait HandleEncoding {
fn into_write_handle(self) -> FileHandle;
fn into_read_handle(self) -> FileHandle;
fn is_write_handle(&self) -> bool;
fn is_read_handle(&self) -> bool;
}
impl HandleEncoding for FileHandle {
fn into_write_handle(self) -> FileHandle {
assert!(self < HANDLE_UPPDER_BOUND);
0x4000_0000 | self
}
fn is_write_handle(&self) -> bool {
0x4000_0000 & self > 0
}
fn into_read_handle(self) -> FileHandle {
assert!(self < HANDLE_UPPDER_BOUND);
self
}
fn is_read_handle(&self) -> bool {
!self.is_write_handle()
}
}
struct Sequence {
range: std::ops::Range<FileHandle>,
}
impl Sequence {
fn new(start: FileHandle, end: FileHandle) -> Self {
Sequence {
range: (start..end),
}
}
fn next(&mut self) -> anyhow::Result<FileHandle> {
self.range
.next()
.ok_or_else(|| anyhow::anyhow!("Reached max sequence"))
}
}
type FileHandle = i32;
const HANDLE_UPPDER_BOUND: FileHandle = 0x1000_0000;
struct HandleRegistry<T> {
entries: HashMap<FileHandle, T>,
}
impl<T> HandleRegistry<T> {
fn add(&mut self, handle: FileHandle, obj: T) -> anyhow::Result<()> {
anyhow::ensure!(
self.entries.get(&handle).is_none(),
"Reuse a existed handle: {}",
handle
);
self.entries.insert(handle, obj);
Ok(())
}
fn get_mut(&mut self, handle: FileHandle) -> anyhow::Result<&mut T> {
self.entries
.get_mut(&handle)
.ok_or_else(|| anyhow::anyhow!("Get an invalid handle: {}", handle))
}
fn remove(&mut self, handle: FileHandle) -> anyhow::Result<()> {
self.entries
.remove(&handle)
.ok_or_else(|| anyhow::anyhow!("Remove an invalid handle: {}", handle))?;
Ok(())
}
}
impl<T> std::default::Default for HandleRegistry<T> {
fn default() -> Self {
HandleRegistry {
entries: HashMap::<FileHandle, T>::new(),
}
}
}
thread_local! {
pub static CONTEXT: RefCell<Option<Context>> = RefCell::new(None);
}
pub fn reset_thread_context() -> anyhow::Result<()> {
CONTEXT.with(|ctx| {
let mut ctx = ctx.borrow_mut();
anyhow::ensure!(ctx.is_some(), "Context not initialized");
*ctx = None;
Ok(())
})
}
pub fn set_thread_context(context: Context) -> anyhow::Result<()> {
CONTEXT.with(|ctx| {
let mut ctx = ctx.borrow_mut();
anyhow::ensure!(ctx.is_none(), "Context already initialized");
*ctx = Some(context);
Ok(())
})
}
pub fn rtc_open_input(fid: &str) -> anyhow::Result<FileHandle> {
CONTEXT.with(|ctx| {
let mut ctx = ctx.borrow_mut();
anyhow::ensure!(ctx.is_some(), "Context not initialized");
ctx.as_mut().unwrap().open_input(fid)
})
}
pub fn rtc_create_output(fid: &str) -> anyhow::Result<FileHandle> {
CONTEXT.with(|ctx| {
let mut ctx = ctx.borrow_mut();
anyhow::ensure!(ctx.is_some(), "Context not initialized");
ctx.as_mut().unwrap().create_output(fid)
})
}
pub fn rtc_read_handle(f: FileHandle, buf: &mut [u8]) -> anyhow::Result<usize> {
CONTEXT.with(|ctx| {
let mut ctx = ctx.borrow_mut();
anyhow::ensure!(ctx.is_some(), "Context not initialized");
ctx.as_mut().unwrap().read_handle(f, buf)
})
}
pub fn rtc_write_handle(f: FileHandle, buf: &[u8]) -> anyhow::Result<usize> {
CONTEXT.with(|ctx| {
let mut ctx = ctx.borrow_mut();
anyhow::ensure!(ctx.is_some(), "Context not initialized");
ctx.as_mut().unwrap().write_handle(f, buf)
})
}
pub fn rtc_close_handle(f: FileHandle) -> anyhow::Result<()> {
CONTEXT.with(|ctx| {
let mut ctx = ctx.borrow_mut();
anyhow::ensure!(ctx.is_some(), "Context not initialized");
ctx.as_mut().unwrap().close_handle(f)
})
}
#[cfg(feature = "enclave_unit_test")]
pub mod tests {
use super::*;
use std::path::PathBuf;
use std::str::FromStr;
use teaclave_crypto::TeaclaveFile128Key;
use teaclave_runtime::RawIoRuntime;
use teaclave_test_utils::*;
use teaclave_types::hashmap;
use teaclave_types::FileAuthTag;
use teaclave_types::StagedFileInfo;
use teaclave_types::StagedFiles;
pub fn run_tests() -> bool {
run_tests!(test_file_handle_encoding, test_rtc_api,)
}
fn test_file_handle_encoding() {
assert_eq!(5, 5.into_read_handle());
assert_eq!(0x4000_0006, 6.into_write_handle());
assert_eq!(true, 0x4000_0000.is_write_handle());
assert_eq!(false, 0x4000_0000.is_read_handle());
assert_eq!(true, 0x9.is_read_handle());
assert_eq!(false, 0xff.is_write_handle());
}
fn test_rtc_api() {
let input = PathBuf::from_str("fixtures/functions/mesapy/input.txt").unwrap();
let output = PathBuf::from_str("fixtures/functions/mesapy/output.txt.out").unwrap();
let input_info =
StagedFileInfo::new(input, TeaclaveFile128Key::random(), FileAuthTag::mock());
let output_info =
StagedFileInfo::new(output, TeaclaveFile128Key::random(), FileAuthTag::mock());
let in_fid = "in_f1";
let out_fid = "out_f1";
let input_files = StagedFiles::new(hashmap!(in_fid => input_info));
let output_files = StagedFiles::new(hashmap!(out_fid => output_info));
let runtime = Box::new(RawIoRuntime::new(input_files, output_files));
set_thread_context(Context::new(runtime)).unwrap();
let expected_input = b"Hello\nWorld";
let f = rtc_open_input(&in_fid).unwrap();
let mut buf = [0u8; 128];
let size = rtc_read_handle(f, &mut buf).unwrap();
assert_eq!(&expected_input[..], &buf[..size]);
assert!(rtc_close_handle(f).is_ok());
assert!(rtc_close_handle(f).is_err());
let f = rtc_create_output(&out_fid).unwrap();
let size = rtc_write_handle(f, &expected_input[..]).unwrap();
assert_eq!(size, expected_input.len());
assert!(rtc_close_handle(f).is_ok());
assert!(rtc_close_handle(f).is_err());
reset_thread_context().unwrap();
}
}
use std::ffi::CStr;
/*
* uint c_open_input(char* file_id, int* out_fd);
*
*/
#[allow(unused)]
#[no_mangle]
extern "C" fn c_open_input(fid: *mut c_char, out_handle: *mut c_int) -> c_uint {
debug!("c_open_input");
let fid = unsafe { CStr::from_ptr(fid).to_string_lossy().into_owned() };
match rtc_open_input(&fid) {
Ok(handle) => {
unsafe {
*out_handle = handle;
}
FFI_OK
}
Err(e) => {
info!("c_open_file: {:?}", e);
FFI_FILE_ERROR
}
}
}
/*
* uint c_create_output(char* file_id, int* out_fd);
*
*/
#[allow(unused)]
#[no_mangle]
extern "C" fn c_create_output(fid: *mut c_char, out_handle: *mut c_int) -> c_uint {
debug!("c_create_input");
let fid = unsafe { CStr::from_ptr(fid).to_string_lossy().into_owned() };
match rtc_create_output(&fid) {
Ok(handle) => {
unsafe {
*out_handle = handle;
}
FFI_OK
}
Err(e) => {
info!("c_open_file: {:?}", e);
FFI_FILE_ERROR
}
}
}
/*
* uint c_read_file(int fd, void* out_buf, size_t buf_size, size_t* out_size_read);
*
*/
#[allow(unused)]
#[no_mangle]
extern "C" fn c_read_file(
handle: c_int,
out_buf: *mut c_uchar,
buf_size: size_t,
out_buf_size_p: *mut size_t,
) -> c_uint {
debug!("c_read_file");
let out: &mut [u8] = unsafe { slice::from_raw_parts_mut(out_buf, buf_size) };
match rtc_read_handle(handle, out) {
Ok(size) => {
unsafe {
*out_buf_size_p = size;
}
FFI_OK
}
Err(e) => {
info!("c_read_file: {:?}", e);
FFI_FILE_ERROR
}
}
}
/*
* uint c_write_file(int fd, void* buf, size_t buf_size, size_t* out_size_written);
*/
#[allow(unused)]
#[no_mangle]
extern "C" fn c_write_file(
handle: c_int,
in_buf: *mut c_uchar,
buf_size: size_t,
buf_size_p: *mut size_t,
) -> c_uint {
debug!("c_write_file");
let in_buf: &[u8] = unsafe { slice::from_raw_parts_mut(in_buf, buf_size) };
match rtc_write_handle(handle, in_buf) {
Ok(size) => {
unsafe {
*buf_size_p = size;
}
FFI_OK
}
Err(e) => {
info!("c_write_file: {:?}", e);
FFI_FILE_ERROR
}
}
}
/*
* uint c_close_file(int fd);
*/
#[allow(unused)]
#[no_mangle]
extern "C" fn c_close_file(handle: c_int) -> c_uint {
debug!("c_close_file");
match rtc_close_handle(handle) {
Ok(size) => FFI_OK,
Err(e) => {
info!("c_close_file: {:?}", e);
FFI_FILE_ERROR
}
}
}