From 29ebf8e60cd6b7979df1646c48fa7bf4611681d6 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Tue, 26 Mar 2024 07:54:08 +0000 Subject: [PATCH] Support reading argv and envp from init stack --- kernel/aster-nix/src/process/clone.rs | 4 +- kernel/aster-nix/src/process/mod.rs | 1 + kernel/aster-nix/src/process/process/mod.rs | 10 +- .../process_vm/{user_heap.rs => heap.rs} | 70 +-- .../elf => process_vm/init_stack}/aux_vec.rs | 2 - .../src/process/process_vm/init_stack/mod.rs | 410 ++++++++++++++++++ .../aster-nix/src/process/process_vm/mod.rs | 92 ++-- .../process/program_loader/elf/init_stack.rs | 363 ---------------- .../process/program_loader/elf/load_elf.rs | 142 ++++-- .../src/process/program_loader/elf/mod.rs | 3 - .../src/process/program_loader/mod.rs | 41 +- kernel/aster-nix/src/process/rlimit.rs | 2 +- kernel/aster-nix/src/syscall/brk.rs | 2 +- kernel/aster-nix/src/syscall/constants.rs | 4 - kernel/aster-nix/src/syscall/execve.rs | 2 +- kernel/aster-nix/src/util/mod.rs | 14 +- kernel/aster-nix/src/vdso.rs | 5 +- kernel/aster-nix/src/vm/vmar/mod.rs | 6 + 18 files changed, 654 insertions(+), 519 deletions(-) rename kernel/aster-nix/src/process/process_vm/{user_heap.rs => heap.rs} (54%) rename kernel/aster-nix/src/process/{program_loader/elf => process_vm/init_stack}/aux_vec.rs (98%) create mode 100644 kernel/aster-nix/src/process/process_vm/init_stack/mod.rs delete mode 100644 kernel/aster-nix/src/process/program_loader/elf/init_stack.rs diff --git a/kernel/aster-nix/src/process/clone.rs b/kernel/aster-nix/src/process/clone.rs index a9108c177..2ed1cdc8e 100644 --- a/kernel/aster-nix/src/process/clone.rs +++ b/kernel/aster-nix/src/process/clone.rs @@ -358,9 +358,7 @@ fn clone_vm(parent_process_vm: &ProcessVm, clone_flags: CloneFlags) -> Result::fork_from(parent_process_vm.root_vmar())?; - let user_heap = parent_process_vm.user_heap().clone(); - Ok(ProcessVm::new(user_heap, root_vmar)) + ProcessVm::fork_from(parent_process_vm) } } diff --git a/kernel/aster-nix/src/process/mod.rs b/kernel/aster-nix/src/process/mod.rs index 71fbacb42..090eebd16 100644 --- a/kernel/aster-nix/src/process/mod.rs +++ b/kernel/aster-nix/src/process/mod.rs @@ -26,6 +26,7 @@ pub use process::{ Terminal, }; pub use process_filter::ProcessFilter; +pub use process_vm::{MAX_ARGV_NUMBER, MAX_ARG_LEN, MAX_ENVP_NUMBER, MAX_ENV_LEN}; pub use program_loader::{check_executable_file, load_program_to_vm}; pub use rlimit::ResourceType; pub use term_status::TermStatus; diff --git a/kernel/aster-nix/src/process/process/mod.rs b/kernel/aster-nix/src/process/process/mod.rs index 7792c97f9..b818e5cb4 100644 --- a/kernel/aster-nix/src/process/process/mod.rs +++ b/kernel/aster-nix/src/process/process/mod.rs @@ -3,7 +3,7 @@ use super::{ posix_thread::PosixThreadExt, process_table, - process_vm::{user_heap::UserHeap, ProcessVm}, + process_vm::{Heap, InitStackReader, ProcessVm}, rlimit::ResourceLimits, signal::{ constants::SIGCHLD, sig_disposition::SigDispositions, sig_mask::SigMask, signals::Signal, @@ -501,8 +501,12 @@ impl Process { self.process_vm.root_vmar() } - pub fn user_heap(&self) -> &UserHeap { - self.process_vm.user_heap() + pub fn heap(&self) -> &Heap { + self.process_vm.heap() + } + + pub fn init_stack_reader(&self) -> InitStackReader { + self.process_vm.init_stack_reader() } // ************** File system **************** diff --git a/kernel/aster-nix/src/process/process_vm/user_heap.rs b/kernel/aster-nix/src/process/process_vm/heap.rs similarity index 54% rename from kernel/aster-nix/src/process/process_vm/user_heap.rs rename to kernel/aster-nix/src/process/process_vm/heap.rs index 6853419b9..bf5049b36 100644 --- a/kernel/aster-nix/src/process/process_vm/user_heap.rs +++ b/kernel/aster-nix/src/process/process_vm/heap.rs @@ -14,38 +14,48 @@ use crate::{ }, }; +/// The base address of user heap pub const USER_HEAP_BASE: Vaddr = 0x0000_0000_1000_0000; -pub const USER_HEAP_SIZE_LIMIT: usize = PAGE_SIZE * 1000; +/// The max allowed size of user heap +pub const USER_HEAP_SIZE_LIMIT: usize = PAGE_SIZE * 1000; // 4MB #[derive(Debug)] -pub struct UserHeap { - /// the low address of user heap - heap_base: Vaddr, - /// the max heap size - heap_size_limit: usize, +pub struct Heap { + /// The lowest address of the heap + base: Vaddr, + /// The heap size limit + limit: usize, + /// The current heap highest address current_heap_end: AtomicUsize, } -impl UserHeap { +impl Heap { pub const fn new() -> Self { - UserHeap { - heap_base: USER_HEAP_BASE, - heap_size_limit: USER_HEAP_SIZE_LIMIT, + Heap { + base: USER_HEAP_BASE, + limit: USER_HEAP_SIZE_LIMIT, current_heap_end: AtomicUsize::new(USER_HEAP_BASE), } } - 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(); - let vmar_map_options = root_vmar - .new_map(heap_vmo, perms) - .unwrap() - .offset(self.heap_base) - .size(self.heap_size_limit); - vmar_map_options.build().unwrap(); - self.current_heap_end.load(Ordering::Relaxed) + /// Inits and maps the heap Vmo + pub(super) fn alloc_and_map_vmo(&self, root_vmar: &Vmar) -> Result<()> { + let heap_vmo = { + let vmo_options = VmoOptions::::new(0).flags(VmoFlags::RESIZABLE); + vmo_options.alloc()? + }; + let vmar_map_options = { + let perms = VmPerms::READ | VmPerms::WRITE; + root_vmar + .new_map(heap_vmo, perms) + .unwrap() + .offset(self.base) + .size(self.limit) + }; + vmar_map_options.build()?; + + self.set_uninitialized(); + Ok(()) } pub fn brk(&self, new_heap_end: Option) -> Result { @@ -54,7 +64,7 @@ impl UserHeap { match new_heap_end { None => Ok(self.current_heap_end.load(Ordering::Relaxed)), Some(new_heap_end) => { - if new_heap_end > self.heap_base + self.heap_size_limit { + if new_heap_end > self.base + self.limit { return_errno_with_message!(Errno::ENOMEM, "heap size limit was met."); } let current_heap_end = self.current_heap_end.load(Ordering::Acquire); @@ -62,7 +72,7 @@ impl UserHeap { // FIXME: should we allow shrink current user heap? return Ok(current_heap_end); } - let new_size = (new_heap_end - self.heap_base).align_up(PAGE_SIZE); + let new_size = (new_heap_end - self.base).align_up(PAGE_SIZE); let heap_mapping = root_vmar.get_vm_mapping(USER_HEAP_BASE)?; let heap_vmo = heap_mapping.vmo(); heap_vmo.resize(new_size)?; @@ -72,21 +82,17 @@ 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, root_vmar: &Vmar) { - self.current_heap_end - .store(self.heap_base, Ordering::Relaxed); - self.init(root_vmar); + pub(super) fn set_uninitialized(&self) { + self.current_heap_end.store(self.base, Ordering::Relaxed); } } -impl Clone for UserHeap { +impl Clone for Heap { fn clone(&self) -> Self { let current_heap_end = self.current_heap_end.load(Ordering::Relaxed); Self { - heap_base: self.heap_base, - heap_size_limit: self.heap_size_limit, + base: self.base, + limit: self.limit, current_heap_end: AtomicUsize::new(current_heap_end), } } diff --git a/kernel/aster-nix/src/process/program_loader/elf/aux_vec.rs b/kernel/aster-nix/src/process/process_vm/init_stack/aux_vec.rs similarity index 98% rename from kernel/aster-nix/src/process/program_loader/elf/aux_vec.rs rename to kernel/aster-nix/src/process/process_vm/init_stack/aux_vec.rs index e125cbd90..92d9d39fc 100644 --- a/kernel/aster-nix/src/process/program_loader/elf/aux_vec.rs +++ b/kernel/aster-nix/src/process/process_vm/init_stack/aux_vec.rs @@ -2,8 +2,6 @@ use crate::prelude::*; -/// This implementation is from occlum. - /// Auxiliary Vector. /// /// # What is Auxiliary Vector? diff --git a/kernel/aster-nix/src/process/process_vm/init_stack/mod.rs b/kernel/aster-nix/src/process/process_vm/init_stack/mod.rs new file mode 100644 index 000000000..e6188a29b --- /dev/null +++ b/kernel/aster-nix/src/process/process_vm/init_stack/mod.rs @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! The init stack for the process. +//! The init stack is used to store the `argv` and `envp` and auxiliary vectors. +//! We can read `argv` and `envp` of a process from the init stack. +//! Usually, the lowest address of init stack is +//! the highest address of the user stack of the first thread. +//! +//! However, the init stack will be mapped to user space +//! and the user process can write the content of init stack, +//! so the content reading from init stack may not be the same as the process init status. +//! + +use core::{ + mem, + sync::atomic::{AtomicUsize, Ordering}, +}; + +use align_ext::AlignExt; +use aster_frame::vm::{VmIo, MAX_USERSPACE_VADDR}; +use aster_rights::{Full, Rights}; + +use self::aux_vec::{AuxKey, AuxVec}; +use crate::{ + prelude::*, + util::read_cstring_from_vmar, + vm::{perms::VmPerms, vmar::Vmar, vmo::VmoOptions}, +}; + +pub mod aux_vec; + +/// Set the initial stack size to 8 megabytes, following the default Linux stack size limit. +pub const INIT_STACK_SIZE: usize = 8 * 1024 * 1024; // 8 MB + +/// The max number of arguments that can be used to creating a new process. +pub const MAX_ARGV_NUMBER: usize = 128; +/// The max number of environmental variables that can be used to creating a new process. +pub const MAX_ENVP_NUMBER: usize = 128; +/// The max length of each argument to create a new process. +pub const MAX_ARG_LEN: usize = 2048; +/// The max length of each environmental variable (the total length of key-value pair) to create a new process. +pub const MAX_ENV_LEN: usize = 128; + +/* + * Illustration of the virtual memory space containing the processes' init stack: + * + * (high address) + * +---------------------+ <------+ Highest address + * | | Random stack paddings + * +---------------------+ <------+ The base of stack (stack grows down) + * | | + * | Null-terminated | + * | strings referenced | + * | by variables below | + * | | + * +---------------------+ + * | AT_NULL | + * +---------------------+ + * | AT_NULL | + * +---------------------+ + * | ... | + * +---------------------+ + * | aux_val[0] | + * +---------------------+ + * | aux_key[0] | <------+ Auxiliary table + * +---------------------+ + * | NULL | + * +---------------------+ + * | ... | + * +---------------------+ + * | char* envp[0] | <------+ Environment variables + * +---------------------+ + * | NULL | + * +---------------------+ + * | char* argv[argc-1] | + * +---------------------+ + * | ... | + * +---------------------+ + * | char* argv[0] | + * +---------------------+ + * | long argc | <------+ Program arguments + * +---------------------+ + * | | + * | | + * +---------------------+ + * | | + * +---------------------+ <------+ User stack default rlimit + * (low address) + */ + +/// The initial portion of the main stack of a process. +#[derive(Debug, Clone)] +pub struct InitStack { + /// The initial highest address. + /// The stack grows down from this address + initial_top: Vaddr, + /// The max allowed stack size + max_size: usize, + /// The current stack pointer. + /// Before initialized, `pos` points to the `initial_top`, + /// After initialized, `pos` points to the user stack pointer(rsp) + /// of the process. + pos: Arc, +} + +impl InitStack { + pub(super) fn new() -> Self { + let nr_pages_padding = { + let mut random_nr_pages_padding: u8 = 0; + getrandom::getrandom(random_nr_pages_padding.as_bytes_mut()).unwrap(); + random_nr_pages_padding as usize + }; + let initial_top = MAX_USERSPACE_VADDR - PAGE_SIZE * nr_pages_padding; + let max_size = INIT_STACK_SIZE; + Self { + initial_top, + max_size, + pos: Arc::new(AtomicUsize::new(initial_top)), + } + } + + /// Init and map the vmo for init stack + pub(super) fn alloc_and_map_vmo(&self, root_vmar: &Vmar) -> Result<()> { + let vmo = { + let vmo_options = VmoOptions::::new(self.max_size); + vmo_options.alloc()? + }; + + let vmar_map_options = { + let perms = VmPerms::READ | VmPerms::WRITE; + let map_addr = self.initial_top - self.max_size; + debug_assert!(map_addr % PAGE_SIZE == 0); + root_vmar.new_map(vmo, perms)?.offset(map_addr) + }; + + vmar_map_options.build()?; + + self.set_uninitialized(); + Ok(()) + } + + /// Returns the user stack top(highest address), used to setup rsp. + /// + /// This method should only be called after the stack is initialized. + pub fn user_stack_top(&self) -> Vaddr { + let stack_top = self.pos(); + debug_assert!(self.is_initialized()); + + stack_top + } + + pub(super) fn writer<'a>( + &self, + vmar: &'a Vmar, + argv: Vec, + envp: Vec, + auxvec: AuxVec, + ) -> InitStackWriter<'a> { + // The stack should be written only once. + debug_assert!(!self.is_initialized()); + InitStackWriter { + pos: self.pos.clone(), + vmar, + argv, + envp, + auxvec, + } + } + + pub(super) fn reader<'a>(&self, vmar: &'a Vmar) -> InitStackReader<'a> { + // The stack should only be read after initialized + debug_assert!(self.is_initialized()); + InitStackReader { + base: self.pos(), + vmar, + } + } + + fn is_initialized(&self) -> bool { + self.pos() != self.initial_top + } + + fn set_uninitialized(&self) { + self.pos.store(self.initial_top, Ordering::Relaxed); + } + + fn pos(&self) -> Vaddr { + self.pos.load(Ordering::Relaxed) + } +} + +/// A writer to initialize the content of an `InitStack`. +pub struct InitStackWriter<'a> { + pos: Arc, + vmar: &'a Vmar, + argv: Vec, + envp: Vec, + auxvec: AuxVec, +} + +impl<'a> InitStackWriter<'a> { + pub fn write(mut self) -> Result<()> { + // FIXME: Some OSes may put the first page of excutable file here + // for interpreting elf headers. + + let argc = self.argv.len() as u64; + + // Write envp string + let envp_pointers = self.write_envp_strings()?; + // Write argv string + let argv_pointers = self.write_argv_strings()?; + // Generate random values for auxvec + let random_value_pointer = { + let random_value = generate_random_for_aux_vec(); + self.write_bytes(&random_value)? + }; + self.auxvec.set(AuxKey::AT_RANDOM, random_value_pointer)?; + + self.adjust_stack_alignment(&envp_pointers, &argv_pointers)?; + self.write_aux_vec()?; + self.write_envp_pointers(envp_pointers)?; + self.write_argv_pointers(argv_pointers)?; + + // write argc + self.write_u64(argc)?; + + // Ensure stack top is 16-bytes aligned + debug_assert_eq!(self.pos() & !0xf, self.pos()); + + Ok(()) + } + + fn write_envp_strings(&self) -> Result> { + let mut envp_pointers = Vec::with_capacity(self.envp.len()); + for envp in self.envp.iter() { + let pointer = self.write_cstring(envp)?; + envp_pointers.push(pointer); + } + Ok(envp_pointers) + } + + fn write_argv_strings(&self) -> Result> { + let mut argv_pointers = Vec::with_capacity(self.argv.len()); + for argv in self.argv.iter().rev() { + let pointer = self.write_cstring(argv)?; + debug!("argv address = 0x{:x}", pointer); + argv_pointers.push(pointer); + } + argv_pointers.reverse(); + Ok(argv_pointers) + } + + /// Libc ABI requires 16-byte alignment of the stack entrypoint. + /// Current postion of the stack is 8-byte aligned already, insert 8 byte + /// to meet the requirement if necessary. + fn adjust_stack_alignment(&self, envp_pointers: &[u64], argv_pointers: &[u64]) -> Result<()> { + // Ensure 8-byte alignment + self.write_u64(0)?; + let auxvec_size = (self.auxvec.table().len() + 1) * (mem::size_of::() * 2); + let envp_pointers_size = (envp_pointers.len() + 1) * mem::size_of::(); + let argv_pointers_size = (argv_pointers.len() + 1) * mem::size_of::(); + let argc_size = mem::size_of::(); + let to_write_size = auxvec_size + envp_pointers_size + argv_pointers_size + argc_size; + if (self.pos() - to_write_size) % 16 != 0 { + self.write_u64(0)?; + } + Ok(()) + } + + fn write_aux_vec(&self) -> Result<()> { + // Write NULL auxilary + self.write_u64(0)?; + self.write_u64(AuxKey::AT_NULL as u64)?; + // Write Auxiliary vectors + let aux_vec: Vec<_> = self + .auxvec + .table() + .iter() + .map(|(aux_key, aux_value)| (*aux_key, *aux_value)) + .collect(); + for (aux_key, aux_value) in aux_vec.iter() { + self.write_u64(*aux_value)?; + self.write_u64(*aux_key as u64)?; + } + Ok(()) + } + + fn write_envp_pointers(&self, mut envp_pointers: Vec) -> Result<()> { + // write NULL pointer + self.write_u64(0)?; + // write envp pointers + envp_pointers.reverse(); + for envp_pointer in envp_pointers { + self.write_u64(envp_pointer)?; + } + Ok(()) + } + + fn write_argv_pointers(&self, mut argv_pointers: Vec) -> Result<()> { + // write 0 + self.write_u64(0)?; + // write argv pointers + argv_pointers.reverse(); + for argv_pointer in argv_pointers { + self.write_u64(argv_pointer)?; + } + Ok(()) + } + + /// Writes u64 to the stack. + /// Returns the writing address + fn write_u64(&self, val: u64) -> Result { + let start_address = (self.pos() - 8).align_down(8); + self.pos.store(start_address, Ordering::Relaxed); + self.vmar.write_val(start_address, &val)?; + Ok(self.pos() as u64) + } + + /// Writes a CString including the ending null byte to the stack. + /// Returns the writing address + fn write_cstring(&self, val: &CString) -> Result { + let bytes = val.as_bytes_with_nul(); + self.write_bytes(bytes) + } + + /// Writes u64 to the stack. + /// Returns the writing address. + fn write_bytes(&self, bytes: &[u8]) -> Result { + let len = bytes.len(); + self.pos.fetch_sub(len, Ordering::Relaxed); + let pos = self.pos(); + self.vmar.write_bytes(pos, bytes)?; + Ok(pos as u64) + } + + fn pos(&self) -> Vaddr { + self.pos.load(Ordering::Relaxed) + } +} + +fn generate_random_for_aux_vec() -> [u8; 16] { + let mut rand_val = [0; 16]; + getrandom::getrandom(&mut rand_val).unwrap(); + rand_val +} + +/// A reader to parse the content of an `InitStack`. +pub struct InitStackReader<'a> { + base: Vaddr, + vmar: &'a Vmar, +} + +impl<'a> InitStackReader<'a> { + /// Read argc from the process init stack + pub fn argc(&self) -> Result { + let stack_base = self.user_stack_top(); + Ok(self.vmar.read_val(stack_base)?) + } + + /// Read argv from the process init stack + pub fn argv(&self) -> Result> { + let argc = self.argc()? as usize; + // base = stack bottom + the size of argc + let base = self.user_stack_top() + 8; + + let mut argv = Vec::with_capacity(argc); + for i in 0..argc { + let arg_ptr = { + let offset = base + i * 8; + self.vmar.read_val::(offset)? + }; + + let arg = read_cstring_from_vmar(self.vmar, arg_ptr, MAX_ARG_LEN)?; + argv.push(arg); + } + + Ok(argv) + } + + /// Read envp from the process + pub fn envp(&self) -> Result> { + let argc = self.argc()? as usize; + // base = stack bottom + // + the size of argc(8) + // + the size of arg pointer(8) * the number of arg(argc) + // + the size of null pointer(8) + let base = self.user_stack_top() + 8 + 8 * argc + 8; + + let mut envp = Vec::new(); + for i in 0..MAX_ENVP_NUMBER { + let envp_ptr = { + let offset = base + i * 8; + self.vmar.read_val::(offset)? + }; + + if envp_ptr == 0 { + break; + } + + let env = read_cstring_from_vmar(self.vmar, envp_ptr, MAX_ENV_LEN)?; + envp.push(env); + } + + Ok(envp) + } + + pub const fn user_stack_top(&self) -> Vaddr { + self.base + } +} diff --git a/kernel/aster-nix/src/process/process_vm/mod.rs b/kernel/aster-nix/src/process/process_vm/mod.rs index 52a7541ae..51b8bf58a 100644 --- a/kernel/aster-nix/src/process/process_vm/mod.rs +++ b/kernel/aster-nix/src/process/process_vm/mod.rs @@ -1,17 +1,29 @@ // SPDX-License-Identifier: MPL-2.0 -//! This module defines the UserVm of a process. -//! The UserSpace of a process only contains the virtual-physical memory mapping. -//! But we cannot know which vaddr is user heap, which vaddr is mmap areas. -//! So we define a UserVm struct to store such infomation. -//! Briefly, it contains the exact usage of each segment of virtual spaces. +//! This module defines struct `ProcessVm` +//! to represent the layout of user space process virtual memory. +//! +//! The `ProcessVm` struct contains `Vmar`, +//! which stores all existing memory mappings. +//! The `Vm` also contains +//! the basic info of process level vm segments, +//! like init stack and heap. -pub mod user_heap; +mod heap; +mod init_stack; use aster_rights::Full; -use user_heap::UserHeap; +pub use heap::Heap; -use crate::vm::vmar::Vmar; +pub use self::{ + heap::USER_HEAP_SIZE_LIMIT, + init_stack::{ + aux_vec::{AuxKey, AuxVec}, + InitStack, InitStackReader, InitStackWriter, INIT_STACK_SIZE, MAX_ARGV_NUMBER, MAX_ARG_LEN, + MAX_ENVP_NUMBER, MAX_ENV_LEN, + }, +}; +use crate::{prelude::*, vm::vmar::Vmar}; /* * The user's virtual memory space layout looks like below. @@ -49,51 +61,77 @@ use crate::vm::vmar::Vmar; * (low address) */ -/// The virtual space usage. -/// This struct is used to control brk and mmap now. +// The process user space virtual memory pub struct ProcessVm { - user_heap: UserHeap, root_vmar: Vmar, + init_stack: InitStack, + heap: Heap, } impl Clone for ProcessVm { fn clone(&self) -> Self { Self { root_vmar: self.root_vmar.dup().unwrap(), - user_heap: self.user_heap.clone(), + init_stack: self.init_stack.clone(), + heap: self.heap.clone(), } } } impl ProcessVm { + /// Allocates a new `ProcessVm` pub fn alloc() -> Self { let root_vmar = Vmar::::new_root(); - let user_heap = UserHeap::new(); - user_heap.init(&root_vmar); - ProcessVm { - user_heap, - root_vmar, - } - } - - pub fn new(user_heap: UserHeap, root_vmar: Vmar) -> Self { + let init_stack = InitStack::new(); + init_stack.alloc_and_map_vmo(&root_vmar).unwrap(); + let heap = Heap::new(); + heap.alloc_and_map_vmo(&root_vmar).unwrap(); Self { - user_heap, root_vmar, + heap, + init_stack, } } - pub fn user_heap(&self) -> &UserHeap { - &self.user_heap + /// Forks a `ProcessVm` from `other`. + /// + /// The returned `ProcessVm` will have a forked `Vmar`. + pub fn fork_from(other: &ProcessVm) -> Result { + let root_vmar = Vmar::::fork_from(&other.root_vmar)?; + Ok(Self { + root_vmar, + heap: other.heap.clone(), + init_stack: other.init_stack.clone(), + }) } pub fn root_vmar(&self) -> &Vmar { &self.root_vmar } - /// Set user vm to the init status - pub fn clear(&self) { + /// Returns a reader for reading contents from + /// the `InitStack`. + pub fn init_stack_reader(&self) -> InitStackReader { + self.init_stack.reader(&self.root_vmar) + } + + pub(super) fn init_stack_writer( + &self, + argv: Vec, + envp: Vec, + aux_vec: AuxVec, + ) -> InitStackWriter { + self.init_stack.writer(&self.root_vmar, argv, envp, aux_vec) + } + + pub(super) fn heap(&self) -> &Heap { + &self.heap + } + + /// Clears existing mappings and then maps stack and heap vmo. + pub(super) fn clear_and_map(&self) { self.root_vmar.clear().unwrap(); - self.user_heap.set_default(&self.root_vmar); + self.init_stack.alloc_and_map_vmo(&self.root_vmar).unwrap(); + self.heap.alloc_and_map_vmo(&self.root_vmar).unwrap(); } } diff --git a/kernel/aster-nix/src/process/program_loader/elf/init_stack.rs b/kernel/aster-nix/src/process/program_loader/elf/init_stack.rs deleted file mode 100644 index 29f1d9cc3..000000000 --- a/kernel/aster-nix/src/process/program_loader/elf/init_stack.rs +++ /dev/null @@ -1,363 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -//! This module defines the process initial stack. -//! The process initial stack, contains arguments, environmental variables and auxiliary vectors -//! The data layout of init stack can be seen in Figure 3.9 in - -use core::mem; - -use align_ext::AlignExt; -use aster_frame::vm::{VmIo, VmPerm, MAX_USERSPACE_VADDR}; -use aster_rights::{Full, Rights}; - -use super::{ - aux_vec::{AuxKey, AuxVec}, - elf_file::Elf, - load_elf::LdsoLoadInfo, -}; -use crate::{ - prelude::*, - vm::{perms::VmPerms, vmar::Vmar, vmo::VmoOptions}, -}; - -/// Set the initial stack size to 8 megabytes, following the default Linux stack size limit. -pub const INIT_STACK_SIZE: usize = 8 * 1024 * 1024; // 8 MB - -/* - * Illustration of the virtual memory space containing the processes' init stack: - * - * (high address) - * +---------------------+ <------+ Highest address - * | | Random stack paddings - * +---------------------+ <------+ The base of stack (stack grows down) - * | | - * | Null-terminated | - * | strings referenced | - * | by variables below | - * | | - * +---------------------+ - * | AT_NULL | - * +---------------------+ - * | AT_NULL | - * +---------------------+ - * | ... | - * +---------------------+ - * | aux_val[0] | - * +---------------------+ - * | aux_key[0] | <------+ Auxiliary table - * +---------------------+ - * | NULL | - * +---------------------+ - * | ... | - * +---------------------+ - * | char* envp[0] | <------+ Environment variables - * +---------------------+ - * | NULL | - * +---------------------+ - * | char* argv[argc-1] | - * +---------------------+ - * | ... | - * +---------------------+ - * | char* argv[0] | - * +---------------------+ - * | long argc | <------+ Program arguments - * +---------------------+ - * | | - * | | - * +---------------------+ - * | | - * +---------------------+ <------+ User stack default rlimit - * (low address) - */ -pub struct InitStack { - /// The high address of init stack - init_stack_top: Vaddr, - init_stack_size: usize, - pos: usize, - /// Command line args - argv: Vec, - /// Environmental variables - envp: Vec, -} - -impl InitStack { - /// initialize user stack on base addr - pub fn new( - init_stack_top: Vaddr, - init_stack_size: usize, - argv: Vec, - envp: Vec, - ) -> Self { - Self { - init_stack_top, - init_stack_size, - pos: init_stack_top, - argv, - envp, - } - } - - pub fn new_default_config(argv: Vec, envp: Vec) -> Self { - let nr_pages_padding = { - let mut random_nr_pages_padding: u8 = 0; - getrandom::getrandom(random_nr_pages_padding.as_bytes_mut()).unwrap(); - random_nr_pages_padding as usize - }; - let init_stack_top = MAX_USERSPACE_VADDR - PAGE_SIZE * nr_pages_padding; - let init_stack_size = INIT_STACK_SIZE; - InitStack::new(init_stack_top, init_stack_size, argv, envp) - } - - /// the user stack top(high address), used to setup rsp - pub fn user_stack_top(&self) -> Vaddr { - let stack_top = self.pos; - // ensure stack top is 16-bytes aligned - debug_assert!(stack_top & !0xf == stack_top); - stack_top - } - - /// the user stack bottom(low address) - const fn user_stack_bottom(&self) -> Vaddr { - self.init_stack_top - self.init_stack_size - } - - pub fn init( - &mut self, - root_vmar: &Vmar, - elf: &Elf, - ldso_load_info: &Option, - aux_vec: &mut AuxVec, - ) -> Result<()> { - self.map_and_zeroed(root_vmar)?; - self.write_stack_content(root_vmar, elf, ldso_load_info, aux_vec)?; - self.debug_print_stack_content(root_vmar); - Ok(()) - } - - fn map_and_zeroed(&self, root_vmar: &Vmar) -> Result<()> { - let vmo_options = VmoOptions::::new(self.init_stack_size); - let vmo = vmo_options.alloc()?; - vmo.clear(0..vmo.size())?; - let perms = VmPerms::READ | VmPerms::WRITE; - let vmar_map_options = root_vmar - .new_map(vmo, perms)? - .offset(self.user_stack_bottom()); - vmar_map_options.build().unwrap(); - Ok(()) - } - - /// Libc ABI requires 16-byte alignment of the stack entrypoint. - /// Current postion of the stack is 8-byte aligned already, insert 8 byte - /// to meet the requirement if necessary. - fn adjust_stack_alignment( - &mut self, - root_vmar: &Vmar, - envp_pointers: &[u64], - argv_pointers: &[u64], - aux_vec: &AuxVec, - ) -> Result<()> { - // ensure 8-byte alignment - self.write_u64(0, root_vmar)?; - let auxvec_size = (aux_vec.table().len() + 1) * (mem::size_of::() * 2); - let envp_pointers_size = (envp_pointers.len() + 1) * mem::size_of::(); - let argv_pointers_size = (argv_pointers.len() + 1) * mem::size_of::(); - let argc_size = mem::size_of::(); - let to_write_size = auxvec_size + envp_pointers_size + argv_pointers_size + argc_size; - if (self.pos - to_write_size) % 16 != 0 { - self.write_u64(0, root_vmar)?; - } - Ok(()) - } - - fn write_stack_content( - &mut self, - root_vmar: &Vmar, - elf: &Elf, - ldso_load_info: &Option, - aux_vec: &mut AuxVec, - ) -> Result<()> { - // FIXME: Some OSes may put the first page of excutable file here - // for interpreting elf headers. - - // write envp string - let envp_pointers = self.write_envp_strings(root_vmar)?; - // write argv string - let argv_pointers = self.write_argv_strings(root_vmar)?; - // write random value - let random_value = generate_random_for_aux_vec(); - let random_value_pointer = self.write_bytes(&random_value, root_vmar)?; - aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?; - if let Some(ldso_load_info) = ldso_load_info { - let ldso_base = ldso_load_info.base_addr(); - aux_vec.set(AuxKey::AT_BASE, ldso_base as u64)?; - } - self.adjust_stack_alignment(root_vmar, &envp_pointers, &argv_pointers, aux_vec)?; - self.write_aux_vec(root_vmar, aux_vec)?; - self.write_envp_pointers(root_vmar, envp_pointers)?; - self.write_argv_pointers(root_vmar, argv_pointers)?; - // write argc - let argc = self.argc(); - self.write_u64(argc, root_vmar)?; - Ok(()) - } - - fn write_envp_strings(&mut self, root_vmar: &Vmar) -> Result> { - let envp = self.envp.to_vec(); - let mut envp_pointers = Vec::with_capacity(envp.len()); - for envp in envp.iter() { - let pointer = self.write_cstring(envp, root_vmar)?; - envp_pointers.push(pointer); - } - Ok(envp_pointers) - } - - fn write_argv_strings(&mut self, root_vmar: &Vmar) -> Result> { - let argv = self.argv.to_vec(); - let mut argv_pointers = Vec::with_capacity(argv.len()); - for argv in argv.iter().rev() { - let pointer = self.write_cstring(argv, root_vmar)?; - debug!("argv address = 0x{:x}", pointer); - argv_pointers.push(pointer); - } - argv_pointers.reverse(); - Ok(argv_pointers) - } - - fn write_aux_vec(&mut self, root_vmar: &Vmar, aux_vec: &AuxVec) -> Result<()> { - // Write NULL auxilary - self.write_u64(0, root_vmar)?; - self.write_u64(AuxKey::AT_NULL as u64, root_vmar)?; - // Write Auxiliary vectors - let aux_vec: Vec<_> = aux_vec - .table() - .iter() - .map(|(aux_key, aux_value)| (*aux_key, *aux_value)) - .collect(); - for (aux_key, aux_value) in aux_vec.iter() { - self.write_u64(*aux_value, root_vmar)?; - self.write_u64(*aux_key as u64, root_vmar)?; - } - Ok(()) - } - - fn write_envp_pointers( - &mut self, - root_vmar: &Vmar, - mut envp_pointers: Vec, - ) -> Result<()> { - // write NULL pointer - self.write_u64(0, root_vmar)?; - // write envp pointers - envp_pointers.reverse(); - for envp_pointer in envp_pointers { - self.write_u64(envp_pointer, root_vmar)?; - } - Ok(()) - } - - fn write_argv_pointers( - &mut self, - root_vmar: &Vmar, - mut argv_pointers: Vec, - ) -> Result<()> { - // write 0 - self.write_u64(0, root_vmar)?; - // write argv pointers - argv_pointers.reverse(); - for argv_pointer in argv_pointers { - self.write_u64(argv_pointer, root_vmar)?; - } - Ok(()) - } - - /// Command line argument counter - pub fn argc(&self) -> u64 { - self.argv.len() as u64 - } - - /// Command linke argument start address - pub fn argv(&self) -> u64 { - self.user_stack_top() as u64 + 8 - } - - /// Environmental variables counter - pub fn envc(&self) -> u64 { - self.envp.len() as u64 - } - - /// Environmental variables pointers - pub fn envp(&self) -> u64 { - 0 - } - - /// returns the top address of init stack. - /// It should points to a fixed address. - pub const fn init_stack_top(&self) -> Vaddr { - self.init_stack_top - } - - /// returns the u64 start address - fn write_u64(&mut self, val: u64, root_vmar: &Vmar) -> Result { - let start_address = (self.pos - 8).align_down(8); - self.pos = start_address; - root_vmar.write_val(start_address, &val)?; - Ok(self.pos as u64) - } - - fn write_bytes(&mut self, bytes: &[u8], root_vmar: &Vmar) -> Result { - let len = bytes.len(); - self.pos -= len; - root_vmar.write_bytes(self.pos, bytes)?; - Ok(self.pos as u64) - } - - /// returns the string start address - /// cstring will with end null byte. - fn write_cstring(&mut self, val: &CString, root_vmar: &Vmar) -> Result { - let bytes = val.as_bytes_with_nul(); - self.write_bytes(bytes, root_vmar) - } - - pub const fn perm() -> VmPerm { - VmPerm::RWU - } - - fn debug_print_stack_content(&self, root_vmar: &Vmar) { - debug!("print stack content:"); - let stack_top = self.user_stack_top(); - let argc = root_vmar.read_val::(stack_top).unwrap(); - debug!("argc = {}", argc); - } -} - -pub fn init_aux_vec(elf: &Elf, elf_map_addr: Vaddr, vdso_text_base: Vaddr) -> Result { - let mut aux_vec = AuxVec::new(); - aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?; - let ph_addr = if elf.is_shared_object() { - elf.ph_addr()? + elf_map_addr - } else { - elf.ph_addr()? - }; - aux_vec.set(AuxKey::AT_PHDR, ph_addr as u64)?; - aux_vec.set(AuxKey::AT_PHNUM, elf.ph_count() as u64)?; - aux_vec.set(AuxKey::AT_PHENT, elf.ph_ent() as u64)?; - let elf_entry = if elf.is_shared_object() { - let base_load_offset = elf.base_load_address_offset(); - elf.entry_point() + elf_map_addr - base_load_offset as usize - } else { - elf.entry_point() - }; - aux_vec.set(AuxKey::AT_ENTRY, elf_entry as u64)?; - aux_vec.set(AuxKey::AT_SYSINFO_EHDR, vdso_text_base as u64)?; - Ok(aux_vec) -} - -/// generate random [u8; 16]. -/// FIXME: generate really random value. Now only return array with fixed values. -fn generate_random_for_aux_vec() -> [u8; 16] { - let mut rand_val = [0; 16]; - for i in 0..16u8 { - rand_val[i as usize] = 0xff - i; - } - rand_val -} diff --git a/kernel/aster-nix/src/process/program_loader/elf/load_elf.rs b/kernel/aster-nix/src/process/program_loader/elf/load_elf.rs index f1a1b404f..7b1c58ec1 100644 --- a/kernel/aster-nix/src/process/program_loader/elf/load_elf.rs +++ b/kernel/aster-nix/src/process/program_loader/elf/load_elf.rs @@ -20,10 +20,10 @@ use crate::{ prelude::*, process::{ do_exit_group, - process_vm::ProcessVm, - program_loader::elf::init_stack::{init_aux_vec, InitStack}, + process_vm::{AuxKey, AuxVec, ProcessVm}, TermStatus, }, + vdso::vdso_vmo, vm::{ perms::VmPerms, vmar::Vmar, @@ -31,10 +31,10 @@ use crate::{ }, }; -/// load elf to the root vmar. this function will -/// 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. +/// Loads elf to the process vm. +/// +/// This function will map elf segments and +/// initialize process init stack. pub fn load_elf_to_vm( process_vm: &ProcessVm, file_header: &[u8], @@ -42,31 +42,48 @@ pub fn load_elf_to_vm( fs_resolver: &FsResolver, argv: Vec, envp: Vec, - vdso_text_base: Vaddr, ) -> Result { - let elf = Elf::parse_elf(file_header)?; + let parsed_elf = Elf::parse_elf(file_header)?; - let ldso = if elf.is_shared_object() { - Some(lookup_and_parse_ldso(&elf, file_header, fs_resolver)?) + let ldso = if parsed_elf.is_shared_object() { + Some(lookup_and_parse_ldso( + &parsed_elf, + file_header, + fs_resolver, + )?) } else { None }; - match init_and_map_vmos( - process_vm, - ldso, - &elf, - &elf_file, - argv, - envp, - vdso_text_base, - ) { - Ok(elf_load_info) => 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. + match init_and_map_vmos(process_vm, ldso, &parsed_elf, &elf_file) { + Ok((entry_point, mut aux_vec)) => { + // Map and set vdso entry. + // Since vdso does not require being mapped to any specific address, + // vdso is mapped after the elf file, heap and stack are mapped. + if let Some(vdso_text_base) = map_vdso_to_vm(process_vm) { + aux_vec + .set(AuxKey::AT_SYSINFO_EHDR, vdso_text_base as u64) + .unwrap(); + } - // FIXME: if `current` macro is used when creating the init process, + let init_stack_writer = process_vm.init_stack_writer(argv, envp, aux_vec); + init_stack_writer.write().unwrap(); + + let user_stack_top = process_vm.init_stack_reader().user_stack_top(); + Ok(ElfLoadInfo { + entry_point, + user_stack_top, + }) + } + Err(_) => { + // Since the process_vm is in invalid state, + // the process cannot return to user space again, + // so `Vmar::clear` and `do_exit_group` are called here. + // FIXME: sending a fault signal is an alternative approach. + process_vm.root_vmar().clear().unwrap(); + + // FIXME: `current` macro will be used in `do_exit_group`. + // if the macro is used when creating the init process, // the macro will panic. This corner case should be handled later. // FIXME: how to set the correct exit status? do_exit_group(TermStatus::Exited(1)); @@ -105,12 +122,9 @@ fn load_ldso(root_vmar: &Vmar, ldso_file: &Dentry, ldso_elf: &Elf) -> Resu fn init_and_map_vmos( process_vm: &ProcessVm, ldso: Option<(Arc, Elf)>, - elf: &Elf, + parsed_elf: &Elf, elf_file: &Dentry, - argv: Vec, - envp: Vec, - vdso_text_base: Vaddr, -) -> Result { +) -> Result<(Vaddr, AuxVec)> { 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. @@ -120,23 +134,27 @@ fn init_and_map_vmos( None }; - let map_addr = map_segment_vmos(elf, root_vmar, elf_file)?; - let mut aux_vec = init_aux_vec(elf, map_addr, vdso_text_base)?; - let mut init_stack = InitStack::new_default_config(argv, envp); - init_stack.init(root_vmar, elf, &ldso_load_info, &mut aux_vec)?; + let elf_map_addr = map_segment_vmos(parsed_elf, root_vmar, elf_file)?; + + let aux_vec = { + let ldso_base = ldso_load_info + .as_ref() + .map(|load_info| load_info.base_addr()); + init_aux_vec(parsed_elf, elf_map_addr, ldso_base)? + }; + let entry_point = if let Some(ldso_load_info) = ldso_load_info { // Normal shared object ldso_load_info.entry_point() - } else if elf.is_shared_object() { + } else if parsed_elf.is_shared_object() { // ldso itself - elf.entry_point() + map_addr + parsed_elf.entry_point() + elf_map_addr } else { // statically linked executable - elf.entry_point() + parsed_elf.entry_point() }; - let elf_load_info = ElfLoadInfo::new(entry_point, init_stack.user_stack_top()); - Ok(elf_load_info) + Ok((entry_point, aux_vec)) } pub struct LdsoLoadInfo { @@ -371,3 +389,51 @@ fn check_segment_align(program_header: &ProgramHeader64) -> Result<()> { } Ok(()) } + +pub fn init_aux_vec(elf: &Elf, elf_map_addr: Vaddr, ldso_base: Option) -> Result { + let mut aux_vec = AuxVec::new(); + aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?; + let ph_addr = if elf.is_shared_object() { + elf.ph_addr()? + elf_map_addr + } else { + elf.ph_addr()? + }; + aux_vec.set(AuxKey::AT_PHDR, ph_addr as u64)?; + aux_vec.set(AuxKey::AT_PHNUM, elf.ph_count() as u64)?; + aux_vec.set(AuxKey::AT_PHENT, elf.ph_ent() as u64)?; + let elf_entry = if elf.is_shared_object() { + let base_load_offset = elf.base_load_address_offset(); + elf.entry_point() + elf_map_addr - base_load_offset as usize + } else { + elf.entry_point() + }; + aux_vec.set(AuxKey::AT_ENTRY, elf_entry as u64)?; + + if let Some(ldso_base) = ldso_base { + aux_vec.set(AuxKey::AT_BASE, ldso_base as u64)?; + } + Ok(aux_vec) +} + +/// Map the vdso vmo to the corresponding virtual memory address. +fn map_vdso_to_vm(process_vm: &ProcessVm) -> Option { + let root_vmar = process_vm.root_vmar(); + let vdso_vmo = vdso_vmo()?; + + let options = root_vmar + .new_map(vdso_vmo.dup().unwrap(), VmPerms::empty()) + .unwrap() + .size(5 * PAGE_SIZE); + let vdso_data_base = options.build().unwrap(); + let vdso_text_base = vdso_data_base + 0x4000; + + let data_perms = VmPerms::READ | VmPerms::WRITE; + let text_perms = VmPerms::READ | VmPerms::EXEC; + root_vmar + .protect(data_perms, vdso_data_base..vdso_data_base + PAGE_SIZE) + .unwrap(); + root_vmar + .protect(text_perms, vdso_text_base..vdso_text_base + PAGE_SIZE) + .unwrap(); + Some(vdso_text_base) +} diff --git a/kernel/aster-nix/src/process/program_loader/elf/mod.rs b/kernel/aster-nix/src/process/program_loader/elf/mod.rs index 35dbc0e75..1983511ef 100644 --- a/kernel/aster-nix/src/process/program_loader/elf/mod.rs +++ b/kernel/aster-nix/src/process/program_loader/elf/mod.rs @@ -1,9 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 -mod aux_vec; mod elf_file; -mod init_stack; mod load_elf; -pub use init_stack::INIT_STACK_SIZE; pub use load_elf::{load_elf_to_vm, ElfLoadInfo}; diff --git a/kernel/aster-nix/src/process/program_loader/mod.rs b/kernel/aster-nix/src/process/program_loader/mod.rs index 035c12fff..ecfee504a 100644 --- a/kernel/aster-nix/src/process/program_loader/mod.rs +++ b/kernel/aster-nix/src/process/program_loader/mod.rs @@ -14,33 +14,8 @@ use crate::{ utils::Dentry, }, prelude::*, - vdso::vdso_vmo, - vm::perms::VmPerms, }; -/// Map the vdso vmo to the corresponding virtual memory address. -pub fn map_vdso_to_vm(process_vm: &ProcessVm) -> Vaddr { - let root_vmar = process_vm.root_vmar(); - let vdso_vmo = vdso_vmo(); - - let options = root_vmar - .new_map(vdso_vmo.dup().unwrap(), VmPerms::empty()) - .unwrap() - .size(5 * PAGE_SIZE); - let vdso_data_base = options.build().unwrap(); - let vdso_text_base = vdso_data_base + 0x4000; - - let data_perms = VmPerms::READ | VmPerms::WRITE; - let text_perms = VmPerms::READ | VmPerms::EXEC; - root_vmar - .protect(data_perms, vdso_data_base..vdso_data_base + PAGE_SIZE) - .unwrap(); - root_vmar - .protect(text_perms, vdso_text_base..vdso_text_base + PAGE_SIZE) - .unwrap(); - vdso_text_base -} - /// 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. @@ -84,17 +59,11 @@ pub fn load_program_to_vm( recursion_limit - 1, ); } - process_vm.clear(); - let vdso_text_base = map_vdso_to_vm(process_vm); - let elf_load_info = load_elf_to_vm( - process_vm, - &*file_header, - elf_file, - fs_resolver, - argv, - envp, - vdso_text_base, - )?; + + process_vm.clear_and_map(); + + let elf_load_info = + load_elf_to_vm(process_vm, &*file_header, elf_file, fs_resolver, argv, envp)?; Ok((abs_path, elf_load_info)) } diff --git a/kernel/aster-nix/src/process/rlimit.rs b/kernel/aster-nix/src/process/rlimit.rs index aa7a8e6f0..6ef1a178d 100644 --- a/kernel/aster-nix/src/process/rlimit.rs +++ b/kernel/aster-nix/src/process/rlimit.rs @@ -2,7 +2,7 @@ #![allow(non_camel_case_types)] -use super::{process_vm::user_heap::USER_HEAP_SIZE_LIMIT, program_loader::elf::INIT_STACK_SIZE}; +use super::process_vm::{INIT_STACK_SIZE, USER_HEAP_SIZE_LIMIT}; use crate::prelude::*; pub struct ResourceLimits { diff --git a/kernel/aster-nix/src/syscall/brk.rs b/kernel/aster-nix/src/syscall/brk.rs index c7e1fbd38..da0b77d97 100644 --- a/kernel/aster-nix/src/syscall/brk.rs +++ b/kernel/aster-nix/src/syscall/brk.rs @@ -16,7 +16,7 @@ pub fn sys_brk(heap_end: u64) -> Result { }; debug!("new heap end = {:x?}", heap_end); let current = current!(); - let user_heap = current.user_heap(); + let user_heap = current.heap(); let new_heap_end = user_heap.brk(new_heap_end)?; Ok(SyscallReturn::Return(new_heap_end as _)) diff --git a/kernel/aster-nix/src/syscall/constants.rs b/kernel/aster-nix/src/syscall/constants.rs index 80d600ab6..f3ed50aea 100644 --- a/kernel/aster-nix/src/syscall/constants.rs +++ b/kernel/aster-nix/src/syscall/constants.rs @@ -4,7 +4,3 @@ /// LONGEST ALLOWED FILENAME pub const MAX_FILENAME_LEN: usize = 4096; -pub const MAX_ARGV_NUMBER: usize = 128; -pub const MAX_ENVP_NUMBER: usize = 128; -pub const MAX_ARG_LEN: usize = 2048; -pub const MAX_ENV_LEN: usize = 128; diff --git a/kernel/aster-nix/src/syscall/execve.rs b/kernel/aster-nix/src/syscall/execve.rs index 2a32c1cc0..f43c83027 100644 --- a/kernel/aster-nix/src/syscall/execve.rs +++ b/kernel/aster-nix/src/syscall/execve.rs @@ -15,7 +15,7 @@ use crate::{ process::{ check_executable_file, credentials_mut, load_program_to_vm, posix_thread::{PosixThreadExt, ThreadName}, - Credentials, + Credentials, MAX_ARGV_NUMBER, MAX_ARG_LEN, MAX_ENVP_NUMBER, MAX_ENV_LEN, }, syscall::{SYS_EXECVE, SYS_EXECVEAT}, util::{read_cstring_from_user, read_val_from_user}, diff --git a/kernel/aster-nix/src/util/mod.rs b/kernel/aster-nix/src/util/mod.rs index 3d63dc352..effb567ca 100644 --- a/kernel/aster-nix/src/util/mod.rs +++ b/kernel/aster-nix/src/util/mod.rs @@ -3,8 +3,9 @@ use core::mem; use aster_frame::vm::VmIo; +use aster_rights::Full; -use crate::prelude::*; +use crate::{prelude::*, vm::vmar::Vmar}; pub mod net; /// Read bytes into the `dest` buffer @@ -50,13 +51,20 @@ pub fn write_val_to_user(dest: Vaddr, val: &T) -> Result<()> { /// The original Linux implementation can be found at: /// pub fn read_cstring_from_user(addr: Vaddr, max_len: usize) -> Result { + let current = current!(); + let vmar = current.root_vmar(); + read_cstring_from_vmar(vmar, addr, max_len) +} + +/// Read CString from `vmar`. If possible, use `read_cstring_from_user` instead. +pub fn read_cstring_from_vmar(vmar: &Vmar, addr: Vaddr, max_len: usize) -> Result { let mut buffer: Vec = Vec::with_capacity(max_len); let mut cur_addr = addr; macro_rules! read_one_byte_at_a_time_while { ($cond:expr) => { while $cond { - let byte = read_val_from_user::(cur_addr)?; + let byte = vmar.read_val::(cur_addr)?; buffer.push(byte); if byte == 0 { return Ok(CString::from_vec_with_nul(buffer) @@ -74,7 +82,7 @@ pub fn read_cstring_from_user(addr: Vaddr, max_len: usize) -> Result { // Handle the rest of the bytes in bulk while (buffer.len() + mem::size_of::()) <= max_len { - let Ok(word) = read_val_from_user::(cur_addr) else { + let Ok(word) = vmar.read_val::(cur_addr) else { break; }; diff --git a/kernel/aster-nix/src/vdso.rs b/kernel/aster-nix/src/vdso.rs index 6c5508f1d..7f735286d 100644 --- a/kernel/aster-nix/src/vdso.rs +++ b/kernel/aster-nix/src/vdso.rs @@ -258,6 +258,7 @@ pub(super) fn init() { } /// Return the vdso vmo. -pub(crate) fn vdso_vmo() -> Arc { - VDSO.get().unwrap().vmo().clone() +pub(crate) fn vdso_vmo() -> Option> { + // We allow that vdso does not exist + VDSO.get().map(|vdso| vdso.vmo()) } diff --git a/kernel/aster-nix/src/vm/vmar/mod.rs b/kernel/aster-nix/src/vm/vmar/mod.rs index d494b293e..47f6a3e3f 100644 --- a/kernel/aster-nix/src/vm/vmar/mod.rs +++ b/kernel/aster-nix/src/vm/vmar/mod.rs @@ -56,6 +56,12 @@ pub trait VmarRightsOp { fn check_rights(&self, rights: Rights) -> Result<()>; } +impl PartialEq for Vmar { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} + impl VmarRightsOp for Vmar { default fn rights(&self) -> Rights { unimplemented!()