Clean up `ProgramToLoad`

This commit is contained in:
Ruihan Li 2025-12-13 21:20:56 +08:00
parent 2b8ccbf3d2
commit e2f7b0eb73
7 changed files with 55 additions and 49 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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)?
};

View File

@ -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)?;

View File

@ -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;

View File

@ -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<dyn Inode>,
elf_headers: ElfHeaders,
argv: Vec<CString>,
@ -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<dyn Inode>,
/// Constructs a new `ProgramToLoad` from an inode and handles shebang interpretation if
/// necessary.
pub(super) fn build_from_inode(
mut elf_inode: Arc<dyn Inode>,
fs_resolver: &FsResolver,
argv: Vec<CString>,
mut argv: Vec<CString>,
envp: Vec<CString>,
recursion_limit: usize,
) -> Result<Self> {
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<ElfLoadInfo> {
pub(super) fn load_to_vmar(self, vmar: &Vmar, fs_resolver: &FsResolver) -> Result<ElfLoadInfo> {
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<dyn Inode>) -> 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");
}

View File

@ -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)
}