Use better interfaces in exec/clone

This commit is contained in:
4lDO2
2022-07-26 15:50:00 +02:00
parent d32dd52d3b
commit 985b2a59a9
4 changed files with 77 additions and 28 deletions
+3 -13
View File
@@ -33,6 +33,7 @@ fn copy_str(cur_pid_fd: usize, new_pid_fd: usize, key: &str) -> Result<()> {
let cur_name_fd = FdGuard::new(syscall::dup(cur_pid_fd, key.as_bytes())?);
let new_name_fd = FdGuard::new(syscall::dup(new_pid_fd, key.as_bytes())?);
// TODO: Max path size?
let mut buf = [0_u8; 256];
let len = syscall::read(*cur_name_fd, &mut buf)?;
let buf = buf.get(..len).ok_or(Error::new(ENAMETOOLONG))?;
@@ -194,19 +195,8 @@ fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
}
let map_flags = MapFlags::from_bits_truncate(flags);
let mapped_address = unsafe {
let fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, format!("grant-{:x}", addr).as_bytes())?);
syscall::fmap(*fd, &syscall::Map { address: 0, size, flags: map_flags, offset })?
};
let mut buf = [0_u8; size_of::<usize>() * 4];
let mut chunks = buf.array_chunks_mut::<{size_of::<usize>()}>();
*chunks.next().unwrap() = usize::to_ne_bytes(addr);
*chunks.next().unwrap() = usize::to_ne_bytes(size);
*chunks.next().unwrap() = usize::to_ne_bytes(map_flags.bits());
*chunks.next().unwrap() = usize::to_ne_bytes(mapped_address);
let _ = syscall::write(*new_addr_space_fd, &buf)?;
let grant_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, format!("grant-{:x}", addr).as_bytes())?);
redox_exec::mmap_remote(&new_addr_space_fd, &grant_fd, offset, addr, size, map_flags)?;
}
let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?);
+4 -1
View File
@@ -15,12 +15,14 @@ fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], total_arg
let image_file = FdGuard::new(fd as usize);
let open_via_dup = FdGuard::new(syscall::open("thisproc:current/open_via_dup", 0)?);
let memory = FdGuard::new(syscall::open("memory:", 0)?);
let addrspace_selection_fd = match redox_exec::fexec_impl(image_file, open_via_dup, path, args.iter().rev(), envs.iter().rev(), total_args_envs_size, interp_override)? {
let addrspace_selection_fd = match redox_exec::fexec_impl(image_file, open_via_dup, &memory, path, args.iter().rev(), envs.iter().rev(), total_args_envs_size, interp_override)? {
FexecResult::Normal { addrspace_handle } => addrspace_handle,
FexecResult::Interp { image_file, open_via_dup, path, interp_override: new_interp_override } => {
drop(image_file);
drop(open_via_dup);
drop(memory);
// According to elf(5), PT_INTERP requires that the interpreter path be
// null-terminated. Violating this should therefore give the "format error" ENOEXEC.
@@ -29,6 +31,7 @@ fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], total_arg
return execve(path_cstr, ArgEnv::Parsed { total_args_envs_size, args, envs }, Some(new_interp_override));
}
};
drop(memory);
// Dropping this FD will cause the address space switch.
drop(addrspace_selection_fd);
+1 -1
View File
@@ -3,7 +3,7 @@ use core::arch::asm;
use syscall::{
self,
data::{CloneInfo, Map, Stat as redox_stat, StatVfs as redox_statvfs, TimeSpec as redox_timespec},
data::{Map, Stat as redox_stat, StatVfs as redox_statvfs, TimeSpec as redox_timespec},
PtraceEvent, Result,
};
+69 -13
View File
@@ -33,7 +33,7 @@ pub struct InterpOverride {
tree: BTreeMap<usize, usize>,
}
pub fn fexec_impl<A, E>(image_file: FdGuard, open_via_dup: FdGuard, path: &[u8], args: A, envs: E, total_args_envs_size: usize, mut interp_override: Option<InterpOverride>) -> Result<FexecResult>
pub fn fexec_impl<A, E>(image_file: FdGuard, open_via_dup: FdGuard, memory_scheme_fd: &FdGuard, path: &[u8], args: A, envs: E, total_args_envs_size: usize, mut interp_override: Option<InterpOverride>) -> Result<FexecResult>
where
A: IntoIterator,
E: IntoIterator,
@@ -122,7 +122,7 @@ where
});
}
PT_LOAD => {
mprotect_remote(*grants_fd, vaddr, size, flags)?;
allocate_remote(&grants_fd, memory_scheme_fd, vaddr, size, syscall::PROT_READ | syscall::PROT_WRITE)?;
syscall::lseek(*image_file as usize, segment.p_offset as isize, SEEK_SET).map_err(|_| Error::new(EIO))?;
syscall::lseek(*memory_fd, segment.p_vaddr as isize, SEEK_SET).map_err(|_| Error::new(EIO))?;
@@ -130,6 +130,7 @@ where
read_all(*image_file as usize, None, &mut buf[..size]).map_err(|_| Error::new(EIO))?;
let _ = syscall::write(*memory_fd, &buf[..size]).map_err(|_| Error::new(EIO))?;
}
mprotect_remote(&grants_fd, vaddr, size, flags)?;
if !tree.range(..=vaddr).next_back().filter(|(start, size)| **start + **size > vaddr).is_some() {
tree.insert(vaddr, size);
@@ -142,7 +143,7 @@ where
const STACK_TOP: usize = 1 << 47;
const STACK_SIZE: usize = 1024 * 1024;
mprotect_remote(*grants_fd, STACK_TOP - STACK_SIZE, STACK_SIZE, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
allocate_remote(&grants_fd, memory_scheme_fd, STACK_TOP - STACK_SIZE, STACK_SIZE, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
tree.insert(STACK_TOP - STACK_SIZE, STACK_SIZE);
let mut sp = STACK_TOP - 256;
@@ -160,9 +161,9 @@ where
let pheaders_size_aligned = (pheaders_to_convey.len()+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE;
let pheaders = find_free_target_addr(&tree, pheaders_size_aligned).ok_or(Error::new(ENOMEM))?;
tree.insert(pheaders, pheaders_size_aligned);
mprotect_remote(*grants_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ)?;
allocate_remote(&grants_fd, memory_scheme_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
write_all(*memory_fd, Some(pheaders as u64), &pheaders_to_convey)?;
mprotect_remote(&grants_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ)?;
push(0)?;
push(AT_NULL)?;
@@ -181,7 +182,7 @@ where
let args_envs_size_aligned = (total_args_envs_size+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE;
let target_args_env_address = find_free_target_addr(&tree, args_envs_size_aligned).ok_or(Error::new(ENOMEM))?;
mprotect_remote(*grants_fd, target_args_env_address, args_envs_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
allocate_remote(&grants_fd, memory_scheme_fd, target_args_env_address, args_envs_size_aligned, MapFlags::PROT_READ | MapFlags::PROT_WRITE)?;
tree.insert(target_args_env_address, args_envs_size_aligned);
let mut offset = 0;
@@ -226,7 +227,7 @@ where
if let Ok(name_fd) = syscall::dup(*open_via_dup, b"name").map(FdGuard::new) {
let _ = syscall::write(*name_fd, interp_override.as_ref().map_or(path, |o| &o.name));
}
{
if interp_override.is_some() {
let mmap_min_fd = FdGuard::new(syscall::dup(*open_via_dup, b"mmap-min-addr")?);
let _ = syscall::write(*mmap_min_fd, &usize::to_ne_bytes(tree.iter().rev().nth(1).map_or(0, |(off, len)| *off + *len)));
}
@@ -237,14 +238,67 @@ where
Ok(FexecResult::Normal { addrspace_handle: addrspace_selection_fd })
}
fn mprotect_remote(socket: usize, addr: usize, len: usize, flags: MapFlags) -> Result<()> {
let mut grants_buf = [0_u8; 24];
grants_buf[..8].copy_from_slice(&usize::to_ne_bytes(addr));
grants_buf[8..16].copy_from_slice(&usize::to_ne_bytes(len));
grants_buf[16..24].copy_from_slice(&usize::to_ne_bytes(flags.bits()));
syscall::write(socket, &grants_buf)?;
fn write_usizes<const N: usize>(fd: &FdGuard, usizes: [usize; N]) -> Result<()> {
let _ = syscall::write(**fd, unsafe { plain::as_bytes(&usizes) });
Ok(())
}
fn allocate_remote(addrspace_fd: &FdGuard, memory_scheme_fd: &FdGuard, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> {
mmap_remote(addrspace_fd, memory_scheme_fd, 0, dst_addr, len, flags)
}
pub fn mmap_remote(addrspace_fd: &FdGuard, fd: &FdGuard, offset: usize, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> {
write_usizes(addrspace_fd, [
// op
syscall::flag::ADDRSPACE_OP_MMAP,
// fd
**fd,
// "offset"
offset,
// address
dst_addr,
// size
len,
// flags
(flags | MapFlags::MAP_FIXED_NOREPLACE).bits(),
])
}
pub fn mprotect_remote(addrspace_fd: &FdGuard, addr: usize, len: usize, flags: MapFlags) -> Result<()> {
write_usizes(addrspace_fd, [
// op
syscall::flag::ADDRSPACE_OP_MPROTECT,
// address
addr,
// size
len,
// flags
flags.bits(),
])
}
pub fn munmap_remote(addrspace_fd: &FdGuard, addr: usize, len: usize) -> Result<()> {
write_usizes(addrspace_fd, [
// op
syscall::flag::ADDRSPACE_OP_MUNMAP,
// address
addr,
// size
len,
])
}
pub fn munmap_transfer(src: &FdGuard, dst: &FdGuard, src_addr: usize, dst_addr: usize, len: usize, flags: MapFlags) -> Result<()> {
write_usizes(dst, [
// op
syscall::flag::ADDRSPACE_OP_TRANSFER,
// fd
**src,
// "offset" (source address)
src_addr,
// address
dst_addr,
// size
len,
// flags
(flags | MapFlags::MAP_FIXED_NOREPLACE).bits(),
])
}
fn read_all(fd: usize, offset: Option<u64>, buf: &mut [u8]) -> Result<()> {
if let Some(offset) = offset {
syscall::lseek(fd, offset as isize, SEEK_SET)?;
@@ -276,6 +330,8 @@ fn write_all(fd: usize, offset: Option<u64>, buf: &[u8]) -> Result<()> {
Ok(())
}
// TODO: With the introduction of remote mmaps, remove this and let the kernel handle address
// allocation.
fn find_free_target_addr(tree: &BTreeMap<usize, usize>, size: usize) -> Option<usize> {
let mut iterator = tree.iter().peekable();