From a712bfe70a72981bb53b9dfadb934071d4905122 Mon Sep 17 00:00:00 2001 From: "Tate, Hongliang Tian" Date: Mon, 7 Jan 2019 19:20:01 +0800 Subject: [PATCH] Add pipe test --- src/libos/include/syscall.h | 4 +- src/libos/src/lib.rs | 3 +- src/libos/src/process/mod.rs | 2 +- src/libos/src/process/spawn/mod.rs | 35 ++++++++++++-- src/libos/src/syscall/mod.rs | 70 +++++++++++++++++++++++++-- src/libos/src/syscall/syscall_entry.c | 3 +- src/libos/src/vm/process_vm.rs | 22 +++++---- test/Makefile | 2 +- test/malloc/main.c | 5 ++ test/pipe/Makefile | 4 ++ test/pipe/main.c | 53 ++++++++++++++++++++ 11 files changed, 178 insertions(+), 25 deletions(-) create mode 100644 test/pipe/Makefile create mode 100644 test/pipe/main.c diff --git a/src/libos/include/syscall.h b/src/libos/include/syscall.h index a9dcbffa..5b97301f 100644 --- a/src/libos/include/syscall.h +++ b/src/libos/include/syscall.h @@ -26,8 +26,8 @@ extern int occlum_dup2(int old_fd, int new_fd); extern int occlum_dup3(int old_fd, int new_fd, int flags); extern int occlum_spawn(int* child_pid, const char* path, - const char** argv, - const char** envp); + const char** argv, const char** envp, + void* file_actions); extern int occlum_wait4(int child_pid, int* status, int options/*, struct rusage* rusage*/); extern void occlum_exit(int status); extern unsigned int occlum_getpid(void); diff --git a/src/libos/src/lib.rs b/src/libos/src/lib.rs index b33bcc5f..be4a9ca2 100644 --- a/src/libos/src/lib.rs +++ b/src/libos/src/lib.rs @@ -78,8 +78,9 @@ fn do_boot(path_str: &str) -> Result<(), Error> { let argv = std::vec::Vec::new(); let envp = std::vec::Vec::new(); + let file_actions = Vec::new(); let parent = &process::IDLE_PROCESS; - process::do_spawn(&path_str, &argv, &envp, parent)?; + process::do_spawn(&path_str, &argv, &envp, &file_actions, parent)?; Ok(()) } diff --git a/src/libos/src/process/mod.rs b/src/libos/src/process/mod.rs index bd58d5c2..7ce185ea 100644 --- a/src/libos/src/process/mod.rs +++ b/src/libos/src/process/mod.rs @@ -3,7 +3,7 @@ pub use self::task::{get_current, run_task}; pub mod table { pub use super::process_table::{get}; } -pub use self::spawn::{do_spawn}; +pub use self::spawn::{do_spawn, FileAction}; pub use self::exit::{do_exit, do_wait4, ChildProcessFilter}; pub use self::wait::{Waiter, WaitQueue}; diff --git a/src/libos/src/process/spawn/mod.rs b/src/libos/src/process/spawn/mod.rs index db4ab682..c9b0e978 100644 --- a/src/libos/src/process/spawn/mod.rs +++ b/src/libos/src/process/spawn/mod.rs @@ -1,5 +1,5 @@ use super::*; -use fs::{File, StdinFile, StdoutFile/*, StderrFile*/, FileTable}; +use fs::{File, FileDesc, StdinFile, StdoutFile/*, StderrFile*/, FileTable}; use std::path::Path; use std::ffi::{CStr, CString}; use std::sgxfs::SgxFile; @@ -14,8 +14,16 @@ mod init_vm; mod elf_helper; mod segment; +#[derive(Debug)] +pub enum FileAction { + // TODO: Add open action + // Open(...) + Dup2(FileDesc, FileDesc), + Close(FileDesc), +} + pub fn do_spawn>(elf_path: &P, argv: &[CString], envp: &[CString], - parent_ref: &ProcessRef) + file_actions: &[FileAction], parent_ref: &ProcessRef) -> Result { let mut elf_buf = { @@ -55,7 +63,7 @@ pub fn do_spawn>(elf_path: &P, argv: &[CString], envp: &[CString] let stack_top = vm.get_stack_top(); init_task(program_entry, stack_top, argv, envp)? }; - let files = init_files(parent_ref)?; + let files = init_files(parent_ref, file_actions)?; let exec_path = elf_path.as_ref().to_str().unwrap(); Process::new(exec_path, task, vm, files)? }; @@ -65,12 +73,29 @@ pub fn do_spawn>(elf_path: &P, argv: &[CString], envp: &[CString] Ok(new_pid) } -fn init_files(parent_ref: &ProcessRef) -> Result { +fn init_files(parent_ref: &ProcessRef, file_actions: &[FileAction]) + -> Result +{ // Usually, we just inherit the file table from the parent let parent = parent_ref.lock().unwrap(); let should_inherit_file_table = parent.get_pid() > 0; if should_inherit_file_table { - return Ok(parent.get_files().clone()); + let mut cloned_file_table = parent.get_files().clone(); + // Perform file actions to modify the cloned file table + for file_action in file_actions { + match file_action { + FileAction::Dup2(old_fd, new_fd) => { + let file = cloned_file_table.get(*old_fd)?; + if old_fd != new_fd { + cloned_file_table.put_at(*new_fd, file, false); + } + }, + FileAction::Close(fd) => { + cloned_file_table.del(*fd)?; + } + } + } + return Ok(cloned_file_table); } drop(parent); diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index ebb0fe27..44945663 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -1,10 +1,11 @@ use super::*; use prelude::*; +use std::{ptr}; use {std, fs, process, vm}; use std::ffi::{CStr, CString}; use fs::{off_t, FileDesc}; use vm::{VMAreaFlags, VMResizeOptions}; -use process::{pid_t, ChildProcessFilter}; +use process::{pid_t, ChildProcessFilter, FileAction}; // Use the internal syscall wrappers from sgx_tstd //use std::libc_fs as fs; //use std::libc_io as io; @@ -413,19 +414,77 @@ pub extern "C" fn occlum_unknown(num: u32) println!("[WARNING] Unknown syscall (num = {})", num); } + +/* + * This Rust-version of fdop correspond to the C-version one in Occlum. + * See /src/process/fdop.h. + */ +const FDOP_CLOSE : u32 = 1; +const FDOP_DUP2 : u32 = 2; +const FDOP_OPEN : u32 = 3; + +#[repr(C)] +#[derive(Debug)] +pub struct FdOp { + // We actually switch the prev and next fields in the libc definition. + prev: *const FdOp, + next: *const FdOp, + cmd: u32, + fd: u32, + srcfd: u32, + oflag: u32, + mode: u32, + path: *const u8, +} + +fn clone_file_actions_from_user_safely(fdop_ptr: *const FdOp) + -> Result, Error> +{ + let mut file_actions = Vec::new(); + + let mut fdop_ptr = fdop_ptr; + while fdop_ptr != ptr::null() { + check_ptr_from_user(fdop_ptr)?; + let fdop = unsafe { &*fdop_ptr }; + + let file_action = match fdop.cmd { + FDOP_CLOSE => { + FileAction::Close(fdop.fd) + }, + FDOP_DUP2 => { + FileAction::Dup2(fdop.srcfd, fdop.fd) + }, + FDOP_OPEN => { + return errno!(EINVAL, "Not implemented"); + }, + _ => { + return errno!(EINVAL, "Unknown file action command"); + }, + }; + file_actions.push(file_action); + + fdop_ptr = fdop.next; + } + + Ok(file_actions) +} + fn do_spawn(child_pid_ptr: *mut c_uint, path: *const c_char, argv: *const *const c_char, - envp: *const *const c_char) + envp: *const *const c_char, + fdop_list: *const FdOp, + ) -> Result<(), Error> { check_mut_ptr_from_user(child_pid_ptr)?; let path = clone_cstring_from_user_safely(path)?; let argv = clone_cstrings_from_user_safely(argv)?; let envp = clone_cstrings_from_user_safely(envp)?; + let file_actions = clone_file_actions_from_user_safely(fdop_list)?; let parent = process::get_current(); - let child_pid = process::do_spawn(&path, &argv, &envp, &parent)?; + let child_pid = process::do_spawn(&path, &argv, &envp, &file_actions, &parent)?; unsafe { *child_pid_ptr = child_pid }; Ok(()) @@ -434,9 +493,10 @@ fn do_spawn(child_pid_ptr: *mut c_uint, #[no_mangle] 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, + fdop_list: *const FdOp) -> c_int { - match do_spawn(child_pid, path, argv, envp) { + match do_spawn(child_pid, path, argv, envp, fdop_list) { Ok(()) => 0, Err(e) => { e.errno.as_retval() } } diff --git a/src/libos/src/syscall/syscall_entry.c b/src/libos/src/syscall/syscall_entry.c index bbb94b3a..d47b90b0 100644 --- a/src/libos/src/syscall/syscall_entry.c +++ b/src/libos/src/syscall/syscall_entry.c @@ -65,7 +65,8 @@ long dispatch_syscall(int num, long arg0, long arg1, long arg2, long arg3, long DECL_SYSCALL_ARG(const char*, path, arg1); DECL_SYSCALL_ARG(const char**, argv, arg2); DECL_SYSCALL_ARG(const char**, envp, arg3); - ret = occlum_spawn(child_pid, path, argv, envp); + DECL_SYSCALL_ARG(void*, file_actions, arg4); + ret = occlum_spawn(child_pid, path, argv, envp, file_actions); break; } case SYS_wait4: { diff --git a/src/libos/src/vm/process_vm.rs b/src/libos/src/vm/process_vm.rs index 1de4a523..5401baec 100644 --- a/src/libos/src/vm/process_vm.rs +++ b/src/libos/src/vm/process_vm.rs @@ -31,6 +31,7 @@ pub struct ProcessVM { heap_vma: VMArea, stack_vma: VMArea, mmap_vmas: Vec>, + brk: usize, } impl ProcessVM { @@ -46,6 +47,8 @@ impl ProcessVM { let (code_vma, data_vma, heap_vma, stack_vma) = ProcessVM::alloc_vmas(&mut data_domain, code_size, data_size, heap_size, stack_size)?; + // Initial value of the program break + let brk = heap_vma.get_start(); // No mmapped vmas initially let mmap_vmas = Vec::new(); @@ -56,6 +59,7 @@ impl ProcessVM { heap_vma, stack_vma, mmap_vmas, + brk, }) } @@ -128,7 +132,7 @@ impl ProcessVM { } pub fn get_brk(&self) -> usize { - self.get_heap_vma().get_end() + self.brk } pub fn get_mmap_start(&self) -> usize { @@ -196,17 +200,17 @@ impl ProcessVM { if new_brk == 0 { return Ok(self.get_brk()); } + else if new_brk < self.heap_vma.get_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); + } let resize_options = { let brk_start = self.get_brk_start(); - - let new_heap_size = { - if new_brk < brk_start { - return Err(Error::new(Errno::EINVAL, "Invalid brk")); - } - new_brk - 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 diff --git a/test/Makefile b/test/Makefile index 32ef990b..eab555a3 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,7 +1,7 @@ CUR_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) PROJECT_DIR := $(realpath $(CUR_DIR)/../) -TEST_SUITES := empty hello_world malloc file getpid spawn +TEST_SUITES := empty hello_world malloc file getpid spawn pipe BUILD_TEST_SUITES := $(TEST_SUITES:%=%) RUN_TEST_SUITES := $(TEST_SUITES:%=test-%) CLEAN_TEST_SUITES := $(TEST_SUITES:%=clean-%) diff --git a/test/malloc/main.c b/test/malloc/main.c index 1aec5002..b291cfea 100644 --- a/test/malloc/main.c +++ b/test/malloc/main.c @@ -9,6 +9,11 @@ int main(void) { for (size_t buf_size = MIN_SIZE; buf_size <= MAX_SIZE; buf_size *= 4) { printf("buf_size = %lu\n", buf_size); void* buf = malloc(buf_size); + /* FIXME: why the first call to malloc always fail? + if (buf == NULL) { + printf("ERROR: failed to malloc for a buffer of %lu size\n", buf_size); + } + */ free(buf); } printf("Done.\n"); diff --git a/test/pipe/Makefile b/test/pipe/Makefile new file mode 100644 index 00000000..b71a4d49 --- /dev/null +++ b/test/pipe/Makefile @@ -0,0 +1,4 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := diff --git a/test/pipe/main.c b/test/pipe/main.c new file mode 100644 index 00000000..5b31de6e --- /dev/null +++ b/test/pipe/main.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include +#include + +int main(void) { + // XXX: this is a hack! remove this in the future + void* ptr = malloc(64); + free(ptr); + + int pipe_fds[2]; + if (pipe(pipe_fds) < 0) { + printf("ERROR: failed to create a pipe\n"); + return -1; + } + int pipe_rd_fd = pipe_fds[0]; + int pipe_wr_fd = pipe_fds[1]; + + posix_spawn_file_actions_t file_actions; + posix_spawn_file_actions_init(&file_actions); + posix_spawn_file_actions_adddup2(&file_actions, pipe_wr_fd, STDOUT_FILENO); + posix_spawn_file_actions_addclose(&file_actions, pipe_rd_fd); + + int child_pid; + if (posix_spawn(&child_pid, "hello_world/bin.encrypted", &file_actions, + NULL, NULL, NULL) < 0) { + printf("ERROR: failed to spawn a child process\n"); + return -1; + } + close(pipe_wr_fd); + + const char* expected_str = "Hello World\n"; + size_t expected_len = strlen(expected_str); + char actual_str[32] = {0}; + ssize_t actual_len; + do { + actual_len = read(pipe_rd_fd, actual_str, sizeof(actual_str) - 1); + } while (actual_len == 0); + if (strncmp(expected_str, actual_str, expected_len) != 0) { + printf("ERROR: received string is not as expected\n"); + return -1; + } + + int status = 0; + if (wait4(child_pid, &status, 0, NULL) < 0) { + printf("ERROR: failed to wait4 the child process\n"); + return -1; + } + return 0; +}