diff --git a/TODO.md b/TODO.md index 21a0fa99..73ba2a07 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,3 @@ # TODOs -- [ ] Add README.md -- [ ] Add file table -- [ ] Add more syscalls -- [ ] Pass argc and argv - -- [ ] Add libc +- [ ] Remove src/libc +- [ ] Check all dangerous arithmetic overflows and underflows diff --git a/src/libos/Cargo.lock b/src/libos/Cargo.lock index 4b618375..cfd537b4 100644 --- a/src/libos/Cargo.lock +++ b/src/libos/Cargo.lock @@ -1,5 +1,5 @@ [[package]] -name = "Rusgx" +name = "Occlum" version = "0.0.1" dependencies = [ "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/libos/Cargo.toml b/src/libos/Cargo.toml index 5f3d783c..75f56a60 100644 --- a/src/libos/Cargo.toml +++ b/src/libos/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "Rusgx" +name = "Occlum" version = "0.0.1" [lib] diff --git a/src/libos/src/init_stack.rs b/src/libos/src/init_stack.rs new file mode 100644 index 00000000..dfa0065c --- /dev/null +++ b/src/libos/src/init_stack.rs @@ -0,0 +1,345 @@ +use prelude::*; +use {std, std::mem, std::ptr}; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +/* + * The initial stack of a process looks like below: + * + * + * +---------------------+ <------+ Top of stack + * | | (high address) + * | Null-terminated | + * | strings referenced | + * | by variables below | + * | | + * +---------------------+ + * | AT_NULL | + * +---------------------+ + * | AT_NULL | + * +---------------------+ + * | ... | + * +---------------------+ + * | aux_val[0] | + * +---------------------+ + * | aux_key[0] | <------+ Auxiliary table + * +---------------------+ + * | NULL | + * +---------------------+ + * | ... | + * +---------------------+ + * | char* envp[0] | <------+ Environment variabls + * +---------------------+ + * | NULL | + * +---------------------+ + * | char* argv[argc-1] | + * +---------------------+ + * | ... | + * +---------------------+ + * | char* argv[0] | + * +---------------------+ + * | long argc | <------+ Program arguments + * +---------------------+ + * | | + * | | + * + + + * + */ + +pub fn do_init_process_stack<'a, 'b>(stack: &'a StackBuf, + argv: &'b [CString], envp: &'b [CString], auxtbl: &'b AuxTable) + -> Result<(), Error> +{ + let envp_cloned = clone_cstrings_on_stack(stack, envp)?; + let argv_cloned = clone_cstrings_on_stack(stack, argv)?; + dump_auxtbl_on_stack(stack, auxtbl)?; + dump_cstrptrs_on_stack(stack, &envp_cloned); + dump_cstrptrs_on_stack(stack, &argv_cloned); + stack.put(argv.len() as u64); + Ok(()) +} + +/// StackBuf is a buffer that is filled in from high addresses to low +/// (just as a stack). The range of available memory of a StackBuf is from +/// [self.bottom, self.top). +#[derive(Debug)] +pub struct StackBuf { + stack_top: *const u8, + stack_bottom: *const u8, + stack_pos: Cell<*const u8>, +} + +impl Default for StackBuf { + fn default() -> StackBuf { + StackBuf { + stack_top: 0 as *const u8, + stack_bottom: 0 as *const u8, + stack_pos: Cell::new(0 as *const u8), + } + } +} + +impl StackBuf { + pub fn new(stack_top: *const u8, stack_bottom: *const u8) -> Result{ + if stack_top as usize <= stack_bottom as usize{ + return Err(Error::new(Errno::EINVAL, "Invalid stack range")); + }; + Ok(StackBuf { + stack_top: stack_top, + stack_bottom: stack_bottom, + stack_pos: Cell::new(stack_top), + }) + } + + pub fn put(&self, val: T) -> Result<*const T, Error> + where T : Copy + { + let val_size = mem::size_of::(); + let val_align = mem::align_of::(); + let val_ptr = self.alloc(val_size, val_align)? as *mut T; + unsafe { ptr::write(val_ptr, val); } + Ok(val_ptr as *const T) + } + + pub fn put_slice(&self, vals: &[T]) -> Result<*const T, Error> + where T: Copy + { + let val_size = mem::size_of::(); + let val_align = mem::align_of::(); + let total_size = val_size * vals.len(); + let base_ptr = self.alloc(total_size, val_align)? as *mut T; + + let mut val_ptr = base_ptr; + for v in vals { + unsafe { ptr::write(val_ptr, *v); } + val_ptr = unsafe { val_ptr.offset(1) }; + } + + Ok(base_ptr as *const T) + } + + pub fn put_cstr(&self, cstr: &CStr) -> Result<*const u8, Error> { + let bytes = cstr.to_bytes_with_nul(); + self.put_slice(bytes) + } + + pub fn get_pos(&self) -> *const u8 { + self.stack_pos.get() + } + + fn alloc(&self, size: usize, align_power2: usize) -> Result<*mut u8, Error> { + if size == 0 || !align_power2.is_power_of_two() { + return Err(Error::new(Errno::EINVAL, "Invalid size or align")); + } + + let old_pos = { + let old_pos = self.stack_pos.get() as usize; + let remain_size = old_pos - self.stack_bottom as usize; + if size > remain_size { + return Err(Error::new(Errno::ENOMEM, "No enough space in buffer")); + } + old_pos + }; + + let new_pos = { + let mask = (-(align_power2 as isize)) as usize; + let new_pos = (old_pos - size) & mask; + if new_pos < self.stack_bottom as usize { + return Err(Error::new(Errno::ENOMEM, "No enough space in buffer")); + } + new_pos as *const u8 + }; + self.stack_pos.set(new_pos); + + Ok(new_pos as *mut u8) + } +} + + +fn clone_cstrings_on_stack<'a, 'b>(stack: &'a StackBuf, + cstrings: &'b [CString]) + -> Result, Error> +{ + let mut cstrs_cloned = Vec::new(); + for cs in cstrings.iter().rev() { + let cstrp_cloned = stack.put_cstr(cs)?; + let cstr_cloned = unsafe { + CStr::from_ptr::<'a>(cstrp_cloned as *const c_char) + }; + cstrs_cloned.push(cstr_cloned); + } + cstrs_cloned.reverse(); + Ok(cstrs_cloned) +} + +fn dump_auxtbl_on_stack<'a, 'b>(stack: &'a StackBuf, auxtbl: &'b AuxTable) + -> Result<(), Error> +{ + // For every key-value pari, dump the value first, then the key + stack.put(AuxKey::AT_NULL as u64); + stack.put(AuxKey::AT_NULL as u64); + for (aux_key, aux_val) in auxtbl { + stack.put(aux_val); + stack.put(aux_key as u64); + } + Ok(()) +} + +fn dump_cstrptrs_on_stack<'a, 'b>(stack: &'a StackBuf, strptrs: &'b [&'a CStr]) + -> Result<(), Error> +{ + stack.put(0 as u64); // End with a NULL pointer + for sp in strptrs.iter().rev() { + stack.put(sp.as_ptr() as u64); + } + Ok(()) +} + + +/* Symbolic values for the entries in the auxiliary table + put on the initial stack */ +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum AuxKey { + AT_NULL = 0, /* end of vector */ + AT_IGNORE = 1, /* entry should be ignored */ + AT_EXECFD = 2, /* file descriptor of program */ + AT_PHDR = 3, /* program headers for program */ + AT_PHENT = 4, /* size of program header entry */ + AT_PHNUM = 5, /* number of program headers */ + AT_PAGESZ = 6, /* system page size */ + AT_BASE = 7, /* base address of interpreter */ + AT_FLAGS = 8, /* flags */ + AT_ENTRY = 9, /* entry point of program */ + AT_NOTELF = 10, /* program is not ELF */ + AT_UID = 11, /* real uid */ + AT_EUID = 12, /* effective uid */ + AT_GID = 13, /* real gid */ + AT_EGID = 14, /* effective gid */ + AT_PLATFORM = 15, /* string identifying CPU for optimizations */ + AT_HWCAP = 16, /* arch dependent hints at CPU capabilities */ + AT_CLKTCK = 17, /* frequency at which times() increments */ + + /* 18...22 not used */ + + AT_SECURE = 23, /* secure mode boolean */ + AT_BASE_PLATFORM = 24, /* string identifying real platform, may + * differ from AT_PLATFORM. */ + AT_RANDOM = 25, /* address of 16 random bytes */ + AT_HWCAP2 = 26, /* extension of AT_HWCAP */ + + /* 28...30 not used */ + + AT_EXECFN = 31, /* filename of program */ +} + + +static AUX_KEYS: &'static [AuxKey] = &[ + AuxKey::AT_NULL, + AuxKey::AT_IGNORE, + AuxKey::AT_EXECFD, + AuxKey::AT_PHDR, + AuxKey::AT_PHENT, + AuxKey::AT_PHNUM, + AuxKey::AT_PAGESZ, + AuxKey::AT_BASE, + AuxKey::AT_FLAGS, + AuxKey::AT_ENTRY, + AuxKey::AT_NOTELF, + AuxKey::AT_UID, + AuxKey::AT_EUID, + AuxKey::AT_GID, + AuxKey::AT_EGID, + AuxKey::AT_PLATFORM, + AuxKey::AT_HWCAP, + AuxKey::AT_CLKTCK, + AuxKey::AT_SECURE, + AuxKey::AT_BASE_PLATFORM, + AuxKey::AT_RANDOM, + AuxKey::AT_HWCAP2, + AuxKey::AT_EXECFN, +]; + +impl AuxKey { + pub const MAX: usize = 32; + + pub fn next(&self) -> Option { + let self_idx = AUX_KEYS.iter().position(|x| *x == *self).unwrap(); + let next_idx = self_idx + 1; + if next_idx < AUX_KEYS.len() { + Some(AUX_KEYS[next_idx]) + } + else { + None + } + } +} + + +#[derive(Clone, Default, Copy, Debug)] +pub struct AuxTable { + values: [Option; AuxKey::MAX], +} + +impl AuxTable { + pub fn new() -> AuxTable { + AuxTable { + values: [None; AuxKey::MAX] + } + } + + pub fn set_val(&mut self, key: AuxKey, val: u64) -> Result<(), Error>{ + if key == AuxKey::AT_NULL || key == AuxKey::AT_IGNORE { + return Err(Error::new(Errno::EINVAL, "Illegal key")); + } + self.values[key as usize] = Some(val); + Ok(()) + } + + pub fn get_val(&self, key: AuxKey) -> Option { + self.values[key as usize] + } + + pub fn del_val(&mut self, key: AuxKey) { + self.values[key as usize] = None; + } + + pub fn iter<'a>(&'a self) -> AuxTableIter<'a> { + AuxTableIter { + tbl: self, + key: Some(AuxKey::AT_NULL), + } + } +} + +impl<'a> IntoIterator for &'a AuxTable { + type Item = (AuxKey, u64); + type IntoIter = AuxTableIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +pub struct AuxTableIter<'a> { + tbl: &'a AuxTable, + key: Option, +} + +impl<'a> Iterator for AuxTableIter<'a> { + type Item = (AuxKey, u64); + + fn next(&mut self) -> Option<(AuxKey, u64)> { + loop { + if self.key == None { return None; } + let key = self.key.unwrap(); + + let item = self.tbl.get_val(key).map(|val| (key, val) ); + self.key = key.next(); + + if item != None { + return item; + } + } + } +} diff --git a/src/libos/src/lib.rs b/src/libos/src/lib.rs index 60b31647..357553e5 100644 --- a/src/libos/src/lib.rs +++ b/src/libos/src/lib.rs @@ -34,6 +34,7 @@ mod mm; mod process; mod syscall; mod vma; +mod init_stack; /// Export system calls pub use syscall::*; @@ -48,7 +49,9 @@ pub extern "C" fn libos_boot(path_buf: *const i8) -> i32 { let _ = backtrace::enable_backtrace("libocclum.signed.so", PrintFormat::Short); panic::catch_unwind(||{ backtrace::__rust_begin_short_backtrace(||{ - process::do_spawn(&path_str); + let argv = std::vec::Vec::new(); + let envp = std::vec::Vec::new(); + process::do_spawn(&path_str, &argv, &envp); }) }).ok(); diff --git a/src/libos/src/prelude.rs b/src/libos/src/prelude.rs index cb3d1716..108a5729 100644 --- a/src/libos/src/prelude.rs +++ b/src/libos/src/prelude.rs @@ -7,10 +7,12 @@ pub use sgx_trts::libc; pub use std::marker::{Sync, Send}; pub use std::sync::{Arc, SgxMutex, SgxMutexGuard, SgxRwLock, SgxRwLockReadGuard, SgxRwLockWriteGuard}; +pub use std::cell::{Cell}; pub use std::result::Result; pub use std::borrow::BorrowMut; pub use std::boxed::Box; pub use std::vec::Vec; +pub use std::string::{String}; pub use std::collections::{HashMap, VecDeque}; pub use std::fmt::{Debug, Display}; diff --git a/src/libos/src/process.rs b/src/libos/src/process.rs index cabd01eb..7929f5af 100644 --- a/src/libos/src/process.rs +++ b/src/libos/src/process.rs @@ -7,6 +7,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sgxfs::SgxFile; use std::thread; use std::cell::Cell; +use std::ffi::{CStr, CString}; use xmas_elf::{ElfFile, header, program, sections}; use xmas_elf::symbol_table::Entry; @@ -14,6 +15,7 @@ use xmas_elf::symbol_table::Entry; use vma::Vma; use file::{File, StdinFile, StdoutFile/*, StderrFile*/}; use file_table::{FileTable}; +use init_stack::{StackBuf, AuxKey, AuxTable, do_init_process_stack}; lazy_static! { static ref PROCESS_TABLE: SgxMutex> = { @@ -45,8 +47,10 @@ fn free_pid(pid: u32) { } -pub fn do_spawn>(elf_path: &P) -> Result { - let elf_buf = open_elf(elf_path) +pub fn do_spawn>(elf_path: &P, argv: &[CString], envp: &[CString]) + -> Result +{ + let elf_buf = open_elf(&elf_path) .map_err(|e| (e.errno, "Failed to open the ELF file"))?; let elf_file = { @@ -64,7 +68,7 @@ pub fn do_spawn>(elf_path: &P) -> Result { }; let new_process = { - let mut new_process = Process::new(&elf_file) + let mut new_process = Process::new(&elf_file, argv, envp) .map_err(|e| (Errno::EUNDEF, "Failed to create the process"))?; { @@ -206,7 +210,7 @@ fn open_elf>(path: &P) -> Result, Error> { } -#[derive(Clone, Debug, Default)] +#[derive(Debug, Default)] #[repr(C)] pub struct Process { pub task: Task, @@ -223,14 +227,16 @@ pub struct Process { pub type ProcessRef = Arc>; impl Process { - pub fn new(elf_file: &ElfFile) -> Result { + pub fn new(elf_file: &ElfFile, argv: &[CString], envp: &[CString]) + -> Result + { let mut new_process : Process = Default::default(); new_process.create_process_image(elf_file)?; new_process.link_syscalls(elf_file)?; new_process.mprotect()?; new_process.task = Task { - user_stack_addr: new_process.stack_vma.mem_end - 16, + user_stack_addr: new_process.init_stack(argv, envp)? as usize, user_entry_addr: new_process.program_entry_addr, fs_base_addr: 0, .. Default::default() @@ -241,6 +247,25 @@ impl Process { Ok(new_process) } + fn init_stack(&mut self, argv: &[CString], envp: &[CString]) + -> Result<*const u8, Error> + { + let stack = StackBuf::new(self.stack_vma.mem_end as *const u8, + self.stack_vma.mem_begin as *const u8)?; + + let mut auxtbl = AuxTable::new(); + auxtbl.set_val(AuxKey::AT_PAGESZ, 4096)?; + auxtbl.set_val(AuxKey::AT_UID, 0)?; + auxtbl.set_val(AuxKey::AT_GID, 0)?; + auxtbl.set_val(AuxKey::AT_EUID, 0)?; + auxtbl.set_val(AuxKey::AT_EGID, 0)?; + auxtbl.set_val(AuxKey::AT_SECURE, 0)?; + + do_init_process_stack(&stack, &argv, &envp, &auxtbl)?; + + Ok(stack.get_pos()) + } + fn create_process_image(self: &mut Process, elf_file: &ElfFile) -> Result<(), Error> { diff --git a/src/libos/src/syscall.rs b/src/libos/src/syscall.rs index 80d51a60..22cd49ad 100644 --- a/src/libos/src/syscall.rs +++ b/src/libos/src/syscall.rs @@ -1,15 +1,36 @@ use prelude::*; use {std, file, file_table, fs, process}; -use std::ffi::CStr; // a borrowed C string +use std::ffi::{CStr, CString}; // Use the internal syscall wrappers from sgx_tstd //use std::libc_fs as fs; //use std::libc_io as io; -// TODO: check all pointer passed from user belongs to user space +fn check_ptr_from_user(user_ptr: *const T) -> Result<*const T, Error> { + Ok(user_ptr) +} + +fn check_mut_ptr_from_user(user_ptr: *mut T) -> Result<*mut T, Error> { + Ok(user_ptr) +} + +fn clone_string_from_user_safely(user_ptr: *const c_char) + -> Result +{ + let user_ptr = check_ptr_from_user(user_ptr)?; + let string = unsafe { + CStr::from_ptr(user_ptr).to_string_lossy().into_owned() + }; + Ok(string) +} + +fn clone_cstrings_from_user_safely(user_ptr: *const *const c_char) + -> Result, Error> +{ + let cstrings = Vec::new(); + Ok(cstrings) +} -/* -*/ #[no_mangle] pub extern "C" fn occlum_open(path_buf: * const c_char, flags: c_int, mode: c_int) -> c_int { let path = unsafe { @@ -91,22 +112,31 @@ pub extern "C" fn occlum_unknown(num: u32) println!("[WARNING] Unknown syscall (num = {})", num); } +fn do_spawn(child_pid_ptr: *mut c_uint, + path: *const c_char, + argv: *const *const c_char, + envp: *const *const c_char) + -> Result<(), Error> +{ + let child_pid_ptr = check_mut_ptr_from_user(child_pid_ptr)?; + let path = clone_string_from_user_safely(path)?; + let argv = clone_cstrings_from_user_safely(argv)?; + let envp = clone_cstrings_from_user_safely(envp)?; + + let child_pid = process::do_spawn(&path, &argv, &envp)?; + + unsafe { *child_pid_ptr = child_pid }; + Ok(()) +} + #[no_mangle] -pub extern "C" fn occlum_spawn(child_pid: *mut c_int, path: *const c_char, +pub extern "C" fn occlum_spawn( + child_pid: *mut c_uint, path: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int { - let mut ret = 0; - let path_str = unsafe { - CStr::from_ptr(path as * const i8).to_string_lossy().into_owned() - }; - match process::do_spawn(&path_str) { - Ok(new_pid) => unsafe { - *child_pid = new_pid as c_int; - 0 - }, - Err(e) => { - e.errno.as_retval() - } + match do_spawn(child_pid, path, argv, envp) { + Ok(()) => 0, + Err(e) => { e.errno.as_retval() } } }