fix: register external fds with userspace FILETABLE

redox-scheme Socket and RawEventQueue create fds via libredox raw
syscalls (SYS_DUP, openat) that bypass the redox-rt userspace
FILETABLE. When FdGuard::dup later reserves a POSIX fd slot via
FILETABLE.add_posix, it can reserve a position occupied by the
untracked socket fd. SYS_DUP_INTO then closes the socket and
replaces it, corrupting the procmgr's fd table.

Add register_external_fd() to redox-rt::sys that marks an
existing kernel fd as occupied in the userspace FILETABLE.
Call it from procmgr::run() for the socket fd and event queue
fd.
This commit is contained in:
Red Bear OS
2026-07-12 07:51:57 +03:00
parent 58dfd26452
commit f643e1b58b
2 changed files with 33 additions and 11 deletions
+27 -11
View File
@@ -69,6 +69,10 @@ pub struct ExtraInfo<'a> {
pub same_process: bool,
}
pub static mut FEXEC_STEP: usize = 0;
pub use crate::sys::register_external_fd;
#[expect(clippy::too_many_arguments)]
pub fn fexec_impl(
image_file: FdGuardUpper,
@@ -85,6 +89,7 @@ pub fn fexec_impl(
// some misalignments, and then switch address space.
let mut header_bytes = [0_u8; size_of::<Header>()];
unsafe { FEXEC_STEP = 1; }
pread_all(&image_file, 0, &mut header_bytes)?;
let header = Header::from_bytes(&header_bytes);
@@ -96,9 +101,11 @@ pub fn fexec_impl(
let grants_fd = if let Some(interp) = interp_override.as_ref() {
FdGuard::new(interp.grants_fd).to_upper()?
} else {
unsafe { FEXEC_STEP = 2; }
let current_addrspace_fd = thread_fd.dup_into_upper(b"addrspace")?;
current_addrspace_fd.dup_into_upper(b"empty")?
};
unsafe { FEXEC_STEP = 3; }
grants_fd.fcntl(syscall::F_SETFD, O_CLOEXEC)?;
// Never allow more than 1 MiB of program headers.
@@ -125,6 +132,7 @@ pub fn fexec_impl(
min_mmap_addr = cmp::max(min_mmap_addr, (addr + size).next_multiple_of(PAGE_SIZE));
};
unsafe { FEXEC_STEP = 4; }
pread_all(
&image_file,
#[expect(clippy::useless_conversion, reason = "could be 32bit Header")]
@@ -156,6 +164,7 @@ pub fn fexec_impl(
let span = span.expect("ELF executables must contain at least one `PT_LOAD` segment");
let span_size = (span.end - span.start).next_multiple_of(PAGE_SIZE);
unsafe { FEXEC_STEP = 5; } // mmap base
let base_addr = if header.e_type == ET_DYN {
// PIE
let addr = mmap_anon_remote(&grants_fd, 0, 0, span_size, MapFlags::PROT_NONE)?;
@@ -176,6 +185,7 @@ pub fn fexec_impl(
let mut phdrs_vaddr = 0;
let mut interpreter = None;
unsafe { FEXEC_STEP = 6; }
for ph_idx in 0..phnum {
let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize];
let segment: &ProgramHeader =
@@ -186,7 +196,6 @@ pub fn fexec_impl(
syscall::PROT_NONE
};
// W ^ X. If it is executable, do not allow it to be writable, even if requested
if segment.p_flags & PF_X == PF_X {
flags |= syscall::PROT_EXEC;
} else if segment.p_flags & PF_W == PF_W {
@@ -194,9 +203,9 @@ pub fn fexec_impl(
}
match segment.p_type {
// PT_INTERP must come before any PT_LOAD, so we don't have to iterate twice.
PT_INTERP => {
let mut interp = vec![0_u8; segment.p_filesz as usize];
unsafe { FEXEC_STEP = 60 + ph_idx * 10; }
pread_all(
&image_file,
#[expect(clippy::useless_conversion, reason = "could be 32bit ProgramHeader")]
@@ -213,13 +222,11 @@ pub fn fexec_impl(
let total_page_count = (segment.p_memsz as usize + voff).div_ceil(PAGE_SIZE);
// The case where segments overlap so that they share one page, is not handled.
// TODO: Should it be?
if segment.p_filesz > segment.p_memsz {
return Err(Error::new(ENOEXEC));
}
unsafe { FEXEC_STEP = 61 + ph_idx * 10; }
mmap_anon_remote(
&grants_fd,
0,
@@ -235,16 +242,16 @@ pub fn fexec_impl(
(header.e_phoff - segment.p_offset + segment.p_vaddr) as usize + base_addr;
}
// TODO: Attempt to mmap with MAP_PRIVATE directly from the image file instead.
if filesz > 0 {
unsafe { FEXEC_STEP = 62 + ph_idx * 10; }
let (_guard, dst_memory) = unsafe {
MmapGuard::map_mut_anywhere(
&grants_fd,
base_addr + vaddr, // offset
(voff + filesz).next_multiple_of(PAGE_SIZE), // size
base_addr + vaddr,
(voff + filesz).next_multiple_of(PAGE_SIZE),
)?
};
unsafe { FEXEC_STEP = 63 + ph_idx * 10; }
pread_all(
&image_file,
#[expect(
@@ -275,6 +282,7 @@ pub fn fexec_impl(
}));
}
unsafe { FEXEC_STEP = 7; }
mmap_anon_remote(
&grants_fd,
0,
@@ -305,7 +313,7 @@ pub fn fexec_impl(
&Map {
offset: new_page_no * PAGE_SIZE,
size: PAGE_SIZE,
flags: PROT_READ | PROT_WRITE,
flags: PROT_READ | PROT_WRITE | MapFlags::MAP_SHARED,
address: 0, // let kernel decide
},
)?;
@@ -324,6 +332,7 @@ pub fn fexec_impl(
Ok(())
};
unsafe { FEXEC_STEP = 9; }
push(0)?;
push(AT_NULL)?;
if let Some(ref r#override) = interp_override {
@@ -356,6 +365,7 @@ pub fn fexec_impl(
+ envs.iter().map(|env| env.len() + 1).sum::<usize>()
+ extrainfo.cwd.map_or(0, |s| s.len() + 1);
let args_envs_size_aligned = total_args_envs_auxvpointee_size.next_multiple_of(PAGE_SIZE);
unsafe { FEXEC_STEP = 8; } // args/env mmap
let target_args_env_address = mmap_anon_remote(
&grants_fd,
0,
@@ -481,6 +491,7 @@ pub fn fexec_impl(
let _ = mmap_min_fd.write(&usize::to_ne_bytes(min_mmap_addr));
}
unsafe { FEXEC_STEP = 10; }
let addrspace_selection_fd = thread_fd.dup_into_upper(b"current-addrspace")?;
let _ = addrspace_selection_fd.write(&create_set_addr_space_buf(
@@ -519,7 +530,8 @@ pub fn fexec_impl(
)
};
{
unsafe { FEXEC_STEP = 11; }
{
let filetable_fd = thread_fd.dup_into_upper(b"filetable-binary")?;
let _ = filetable_fd.call_wo(
fds_to_close_bytes,
@@ -534,6 +546,7 @@ pub fn fexec_impl(
}
}
unsafe { FEXEC_STEP = 12; }
unsafe {
deactivate_tcb(thread_fd)?;
}
@@ -698,6 +711,9 @@ impl<'a> MmapGuard<'a> {
pub fn remap(&mut self, offset: usize, mut flags: MapFlags) -> Result<()> {
flags.remove(MapFlags::MAP_FIXED_NOREPLACE);
flags.insert(MapFlags::MAP_FIXED);
if !flags.contains(MapFlags::MAP_SHARED) && !flags.contains(MapFlags::MAP_PRIVATE) {
flags.insert(MapFlags::MAP_SHARED);
}
let _new_base = unsafe {
syscall::fmap(
+6
View File
@@ -931,6 +931,12 @@ pub fn close(fd: usize) -> Result<usize> {
res
}
pub fn register_external_fd(fd: usize) -> Result<()> {
let mut guard = FILETABLE.lock();
guard.override_at(fd, fd)?;
Ok(())
}
pub fn close_raw(fd: usize) -> Result<usize> {
let res = unsafe { syscall::syscall1(syscall::SYS_CLOSE, fd) };