From 8846c62b5ee0896dc4296db6903a74296a8cb56a Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Mon, 8 Apr 2019 23:27:40 +0800 Subject: [PATCH 01/10] Add uname --- src/libos/src/lib.rs | 1 + src/libos/src/misc/mod.rs | 5 ++++ src/libos/src/misc/uname.rs | 51 ++++++++++++++++++++++++++++++++++++ src/libos/src/prelude.rs | 1 + src/libos/src/syscall/mod.rs | 9 +++++++ test/Makefile | 2 +- test/uname/Makefile | 5 ++++ test/uname/main.c | 14 ++++++++++ 8 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/libos/src/misc/mod.rs create mode 100644 src/libos/src/misc/uname.rs create mode 100644 test/uname/Makefile create mode 100644 test/uname/main.c diff --git a/src/libos/src/lib.rs b/src/libos/src/lib.rs index 8b3a612d..95d5fe69 100644 --- a/src/libos/src/lib.rs +++ b/src/libos/src/lib.rs @@ -41,6 +41,7 @@ mod syscall; mod time; mod util; mod vm; +mod misc; use prelude::*; diff --git a/src/libos/src/misc/mod.rs b/src/libos/src/misc/mod.rs new file mode 100644 index 00000000..809e7467 --- /dev/null +++ b/src/libos/src/misc/mod.rs @@ -0,0 +1,5 @@ +use super::*; + +mod uname; + +pub use self::uname::{utsname_t, do_uname}; diff --git a/src/libos/src/misc/uname.rs b/src/libos/src/misc/uname.rs new file mode 100644 index 00000000..0b246f45 --- /dev/null +++ b/src/libos/src/misc/uname.rs @@ -0,0 +1,51 @@ +use super::*; +use std::ffi::{CStr, CString}; + +/// A sample of `struct utsname` +/// ``` +/// sysname = Linux +/// nodename = tian-nuc +/// release = 4.15.0-42-generic +/// version = #45~16.04.1-Ubuntu SMP Mon Nov 19 13:02:27 UTC 2018 +/// machine = x86_64 +/// domainname = (none) +/// ``` +/// +/// By the way, UTS stands for UNIX Timesharing System. +#[repr(C)] +#[derive(Copy, Clone)] +pub struct utsname_t { + sysname: [u8; 65], + nodename: [u8; 65], + release: [u8; 65], + version: [u8; 65], + machine: [u8; 65], + domainname: [u8; 65], +} + +pub fn do_uname(name: &mut utsname_t) -> Result<(), Error> { + copy_from_cstr_to_u8_array(&SYSNAME, &mut name.sysname); + copy_from_cstr_to_u8_array(&NODENAME, &mut name.nodename); + copy_from_cstr_to_u8_array(&RELEASE, &mut name.release); + copy_from_cstr_to_u8_array(&VERSION, &mut name.version); + copy_from_cstr_to_u8_array(&MACHINE, &mut name.machine); + copy_from_cstr_to_u8_array(&DOMAINNAME, &mut name.domainname); + Ok(()) +} + +lazy_static! { + static ref SYSNAME : CString = CString::new("Occlum").unwrap(); + static ref NODENAME: CString = CString::new("occlum-node").unwrap(); + static ref RELEASE: CString = CString::new("0.1").unwrap(); + static ref VERSION: CString = CString::new("0.1").unwrap(); + static ref MACHINE: CString = CString::new("x86-64").unwrap(); + static ref DOMAINNAME: CString = CString::new("").unwrap(); +} + +fn copy_from_cstr_to_u8_array(src: &CStr, dst: &mut [u8]) { + let src : &[u8] = src.to_bytes_with_nul(); + let len = min(dst.len() - 1, src.len()); + dst[..len].copy_from_slice(&src[..len]); + dst[len] = 0; +} + diff --git a/src/libos/src/prelude.rs b/src/libos/src/prelude.rs index 2c9708b3..a7057b07 100644 --- a/src/libos/src/prelude.rs +++ b/src/libos/src/prelude.rs @@ -21,6 +21,7 @@ pub use std::iter::Iterator; pub use std::rc::Rc; pub use std::string::String; pub use std::vec::Vec; +pub use std::cmp::{min, max}; pub use errno::Errno; pub use errno::Errno::*; diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index a0b392d8..76d41be8 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -8,6 +8,7 @@ use std::ptr; use time::timeval_t; use util::mem_util::from_user::*; use vm::{VMAreaFlags, VMResizeOptions}; +use misc::{utsname_t}; use super::*; @@ -131,6 +132,8 @@ pub extern "C" fn dispatch_syscall( SYS_GETTIMEOFDAY => do_gettimeofday(arg0 as *mut timeval_t), + SYS_UNAME => do_uname(arg0 as *mut utsname_t), + _ => do_unknown(num, arg0, arg1, arg2, arg3, arg4, arg5), }; debug!("syscall return: {:?}", ret); @@ -696,3 +699,9 @@ fn do_set_tid_address(tidptr: *mut pid_t) -> Result { check_mut_ptr(tidptr)?; process::do_set_tid_address(tidptr).map(|tid| tid as isize) } + +fn do_uname(name: *mut utsname_t) -> Result { + check_mut_ptr(name)?; + let name = unsafe { &mut *name }; + misc::do_uname(name).map(|_| 0) +} diff --git a/test/Makefile b/test/Makefile index 8d698b00..98152f6b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ PROJECT_DIR := $(realpath $(CUR_DIR)/../) # Dependencies: need to be compiled but not to run by any Makefile target TEST_DEPS := dev_null # Tests: need to be compiled and run by test-% target -TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link tls pthread +TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link tls pthread uname # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput diff --git a/test/uname/Makefile b/test/uname/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/uname/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/uname/main.c b/test/uname/main.c new file mode 100644 index 00000000..18d64e31 --- /dev/null +++ b/test/uname/main.c @@ -0,0 +1,14 @@ +#include +#include + +int main(void) { + struct utsname name; + uname(&name); + printf("sysname = %s\n", (const char*)&name.sysname); + printf("nodename = %s\n", (const char*)&name.nodename); + printf("release = %s\n", (const char*)&name.release); + printf("version = %s\n", (const char*)&name.version); + printf("machine = %s\n", (const char*)&name.machine); + printf("domainname = %s\n", (const char*)&name.__domainname); + return 0; +} From b003f4ce43c3a9cc2a14bd6df0b51a67e9e92728 Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Tue, 9 Apr 2019 20:39:50 +0800 Subject: [PATCH 02/10] Add prlimit64, getrlimit, and setrlimit --- src/libos/src/misc/mod.rs | 2 + src/libos/src/misc/rlimit.rs | 123 +++++++++++++++++++++++++ src/libos/src/process/mod.rs | 6 +- src/libos/src/process/process.rs | 6 ++ src/libos/src/process/process_table.rs | 6 +- src/libos/src/process/spawn/mod.rs | 4 +- src/libos/src/process/thread.rs | 3 +- src/libos/src/syscall/mod.rs | 27 +++++- test/Makefile | 2 +- test/rlimit/Makefile | 5 + test/rlimit/main.c | 15 +++ 11 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 src/libos/src/misc/rlimit.rs create mode 100644 test/rlimit/Makefile create mode 100644 test/rlimit/main.c diff --git a/src/libos/src/misc/mod.rs b/src/libos/src/misc/mod.rs index 809e7467..b6567883 100644 --- a/src/libos/src/misc/mod.rs +++ b/src/libos/src/misc/mod.rs @@ -1,5 +1,7 @@ use super::*; mod uname; +mod rlimit; pub use self::uname::{utsname_t, do_uname}; +pub use self::rlimit::{rlimit_t, resource_t, ResourceLimits, ResourceLimitsRef, do_prlimit}; diff --git a/src/libos/src/misc/rlimit.rs b/src/libos/src/misc/rlimit.rs new file mode 100644 index 00000000..f55acd6c --- /dev/null +++ b/src/libos/src/misc/rlimit.rs @@ -0,0 +1,123 @@ +use super::*; +use process::{pid_t}; + +#[derive(Debug, Copy, Clone)] +pub struct ResourceLimits { + rlimits: [rlimit_t; RLIMIT_COUNT], +} +pub type ResourceLimitsRef = Arc>; + +impl ResourceLimits { + pub fn get(&self, resource: resource_t) -> &rlimit_t { + &self.rlimits[resource as usize] + } + + pub fn get_mut(&mut self, resource: resource_t) -> &mut rlimit_t { + &mut self.rlimits[resource as usize] + } +} + +impl Default for ResourceLimits { + fn default() -> ResourceLimits { + // TODO: set appropriate limits for resources + let mut rlimits = ResourceLimits { + rlimits: [ Default::default(); RLIMIT_COUNT ], + }; + rlimits + } +} + + +#[derive(Debug, Copy, Clone)] +#[allow(non_camel_case_types)] +pub struct rlimit_t { + cur: u64, + max: u64, +} + +impl Default for rlimit_t { + fn default() -> rlimit_t { + rlimit_t { + cur: u64::max_value(), + max: u64::max_value(), + } + } +} + + +#[derive(Debug, Copy, Clone)] +#[allow(non_camel_case_types)] +pub enum resource_t { + RLIMIT_CPU = 0, + RLIMIT_FSIZE = 1, + RLIMIT_DATA = 2, + RLIMIT_STACK = 3, + RLIMIT_CORE = 4, + RLIMIT_RSS = 5, + RLIMIT_NPROC = 6, + RLIMIT_NOFILE = 7, + RLIMIT_MEMLOCK = 8, + RLIMIT_AS = 9, + RLIMIT_LOCKS = 10, + RLIMIT_SIGPENDING = 11, + RLIMIT_MSGQUEUE = 12, + RLIMIT_NICE = 13, + RLIMIT_RTPRIO = 14, +} +const RLIMIT_COUNT: usize = 15; + +impl resource_t { + pub fn from_u32(bits: u32) -> Result { + match bits { + 0 => Ok(resource_t::RLIMIT_CPU), + 1 => Ok(resource_t::RLIMIT_FSIZE), + 2 => Ok(resource_t::RLIMIT_DATA), + 3 => Ok(resource_t::RLIMIT_STACK), + 4 => Ok(resource_t::RLIMIT_CORE), + 5 => Ok(resource_t::RLIMIT_RSS), + 6 => Ok(resource_t::RLIMIT_NPROC), + 7 => Ok(resource_t::RLIMIT_NOFILE), + 8 => Ok(resource_t::RLIMIT_MEMLOCK), + 9 => Ok(resource_t::RLIMIT_AS), + 10 => Ok(resource_t::RLIMIT_LOCKS), + 11 => Ok(resource_t::RLIMIT_SIGPENDING), + 12 => Ok(resource_t::RLIMIT_MSGQUEUE), + 13 => Ok(resource_t::RLIMIT_NICE), + 14 => Ok(resource_t::RLIMIT_RTPRIO), + _ => errno!(EINVAL, "invalid resource"), + } + } +} + + +pub fn do_prlimit( + pid: pid_t, + resource: resource_t, + new_limit: Option<&rlimit_t>, + old_limit: Option<&mut rlimit_t>, +) -> Result<(), Error> { + let process_ref = if pid == 0 { + process::get_current() + } + else { + process::get(pid)? + }; + let mut process = process_ref.lock().unwrap(); + let rlimits_ref = process.get_rlimits(); + let mut rlimits = rlimits_ref.lock().unwrap(); + if let Some(old_limit) = old_limit { + *old_limit = *rlimits.get(resource) + } + if let Some(new_limit) = new_limit { + *rlimits.get_mut(resource) = *new_limit; + } + Ok(()) +} + +pub fn do_getrlimit(resource: resource_t, old_limit: &mut rlimit_t) -> Result<(), Error> { + do_prlimit(0 as pid_t, resource, None, Some(old_limit)) +} + +pub fn do_setrlimit(resource: resource_t, new_limit: &rlimit_t) -> Result<(), Error> { + do_prlimit(0 as pid_t, resource, Some(new_limit), None) +} diff --git a/src/libos/src/process/mod.rs b/src/libos/src/process/mod.rs index 8ec293ec..86c6017e 100644 --- a/src/libos/src/process/mod.rs +++ b/src/libos/src/process/mod.rs @@ -1,8 +1,6 @@ pub use self::process::{Status, IDLE_PROCESS}; pub use self::task::{get_current, run_task}; -pub mod table { - pub use super::process_table::get; -} +pub use self::process_table::{get}; pub use self::exit::{do_exit, do_wait4, ChildProcessFilter}; pub use self::spawn::{do_spawn, FileAction}; pub use self::wait::{WaitQueue, Waiter}; @@ -30,6 +28,7 @@ pub struct Process { waiting_children: Option>, vm: ProcessVMRef, file_table: FileTableRef, + rlimits: ResourceLimitsRef, } pub type ProcessRef = Arc>; @@ -79,3 +78,4 @@ use self::task::Task; use super::*; use fs::{File, FileRef, FileTable}; use vm::{ProcessVM, VMRangeTrait}; +use misc::{ResourceLimitsRef}; diff --git a/src/libos/src/process/process.rs b/src/libos/src/process/process.rs index e932b081..74260c18 100644 --- a/src/libos/src/process/process.rs +++ b/src/libos/src/process/process.rs @@ -20,6 +20,7 @@ lazy_static! { waiting_children: Default::default(), vm: Default::default(), file_table: Default::default(), + rlimits: Default::default(), })) }; } @@ -30,6 +31,7 @@ impl Process { task: Task, vm_ref: ProcessVMRef, file_table_ref: FileTableRef, + rlimits_ref: ResourceLimitsRef, ) -> Result<(pid_t, ProcessRef), Error> { let new_pid = process_table::alloc_pid(); let new_process_ref = Arc::new(SgxMutex::new(Process { @@ -46,6 +48,7 @@ impl Process { waiting_children: None, vm: vm_ref, file_table: file_table_ref, + rlimits: rlimits_ref, })); Ok((new_pid, new_process_ref)) } @@ -97,6 +100,9 @@ impl Process { self.cwd += path; } } + pub fn get_rlimits(&self) -> &ResourceLimitsRef { + &self.rlimits + } } impl Drop for Process { diff --git a/src/libos/src/process/process_table.rs b/src/libos/src/process/process_table.rs index fdda8d3e..ed800df7 100644 --- a/src/libos/src/process/process_table.rs +++ b/src/libos/src/process/process_table.rs @@ -14,8 +14,10 @@ pub fn remove(pid: pid_t) { PROCESS_TABLE.lock().unwrap().remove(&pid); } -pub fn get(pid: pid_t) -> Option { - PROCESS_TABLE.lock().unwrap().get(&pid).map(|pr| pr.clone()) +pub fn get(pid: pid_t) -> Result { + PROCESS_TABLE.lock().unwrap().get(&pid) + .map(|pr| pr.clone()) + .ok_or_else(|| Error::new(Errno::ENOENT, "process not found")) } static NEXT_PID: AtomicU32 = AtomicU32::new(1); diff --git a/src/libos/src/process/spawn/mod.rs b/src/libos/src/process/spawn/mod.rs index c32a7e4c..3dd58ec1 100644 --- a/src/libos/src/process/spawn/mod.rs +++ b/src/libos/src/process/spawn/mod.rs @@ -6,6 +6,7 @@ use std::ffi::{CStr, CString}; use std::path::Path; use std::sgxfs::SgxFile; use vm::{ProcessVM, VMRangeTrait}; +use misc::{ResourceLimitsRef}; use super::*; use super::task::Task; @@ -72,7 +73,8 @@ pub fn do_spawn>( let files = init_files(parent_ref, file_actions)?; Arc::new(SgxMutex::new(files)) }; - Process::new(&cwd, task, vm_ref, files_ref)? + let rlimits_ref = Default::default(); + Process::new(&cwd, task, vm_ref, files_ref, rlimits_ref)? }; parent_adopts_new_child(&parent_ref, &new_process_ref); process_table::put(new_pid, new_process_ref.clone()); diff --git a/src/libos/src/process/thread.rs b/src/libos/src/process/thread.rs index 8d8d925a..e28187d9 100644 --- a/src/libos/src/process/thread.rs +++ b/src/libos/src/process/thread.rs @@ -51,8 +51,9 @@ pub fn do_clone( let task = new_thread_task(stack_addr, new_tls)?; let vm_ref = current.get_vm().clone(); let files_ref = current.get_files().clone(); + let rlimits_ref = current.get_rlimits().clone(); let cwd = ¤t.cwd; - Process::new(cwd, task, vm_ref, files_ref)? + Process::new(cwd, task, vm_ref, files_ref, rlimits_ref)? }; if let Some(ctid) = ctid { diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 76d41be8..1e642086 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -8,7 +8,7 @@ use std::ptr; use time::timeval_t; use util::mem_util::from_user::*; use vm::{VMAreaFlags, VMResizeOptions}; -use misc::{utsname_t}; +use misc::{utsname_t, resource_t, rlimit_t}; use super::*; @@ -134,6 +134,8 @@ pub extern "C" fn dispatch_syscall( SYS_UNAME => do_uname(arg0 as *mut utsname_t), + SYS_PRLIMIT64 => do_prlimit(arg0 as pid_t, arg1 as u32, arg2 as *const rlimit_t, arg3 as *mut rlimit_t), + _ => do_unknown(num, arg0, arg1, arg2, arg3, arg4, arg5), }; debug!("syscall return: {:?}", ret); @@ -705,3 +707,26 @@ fn do_uname(name: *mut utsname_t) -> Result { let name = unsafe { &mut *name }; misc::do_uname(name).map(|_| 0) } + +fn do_prlimit(pid: pid_t, resource: u32, new_limit: *const rlimit_t, old_limit: *mut rlimit_t) -> Result { + let resource = resource_t::from_u32(resource)?; + let new_limit = { + if new_limit != ptr::null() { + check_ptr(new_limit)?; + Some(unsafe { &*new_limit }) + } + else { + None + } + }; + let old_limit = { + if old_limit != ptr::null_mut() { + check_mut_ptr(old_limit)?; + Some(unsafe { &mut *old_limit }) + } + else { + None + } + }; + misc::do_prlimit(pid, resource, new_limit, old_limit).map(|_| 0) +} diff --git a/test/Makefile b/test/Makefile index 98152f6b..f300e9ff 100644 --- a/test/Makefile +++ b/test/Makefile @@ -4,7 +4,7 @@ PROJECT_DIR := $(realpath $(CUR_DIR)/../) # Dependencies: need to be compiled but not to run by any Makefile target TEST_DEPS := dev_null # Tests: need to be compiled and run by test-% target -TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link tls pthread uname +TESTS := empty argv hello_world malloc file getpid spawn pipe time truncate readdir mkdir link tls pthread uname rlimit # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput diff --git a/test/rlimit/Makefile b/test/rlimit/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/rlimit/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/rlimit/main.c b/test/rlimit/main.c new file mode 100644 index 00000000..5e1943e5 --- /dev/null +++ b/test/rlimit/main.c @@ -0,0 +1,15 @@ +#include +#include + +int main(int argc, const char* argv[]) { + struct rlimit rlim; + if (getrlimit(RLIMIT_AS, &rlim) < 0) { + printf("ERROR: getrlimit failed\n"); + return -1; + } + if (setrlimit(RLIMIT_AS, &rlim) < 0) { + printf("ERROR: getrlimit failed\n"); + return -1; + } + return 0; +} From d7e266d0e3a2f3fc242bbd963a8569ebb8375bfa Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Tue, 9 Apr 2019 23:01:10 +0800 Subject: [PATCH 03/10] Add access and faccessat --- src/libos/src/fs/access.rs | 52 ++++++++++++++++++++++++++++++++++++ src/libos/src/fs/mod.rs | 2 ++ src/libos/src/syscall/mod.rs | 25 +++++++++++++++-- test/truncate/main.c | 5 ++++ 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/libos/src/fs/access.rs diff --git a/src/libos/src/fs/access.rs b/src/libos/src/fs/access.rs new file mode 100644 index 00000000..e2910a73 --- /dev/null +++ b/src/libos/src/fs/access.rs @@ -0,0 +1,52 @@ +use super::*; + +//int faccessat(int dirfd, const char *pathname, int mode, int flags); +//int access(const char *pathname, int mode); + +bitflags! { + pub struct AccessModes : u32 { + const X_OK = 1; + const W_OK = 2; + const R_OK = 4; + } +} + +impl AccessModes { + pub fn from_u32(bits: u32) -> Result { + AccessModes::from_bits(bits).ok_or_else(|| Error::new(Errno::EINVAL, "invalid mode")) + } +} + + +bitflags! { + pub struct AccessFlags : u32 { + const AT_SYMLINK_NOFOLLOW = 0x100; + const AT_EACCESS = 0x200; + } +} + +impl AccessFlags { + pub fn from_u32(bits: u32) -> Result { + AccessFlags::from_bits(bits).ok_or_else(|| Error::new(Errno::EINVAL, "invalid flags")) + } +} + + +pub const AT_FDCWD : i32 = -100; + +pub fn do_faccessat(dirfd: Option, path: &str, mode: AccessModes, flags: AccessFlags) -> Result<(), Error> { + match dirfd { + // TODO: handle dirfd + Some(dirfd) => errno!(ENOSYS, "cannot accept dirfd"), + None => do_access(path, mode), + } +} + +pub fn do_access(path: &str, mode: AccessModes) -> Result<(), Error> { + let current_ref = process::get_current(); + let mut current = current_ref.lock().unwrap(); + let inode = current.lookup_inode(path)?; + //let metadata = inode.get_metadata(); + // TODO: check metadata.mode with mode + Ok(()) +} diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 162f7062..8142f23a 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -11,12 +11,14 @@ pub use self::file_table::{FileDesc, FileTable}; pub use self::inode_file::{INodeExt, INodeFile, ROOT_INODE}; use self::inode_file::OpenOptions; pub use self::pipe::Pipe; +pub use self::access::{AccessModes, AccessFlags, AT_FDCWD, do_access, do_faccessat}; mod file; mod file_table; mod inode_file; mod pipe; mod sgx_impl; +mod access; // TODO: use the type defined in Rust libc. // diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 1e642086..af0319a3 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -1,6 +1,5 @@ use {fs, process, std, vm}; -use fs::{FileDesc, off_t}; -use fs::File; +use fs::{File, FileDesc, off_t, AccessModes, AccessFlags, AT_FDCWD}; use prelude::*; use process::{ChildProcessFilter, FileAction, pid_t, CloneFlags, FutexFlags, FutexOp}; use std::ffi::{CStr, CString}; @@ -57,6 +56,8 @@ pub extern "C" fn dispatch_syscall( SYS_STAT => do_stat(arg0 as *const i8, arg1 as *mut fs::Stat), SYS_FSTAT => do_fstat(arg0 as FileDesc, arg1 as *mut fs::Stat), SYS_LSTAT => do_lstat(arg0 as *const i8, arg1 as *mut fs::Stat), + SYS_ACCESS => do_access(arg0 as *const i8, arg1 as u32), + SYS_FACCESSAT => do_faccessat(arg0 as i32, arg1 as *const i8, arg2 as u32, arg3 as u32), SYS_LSEEK => do_lseek(arg0 as FileDesc, arg1 as off_t, arg2 as i32), SYS_FSYNC => do_fsync(arg0 as FileDesc), SYS_FDATASYNC => do_fdatasync(arg0 as FileDesc), @@ -730,3 +731,23 @@ fn do_prlimit(pid: pid_t, resource: u32, new_limit: *const rlimit_t, old_limit: }; misc::do_prlimit(pid, resource, new_limit, old_limit).map(|_| 0) } + +fn do_access(path: *const i8, mode: u32) -> Result { + let path = clone_cstring_safely(path)?.to_string_lossy().into_owned(); + let mode = AccessModes::from_u32(mode)?; + fs::do_access(&path, mode).map(|_| 0) +} + +fn do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32) -> Result { + let dirfd = if dirfd >= 0 { + Some(dirfd as FileDesc) + } else if dirfd == AT_FDCWD { + None + } else { + return errno!(EINVAL, "invalid dirfd"); + }; + let path = clone_cstring_safely(path)?.to_string_lossy().into_owned(); + let mode = AccessModes::from_u32(mode)?; + let flags = AccessFlags::from_u32(flags)?; + fs::do_faccessat(dirfd, &path, mode, flags).map(|_| 0) +} diff --git a/test/truncate/main.c b/test/truncate/main.c index 021fe3a8..36a3d45a 100644 --- a/test/truncate/main.c +++ b/test/truncate/main.c @@ -21,6 +21,11 @@ int main(int argc, const char* argv[]) { return fd; } + if (access(FILE_NAME, F_OK) < 0) { + printf("cannot access the new file\n"); + return -1; + } + ret = ftruncate(fd, TRUNC_LEN); if (ret < 0) { printf("failed to ftruncate the file\n"); From e335e8df1d068ee089bd29b67ee4ae629a293ecd Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Tue, 9 Apr 2019 23:54:31 +0800 Subject: [PATCH 04/10] Add sigaction and sigprocmask (but not implemented) --- src/libos/src/syscall/mod.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index af0319a3..139cd4cc 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -82,10 +82,14 @@ pub extern "C" fn dispatch_syscall( arg4 as *const FdOp, ), SYS_WAIT4 => do_wait4(arg0 as i32, arg1 as *mut i32), + SYS_GETPID => do_getpid(), SYS_GETTID => do_gettid(), SYS_GETPPID => do_getppid(), + SYS_RT_SIGACTION => do_rt_sigaction(), + SYS_RT_SIGPROCMASK => do_rt_sigprocmask(), + SYS_CLONE => do_clone( arg0 as u32, arg1 as usize, @@ -751,3 +755,13 @@ fn do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32) -> Result Result { + Ok(0) +} + +fn do_rt_sigprocmask() -> Result { + Ok(0) +} From 785d3237b91c7fb4e43da2fdcb26119393d283ab Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Wed, 10 Apr 2019 00:13:57 +0800 Subject: [PATCH 05/10] Add getuid, getgid, geteuid, getegid, etc. --- src/libos/src/process/process.rs | 4 ++-- src/libos/src/process/thread.rs | 2 ++ src/libos/src/syscall/mod.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/libos/src/process/process.rs b/src/libos/src/process/process.rs index 74260c18..fbfeffd3 100644 --- a/src/libos/src/process/process.rs +++ b/src/libos/src/process/process.rs @@ -10,7 +10,7 @@ lazy_static! { task: Default::default(), status: Default::default(), pid: 0, - pgid: 0, + pgid: 1, tgid: 0, exit_status: 0, cwd: "/".to_owned(), @@ -38,7 +38,7 @@ impl Process { task: task, status: Default::default(), pid: new_pid, - pgid: new_pid, + pgid: 1, // TODO: implement pgid tgid: new_pid, cwd: cwd.to_owned(), clear_child_tid: None, diff --git a/src/libos/src/process/thread.rs b/src/libos/src/process/thread.rs index e28187d9..31837341 100644 --- a/src/libos/src/process/thread.rs +++ b/src/libos/src/process/thread.rs @@ -68,6 +68,8 @@ pub fn do_clone( let mut new_thread = new_thread_ref.lock().unwrap(); parent.children.push(Arc::downgrade(&new_thread_ref)); new_thread.parent = Some(parent_ref.clone()); + + new_thread.tgid = current.tgid; } process_table::put(new_thread_pid, new_thread_ref.clone()); diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 139cd4cc..7944ce36 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -86,6 +86,12 @@ pub extern "C" fn dispatch_syscall( SYS_GETPID => do_getpid(), SYS_GETTID => do_gettid(), SYS_GETPPID => do_getppid(), + SYS_GETPGID => do_getpgid(), + + SYS_GETUID => do_getuid(), + SYS_GETGID => do_getgid(), + SYS_GETEUID => do_geteuid(), + SYS_GETEGID => do_getegid(), SYS_RT_SIGACTION => do_rt_sigaction(), SYS_RT_SIGPROCMASK => do_rt_sigprocmask(), @@ -577,6 +583,30 @@ fn do_getppid() -> Result { Ok(ppid as isize) } +fn do_getpgid() -> Result { + let pgid = process::do_getpgid(); + Ok(pgid as isize) +} + +// TODO: implement uid, gid, euid, egid + +fn do_getuid() -> Result { + Ok(0) +} + +fn do_getgid() -> Result { + Ok(0) +} + +fn do_geteuid() -> Result { + Ok(0) +} + +fn do_getegid() -> Result { + Ok(0) +} + + fn do_pipe2(fds_u: *mut i32, flags: u32) -> Result { check_mut_array(fds_u, 2)?; // TODO: how to deal with open flags??? From 44ef19726f52f8e065321c7f897a2ea42eed3811 Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Wed, 10 Apr 2019 14:45:51 +0800 Subject: [PATCH 06/10] Add fcntl --- src/libos/src/fs/file_table.rs | 79 ++++++++++++++++++++++++++++---- src/libos/src/fs/mod.rs | 83 +++++++++++++++++++++++++++++++++- src/libos/src/syscall/mod.rs | 8 +++- 3 files changed, 158 insertions(+), 12 deletions(-) diff --git a/src/libos/src/fs/file_table.rs b/src/libos/src/fs/file_table.rs index 14f54241..0e60c108 100644 --- a/src/libos/src/fs/file_table.rs +++ b/src/libos/src/fs/file_table.rs @@ -11,12 +11,6 @@ pub struct FileTable { num_fds: usize, } -#[derive(Debug, Clone)] -struct FileTableEntry { - file: FileRef, - close_on_spawn: bool, -} - impl FileTable { pub fn new() -> FileTable { FileTable { @@ -25,12 +19,38 @@ impl FileTable { } } + pub fn dup(&mut self, fd: FileDesc, min_fd: FileDesc, close_on_spawn: bool) -> Result { + let file_ref = self.get(fd)?; + + let min_fd = min_fd as usize; + let min_free_fd = { + let mut table = &mut self.table; + + // Make sure that min_fd does not exceed the capacity of the table + if min_fd >= table.len() { + let expand_size = min_fd - table.len() + 1; + for _ in 0..expand_size { + table.push(None) + } + } + + table.iter() + .enumerate() + .skip(min_fd as usize) + .find(|&(idx, opt)| opt.is_none()) + .unwrap().0 + } as FileDesc; + + self.put_at(min_free_fd, file_ref, close_on_spawn); + + Ok(min_free_fd) + } + pub fn put(&mut self, file: FileRef, close_on_spawn: bool) -> FileDesc { let mut table = &mut self.table; let min_free_fd = if self.num_fds < table.len() { - table - .iter() + table.iter() .enumerate() .find(|&(idx, opt)| opt.is_none()) .unwrap() @@ -59,13 +79,30 @@ impl FileTable { } pub fn get(&self, fd: FileDesc) -> Result { + let entry = self.get_entry(fd)?; + Ok(entry.file.clone()) + } + + pub fn get_entry(&self, fd: FileDesc) -> Result<&FileTableEntry, Error> { if fd as usize >= self.table.len() { return errno!(EBADF, "Invalid file descriptor"); } let table = &self.table; match table[fd as usize].as_ref() { - Some(table_entry) => Ok(table_entry.file.clone()), + Some(table_entry) => Ok(table_entry), + None => errno!(EBADF, "Invalid file descriptor"), + } + } + + pub fn get_entry_mut(&mut self, fd: FileDesc) -> Result<&mut FileTableEntry, Error> { + if fd as usize >= self.table.len() { + return errno!(EBADF, "Invalid file descriptor"); + } + + let table = &mut self.table; + match table[fd as usize].as_mut() { + Some(table_entry) => Ok(table_entry), None => errno!(EBADF, "Invalid file descriptor"), } } @@ -114,11 +151,33 @@ impl Clone for FileTable { } } +#[derive(Debug, Clone)] +pub struct FileTableEntry { + file: FileRef, + close_on_spawn: bool, +} + impl FileTableEntry { - fn new(file: FileRef, close_on_spawn: bool) -> FileTableEntry { + pub fn new(file: FileRef, close_on_spawn: bool) -> FileTableEntry { FileTableEntry { file, close_on_spawn, } } + + pub fn get_file(&self) -> &FileRef { + &self.file + } + + pub fn is_close_on_spawn(&self) -> bool { + self.close_on_spawn + } + + pub fn get_file_mut(&mut self) -> &mut FileRef { + &mut self.file + } + + pub fn set_close_on_spawn(&mut self, close_on_spawn: bool) { + self.close_on_spawn = close_on_spawn; + } } diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 8142f23a..2cddb5a3 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -390,7 +390,7 @@ fn split_path(path: &str) -> (&str, &str) { } bitflags! { - struct OpenFlags: u32 { + pub struct OpenFlags: u32 { /// read only const RDONLY = 0; /// write only @@ -614,3 +614,84 @@ pub unsafe fn write_cstr(ptr: *mut u8, s: &str) { ptr.copy_from(s.as_ptr(), s.len()); ptr.add(s.len()).write(0); } + + +#[derive(Debug)] +pub enum FcntlCmd { + /// Duplicate the file descriptor fd using the lowest-numbered available + /// file descriptor greater than or equal to arg. + DupFd(FileDesc), + /// As for `DupFd`, but additionally set the close-on-exec flag for the + /// duplicate file descriptor. + DupFdCloexec(FileDesc), + /// Return (as the function result) the file descriptor flags + GetFd(), + /// Set the file descriptor to be close-on-exec or not + SetFd(u32), + /// Get the file status flags + GetFl(), + /// Set the file status flags + SetFl(OpenFlags), +} + +pub const F_DUPFD : u32 = 0; +pub const F_GETFD : u32 = 1; +pub const F_SETFD : u32 = 2; +pub const F_GETFL : u32 = 3; +pub const F_SETFL : u32 = 4; +pub const F_DUPFD_CLOEXEC : u32 = 1030; + +pub const FD_CLOEXEC : u32 = 1; + +impl FcntlCmd { + #[deny(unreachable_patterns)] + pub fn from_raw(cmd: u32, arg: u64) -> Result { + Ok(match cmd { + F_DUPFD => FcntlCmd::DupFd(arg as FileDesc), + F_DUPFD_CLOEXEC => FcntlCmd::DupFdCloexec(arg as FileDesc), + F_GETFD => FcntlCmd::GetFd(), + F_SETFD => FcntlCmd::SetFd(arg as u32), + F_GETFL => FcntlCmd::GetFl(), + F_SETFL => FcntlCmd::SetFl(OpenFlags::from_bits_truncate(arg as u32)), + _ => return errno!(EINVAL, "invalid command"), + }) + } +} + +pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result { + info!("do_fcntl: {:?}, {:?}", &fd, cmd); + let current_ref = process::get_current(); + let mut current = current_ref.lock().unwrap(); + let files_ref = current.get_files(); + let mut files = files_ref.lock().unwrap(); + Ok(match cmd { + FcntlCmd::DupFd(min_fd) => { + let dup_fd = files.dup(fd, *min_fd, false)?; + dup_fd as isize + }, + FcntlCmd::DupFdCloexec(min_fd) => { + let dup_fd = files.dup(fd, *min_fd, true)?; + dup_fd as isize + }, + FcntlCmd::GetFd() => { + let entry = files.get_entry(fd)?; + let fd_flags = if entry.is_close_on_spawn() { + FD_CLOEXEC + } else { + 0 + }; + fd_flags as isize + }, + FcntlCmd::SetFd(fd_flags) => { + let entry = files.get_entry_mut(fd)?; + entry.set_close_on_spawn((fd_flags & FD_CLOEXEC) != 0); + 0 + }, + FcntlCmd::GetFl() => { + unimplemented!(); + }, + FcntlCmd::SetFl(flags) => { + unimplemented!(); + }, + }) +} diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 7944ce36..0cd25ab4 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -1,5 +1,5 @@ use {fs, process, std, vm}; -use fs::{File, FileDesc, off_t, AccessModes, AccessFlags, AT_FDCWD}; +use fs::{File, FileDesc, off_t, AccessModes, AccessFlags, AT_FDCWD, FcntlCmd}; use prelude::*; use process::{ChildProcessFilter, FileAction, pid_t, CloneFlags, FutexFlags, FutexOp}; use std::ffi::{CStr, CString}; @@ -72,6 +72,7 @@ pub extern "C" fn dispatch_syscall( SYS_RMDIR => do_rmdir(arg0 as *const i8), SYS_LINK => do_link(arg0 as *const i8, arg1 as *const i8), SYS_UNLINK => do_unlink(arg0 as *const i8), + SYS_FCNTL => do_fcntl(arg0 as FileDesc, arg1 as u32, arg2 as u64), SYS_EXIT => do_exit(arg0 as i32), SYS_SPAWN => do_spawn( @@ -726,6 +727,11 @@ fn do_unlink(path: *const i8) -> Result { Ok(0) } +fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result { + let cmd = FcntlCmd::from_raw(cmd, arg)?; + fs::do_fcntl(fd, &cmd) +} + fn do_arch_prctl(code: u32, addr: *mut usize) -> Result { let code = process::ArchPrctlCode::from_u32(code)?; check_mut_ptr(addr)?; From 356884679bc5e4d257152d1f930713510bfb5853 Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Wed, 10 Apr 2019 14:58:00 +0800 Subject: [PATCH 07/10] Add readlink --- src/libos/src/fs/mod.rs | 5 +++++ src/libos/src/syscall/mod.rs | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 2cddb5a3..c9a731ea 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -695,3 +695,8 @@ pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result { }, }) } + +pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result { + // TODO: support symbolic links + errno!(EINVAL, "not a symbolic link") +} diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 0cd25ab4..852f6862 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -72,6 +72,7 @@ pub extern "C" fn dispatch_syscall( SYS_RMDIR => do_rmdir(arg0 as *const i8), SYS_LINK => do_link(arg0 as *const i8, arg1 as *const i8), SYS_UNLINK => do_unlink(arg0 as *const i8), + SYS_READLINK => do_readlink(arg0 as *const i8, arg1 as *mut u8, arg2 as usize), SYS_FCNTL => do_fcntl(arg0 as FileDesc, arg1 as u32, arg2 as u64), SYS_EXIT => do_exit(arg0 as i32), @@ -727,6 +728,16 @@ fn do_unlink(path: *const i8) -> Result { Ok(0) } +fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result { + let path = clone_cstring_safely(path)?.to_string_lossy().into_owned(); + let buf = { + check_array(buf, size)?; + unsafe { std::slice::from_raw_parts_mut(buf, size) } + }; + let len = fs::do_readlink(&path, buf)?; + Ok(len as isize) +} + fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result { let cmd = FcntlCmd::from_raw(cmd, arg)?; fs::do_fcntl(fd, &cmd) From 895f70a2dc1a100cec5ce047df61ff86f78a62ad Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Wed, 10 Apr 2019 18:50:34 +0800 Subject: [PATCH 08/10] Refactor VMRange allocation for readability --- src/libos/src/vm/mod.rs | 46 +++---- src/libos/src/vm/process_vm.rs | 10 +- src/libos/src/vm/vm_range.rs | 240 +++++++++++++++++++-------------- 3 files changed, 168 insertions(+), 128 deletions(-) diff --git a/src/libos/src/vm/mod.rs b/src/libos/src/vm/mod.rs index db1500ab..b9d03cc8 100644 --- a/src/libos/src/vm/mod.rs +++ b/src/libos/src/vm/mod.rs @@ -101,11 +101,11 @@ impl VMAreaFlags { } } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Default)] pub struct VMAllocOptions { size: usize, addr: VMAddrOption, - growth: Option, + growth: VMGrowthType, } impl VMAllocOptions { @@ -128,7 +128,7 @@ impl VMAllocOptions { } pub fn growth(&mut self, growth: VMGrowthType) -> Result<&mut Self, Error> { - self.growth = Some(growth); + self.growth = growth; Ok(self) } } @@ -143,15 +143,6 @@ impl fmt::Debug for VMAllocOptions { } } -impl Default for VMAllocOptions { - fn default() -> VMAllocOptions { - VMAllocOptions { - size: 0, - addr: VMAddrOption::Any, - growth: None, - } - } -} #[derive(Clone, Copy, Debug, PartialEq)] pub enum VMAddrOption { @@ -161,6 +152,12 @@ pub enum VMAddrOption { Beyond(usize), // Must be greater or equal to the given address } +impl Default for VMAddrOption { + fn default() -> VMAddrOption { + VMAddrOption::Any + } +} + impl VMAddrOption { pub fn is_addr_given(&self) -> bool { match self { @@ -179,18 +176,26 @@ impl VMAddrOption { } } + /// How VMRange may grow: #[derive(Clone, Copy, Debug, PartialEq)] pub enum VMGrowthType { + Fixed, Upward, // e.g., mmaped regions grow upward Downward, // e.g., stacks grows downward - Fixed, } -#[derive(Clone, Debug)] +impl Default for VMGrowthType { + fn default() -> VMGrowthType { + VMGrowthType::Fixed + } +} + + +#[derive(Clone, Debug, Default)] pub struct VMResizeOptions { new_size: usize, - new_addr: Option, + new_addr: VMAddrOption, } impl VMResizeOptions { @@ -205,16 +210,7 @@ impl VMResizeOptions { } pub fn addr(&mut self, new_addr: VMAddrOption) -> &mut Self { - self.new_addr = Some(new_addr); + self.new_addr = new_addr; self } } - -impl Default for VMResizeOptions { - fn default() -> VMResizeOptions { - VMResizeOptions { - new_size: 0, - new_addr: None, - } - } -} diff --git a/src/libos/src/vm/process_vm.rs b/src/libos/src/vm/process_vm.rs index f9043dfa..126ddf41 100644 --- a/src/libos/src/vm/process_vm.rs +++ b/src/libos/src/vm/process_vm.rs @@ -156,7 +156,8 @@ impl ProcessVM { if addr < mmap_start_addr { return Err(Error::new(Errno::EINVAL, "Beyond valid memory range")); } - VMAddrOption::Fixed(addr) + // TODO: Fixed or Hint? Should hanle mmap flags + VMAddrOption::Hint(addr) })? .growth(VMGrowthType::Upward)?; alloc_options @@ -168,11 +169,10 @@ impl ProcessVM { Ok(addr) } + // TODO: handle the case when the given range [addr, addr + size) + // does not match exactly with any vma. For example, when this range + // cover multiple ranges or cover some range partially. pub fn munmap(&mut self, addr: usize, size: usize) -> Result<(), Error> { - // TODO: handle the case when the given range [addr, addr + size) - // does not match exactly with any vma. For example, when this range - // cover multiple ranges or cover some range partially. - let mmap_vma_i = { let mmap_vma_i = self .get_mmap_vmas() diff --git a/src/libos/src/vm/vm_range.rs b/src/libos/src/vm/vm_range.rs index c2bfdce4..0a71486d 100644 --- a/src/libos/src/vm/vm_range.rs +++ b/src/libos/src/vm/vm_range.rs @@ -60,107 +60,20 @@ impl VMRange { } pub fn alloc_subrange(&mut self, options: &VMAllocOptions) -> Result { - // Get valid parameters from options - let size = options.size; - let addr = options.addr; - let growth = options.growth.unwrap_or(VMGrowthType::Fixed); + // Find a free space that satisfies the options + let free_space = self.look_for_free_space(options)?; - // Lazy initialize the subrange array upon the first allocation - if !self.has_subranges() { - self.init_subranges()?; - } + let (new_subrange_start, new_subrange_end) = + self.alloc_from_free_space(&free_space, options); + debug_assert!(free_space.contains(new_subrange_start)); + debug_assert!(free_space.contains(new_subrange_end)); - // Find a free space for allocating a VMRange - let free_space = { - // Look for the minimal big-enough free space - let mut min_big_enough_free_space: Option = None; - let sub_ranges = self.get_subranges(); - for (idx, range_pair) in sub_ranges.windows(2).enumerate() { - let pre_range = &range_pair[0]; - let next_range = &range_pair[1]; - - let mut free_range = { - let free_range_start = pre_range.get_end(); - let free_range_end = next_range.get_start(); - - let free_range_size = free_range_end - free_range_start; - if free_range_size < size { - continue; - } - - free_range_start..free_range_end - }; - - match addr { - VMAddrOption::Hint(addr) | VMAddrOption::Fixed(addr) => { - if !free_range.contains(&addr) { - continue; - } - free_range.start = addr; - } - VMAddrOption::Beyond(addr) => { - if free_range.start < addr { - continue; - } - } - _ => {} - } - - let free_space = Some(FreeSpace { - index_in_subranges: idx + 1, - start: free_range.start, - end: free_range.end, - may_neighbor_grow: ( - pre_range.growth == VMGrowthType::Upward, - next_range.growth == VMGrowthType::Downward, - ), - }); - - if min_big_enough_free_space == None || free_space < min_big_enough_free_space { - min_big_enough_free_space = free_space; - - match addr { - VMAddrOption::Hint(addr) | VMAddrOption::Fixed(addr) => break, - _ => {} - } - } - } - - if min_big_enough_free_space.is_none() { - return errno!(ENOMEM, "No enough space"); - } - min_big_enough_free_space.unwrap() - }; - - // Given the free space, determine the start and end of the sub-range - let (new_subrange_start, new_subrange_end) = match addr { - VMAddrOption::Any | VMAddrOption::Beyond(_) => { - let should_no_gap_to_pre_domain = - free_space.may_neighbor_grow.0 == false && growth != VMGrowthType::Downward; - let should_no_gap_to_next_domain = - free_space.may_neighbor_grow.1 == false && growth != VMGrowthType::Upward; - let domain_start = if should_no_gap_to_pre_domain { - free_space.start - } else if should_no_gap_to_next_domain { - free_space.end - size - } else { - // We want to leave some space at both ends in case - // this sub-range or neighbor sub-range needs to grow later. - // As a simple heuristic, we put this sub-range near the - // center between the previous and next sub-ranges. - free_space.start + (free_space.get_size() - size) / 2 - }; - (domain_start, domain_start + size) - } - VMAddrOption::Fixed(addr) => (addr, addr + size), - VMAddrOption::Hint(addr) => { - return errno!(EINVAL, "Not implemented"); - } - }; - - let new_subrange_inner = VMRangeInner::new(new_subrange_start, new_subrange_end, growth); + let new_subrange_inner = VMRangeInner::new(new_subrange_start, + new_subrange_end, + options.growth); self.get_subranges_mut() .insert(free_space.index_in_subranges, new_subrange_inner); + // Although there are two copies of the newly created VMRangeInner obj, // we can keep them in sync as all mutation on VMRange object must // be carried out through dealloc_subrange() and resize_subrange() that @@ -223,7 +136,7 @@ impl VMRange { } } - fn init_subranges(&mut self) -> Result<(), Error> { + fn init_subrange_array(&mut self) -> Result<(), Error> { // Use dummy VMRange as sentinel object at both ends to make the allocation // and deallocation algorithm simpler let start = self.get_start(); @@ -234,6 +147,132 @@ impl VMRange { Ok(()) } + // Find a free space for allocating a sub VMRange + fn look_for_free_space(&mut self, options: &VMAllocOptions) -> Result { + // TODO: reduce the complexity from O(N) to O(log(N)), where N is + // the number of existing subranges. + + // Lazy initialize the subrange array upon the first allocation + if !self.has_subranges() { + self.init_subrange_array()?; + } + + // Get valid parameters from options + let size = options.size; + let addr = options.addr; + let growth = options.growth; + + // Record the minimal free space that satisfies the options + let mut min_big_enough_free_space: Option = None; + + let sub_ranges = self.get_subranges(); + for (idx, range_pair) in sub_ranges.windows(2).enumerate() { + let pre_range = &range_pair[0]; + let next_range = &range_pair[1]; + + let (free_range_start, free_range_end)= { + let free_range_start = pre_range.get_end(); + let free_range_end = next_range.get_start(); + + let free_range_size = free_range_end - free_range_start; + if free_range_size < size { + continue; + } + + (free_range_start, free_range_end) + }; + let mut free_space = FreeSpace { + index_in_subranges: idx + 1, + start: free_range_start, + end: free_range_end, + may_neighbor_grow: ( + pre_range.growth == VMGrowthType::Upward, + next_range.growth == VMGrowthType::Downward, + ), + }; + + match addr { + // Want a minimal free_space + VMAddrOption::Any => { }, + // Prefer to have free_space.start == addr + VMAddrOption::Hint(addr) => { + if free_space.contains(addr) { + if free_space.end - addr >= size { + free_space.start = addr; + return Ok(free_space); + } + } + }, + // Must have free_space.start == addr + VMAddrOption::Fixed(addr) => { + if !free_space.contains(addr) { + continue; + } + if free_space.end - addr < size { + return errno!(ENOMEM, "not enough memory"); + } + free_space.start = addr; + return Ok(free_space); + } + // Must have free_space.start >= addr + VMAddrOption::Beyond(addr) => { + if free_space.end < addr { + continue; + } + if free_space.contains(addr) { + free_space.start = addr; + if free_space.get_size() < size { + continue; + } + } + }, + } + + if min_big_enough_free_space == None || + free_space < *min_big_enough_free_space.as_ref().unwrap() { + min_big_enough_free_space = Some(free_space); + } + } + + min_big_enough_free_space + .ok_or_else(|| Error::new(Errno::ENOMEM, "not enough space")) + } + + fn alloc_from_free_space(&self, free_space: &FreeSpace, options: &VMAllocOptions) -> (usize, usize) { + // Get valid parameters from options + let size = options.size; + let addr_option = options.addr; + let growth = options.growth; + + if let VMAddrOption::Fixed(addr) = addr_option { + return (addr, addr + size); + } + else if let VMAddrOption::Hint(addr) = addr_option { + if free_space.start == addr { + return (addr, addr + size); + } + } + + let should_no_gap_to_pre_domain = + free_space.may_neighbor_grow.0 == false && growth != VMGrowthType::Downward; + let should_no_gap_to_next_domain = + free_space.may_neighbor_grow.1 == false && growth != VMGrowthType::Upward; + + let addr = if should_no_gap_to_pre_domain { + free_space.start + } else if should_no_gap_to_next_domain { + free_space.end - size + } else { + // We want to leave some space at both ends in case + // this sub-range or neighbor sub-range needs to grow later. + // As a simple heuristic, we put this sub-range near the + // center between the previous and next sub-ranges. + free_space.start + (free_space.get_size() - size) / 2 + }; + + (addr, addr + size) + } + fn ensure_subrange_is_a_child(&self, subrange: &VMRange) { // FIXME: /*if subrange.parent_range != self as *const VMRange { @@ -478,9 +517,14 @@ impl FreeSpace { pressure += if self.may_neighbor_grow.1 { 1 } else { 0 }; pressure } + fn get_size(&self) -> usize { self.end - self.start } + + fn contains(&self, addr: usize) -> bool { + self.start <= addr && addr < self.end + } } impl PartialEq for FreeSpace { From 862601604c92d57cd6b028f1d23e09c7e0496e24 Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Thu, 11 Apr 2019 18:26:40 +0800 Subject: [PATCH 09/10] Refactor VMRange code and fix two bugs The first bug is that a VMRange may not be allocated to a 4KB-aligned address. The second bug is that a VMRange may not be deallocated by its parent VMRange. --- src/libos/src/prelude.rs | 4 + src/libos/src/vm/mod.rs | 55 +++--------- src/libos/src/vm/process_vm.rs | 147 +++++++++++++++++++-------------- src/libos/src/vm/vm_area.rs | 125 +++++++++++++++++++++++++++- src/libos/src/vm/vm_domain.rs | 41 --------- src/libos/src/vm/vm_range.rs | 110 ++++++++++-------------- src/libos/src/vm/vm_space.rs | 33 -------- 7 files changed, 266 insertions(+), 249 deletions(-) delete mode 100644 src/libos/src/vm/vm_domain.rs delete mode 100644 src/libos/src/vm/vm_space.rs diff --git a/src/libos/src/prelude.rs b/src/libos/src/prelude.rs index a7057b07..6a124725 100644 --- a/src/libos/src/prelude.rs +++ b/src/libos/src/prelude.rs @@ -55,3 +55,7 @@ pub fn align_up(addr: usize, align: usize) -> usize { pub fn align_down(addr: usize, align: usize) -> usize { addr & !(align - 1) } + +pub fn unbox(value: Box) -> T { + *value +} diff --git a/src/libos/src/vm/mod.rs b/src/libos/src/vm/mod.rs index b9d03cc8..4ec6b4b8 100644 --- a/src/libos/src/vm/mod.rs +++ b/src/libos/src/vm/mod.rs @@ -3,17 +3,15 @@ use prelude::*; use process::{get_current, Process, ProcessRef}; use std::fmt; -// TODO: Rename VMSpace to VMUniverse - #[macro_use] mod vm_range; -mod process_vm; mod vm_area; -mod vm_domain; -mod vm_space; +mod process_vm; -pub use self::process_vm::ProcessVM; pub use self::vm_range::{VMRange, VMRangeTrait}; +pub use self::vm_area::{VMSpace, VMDomain, VMArea, VMAreaFlags, VM_AREA_FLAG_R, VM_AREA_FLAG_W, VM_AREA_FLAG_X}; +pub use self::process_vm::ProcessVM; + // TODO: separate proc and flags // TODO: accept fd and offset @@ -56,23 +54,6 @@ pub fn do_brk(addr: usize) -> Result { pub const PAGE_SIZE: usize = 4096; -#[derive(Debug)] -pub struct VMSpace { - range: VMRange, - guard_type: VMGuardAreaType, -} - -#[derive(Debug, Default)] -pub struct VMDomain { - range: VMRange, -} - -#[derive(Debug, Default)] -pub struct VMArea { - range: VMRange, - flags: VMAreaFlags, -} - #[derive(Clone, Copy, Debug, PartialEq)] pub enum VMGuardAreaType { None, @@ -80,32 +61,13 @@ pub enum VMGuardAreaType { Dynamic { size: usize }, } -#[derive(Copy, Clone, Debug, Default, PartialEq)] -pub struct VMAreaFlags(pub u32); -pub const VM_AREA_FLAG_R: u32 = 0x1; -pub const VM_AREA_FLAG_W: u32 = 0x2; -pub const VM_AREA_FLAG_X: u32 = 0x4; - -impl VMAreaFlags { - pub fn can_execute(&self) -> bool { - self.0 & VM_AREA_FLAG_X == VM_AREA_FLAG_X - } - - pub fn can_write(&self) -> bool { - self.0 & VM_AREA_FLAG_W == VM_AREA_FLAG_W - } - - pub fn can_read(&self) -> bool { - self.0 & VM_AREA_FLAG_R == VM_AREA_FLAG_R - } -} - -#[derive(Clone, Copy, PartialEq, Default)] +#[derive(Clone, PartialEq, Default)] pub struct VMAllocOptions { size: usize, addr: VMAddrOption, growth: VMGrowthType, + description: String, } impl VMAllocOptions { @@ -131,6 +93,11 @@ impl VMAllocOptions { self.growth = growth; Ok(self) } + + pub fn description(&mut self, description: &str) -> Result<&mut Self, Error> { + self.description = description.to_owned(); + Ok(self) + } } impl fmt::Debug for VMAllocOptions { diff --git a/src/libos/src/vm/process_vm.rs b/src/libos/src/vm/process_vm.rs index 126ddf41..dea672f5 100644 --- a/src/libos/src/vm/process_vm.rs +++ b/src/libos/src/vm/process_vm.rs @@ -11,7 +11,7 @@ lazy_static! { (addr, size) }; let vm_space = unsafe { - match VMSpace::new(addr, size, VMGuardAreaType::None) { + match VMSpace::new(addr, size, VMGuardAreaType::None, "DATA_SPACE") { Ok(vm_space) => vm_space, Err(_) => panic!("Failed to create a VMSpace"), } @@ -27,11 +27,11 @@ extern "C" { #[derive(Debug, Default)] pub struct ProcessVM { //code_domain: VMDomain, - data_domain: VMDomain, - code_vma: VMArea, - data_vma: VMArea, - heap_vma: VMArea, - stack_vma: VMArea, + data_domain: Option>, + code_vma: Option>, + data_vma: Option>, + heap_vma: Option>, + stack_vma: Option>, mmap_vmas: Vec>, brk: usize, } @@ -44,29 +44,39 @@ impl ProcessVM { stack_size: usize, mmap_size: usize, ) -> Result { - let data_domain_size = code_size + data_size + heap_size + stack_size + mmap_size; - let mut data_domain = DATA_SPACE.lock().unwrap().alloc_domain(data_domain_size)?; - - let (code_vma, data_vma, heap_vma, stack_vma) = ProcessVM::alloc_vmas( - &mut data_domain, - code_size, - data_size, - heap_size, - stack_size, - )?; + // Allocate the data domain from the global data space + let mut data_domain = { + let data_domain_size = code_size + data_size + heap_size + + stack_size + mmap_size; + let data_domain = DATA_SPACE.lock().unwrap().alloc_domain( + data_domain_size, "data_domain")?; + data_domain + }; + // Allocate vmas from the data domain + let (code_vma, data_vma, heap_vma, stack_vma) = + match ProcessVM::alloc_vmas(&mut data_domain, code_size, + data_size, heap_size, stack_size) { + Err(e) => { + // Note: we need to handle error here so that we can + // deallocate the data domain explictly. + DATA_SPACE.lock().unwrap().dealloc_domain(data_domain); + return Err(e); + }, + Ok(vmas) => vmas, + }; // Initial value of the program break let brk = heap_vma.get_start(); // No mmapped vmas initially let mmap_vmas = Vec::new(); let vm = ProcessVM { - data_domain, - code_vma, - data_vma, - heap_vma, - stack_vma, - mmap_vmas, - brk, + data_domain: Some(Box::new(data_domain)), + code_vma: Some(Box::new(code_vma)), + data_vma: Some(Box::new(data_vma)), + heap_vma: Some(Box::new(heap_vma)), + stack_vma: Some(Box::new(stack_vma)), + mmap_vmas: mmap_vmas, + brk: brk, }; Ok(vm) } @@ -79,11 +89,12 @@ impl ProcessVM { stack_size: usize, ) -> Result<(VMArea, VMArea, VMArea, VMArea), Error> { let mut addr = data_domain.get_start(); - let mut alloc_vma_continuously = - |addr: &mut usize, size, flags, growth| -> Result<_, Error> { + |addr: &mut usize, size, flags, growth, desc| -> Result<_, Error> { let mut options = VMAllocOptions::new(size)?; - options.addr(VMAddrOption::Fixed(*addr))?.growth(growth)?; + options.addr(VMAddrOption::Fixed(*addr))? + .growth(growth)? + .description(desc)?; let new_vma = data_domain.alloc_area(&options, flags)?; *addr += size; Ok(new_vma) @@ -92,39 +103,39 @@ impl ProcessVM { let rx_flags = VMAreaFlags(VM_AREA_FLAG_R | VM_AREA_FLAG_X); let rw_flags = VMAreaFlags(VM_AREA_FLAG_R | VM_AREA_FLAG_W); - let code_vma = alloc_vma_continuously(&mut addr, code_size, rx_flags, VMGrowthType::Fixed)?; - let data_vma = alloc_vma_continuously(&mut addr, data_size, rw_flags, VMGrowthType::Fixed)?; - let heap_vma = alloc_vma_continuously(&mut addr, 0, rw_flags, VMGrowthType::Upward)?; + let code_vma = alloc_vma_continuously(&mut addr, code_size, rx_flags, VMGrowthType::Fixed, "code_vma")?; + let data_vma = alloc_vma_continuously(&mut addr, data_size, rw_flags, VMGrowthType::Fixed, "data_vma")?; + let heap_vma = alloc_vma_continuously(&mut addr, 0, rw_flags, VMGrowthType::Upward, "heap_vma")?; // Preserve the space for heap addr += heap_size; // After the heap is the stack let stack_vma = - alloc_vma_continuously(&mut addr, stack_size, rw_flags, VMGrowthType::Downward)?; + alloc_vma_continuously(&mut addr, stack_size, rw_flags, VMGrowthType::Downward, "stack_vma")?; Ok((code_vma, data_vma, heap_vma, stack_vma)) } pub fn get_base_addr(&self) -> usize { - self.code_vma.get_start() + self.get_code_vma().get_start() } pub fn get_code_vma(&self) -> &VMArea { - &self.code_vma + &self.code_vma.as_ref().unwrap() } pub fn get_data_vma(&self) -> &VMArea { - &self.data_vma + &self.data_vma.as_ref().unwrap() } pub fn get_heap_vma(&self) -> &VMArea { - &self.heap_vma + &self.heap_vma.as_ref().unwrap() } pub fn get_stack_vma(&self) -> &VMArea { - &self.stack_vma + &self.stack_vma.as_ref().unwrap() } pub fn get_stack_top(&self) -> usize { - self.stack_vma.get_end() + self.get_stack_vma().get_end() } pub fn get_mmap_vmas(&self) -> &[Box] { @@ -163,7 +174,8 @@ impl ProcessVM { alloc_options }; // TODO: when failed, try to resize data_domain - let new_mmap_vma = self.data_domain.alloc_area(&alloc_options, flags)?; + let new_mmap_vma = self.get_data_domain_mut() + .alloc_area(&alloc_options, flags)?; let addr = new_mmap_vma.get_start(); self.mmap_vmas.push(Box::new(new_mmap_vma)); Ok(addr) @@ -184,8 +196,8 @@ impl ProcessVM { mmap_vma_i.unwrap() }; - let mut removed_mmap_vma = self.mmap_vmas.swap_remove(mmap_vma_i); - self.data_domain.dealloc_area(&mut removed_mmap_vma); + let removed_mmap_vma = self.mmap_vmas.swap_remove(mmap_vma_i); + self.get_data_domain_mut().dealloc_area(unbox(removed_mmap_vma)); Ok(()) } @@ -200,43 +212,52 @@ impl ProcessVM { } pub fn brk(&mut self, new_brk: usize) -> Result { + let (heap_start, heap_end) = { + let heap_vma = self.heap_vma.as_ref().unwrap(); + (heap_vma.get_start(), heap_vma.get_end()) + }; if new_brk == 0 { return Ok(self.get_brk()); - } else if new_brk < self.heap_vma.get_start() { + } else if new_brk < heap_start { return errno!(EINVAL, "New brk address is too low"); - } else if new_brk <= self.heap_vma.get_end() { - self.brk = new_brk; - return Ok(new_brk); + } else if new_brk > heap_end { + // TODO: init the memory with zeros for the expanded area + let resize_options = { + let new_heap_end = align_up(new_brk, 4096); + let new_heap_size = new_heap_end - heap_start; + let mut options = VMResizeOptions::new(new_heap_size)?; + options.addr(VMAddrOption::Fixed(heap_start)); + options + }; + let heap_vma = self.heap_vma.as_mut().unwrap(); + let data_domain = self.data_domain.as_mut().unwrap(); + data_domain.resize_area(heap_vma, &resize_options)?; } + self.brk = new_brk; + return Ok(new_brk); + } - // TODO: init the memory with zeros for the expanded area - let resize_options = { - let brk_start = self.get_brk_start(); - let new_heap_size = align_up(new_brk, 4096) - brk_start; - let mut options = VMResizeOptions::new(new_heap_size)?; - options.addr(VMAddrOption::Fixed(brk_start)); - options - }; - self.data_domain - .resize_area(&mut self.heap_vma, &resize_options)?; - Ok(new_brk) + fn get_data_domain_mut(&mut self) -> &mut Box { + self.data_domain.as_mut().unwrap() } } impl Drop for ProcessVM { fn drop(&mut self) { - let data_domain = &mut self.data_domain; - // Remove all vma from the domain - data_domain.dealloc_area(&mut self.code_vma); - data_domain.dealloc_area(&mut self.data_vma); - data_domain.dealloc_area(&mut self.heap_vma); - data_domain.dealloc_area(&mut self.stack_vma); - for mmap_vma in &mut self.mmap_vmas { - data_domain.dealloc_area(mmap_vma); + { + let data_domain = self.data_domain.as_mut().unwrap(); + data_domain.dealloc_area(unbox(self.code_vma.take().unwrap())); + data_domain.dealloc_area(unbox(self.data_vma.take().unwrap())); + data_domain.dealloc_area(unbox(self.heap_vma.take().unwrap())); + data_domain.dealloc_area(unbox(self.stack_vma.take().unwrap())); + for mmap_vma in self.mmap_vmas.drain(..) { + data_domain.dealloc_area(unbox(mmap_vma)); + } } // Remove the domain from its parent space - DATA_SPACE.lock().unwrap().dealloc_domain(data_domain); + DATA_SPACE.lock().unwrap().dealloc_domain( + unbox(self.data_domain.take().unwrap())); } } diff --git a/src/libos/src/vm/vm_area.rs b/src/libos/src/vm/vm_area.rs index 60cab4cc..459a57f2 100644 --- a/src/libos/src/vm/vm_area.rs +++ b/src/libos/src/vm/vm_area.rs @@ -1,6 +1,107 @@ use super::*; -impl super::VMArea { +#[derive(Debug)] +pub struct VMSpace { + range: VMRange, + guard_type: VMGuardAreaType, +} + +impl_vmrange_trait_for!(VMSpace, range); + +impl VMSpace { + pub unsafe fn new( + addr: usize, + size: usize, + guard_type: VMGuardAreaType, + desc: &str, + ) -> Result { + let addr = align_up(addr, PAGE_SIZE); + let size = align_down(size, PAGE_SIZE); + let range = unsafe { VMRange::new(addr, addr + size, VMGrowthType::Fixed, desc)? }; + Ok(VMSpace { range, guard_type }) + } + + pub fn get_guard_type(&self) -> VMGuardAreaType { + self.guard_type + } + + pub fn alloc_domain(&mut self, size: usize, desc: &str) -> Result { + let mut options = VMAllocOptions::new(size)?; + options.growth(VMGrowthType::Upward)? + .description(desc)?; + + let new_range = self.range.alloc_subrange(&options)?; + Ok(VMDomain { range: new_range }) + } + + pub fn dealloc_domain(&mut self, mut domain: VMDomain) { + self.range.dealloc_subrange(&mut domain.range) + } + + pub fn resize_domain(&mut self, domain: &mut VMDomain, new_size: usize) -> Result<(), Error> { + let options = VMResizeOptions::new(new_size)?; + self.range.resize_subrange(&mut domain.range, &options) + } +} + + +#[derive(Debug)] +pub struct VMDomain { + range: VMRange, +} + +impl_vmrange_trait_for!(VMDomain, range); + +impl VMDomain { + pub fn alloc_area( + &mut self, + options: &VMAllocOptions, + flags: VMAreaFlags, + ) -> Result { + let new_range = self.range.alloc_subrange(options)?; + + // Init the memory area with all zeros + unsafe { + let mem_ptr = new_range.get_start() as *mut c_void; + let mem_size = new_range.get_size() as size_t; + memset(mem_ptr, 0 as c_int, mem_size); + } + + Ok(VMArea { + range: new_range, + flags: flags, + }) + } + + pub fn dealloc_area(&mut self, mut area: VMArea) { + self.range.dealloc_subrange(&mut area.range) + } + + pub fn resize_area( + &mut self, + area: &mut VMArea, + options: &VMResizeOptions, + ) -> Result<(), Error> { + // TODO: init memory with zeros when expanding! + self.range.resize_subrange(&mut area.range, options) + } +} + +#[link(name = "sgx_tstdc")] +extern "C" { + pub fn memset(p: *mut c_void, c: c_int, n: size_t) -> *mut c_void; +} + + +#[derive(Debug)] +pub struct VMArea { + range: VMRange, + flags: VMAreaFlags, +} + +impl_vmrange_trait_for!(VMArea, range); + +impl VMArea { pub fn get_flags(&self) -> &VMAreaFlags { &self.flags } @@ -9,3 +110,25 @@ impl super::VMArea { &mut self.flags } } + + +#[derive(Copy, Clone, Debug, Default, PartialEq)] +pub struct VMAreaFlags(pub u32); + +pub const VM_AREA_FLAG_R: u32 = 0x1; +pub const VM_AREA_FLAG_W: u32 = 0x2; +pub const VM_AREA_FLAG_X: u32 = 0x4; + +impl VMAreaFlags { + pub fn can_execute(&self) -> bool { + self.0 & VM_AREA_FLAG_X == VM_AREA_FLAG_X + } + + pub fn can_write(&self) -> bool { + self.0 & VM_AREA_FLAG_W == VM_AREA_FLAG_W + } + + pub fn can_read(&self) -> bool { + self.0 & VM_AREA_FLAG_R == VM_AREA_FLAG_R + } +} diff --git a/src/libos/src/vm/vm_domain.rs b/src/libos/src/vm/vm_domain.rs deleted file mode 100644 index 42d008f6..00000000 --- a/src/libos/src/vm/vm_domain.rs +++ /dev/null @@ -1,41 +0,0 @@ -use super::*; - -impl VMDomain { - pub fn alloc_area( - &mut self, - options: &VMAllocOptions, - flags: VMAreaFlags, - ) -> Result { - let new_range = self.range.alloc_subrange(options)?; - - // Init the memory area with all zeros - unsafe { - let mem_ptr = new_range.get_start() as *mut c_void; - let mem_size = new_range.get_size() as size_t; - memset(mem_ptr, 0 as c_int, mem_size); - } - - Ok(VMArea { - range: new_range, - flags: flags, - }) - } - - pub fn dealloc_area(&mut self, area: &mut VMArea) { - self.range.dealloc_subrange(&mut area.range) - } - - pub fn resize_area( - &mut self, - area: &mut VMArea, - options: &VMResizeOptions, - ) -> Result<(), Error> { - // TODO: init memory with zeros when expanding! - self.range.resize_subrange(&mut area.range, options) - } -} - -#[link(name = "sgx_tstdc")] -extern "C" { - pub fn memset(p: *mut c_void, c: c_int, n: size_t) -> *mut c_void; -} diff --git a/src/libos/src/vm/vm_range.rs b/src/libos/src/vm/vm_range.rs index 0a71486d..75d54230 100644 --- a/src/libos/src/vm/vm_range.rs +++ b/src/libos/src/vm/vm_range.rs @@ -35,44 +35,51 @@ macro_rules! impl_vmrange_trait_for { }; } -impl_vmrange_trait_for!(VMRange, inner); -impl_vmrange_trait_for!(VMSpace, range); -impl_vmrange_trait_for!(VMDomain, range); -impl_vmrange_trait_for!(VMArea, range); - #[derive(Debug)] pub struct VMRange { inner: VMRangeInner, - parent_range: *const VMRange, sub_ranges: Option>, + is_dealloced: bool, + description: String, } +impl_vmrange_trait_for!(VMRange, inner); + impl VMRange { - pub unsafe fn new(start: usize, end: usize, growth: VMGrowthType) -> Result { + pub unsafe fn new(start: usize, end: usize, growth: VMGrowthType, description: &str) -> Result { if start % PAGE_SIZE != 0 || end % PAGE_SIZE != 0 { return errno!(EINVAL, "Invalid start and/or end"); } Ok(VMRange { inner: VMRangeInner::new(start, end, growth), - parent_range: 0 as *const VMRange, sub_ranges: None, + is_dealloced: false, + description: description.to_owned(), }) } pub fn alloc_subrange(&mut self, options: &VMAllocOptions) -> Result { + debug_assert!(!self.is_dealloced); + + // Lazy initialize the subrange array upon the first allocation + if self.sub_ranges.is_none() { + self.init_subrange_array()?; + } + // Find a free space that satisfies the options let free_space = self.look_for_free_space(options)?; + // Allocate a new subrange from the free space + let (new_subrange_idx, new_subrange_inner) = { + let (new_subrange_start, new_subrange_end) = + self.alloc_from_free_space(&free_space, options); + debug_assert!(free_space.contains(new_subrange_start)); + debug_assert!(free_space.contains(new_subrange_end)); - let (new_subrange_start, new_subrange_end) = - self.alloc_from_free_space(&free_space, options); - debug_assert!(free_space.contains(new_subrange_start)); - debug_assert!(free_space.contains(new_subrange_end)); - - let new_subrange_inner = VMRangeInner::new(new_subrange_start, - new_subrange_end, - options.growth); + (free_space.index_in_subranges, VMRangeInner::new( + new_subrange_start, new_subrange_end, options.growth)) + }; self.get_subranges_mut() - .insert(free_space.index_in_subranges, new_subrange_inner); + .insert(new_subrange_idx, new_subrange_inner); // Although there are two copies of the newly created VMRangeInner obj, // we can keep them in sync as all mutation on VMRange object must @@ -82,29 +89,28 @@ impl VMRange { // other in child, in dealloc_subrange and resize_subrange functions. Ok(VMRange { inner: new_subrange_inner, - parent_range: self as *const VMRange, sub_ranges: None, + is_dealloced: false, + description: options.description.clone(), }) } pub fn dealloc_subrange(&mut self, subrange: &mut VMRange) { - self.ensure_subrange_is_a_child(subrange); - if subrange.has_subranges() { - panic!("A range can only be dealloc'ed when it has no sub-ranges"); - } + debug_assert!(!self.is_dealloced); + debug_assert!(!subrange.is_dealloced); + debug_assert!(self.sub_ranges.is_some()); // Remove the sub-range let domain_i = self.position_subrange(subrange); self.get_subranges_mut().remove(domain_i); - // When all sub-ranges are removed, remove the sub-range array if self.get_subranges().len() == 2 { // two sentinel sub-ranges excluded self.sub_ranges = None; } - // Mark a range as dealloc'ed - subrange.mark_as_dealloced(); + subrange.inner.end = subrange.inner.start; + subrange.is_dealloced = true; } pub fn resize_subrange( @@ -112,7 +118,9 @@ impl VMRange { subrange: &mut VMRange, options: &VMResizeOptions, ) -> Result<(), Error> { - self.ensure_subrange_is_a_child(subrange); + debug_assert!(!self.is_dealloced); + debug_assert!(!subrange.is_dealloced); + debug_assert!(self.sub_ranges.is_some()); // Get valid parameters from options let new_size = options.new_size; @@ -136,6 +144,10 @@ impl VMRange { } } + pub fn get_description(&self) -> &str { + &self.description + } + fn init_subrange_array(&mut self) -> Result<(), Error> { // Use dummy VMRange as sentinel object at both ends to make the allocation // and deallocation algorithm simpler @@ -152,11 +164,6 @@ impl VMRange { // TODO: reduce the complexity from O(N) to O(log(N)), where N is // the number of existing subranges. - // Lazy initialize the subrange array upon the first allocation - if !self.has_subranges() { - self.init_subrange_array()?; - } - // Get valid parameters from options let size = options.size; let addr = options.addr; @@ -267,19 +274,13 @@ impl VMRange { // this sub-range or neighbor sub-range needs to grow later. // As a simple heuristic, we put this sub-range near the // center between the previous and next sub-ranges. - free_space.start + (free_space.get_size() - size) / 2 + let offset = align_down((free_space.get_size() - size) / 2, PAGE_SIZE); + free_space.start + offset }; (addr, addr + size) } - fn ensure_subrange_is_a_child(&self, subrange: &VMRange) { - // FIXME: - /*if subrange.parent_range != self as *const VMRange { - panic!("This range does not contain the given sub-range"); - }*/ - } - fn position_subrange(&self, subrange: &VMRange) -> usize { let sub_ranges = self.get_subranges(); sub_ranges @@ -296,10 +297,6 @@ impl VMRange { self.sub_ranges.as_mut().unwrap() } - fn has_subranges(&self) -> bool { - self.sub_ranges.is_some() - } - fn shrink_subrange_to(&mut self, subrange: &mut VMRange, new_size: usize) -> Result<(), Error> { let subrange_i = self.position_subrange(subrange); let subranges = self.get_subranges_mut(); @@ -379,15 +376,6 @@ impl VMRange { } Ok(()) } - - fn mark_as_dealloced(&mut self) { - self.parent_range = 0 as *const VMRange; - self.inner.start = self.inner.end; - } - - fn is_dealloced(&self) -> bool { - self.parent_range == 0 as *const VMRange - } } impl PartialOrd for VMRange { @@ -404,29 +392,15 @@ impl PartialEq for VMRange { impl Drop for VMRange { fn drop(&mut self) { - if !self.is_dealloced() { - println!("VMRange::drop::panic1"); + if !self.is_dealloced { panic!("A range must be dealloc'ed before drop"); } - if self.has_subranges() { - println!("VMRange::drop::panic2"); - panic!("All sub-ranges must be removed explicitly before drop"); - } } } unsafe impl Send for VMRange {} unsafe impl Sync for VMRange {} -impl Default for VMRange { - fn default() -> VMRange { - VMRange { - inner: VMRangeInner::new(0, 0, VMGrowthType::Fixed), - parent_range: 0 as *const VMRange, - sub_ranges: None, - } - } -} #[derive(Clone, Copy)] pub struct VMRangeInner { @@ -437,6 +411,8 @@ pub struct VMRangeInner { impl VMRangeInner { pub fn new(start: usize, end: usize, growth: VMGrowthType) -> VMRangeInner { + debug_assert!(start % PAGE_SIZE == 0); + debug_assert!(end % PAGE_SIZE == 0); VMRangeInner { start: start, end: end, diff --git a/src/libos/src/vm/vm_space.rs b/src/libos/src/vm/vm_space.rs deleted file mode 100644 index 9d48f4e9..00000000 --- a/src/libos/src/vm/vm_space.rs +++ /dev/null @@ -1,33 +0,0 @@ -use super::*; - -impl VMSpace { - pub unsafe fn new( - addr: usize, - size: usize, - guard_type: VMGuardAreaType, - ) -> Result { - let range = unsafe { VMRange::new(addr, addr + size, VMGrowthType::Fixed)? }; - Ok(VMSpace { range, guard_type }) - } - - pub fn get_guard_type(&self) -> VMGuardAreaType { - self.guard_type - } - - pub fn alloc_domain(&mut self, size: usize) -> Result { - let mut options = VMAllocOptions::new(size)?; - options.growth(VMGrowthType::Upward)?; - - let new_range = self.range.alloc_subrange(&options)?; - Ok(VMDomain { range: new_range }) - } - - pub fn dealloc_domain(&mut self, domain: &mut VMDomain) { - self.range.dealloc_subrange(&mut domain.range) - } - - pub fn resize_domain(&mut self, domain: &mut VMDomain, new_size: usize) -> Result<(), Error> { - let options = VMResizeOptions::new(new_size)?; - self.range.resize_subrange(&mut domain.range, &options) - } -} From 2957fa99d5476d4cf6adf6c8c057ff0e689c287b Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Thu, 11 Apr 2019 19:21:02 +0800 Subject: [PATCH 10/10] Make sure VMRange fills its memory with zeros if necessary --- src/libos/src/vm/mod.rs | 12 ++++++++ src/libos/src/vm/process_vm.rs | 26 ++++++++++------- src/libos/src/vm/vm_area.rs | 14 --------- src/libos/src/vm/vm_range.rs | 53 +++++++++++++++++++++++++++------- 4 files changed, 70 insertions(+), 35 deletions(-) diff --git a/src/libos/src/vm/mod.rs b/src/libos/src/vm/mod.rs index 4ec6b4b8..d810f3a1 100644 --- a/src/libos/src/vm/mod.rs +++ b/src/libos/src/vm/mod.rs @@ -68,6 +68,7 @@ pub struct VMAllocOptions { addr: VMAddrOption, growth: VMGrowthType, description: String, + fill_zeros: bool, } impl VMAllocOptions { @@ -98,6 +99,11 @@ impl VMAllocOptions { self.description = description.to_owned(); Ok(self) } + + pub fn fill_zeros(&mut self, fill_zeros: bool) -> Result<&mut Self, Error> { + self.fill_zeros = fill_zeros; + Ok(self) + } } impl fmt::Debug for VMAllocOptions { @@ -163,6 +169,7 @@ impl Default for VMGrowthType { pub struct VMResizeOptions { new_size: usize, new_addr: VMAddrOption, + fill_zeros: bool, } impl VMResizeOptions { @@ -180,4 +187,9 @@ impl VMResizeOptions { self.new_addr = new_addr; self } + + pub fn fill_zeros(&mut self, fill_zeros: bool) -> &mut Self { + self.fill_zeros = fill_zeros; + self + } } diff --git a/src/libos/src/vm/process_vm.rs b/src/libos/src/vm/process_vm.rs index dea672f5..f4a416d3 100644 --- a/src/libos/src/vm/process_vm.rs +++ b/src/libos/src/vm/process_vm.rs @@ -90,11 +90,12 @@ impl ProcessVM { ) -> Result<(VMArea, VMArea, VMArea, VMArea), Error> { let mut addr = data_domain.get_start(); let mut alloc_vma_continuously = - |addr: &mut usize, size, flags, growth, desc| -> Result<_, Error> { + |addr: &mut usize, desc, size, flags, growth, fill_zeros| -> Result<_, Error> { let mut options = VMAllocOptions::new(size)?; options.addr(VMAddrOption::Fixed(*addr))? .growth(growth)? - .description(desc)?; + .description(desc)? + .fill_zeros(fill_zeros)?; let new_vma = data_domain.alloc_area(&options, flags)?; *addr += size; Ok(new_vma) @@ -103,14 +104,17 @@ impl ProcessVM { let rx_flags = VMAreaFlags(VM_AREA_FLAG_R | VM_AREA_FLAG_X); let rw_flags = VMAreaFlags(VM_AREA_FLAG_R | VM_AREA_FLAG_W); - let code_vma = alloc_vma_continuously(&mut addr, code_size, rx_flags, VMGrowthType::Fixed, "code_vma")?; - let data_vma = alloc_vma_continuously(&mut addr, data_size, rw_flags, VMGrowthType::Fixed, "data_vma")?; - let heap_vma = alloc_vma_continuously(&mut addr, 0, rw_flags, VMGrowthType::Upward, "heap_vma")?; + let code_vma = alloc_vma_continuously(&mut addr, "code_vma", code_size, + rx_flags, VMGrowthType::Fixed, true)?; + let data_vma = alloc_vma_continuously(&mut addr, "data_vma", data_size, + rw_flags, VMGrowthType::Fixed, true)?; + let heap_vma = alloc_vma_continuously(&mut addr, "heap_vma", 0, + rw_flags, VMGrowthType::Upward, true)?; // Preserve the space for heap addr += heap_size; // After the heap is the stack - let stack_vma = - alloc_vma_continuously(&mut addr, stack_size, rw_flags, VMGrowthType::Downward, "stack_vma")?; + let stack_vma = alloc_vma_continuously(&mut addr, "stack_vma", stack_size, + rw_flags, VMGrowthType::Downward, false)?; Ok((code_vma, data_vma, heap_vma, stack_vma)) } @@ -191,7 +195,7 @@ impl ProcessVM { .iter() .position(|vma| vma.get_start() == addr && vma.get_end() == addr + size); if mmap_vma_i.is_none() { - return Ok(()); + return errno!(EINVAL, "memory area not found"); } mmap_vma_i.unwrap() }; @@ -221,12 +225,12 @@ impl ProcessVM { } else if new_brk < heap_start { return errno!(EINVAL, "New brk address is too low"); } else if new_brk > heap_end { - // TODO: init the memory with zeros for the expanded area let resize_options = { - let new_heap_end = align_up(new_brk, 4096); + let new_heap_end = align_up(new_brk, PAGE_SIZE); let new_heap_size = new_heap_end - heap_start; let mut options = VMResizeOptions::new(new_heap_size)?; - options.addr(VMAddrOption::Fixed(heap_start)); + options.addr(VMAddrOption::Fixed(heap_start)) + .fill_zeros(true); options }; let heap_vma = self.heap_vma.as_mut().unwrap(); diff --git a/src/libos/src/vm/vm_area.rs b/src/libos/src/vm/vm_area.rs index 459a57f2..356cf540 100644 --- a/src/libos/src/vm/vm_area.rs +++ b/src/libos/src/vm/vm_area.rs @@ -59,14 +59,6 @@ impl VMDomain { flags: VMAreaFlags, ) -> Result { let new_range = self.range.alloc_subrange(options)?; - - // Init the memory area with all zeros - unsafe { - let mem_ptr = new_range.get_start() as *mut c_void; - let mem_size = new_range.get_size() as size_t; - memset(mem_ptr, 0 as c_int, mem_size); - } - Ok(VMArea { range: new_range, flags: flags, @@ -82,16 +74,10 @@ impl VMDomain { area: &mut VMArea, options: &VMResizeOptions, ) -> Result<(), Error> { - // TODO: init memory with zeros when expanding! self.range.resize_subrange(&mut area.range, options) } } -#[link(name = "sgx_tstdc")] -extern "C" { - pub fn memset(p: *mut c_void, c: c_int, n: size_t) -> *mut c_void; -} - #[derive(Debug)] pub struct VMArea { diff --git a/src/libos/src/vm/vm_range.rs b/src/libos/src/vm/vm_range.rs index 75d54230..c3e305e0 100644 --- a/src/libos/src/vm/vm_range.rs +++ b/src/libos/src/vm/vm_range.rs @@ -81,6 +81,15 @@ impl VMRange { self.get_subranges_mut() .insert(new_subrange_idx, new_subrange_inner); + if options.fill_zeros { + // Init the memory area with all zeros + unsafe { + let mem_ptr = new_subrange_inner.get_start() as *mut c_void; + let mem_size = new_subrange_inner.get_size() as size_t; + memset(mem_ptr, 0 as c_int, mem_size); + } + } + // Although there are two copies of the newly created VMRangeInner obj, // we can keep them in sync as all mutation on VMRange object must // be carried out through dealloc_subrange() and resize_subrange() that @@ -140,7 +149,7 @@ impl VMRange { } // Grow else { - self.grow_subrange_to(subrange, new_size) + self.grow_subrange_to(subrange, new_size, options.fill_zeros) } } @@ -340,39 +349,58 @@ impl VMRange { Ok(()) } - fn grow_subrange_to(&mut self, subrange: &mut VMRange, new_size: usize) -> Result<(), Error> { + fn grow_subrange_to(&mut self, subrange: &mut VMRange, new_size: usize, fill_zeros: bool) -> Result<(), Error> { let subrange_i = self.position_subrange(subrange); let subranges = self.get_subranges_mut(); + let subrange_old_start = subrange.inner.start; + let subrange_old_end = subrange.inner.end; + let subrange_old_size = subrange.get_size(); + if subrange.inner.growth == VMGrowthType::Upward { - // Can we grow? + // Can we grow upward? let max_new_size = { let next_subrange = &subranges[subrange_i + 1]; - next_subrange.start - subrange.inner.start + next_subrange.start - subrange_old_start }; if new_size > max_new_size { return errno!(ENOMEM, "Cannot grow to new size"); } // Do grow - let subrange_new_end = subrange.inner.start + new_size; + let subrange_new_end = subrange_old_start + new_size; subrange.inner.end = subrange_new_end; // Sync state subranges[subrange_i].end = subrange_new_end; - } else { - // self.growth == VMGrowthType::Downward - // Can we grow? + // Init memory + if fill_zeros { + unsafe { + let mem_ptr = subrange_old_end as *mut c_void; + let mem_size = (subrange_new_end - subrange_old_end) as size_t; + memset(mem_ptr, 0 as c_int, mem_size); + } + } + } else { // self.growth == VMGrowthType::Downward + // Can we grow downard? let max_new_size = { let pre_subrange = &subranges[subrange_i - 1]; - subrange.inner.end - pre_subrange.end + subrange_old_end - pre_subrange.end }; if new_size > max_new_size { return errno!(ENOMEM, "Cannot grow to new size"); } // Do grow - let subrange_new_start = subrange.inner.end - new_size; + let subrange_new_start = subrange_old_end - new_size; subrange.inner.start = subrange_new_start; // Sync state subranges[subrange_i].start = subrange_new_start; + // Init memory + if fill_zeros { + unsafe { + let mem_ptr = subrange_new_start as *mut c_void; + let mem_size = (subrange_old_start - subrange_new_start) as size_t; + memset(mem_ptr, 0 as c_int, mem_size); + } + } } Ok(()) } @@ -532,3 +560,8 @@ impl PartialOrd for FreeSpace { } } } + +#[link(name = "sgx_tstdc")] +extern "C" { + pub fn memset(p: *mut c_void, c: c_int, n: size_t) -> *mut c_void; +}