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:
+27
-11
@@ -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(
|
||||
|
||||
@@ -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) };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user