diff --git a/kernel/src/process/execve.rs b/kernel/src/process/execve.rs index 554460aae..006309ddc 100644 --- a/kernel/src/process/execve.rs +++ b/kernel/src/process/execve.rs @@ -15,10 +15,10 @@ use crate::{ }, prelude::*, process::{ - ContextUnshareAdminApi, Credentials, Process, ProgramToLoad, + ContextUnshareAdminApi, Credentials, Process, posix_thread::{PosixThread, ThreadLocal, ThreadName, sigkill_other_threads, thread_table}, process_vm::{MAX_LEN_STRING_ARG, MAX_NR_STRING_ARGS, unshare_and_renew_vmar}, - program_loader::elf::ElfLoadInfo, + program_loader::{ProgramToLoad, elf::ElfLoadInfo}, signal::{ HandlePendingSignal, PauseReason, SigStack, constants::{SIGCHLD, SIGKILL}, @@ -50,8 +50,10 @@ pub fn do_execve( let fs_ref = ctx.thread_local.borrow_fs(); let fs_resolver = fs_ref.resolver().read(); + let elf_inode = elf_file.inode(); - let program_to_load = ProgramToLoad::build_from_inode(elf_inode, &fs_resolver, argv, envp, 1)?; + let program_to_load = + ProgramToLoad::build_from_inode(elf_inode.clone(), &fs_resolver, argv, envp)?; // Ensure no other thread is concurrently performing exit_group or execve. // If such an operation is in progress, return EAGAIN. diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 52adde778..d4a144eec 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -39,7 +39,6 @@ pub use process::{ }; pub use process_filter::ProcessFilter; pub use process_vm::ProcessVm; -pub use program_loader::{ProgramToLoad, check_executable_inode}; pub use rlimit::ResourceType; pub use stats::collect_process_creation_count; pub use term_status::TermStatus; diff --git a/kernel/src/process/process/init_proc.rs b/kernel/src/process/process/init_proc.rs index 09f88cfde..e291b6f0d 100644 --- a/kernel/src/process/process/init_proc.rs +++ b/kernel/src/process/process/init_proc.rs @@ -13,10 +13,11 @@ use crate::{ }, prelude::*, process::{ - Credentials, ProgramToLoad, UserNamespace, + Credentials, UserNamespace, posix_thread::{PosixThreadBuilder, ThreadName, allocate_posix_tid}, process_table, process_vm::new_vmar_and_map, + program_loader::ProgramToLoad, rlimit::new_resource_limits_for_init, signal::sig_disposition::SigDispositions, }, @@ -109,7 +110,7 @@ fn create_init_task( let elf_load_info = { let fs_resolver = fs.resolver().read(); let program_to_load = - ProgramToLoad::build_from_inode(elf_path.inode(), &fs_resolver, argv, envp, 1)?; + ProgramToLoad::build_from_inode(elf_path.inode().clone(), &fs_resolver, argv, envp)?; let vmar = process.lock_vmar(); program_to_load.load_to_vmar(vmar.unwrap(), &fs_resolver)? }; diff --git a/kernel/src/process/program_loader/elf/load_elf.rs b/kernel/src/process/program_loader/elf/load_elf.rs index 11e8423fa..d68dfffc1 100644 --- a/kernel/src/process/program_loader/elf/load_elf.rs +++ b/kernel/src/process/program_loader/elf/load_elf.rs @@ -16,8 +16,8 @@ use crate::{ }, prelude::*, process::{ - check_executable_inode, process_vm::{AuxKey, AuxVec}, + program_loader::check_executable_inode, }, vm::{ perms::VmPerms, @@ -102,7 +102,7 @@ fn lookup_and_parse_ldso( let ldso_elf = { let inode = ldso_file.inode(); - check_executable_inode(inode)?; + check_executable_inode(inode.as_ref())?; let mut buf = Box::new([0u8; PAGE_SIZE]); let len = inode.read_bytes_at(0, &mut *buf)?; diff --git a/kernel/src/process/program_loader/elf/mod.rs b/kernel/src/process/program_loader/elf/mod.rs index 9588ecaea..affa5c064 100644 --- a/kernel/src/process/program_loader/elf/mod.rs +++ b/kernel/src/process/program_loader/elf/mod.rs @@ -4,5 +4,6 @@ mod elf_file; mod load_elf; mod relocate; -pub use elf_file::ElfHeaders; -pub use load_elf::{ElfLoadInfo, load_elf_to_vmar}; +pub(super) use elf_file::ElfHeaders; +pub(in crate::process) use load_elf::ElfLoadInfo; +pub(super) use load_elf::load_elf_to_vmar; diff --git a/kernel/src/process/program_loader/mod.rs b/kernel/src/process/program_loader/mod.rs index 8325d74a4..9bf1edf95 100644 --- a/kernel/src/process/program_loader/mod.rs +++ b/kernel/src/process/program_loader/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 -pub mod elf; +pub(super) mod elf; mod shebang; use self::{ @@ -20,7 +20,7 @@ use crate::{ /// /// This struct encapsulates the ELF file to be executed along with its header data, /// the `argv` and the `envp` which is required for the program execution. -pub struct ProgramToLoad { +pub(super) struct ProgramToLoad { elf_inode: Arc, elf_headers: ElfHeaders, argv: Vec, @@ -28,50 +28,56 @@ pub struct ProgramToLoad { } impl ProgramToLoad { - /// Constructs a new `ProgramToLoad` from a file, handling shebang interpretation if needed. - /// - /// About `recursion_limit`: recursion limit is used to limit th recursion depth of shebang executables. - /// If the interpreter(the program behind #!) of shebang executable is also a shebang, - /// then it will trigger recursion. We will try to setup 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 build_from_inode( - elf_inode: &Arc, + /// Constructs a new `ProgramToLoad` from an inode and handles shebang interpretation if + /// necessary. + pub(super) fn build_from_inode( + mut elf_inode: Arc, fs_resolver: &FsResolver, - argv: Vec, + mut argv: Vec, envp: Vec, - recursion_limit: usize, ) -> Result { - let file_first_page = { - // Read the first page of file header, which must contain the ELF header. - let mut buffer = Box::new([0u8; PAGE_SIZE]); - elf_inode.read_bytes_at(0, &mut *buffer)?; - buffer - }; - if let Some(mut new_argv) = parse_shebang_line(&*file_first_page)? { - if recursion_limit == 0 { + check_executable_inode(elf_inode.as_ref())?; + + // A limit to the recursion depth of shebang executables. + // + // If the interpreter is a shebang, then recursion will be triggered. If it loops, we + // should fail. We follow the same limit as Linux. + let mut recursive_limit = 5; + + let (file_first_page, len) = loop { + // Read the first page of the file, which should contain a shebang or an ELF header. + let (file_first_page, len) = { + let mut buffer = Box::new([0u8; PAGE_SIZE]); + let len = elf_inode.read_bytes_at(0, &mut *buffer)?; + (buffer, len) + }; + + let Some(mut new_argv) = parse_shebang_line(&file_first_page[..len])? else { + break (file_first_page, len); + }; + + if recursive_limit == 0 { return_errno_with_message!(Errno::ELOOP, "the recursieve limit is reached"); } - new_argv.extend_from_slice(&argv); + recursive_limit -= 1; + let interpreter = { let filename = new_argv[0].to_str()?.to_string(); let fs_path = FsPath::try_from(filename.as_str())?; fs_resolver.lookup_inode(&fs_path)? }; - check_executable_inode(interpreter.inode())?; - return Self::build_from_inode( - interpreter.inode(), - fs_resolver, - new_argv, - envp, - recursion_limit - 1, - ); - } + check_executable_inode(interpreter.inode().as_ref())?; - let elf_headers = ElfHeaders::parse(&*file_first_page)?; + // Update the argument list and the executable inode. Then, try again. + new_argv.extend(argv); + argv = new_argv; + elf_inode = interpreter.inode().clone(); + }; + + let elf_headers = ElfHeaders::parse(&file_first_page[..len])?; Ok(Self { - elf_inode: elf_inode.clone(), + elf_inode, elf_headers, argv, envp, @@ -81,7 +87,7 @@ impl ProgramToLoad { /// Loads the executable into the specified virtual memory space. /// /// Returns the information about the ELF loading process. - pub fn load_to_vmar(self, vmar: &Vmar, fs_resolver: &FsResolver) -> Result { + pub(super) fn load_to_vmar(self, vmar: &Vmar, fs_resolver: &FsResolver) -> Result { let elf_load_info = load_elf_to_vmar( vmar, &self.elf_inode, @@ -95,7 +101,7 @@ impl ProgramToLoad { } } -pub fn check_executable_inode(inode: &Arc) -> Result<()> { +fn check_executable_inode(inode: &dyn Inode) -> Result<()> { if inode.type_().is_directory() { return_errno_with_message!(Errno::EISDIR, "the inode is a directory"); } diff --git a/kernel/src/syscall/execve.rs b/kernel/src/syscall/execve.rs index 7c906f94a..9c342038a 100644 --- a/kernel/src/syscall/execve.rs +++ b/kernel/src/syscall/execve.rs @@ -9,7 +9,7 @@ use crate::{ fs_resolver::{AT_FDCWD, FsPath, PathOrInode}, }, prelude::*, - process::{check_executable_inode, do_execve}, + process::do_execve, }; pub fn sys_execve( @@ -74,9 +74,6 @@ fn lookup_executable_file( } }; - let inode = path_or_inode.inode(); - check_executable_inode(inode)?; - Ok(path_or_inode) }