Init program stack to pass argc, argv, etc.

This commit is contained in:
Tate, Hongliang Tian 2018-11-28 14:43:47 +08:00
parent f0e7bae0d7
commit cf77c3a9a8
8 changed files with 433 additions and 32 deletions

@ -1,7 +1,3 @@
# TODOs # TODOs
- [ ] Add README.md - [ ] Remove src/libc
- [ ] Add file table - [ ] Check all dangerous arithmetic overflows and underflows
- [ ] Add more syscalls
- [ ] Pass argc and argv
- [ ] Add libc

2
src/libos/Cargo.lock generated

@ -1,5 +1,5 @@
[[package]] [[package]]
name = "Rusgx" name = "Occlum"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",

@ -1,5 +1,5 @@
[package] [package]
name = "Rusgx" name = "Occlum"
version = "0.0.1" version = "0.0.1"
[lib] [lib]

345
src/libos/src/init_stack.rs Normal file

@ -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<StackBuf, Error>{
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<T>(&self, val: T) -> Result<*const T, Error>
where T : Copy
{
let val_size = mem::size_of::<T>();
let val_align = mem::align_of::<T>();
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<T>(&self, vals: &[T]) -> Result<*const T, Error>
where T: Copy
{
let val_size = mem::size_of::<T>();
let val_align = mem::align_of::<T>();
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<Vec<&'a CStr>, 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<AuxKey> {
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<u64>; 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<u64> {
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<AuxKey>,
}
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;
}
}
}
}

@ -34,6 +34,7 @@ mod mm;
mod process; mod process;
mod syscall; mod syscall;
mod vma; mod vma;
mod init_stack;
/// Export system calls /// Export system calls
pub use syscall::*; 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); let _ = backtrace::enable_backtrace("libocclum.signed.so", PrintFormat::Short);
panic::catch_unwind(||{ panic::catch_unwind(||{
backtrace::__rust_begin_short_backtrace(||{ 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(); }).ok();

@ -7,10 +7,12 @@ pub use sgx_trts::libc;
pub use std::marker::{Sync, Send}; pub use std::marker::{Sync, Send};
pub use std::sync::{Arc, SgxMutex, SgxMutexGuard, SgxRwLock, pub use std::sync::{Arc, SgxMutex, SgxMutexGuard, SgxRwLock,
SgxRwLockReadGuard, SgxRwLockWriteGuard}; SgxRwLockReadGuard, SgxRwLockWriteGuard};
pub use std::cell::{Cell};
pub use std::result::Result; pub use std::result::Result;
pub use std::borrow::BorrowMut; pub use std::borrow::BorrowMut;
pub use std::boxed::Box; pub use std::boxed::Box;
pub use std::vec::Vec; pub use std::vec::Vec;
pub use std::string::{String};
pub use std::collections::{HashMap, VecDeque}; pub use std::collections::{HashMap, VecDeque};
pub use std::fmt::{Debug, Display}; pub use std::fmt::{Debug, Display};

@ -7,6 +7,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
use std::sgxfs::SgxFile; use std::sgxfs::SgxFile;
use std::thread; use std::thread;
use std::cell::Cell; use std::cell::Cell;
use std::ffi::{CStr, CString};
use xmas_elf::{ElfFile, header, program, sections}; use xmas_elf::{ElfFile, header, program, sections};
use xmas_elf::symbol_table::Entry; use xmas_elf::symbol_table::Entry;
@ -14,6 +15,7 @@ use xmas_elf::symbol_table::Entry;
use vma::Vma; use vma::Vma;
use file::{File, StdinFile, StdoutFile/*, StderrFile*/}; use file::{File, StdinFile, StdoutFile/*, StderrFile*/};
use file_table::{FileTable}; use file_table::{FileTable};
use init_stack::{StackBuf, AuxKey, AuxTable, do_init_process_stack};
lazy_static! { lazy_static! {
static ref PROCESS_TABLE: SgxMutex<HashMap<u32, ProcessRef>> = { static ref PROCESS_TABLE: SgxMutex<HashMap<u32, ProcessRef>> = {
@ -45,8 +47,10 @@ fn free_pid(pid: u32) {
} }
pub fn do_spawn<P: AsRef<Path>>(elf_path: &P) -> Result<u32, Error> { pub fn do_spawn<P: AsRef<Path>>(elf_path: &P, argv: &[CString], envp: &[CString])
let elf_buf = open_elf(elf_path) -> Result<u32, Error>
{
let elf_buf = open_elf(&elf_path)
.map_err(|e| (e.errno, "Failed to open the ELF file"))?; .map_err(|e| (e.errno, "Failed to open the ELF file"))?;
let elf_file = { let elf_file = {
@ -64,7 +68,7 @@ pub fn do_spawn<P: AsRef<Path>>(elf_path: &P) -> Result<u32, Error> {
}; };
let new_process = { 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"))?; .map_err(|e| (Errno::EUNDEF, "Failed to create the process"))?;
{ {
@ -206,7 +210,7 @@ fn open_elf<P: AsRef<Path>>(path: &P) -> Result<Vec<u8>, Error> {
} }
#[derive(Clone, Debug, Default)] #[derive(Debug, Default)]
#[repr(C)] #[repr(C)]
pub struct Process { pub struct Process {
pub task: Task, pub task: Task,
@ -223,14 +227,16 @@ pub struct Process {
pub type ProcessRef = Arc<SgxMutex<Process>>; pub type ProcessRef = Arc<SgxMutex<Process>>;
impl Process { impl Process {
pub fn new(elf_file: &ElfFile) -> Result<Process, Error> { pub fn new(elf_file: &ElfFile, argv: &[CString], envp: &[CString])
-> Result<Process, Error>
{
let mut new_process : Process = Default::default(); let mut new_process : Process = Default::default();
new_process.create_process_image(elf_file)?; new_process.create_process_image(elf_file)?;
new_process.link_syscalls(elf_file)?; new_process.link_syscalls(elf_file)?;
new_process.mprotect()?; new_process.mprotect()?;
new_process.task = Task { 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, user_entry_addr: new_process.program_entry_addr,
fs_base_addr: 0, fs_base_addr: 0,
.. Default::default() .. Default::default()
@ -241,6 +247,25 @@ impl Process {
Ok(new_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) fn create_process_image(self: &mut Process, elf_file: &ElfFile)
-> Result<(), Error> -> Result<(), Error>
{ {

@ -1,15 +1,36 @@
use prelude::*; use prelude::*;
use {std, file, file_table, fs, process}; 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 the internal syscall wrappers from sgx_tstd
//use std::libc_fs as fs; //use std::libc_fs as fs;
//use std::libc_io as io; //use std::libc_io as io;
// TODO: check all pointer passed from user belongs to user space fn check_ptr_from_user<T>(user_ptr: *const T) -> Result<*const T, Error> {
Ok(user_ptr)
}
fn check_mut_ptr_from_user<T>(user_ptr: *mut T) -> Result<*mut T, Error> {
Ok(user_ptr)
}
fn clone_string_from_user_safely(user_ptr: *const c_char)
-> Result<String, Error>
{
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<Vec<CString>, Error>
{
let cstrings = Vec::new();
Ok(cstrings)
}
/*
*/
#[no_mangle] #[no_mangle]
pub extern "C" fn occlum_open(path_buf: * const c_char, flags: c_int, mode: c_int) -> c_int { pub extern "C" fn occlum_open(path_buf: * const c_char, flags: c_int, mode: c_int) -> c_int {
let path = unsafe { let path = unsafe {
@ -91,22 +112,31 @@ pub extern "C" fn occlum_unknown(num: u32)
println!("[WARNING] Unknown syscall (num = {})", num); 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] #[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 argv: *const *const c_char, envp: *const *const c_char) -> c_int
{ {
let mut ret = 0; match do_spawn(child_pid, path, argv, envp) {
let path_str = unsafe { Ok(()) => 0,
CStr::from_ptr(path as * const i8).to_string_lossy().into_owned() Err(e) => { e.errno.as_retval() }
};
match process::do_spawn(&path_str) {
Ok(new_pid) => unsafe {
*child_pid = new_pid as c_int;
0
},
Err(e) => {
e.errno.as_retval()
}
} }
} }