Skip to content

Commit

Permalink
fix: 修复了do execve 加载程序失败时,没能正确返回错误码给用户态的问题 (#1042)
Browse files Browse the repository at this point in the history
* fix: 修复了do execve 加载程序失败时,没能正确返回错误码给用户态的问题
  • Loading branch information
fslongjin authored Nov 12, 2024
1 parent 7c28051 commit 0f094e5
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 170 deletions.
93 changes: 8 additions & 85 deletions kernel/src/arch/riscv64/process/syscall.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -104,8 +29,6 @@ impl Syscall {
regs.status.update_fs(FS::Clean);
regs.status.update_sum(true);

drop(param);

return Ok(());
}

Expand Down
90 changes: 8 additions & 82 deletions kernel/src/arch/x86_64/process/syscall.rs
Original file line number Diff line number Diff line change
@@ -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的指针写到寄存器内
Expand All @@ -114,8 +42,6 @@ impl Syscall {
regs.rflags = 0x200;
regs.rax = 1;

drop(param);

// debug!("regs: {:?}\n", regs);

// crate::debug!(
Expand Down
118 changes: 115 additions & 3 deletions kernel/src/process/syscall.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}

0 comments on commit 0f094e5

Please sign in to comment.