From 09e6f9af88fcee648ecf8ae1830fcf27ec3e1595 Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Sun, 31 May 2026 23:07:57 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E9=A1=B5=E7=9A=84=E6=B8=85=E9=9B=B6=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=80=81=E6=97=A0=E6=B3=95=E8=BF=9B=E5=85=A5?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/src/kernel/task/task_struct.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/os/src/kernel/task/task_struct.rs b/os/src/kernel/task/task_struct.rs index 4bd4232e..978f93d6 100644 --- a/os/src/kernel/task/task_struct.rs +++ b/os/src/kernel/task/task_struct.rs @@ -2,10 +2,7 @@ //! //! 包含任务的核心信息,如上下文、状态、内存空间等 #![allow(dead_code)] -use core::{ - mem::size_of, - sync::atomic::{AtomicPtr, Ordering}, -}; +use core::sync::atomic::{AtomicPtr, Ordering}; use alloc::{string::String, sync::Arc, vec::Vec}; @@ -20,7 +17,7 @@ use crate::{ task::{forkret, task_state::TaskState}, }, mm::{ - address::{ConvertablePA, PageNum, UA, VA}, + address::{ConvertablePA, PageNum, UA, UsizeConvert, VA}, frame_allocator::{FrameRangeTracker, FrameTracker}, memory_space::MemorySpace, }, @@ -309,8 +306,12 @@ impl Task { // 4. 配置 TrapFrame (新的上下文) // SAFETY: tfptr 指向的内存已经被分配且可写,并由 task 拥有 unsafe { - // 清零整个 TrapFrame,避免旧值泄漏到用户态 - core::ptr::write_bytes(tf_ptr, 0, size_of::()); + // 原先:清零整个 TrapFrame,避免旧值泄漏到用户态。 + // 注意:write_bytes 的 count 以 TrapFrame 为单位(非字节), + // 这里只清零 1 个 TrapFrame。若误传 size_of::(), + // 会写 280*280 字节、越界冲掉当前 satp 指向的页表,导致静默死循环。 + // 由于 set_exec_trap_frame 内部已 *self = Self::zero_init() 全量清零, + // 这行 write_bytes 在功能上完全冗余,故直接删除 ::set_exec_trap_frame_from_layout( &mut *tf_ptr, initial_pc.as_usize(), From 59dc7e81414add36cfcf9503c2ffba5fe702d9ee Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 1 Jun 2026 20:49:49 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E6=97=A0=20s?= =?UTF-8?q?hebang=20=E8=84=9A=E6=9C=AC=E7=9B=B4=E6=8E=A5=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E6=8A=A5=20"Is=20a=20directory"=20=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:fork/clone 未将 exe_path 传给子进程,导致子 shell 的 /proc/self/exe 退化解析为 "/"。busybox 对无 shebang 脚本拿到 ENOEXEC 后会 re-exec /proc/self/exe,于是实际 execve "/"(目录), 返回 EISDIR,表现为 ./xxx.sh 报 "Is a directory"(而 sh xxx.sh 正常)。 - clone:从父进程拷贝 exe_path 到子进程,修复 /proc/self/exe 解析 - exec_ops/exec_loader:execve 目标非普通文件时按真实类型返回 errno, 仅真目录返回 EISDIR,其余(字符/块设备、fifo、socket)返回 EACCES, 新增 ExecImageError::NotRegular 携带 inode 类型 - adapter:stat/statx 的 st_mode 在缺少 S_IFMT 时按 inode_type 补全类型位 - proc:符号链接 inode 的 mode 补上 S_IFLNK 类型位 --- os/src/fs/proc/inode.rs | 4 ++-- os/src/kernel/syscall/task/clone_ops.rs | 3 +++ os/src/kernel/syscall/task/exec_ops.rs | 12 +++++++++++- os/src/kernel/syscall/task/mod.rs | 4 ++-- os/src/kernel/task/exec_loader.rs | 5 +++-- os/src/vfs/adapter.rs | 21 +++++++++++++++++++-- 6 files changed, 40 insertions(+), 9 deletions(-) diff --git a/os/src/fs/proc/inode.rs b/os/src/fs/proc/inode.rs index 78b87d4c..d411ea8d 100644 --- a/os/src/fs/proc/inode.rs +++ b/os/src/fs/proc/inode.rs @@ -176,7 +176,7 @@ impl ProcInode { metadata: SpinLock::new(InodeMetadata { inode_no, inode_type: InodeType::Symlink, - mode: FileMode::from_bits_truncate(0o777), + mode: FileMode::from_bits_truncate(0o777 | FileMode::S_IFLNK.bits()), uid: 0, gid: 0, size: target.len(), @@ -211,7 +211,7 @@ impl ProcInode { metadata: SpinLock::new(InodeMetadata { inode_no, inode_type: InodeType::Symlink, - mode: FileMode::from_bits_truncate(0o777), + mode: FileMode::from_bits_truncate(0o777 | FileMode::S_IFLNK.bits()), uid: 0, gid: 0, size: 0, // 动态链接的大小未知 diff --git a/os/src/kernel/syscall/task/clone_ops.rs b/os/src/kernel/syscall/task/clone_ops.rs index ea98d235..5098a94f 100644 --- a/os/src/kernel/syscall/task/clone_ops.rs +++ b/os/src/kernel/syscall/task/clone_ops.rs @@ -46,6 +46,7 @@ pub fn clone( fs, uts, rlimit, + exe_path, ) = { let _guard = crate::sync::PreemptGuard::new(); let cpu = current_cpu(); @@ -66,6 +67,7 @@ pub fn clone( task.fs.clone(), task.uts_namespace.clone(), task.rlimit.clone(), + task.exe_path.clone(), ) }; let exit_signal = requested_flags.get_exit_signal(); @@ -132,6 +134,7 @@ pub fn clone( fd_table, fs, ); + child_task.exe_path = exe_path; if requested_flags.contains(CloneFlags::CHILD_SETTID) { unsafe { diff --git a/os/src/kernel/syscall/task/exec_ops.rs b/os/src/kernel/syscall/task/exec_ops.rs index 0f590d18..298faeb4 100644 --- a/os/src/kernel/syscall/task/exec_ops.rs +++ b/os/src/kernel/syscall/task/exec_ops.rs @@ -41,7 +41,7 @@ pub fn execve( Err(_) => return -EIO, }; if meta.inode_type != crate::vfs::InodeType::File { - return -EISDIR; + return exec_non_file_errno(meta.inode_type); } let prefix_len = core::cmp::min(meta.size, 256); @@ -173,6 +173,13 @@ fn parse_hashbang(data: &[u8]) -> Result<(&str, Option<&str>), HashbangError> { Ok((interpreter_path, interpreter_arg)) } +fn exec_non_file_errno(inode_type: crate::vfs::InodeType) -> c_int { + match inode_type { + crate::vfs::InodeType::Directory => -EISDIR, + _ => -EACCES, + } +} + /// 执行一个新程序(execve)的准备阶段:解析 ELF 并创建新的地址空间 fn do_execve_prepare( path: &str, @@ -181,6 +188,9 @@ fn do_execve_prepare( Ok(p) => p, Err(crate::kernel::task::ExecImageError::Fs(FsError::NotFound)) => return Err(-ENOENT), Err(crate::kernel::task::ExecImageError::Fs(FsError::IsDirectory)) => return Err(-EISDIR), + Err(crate::kernel::task::ExecImageError::NotRegular(inode_type)) => { + return Err(exec_non_file_errno(inode_type)); + } Err(crate::kernel::task::ExecImageError::Fs(_)) => return Err(-EIO), Err(crate::kernel::task::ExecImageError::Paging( crate::mm::page_table::PagingError::OutOfMemory, diff --git a/os/src/kernel/syscall/task/mod.rs b/os/src/kernel/syscall/task/mod.rs index 05459fe4..2a830f13 100644 --- a/os/src/kernel/syscall/task/mod.rs +++ b/os/src/kernel/syscall/task/mod.rs @@ -30,8 +30,8 @@ use crate::{ sync::SpinLock, uapi::{ errno::{ - EAGAIN, EFAULT, EINTR, EINVAL, EIO, EISDIR, ENOENT, ENOEXEC, ENOMEM, ENOSYS, EPERM, - ESRCH, ETIMEDOUT, + EACCES, EAGAIN, EFAULT, EINTR, EINVAL, EIO, EISDIR, ENOENT, ENOEXEC, ENOMEM, ENOSYS, + EPERM, ESRCH, ETIMEDOUT, }, futex::{FUTEX_CLOCK_REALTIME, FUTEX_PRIVATE, FUTEX_WAIT, FUTEX_WAKE, RobustListHead}, resource::{RLIM_NLIMITS, Rlimit, Rusage}, diff --git a/os/src/kernel/task/exec_loader.rs b/os/src/kernel/task/exec_loader.rs index 18849639..666da83f 100644 --- a/os/src/kernel/task/exec_loader.rs +++ b/os/src/kernel/task/exec_loader.rs @@ -13,6 +13,7 @@ use crate::vfs::{FsError, Inode, InodeType}; pub enum ExecImageError { Fs(FsError), InvalidElf, + NotRegular(InodeType), Paging(PagingError), } @@ -429,7 +430,7 @@ pub fn prepare_exec_image_from_path(path: &str) -> Result Result u32 { + let mode = meta.mode.bits(); + if mode & crate::vfs::FileMode::S_IFMT.bits() != 0 { + return mode; + } + + mode | match meta.inode_type { + InodeType::File => crate::vfs::FileMode::S_IFREG.bits(), + InodeType::Directory => crate::vfs::FileMode::S_IFDIR.bits(), + InodeType::Symlink => crate::vfs::FileMode::S_IFLNK.bits(), + InodeType::CharDevice => crate::vfs::FileMode::S_IFCHR.bits(), + InodeType::BlockDevice => crate::vfs::FileMode::S_IFBLK.bits(), + InodeType::Fifo => crate::vfs::FileMode::S_IFIFO.bits(), + InodeType::Socket => crate::vfs::FileMode::S_IFSOCK.bits(), + } +} + /// 将InodeType转换为d_type值 pub fn inode_type_to_d_type(t: InodeType) -> u8 { match t { From e150d3613279dd6880725722f1e91a357b9084a2 Mon Sep 17 00:00:00 2001 From: LittleSand <1840309785@qq.com> Date: Mon, 1 Jun 2026 23:11:00 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=E6=A0=B9=E6=8D=AE=20Linux/POSIX=20?= =?UTF-8?q?=E6=A0=87=E5=87=86=20=E4=BF=AE=E5=A4=8D=E8=BF=99=E4=B8=AA?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=80=BC=EF=BC=8C=E4=B9=8B=E5=90=8E=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=20Permission=20denied=E6=98=AFbusybox=20=E7=9A=84shel?= =?UTF-8?q?l=E7=9A=84=E8=87=AA=E5=8F=91=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/src/kernel/syscall/task/exec_ops.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/os/src/kernel/syscall/task/exec_ops.rs b/os/src/kernel/syscall/task/exec_ops.rs index 298faeb4..80a02aed 100644 --- a/os/src/kernel/syscall/task/exec_ops.rs +++ b/os/src/kernel/syscall/task/exec_ops.rs @@ -173,11 +173,11 @@ fn parse_hashbang(data: &[u8]) -> Result<(&str, Option<&str>), HashbangError> { Ok((interpreter_path, interpreter_arg)) } -fn exec_non_file_errno(inode_type: crate::vfs::InodeType) -> c_int { - match inode_type { - crate::vfs::InodeType::Directory => -EISDIR, - _ => -EACCES, - } +// 按 POSIX/Linux 语义:对非普通文件(含目录)调用 execve 一律返回 EACCES。 +// 真实内核 may_open() 对 S_IFDIR + MAY_EXEC 返回 -EACCES;EISDIR 仅用于写目录等场景。 +// busybox/bash 拿到 EACCES 后会自行处理(bash 会 stat 后输出 "Is a directory")。 +fn exec_non_file_errno(_inode_type: crate::vfs::InodeType) -> c_int { + -EACCES } /// 执行一个新程序(execve)的准备阶段:解析 ELF 并创建新的地址空间