diff --git a/src/platform/redox/clone.rs b/src/platform/redox/clone.rs new file mode 100644 index 0000000000000000000000000000000000000000..8877ef91e1b2881b035154e53de4411b089f7bf3 --- /dev/null +++ b/src/platform/redox/clone.rs @@ -0,0 +1,278 @@ +use core::arch::global_asm; +use core::mem::size_of; + +use alloc::boxed::Box; +use alloc::vec::Vec; + +use syscall::data::Map; +use syscall::flag::{MapFlags, O_CLOEXEC}; +use syscall::error::{Error, Result, EINVAL, ENAMETOOLONG}; +use syscall::SIGCONT; + +use super::extra::{create_set_addr_space_buf, FdGuard}; + +fn new_context() -> Result<(FdGuard, usize)> { + // Create a new context (fields such as uid/gid will be inherited from the current context). + let fd = FdGuard::new(syscall::open("thisproc:new/open_via_dup", O_CLOEXEC)?); + + // Extract pid. + let mut buffer = [0_u8; 64]; + let len = syscall::fpath(*fd, &mut buffer)?; + let buffer = buffer.get(..len).ok_or(Error::new(ENAMETOOLONG))?; + + let colon_idx = buffer.iter().position(|c| *c == b':').ok_or(Error::new(EINVAL))?; + let slash_idx = buffer.iter().skip(colon_idx).position(|c| *c == b'/').ok_or(Error::new(EINVAL))? + colon_idx; + let pid_bytes = buffer.get(colon_idx + 1..slash_idx).ok_or(Error::new(EINVAL))?; + let pid_str = core::str::from_utf8(pid_bytes).map_err(|_| Error::new(EINVAL))?; + let pid = pid_str.parse::<usize>().map_err(|_| Error::new(EINVAL))?; + + Ok((fd, pid)) +} + +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())?); + + 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))?; + + syscall::write(*new_name_fd, &buf)?; + + Ok(()) +} +#[cfg(target_arch = "x86_64")] +fn copy_float_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { + // Copy environment registers. + { + let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?); + let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?); + + let mut env_regs = syscall::EnvRegisters::default(); + let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?; + let _ = syscall::write(*new_env_regs_fd, &env_regs)?; + } + // Copy float registers. + { + let cur_float_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/float")?); + let new_float_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/float")?); + + let mut float_regs = syscall::FloatRegisters::default(); + let _ = syscall::read(*cur_float_regs_fd, &mut float_regs)?; + let _ = syscall::write(*new_float_regs_fd, &float_regs)?; + } + + Ok(()) +} + +/// Spawns a new context sharing the same address space as the current one (i.e. a new thread). +pub unsafe fn pte_clone_impl(stack: *mut usize) -> Result<usize> { + let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?); + let (new_pid_fd, new_pid) = new_context()?; + + // Allocate a new signal stack. + { + let sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?); + + const SIGSTACK_SIZE: usize = 1024 * 256; + + // TODO: Put sigstack at high addresses? + let target_sigstack = syscall::fmap(!0, &Map { address: 0, flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE, offset: 0, size: SIGSTACK_SIZE })? + SIGSTACK_SIZE; + + let _ = syscall::write(*sigstack_fd, &usize::to_ne_bytes(target_sigstack))?; + } + + copy_str(*cur_pid_fd, *new_pid_fd, "name")?; + copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?; + + // Reuse existing address space + { + let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?); + let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?); + + let buf = create_set_addr_space_buf(*cur_addr_space_fd, pte_clone_ret as usize, stack as usize); + let _ = syscall::write(*new_addr_space_sel_fd, &buf)?; + } + + // Reuse file table + { + let cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?); + let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?); + + let _ = syscall::write(*new_filetable_sel_fd, &usize::to_ne_bytes(*cur_filetable_fd))?; + } + + copy_float_env_regs(*cur_pid_fd, *new_pid_fd)?; + + // Unblock context. + syscall::kill(new_pid, SIGCONT)?; + + Ok(0) +} +/// Spawns a new context which will not share the same address space as the current one. File +/// descriptors from other schemes are reobtained with `dup`, and grants referencing such file +/// descriptors are reobtained through `fmap`. Other mappings are kept but duplicated using CoW. +pub fn fork_impl() -> Result<usize> { + unsafe { + Error::demux(fork_wrapper()) + } +} + +fn fork_inner(initial_rsp: *mut usize) -> Result<usize> { + let new_pid = { + let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?); + let (new_pid_fd, new_pid) = new_context()?; + + // Do not allocate new signal stack, but copy existing address (all memory will be re-mapped + // CoW later). + { + let cur_sigstack_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigstack")?); + let new_sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?); + + let mut sigstack_buf = usize::to_ne_bytes(0); + + let _ = syscall::read(*cur_sigstack_fd, &mut sigstack_buf); + let _ = syscall::write(*new_sigstack_fd, &sigstack_buf); + } + + copy_str(*cur_pid_fd, *new_pid_fd, "name")?; + copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?; + + // CoW-duplicate address space. + { + let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?); + + // FIXME: Find mappings which use external file descriptors + + let new_addr_space_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, b"exclusive")?); + + let mut buf = vec! [0_u8; 4096]; + let mut bytes_read = 0; + + loop { + let new_bytes_read = syscall::read(*cur_addr_space_fd, &mut buf[bytes_read..])?; + + if new_bytes_read == 0 { break } + + bytes_read += new_bytes_read; + } + let bytes = &buf[..bytes_read]; + + for struct_bytes in bytes.array_chunks::<{size_of::<usize>() * 4}>() { + let mut words = struct_bytes.array_chunks::<{size_of::<usize>()}>().copied().map(usize::from_ne_bytes); + + let addr = words.next().unwrap(); + let size = words.next().unwrap(); + let flags = words.next().unwrap(); + let offset = words.next().unwrap(); + + if flags & 0x8000_0000 == 0 { + continue; + } + 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 new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?); + + let buf = create_set_addr_space_buf(*new_addr_space_fd, fork_ret as usize, initial_rsp as usize); + let _ = syscall::write(*new_addr_space_sel_fd, &buf)?; + } + + // Copy existing files into new file table, but do not reuse the same file table (i.e. new + // parent FDs will not show up for the child). + { + let cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?); + // TODO: Use cross_scheme_links or something similar to avoid copying the file table in the + // kernel. + let new_filetable_fd = FdGuard::new(syscall::dup(*cur_filetable_fd, b"copy")?); + let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?); + + let _ = syscall::write(*new_filetable_sel_fd, &usize::to_ne_bytes(*new_filetable_fd)); + } + copy_float_env_regs(*cur_pid_fd, *new_pid_fd)?; + + new_pid + }; + + // Unblock context. + syscall::kill(new_pid, SIGCONT)?; + + Ok(new_pid) +} +#[no_mangle] +unsafe extern "sysv64" fn __relibc_internal_fork_impl(initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp)) +} + +#[no_mangle] +core::arch::global_asm!(" + .p2align 6 + .globl fork_wrapper + .type fork_wrapper, @function +fork_wrapper: + push rbp + mov rbp, rsp + + push rbx + push rbp + push r12 + push r13 + push r14 + push r15 + + mov rdi, rsp + call __relibc_internal_fork_impl + jmp 2f + +fork_ret: + xor rax, rax +2: + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + + pop rbp + ret + .size fork_wrapper, . - fork_wrapper + + .globl pte_clone_ret + .type pte_clone_ret, @function +pte_clone_ret: + + # Load registers + pop rax + pop rdi + pop rsi + pop rdx + pop rcx + pop r8 + pop r9 + + # Call entry point + call rax + + ret + .size pte_clone_ret, . - pte_clone_ret +"); + +extern "sysv64" { + fn fork_wrapper() -> usize; + fn fork_ret(); + fn pte_clone_ret(); +} diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs index f2231068b4f8b735f92085cf143d2657ebe03e8a..77c03b7cc67031a313804f56ed9ee7ca0c19849a 100644 --- a/src/platform/redox/exec.rs +++ b/src/platform/redox/exec.rs @@ -1,5 +1,5 @@ use core::convert::TryFrom; -use super::extra::FdGuard; +use super::extra::{create_set_addr_space_buf, FdGuard}; use alloc::{ collections::{btree_map::Entry, BTreeMap}, @@ -16,7 +16,7 @@ use crate::fs::File; fn read_all(fd: usize, offset: Option<u64>, buf: &mut [u8]) -> Result<()> { if let Some(offset) = offset { - syscall::lseek(fd, offset as isize, syscall::SEEK_SET).unwrap(); + syscall::lseek(fd, offset as isize, SEEK_SET)?; } let mut total_bytes_read = 0; @@ -29,6 +29,21 @@ fn read_all(fd: usize, offset: Option<u64>, buf: &mut [u8]) -> Result<()> { } Ok(()) } +fn write_all(fd: usize, offset: Option<u64>, buf: &[u8]) -> Result<()> { + if let Some(offset) = offset { + syscall::lseek(fd, offset as isize, SEEK_SET)?; + } + + let mut total_bytes_written = 0; + + while total_bytes_written < buf.len() { + total_bytes_written += match syscall::write(fd, &buf[total_bytes_written..])? { + 0 => return Err(Error::new(EIO)), + bytes_written => bytes_written, + } + } + Ok(()) +} fn find_free_target_addr(tree: &BTreeMap<usize, usize>, size: usize) -> Option<usize> { let mut iterator = tree.iter().peekable(); @@ -55,6 +70,14 @@ const PAGE_SIZE: usize = 4096; const FD_ANONYMOUS: usize = !0; pub fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], args_envs_size_without_nul: usize) -> Result<usize> { + let addrspace_selection_fd = fexec_impl_inner(file, path, args, envs, args_envs_size_without_nul)?; + + // Dropping this FD will cause the address space switch. + drop(addrspace_selection_fd); + + unreachable!(); +} +fn fexec_impl_inner(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], args_envs_size_without_nul: usize) -> Result<FdGuard> { use goblin::elf64::{header::Header, program_header::program_header64::{ProgramHeader, PT_LOAD, PF_W, PF_X}}; let fd = *file as usize; @@ -65,7 +88,6 @@ pub fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], args_ // some misalignments, and then execute the SYS_EXEC syscall to replace the program memory // entirely. - // TODO: setuid/setgid // TODO: Introduce RAII guards to all owned allocations so that no leaks occur in case of // errors. @@ -146,9 +168,7 @@ pub fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], args_ let mut push = |word: usize| { sp -= core::mem::size_of::<usize>(); - let _ = syscall::lseek(*memory_fd, sp as isize, SEEK_SET)?; - let _ = syscall::write(*memory_fd, &usize::to_ne_bytes(word))?; - Ok(()) + write_all(*memory_fd, Some(sp as u64), &usize::to_ne_bytes(word)) }; let pheaders_size_aligned = (pheaders_size+PAGE_SIZE-1)/PAGE_SIZE*PAGE_SIZE; @@ -156,8 +176,7 @@ pub fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], args_ tree.insert(pheaders, pheaders_size_aligned); mprotect_remote(*grants_fd, pheaders, pheaders_size_aligned, MapFlags::PROT_READ)?; - syscall::lseek(*memory_fd, pheaders as isize, SEEK_SET).map_err(|_| Error::new(EIO))?; - syscall::write(*memory_fd, &phs).map_err(|_| Error::new(EIO))?; + write_all(*memory_fd, Some(pheaders as u64), &phs)?; push(0)?; push(AT_NULL)?; @@ -186,8 +205,7 @@ pub fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], args_ if is_args { argc += 1; } push(target_args_env_address + offset)?; - syscall::lseek(*memory_fd, (target_args_env_address + offset) as isize, SEEK_SET).map_err(|_| Error::new(EIO))?; - let _ = syscall::write(*memory_fd, source_slice).map_err(|_| Error::new(EIO))?; + write_all(*memory_fd, Some((target_args_env_address + offset) as u64), source_slice)?; offset += source_slice.len() + 1; } } @@ -197,21 +215,15 @@ pub fn fexec_impl(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], args_ unsafe { crate::ld_so::tcb::Tcb::deactivate(); } // TODO: Restore old name if exec failed? - if let Ok(name_fd) = syscall::open("thisproc:current/name", O_WRONLY) { - let _ = syscall::write(name_fd, path); - let _ = syscall::close(name_fd); + if let Ok(name_fd) = syscall::open("thisproc:current/name", O_WRONLY).map(FdGuard::new) { + let _ = syscall::write(*name_fd, path); } - drop(file); let addrspace_selection_fd = FdGuard::new(syscall::open("thisproc:current/current-addrspace", O_WRONLY)?); - let mut buf = [0_u8; 24]; - buf[..8].copy_from_slice(&usize::to_ne_bytes(*grants_fd)); - buf[8..16].copy_from_slice(&usize::to_ne_bytes(sp)); - buf[16..24].copy_from_slice(&usize::to_ne_bytes(header.e_entry as usize)); + let _ = syscall::write(*addrspace_selection_fd, &create_set_addr_space_buf(*grants_fd, header.e_entry as usize, sp)); - let _ = syscall::write(*addrspace_selection_fd, &buf); - unreachable!(); + Ok(addrspace_selection_fd) } fn mprotect_remote(socket: usize, addr: usize, len: usize, flags: MapFlags) -> Result<()> { let mut grants_buf = [0_u8; 24]; diff --git a/src/platform/redox/extra.rs b/src/platform/redox/extra.rs index dc7f46868bc3167a16b1ce6f558dc8364a795242..3e3ba46d3cd68d7b450b533b70bc6df93fa14650 100644 --- a/src/platform/redox/extra.rs +++ b/src/platform/redox/extra.rs @@ -1,10 +1,4 @@ -use core::{mem, ptr, slice}; -use core::arch::global_asm; - -use syscall::data::Map; -use syscall::flag::{MapFlags, O_CLOEXEC}; -use syscall::error::{Error, Result, EINVAL, ENAMETOOLONG}; -use syscall::SIGCONT; +use core::{mem::size_of, ptr, slice}; use crate::platform::{sys::e, types::*}; @@ -84,237 +78,11 @@ impl Drop for FdGuard { } } } - -fn new_context() -> Result<(FdGuard, usize)> { - // Create a new context (fields such as uid/gid will be inherited from the current context). - let fd = FdGuard::new(syscall::open("thisproc:new/open_via_dup", O_CLOEXEC)?); - - // Extract pid. - let mut buffer = [0_u8; 64]; - let len = syscall::fpath(*fd, &mut buffer)?; - let buffer = buffer.get(..len).ok_or(Error::new(ENAMETOOLONG))?; - - let colon_idx = buffer.iter().position(|c| *c == b':').ok_or(Error::new(EINVAL))?; - let slash_idx = buffer.iter().skip(colon_idx).position(|c| *c == b'/').ok_or(Error::new(EINVAL))? + colon_idx; - let pid_bytes = buffer.get(colon_idx + 1..slash_idx).ok_or(Error::new(EINVAL))?; - let pid_str = core::str::from_utf8(pid_bytes).map_err(|_| Error::new(EINVAL))?; - let pid = pid_str.parse::<usize>().map_err(|_| Error::new(EINVAL))?; - - Ok((fd, pid)) -} - -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())?); - - 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))?; - - syscall::write(*new_name_fd, &buf)?; - - Ok(()) -} -#[cfg(target_arch = "x86_64")] -fn copy_float_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { - // Copy environment registers. - { - let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?); - let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?); - - let mut env_regs = syscall::EnvRegisters::default(); - let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?; - let _ = syscall::write(*new_env_regs_fd, &env_regs)?; - } - // Copy float registers. - { - let cur_float_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/float")?); - let new_float_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/float")?); - - let mut float_regs = syscall::FloatRegisters::default(); - let _ = syscall::read(*cur_float_regs_fd, &mut float_regs)?; - let _ = syscall::write(*new_float_regs_fd, &float_regs)?; - } - - Ok(()) -} - -/// Spawns a new context sharing the same address space as the current one (i.e. a new thread). -pub unsafe fn pte_clone_impl(stack: *mut usize) -> Result<usize> { - let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?); - let (new_pid_fd, new_pid) = new_context()?; - - // Allocate a new signal stack. - { - let sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?); - - const SIGSTACK_SIZE: usize = 1024 * 256; - - // TODO: Put sigstack at high addresses? - let target_sigstack = syscall::fmap(!0, &Map { address: 0, flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE, offset: 0, size: SIGSTACK_SIZE })? + SIGSTACK_SIZE; - - let _ = syscall::write(*sigstack_fd, &usize::to_ne_bytes(target_sigstack))?; - } - - copy_str(*cur_pid_fd, *new_pid_fd, "name")?; - copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?; - - // Reuse existing address space - { - let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?); - let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?); - - let buf = create_set_addr_space_buf(*cur_addr_space_fd, pte_clone_ret as usize, stack as usize); - let _ = syscall::write(*new_addr_space_sel_fd, &buf)?; - } - - // Reuse file table - { - let cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?); - let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?); - - let _ = syscall::write(*new_filetable_sel_fd, &usize::to_ne_bytes(*cur_filetable_fd))?; - } - - - copy_float_env_regs(*cur_pid_fd, *new_pid_fd)?; - - // Unblock context. - syscall::kill(new_pid, SIGCONT); - - Ok(0) -} -fn create_set_addr_space_buf(space: usize, ip: usize, sp: usize) -> [u8; mem::size_of::<usize>() * 3] { - let mut buf = [0_u8; 3 * mem::size_of::<usize>()]; - let mut chunks = buf.array_chunks_mut::<{mem::size_of::<usize>()}>(); +pub fn create_set_addr_space_buf(space: usize, ip: usize, sp: usize) -> [u8; size_of::<usize>() * 3] { + let mut buf = [0_u8; 3 * size_of::<usize>()]; + let mut chunks = buf.array_chunks_mut::<{size_of::<usize>()}>(); *chunks.next().unwrap() = usize::to_ne_bytes(space); *chunks.next().unwrap() = usize::to_ne_bytes(sp); *chunks.next().unwrap() = usize::to_ne_bytes(ip); buf } -/// Spawns a new context which will not share the same address space as the current one. File -/// descriptors from other schemes are reobtained with `dup`, and grants referencing such file -/// descriptors are reobtained through `fmap`. Other mappings are kept but duplicated using CoW. -pub fn fork_impl() -> Result<usize> { - unsafe { - Error::demux(fork_wrapper()) - } -} - -fn fork_inner(initial_rsp: *mut usize) -> Result<usize> { - let new_pid = { - let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?); - let (new_pid_fd, new_pid) = new_context()?; - - // Do not allocate new signal stack, but copy existing address (all memory will be re-mapped - // CoW later). - { - let cur_sigstack_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigstack")?); - let new_sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?); - - let mut sigstack_buf = usize::to_ne_bytes(0); - - let _ = syscall::read(*cur_sigstack_fd, &mut sigstack_buf); - let _ = syscall::write(*new_sigstack_fd, &sigstack_buf); - } - - copy_str(*cur_pid_fd, *new_pid_fd, "name")?; - copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?; - - // CoW-duplicate address space. - { - let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?); - - // FIXME: Find mappings which use external file descriptors - - let new_addr_space_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, b"exclusive")?); - let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?); - - let buf = create_set_addr_space_buf(*new_addr_space_fd, fork_ret as usize, initial_rsp as usize); - let _ = syscall::write(*new_addr_space_sel_fd, &buf)?; - } - - // Copy existing files into new file table, but do not reuse the same file table (i.e. new - // parent FDs will not show up for the child). - { - let cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?); - // TODO: Use cross_scheme_links or something similar to avoid copying the file table in the - // kernel. - let new_filetable_fd = FdGuard::new(syscall::dup(*cur_filetable_fd, b"copy")?); - let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?); - - let _ = syscall::write(*new_filetable_sel_fd, &usize::to_ne_bytes(*new_filetable_fd)); - } - copy_float_env_regs(*cur_pid_fd, *new_pid_fd)?; - - new_pid - }; - - // Unblock context. - syscall::kill(new_pid, SIGCONT); - - Ok(new_pid) -} -#[no_mangle] -unsafe extern "sysv64" fn __relibc_internal_fork_impl(initial_rsp: *mut usize) -> usize { - Error::mux(fork_inner(initial_rsp)) -} - -core::arch::global_asm!(" - .p2align 6 - .globl fork_wrapper - .type fork_wrapper, @function -fork_wrapper: - push rbp - mov rbp, rsp - - push rbx - push rbp - push r12 - push r13 - push r14 - push r15 - - mov rdi, rsp - call __relibc_internal_fork_impl - jmp 2f - -fork_ret: - xor rax, rax -2: - pop r15 - pop r14 - pop r13 - pop r12 - pop rbp - pop rbx - - pop rbp - ret - .size fork_wrapper, . - fork_wrapper - - .globl pte_clone_ret - .type pte_clone_ret, @function -pte_clone_ret: - - # Load registers - pop rax - pop rdi - pop rsi - pop rdx - pop rcx - pop r8 - pop r9 - - # Call entry point - call rax - - ret - .size pte_clone_ret, . - pte_clone_ret -"); - -extern "sysv64" { - fn fork_wrapper() -> usize; - fn fork_ret(); - fn pte_clone_ret(); -} diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index a51b56dce885639d3f6931389b3d98f5676e1d53..ab5deedb94b0588600c22afd7667e0dd805d1132 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -34,6 +34,7 @@ use super::{errno, types::*, Pal, Read}; static mut BRK_CUR: *mut c_void = ptr::null_mut(); static mut BRK_END: *mut c_void = ptr::null_mut(); +mod clone; mod epoll; mod exec; mod extra; @@ -355,6 +356,10 @@ impl Pal for Sys { // Close all O_CLOEXEC file descriptors. TODO: close_range? { + // NOTE: This approach of implementing O_CLOEXEC will not work in multithreaded + // scenarios. While execve() is undefined according to POSIX if there exist sibling + // threads, it could still be allowed by keeping certain file descriptors and instead + // set the active file table. let name = CStr::from_bytes_with_nul(b"thisproc:current/filetable\0").expect("string should be valid"); let files_fd = match File::open(name, fcntl::O_RDONLY) { Ok(f) => f, @@ -398,7 +403,9 @@ impl Pal for Sys { // TODO: Plus, at this point fexecve is not implemented (but specified in // POSIX.1-2008), and to avoid bad syscalls such as fpath, passing a file descriptor // would be better. - escalate_fd.write_all(path.to_bytes()); + if escalate_fd.write_all(path.to_bytes()).is_err() { + return -1; + } // Second, we write the flattened args and envs with NUL characters separating // individual items. @@ -455,7 +462,7 @@ impl Pal for Sys { } fn fork() -> pid_t { - e(extra::fork_impl()) as pid_t + e(clone::fork_impl()) as pid_t } fn fstat(fildes: c_int, buf: *mut stat) -> c_int { @@ -938,7 +945,7 @@ impl Pal for Sys { #[cfg(target_arch = "x86_64")] unsafe fn pte_clone(stack: *mut usize) -> pid_t { - e(extra::pte_clone_impl(stack)) as pid_t + e(clone::pte_clone_impl(stack)) as pid_t } fn read(fd: c_int, buf: &mut [u8]) -> ssize_t {