From dbc1e79e56fd1bd303c4aea168366f0138cdc5d3 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Fri, 1 Sep 2023 10:25:37 +0800 Subject: [PATCH] Clear vmar later to allow execve return error --- services/libs/jinux-std/src/process/clone.rs | 29 +++-- services/libs/jinux-std/src/process/mod.rs | 29 ++--- .../process/posix_thread/posix_thread_ext.rs | 12 +- .../jinux-std/src/process/process_vm/mod.rs | 32 ++++-- .../src/process/process_vm/user_heap.rs | 10 +- .../process/program_loader/elf/load_elf.rs | 105 ++++++++++++------ .../src/process/program_loader/elf/mod.rs | 2 +- .../src/process/program_loader/mod.rs | 16 +-- services/libs/jinux-std/src/syscall/execve.rs | 16 ++- 9 files changed, 149 insertions(+), 102 deletions(-) diff --git a/services/libs/jinux-std/src/process/clone.rs b/services/libs/jinux-std/src/process/clone.rs index ffb3bd47..77f23db2 100644 --- a/services/libs/jinux-std/src/process/clone.rs +++ b/services/libs/jinux-std/src/process/clone.rs @@ -22,7 +22,10 @@ use crate::{ vm::vmar::Vmar, }; -use super::{posix_thread::PosixThread, signal::sig_disposition::SigDispositions, Process}; +use super::{ + posix_thread::PosixThread, process_vm::ProcessVm, signal::sig_disposition::SigDispositions, + Process, +}; bitflags! { pub struct CloneFlags: u32 { @@ -200,9 +203,15 @@ fn clone_child_process(parent_context: UserContext, clone_args: CloneArgs) -> Re let clone_flags = clone_args.clone_flags; // clone vm - let parent_root_vmar = current.root_vmar(); - let child_root_vmar = clone_vm(parent_root_vmar, clone_flags)?; - let child_user_vm = current.user_vm().clone(); + let child_root_vmar = { + let parent_root_vmar = current.root_vmar(); + clone_vm(parent_root_vmar, clone_flags)? + }; + + let child_process_vm = { + let child_user_heap = current.user_heap().clone(); + ProcessVm::new(child_user_heap, child_root_vmar.dup()?) + }; // clone user space let child_cpu_context = clone_cpu_context( @@ -249,8 +258,7 @@ fn clone_child_process(parent_context: UserContext, clone_args: CloneArgs) -> Re parent, vec![child_thread], child_elf_path, - child_user_vm, - child_root_vmar.clone(), + child_process_vm, Weak::new(), child_file_table, child_fs, @@ -316,14 +324,11 @@ fn clone_parent_settid( /// clone child vmar. If CLONE_VM is set, both threads share the same root vmar. /// Otherwise, fork a new copy-on-write vmar. -fn clone_vm( - parent_root_vmar: &Arc>, - clone_flags: CloneFlags, -) -> Result>> { +fn clone_vm(parent_root_vmar: &Vmar, clone_flags: CloneFlags) -> Result> { if clone_flags.contains(CloneFlags::CLONE_VM) { - Ok(parent_root_vmar.clone()) + Ok(parent_root_vmar.dup()?) } else { - Ok(Arc::new(parent_root_vmar.fork_vmar()?)) + Ok(parent_root_vmar.fork_vmar()?) } } diff --git a/services/libs/jinux-std/src/process/mod.rs b/services/libs/jinux-std/src/process/mod.rs index 03c75de5..e4f9263b 100644 --- a/services/libs/jinux-std/src/process/mod.rs +++ b/services/libs/jinux-std/src/process/mod.rs @@ -3,7 +3,7 @@ use core::sync::atomic::{AtomicI32, Ordering}; use self::posix_thread::posix_thread_ext::PosixThreadExt; use self::process_group::ProcessGroup; use self::process_vm::user_heap::UserHeap; -use self::process_vm::UserVm; +use self::process_vm::ProcessVm; use self::rlimit::ResourceLimits; use self::signal::constants::SIGCHLD; use self::signal::sig_disposition::SigDispositions; @@ -45,8 +45,7 @@ pub struct Process { // Immutable Part pid: Pid, - user_vm: UserVm, - root_vmar: Arc>, + process_vm: ProcessVm, /// wait for child status changed waiting_children: WaitQueue, @@ -98,8 +97,7 @@ impl Process { parent: Weak, threads: Vec>, executable_path: String, - user_vm: UserVm, - root_vmar: Arc>, + process_vm: ProcessVm, process_group: Weak, file_table: Arc>, fs: Arc>, @@ -113,8 +111,7 @@ impl Process { pid, threads: Mutex::new(threads), executable_path: RwLock::new(executable_path), - user_vm, - root_vmar, + process_vm, waiting_children, exit_code: AtomicI32::new(0), status: Mutex::new(ProcessStatus::Runnable), @@ -157,13 +154,12 @@ impl Process { argv: Vec, envp: Vec, ) -> Result> { - let root_vmar = Vmar::::new_root()?; let fs = FsResolver::new(); let umask = FileCreationMask::default(); let pid = allocate_tid(); let parent = Weak::new(); let process_group = Weak::new(); - let user_vm = UserVm::new(&root_vmar)?; + let process_vm = ProcessVm::alloc()?; let file_table = FileTable::new_with_stdio(); let sig_dispositions = SigDispositions::new(); let user_process = Arc::new(Process::new( @@ -171,8 +167,7 @@ impl Process { parent, vec![], executable_path.to_string(), - user_vm, - Arc::new(root_vmar), + process_vm, process_group, Arc::new(Mutex::new(file_table)), Arc::new(RwLock::new(fs)), @@ -182,7 +177,7 @@ impl Process { let thread = Thread::new_posix_thread_from_executable( pid, - &user_process.root_vmar(), + &user_process.process_vm, &user_process.fs().read(), executable_path, Arc::downgrade(&user_process), @@ -315,18 +310,18 @@ impl Process { } /// returns the user_vm - pub fn user_vm(&self) -> &UserVm { - &self.user_vm + pub fn process_vm(&self) -> &ProcessVm { + &self.process_vm } /// returns the root vmar - pub fn root_vmar(&self) -> &Arc> { - &self.root_vmar + pub fn root_vmar(&self) -> &Vmar { + &self.process_vm.root_vmar() } /// returns the user heap if the process does have, otherwise None pub fn user_heap(&self) -> &UserHeap { - self.user_vm.user_heap() + self.process_vm.user_heap() } /// free zombie child with pid, returns the exit code of child process. diff --git a/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs b/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs index 7e11ead7..9f42ba24 100644 --- a/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs +++ b/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs @@ -1,12 +1,10 @@ use jinux_frame::{cpu::UserContext, user::UserSpace}; -use jinux_rights::Full; use crate::{ fs::fs_resolver::{FsPath, FsResolver, AT_FDCWD}, prelude::*, - process::{program_loader::load_program_to_root_vmar, Process}, + process::{process_vm::ProcessVm, program_loader::load_program_to_vm, Process}, thread::{Thread, Tid}, - vm::vmar::Vmar, }; use super::{builder::PosixThreadBuilder, name::ThreadName, PosixThread}; @@ -14,7 +12,7 @@ pub trait PosixThreadExt { fn as_posix_thread(&self) -> Option<&PosixThread>; fn new_posix_thread_from_executable( tid: Tid, - root_vmar: &Vmar, + process_vm: &ProcessVm, fs_resolver: &FsResolver, executable_path: &str, process: Weak, @@ -27,7 +25,7 @@ impl PosixThreadExt for Thread { /// This function should only be called when launch shell() fn new_posix_thread_from_executable( tid: Tid, - root_vmar: &Vmar, + process_vm: &ProcessVm, fs_resolver: &FsResolver, executable_path: &str, process: Weak, @@ -39,9 +37,9 @@ impl PosixThreadExt for Thread { fs_resolver.lookup(&fs_path)? }; let (_, elf_load_info) = - load_program_to_root_vmar(root_vmar, elf_file, argv, envp, fs_resolver, 1)?; + load_program_to_vm(process_vm, elf_file, argv, envp, fs_resolver, 1)?; - let vm_space = root_vmar.vm_space().clone(); + let vm_space = process_vm.root_vmar().vm_space().clone(); let mut cpu_ctx = UserContext::default(); cpu_ctx.set_rip(elf_load_info.entry_point() as _); cpu_ctx.set_rsp(elf_load_info.user_stack_top() as _); diff --git a/services/libs/jinux-std/src/process/process_vm/mod.rs b/services/libs/jinux-std/src/process/process_vm/mod.rs index f97b58dd..e530a915 100644 --- a/services/libs/jinux-std/src/process/process_vm/mod.rs +++ b/services/libs/jinux-std/src/process/process_vm/mod.rs @@ -41,24 +41,40 @@ use crate::vm::vmar::Vmar; /// The virtual space usage. /// This struct is used to control brk and mmap now. -#[derive(Debug, Clone)] -pub struct UserVm { +pub struct ProcessVm { user_heap: UserHeap, + root_vmar: Vmar, } -impl UserVm { - pub fn new(root_vmar: &Vmar) -> Result { +impl ProcessVm { + pub fn alloc() -> Result { + let root_vmar = Vmar::::new_root()?; let user_heap = UserHeap::new(); - user_heap.init(root_vmar).unwrap(); - Ok(UserVm { user_heap }) + user_heap.init(&root_vmar); + Ok(ProcessVm { + user_heap, + root_vmar, + }) + } + + pub fn new(user_heap: UserHeap, root_vmar: Vmar) -> Self { + Self { + user_heap, + root_vmar, + } } pub fn user_heap(&self) -> &UserHeap { &self.user_heap } + pub fn root_vmar(&self) -> &Vmar { + &self.root_vmar + } + /// Set user vm to the init status - pub fn set_default(&self) -> Result<()> { - self.user_heap.set_default() + pub fn clear(&self) { + self.root_vmar.clear().unwrap(); + self.user_heap.set_default(&self.root_vmar); } } diff --git a/services/libs/jinux-std/src/process/process_vm/user_heap.rs b/services/libs/jinux-std/src/process/process_vm/user_heap.rs index 99f311b2..08b1bc76 100644 --- a/services/libs/jinux-std/src/process/process_vm/user_heap.rs +++ b/services/libs/jinux-std/src/process/process_vm/user_heap.rs @@ -30,7 +30,7 @@ impl UserHeap { } } - pub fn init(&self, root_vmar: &Vmar) -> Result { + pub fn init(&self, root_vmar: &Vmar) -> Vaddr { let perms = VmPerms::READ | VmPerms::WRITE; let vmo_options = VmoOptions::::new(0).flags(VmoFlags::RESIZABLE); let heap_vmo = vmo_options.alloc().unwrap(); @@ -40,7 +40,7 @@ impl UserHeap { .offset(self.heap_base) .size(self.heap_size_limit); vmar_map_options.build().unwrap(); - return Ok(self.current_heap_end.load(Ordering::Relaxed)); + return self.current_heap_end.load(Ordering::Relaxed); } pub fn brk(&self, new_heap_end: Option) -> Result { @@ -71,12 +71,10 @@ impl UserHeap { /// Set heap to the default status. i.e., point the heap end to heap base. /// This function will we called in execve. - pub fn set_default(&self) -> Result<()> { + pub fn set_default(&self, root_vmar: &Vmar) { self.current_heap_end .store(self.heap_base, Ordering::Relaxed); - let current = current!(); - self.init(current.root_vmar())?; - Ok(()) + self.init(root_vmar); } } diff --git a/services/libs/jinux-std/src/process/program_loader/elf/load_elf.rs b/services/libs/jinux-std/src/process/program_loader/elf/load_elf.rs index 8c6199e7..7ec1a24c 100644 --- a/services/libs/jinux-std/src/process/program_loader/elf/load_elf.rs +++ b/services/libs/jinux-std/src/process/program_loader/elf/load_elf.rs @@ -3,7 +3,9 @@ use crate::fs::fs_resolver::{FsPath, FsResolver, AT_FDCWD}; use crate::fs::utils::Dentry; +use crate::process::process_vm::ProcessVm; use crate::process::program_loader::elf::init_stack::{init_aux_vec, InitStack}; +use crate::process::TermStatus; use crate::vm::perms::VmPerms; use crate::vm::vmo::{VmoOptions, VmoRightsOp}; use crate::{ @@ -11,6 +13,7 @@ use crate::{ vm::{vmar::Vmar, vmo::Vmo}, }; use align_ext::AlignExt; +use jinux_frame::task::Task; use jinux_frame::vm::{VmIo, VmPerm}; use jinux_rights::{Full, Rights}; use xmas_elf::program::{self, ProgramHeader64}; @@ -21,8 +24,8 @@ use super::elf_file::Elf; /// 1. read the vaddr of each segment to get all elf pages. /// 2. create a vmo for each elf segment, create a pager for each segment. Then map the vmo to the root vmar. /// 3. write proper content to the init stack. -pub fn load_elf_to_root_vmar( - root_vmar: &Vmar, +pub fn load_elf_to_vm( + process_vm: &ProcessVm, file_header: &[u8], elf_file: Arc, fs_resolver: &FsResolver, @@ -30,13 +33,75 @@ pub fn load_elf_to_root_vmar( envp: Vec, ) -> Result { let elf = Elf::parse_elf(file_header)?; - let ldso_load_info = if let Ok(ldso_load_info) = - load_ldso_for_shared_object(root_vmar, &elf, file_header, fs_resolver) - { - Some(ldso_load_info) + + let ldso = if elf.is_shared_object() { + Some(lookup_and_parse_ldso(&elf, file_header, fs_resolver)?) } else { None }; + + process_vm.clear(); + + match init_and_map_vmos(process_vm, ldso, &elf, &elf_file, argv, envp) { + Ok(elf_load_info) => return Ok(elf_load_info), + Err(e) => { + // Since the process_vm is cleared, the process cannot return to user space again, + // so exit_group is called here. + + // FIXME: if `current` macro is used when creating the init process, + // the macro will panic. This corner case should be handled later. + let current = current!(); + // FIXME: how to set the correct exit status? + current.exit_group(TermStatus::Exited(1)); + Task::current().exit(); + } + } +} + +fn lookup_and_parse_ldso( + elf: &Elf, + file_header: &[u8], + fs_resolver: &FsResolver, +) -> Result<(Arc, Elf)> { + let ldso_file = { + let ldso_path = elf.ldso_path(file_header)?; + let fs_path = FsPath::new(AT_FDCWD, &ldso_path)?; + fs_resolver.lookup(&fs_path)? + }; + let ldso_elf = { + let mut buf = Box::new([0u8; PAGE_SIZE]); + let vnode = ldso_file.vnode(); + vnode.read_at(0, &mut *buf)?; + Elf::parse_elf(&*buf)? + }; + Ok((ldso_file, ldso_elf)) +} + +fn load_ldso(root_vmar: &Vmar, ldso_file: &Dentry, ldso_elf: &Elf) -> Result { + let map_addr = map_segment_vmos(&ldso_elf, root_vmar, &ldso_file)?; + Ok(LdsoLoadInfo::new( + ldso_elf.entry_point() + map_addr, + map_addr, + )) +} + +fn init_and_map_vmos( + process_vm: &ProcessVm, + ldso: Option<(Arc, Elf)>, + elf: &Elf, + elf_file: &Dentry, + argv: Vec, + envp: Vec, +) -> Result { + let root_vmar = process_vm.root_vmar(); + + // After we clear process vm, if any error happens, we must call exit_group instead of return to user space. + let ldso_load_info = if let Some((ldso_file, ldso_elf)) = ldso { + Some(load_ldso(root_vmar, &ldso_file, &ldso_elf)?) + } else { + None + }; + let map_addr = map_segment_vmos(&elf, root_vmar, &elf_file)?; let mut aux_vec = init_aux_vec(&elf, map_addr)?; let mut init_stack = InitStack::new_default_config(argv, envp); @@ -55,37 +120,9 @@ pub fn load_elf_to_root_vmar( }; let elf_load_info = ElfLoadInfo::new(entry_point, init_stack.user_stack_top()); - debug!("load elf succeeds."); Ok(elf_load_info) } -fn load_ldso_for_shared_object( - root_vmar: &Vmar, - elf: &Elf, - file_header: &[u8], - fs_resolver: &FsResolver, -) -> Result { - if !elf.is_shared_object() { - return_errno_with_message!(Errno::EINVAL, "not shared object"); - } - let ldso_file = { - let ldso_path = elf.ldso_path(file_header)?; - let fs_path = FsPath::new(AT_FDCWD, &ldso_path)?; - fs_resolver.lookup(&fs_path)? - }; - let ldso_elf = { - let mut buf = Box::new([0u8; PAGE_SIZE]); - let vnode = ldso_file.vnode(); - vnode.read_at(0, &mut *buf)?; - Elf::parse_elf(&*buf)? - }; - let map_addr = map_segment_vmos(&ldso_elf, root_vmar, &ldso_file)?; - Ok(LdsoLoadInfo::new( - ldso_elf.entry_point() + map_addr, - map_addr, - )) -} - pub struct LdsoLoadInfo { entry_point: Vaddr, base_addr: Vaddr, diff --git a/services/libs/jinux-std/src/process/program_loader/elf/mod.rs b/services/libs/jinux-std/src/process/program_loader/elf/mod.rs index 0449d134..20ebdd39 100644 --- a/services/libs/jinux-std/src/process/program_loader/elf/mod.rs +++ b/services/libs/jinux-std/src/process/program_loader/elf/mod.rs @@ -4,4 +4,4 @@ mod init_stack; mod load_elf; pub use init_stack::INIT_STACK_SIZE; -pub use load_elf::{load_elf_to_root_vmar, ElfLoadInfo}; +pub use load_elf::{load_elf_to_vm, ElfLoadInfo}; diff --git a/services/libs/jinux-std/src/process/program_loader/mod.rs b/services/libs/jinux-std/src/process/program_loader/mod.rs index a3c5ee11..2544ccb1 100644 --- a/services/libs/jinux-std/src/process/program_loader/mod.rs +++ b/services/libs/jinux-std/src/process/program_loader/mod.rs @@ -4,12 +4,12 @@ mod shebang; use crate::fs::fs_resolver::{FsPath, FsResolver, AT_FDCWD}; use crate::fs::utils::Dentry; use crate::prelude::*; -use crate::vm::vmar::Vmar; -use jinux_rights::Full; -use self::elf::{load_elf_to_root_vmar, ElfLoadInfo}; +use self::elf::{load_elf_to_vm, ElfLoadInfo}; use self::shebang::parse_shebang_line; +use super::process_vm::ProcessVm; + /// Load an executable to root vmar, including loading programe image, preparing heap and stack, /// initializing argv, envp and aux tables. /// About recursion_limit: recursion limit is used to limit th recursion depth of shebang executables. @@ -17,8 +17,8 @@ use self::shebang::parse_shebang_line; /// then it will trigger recursion. We will try to setup root vmar for the interpreter. /// I guess for most cases, setting the recursion_limit as 1 should be enough. /// because the interpreter is usually an elf binary(e.g., /bin/bash) -pub fn load_program_to_root_vmar( - root_vmar: &Vmar, +pub fn load_program_to_vm( + process_vm: &ProcessVm, elf_file: Arc, argv: Vec, envp: Vec, @@ -44,8 +44,8 @@ pub fn load_program_to_root_vmar( fs_resolver.lookup(&fs_path)? }; check_executable_file(&interpreter)?; - return load_program_to_root_vmar( - root_vmar, + return load_program_to_vm( + process_vm, interpreter, new_argv, envp, @@ -54,7 +54,7 @@ pub fn load_program_to_root_vmar( ); } let elf_load_info = - load_elf_to_root_vmar(root_vmar, &*file_header, elf_file, fs_resolver, argv, envp)?; + load_elf_to_vm(process_vm, &*file_header, elf_file, fs_resolver, argv, envp)?; Ok((abs_path, elf_load_info)) } diff --git a/services/libs/jinux-std/src/syscall/execve.rs b/services/libs/jinux-std/src/syscall/execve.rs index a89bfcb9..47c7fe3b 100644 --- a/services/libs/jinux-std/src/syscall/execve.rs +++ b/services/libs/jinux-std/src/syscall/execve.rs @@ -8,7 +8,7 @@ use crate::log_syscall_entry; use crate::prelude::*; use crate::process::posix_thread::name::ThreadName; use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; -use crate::process::program_loader::{check_executable_file, load_program_to_root_vmar}; +use crate::process::program_loader::{check_executable_file, load_program_to_vm}; use crate::syscall::{SYS_EXECVE, SYS_EXECVEAT}; use crate::util::{read_cstring_from_user, read_val_from_user}; @@ -96,15 +96,13 @@ fn do_execve( *posix_thread.clear_child_tid().lock() = 0; let current = current!(); - // destroy root vmars - let root_vmar = current.root_vmar(); - root_vmar.clear()?; - current.user_vm().set_default()?; - // load elf content to new vm space - let fs_resolver = &*current.fs().read(); + debug!("load program to root vmar"); - let (new_executable_path, elf_load_info) = - load_program_to_root_vmar(root_vmar, elf_file, argv, envp, fs_resolver, 1)?; + let (new_executable_path, elf_load_info) = { + let fs_resolver = &*current.fs().read(); + let process_vm = current.process_vm(); + load_program_to_vm(process_vm, elf_file, argv, envp, fs_resolver, 1)? + }; debug!("load elf in execve succeeds"); // set executable path *current.executable_path().write() = new_executable_path;