fix: 修复了do execve 加载程序失败时,没能正确返回错误码给用户态的问题 (#1042)

* fix: 修复了do execve 加载程序失败时,没能正确返回错误码给用户态的问题
This commit is contained in:
LoGin 2024-11-12 14:01:26 +08:00 committed by GitHub
parent 7c28051e8c
commit 0f094e50de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 131 additions and 170 deletions

View File

@ -1,96 +1,21 @@
use alloc::{ffi::CString, string::String, vec::Vec};
use riscv::register::sstatus::{FS, SPP};
use system_error::SystemError;
use crate::{
arch::{interrupt::TrapFrame, CurrentIrqArch},
exception::InterruptArch,
mm::ucontext::AddressSpace,
process::{
exec::{load_binary_file, ExecParam, ExecParamFlags},
ProcessManager,
},
arch::interrupt::TrapFrame,
mm::VirtAddr,
process::exec::{BinaryLoaderResult, ExecParam},
syscall::Syscall,
};
impl Syscall {
pub fn do_execve(
path: String,
argv: Vec<CString>,
envp: Vec<CString>,
pub fn arch_do_execve(
regs: &mut TrapFrame,
param: &ExecParam,
load_result: &BinaryLoaderResult,
user_sp: VirtAddr,
argv_ptr: VirtAddr,
) -> Result<(), SystemError> {
// 关中断,防止在设置地址空间的时候,发生中断,然后进调度器,出现错误。
let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
let pcb = ProcessManager::current_pcb();
// crate::debug!(
// "pid: {:?} do_execve: path: {:?}, argv: {:?}, envp: {:?}\n",
// pcb.pid(),
// path,
// argv,
// envp
// );
let mut basic_info = pcb.basic_mut();
// 暂存原本的用户地址空间的引用(因为如果在切换页表之前释放了它可能会造成内存use after free)
let old_address_space = basic_info.user_vm();
// 在pcb中原来的用户地址空间
unsafe {
basic_info.set_user_vm(None);
}
// 创建新的地址空间并设置为当前地址空间
let address_space = AddressSpace::new(true).expect("Failed to create new address space");
unsafe {
basic_info.set_user_vm(Some(address_space.clone()));
}
// to avoid deadlock
drop(basic_info);
assert!(
AddressSpace::is_current(&address_space),
"Failed to set address space"
);
// debug!("Switch to new address space");
// 切换到新的用户地址空间
unsafe { address_space.read().user_mapper.utable.make_current() };
drop(old_address_space);
drop(irq_guard);
// debug!("to load binary file");
let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC)?;
// 加载可执行文件
let load_result = load_binary_file(&mut param)?;
// debug!("load binary file done");
// debug!("argv: {:?}, envp: {:?}", argv, envp);
param.init_info_mut().args = argv;
param.init_info_mut().envs = envp;
// 把proc_init_info写到用户栈上
let mut ustack_message = unsafe {
address_space
.write()
.user_stack_mut()
.expect("No user stack found")
.clone_info_only()
};
let (user_sp, argv_ptr) = unsafe {
param
.init_info()
.push_at(
// address_space
// .write()
// .user_stack_mut()
// .expect("No user stack found"),
&mut ustack_message,
)
.expect("Failed to push proc_init_info to user stack")
};
address_space.write().user_stack = Some(ustack_message);
// debug!("write proc_init_info to user stack done");
regs.a0 = param.init_info().args.len();
@ -104,8 +29,6 @@ impl Syscall {
regs.status.update_fs(FS::Clean);
regs.status.update_sum(true);
drop(param);
return Ok(());
}

View File

@ -1,99 +1,27 @@
use alloc::{ffi::CString, string::String, sync::Arc, vec::Vec};
use alloc::sync::Arc;
use system_error::SystemError;
use crate::{
arch::{
interrupt::TrapFrame,
process::table::{USER_CS, USER_DS},
CurrentIrqArch,
},
exception::InterruptArch,
mm::ucontext::AddressSpace,
mm::VirtAddr,
process::{
exec::{load_binary_file, ExecParam, ExecParamFlags},
exec::{BinaryLoaderResult, ExecParam},
ProcessControlBlock, ProcessManager,
},
syscall::{user_access::UserBufferWriter, Syscall},
};
impl Syscall {
pub fn do_execve(
path: String,
argv: Vec<CString>,
envp: Vec<CString>,
pub fn arch_do_execve(
regs: &mut TrapFrame,
param: &ExecParam,
load_result: &BinaryLoaderResult,
user_sp: VirtAddr,
argv_ptr: VirtAddr,
) -> Result<(), SystemError> {
// 关中断,防止在设置地址空间的时候,发生中断,然后进调度器,出现错误。
let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
let pcb = ProcessManager::current_pcb();
// log::debug!(
// "pid: {:?} do_execve: path: {:?}, argv: {:?}, envp: {:?}\n",
// pcb.pid(),
// path,
// argv,
// envp
// );
let mut basic_info = pcb.basic_mut();
// 暂存原本的用户地址空间的引用(因为如果在切换页表之前释放了它可能会造成内存use after free)
let old_address_space = basic_info.user_vm();
// 在pcb中原来的用户地址空间
unsafe {
basic_info.set_user_vm(None);
}
// 创建新的地址空间并设置为当前地址空间
let address_space = AddressSpace::new(true).expect("Failed to create new address space");
unsafe {
basic_info.set_user_vm(Some(address_space.clone()));
}
// to avoid deadlock
drop(basic_info);
assert!(
AddressSpace::is_current(&address_space),
"Failed to set address space"
);
// debug!("Switch to new address space");
// 切换到新的用户地址空间
unsafe { address_space.read().user_mapper.utable.make_current() };
drop(old_address_space);
drop(irq_guard);
// debug!("to load binary file");
let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC)?;
// 加载可执行文件
let load_result = load_binary_file(&mut param)?;
// debug!("load binary file done");
// debug!("argv: {:?}, envp: {:?}", argv, envp);
param.init_info_mut().args = argv;
param.init_info_mut().envs = envp;
// 把proc_init_info写到用户栈上
let mut ustack_message = unsafe {
address_space
.write()
.user_stack_mut()
.expect("No user stack found")
.clone_info_only()
};
let (user_sp, argv_ptr) = unsafe {
param
.init_info()
.push_at(
// address_space
// .write()
// .user_stack_mut()
// .expect("No user stack found"),
&mut ustack_message,
)
.expect("Failed to push proc_init_info to user stack")
};
address_space.write().user_stack = Some(ustack_message);
// debug!("write proc_init_info to user stack done");
// 兼容旧版libc把argv的指针写到寄存器内
@ -114,8 +42,6 @@ impl Syscall {
regs.rflags = 0x200;
regs.rax = 1;
drop(param);
// debug!("regs: {:?}\n", regs);
// crate::debug!(

View File

@ -1,24 +1,34 @@
use core::ffi::c_void;
use alloc::{ffi::CString, string::ToString, sync::Arc, vec::Vec};
use alloc::{
ffi::CString,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use log::error;
use system_error::SystemError;
use super::{
abi::WaitOption,
cred::{Kgid, Kuid},
exec::{load_binary_file, ExecParam, ExecParamFlags},
exit::kernel_wait4,
fork::{CloneFlags, KernelCloneArgs},
resource::{RLimit64, RLimitID, RUsage, RUsageWho},
KernelStack, Pid, ProcessManager,
};
use crate::{
arch::{interrupt::TrapFrame, MMArch},
arch::{interrupt::TrapFrame, CurrentIrqArch, MMArch},
exception::InterruptArch,
filesystem::{
procfs::procfs_register_pid,
vfs::{file::FileDescriptorVec, MAX_PATHLEN},
},
mm::{ucontext::UserStack, verify_area, MemoryManagementArch, VirtAddr},
mm::{
ucontext::{AddressSpace, UserStack},
verify_area, MemoryManagementArch, VirtAddr,
},
process::ProcessControlBlock,
sched::completion::Completion,
syscall::{
@ -139,6 +149,54 @@ impl Syscall {
return Ok(());
}
pub fn do_execve(
path: String,
argv: Vec<CString>,
envp: Vec<CString>,
regs: &mut TrapFrame,
) -> Result<(), SystemError> {
let address_space = AddressSpace::new(true).expect("Failed to create new address space");
// debug!("to load binary file");
let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC)?;
let old_vm = do_execve_switch_user_vm(address_space.clone());
// 加载可执行文件
let load_result = load_binary_file(&mut param).inspect_err(|_| {
if let Some(old_vm) = old_vm {
do_execve_switch_user_vm(old_vm);
}
})?;
// debug!("load binary file done");
// debug!("argv: {:?}, envp: {:?}", argv, envp);
param.init_info_mut().args = argv;
param.init_info_mut().envs = envp;
// 把proc_init_info写到用户栈上
let mut ustack_message = unsafe {
address_space
.write()
.user_stack_mut()
.expect("No user stack found")
.clone_info_only()
};
let (user_sp, argv_ptr) = unsafe {
param
.init_info()
.push_at(
// address_space
// .write()
// .user_stack_mut()
// .expect("No user stack found"),
&mut ustack_message,
)
.expect("Failed to push proc_init_info to user stack")
};
address_space.write().user_stack = Some(ustack_message);
Self::arch_do_execve(regs, &param, &load_result, user_sp, argv_ptr)
}
pub fn wait4(
pid: i64,
wstatus: *mut i32,
@ -499,3 +557,57 @@ impl Syscall {
return Ok(0);
}
}
/// 切换用户虚拟内存空间
///
/// 该函数用于在执行系统调用 `execve` 时切换用户进程的虚拟内存空间。
///
/// # 参数
/// - `new_vm`: 新的用户地址空间,类型为 `Arc<AddressSpace>`。
///
/// # 返回值
/// - 返回旧的用户地址空间的引用,类型为 `Option<Arc<AddressSpace>>`。
///
/// # 错误处理
/// 如果地址空间切换失败,函数会触发断言失败,并输出错误信息。
fn do_execve_switch_user_vm(new_vm: Arc<AddressSpace>) -> Option<Arc<AddressSpace>> {
// 关中断,防止在设置地址空间的时候,发生中断,然后进调度器,出现错误。
let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
let pcb = ProcessManager::current_pcb();
// log::debug!(
// "pid: {:?} do_execve: path: {:?}, argv: {:?}, envp: {:?}\n",
// pcb.pid(),
// path,
// argv,
// envp
// );
let mut basic_info = pcb.basic_mut();
// 暂存原本的用户地址空间的引用(因为如果在切换页表之前释放了它可能会造成内存use after free)
let old_address_space = basic_info.user_vm();
// 在pcb中原来的用户地址空间
unsafe {
basic_info.set_user_vm(None);
}
// 创建新的地址空间并设置为当前地址空间
unsafe {
basic_info.set_user_vm(Some(new_vm.clone()));
}
// to avoid deadlock
drop(basic_info);
assert!(
AddressSpace::is_current(&new_vm),
"Failed to set address space"
);
// debug!("Switch to new address space");
// 切换到新的用户地址空间
unsafe { new_vm.read().user_mapper.utable.make_current() };
drop(irq_guard);
old_address_space
}