From df8e2deddcdcc5621ea194e20cd3a5858d1e0aae Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 7 Jul 2022 12:20:44 +0200 Subject: [PATCH] Fix file descriptor leak in fork(). --- src/platform/redox/clone.rs | 55 +++++++++++++++++++++++++------------ src/platform/redox/exec.rs | 3 -- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/platform/redox/clone.rs b/src/platform/redox/clone.rs index 8877ef91..398541bf 100644 --- a/src/platform/redox/clone.rs +++ b/src/platform/redox/clone.rs @@ -119,9 +119,11 @@ pub fn fork_impl() -> Result<usize> { } fn fork_inner(initial_rsp: *mut usize) -> Result<usize> { - let new_pid = { + let (cur_filetable_fd, new_pid_fd, 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()?; + (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). @@ -138,6 +140,18 @@ fn fork_inner(initial_rsp: *mut usize) -> Result<usize> { copy_str(*cur_pid_fd, *new_pid_fd, "name")?; copy_str(*cur_pid_fd, *new_pid_fd, "cwd")?; + // 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). + { + cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?); + + // This must be done before the address space is copied. + unsafe { + initial_rsp.write(*cur_filetable_fd); + initial_rsp.add(1).write(*new_pid_fd); + } + } + // CoW-duplicate address space. { let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?); @@ -190,22 +204,18 @@ fn fork_inner(initial_rsp: *mut usize) -> Result<usize> { 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 - }; + } + // Copy the file table. We do this last to ensure that all previously used file descriptors are + // closed. The only exception -- the filetable selection fd and the current filetable fd -- + // will be closed by the child process. + { + // 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)); + } // Unblock context. syscall::kill(new_pid, SIGCONT)?; @@ -216,6 +226,11 @@ fn fork_inner(initial_rsp: *mut usize) -> Result<usize> { unsafe extern "sysv64" fn __relibc_internal_fork_impl(initial_rsp: *mut usize) -> usize { Error::mux(fork_inner(initial_rsp)) } +#[no_mangle] +unsafe extern "sysv64" fn __relibc_internal_fork_hook(cur_filetable_fd: usize, new_pid_fd: usize) { + let _ = syscall::close(cur_filetable_fd); + let _ = syscall::close(new_pid_fd); +} #[no_mangle] core::arch::global_asm!(" @@ -233,13 +248,19 @@ fork_wrapper: push r14 push r15 + sub rsp, 16 + mov rdi, rsp call __relibc_internal_fork_impl jmp 2f fork_ret: + mov rdi, [rsp] + mov rsi, [rsp + 8] + call __relibc_internal_fork_hook xor rax, rax 2: + add rsp, 16 pop r15 pop r14 pop r13 diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs index 77c03b7c..bdda06da 100644 --- a/src/platform/redox/exec.rs +++ b/src/platform/redox/exec.rs @@ -88,9 +88,6 @@ fn fexec_impl_inner(file: File, path: &[u8], args: &[&[u8]], envs: &[&[u8]], arg // some misalignments, and then execute the SYS_EXEC syscall to replace the program memory // entirely. - // TODO: Introduce RAII guards to all owned allocations so that no leaks occur in case of - // errors. - let mut header_bytes = [0_u8; core::mem::size_of::<Header>()]; read_all(fd, Some(0), &mut header_bytes)?; let header = Header::from_bytes(&header_bytes); -- GitLab