Skip to content

fix:修复用户态进入,还有一些路径映射问题#258

Merged
a6d9a6m merged 3 commits into
comix-kernel:mainfrom
a6d9a6m:new-main
Jun 1, 2026
Merged

fix:修复用户态进入,还有一些路径映射问题#258
a6d9a6m merged 3 commits into
comix-kernel:mainfrom
a6d9a6m:new-main

Conversation

@a6d9a6m

@a6d9a6m a6d9a6m commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

🐛 Bug 报告:用户态启动与脚本执行修复(PR 含两处修复)

本 PR 修复了两个相互独立、但都阻断「进入用户态后正常跑测例」的缺陷:

  1. TrapFrame 清零越界导致无法进入用户态(fix 提交 09e6f9a
  2. 无 shebang 脚本直接执行报 "Is a directory"(fix 提交 59dc7e8

Bug 1:TrapFrame 清零参数越界,导致用户态无法进入

🐞 描述 Bug

构造首个用户任务 / execve 切换上下文时,对 TrapFrame 做清零的
core::ptr::write_bytes 误把字节数当作 count 传入。write_bytes
count 以「元素(TrapFrame)个数」为单位,传入 size_of::<TrapFrame>()
会写 约 280 × 280 字节,远超单个 TrapFrame,越界冲掉当前 satp 指向的
页表页,造成静默死循环,内核始终无法切到用户态。

⚙️ 复现步骤

  1. 在 QEMU(RISC-V virt) 上正常编译并启动内核(make run)。
  2. 内核完成初始化后尝试 kernel_execve("/sbin/init") 进入用户态。
  3. 观察到内核卡死/静默死循环,串口无 init/shell 输出,无法进入用户态。

预期行为

清零应只作用于 1 个 TrapFrame;内核能正常切换到用户态并拉起
/sbin/init/bin/sh,串口出现 shell 提示符。

实际行为

write_bytes(tf_ptr, 0, size_of::<TrapFrame>()) 把 count 当字节数,
写入 size_of × size_of 字节越界,破坏 satp 所指页表,内核静默死循环,
始终无法进入用户态。

✅ 修复方案

由于 set_exec_trap_frame_from_layout 内部已通过 *self = Self::zero_init()
做了全量清零,该 write_bytes 行在功能上完全冗余,直接删除,消除越界写。

涉及文件:os/src/kernel/task/task_struct.rs

🖥️ 环境信息

  • 目标架构 (Target Architecture): RISC-V 64(默认;LoongArch64 亦支持)
  • 模拟器/硬件平台 (Emulator/Hardware): QEMU qemu-system-riscv64 -machine virt
  • 宿主机操作系统 (Host OS): Linux (WSL2, kernel 5.15 microsoft-standard)
  • 编译工具链版本 (Toolchain Version): Rust nightly-2025-10-28
  • 代码提交哈希 (Commit Hash): 修复提交 09e6f9a(缺陷存在于此前版本)

📝 截图或日志

[kernel_execve] Switching to user mode
... (此后内核静默卡死,无任何用户态输出)


Bug 2:无 shebang 脚本直接执行报 "Is a directory"

🐞 描述 Bug

进入用户态后,直接执行不带 shebang 的脚本 ./iperf_testcode.sh
Is a directory(EISDIR),而 sh iperf_testcode.sh 可正常执行。
根因是 fork/clone 未把父进程的 exe_path 传给子进程:子 shell 的
/proc/self/exeexe_path == None 退化解析为 "/"。busybox 对无
shebang 脚本拿到 ENOEXEC 后会 re-exec /proc/self/exe,于是实际
execve("/")(目录)→ 返回 EISDIR,表现为脚本被「识别成目录」。

⚙️ 复现步骤

  1. 在 QEMU(RISC-V virt) 上 make run,进入用户态 busybox shell。
  2. 确认 /iperf_testcode.sh 为普通文件(inode 122, mode 0644),且无 shebang
  3. 执行 ./iperf_testcode.sh,看到错误 /bin/sh: ./iperf_testcode.sh: Is a directory
  4. 对照执行 sh iperf_testcode.sh,可正常输出 #### OS COMP TEST GROUP START iperf ####

预期行为

./iperf_testcode.shsh iperf_testcode.sh 行为一致:内核对无 shebang
非 ELF 文件返回 ENOEXEC,busybox 经 /proc/self/exe 正确 re-exec 到
/bin/sh 并把脚本当参数解释执行。

实际行为

./iperf_testcode.shIs a directory(EISDIR)并直接失败,回退链断在
/proc/self/exe"/"

✅ 修复方案

  1. clone_ops.rsfork/clone 时从父进程拷贝 exe_path 到子进程,
    修复 /proc/self/exe 解析(根因修复)。
  2. exec_ops.rs / exec_loader.rs / task/mod.rsexecve 目标非普通
    文件时按真实 inode 类型返回 errno——仅真目录返回 EISDIR,其余
    (字符/块设备、fifo、socket)返回 EACCES;新增
    ExecImageError::NotRegular(InodeType) 携带类型信息。
  3. adapter.rsstat/statxst_mode 在缺少 S_IFMT 类型位时,
    inode_type 补全(S_IFREG/S_IFDIR/S_IFLNK/...)。
  4. proc/inode.rs:procfs 符号链接 inode 的 mode 补上 S_IFLNK 类型位。

涉及文件:clone_ops.rsexec_ops.rsexec_loader.rssyscall/task/mod.rs
vfs/adapter.rsfs/proc/inode.rs

🖥️ 环境信息

  • 目标架构 (Target Architecture): RISC-V 64(默认;LoongArch64 亦支持)
  • 模拟器/硬件平台 (Emulator/Hardware): QEMU qemu-system-riscv64 -machine virt
  • 宿主机操作系统 (Host OS): Linux (WSL2, kernel 5.15 microsoft-standard)
  • 编译工具链版本 (Toolchain Version): Rust nightly-2025-10-28
  • 根文件系统: fs-riscv.img(ext4,busybox 用户态,/proc 由 rcS 挂载)
  • 代码提交哈希 (Commit Hash): 修复提交 59dc7e8(缺陷存在于此前版本)

📝 截图或日志

/ # ./iperf_testcode.sh
/bin/sh: ./iperf_testcode.sh: Is a directory

/ # sh iperf_testcode.sh
OS COMP TEST GROUP START iperf

⚠️ 遗留项

proc/inode.rs/proc/<pid>/exe 动态软链的兜底
unwrap_or_else(|| "/".to_string()) 未改动。exe_path 传递修复后正常路径
不再触发它,但「未知 exe 兜底成根目录」本身是把 ENOEXEC 放大成 EISDIR 的
诱因,建议后续改为返回空串或让 readlink 报错,避免再指向 /


a6d9a6m added 2 commits May 31, 2026 23:07
根因: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 类型位

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several improvements to process execution, file mode handling, and task cloning. Specifically, it ensures file type bits are correctly set in symlink inodes and stat structures, propagates exe_path during task cloning, refactors error handling for non-regular files during execve using a new NotRegular error variant, and removes a redundant and potentially unsafe write_bytes call on TrapFrame. Feedback on these changes suggests that according to Linux/POSIX standards, calling execve on a directory should return EACCES instead of EISDIR, and recommends updating exec_non_file_errno to return -EACCES for all non-file types.

Comment thread os/src/kernel/syscall/task/exec_ops.rs Outdated
@a6d9a6m a6d9a6m merged commit d17f93a into comix-kernel:main Jun 1, 2026
1 check passed
a6d9a6m added a commit that referenced this pull request Jun 2, 2026
fix:修复用户态进入,还有一些路径映射问题
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant