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:
+31
-13
@@ -85,7 +85,8 @@ pub fn fexec_impl(
|
||||
// some misalignments, and then switch address space.
|
||||
|
||||
let mut header_bytes = [0_u8; size_of::<Header>()];
|
||||
pread_all(&image_file, 0, &mut header_bytes)?;
|
||||
pread_all(&image_file, 0, &mut header_bytes)
|
||||
.unwrap_or_else(|e| panic!("[fexec] pread header failed: {:?}", e));
|
||||
let header = Header::from_bytes(&header_bytes);
|
||||
|
||||
if header.e_ident[..4] != [0x7F, 0x45, 0x4C, 0x46] {
|
||||
@@ -96,10 +97,13 @@ pub fn fexec_impl(
|
||||
let grants_fd = if let Some(interp) = interp_override.as_ref() {
|
||||
FdGuard::new(interp.grants_fd).to_upper()?
|
||||
} else {
|
||||
let current_addrspace_fd = thread_fd.dup_into_upper(b"addrspace")?;
|
||||
current_addrspace_fd.dup_into_upper(b"empty")?
|
||||
let current_addrspace_fd = thread_fd.dup_into_upper(b"addrspace")
|
||||
.unwrap_or_else(|e| panic!("[fexec] dup addrspace failed: {:?}", e));
|
||||
current_addrspace_fd.dup_into_upper(b"empty")
|
||||
.unwrap_or_else(|e| panic!("[fexec] dup empty failed: {:?}", e))
|
||||
};
|
||||
grants_fd.fcntl(syscall::F_SETFD, O_CLOEXEC)?;
|
||||
grants_fd.fcntl(syscall::F_SETFD, O_CLOEXEC)
|
||||
.unwrap_or_else(|e| panic!("[fexec] fcntl failed: {:?}", e));
|
||||
|
||||
// Never allow more than 1 MiB of program headers.
|
||||
const MAX_PH_SIZE: usize = 1024 * 1024;
|
||||
@@ -131,13 +135,15 @@ pub fn fexec_impl(
|
||||
u64::from(header.e_phoff),
|
||||
phs,
|
||||
)
|
||||
.map_err(|_| Error::new(EIO))?;
|
||||
.map_err(|_| Error::new(EIO))
|
||||
.unwrap_or_else(|_| panic!("[fexec] pread program headers failed"));
|
||||
|
||||
let mut span: Option<Range<usize>> = None;
|
||||
for ph_idx in 0..phnum {
|
||||
let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize];
|
||||
let segment: &ProgramHeader =
|
||||
plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))?;
|
||||
plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))
|
||||
.unwrap_or_else(|_| panic!("[fexec] ph parse failed ph_idx={} phentsize={}", ph_idx, phentsize));
|
||||
if segment.p_type != PT_LOAD {
|
||||
continue;
|
||||
}
|
||||
@@ -158,7 +164,8 @@ pub fn fexec_impl(
|
||||
|
||||
let base_addr = if header.e_type == ET_DYN {
|
||||
// PIE
|
||||
let addr = mmap_anon_remote(&grants_fd, 0, 0, span_size, MapFlags::PROT_NONE)?;
|
||||
let addr = mmap_anon_remote(&grants_fd, 0, 0, span_size, MapFlags::PROT_NONE)
|
||||
.unwrap_or_else(|e| panic!("[fexec] mmap PIE base failed: {:?} span_size={}", e, span_size));
|
||||
update_min_mmap_addr(addr, span_size);
|
||||
addr
|
||||
} else {
|
||||
@@ -168,7 +175,8 @@ pub fn fexec_impl(
|
||||
span.start,
|
||||
span_size,
|
||||
MapFlags::MAP_FIXED_NOREPLACE,
|
||||
)?;
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("[fexec] mmap non-PIE base failed: {:?} span.start={:#x} span_size={}", e, span.start, span_size));
|
||||
update_min_mmap_addr(span.start, span_size);
|
||||
0
|
||||
};
|
||||
@@ -179,7 +187,8 @@ pub fn fexec_impl(
|
||||
for ph_idx in 0..phnum {
|
||||
let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize];
|
||||
let segment: &ProgramHeader =
|
||||
plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))?;
|
||||
plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))
|
||||
.unwrap_or_else(|_| panic!("[fexec] ph parse (2nd loop) failed ph_idx={}", ph_idx));
|
||||
let mut flags = if segment.p_flags & PF_R == PF_R {
|
||||
syscall::PROT_READ
|
||||
} else {
|
||||
@@ -226,7 +235,8 @@ pub fn fexec_impl(
|
||||
base_addr + vaddr,
|
||||
total_page_count * PAGE_SIZE,
|
||||
flags | MapFlags::MAP_FIXED,
|
||||
)?;
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("[fexec] mmap PT_LOAD failed: {:?} vaddr={:#x} pages={}", e, base_addr + vaddr, total_page_count));
|
||||
|
||||
if segment.p_offset <= header.e_phoff
|
||||
&& header.e_phoff < segment.p_offset + segment.p_filesz
|
||||
@@ -281,7 +291,8 @@ pub fn fexec_impl(
|
||||
STACK_TOP - STACK_SIZE,
|
||||
STACK_SIZE,
|
||||
MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_FIXED_NOREPLACE,
|
||||
)?;
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("[fexec] mmap stack failed: {:?}", e));
|
||||
|
||||
let mut sp = STACK_TOP;
|
||||
let mut stack_page = Option::<MmapGuard>::None;
|
||||
@@ -362,7 +373,8 @@ pub fn fexec_impl(
|
||||
0,
|
||||
args_envs_size_aligned,
|
||||
MapFlags::PROT_READ | MapFlags::PROT_WRITE,
|
||||
)?;
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("[fexec] mmap args/env failed: {:?}", e));
|
||||
update_min_mmap_addr(target_args_env_address, args_envs_size_aligned);
|
||||
|
||||
let mut offset = 0;
|
||||
@@ -481,7 +493,8 @@ pub fn fexec_impl(
|
||||
let _ = mmap_min_fd.write(&usize::to_ne_bytes(min_mmap_addr));
|
||||
}
|
||||
|
||||
let addrspace_selection_fd = thread_fd.dup_into_upper(b"current-addrspace")?;
|
||||
let addrspace_selection_fd = thread_fd.dup_into_upper(b"current-addrspace")
|
||||
.unwrap_or_else(|e| panic!("[fexec] dup current-addrspace failed: {:?}", e));
|
||||
|
||||
let _ = addrspace_selection_fd.write(&create_set_addr_space_buf(
|
||||
grants_fd.as_raw_fd(),
|
||||
@@ -794,6 +807,11 @@ impl FileBufReader {
|
||||
Ok(Some(num))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_external_fd(fd: usize) -> Result<()> {
|
||||
crate::sys::register_external_fd(fd)
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct FdGuard<const UPPER: bool = false> {
|
||||
fd: usize,
|
||||
|
||||
@@ -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