From 34f98efd393b41664c65d9fa189ada2be815287e Mon Sep 17 00:00:00 2001 From: Weiteng Chen Date: Fri, 4 Sep 2026 19:00:29 +0000 Subject: [PATCH 1/3] Resolve each raw fd once per syscall Chunked syscalls re-resolved the caller's raw fd for every chunk, so a concurrent close plus open could make a later chunk operate on a different open file description that had reused the raw slot. Resolve the raw fd once into an `AnyTypedFd` that retains the subsystem's `Arc>`, and thread that through the whole syscall. A racing close now makes later chunks fail with `EBADF` instead of silently following the reused slot. This covers `read`/`write`, the `iovec` variants, the large-buffer `read` chunking, `sendfile`, `mmap`, all socket syscalls, `fstat`/`faccessat`, and `dup`. The fd layer is consolidated to support that: - `AnyTypedFd::dispatch` replaces `FilesState::run_on_typed_fd`, with `as_fs`/`fs_only` and the `on_any_fd!` macro covering the cases that previously needed blocks of repeated no-op closures. - fd narrowing happens once, in `Task::typed_fd`/`FilesState::typed_fd`; `FsPath` stores `i32` so callers stop round-tripping through `u32`. - `sys_recvmmsg` and `sys_sendfile` take a single `files` borrow. - `AnyTypedFd` carries `Debug`/`subsystem_name` for diagnostics. Adds regression tests covering fd reuse under `readv`, `dup`, and socket lookups. --- litebox_shim_linux/src/lib.rs | 128 +- litebox_shim_linux/src/syscalls/file.rs | 1440 +++++++++++------------ litebox_shim_linux/src/syscalls/mm.rs | 34 +- litebox_shim_linux/src/syscalls/net.rs | 621 ++++++---- 4 files changed, 1164 insertions(+), 1059 deletions(-) diff --git a/litebox_shim_linux/src/lib.rs b/litebox_shim_linux/src/lib.rs index 6a9d9977d..769a90ca7 100644 --- a/litebox_shim_linux/src/lib.rs +++ b/litebox_shim_linux/src/lib.rs @@ -22,7 +22,6 @@ use alloc::sync::Arc; use core::cell::{Cell, RefCell}; use litebox::{ LiteBox, - fd::TypedFd, mm::{PageManager, linux::PAGE_SIZE}, net::Network, pipes::Pipes, @@ -52,8 +51,6 @@ pub mod syscalls; pub mod transport; mod wait; -use crate::syscalls::file::get_file_descriptor_flags; - pub type DefaultFS = LinuxFS; pub(crate) type LinuxFS = @@ -436,8 +433,9 @@ impl Task { let files = self.files.borrow(); let alive_fds: Vec = files.raw_descriptor_store.read().iter_alive().collect(); for raw_fd in alive_fds { - if let Ok(flags) = get_file_descriptor_flags(raw_fd, &self.global, &files) - && flags.contains(litebox_common_linux::FileDescriptorFlags::FD_CLOEXEC) + if let Ok(fd) = files.typed_fd_from_raw(raw_fd) + && syscalls::file::get_file_descriptor_flags(&fd, &self.global) + .contains(litebox_common_linux::FileDescriptorFlags::FD_CLOEXEC) { let _ = self.do_close(raw_fd); } @@ -446,42 +444,31 @@ impl Task { } impl syscalls::file::FilesState { - #[expect(clippy::too_many_arguments)] - pub(crate) fn run_on_raw_fd( + /// Resolve a userland fd number, rejecting negative values with `EBADF`. + pub(crate) fn typed_fd(&self, fd: i32) -> Result, Errno> { + self.typed_fd_from_raw(usize::try_from(fd).map_err(|_| Errno::EBADF)?) + } + + pub(crate) fn typed_fd_from_raw( &self, fd: usize, - fs: impl FnOnce(&FileFd) -> R, - net: impl FnOnce(&TypedFd>) -> R, - pipes: impl FnOnce(&TypedFd>) -> R, - eventfd: impl FnOnce(&TypedFd>) -> R, - epoll: impl FnOnce(&TypedFd>) -> R, - unix: impl FnOnce(&TypedFd>) -> R, - ) -> Result { + ) -> Result, Errno> { let rds = self.raw_descriptor_store.read(); - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(fs(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(net(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(pipes(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(eventfd(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(epoll(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(unix(&fd)); + + macro_rules! resolve_fd { + ($subsystem:ty, $variant:ident) => { + if let Ok(fd) = rds.fd_from_raw_integer::<$subsystem>(fd) { + return Ok(syscalls::file::AnyTypedFd::$variant(fd)); + } + }; } + + resolve_fd!(LinuxFS, Fs); + resolve_fd!(Network, Network); + resolve_fd!(Pipes, Pipes); + resolve_fd!(syscalls::eventfd::EventfdSubsystem, Eventfd); + resolve_fd!(syscalls::epoll::EpollSubsystem, Epoll); + resolve_fd!(syscalls::unix::UnixSocketSubsystem, Unix); Err(Errno::EBADF) } } @@ -513,23 +500,33 @@ impl ToSyscallResult for Result { } impl Task { - /// A wrapper function around `sys_pread64` that copies data in chunks to avoid OOMing. + /// A wrapper function around `do_pread_with_user_buf` that copies data in chunks to avoid OOMing. fn pread_with_user_buf( &self, fd: i32, buf: UserPtrMut, count: usize, offset: i64, + ) -> Result { + self.with_typed_fd(fd, |fd| self.do_pread_with_user_buf(fd, buf, count, offset)) + } + + fn do_pread_with_user_buf( + &self, + fd: &syscalls::file::AnyTypedFd, + buf: UserPtrMut, + count: usize, + offset: i64, ) -> Result { let mut kernel_buf = vec![0u8; count.min(MAX_KERNEL_BUF_SIZE)]; let mut read_total = 0; while read_total < count { let to_read = (count - read_total).min(kernel_buf.len()); - match self.sys_pread64( - fd, - &mut kernel_buf[..to_read], - offset + (read_total.reinterpret_as_signed() as i64), - ) { + let read_offset = offset + .checked_add(read_total.reinterpret_as_signed() as i64) + .and_then(|offset| usize::try_from(offset).ok()) + .ok_or(Errno::EINVAL)?; + match self.do_read(fd, &mut kernel_buf[..to_read], Some(read_offset)) { Ok(0) => break, // EOF Ok(size) => { buf.copy_from_slice::(read_total, &kernel_buf[..size]) @@ -598,26 +595,36 @@ impl Task { } else { // If the read size is too large, we need to do some extra work to avoid OOMing. // We read data in chunks and update the file offset ourselves only if the read succeeds. - self.sys_lseek(fd, 0, litebox::fs::SeekWhence::RelativeToCurrentOffset) - .inspect_err(|e| { - match *e { - Errno::EBADF => (), // safe errors to return - Errno::ESPIPE => { - unimplemented!("read on non-seekable fds with large buffers"); - } - Errno::EINVAL => { - unreachable!("seekable file should not return EINVAL when getting current offset"); - } - _ => { - unimplemented!("unexpected error from lseek: {}", e); + self.with_typed_fd(fd, |fd| { + self.do_seek( + fd, + 0, + litebox::fs::SeekWhence::RelativeToCurrentOffset, + ) + .inspect_err(|e| { + match *e { + Errno::EBADF => (), // safe errors to return + Errno::ESPIPE => { + unimplemented!("read on non-seekable fds with large buffers"); + } + Errno::EINVAL => { + unreachable!("seekable file should not return EINVAL when getting current offset"); + } + _ => { + unimplemented!("unexpected error from lseek: {}", e); + } } - } - }) - .and_then(|cur_loc| { - self.pread_with_user_buf(fd, buf, count, i64::try_from(cur_loc).unwrap()) + }) + .and_then(|cur_loc| { + self.do_pread_with_user_buf( + fd, + buf, + count, + i64::try_from(cur_loc).unwrap(), + ) .inspect(|read_total| { // Update the file offset to reflect the read we just did. - self.sys_lseek( + self.do_seek( fd, (cur_loc + read_total).reinterpret_as_signed(), litebox::fs::SeekWhence::RelativeToBeginning, @@ -625,6 +632,7 @@ impl Task { // Given that previous lseek and pread succeeded, this lseek should also succeed. .expect("lseek failed"); }) + }) }) } } diff --git a/litebox_shim_linux/src/syscalls/file.rs b/litebox_shim_linux/src/syscalls/file.rs index b1a92bcd2..bfe751e47 100644 --- a/litebox_shim_linux/src/syscalls/file.rs +++ b/litebox_shim_linux/src/syscalls/file.rs @@ -167,6 +167,90 @@ impl FilesState { } } +/// A raw fd resolved once into the subsystem that owns it. +/// +/// Holding the `Arc` keeps the resolution stable: a concurrent `close` makes subsequent +/// operations fail with `EBADF` rather than follow a reused raw slot to another file. +pub(crate) enum AnyTypedFd { + Fs(alloc::sync::Arc>), + Network(alloc::sync::Arc>>), + Pipes(alloc::sync::Arc>>), + Eventfd(alloc::sync::Arc>>), + Epoll(alloc::sync::Arc>>), + Unix(alloc::sync::Arc>>), +} + +/// Apply one subsystem-generic expression to whichever subsystem an [`AnyTypedFd`] holds. +/// +/// Needed because a closure cannot be generic over the subsystem type. +macro_rules! on_any_fd { + ($any:expr, |$fd:ident| $body:expr) => { + match $any { + AnyTypedFd::Fs($fd) => $body, + AnyTypedFd::Network($fd) => $body, + AnyTypedFd::Pipes($fd) => $body, + AnyTypedFd::Eventfd($fd) => $body, + AnyTypedFd::Epoll($fd) => $body, + AnyTypedFd::Unix($fd) => $body, + } + }; +} + +impl AnyTypedFd { + /// The subsystem backing this fd, for diagnostics. + pub(crate) fn subsystem_name(&self) -> &'static str { + match self { + Self::Fs(_) => "fs", + Self::Network(_) => "net", + Self::Pipes(_) => "pipes", + Self::Eventfd(_) => "eventfd", + Self::Epoll(_) => "epoll", + Self::Unix(_) => "unix", + } + } + + /// The filesystem fd behind this descriptor, or `None` for every other subsystem. + pub(crate) fn as_fs(&self) -> Option<&FileFd> { + match self { + Self::Fs(fd) => Some(fd), + _ => None, + } + } + + /// Like [`Self::as_fs`], but fails with `otherwise` for non-filesystem descriptors. + pub(crate) fn fs_only(&self, otherwise: Errno) -> Result<&FileFd, Errno> { + self.as_fs().ok_or(otherwise) + } + + /// Run the handler matching this fd's subsystem. + pub(crate) fn dispatch( + &self, + fs: impl FnOnce(&FileFd) -> R, + net: impl FnOnce(&TypedFd>) -> R, + pipes: impl FnOnce(&TypedFd>) -> R, + eventfd: impl FnOnce(&TypedFd>) -> R, + epoll: impl FnOnce(&TypedFd>) -> R, + unix: impl FnOnce(&TypedFd>) -> R, + ) -> R { + match self { + Self::Fs(fd) => fs(fd), + Self::Network(fd) => net(fd), + Self::Pipes(fd) => pipes(fd), + Self::Eventfd(fd) => eventfd(fd), + Self::Epoll(fd) => epoll(fd), + Self::Unix(fd) => unix(fd), + } + } +} + +impl core::fmt::Debug for AnyTypedFd { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("AnyTypedFd") + .field(&self.subsystem_name()) + .finish() + } +} + /// Path in the file system #[derive(Debug)] enum FsPath { @@ -176,9 +260,9 @@ enum FsPath { Cwd, /// Path is relative to a file descriptor #[expect(dead_code, reason = "currently unused, might want to use later")] - FdRelative { fd: u32, path: CString }, + FdRelative { fd: i32, path: CString }, /// Fd - Fd(u32), + Fd(i32), } /// Maximum size of a file path @@ -201,7 +285,6 @@ impl FsPath { let cpath = path.to_c_str()?.into_owned(); FsPath::Absolute { path: cpath } } else if dirfd >= 0 { - let dirfd = u32::try_from(dirfd).expect("dirfd >= 0"); if path_str.is_empty() { FsPath::Fd(dirfd) } else { @@ -353,21 +436,16 @@ impl Task { /// Handle syscall `ftruncate` pub(crate) fn sys_ftruncate(&self, fd: i32, length: usize) -> Result<(), Errno> { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; let files = self.files.borrow(); - files - .run_on_raw_fd( - raw_fd, - |fd| files.fs.truncate(fd, length, false).map_err(Errno::from), - |_fd| todo!("net"), - |_fd| todo!("pipes"), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - ) - .flatten() + let fd = files.typed_fd(fd)?; + fd.dispatch( + |fd| files.fs.truncate(fd, length, false).map_err(Errno::from), + |_fd| todo!("net"), + |_fd| todo!("pipes"), + |_fd| Err(Errno::EINVAL), + |_fd| Err(Errno::EINVAL), + |_fd| Err(Errno::EINVAL), + ) } /// Handle syscall `mknodat` — create a filesystem node. @@ -437,14 +515,13 @@ impl Task { /// `offset` is an optional offset to read from. If `None`, it will read from the current file position. /// If `Some`, it will read from the specified offset without changing the current file position. pub fn sys_read(&self, fd: i32, buf: &mut [u8], offset: Option) -> Result { - let Ok(raw_fd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; - self.do_read(raw_fd, buf, offset) + let fd = self.typed_fd(fd)?; + self.do_read(&fd, buf, offset) } + pub(crate) fn do_read( &self, - fd: u32, + fd: &AnyTypedFd, buf: &mut [u8], offset: Option, ) -> Result { @@ -452,72 +529,66 @@ impl Task { // We need to do this cell dance because otherwise Rust can't recognize that the two // closures are mutually exclusive. let buf: core::cell::RefCell<&mut [u8]> = core::cell::RefCell::new(buf); - let n = files - .run_on_raw_fd( - fd as usize, - |fd| { - files - .fs - .read(fd, &mut buf.borrow_mut(), offset) - .map_err(Errno::from) - }, - |fd| { - espipe_for_non_seekable_offset(offset)?; - self.global.receive( + let result = fd.dispatch( + |fd| { + files + .fs + .read(fd, &mut buf.borrow_mut(), offset) + .map_err(Errno::from) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + self.global.receive( + &self.wait_cx(), + fd, + &mut buf.borrow_mut(), + litebox_common_linux::ReceiveFlags::empty(), + None, + ) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + self.global + .read_linux_pipe(&self.wait_cx(), fd, &mut buf.borrow_mut()) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + let buf = &mut buf.borrow_mut(); + if buf.len() < size_of::() { + return Err(Errno::EINVAL); + } + let value = file.read(&self.wait_cx())?; + buf[..size_of::()].copy_from_slice(&value.to_le_bytes()); + Ok(size_of::()) + }) + }, + |_fd| Err(Errno::EINVAL), + |fd| { + espipe_for_non_seekable_offset(offset)?; + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + file.recvfrom( &self.wait_cx(), - fd, &mut buf.borrow_mut(), litebox_common_linux::ReceiveFlags::empty(), None, ) - }, - |fd| { - espipe_for_non_seekable_offset(offset)?; - self.global - .read_linux_pipe(&self.wait_cx(), fd, &mut buf.borrow_mut()) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - espipe_for_non_seekable_offset(offset)?; - handle.with_entry(|file| { - let buf = &mut buf.borrow_mut(); - if buf.len() < size_of::() { - return Err(Errno::EINVAL); - } - let value = file.read(&self.wait_cx())?; - buf[..size_of::()].copy_from_slice(&value.to_le_bytes()); - Ok(size_of::()) - }) - }, - |_fd| Err(Errno::EINVAL), - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - espipe_for_non_seekable_offset(offset)?; - handle.with_entry(|file| { - file.recvfrom( - &self.wait_cx(), - &mut buf.borrow_mut(), - litebox_common_linux::ReceiveFlags::empty(), - None, - ) - }) - }, - ) - .flatten()?; - // For datagrams, the returned size represents the actual size of the message, - // which may be larger than the buffer size. - let capped_size = n.min(buf.borrow().len()); - Ok(capped_size) + }) + }, + ); + result.map(|size| size.min(buf.borrow().len())) } /// Handle syscall `write` @@ -525,73 +596,71 @@ impl Task { /// `offset` is an optional offset to write to. If `None`, it will write to the current file position. /// If `Some`, it will write to the specified offset without changing the current file position. pub fn sys_write(&self, fd: i32, buf: &[u8], offset: Option) -> Result { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; + let fd = self.typed_fd(fd)?; + self.do_write(&fd, buf, offset) + } + + fn do_write( + &self, + fd: &AnyTypedFd, + buf: &[u8], + offset: Option, + ) -> Result { let files = self.files.borrow(); - let res = files - .run_on_raw_fd( - raw_fd, - |fd| files.fs.write(fd, buf, offset).map_err(Errno::from), - |fd| { - espipe_for_non_seekable_offset(offset)?; - self.global.sendto( - &self.wait_cx(), - fd, - buf, - litebox_common_linux::SendFlags::empty(), - None, - ) - }, - |fd| { - espipe_for_non_seekable_offset(offset)?; - self.global.write_linux_pipe(&self.wait_cx(), fd, buf) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - espipe_for_non_seekable_offset(offset)?; - handle.with_entry(|file| { - if buf.len() < size_of::() { - return Err(Errno::EINVAL); - } - let value: u64 = u64::from_le_bytes( - buf[..size_of::()] - .try_into() - .map_err(|_| Errno::EINVAL)?, - ); - file.write(&self.wait_cx(), value) - }) - }, - |_fd| Err(Errno::EINVAL), - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - espipe_for_non_seekable_offset(offset)?; - handle.with_entry(|file| { - file.sendto(self, buf, litebox_common_linux::SendFlags::empty(), None) - }) - }, - ) - .flatten(); - if let Err(Errno::EPIPE) = res { + let result = fd.dispatch( + |fd| files.fs.write(fd, buf, offset).map_err(Errno::from), + |fd| { + espipe_for_non_seekable_offset(offset)?; + self.global.sendto( + &self.wait_cx(), + fd, + buf, + litebox_common_linux::SendFlags::empty(), + None, + ) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + self.global.write_linux_pipe(&self.wait_cx(), fd, buf) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + if buf.len() < size_of::() { + return Err(Errno::EINVAL); + } + let value = u64::from_le_bytes( + buf[..size_of::()] + .try_into() + .map_err(|_| Errno::EINVAL)?, + ); + file.write(&self.wait_cx(), value) + }) + }, + |_fd| Err(Errno::EINVAL), + |fd| { + espipe_for_non_seekable_offset(offset)?; + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + file.sendto(self, buf, litebox_common_linux::SendFlags::empty(), None) + }) + }, + ); + if let Err(Errno::EPIPE) = result { self.send_signal(Signal::SIGPIPE, signal::siginfo_kill(Signal::SIGPIPE)); } - res - } - - /// Handle syscall `pread64` - pub fn sys_pread64(&self, fd: i32, buf: &mut [u8], offset: i64) -> Result { - let pos = usize::try_from(offset).map_err(|_| Errno::EINVAL)?; - self.sys_read(fd, buf, Some(pos)) + result } /// Handle syscall `pwrite64` @@ -600,30 +669,23 @@ impl Task { self.sys_write(fd, buf, Some(pos)) } - fn rewind_sendfile_in_fd(&self, in_raw_fd: usize, unread_n: usize) -> Result<(), Errno> { + fn rewind_sendfile_in_fd( + &self, + in_fd: &AnyTypedFd, + unread_n: usize, + ) -> Result<(), Errno> { if unread_n == 0 { return Ok(()); } let rewind = isize::try_from(unread_n).map_err(|_| Errno::EOVERFLOW)?; + let fd = in_fd.fs_only(Errno::EINVAL)?; let files = self.files.borrow(); files - .run_on_raw_fd( - in_raw_fd, - |fd| { - files - .fs - .seek(fd, -rewind, SeekWhence::RelativeToCurrentOffset) - .map(|_| ()) - .map_err(Errno::from) - }, - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - ) - .flatten() + .fs + .seek(fd, -rewind, SeekWhence::RelativeToCurrentOffset) + .map(|_| ()) + .map_err(Errno::from) } /// Handle syscall `sendfile` @@ -634,12 +696,10 @@ impl Task { offset_ptr: Option>, count: usize, ) -> Result { - let Ok(in_raw_fd) = u32::try_from(in_fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; - // TODO: Linux rejects `sendfile` with `EINVAL` when `out_fd` has `O_APPEND` set. - self.check_raw_fd_exists(out_fd)?; + let typed_in_fd = self.typed_fd(in_fd)?; + let typed_out_fd = self.typed_fd(out_fd)?; + // TODO: Linux rejects `sendfile` with `EINVAL` when `out_fd` has `O_APPEND` set. let mut cur_off = offset_ptr .map(|p| { let off = p.read_at_offset::(0).ok_or(Errno::EFAULT)?; @@ -652,6 +712,7 @@ impl Task { let mut kernel_buf = vec![0u8; count.min(PAGE_SIZE)]; let mut total: usize = 0; + let files = self.files.borrow(); while total < count { let to_read = (count - total).min(kernel_buf.len()); @@ -663,20 +724,12 @@ impl Task { } else { Errno::EINVAL }; - let read_result = { - let buf_slice = &mut kernel_buf[..to_read]; - let files = self.files.borrow(); - files - .run_on_raw_fd( - in_raw_fd, - |fd| files.fs.read(fd, buf_slice, cur_off).map_err(Errno::from), - |_fd| Err(non_fs_err), - |_fd| Err(non_fs_err), - |_fd| Err(non_fs_err), - |_fd| Err(non_fs_err), - |_fd| Err(non_fs_err), - ) - .flatten() + let read_result = match typed_in_fd.as_fs() { + Some(fd) => files + .fs + .read(fd, &mut kernel_buf[..to_read], cur_off) + .map_err(Errno::from), + None => Err(non_fs_err), }; let read_n = match read_result { Ok(0) => break, @@ -685,12 +738,12 @@ impl Task { Err(_) => break, }; - let write_result = self.sys_write(out_fd, &kernel_buf[..read_n], None); + let write_result = self.do_write(&typed_out_fd, &kernel_buf[..read_n], None); let write_n = match write_result { Ok(n) => n, Err(e) => { if offset_ptr.is_none() { - self.rewind_sendfile_in_fd(in_raw_fd, read_n)?; + self.rewind_sendfile_in_fd(&typed_in_fd, read_n)?; } if total == 0 { return Err(e); @@ -701,11 +754,11 @@ impl Task { total += write_n; if let Some(ref mut off) = cur_off { - *off += write_n; + *off = off.checked_add(write_n).ok_or(Errno::EOVERFLOW)?; } if write_n < read_n { if offset_ptr.is_none() { - self.rewind_sendfile_in_fd(in_raw_fd, read_n - write_n)?; + self.rewind_sendfile_in_fd(&typed_in_fd, read_n - write_n)?; } break; } @@ -744,44 +797,40 @@ pub(crate) fn try_into_whence(value: i16) -> Result { impl Task { /// Handle syscall `lseek` pub fn sys_lseek(&self, fd: i32, offset: isize, whence: SeekWhence) -> Result { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; + let fd = self.typed_fd(fd)?; + self.do_seek(&fd, offset, whence) + } + + pub(crate) fn do_seek( + &self, + fd: &AnyTypedFd, + offset: isize, + whence: SeekWhence, + ) -> Result { + let fd = fd.fs_only(Errno::ESPIPE)?; let files = self.files.borrow(); - files - .run_on_raw_fd( - raw_fd, - |fd| match files.fs.seek(fd, offset, whence) { - Ok(pos) => Ok(pos), - Err(litebox::fs::errors::SeekError::NotAFile) => { - let base: usize = match whence { - SeekWhence::RelativeToBeginning => 0, - SeekWhence::RelativeToCurrentOffset => self - .global - .litebox - .descriptor_table() - .with_metadata(fd, |off: &Diroff| off.0) - .unwrap_or(0), - SeekWhence::RelativeToEnd => { - return Err(Errno::EINVAL); - } - }; - let new_pos = base.checked_add_signed(offset).ok_or(Errno::EINVAL)?; - self.global - .litebox - .descriptor_table_mut() - .set_entry_metadata(fd, Diroff(new_pos)); - Ok(new_pos) - } - Err(e) => Err(Errno::from(e)), - }, - |_| Err(Errno::ESPIPE), - |_| Err(Errno::ESPIPE), - |_| Err(Errno::ESPIPE), - |_| Err(Errno::ESPIPE), - |_| Err(Errno::ESPIPE), - ) - .flatten() + match files.fs.seek(fd, offset, whence) { + Ok(pos) => Ok(pos), + Err(litebox::fs::errors::SeekError::NotAFile) => { + let base = match whence { + SeekWhence::RelativeToBeginning => 0, + SeekWhence::RelativeToCurrentOffset => self + .global + .litebox + .descriptor_table() + .with_metadata(fd, |off: &Diroff| off.0) + .unwrap_or(0), + SeekWhence::RelativeToEnd => return Err(Errno::EINVAL), + }; + let new_pos = base.checked_add_signed(offset).ok_or(Errno::EINVAL)?; + self.global + .litebox + .descriptor_table_mut() + .set_entry_metadata(fd, Diroff(new_pos)); + Ok(new_pos) + } + Err(error) => Err(Errno::from(error)), + } } fn do_mkdir(&self, pathname: impl path::Arg, mode: Mode) -> Result<(), Errno> { @@ -827,21 +876,12 @@ impl Task { raw_fd: usize, replace: Option>, ) -> Result<(), Errno> { - enum ConsumedFd { - Fs(alloc::sync::Arc>), - Network(alloc::sync::Arc>>), - Pipes(alloc::sync::Arc>>), - Eventfd(alloc::sync::Arc>>), - Epoll(alloc::sync::Arc>>), - Unix(alloc::sync::Arc>>), - } - let files = self.files.borrow(); let mut rds = files.raw_descriptor_store.write(); - let consumed: ConsumedFd = match rds + let consumed: AnyTypedFd = match rds .fd_consume_raw_integer::>(raw_fd) { - Ok(fd) => ConsumedFd::Fs(fd), + Ok(fd) => AnyTypedFd::Fs(fd), Err(litebox::fd::ErrRawIntFd::NotFound) => { if let Some(new_fd) = replace { let success = rds.fd_into_specific_raw_integer(new_fd, raw_fd); @@ -853,23 +893,23 @@ impl Task { if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Network(fd) + AnyTypedFd::Network(fd) } else if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Pipes(fd) + AnyTypedFd::Pipes(fd) } else if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Eventfd(fd) + AnyTypedFd::Eventfd(fd) } else if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Epoll(fd) + AnyTypedFd::Epoll(fd) } else if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Unix(fd) + AnyTypedFd::Unix(fd) } else { unreachable!("all subsystems covered") } @@ -887,23 +927,23 @@ impl Task { drop(rds); match consumed { - ConsumedFd::Fs(fd) => { + AnyTypedFd::Fs(fd) => { if let Ok(raw_fd) = i32::try_from(raw_fd) { self.finalize_elf_patch(raw_fd); } files.fs.close(&fd).map_err(Errno::from) } - ConsumedFd::Network(fd) => self.global.close_socket(&self.wait_cx(), fd), - ConsumedFd::Pipes(fd) => self.global.close_linux_pipe(&fd), - ConsumedFd::Eventfd(fd) => { + AnyTypedFd::Network(fd) => self.global.close_socket(&self.wait_cx(), fd), + AnyTypedFd::Pipes(fd) => self.global.close_linux_pipe(&fd), + AnyTypedFd::Eventfd(fd) => { self.remove_and_drop_descriptor(&fd); Ok(()) } - ConsumedFd::Epoll(fd) => { + AnyTypedFd::Epoll(fd) => { self.remove_and_drop_descriptor(&fd); Ok(()) } - ConsumedFd::Unix(fd) => { + AnyTypedFd::Unix(fd) => { self.remove_and_drop_descriptor(&fd); Ok(()) } @@ -912,12 +952,23 @@ impl Task { /// Handle syscall `close` pub(crate) fn sys_close(&self, fd: i32) -> Result<(), Errno> { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; + let raw_fd = usize::try_from(fd).map_err(|_| Errno::EBADF)?; self.do_close(raw_fd) } + /// Resolve a userland fd number into the subsystem that owns it. + pub(crate) fn typed_fd(&self, fd: i32) -> Result, Errno> { + self.files.borrow().typed_fd(fd) + } + + pub(crate) fn with_typed_fd( + &self, + fd: i32, + f: impl FnOnce(&AnyTypedFd) -> Result, + ) -> Result { + f(&self.typed_fd(fd)?) + } + /// Handle syscall `preadv` pub(crate) fn sys_preadv( &self, @@ -927,15 +978,16 @@ impl Task { offset: i64, ) -> Result { let base_offset = usize::try_from(offset).map_err(|_| Errno::EINVAL)?; - self.check_raw_fd_exists(fd)?; - check_iovcnt(iovcnt)?; - let iovs: &[IoReadVec] = &iovec - .to_owned_slice::(iovcnt) - .ok_or(Errno::EFAULT)?; - let mut kernel_buffer = vec![0u8; PAGE_SIZE]; - read_from_iovec::<_, Platform>(iovs, &mut kernel_buffer, |buf, total| { - let cur_offset = base_offset.checked_add(total).ok_or(Errno::EOVERFLOW)?; - self.sys_read(fd, buf, Some(cur_offset)) + self.with_typed_fd(fd, |fd| { + check_iovcnt(iovcnt)?; + let iovs: &[IoReadVec] = &iovec + .to_owned_slice::(iovcnt) + .ok_or(Errno::EFAULT)?; + let mut kernel_buffer = vec![0u8; PAGE_SIZE]; + read_from_iovec::<_, Platform>(iovs, &mut kernel_buffer, |buf, total| { + let cur_offset = base_offset.checked_add(total).ok_or(Errno::EOVERFLOW)?; + self.do_read(fd, buf, Some(cur_offset)) + }) }) } @@ -948,15 +1000,16 @@ impl Task { offset: i64, ) -> Result { let base_offset = usize::try_from(offset).map_err(|_| Errno::EINVAL)?; - self.check_raw_fd_exists(fd)?; - check_iovcnt(iovcnt)?; - let iovs: &[IoWriteVec] = &iovec - .to_owned_slice::(iovcnt) - .ok_or(Errno::EFAULT)?; - // TODO: Linux ignores pwritev's offset for O_APPEND files; see the O_APPEND bug documented in pwrite(2). - write_to_iovec::<_, Platform>(iovs, |buf, total| { - let cur_offset = base_offset.checked_add(total).ok_or(Errno::EOVERFLOW)?; - self.sys_write(fd, buf, Some(cur_offset)) + self.with_typed_fd(fd, |fd| { + check_iovcnt(iovcnt)?; + let iovs: &[IoWriteVec] = &iovec + .to_owned_slice::(iovcnt) + .ok_or(Errno::EFAULT)?; + // TODO: Linux ignores pwritev's offset for O_APPEND files; see the O_APPEND bug documented in pwrite(2). + write_to_iovec::<_, Platform>(iovs, |buf, total| { + let cur_offset = base_offset.checked_add(total).ok_or(Errno::EOVERFLOW)?; + self.do_write(fd, buf, Some(cur_offset)) + }) }) } @@ -967,38 +1020,22 @@ impl Task { iovec: UserPtr, iovcnt: usize, ) -> Result { - self.check_raw_fd_exists(fd)?; - check_iovcnt(iovcnt)?; - let iovs: &[IoReadVec] = &iovec - .to_owned_slice::(iovcnt) - .ok_or(Errno::EFAULT)?; - let mut kernel_buffer = vec![0u8; PAGE_SIZE]; - // TODO: The data transfers performed by readv() and writev() are atomic: the data - // written by writev() is written as a single block that is not intermingled with - // output from writes in other processes - read_from_iovec::<_, Platform>(iovs, &mut kernel_buffer, |buf, _total| { - self.sys_read(fd, buf, None) + self.with_typed_fd(fd, |fd| { + check_iovcnt(iovcnt)?; + let iovs: &[IoReadVec] = &iovec + .to_owned_slice::(iovcnt) + .ok_or(Errno::EFAULT)?; + let mut kernel_buffer = vec![0u8; PAGE_SIZE]; + // TODO: The data transfers performed by readv() and writev() are atomic: the data + // written by writev() is written as a single block that is not intermingled with + // output from writes in other processes + read_from_iovec::<_, Platform>(iovs, &mut kernel_buffer, |buf, _total| { + self.do_read(fd, buf, None) + }) }) } } -impl Task { - fn check_raw_fd_exists(&self, fd: i32) -> Result<(), Errno> { - let raw_fd = usize::try_from(fd).map_err(|_| Errno::EBADF)?; - if self - .files - .borrow() - .raw_descriptor_store - .read() - .is_alive(raw_fd) - { - Ok(()) - } else { - Err(Errno::EBADF) - } - } -} - /// Linux's `IOV_MAX` / `UIO_MAXIOV`: the kernel rejects iovec counts above this /// with `EINVAL` for `readv`/`writev`/`preadv`/`pwritev`. const IOV_MAX: usize = 1024; @@ -1128,15 +1165,16 @@ impl Task { iovec: UserPtr, iovcnt: usize, ) -> Result { - self.check_raw_fd_exists(fd)?; - check_iovcnt(iovcnt)?; - let iovs: &[IoWriteVec] = &iovec - .to_owned_slice::(iovcnt) - .ok_or(Errno::EFAULT)?; - // TODO: The data transfers performed by readv() and writev() are atomic: the data - // written by writev() is written as a single block that is not intermingled with - // output from writes in other processes - write_to_iovec::<_, Platform>(iovs, |buf, _total| self.sys_write(fd, buf, None)) + self.with_typed_fd(fd, |fd| { + check_iovcnt(iovcnt)?; + let iovs: &[IoWriteVec] = &iovec + .to_owned_slice::(iovcnt) + .ok_or(Errno::EFAULT)?; + // TODO: The data transfers performed by readv() and writev() are atomic: the data + // written by writev() is written as a single block that is not intermingled with + // output from writes in other processes + write_to_iovec::<_, Platform>(iovs, |buf, _total| self.do_write(fd, buf, None)) + }) } fn validate_access_mode(mode: &AccessFlags) -> Result<(), Errno> { @@ -1242,7 +1280,7 @@ impl Task { self.do_access(cwd, mode, caller) } FsPath::Fd(fd) if flags.contains(AtFlags::AT_EMPTY_PATH) => { - let stat: FileStat = descriptor_stat(fd as usize, self)?; + let stat: FileStat = self.with_typed_fd(fd, |fd| self.do_stat(fd))?; let owner = AccessUserInfo { user: stat.st_uid, group: stat.st_gid, @@ -1304,34 +1342,56 @@ impl Task { } } -fn descriptor_stat( - raw_fd: usize, - task: &Task, -) -> Result -where - T: From + From, -{ - // TODO: give correct values for the synthesized branches. - let synthetic = |mode_bits: u32, blksize: usize| FileStat { - st_dev: 0, - st_ino: 0, - st_nlink: 1, - st_mode: mode_bits.trunc(), - st_uid: 0, - st_gid: 0, - st_rdev: 0, - st_size: 0, - st_blksize: blksize, - st_blocks: 0, - ..Default::default() - }; - let socket_mode = litebox_common_linux::InodeType::Socket as u32 - | (Mode::RWXU | Mode::RWXG | Mode::RWXO).bits(); - let rw_user_mode = (Mode::RUSR | Mode::WUSR).bits(); - let files = task.files.borrow(); - files - .run_on_raw_fd( - raw_fd, +pub(crate) fn get_file_descriptor_flags( + fd: &AnyTypedFd, + global: &GlobalState, +) -> FileDescriptorFlags { + // Currently, only one such flag is defined: FD_CLOEXEC, the close-on-exec flag. + // See https://www.man7.org/linux/man-pages/man2/F_GETFD.2const.html + on_any_fd!(fd, |fd| global + .litebox + .descriptor_table() + .with_metadata(fd, |flags: &FileDescriptorFlags| *flags) + .unwrap_or(FileDescriptorFlags::empty())) +} + +fn set_file_descriptor_flags( + fd: &AnyTypedFd, + global: &GlobalState, + flags: FileDescriptorFlags, +) { + on_any_fd!(fd, |fd| { + let _old = global + .litebox + .descriptor_table_mut() + .set_fd_metadata(fd, flags); + }); +} + +impl Task { + pub(crate) fn do_stat(&self, fd: &AnyTypedFd) -> Result + where + T: From + From, + { + // TODO: give correct values for the synthesized branches. + let synthetic = |mode_bits: u32, blksize: usize| FileStat { + st_dev: 0, + st_ino: 0, + st_nlink: 1, + st_mode: mode_bits.trunc(), + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_size: 0, + st_blksize: blksize, + st_blocks: 0, + ..Default::default() + }; + let socket_mode = litebox_common_linux::InodeType::Socket as u32 + | (Mode::RWXU | Mode::RWXG | Mode::RWXO).bits(); + let rw_user_mode = (Mode::RUSR | Mode::WUSR).bits(); + let files = self.files.borrow(); + fd.dispatch( |fd| { files .fs @@ -1342,7 +1402,7 @@ where |_fd| Ok(T::from(synthetic(socket_mode, 4096))), |fd| { Ok(T::from(synthetic( - task.global.linux_pipe_mode_bits(fd)?, + self.global.linux_pipe_mode_bits(fd)?, 4096, ))) }, @@ -1350,71 +1410,12 @@ where |_fd| Ok(T::from(synthetic(rw_user_mode, 0))), |_fd| Ok(T::from(synthetic(socket_mode, 4096))), ) - .flatten() -} - -pub(crate) fn get_file_descriptor_flags( - raw_fd: usize, - global: &GlobalState, - files: &FilesState, -) -> Result { - // Currently, only one such flag is defined: FD_CLOEXEC, the close-on-exec flag. - // See https://www.man7.org/linux/man-pages/man2/F_GETFD.2const.html - fn get_flags( - global: &GlobalState, - fd: &TypedFd, - ) -> FileDescriptorFlags { - global - .litebox - .descriptor_table() - .with_metadata(fd, |flags: &FileDescriptorFlags| *flags) - .unwrap_or(FileDescriptorFlags::empty()) - } - files.run_on_raw_fd( - raw_fd, - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - ) -} - -fn set_file_descriptor_flags( - raw_fd: usize, - global: &GlobalState, - files: &FilesState, - flags: FileDescriptorFlags, -) -> Result<(), Errno> { - fn set_flags( - global: &GlobalState, - fd: &TypedFd, - flags: FileDescriptorFlags, - ) { - let _old = global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, flags); } - files.run_on_raw_fd( - raw_fd, - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - )?; - Ok(()) -} - -impl Task { /// Get the file status of `pathname`. /// /// The `pathname` must be absolute. - fn do_stat>( + fn do_path_stat>( &self, pathname: impl path::Arg, follow_symlink: bool, @@ -1438,7 +1439,7 @@ impl Task { /// Handle syscall `stat` pub fn sys_stat(&self, pathname: impl path::Arg) -> Result { let pathname = self.resolve_path(pathname)?; - self.do_stat(pathname, true) + self.do_path_stat(pathname, true) } /// Handle syscall `lstat` @@ -1448,15 +1449,12 @@ impl Task { /// TODO: we do not support symbolic links yet. pub fn sys_lstat(&self, pathname: impl path::Arg) -> Result { let pathname = self.resolve_path(pathname)?; - self.do_stat(pathname, false) + self.do_path_stat(pathname, false) } /// Handle syscall `fstat` pub fn sys_fstat(&self, fd: i32) -> Result { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; - descriptor_stat(raw_fd, self) + self.with_typed_fd(fd, |fd| self.do_stat(fd)) } fn do_fstatat( @@ -1472,7 +1470,7 @@ impl Task { let fs_path = FsPath::new(dirfd, pathname, get_cwd)?; match fs_path { FsPath::Absolute { path } => { - self.do_stat(path, !flags.contains(AtFlags::AT_SYMLINK_NOFOLLOW)) + self.do_path_stat(path, !flags.contains(AtFlags::AT_SYMLINK_NOFOLLOW)) } FsPath::Cwd if flags.contains(AtFlags::AT_EMPTY_PATH) => { // Take the cwd before locking the context: this lock is not recursive, so a @@ -1484,7 +1482,7 @@ impl Task { Ok(T::from(files.fs.file_status(&context, cwd)?)) } FsPath::Fd(fd) if flags.contains(AtFlags::AT_EMPTY_PATH) => { - descriptor_stat(fd as usize, self) + self.with_typed_fd(fd, |fd| self.do_stat(fd)) } FsPath::Cwd | FsPath::Fd(_) => Err(Errno::ENOENT), FsPath::FdRelative { .. } => { @@ -1545,15 +1543,13 @@ impl Task { } pub(crate) fn sys_fcntl(&self, fd: i32, arg: FcntlArg) -> Result { - let Ok(desc) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; - let files = self.files.borrow(); + let fd = files.typed_fd(fd)?; match arg { - FcntlArg::GETFD => Ok(get_file_descriptor_flags(desc, &self.global, &files)?.bits()), + FcntlArg::GETFD => Ok(get_file_descriptor_flags(&fd, &self.global).bits()), FcntlArg::SETFD(flags) => { - set_file_descriptor_flags(desc, &self.global, &files, flags).map(|()| 0) + set_file_descriptor_flags(&fd, &self.global, flags); + Ok(0) } FcntlArg::GETFL => { macro_rules! getfl_from_metadata { @@ -1580,17 +1576,15 @@ impl Task { handle.with_entry(|file| Ok(file.get_status())) }}; } - Ok(files - .run_on_raw_fd( - desc, + Ok(fd + .dispatch( |fd| getfl_from_metadata!(fd, crate::StdioStatusFlags), |fd| getfl_from_metadata!(fd, crate::syscalls::net::SocketOFlags), |fd| self.global.linux_pipe_status_flags(fd), |fd| getfl_from_handle!(fd), |fd| getfl_from_handle!(fd), |fd| getfl_from_handle!(fd), - ) - .flatten()? + )? .bits()) } FcntlArg::SETFL(flags) => { @@ -1642,8 +1636,7 @@ impl Task { }) }; } - files.run_on_raw_fd( - desc, + fd.dispatch( |fd| { setfl_in_metadata!( fd, @@ -1671,64 +1664,55 @@ impl Task { toggle_flags!(fd); Ok(()) }, - )??; + )?; Ok(0) } FcntlArg::GETLK(lock) => { - self.files - .borrow() - .run_on_raw_fd( - desc, - |_fd| { - let mut flock = - lock.read_at_offset::(0).ok_or(Errno::EFAULT)?; - let lock_type = litebox_common_linux::FlockType::try_from(flock.type_) - .map_err(|_| Errno::EINVAL)?; - if let litebox_common_linux::FlockType::Unlock = lock_type { - return Err(Errno::EINVAL); - } + fd.dispatch( + |_fd| { + let mut flock = lock.read_at_offset::(0).ok_or(Errno::EFAULT)?; + let lock_type = litebox_common_linux::FlockType::try_from(flock.type_) + .map_err(|_| Errno::EINVAL)?; + if let litebox_common_linux::FlockType::Unlock = lock_type { + return Err(Errno::EINVAL); + } - // Note LiteBox does not support multiple processes yet, and one process - // can always acquire the lock it owns, so return `Unlock` unconditionally. - flock.type_ = litebox_common_linux::FlockType::Unlock as i16; - lock.write_at_offset::(0, flock) - .ok_or(Errno::EFAULT)?; - Ok(0) - }, - |_fd| todo!("net"), - |_fd| todo!("pipes"), - |_fd| Err(Errno::EBADF), - |_fd| Err(Errno::EBADF), - |_fd| Err(Errno::EBADF), - ) - .flatten() + // Note LiteBox does not support multiple processes yet, and one process + // can always acquire the lock it owns, so return `Unlock` unconditionally. + flock.type_ = litebox_common_linux::FlockType::Unlock as i16; + lock.write_at_offset::(0, flock) + .ok_or(Errno::EFAULT)?; + Ok(0) + }, + |_fd| todo!("net"), + |_fd| todo!("pipes"), + |_fd| Err(Errno::EBADF), + |_fd| Err(Errno::EBADF), + |_fd| Err(Errno::EBADF), + ) } FcntlArg::SETLK(lock) | FcntlArg::SETLKW(lock) => { - self.files - .borrow() - .run_on_raw_fd( - desc, - |_fd| { - let flock = lock.read_at_offset::(0).ok_or(Errno::EFAULT)?; - let _ = litebox_common_linux::FlockType::try_from(flock.type_) - .map_err(|_| Errno::EINVAL)?; - - // Note LiteBox does not support multiple processes yet, and one process - // can always acquire the lock it owns, so we don't need to maintain anything. - Ok(0) - }, - |_fd| todo!("net"), - |_fd| todo!("pipes"), - |_fd| Err(Errno::EBADF), - |_fd| Err(Errno::EBADF), - |_fd| Err(Errno::EBADF), - ) - .flatten() + fd.dispatch( + |_fd| { + let flock = lock.read_at_offset::(0).ok_or(Errno::EFAULT)?; + let _ = litebox_common_linux::FlockType::try_from(flock.type_) + .map_err(|_| Errno::EINVAL)?; + + // Note LiteBox does not support multiple processes yet, and one process + // can always acquire the lock it owns, so we don't need to maintain anything. + Ok(0) + }, + |_fd| todo!("net"), + |_fd| todo!("pipes"), + |_fd| Err(Errno::EBADF), + |_fd| Err(Errno::EBADF), + |_fd| Err(Errno::EBADF), + ) } FcntlArg::DUPFD { cloexec, min_fd } => { let new_file = self .do_dup_inner( - desc, + &fd, if cloexec { OFlags::CLOEXEC } else { @@ -1915,163 +1899,112 @@ impl Task { /// Handle syscall `ioctl` pub fn sys_ioctl(&self, fd: i32, arg: IoctlArg) -> Result { - let Ok(desc) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; - let files = self.files.borrow(); match arg { IoctlArg::FIONBIO(arg) => { let val = arg.read_at_offset::(0).ok_or(Errno::EFAULT)?; - self.files - .borrow() - .run_on_raw_fd( - desc, - |_file_fd| { - // TODO: stdio NONBLOCK? - #[cfg(debug_assertions)] - litebox_util_log::debug!("set non-blocking on raw fd unimplemented"); - Ok(()) - }, - |socket_fd| { - if let Err(e) = self - .global - .litebox - .descriptor_table_mut() - .with_metadata_mut( - socket_fd, - |crate::syscalls::net::SocketOFlags(flags)| { - flags.set(OFlags::NONBLOCK, val != 0); - }, - ) - { - match e { - MetadataError::ClosedFd => return Err(Errno::EBADF), - MetadataError::NoSuchMetadata => unreachable!(), - } + let fd = files.typed_fd(fd)?; + macro_rules! set_nonblock_on_entry { + ($fd:ident) => {{ + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle($fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + file.set_status(OFlags::NONBLOCK, val != 0); + }); + Ok(()) + }}; + } + fd.dispatch( + |_file_fd| { + // TODO: stdio NONBLOCK? + #[cfg(debug_assertions)] + litebox_util_log::debug!("set non-blocking on raw fd unimplemented"); + Ok(()) + }, + |socket_fd| { + if let Err(e) = self + .global + .litebox + .descriptor_table_mut() + .with_metadata_mut( + socket_fd, + |crate::syscalls::net::SocketOFlags(flags)| { + flags.set(OFlags::NONBLOCK, val != 0); + }, + ) + { + match e { + MetadataError::ClosedFd => return Err(Errno::EBADF), + MetadataError::NoSuchMetadata => unreachable!(), } - Ok(()) - }, - |fd| { - self.global - .pipes - .update_flags(fd, litebox::pipes::Flags::NON_BLOCKING, val != 0) - .map_err(Errno::from) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - handle.with_entry(|file| { - file.set_status(OFlags::NONBLOCK, val != 0); - }); - Ok(()) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - handle.with_entry(|file| { - file.set_status(OFlags::NONBLOCK, val != 0); - }); - Ok(()) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - handle.with_entry(|file| { - file.set_status(OFlags::NONBLOCK, val != 0); - }); - Ok(()) - }, - ) - .flatten()?; + } + Ok(()) + }, + |fd| { + self.global + .pipes + .update_flags(fd, litebox::pipes::Flags::NON_BLOCKING, val != 0) + .map_err(Errno::from) + }, + |fd| set_nonblock_on_entry!(fd), + |fd| set_nonblock_on_entry!(fd), + |fd| set_nonblock_on_entry!(fd), + )?; Ok(0) } - IoctlArg::FIOCLEX => files.run_on_raw_fd( - desc, - |fd| { - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, FileDescriptorFlags::FD_CLOEXEC); - Ok(0) - }, - |_fd| todo!("net"), - |_fd| todo!("pipes"), - |fd| { - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, FileDescriptorFlags::FD_CLOEXEC); - Ok(0) - }, - |fd| { - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, FileDescriptorFlags::FD_CLOEXEC); - Ok(0) - }, - |fd| { - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, FileDescriptorFlags::FD_CLOEXEC); - Ok(0) - }, - )?, + IoctlArg::FIOCLEX => { + let fd = files.typed_fd(fd)?; + macro_rules! set_cloexec { + ($fd:ident) => {{ + let _old = self + .global + .litebox + .descriptor_table_mut() + .set_fd_metadata($fd, FileDescriptorFlags::FD_CLOEXEC); + Ok(0) + }}; + } + fd.dispatch( + |fd| set_cloexec!(fd), + |_fd| todo!("net"), + |_fd| todo!("pipes"), + |fd| set_cloexec!(fd), + |fd| set_cloexec!(fd), + |fd| set_cloexec!(fd), + ) + } IoctlArg::TCGETS(..) | IoctlArg::TCSETS(..) | IoctlArg::TIOCGPTN(..) - | IoctlArg::TIOCGWINSZ(..) => files.run_on_raw_fd( - desc, - |fd| { - if self.is_stdio(&files.fs, fd)? { - let stream = self - .global - .litebox - .descriptor_table() - .with_metadata(fd, |stream: &StdioStream| *stream) - .map_err(|_| { - // TODO: Handle missing `StdioStream` metadata (could happen if - // `/dev/stdin`, `/dev/stdout`, or `/dev/stderr` was reopened). - // XXX(jayb): likely we might want to have some backend-specific - // metadata layer in our file system? - litebox_util_log::error!( - "standard stream is missing StdioStream metadata" - ); - Errno::ENOTTY - })?; - if self.global.platform.is_a_tty(stream) { - self.stdio_ioctl(&arg) - } else { - Err(Errno::ENOTTY) - } - } else { - Err(Errno::ENOTTY) - } - }, - |_fd| Err(Errno::ENOTTY), - |_fd| Err(Errno::ENOTTY), - |_fd| Err(Errno::ENOTTY), - |_fd| Err(Errno::ENOTTY), - |_fd| Err(Errno::ENOTTY), - )?, + | IoctlArg::TIOCGWINSZ(..) => { + let fd = files.typed_fd(fd)?; + let fd = fd.fs_only(Errno::ENOTTY)?; + if !self.is_stdio(&files.fs, fd)? { + return Err(Errno::ENOTTY); + } + let stream = self + .global + .litebox + .descriptor_table() + .with_metadata(fd, |stream: &StdioStream| *stream) + .map_err(|_| { + // TODO: Handle missing `StdioStream` metadata (could happen if + // `/dev/stdin`, `/dev/stdout`, or `/dev/stderr` was reopened). + // XXX(jayb): likely we might want to have some backend-specific + // metadata layer in our file system? + litebox_util_log::error!("standard stream is missing StdioStream metadata"); + Errno::ENOTTY + })?; + if self.global.platform.is_a_tty(stream) { + self.stdio_ioctl(&arg) + } else { + Err(Errno::ENOTTY) + } + } _ => { log_unsupported!("ioctl with arg {:?}", arg); Err(Errno::EINVAL) @@ -2435,13 +2368,13 @@ impl Task { Ok(count) } - fn do_dup(&self, file: usize, flags: OFlags) -> Result { + fn do_dup(&self, file: &AnyTypedFd, flags: OFlags) -> Result { self.do_dup_inner(file, flags, DupFdRequest::LowestAvailable) } fn do_dup_inner( &self, - file: usize, + file: &AnyTypedFd, flags: OFlags, target: DupFdRequest, ) -> Result { @@ -2503,43 +2436,40 @@ impl Task { let close_on_exec = flags.contains(OFlags::CLOEXEC); let files = self.files.borrow(); - files - .run_on_raw_fd( - file, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - let _ = files.fs.close(&fd); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - let _ = self - .global - .close_socket(&self.wait_cx(), alloc::sync::Arc::new(fd)); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - let _ = self.global.close_linux_pipe(&fd); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - self.remove_and_drop_descriptor(&fd); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - self.remove_and_drop_descriptor(&fd); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - self.remove_and_drop_descriptor(&fd); - }) - }, - ) - .map_err(|_| DupFdError::BadFd)? + file.dispatch( + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + let _ = files.fs.close(&fd); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + let _ = self + .global + .close_socket(&self.wait_cx(), alloc::sync::Arc::new(fd)); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + let _ = self.global.close_linux_pipe(&fd); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + self.remove_and_drop_descriptor(&fd); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + self.remove_and_drop_descriptor(&fd); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + self.remove_and_drop_descriptor(&fd); + }) + }, + ) } /// Handle syscall `dup/dup2/dup3` @@ -2553,9 +2483,8 @@ impl Task { newfd: Option, flags: Option, ) -> Result { - self.check_raw_fd_exists(oldfd)?; + let typed_oldfd = self.typed_fd(oldfd)?; let oldfd = u32::try_from(oldfd).map_err(|_| Errno::EBADF)?; - let oldfd_usize = usize::try_from(oldfd).or(Err(Errno::EBADF))?; if let Some(newfd) = newfd { // dup2/dup3 let Ok(newfd) = u32::try_from(newfd) else { @@ -2574,13 +2503,13 @@ impl Task { } let newfd_usize = usize::try_from(newfd).or(Err(Errno::EBADF))?; self.do_dup_inner( - oldfd_usize, + &typed_oldfd, flags.unwrap_or(OFlags::empty()), DupFdRequest::Exact(newfd_usize), ) } else { // dup - self.do_dup(oldfd_usize, flags.unwrap_or(OFlags::empty())) + self.do_dup(&typed_oldfd, flags.unwrap_or(OFlags::empty())) } .map_err(|e| match e { DupFdError::BadFd | DupFdError::TargetFdExceedsLimit => Errno::EBADF, @@ -2622,78 +2551,67 @@ impl Task { dirp: UserPtrMut, count: usize, ) -> Result { - let Ok(fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; let files = self.files.borrow(); - files.run_on_raw_fd( - fd, - |file| { - let dir_off: Diroff = self - .global - .litebox - .descriptor_table() - .with_metadata(file, |off: &Diroff| *off) - .unwrap_or_default(); - let mut dir_off = dir_off.0; - let mut nbytes = 0; - - let mut entries = files.fs.read_dir(file)?; - entries.sort_by(|a, b| a.name.cmp(&b.name)); - - for entry in entries.iter().skip(dir_off) { - // include null terminator and make it aligned - let len = (DIRENT_STRUCT_BYTES_WITHOUT_NAME + entry.name.len() + 1) - .next_multiple_of(align_of::()); - if nbytes + len > count { - // not enough space - if nbytes == 0 { - // not enough space for even a single entry - return Err(Errno::EINVAL); - } - break; - } - let dirent64 = litebox_common_linux::LinuxDirent64 { - ino: entry.ino_info.as_ref().map_or(0, |node_info| node_info.ino) as u64, - off: dir_off as u64, - len: len.trunc(), - typ: litebox_common_linux::DirentType::from(entry.file_type.clone()) as u8, - __name: [0; 0], - }; - let hdr_ptr = UserPtrMut::from_usize(dirp.as_usize() + nbytes); - hdr_ptr - .write_at_offset::(0, dirent64) - .ok_or(Errno::EFAULT)?; - let name_ptr = UserPtrMut::from_usize( - hdr_ptr.as_usize() + DIRENT_STRUCT_BYTES_WITHOUT_NAME, - ); - name_ptr - .write_slice_at_offset::(0, entry.name.as_bytes()) - .ok_or(Errno::EFAULT)?; - // set the null terminator and padding - let zeros_len = len - (DIRENT_STRUCT_BYTES_WITHOUT_NAME + entry.name.len()); - name_ptr - .write_slice_at_offset::( - isize::try_from(entry.name.len()).unwrap(), - &vec![0; zeros_len], - ) - .ok_or(Errno::EFAULT)?; - nbytes += len; - dir_off += 1; + let fd = files.typed_fd(fd)?; + let file = fd.fs_only(Errno::ENOTDIR)?; + + let dir_off: Diroff = self + .global + .litebox + .descriptor_table() + .with_metadata(file, |off: &Diroff| *off) + .unwrap_or_default(); + let mut dir_off = dir_off.0; + let mut nbytes = 0; + + let mut entries = files.fs.read_dir(file)?; + entries.sort_by(|a, b| a.name.cmp(&b.name)); + + for entry in entries.iter().skip(dir_off) { + // include null terminator and make it aligned + let len = (DIRENT_STRUCT_BYTES_WITHOUT_NAME + entry.name.len() + 1) + .next_multiple_of(align_of::()); + if nbytes + len > count { + // not enough space + if nbytes == 0 { + // not enough space for even a single entry + return Err(Errno::EINVAL); } - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_entry_metadata(file, Diroff(dir_off)); - Ok(nbytes) - }, - |_fd| Err(Errno::ENOTDIR), - |_fd| Err(Errno::ENOTDIR), - |_fd| Err(Errno::ENOTDIR), - |_fd| Err(Errno::ENOTDIR), - |_fd| Err(Errno::ENOTDIR), - )? + break; + } + let dirent64 = litebox_common_linux::LinuxDirent64 { + ino: entry.ino_info.as_ref().map_or(0, |node_info| node_info.ino) as u64, + off: dir_off as u64, + len: len.trunc(), + typ: litebox_common_linux::DirentType::from(entry.file_type.clone()) as u8, + __name: [0; 0], + }; + let hdr_ptr = UserPtrMut::from_usize(dirp.as_usize() + nbytes); + hdr_ptr + .write_at_offset::(0, dirent64) + .ok_or(Errno::EFAULT)?; + let name_ptr = + UserPtrMut::from_usize(hdr_ptr.as_usize() + DIRENT_STRUCT_BYTES_WITHOUT_NAME); + name_ptr + .write_slice_at_offset::(0, entry.name.as_bytes()) + .ok_or(Errno::EFAULT)?; + // set the null terminator and padding + let zeros_len = len - (DIRENT_STRUCT_BYTES_WITHOUT_NAME + entry.name.len()); + name_ptr + .write_slice_at_offset::( + isize::try_from(entry.name.len()).unwrap(), + &vec![0; zeros_len], + ) + .ok_or(Errno::EFAULT)?; + nbytes += len; + dir_off += 1; + } + let _old = self + .global + .litebox + .descriptor_table_mut() + .set_entry_metadata(file, Diroff(dir_off)); + Ok(nbytes) } } @@ -2702,10 +2620,84 @@ mod tests { use super::*; use alloc::string::String; use core::cell::Cell; - use litebox::fs::Mode; + use litebox::fs::{Mode, OFlags}; extern crate std; + #[test] + fn readv_does_not_follow_raw_fd_reuse() { + let task = crate::syscalls::tests::init_platform(None); + let replacement_path = "/readv_fd_reuse"; + let replacement = task + .sys_open(replacement_path, OFlags::CREAT | OFlags::WRONLY, Mode::RWXU) + .unwrap(); + task.sys_write(i32::try_from(replacement).unwrap(), b"BBBB", None) + .unwrap(); + task.sys_close(i32::try_from(replacement).unwrap()).unwrap(); + + let (read_fd, write_fd) = task.sys_pipe2(OFlags::empty()).unwrap(); + task.sys_write(i32::try_from(write_fd).unwrap(), b"AAAACCCC", None) + .unwrap(); + + let mut first = [0u8; 4]; + let mut second = [0u8; 4]; + let iovs = [ + IoReadVec { + iov_base: UserPtrMut::from_usize(first.as_mut_ptr().expose_provenance()), + iov_len: first.len(), + }, + IoReadVec { + iov_base: UserPtrMut::from_usize(second.as_mut_ptr().expose_provenance()), + iov_len: second.len(), + }, + ]; + let mut kernel_buffer = [0u8; 4]; + let call = Cell::new(0); + let read_fd = i32::try_from(read_fd).unwrap(); + + let result = task.with_typed_fd(read_fd, |fd| { + read_from_iovec::<_, crate::syscalls::tests::TestPlatform>( + &iovs, + &mut kernel_buffer, + |buf, _total| { + let size = task.do_read(fd, buf, None)?; + if call.replace(call.get() + 1) == 0 { + task.sys_close(read_fd).unwrap(); + let reopened = task + .sys_open(replacement_path, OFlags::RDONLY, Mode::empty()) + .unwrap(); + assert_eq!(i32::try_from(reopened).unwrap(), read_fd); + } + Ok(size) + }, + ) + }); + + assert_eq!(result, Ok(4)); + assert_eq!(&first, b"AAAA"); + assert_eq!(&second, &[0; 4]); + } + + #[test] + fn dup_does_not_follow_raw_fd_reuse() { + let task = crate::syscalls::tests::init_platform(None); + let raw_fd = task + .sys_open("/dev/stdin", OFlags::RDONLY, Mode::empty()) + .unwrap(); + let typed_fd = task.typed_fd(raw_fd.try_into().unwrap()).unwrap(); + + task.sys_close(raw_fd.try_into().unwrap()).unwrap(); + let replacement = task + .sys_open("/dev/stdin", OFlags::RDONLY, Mode::empty()) + .unwrap(); + assert_eq!(replacement, raw_fd); + + assert!(matches!( + task.do_dup(&typed_fd, OFlags::empty()), + Err(DupFdError::BadFd) + )); + } + #[test] fn write_to_iovec_returns_partial_after_later_error() { let first = b"first"; diff --git a/litebox_shim_linux/src/syscalls/mm.rs b/litebox_shim_linux/src/syscalls/mm.rs index 79c255c60..7f2696c76 100644 --- a/litebox_shim_linux/src/syscalls/mm.rs +++ b/litebox_shim_linux/src/syscalls/mm.rs @@ -17,6 +17,7 @@ use litebox_common_linux::{MRemapFlags, MapFlags, ProtFlags, errno::Errno}; use crate::ShimPlatform; use crate::Task; use crate::UserPtrMut; +use crate::syscalls::file::AnyTypedFd; use litebox::utils::TruncateExt as _; use object::elf::{ET_DYN, FileHeader64, PT_LOAD, ProgramHeader64}; use object::endian::LittleEndian; @@ -117,14 +118,15 @@ impl Task { offset: usize, ) -> Result, MappingError> { let is_exec = prot.contains(ProtFlags::PROT_EXEC); + let typed_fd = self.typed_fd(fd).map_err(|_| MappingError::BadFD(fd))?; // Perform the normal mmap first (CoW or memcpy fallback). let result = if let Some(cow_result) = - self.try_cow_mmap_file(suggested_addr, len, &prot, &flags, fd, offset) + self.try_cow_mmap_file(suggested_addr, len, &prot, &flags, &typed_fd, offset) { cow_result? } else { - self.do_mmap_file_memcpy(suggested_addr, len, prot, flags, fd, offset)? + self.do_mmap_file_memcpy(suggested_addr, len, prot, flags, &typed_fd, offset)? }; // Runtime syscall rewriting: patch PROT_EXEC segments in-place. @@ -170,31 +172,15 @@ impl Task { len: usize, prot: &ProtFlags, flags: &MapFlags, - fd: i32, + fd: &AnyTypedFd, offset: usize, ) -> Option, MappingError>> { if !len.is_multiple_of(PAGE_SIZE) { return None; } - let Ok(fd) = u32::try_from(fd).and_then(usize::try_from) else { - return None; - }; - let files = self.files.borrow(); - let raw_fd = fd; - - let static_data = files - .run_on_raw_fd( - raw_fd, - |typed_fd| files.fs.get_static_backing_data(typed_fd), - |_| None, - |_| None, - |_| None, - |_| None, - |_| None, - ) - .ok()??; + let static_data = files.fs.get_static_backing_data(fd.as_fs()?)?; if offset > static_data.len() { return None; @@ -273,7 +259,7 @@ impl Task { len: usize, prot: ProtFlags, flags: MapFlags, - fd: i32, + fd: &AnyTypedFd, offset: usize, ) -> Result, MappingError> { let op = |ptr: UserPtrMut| -> Result { @@ -286,9 +272,11 @@ impl Task { let mut copied = 0; while copied < len { let size = - self.sys_read(fd, &mut buffer, Some(file_offset)) + self.do_read(fd, &mut buffer, Some(file_offset)) .map_err(|e| match e { - Errno::EBADF => MappingError::BadFD(fd), + // The raw fd was resolved once at syscall entry and is intentionally + // not retained; this payload is discarded when converted to EBADF. + Errno::EBADF => MappingError::BadFD(-1), Errno::EISDIR => MappingError::NotAFile, Errno::EACCES => MappingError::NotForReading, _ => unimplemented!(), diff --git a/litebox_shim_linux/src/syscalls/net.rs b/litebox_shim_linux/src/syscalls/net.rs index 7a0de63bc..10ece50d3 100644 --- a/litebox_shim_linux/src/syscalls/net.rs +++ b/litebox_shim_linux/src/syscalls/net.rs @@ -33,7 +33,10 @@ use litebox_common_linux::{ }; use zerocopy::{FromBytes, Immutable, IntoBytes}; -use crate::syscalls::unix::{CSockUnixAddr, UnixSocket, UnixSocketAddr}; +use crate::syscalls::{ + file::AnyTypedFd, + unix::{CSockUnixAddr, UnixSocket, UnixSocketAddr}, +}; use crate::{GlobalState, ShimPlatform, Task}; use crate::{UserPtr, UserPtrMut, syscalls::signal}; @@ -58,45 +61,33 @@ macro_rules! convert_flags { pub(crate) type SocketFd = litebox::net::SocketFd; impl super::file::FilesState { - /// Helper to dispatch socket operations based on socket type (INET vs Unix). - /// - /// This method handles the common pattern of: - /// 1. Looking up the file descriptor - /// 2. Matching on descriptor type - /// 3. Dropping the file table lock before potentially-blocking operations - /// 4. Dispatching to the appropriate handler - /// - /// For `LiteBoxRawFd` sockets, the `inet_op` closure is called with the socket fd. - /// For Unix sockets, the `unix_op` closure is called with a cloned Arc to the socket. - fn with_socket( + fn socket_from_raw(&self, sockfd: i32) -> Result, Errno> { + let fd = self.typed_fd(sockfd)?; + match fd { + AnyTypedFd::Network(_) | AnyTypedFd::Unix(_) => Ok(fd), + _ => Err(Errno::ENOTSOCK), + } + } + + fn with_typed_socket( &self, global: &GlobalState, - sockfd: u32, + socket: &AnyTypedFd, inet_op: impl FnOnce(&SocketFd) -> Result, unix_op: impl FnOnce(&UnixSocket) -> Result, ) -> Result { - let raw_fd = sockfd as usize; - let inet_fd = { - let rds = self.raw_descriptor_store.read(); - rds.fd_from_raw_integer(raw_fd).ok() - }; - if let Some(fd) = inet_fd { - return inet_op(&fd); + match socket { + AnyTypedFd::Network(fd) => inet_op(fd), + AnyTypedFd::Unix(fd) => { + let handle = global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(unix_op) + } + _ => Err(Errno::ENOTSOCK), } - let unix = self - .raw_descriptor_store - .read() - .fd_from_raw_integer::>(raw_fd) - .map_err(|err| match err { - litebox::fd::ErrRawIntFd::NotFound => Errno::EBADF, - litebox::fd::ErrRawIntFd::InvalidSubsystem => Errno::ENOTSOCK, - })?; - let handle = global - .litebox - .descriptor_table() - .entry_handle(&unix) - .ok_or(Errno::EBADF)?; - handle.with_entry(|entry| unix_op(entry)) } } @@ -1260,11 +1251,9 @@ impl Task { addrlen: Option>, flags: SockFlags, ) -> Result { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(sockfd)?; let mut remote_addr = addr.is_some().then(SocketAddress::default); - let fd = self.do_accept(sockfd, remote_addr.as_mut(), flags)?; + let fd = self.do_accept(&socket, remote_addr.as_mut(), flags)?; if let (Some(addr), Some(remote_addr)) = (addr, remote_addr) { let addrlen = addrlen.ok_or(Errno::EFAULT)?; if let Err(err) = write_sockaddr_to_user::(remote_addr, addr, addrlen) { @@ -1278,15 +1267,15 @@ impl Task { } fn do_accept( &self, - sockfd: u32, + socket: &AnyTypedFd, peer: Option<&mut SocketAddress>, flags: SockFlags, ) -> Result { let files = self.files.borrow(); let want_peer = peer.is_some(); - let (file, peer_addr) = files.with_socket( + let (file, peer_addr) = files.with_typed_socket( &self.global, - sockfd, + socket, |fd| { let sock_type = self.global.get_socket_type(fd)?; let mut socket_addr = @@ -1339,16 +1328,18 @@ impl Task { sockaddr: UserPtr, addrlen: usize, ) -> Result<(), Errno> { - let Ok(fd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(fd)?; let sockaddr = read_sockaddr_from_user::(sockaddr, addrlen)?; - self.do_connect(fd, sockaddr) + self.do_connect(&socket, sockaddr) } - fn do_connect(&self, sockfd: u32, sockaddr: SocketAddress) -> Result<(), Errno> { - self.files.borrow().with_socket( + fn do_connect( + &self, + socket: &AnyTypedFd, + sockaddr: SocketAddress, + ) -> Result<(), Errno> { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { let addr = sockaddr.clone().inet().ok_or(Errno::EAFNOSUPPORT)?; self.global.connect(&self.wait_cx(), fd, addr) @@ -1367,16 +1358,14 @@ impl Task { sockaddr: UserPtr, addrlen: usize, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(sockfd)?; let sockaddr = read_sockaddr_from_user::(sockaddr, addrlen)?; - self.do_bind(sockfd, sockaddr) + self.do_bind(&socket, sockaddr) } - fn do_bind(&self, sockfd: u32, sockaddr: SocketAddress) -> Result<(), Errno> { - self.files.borrow().with_socket( + fn do_bind(&self, socket: &AnyTypedFd, sockaddr: SocketAddress) -> Result<(), Errno> { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { let addr = sockaddr.clone().inet().ok_or(Errno::EAFNOSUPPORT)?; self.global.bind(fd, addr) @@ -1390,15 +1379,13 @@ impl Task { /// Handle syscall `listen` pub(crate) fn sys_listen(&self, sockfd: i32, backlog: u16) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; - self.do_listen(sockfd, backlog) + let socket = self.files.borrow().socket_from_raw(sockfd)?; + self.do_listen(&socket, backlog) } - fn do_listen(&self, sockfd: u32, backlog: u16) -> Result<(), Errno> { - self.files.borrow().with_socket( + fn do_listen(&self, socket: &AnyTypedFd, backlog: u16) -> Result<(), Errno> { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| self.global.listen(fd, backlog), |file| file.listen(backlog, &self.global), ) @@ -1414,25 +1401,23 @@ impl Task { addr: Option>, addrlen: u32, ) -> Result { - let Ok(fd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(fd)?; let sockaddr = addr .map(|addr| read_sockaddr_from_user::(addr, addrlen as usize)) .transpose()?; let buf = buf.to_owned_slice::(len).ok_or(Errno::EFAULT)?; - self.do_sendto(fd, &buf, flags, sockaddr) + self.do_sendto(&socket, &buf, flags, sockaddr) } fn do_sendto( &self, - sockfd: u32, + socket: &AnyTypedFd, buf: &[u8], flags: SendFlags, sockaddr: Option, ) -> Result { - let res = self.files.borrow().with_socket( + let res = self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { let sockaddr = sockaddr .clone() @@ -1464,15 +1449,14 @@ impl Task { msg: UserPtr, flags: SendFlags, ) -> Result { - let Ok(fd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(fd)?; let msg = msg.read_at_offset::(0).ok_or(Errno::EFAULT)?; - self.do_sendmsg(fd, &msg, flags) + self.do_sendmsg(&socket, &msg, flags) } + fn do_sendmsg( &self, - sockfd: u32, + socket: &AnyTypedFd, msg: &litebox_common_linux::UserMsgHdr, flags: SendFlags, ) -> Result { @@ -1501,9 +1485,9 @@ impl Task { .ok_or(Errno::EFAULT)?, ) }; - let res = self.files.borrow().with_socket( + let res = self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { let sock_addr = sock_addr .clone() @@ -1538,20 +1522,11 @@ impl Task { vlen: u32, flags: SendFlags, ) -> Result { - let Ok(sockfd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; - let vlen = (vlen as usize).min(UIO_MAXIOV); // Linux looks up the fd before touching vlen/msgvec, so a bogus fd // takes priority over a bogus msgvec pointer or vlen == 0. - self.files.borrow().with_socket( - &self.global, - sockfd, - |_| Ok::<(), Errno>(()), - |_| Ok::<(), Errno>(()), - )?; + let socket = self.files.borrow().socket_from_raw(fd)?; if vlen == 0 { return Ok(0); @@ -1567,7 +1542,7 @@ impl Task { return bail(Errno::EFAULT); }; let inner = mmh.msg_hdr; - let n = match self.do_sendmsg(sockfd, &inner, flags) { + let n = match self.do_sendmsg(&socket, &inner, flags) { Ok(n) => n, Err(e) => return bail(e), }; @@ -1595,14 +1570,12 @@ impl Task { addrlen: UserPtrMut, ) -> Result { const MAX_LEN: usize = 4096; - let Ok(sockfd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(fd)?; let mut source_addr = None; let mut buffer = [0u8; MAX_LEN]; let recv_buf = &mut buffer[..MAX_LEN.min(len)]; let size = self.do_recvfrom( - sockfd, + &socket, recv_buf, flags, if addr.is_some() { @@ -1636,21 +1609,20 @@ impl Task { /// this may be larger than the provided buffer length as the excessive data will be truncated. fn do_recvfrom( &self, - sockfd: u32, + socket: &AnyTypedFd, buf: &mut [u8], flags: ReceiveFlags, source_addr: Option<&mut Option>, ) -> Result { let want_source = source_addr.is_some(); let files = self.files.borrow(); - let raw_fd = usize::try_from(sockfd).or(Err(Errno::EBADF))?; let (size, addr) = { // We need to do this cell dance because otherwise Rust can't recognize that the two // closures are mutually exclusive. let buf: core::cell::RefCell<&mut [u8]> = core::cell::RefCell::new(buf); - files.with_socket( + files.with_typed_socket( &self.global, - raw_fd.trunc(), + socket, |fd| { let mut addr = None; let size = self.global.receive( @@ -1690,21 +1662,19 @@ impl Task { msg_ptr: UserPtrMut, flags: ReceiveFlags, ) -> Result { - let Ok(sockfd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; - let supported_flags = ReceiveFlags::DONTWAIT | ReceiveFlags::TRUNC; if flags.intersects(supported_flags.complement()) { log_unsupported!("Unsupported recvmsg flags: {:?}", flags); return Err(Errno::EINVAL); } - self.do_recvmsg(sockfd, msg_ptr, flags) + let socket = self.files.borrow().socket_from_raw(fd)?; + self.do_recvmsg(&socket, msg_ptr, flags) } + fn do_recvmsg( &self, - sockfd: u32, + socket: &AnyTypedFd, msg_ptr: UserPtrMut, flags: ReceiveFlags, ) -> Result { @@ -1744,7 +1714,7 @@ impl Task { buffer.resize(total_iov_capacity, 0); let recv_buf = &mut buffer[..]; let size = self.do_recvfrom( - sockfd, + socket, recv_buf, flags, if want_source { @@ -1838,20 +1808,21 @@ impl Task { // so a bad timeout takes precedence over EBADF. let timeout_duration = timeout.read::()?; - let Ok(sockfd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; - let vlen = vlen as usize; // Linux looks up the fd before touching vlen/msgvec, so a bogus fd // takes priority over a bogus msgvec pointer or vlen == 0. - let inet_proxy = self.files.borrow().with_socket( - &self.global, - sockfd, - |fd| self.global.get_proxy(fd).map(Some), - |_| Ok(None), - )?; + let (socket, inet_proxy) = { + let files = self.files.borrow(); + let socket = files.socket_from_raw(fd)?; + let inet_proxy = files.with_typed_socket( + &self.global, + &socket, + |fd| self.global.get_proxy(fd).map(Some), + |_| Ok(None), + )?; + (socket, inet_proxy) + }; if vlen == 0 { return Ok(0); @@ -1878,7 +1849,7 @@ impl Task { for i in 0..vlen { let base = msgvec_base + i * stride; let inner_ptr = UserPtrMut::::from_usize(base); - let n = match self.do_recvmsg(sockfd, inner_ptr, iter_flags) { + let n = match self.do_recvmsg(&socket, inner_ptr, iter_flags) { Ok(n) => n, Err(e) => { if received > 0 { @@ -1940,25 +1911,23 @@ impl Task { optval: UserPtr, optlen: usize, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(sockfd)?; let optname = SocketOptionName::try_from(level, optname).ok_or_else(|| { log_unsupported!("setsockopt(level = {level}, optname = {optname})"); Errno::EINVAL })?; - self.do_setsockopt(sockfd, optname, optval, optlen) + self.do_setsockopt(&socket, optname, optval, optlen) } fn do_setsockopt( &self, - sockfd: u32, + socket: &AnyTypedFd, optname: SocketOptionName, optval: UserPtr, optlen: usize, ) -> Result<(), Errno> { - self.files.borrow().with_socket( + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| self.global.setsockopt(fd, optname, optval, optlen), |file| file.setsockopt(&self.global, optname, optval, optlen), ) @@ -1973,9 +1942,7 @@ impl Task { optval: UserPtrMut, optlen: UserPtrMut, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(sockfd)?; let optname = SocketOptionName::try_from(level, optname).ok_or_else(|| { log_unsupported!("setsockopt(level = {level}, optname = {optname})"); Errno::EINVAL @@ -1984,7 +1951,7 @@ impl Task { if len > i32::MAX as u32 { return Err(Errno::EINVAL); } - let new_len = self.do_getsockopt(sockfd, optname, optval, len)?; + let new_len = self.do_getsockopt(&socket, optname, optval, len)?; optlen .write_at_offset::(0, new_len.trunc()) .ok_or(Errno::EFAULT)?; @@ -1995,14 +1962,14 @@ impl Task { /// Returns the length of the option value written to `optval` on success. fn do_getsockopt( &self, - sockfd: u32, + socket: &AnyTypedFd, optname: SocketOptionName, optval: UserPtrMut, len: u32, ) -> Result { - self.files.borrow().with_socket( + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| self.global.getsockopt(fd, optname, optval, len), |file| file.getsockopt(&self.global, optname, optval, len), ) @@ -2015,16 +1982,14 @@ impl Task { addr: UserPtrMut, addrlen: UserPtrMut, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; - let sockaddr = self.do_getsockname(sockfd)?; + let socket = self.files.borrow().socket_from_raw(sockfd)?; + let sockaddr = self.do_getsockname(&socket)?; write_sockaddr_to_user::(sockaddr, addr, addrlen) } - fn do_getsockname(&self, sockfd: u32) -> Result { - self.files.borrow().with_socket( + fn do_getsockname(&self, socket: &AnyTypedFd) -> Result { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { self.global .net @@ -2044,16 +2009,14 @@ impl Task { addr: UserPtrMut, addrlen: UserPtrMut, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; - let sockaddr = self.do_getpeername(sockfd)?; + let socket = self.files.borrow().socket_from_raw(sockfd)?; + let sockaddr = self.do_getpeername(&socket)?; write_sockaddr_to_user::(sockaddr, addr, addrlen) } - fn do_getpeername(&self, sockfd: u32) -> Result { - self.files.borrow().with_socket( + fn do_getpeername(&self, socket: &AnyTypedFd) -> Result { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { self.global .net @@ -2072,18 +2035,16 @@ impl Task { /// Handle syscall `shutdown` pub(crate) fn sys_shutdown(&self, sockfd: i32, how: i32) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; - self.do_shutdown(sockfd, how) + let socket = self.files.borrow().socket_from_raw(sockfd)?; + self.do_shutdown(&socket, how) } - fn do_shutdown(&self, sockfd: u32, how: i32) -> Result<(), Errno> { + fn do_shutdown(&self, socket: &AnyTypedFd, how: i32) -> Result<(), Errno> { // Linux validates the fd (EBADF, ENOTSOCK) before `how` (EINVAL), - // so resolve the socket through `with_socket` first and validate `how` + // so resolve the socket first and validate `how` // only inside the matching branch. - self.files.borrow().with_socket( + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |_fd| { ShutdownHow::try_from(how).map_err(|_| Errno::EINVAL)?; log_unsupported!("shutdown on inet socket"); @@ -2142,13 +2103,23 @@ mod tests { .expect("close socket failed"); } + fn typed_socket( + task: &TestTask, + fd: u32, + ) -> crate::syscalls::file::AnyTypedFd { + task.files + .borrow() + .socket_from_raw(i32::try_from(fd).unwrap()) + .unwrap() + } + /// Helper to read SO_ERROR from a socket via getsockopt. /// Returns the errno integer value (0 means no error). fn get_so_error(task: &TestTask, sockfd: u32) -> u32 { let mut optval: u32 = 0xDEAD; let len = task .do_getsockopt( - sockfd, + &typed_socket(task, sockfd), SocketOptionName::Socket(SocketOption::ERROR), UserPtrMut::from_usize((&raw mut optval).cast::() as usize), core::mem::size_of::().trunc(), @@ -2208,9 +2179,9 @@ mod tests { core::net::Ipv4Addr::from(ip), port, ))); - task.do_bind(server, server_sockaddr.clone()) + task.do_bind(&typed_socket(task, server), server_sockaddr.clone()) .expect("Failed to bind socket"); - task.do_listen(server, 1) + task.do_listen(&typed_socket(task, server), 1) .expect("Failed to listen on socket"); // Create an epoll instance and register the server fd for EPOLLIN @@ -2259,7 +2230,7 @@ mod tests { let mut remote_addr = super::SocketAddress::default(); let client_fd = task .do_accept( - server, + &typed_socket(task, server), Some(&mut remote_addr), if is_nonblocking { SockFlags::NONBLOCK @@ -2268,8 +2239,14 @@ mod tests { }, ) .expect("Failed to accept connection"); - assert_eq!(server_sockaddr, task.do_getsockname(client_fd).unwrap()); - assert_eq!(remote_addr, task.do_getpeername(client_fd).unwrap()); + assert_eq!( + server_sockaddr, + task.do_getsockname(&typed_socket(task, client_fd)).unwrap() + ); + assert_eq!( + remote_addr, + task.do_getpeername(&typed_socket(task, client_fd)).unwrap() + ); let super::SocketAddress::Inet(SocketAddr::V4(remote_addr)) = remote_addr else { panic!("Expected IPv4 address"); }; @@ -2279,7 +2256,12 @@ mod tests { match option { "sendto" => { let n = task - .do_sendto(client_fd, buf.as_bytes(), SendFlags::empty(), None) + .do_sendto( + &typed_socket(task, client_fd), + buf.as_bytes(), + SendFlags::empty(), + None, + ) .expect("Failed to send data"); assert_eq!(n, buf.len()); let output = child_handle @@ -2309,8 +2291,12 @@ mod tests { h }; assert_eq!( - task.do_sendmsg(client_fd, &hdr, SendFlags::empty()) - .expect("Failed to sendmsg"), + task.sys_sendmsg( + i32::try_from(client_fd).unwrap(), + UserPtr::from_usize(&raw const hdr as usize), + SendFlags::empty(), + ) + .expect("Failed to sendmsg"), buf1.len() + buf2.len() ); let output = child_handle @@ -2339,7 +2325,14 @@ mod tests { }; let n = match option { "recvfrom" => task - .do_recvfrom(client_fd, &mut recv_buf, flags, None) + .sys_recvfrom( + i32::try_from(client_fd).unwrap(), + UserPtrMut::from_usize(recv_buf.as_mut_ptr() as usize), + recv_buf.len(), + flags, + None, + UserPtrMut::from_usize(0), + ) .expect("Failed to receive data"), "recvmsg" => { let iovec = [litebox_common_linux::IoVec { @@ -2450,7 +2443,7 @@ mod tests { close_socket(&task, socket_fd); let err = task .do_connect( - socket_fd2, + &typed_socket(&task, socket_fd2), SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([10, 0, 0, 1]), SERVER_PORT, @@ -2492,7 +2485,7 @@ mod tests { core::net::Ipv4Addr::from([10, 0, 0, 1]), SERVER_PORT, ))); - task.do_connect(client_fd, server_addr) + task.do_connect(&typed_socket(&task, client_fd), server_addr) .expect("failed to connect to server"); let so_error = get_so_error(&task, client_fd); assert_eq!( @@ -2502,7 +2495,12 @@ mod tests { let buf = "Hello, world!"; let n = task - .do_sendto(client_fd, buf.as_bytes(), SendFlags::empty(), None) + .do_sendto( + &typed_socket(&task, client_fd), + buf.as_bytes(), + SendFlags::empty(), + None, + ) .unwrap(); assert_eq!(n, buf.len()); @@ -2512,7 +2510,7 @@ mod tests { }; let optval = UserPtr::from_usize((&raw const linger).cast::() as usize); task.do_setsockopt( - client_fd, + &typed_socket(&task, client_fd), SocketOptionName::Socket(SocketOption::LINGER), optval, core::mem::size_of::(), @@ -2553,11 +2551,12 @@ mod tests { core::net::Ipv4Addr::from(TUN_IP_ADDR), SERVER_PORT, ))); - task.do_bind(server_fd, server_addr.clone()) + task.do_bind(&typed_socket(task, server_fd), server_addr.clone()) .expect("failed to bind server"); assert_eq!( server_addr, - task.do_getsockname(server_fd).expect("getsockname failed") + task.do_getsockname(&typed_socket(task, server_fd)) + .expect("getsockname failed") ); // Create an epoll instance and register the server fd for EPOLLIN @@ -2722,7 +2721,7 @@ mod tests { // Send from client to server let msg = "Hello without connect()"; task.do_sendto( - client_fd, + &typed_socket(&task, client_fd), msg.as_bytes(), SendFlags::empty(), Some(server_addr.clone()), @@ -2730,21 +2729,27 @@ mod tests { .expect("failed to sendto"); // Client implicitly bound to an ephemeral port via sendto - let SocketAddress::Inet(client_addr) = - task.do_getsockname(client_fd).expect("getsockname failed") + let SocketAddress::Inet(client_addr) = task + .do_getsockname(&typed_socket(&task, client_fd)) + .expect("getsockname failed") else { panic!("Expected Inet socket address"); }; assert_ne!(client_addr.port(), 0); // Client connects to server address - task.do_connect(client_fd, server_addr.clone()) + task.do_connect(&typed_socket(&task, client_fd), server_addr.clone()) .expect("failed to connect"); // Now client can send without specifying addr let msg = "Hello with connect()"; - task.do_sendto(client_fd, msg.as_bytes(), SendFlags::empty(), None) - .expect("failed to sendto"); + task.do_sendto( + &typed_socket(&task, client_fd), + msg.as_bytes(), + SendFlags::empty(), + None, + ) + .expect("failed to sendto"); close_socket(&task, client_fd); } @@ -2759,7 +2764,7 @@ mod tests { let mut congestion_name = [0u8; 16]; let optlen = task .do_getsockopt( - sockfd, + &typed_socket(&task, sockfd), SocketOptionName::TCP(TcpOption::CONGESTION), UserPtrMut::from_usize(congestion_name.as_mut_ptr() as usize), congestion_name.len().trunc(), @@ -2772,7 +2777,7 @@ mod tests { ); task.do_setsockopt( - sockfd, + &typed_socket(&task, sockfd), SocketOptionName::TCP(TcpOption::CONGESTION), UserPtr::from_usize(congestion_name.as_ptr() as usize), optlen, @@ -2782,7 +2787,7 @@ mod tests { let congestion_name = b"cubic\0"; let err = task .do_setsockopt( - sockfd, + &typed_socket(&task, sockfd), SocketOptionName::TCP(TcpOption::CONGESTION), UserPtr::from_usize(congestion_name.as_ptr() as usize), congestion_name.len(), @@ -2793,7 +2798,7 @@ mod tests { let val: u32 = 1; let optval = UserPtr::from_usize((&raw const val).cast::() as usize); task.do_setsockopt( - sockfd, + &typed_socket(&task, sockfd), SocketOptionName::Socket(SocketOption::KEEPALIVE), optval, core::mem::size_of::(), @@ -2805,7 +2810,7 @@ mod tests { let optval_out = UserPtrMut::from_usize((&raw mut result).cast::() as usize); let len = task .do_getsockopt( - sockfd, + &typed_socket(&task, sockfd), SocketOptionName::Socket(SocketOption::KEEPALIVE), optval_out, core::mem::size_of::().trunc(), @@ -2828,7 +2833,7 @@ mod tests { // Our current implementation returns `ETIMEDOUT` instead of `ENETUNREACH`. let err = task .do_connect( - sockfd, + &typed_socket(&task, sockfd), SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([192, 0, 2, 1]), SERVER_PORT, @@ -2860,6 +2865,42 @@ mod tests { close_socket(&task, socket_fd); close_socket(&task, socket_fd2); } + + #[test] + fn socket_from_raw_rejects_negative_fd() { + let task = init_platform(None); + assert!(matches!( + task.files.borrow().socket_from_raw(-1), + Err(Errno::EBADF) + )); + } + + #[test] + fn typed_socket_does_not_follow_raw_fd_reuse() { + let task = init_platform(None); + let socket_fd = task + .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) + .unwrap(); + let socket = task + .files + .borrow() + .socket_from_raw(i32::try_from(socket_fd).unwrap()) + .unwrap(); + + close_socket(&task, socket_fd); + let replacement = task + .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) + .unwrap(); + assert_eq!(replacement, socket_fd); + + let result = task.files.borrow().with_typed_socket( + &task.global, + &socket, + |fd| task.global.get_proxy(fd).map(|_| ()), + |_| unreachable!(), + ); + assert_eq!(result, Err(Errno::EBADF)); + } } #[cfg(test)] @@ -2893,10 +2934,10 @@ mod unix_tests { ) -> Result { let server_fd = create_unix_socket(task, SockType::Stream, flags); task.do_bind( - server_fd, + &typed_socket(task, server_fd), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), )?; - task.do_listen(server_fd, 1)?; + task.do_listen(&typed_socket(task, server_fd), 1)?; Ok(server_fd) } @@ -2905,6 +2946,27 @@ mod unix_tests { .expect("close socket failed"); } + fn typed_socket( + task: &TestTask, + fd: u32, + ) -> crate::syscalls::file::AnyTypedFd { + task.files + .borrow() + .socket_from_raw(i32::try_from(fd).unwrap()) + .unwrap() + } + + fn recvfrom( + task: &TestTask, + sockfd: u32, + buf: &mut [u8], + flags: ReceiveFlags, + source_addr: Option<&mut Option>, + ) -> Result { + let socket = typed_socket(task, sockfd); + task.do_recvfrom(&socket, buf, flags, source_addr) + } + fn ppoll(task: &TestTask, fd: u32, events: Events) { let fd = i32::try_from(fd).unwrap(); let mut pollfd = [litebox_common_linux::Pollfd { @@ -2937,16 +2999,16 @@ mod unix_tests { let client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); let server_addr = SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())); let client_addr = SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())); - task.do_bind(server_fd, server_addr.clone()) + task.do_bind(&typed_socket(&task, server_fd), server_addr.clone()) .expect("server bind failed"); - task.do_bind(client_fd, client_addr.clone()) + task.do_bind(&typed_socket(&task, client_fd), client_addr.clone()) .expect("client bind failed"); // send message from server to client let msg1 = "Hello from server"; let n = task .do_sendto( - server_fd, + &typed_socket(&task, server_fd), msg1.as_bytes(), SendFlags::empty(), Some(client_addr.clone()), @@ -2956,14 +3018,14 @@ mod unix_tests { let mut buf = [0u8; 64]; let mut source = None; - let n = task - .do_recvfrom( - client_fd, - &mut buf, - ReceiveFlags::empty(), - Some(&mut source), - ) - .expect("recvfrom failed"); + let n = recvfrom( + &task, + client_fd, + &mut buf, + ReceiveFlags::empty(), + Some(&mut source), + ) + .expect("recvfrom failed"); assert_eq!(n, msg1.len()); assert_eq!(&buf[..n], b"Hello from server"); assert_eq!(source, Some(server_addr.clone())); @@ -2972,7 +3034,7 @@ mod unix_tests { let msg2 = "Hello from client"; let n = task .do_sendto( - client_fd, + &typed_socket(&task, client_fd), msg2.as_bytes(), SendFlags::empty(), Some(server_addr), @@ -2982,14 +3044,14 @@ mod unix_tests { let mut buf = [0u8; 64]; let mut source = None; - let n = task - .do_recvfrom( - server_fd, - &mut buf, - ReceiveFlags::empty(), - Some(&mut source), - ) - .expect("recvfrom failed"); + let n = recvfrom( + &task, + server_fd, + &mut buf, + ReceiveFlags::empty(), + Some(&mut source), + ) + .expect("recvfrom failed"); assert_eq!(n, msg2.len()); assert_eq!(&buf[..n], b"Hello from client"); assert_eq!(source, Some(client_addr)); @@ -3012,14 +3074,18 @@ mod unix_tests { let server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); task.do_connect( - client_fd, + &typed_socket(&task, client_fd), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); let mut peer_addr = SocketAddress::default(); let server_conn = task - .do_accept(server_fd, Some(&mut peer_addr), SockFlags::empty()) + .do_accept( + &typed_socket(&task, server_fd), + Some(&mut peer_addr), + SockFlags::empty(), + ) .unwrap(); assert!(matches!( peer_addr, @@ -3027,18 +3093,27 @@ mod unix_tests { )); let msg1 = "Hello, "; let n = task - .do_sendto(server_conn, msg1.as_bytes(), SendFlags::empty(), None) + .do_sendto( + &typed_socket(&task, server_conn), + msg1.as_bytes(), + SendFlags::empty(), + None, + ) .expect("sendto failed"); assert_eq!(n, msg1.len()); let msg2 = "world!"; let n = task - .do_sendto(server_conn, msg2.as_bytes(), SendFlags::empty(), None) + .do_sendto( + &typed_socket(&task, server_conn), + msg2.as_bytes(), + SendFlags::empty(), + None, + ) .expect("sendto failed"); assert_eq!(n, msg2.len()); let mut buf = [0u8; 64]; - let n = task - .do_recvfrom(client_fd, &mut buf, ReceiveFlags::empty(), None) + let n = recvfrom(&task, client_fd, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(n, msg1.len() + msg2.len()); assert_eq!(&buf[..n], b"Hello, world!"); @@ -3055,7 +3130,7 @@ mod unix_tests { let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); let addr = "/unix_stream_socket_refused.sock"; let result = task.do_connect( - client_fd, + &typed_socket(&task, client_fd), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert_eq!(result.unwrap_err(), Errno::ECONNREFUSED); @@ -3064,7 +3139,7 @@ mod unix_tests { let server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); let result = task.do_connect( - client_fd, + &typed_socket(&task, client_fd), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert!(result.is_ok()); @@ -3074,7 +3149,7 @@ mod unix_tests { let another_client = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); let result = task.do_connect( - another_client, + &typed_socket(&task, another_client), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert_eq!(result.unwrap_err(), Errno::ECONNREFUSED); @@ -3089,7 +3164,7 @@ mod unix_tests { // remove the sock file task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); let result = task.do_connect( - client_fd, + &typed_socket(&task, client_fd), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert_eq!(result.unwrap_err(), Errno::ENOENT); @@ -3128,7 +3203,7 @@ mod unix_tests { ppoll(&task, server_fd, Events::OUT); } task.do_connect( - client_fd, + &typed_socket(&task, client_fd), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); @@ -3138,7 +3213,12 @@ mod unix_tests { for (i, client_fd) in client_fds.iter().enumerate() { let msg = alloc::format!("message from connection {i}"); let n = task - .do_sendto(*client_fd, msg.as_bytes(), SendFlags::empty(), None) + .do_sendto( + &typed_socket(&task, *client_fd), + msg.as_bytes(), + SendFlags::empty(), + None, + ) .expect("sendto failed"); assert_eq!(n, msg.len()); } @@ -3155,7 +3235,7 @@ mod unix_tests { } let server_conn = task .do_accept( - server_fd, + &typed_socket(&task, server_fd), None, if is_nonblocking { SockFlags::NONBLOCK @@ -3173,9 +3253,14 @@ mod unix_tests { if is_nonblocking { ppoll(&task, *server_conn_fd, Events::IN); } - let n = task - .do_recvfrom(*server_conn_fd, &mut buf, ReceiveFlags::empty(), None) - .expect("recvfrom failed"); + let n = recvfrom( + &task, + *server_conn_fd, + &mut buf, + ReceiveFlags::empty(), + None, + ) + .expect("recvfrom failed"); assert_eq!(n, msg.len()); assert_eq!(&buf[..n], msg.as_bytes()); } @@ -3212,19 +3297,19 @@ mod unix_tests { let client1_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); task.do_connect( - client1_fd, + &typed_socket(&task, client1_fd), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); // server one is still alive but cannot accept connections let err = task - .do_accept(server1_fd, None, SockFlags::empty()) + .do_accept(&typed_socket(&task, server1_fd), None, SockFlags::empty()) .unwrap_err(); assert_eq!(err, Errno::EAGAIN); let conn_fd = task - .do_accept(server2_fd, None, SockFlags::empty()) + .do_accept(&typed_socket(&task, server2_fd), None, SockFlags::empty()) .unwrap(); close_socket(&task, conn_fd); close_socket(&task, client1_fd); @@ -3233,7 +3318,7 @@ mod unix_tests { close_socket(&task, server1_fd); let client2_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); task.do_connect( - client2_fd, + &typed_socket(&task, client2_fd), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); @@ -3255,7 +3340,7 @@ mod unix_tests { let addr = "/unix_datagram_socket_server.sock"; let server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); task.do_bind( - server_fd, + &typed_socket(&task, server_fd), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); @@ -3263,7 +3348,7 @@ mod unix_tests { let server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); let err = task .do_bind( - server_fd2, + &typed_socket(&task, server_fd2), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap_err(); @@ -3272,7 +3357,7 @@ mod unix_tests { task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); let server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); task.do_bind( - server_fd2, + &typed_socket(&task, server_fd2), SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); @@ -3305,8 +3390,7 @@ mod unix_tests { if is_nonblocking { ppoll(&task, sock2, Events::IN); } - let n = task - .do_recvfrom(sock2, &mut buf, ReceiveFlags::empty(), None) + let n = recvfrom(&task, sock2, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(&buf[..n], b"Message from sock1"); }); @@ -3314,8 +3398,13 @@ mod unix_tests { std::thread::sleep(core::time::Duration::from_millis(100)); // Send from sock1 to sock2 let msg1 = "Message from sock1"; - task.do_sendto(sock1, msg1.as_bytes(), SendFlags::empty(), None) - .expect("sendto failed"); + task.do_sendto( + &typed_socket(&task, sock1), + msg1.as_bytes(), + SendFlags::empty(), + None, + ) + .expect("sendto failed"); let receiver1 = task.spawn_clone_for_test(move |task| { // Receive on sock1 (from sock2) @@ -3323,8 +3412,7 @@ mod unix_tests { if is_nonblocking { ppoll(&task, sock1, Events::IN); } - let n = task - .do_recvfrom(sock1, &mut buf, ReceiveFlags::empty(), None) + let n = recvfrom(&task, sock1, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(&buf[..n], b"Message from sock2"); }); @@ -3332,8 +3420,13 @@ mod unix_tests { std::thread::sleep(core::time::Duration::from_millis(100)); // Send from sock2 to sock1 let msg2 = "Message from sock2"; - task.do_sendto(sock2, msg2.as_bytes(), SendFlags::empty(), None) - .expect("sendto failed"); + task.do_sendto( + &typed_socket(&task, sock2), + msg2.as_bytes(), + SendFlags::empty(), + None, + ) + .expect("sendto failed"); std::thread::sleep(core::time::Duration::from_millis(500)); close_socket(&task, sock1); @@ -3385,7 +3478,7 @@ mod unix_tests { let tv = litebox_common_linux::TimeVal::from(timeout); let optval = UserPtr::from_usize((&raw const tv).cast::() as usize); task.do_setsockopt( - sock1, + &typed_socket(&task, sock1), SocketOptionName::Socket(SocketOption::RCVTIMEO), optval, core::mem::size_of::(), @@ -3393,9 +3486,7 @@ mod unix_tests { .expect("Failed to set SO_RCVTIMEO"); let mut buf = [0u8; 16]; let start = std::time::Instant::now(); - let err = task - .do_recvfrom(sock1, &mut buf, ReceiveFlags::empty(), None) - .unwrap_err(); + let err = recvfrom(&task, sock1, &mut buf, ReceiveFlags::empty(), None).unwrap_err(); let elapsed = start.elapsed(); // Linux returns EAGAIN (not ETIMEDOUT) when SO_RCVTIMEO expires on a blocking recv. assert_eq!(err, Errno::EAGAIN); @@ -3419,7 +3510,9 @@ mod unix_tests { let server_fd = create_unix_server_socket(&task, server_path, SockFlags::empty()).unwrap(); // Server socket should have its bound address - let server_addr = task.do_getsockname(server_fd).unwrap(); + let server_addr = task + .do_getsockname(&typed_socket(&task, server_fd)) + .unwrap(); assert_eq!( server_addr, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) @@ -3429,7 +3522,9 @@ mod unix_tests { let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); // Before connect, client should have unnamed address - let client_addr = task.do_getsockname(client_fd).unwrap(); + let client_addr = task + .do_getsockname(&typed_socket(&task, client_fd)) + .unwrap(); assert!(matches!( client_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) @@ -3437,37 +3532,47 @@ mod unix_tests { // Connect client to server task.do_connect( - client_fd, + &typed_socket(&task, client_fd), SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())), ) .unwrap(); // After connect, client's getsockname should still be unnamed - let client_local_addr = task.do_getsockname(client_fd).unwrap(); + let client_local_addr = task + .do_getsockname(&typed_socket(&task, client_fd)) + .unwrap(); assert!(matches!( client_local_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); // Client's getpeername should return server's address - let client_peer_addr = task.do_getpeername(client_fd).unwrap(); + let client_peer_addr = task + .do_getpeername(&typed_socket(&task, client_fd)) + .unwrap(); assert_eq!( client_peer_addr, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Accept connection on server - let server_conn = task.do_accept(server_fd, None, SockFlags::empty()).unwrap(); + let server_conn = task + .do_accept(&typed_socket(&task, server_fd), None, SockFlags::empty()) + .unwrap(); // Server connection's local address should be the server's bound address - let server_conn_local = task.do_getsockname(server_conn).unwrap(); + let server_conn_local = task + .do_getsockname(&typed_socket(&task, server_conn)) + .unwrap(); assert_eq!( server_conn_local, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Server connection's peer address should be unnamed (client didn't bind) - let server_conn_peer = task.do_getpeername(server_conn).unwrap(); + let server_conn_peer = task + .do_getpeername(&typed_socket(&task, server_conn)) + .unwrap(); assert!(matches!( server_conn_peer, SocketAddress::Unix(UnixSocketAddr::Unnamed) @@ -3490,13 +3595,17 @@ mod unix_tests { let client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); // Before bind, both should have unnamed addresses - let server_addr = task.do_getsockname(server_fd).unwrap(); + let server_addr = task + .do_getsockname(&typed_socket(&task, server_fd)) + .unwrap(); assert!(matches!( server_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); - let client_addr = task.do_getsockname(client_fd).unwrap(); + let client_addr = task + .do_getsockname(&typed_socket(&task, client_fd)) + .unwrap(); assert!(matches!( client_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) @@ -3504,13 +3613,15 @@ mod unix_tests { // Bind server task.do_bind( - server_fd, + &typed_socket(&task, server_fd), SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())), ) .unwrap(); // After bind, server should have its bound address - let server_local = task.do_getsockname(server_fd).unwrap(); + let server_local = task + .do_getsockname(&typed_socket(&task, server_fd)) + .unwrap(); assert_eq!( server_local, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) @@ -3518,13 +3629,15 @@ mod unix_tests { // Bind client task.do_bind( - client_fd, + &typed_socket(&task, client_fd), SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())), ) .unwrap(); // After bind, client should have its bound address - let client_local = task.do_getsockname(client_fd).unwrap(); + let client_local = task + .do_getsockname(&typed_socket(&task, client_fd)) + .unwrap(); assert_eq!( client_local, SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())) @@ -3532,27 +3645,31 @@ mod unix_tests { // Connect client to server task.do_connect( - client_fd, + &typed_socket(&task, client_fd), SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())), ) .unwrap(); // After connect, getsockname should still return client's bound address - let client_local_after_connect = task.do_getsockname(client_fd).unwrap(); + let client_local_after_connect = task + .do_getsockname(&typed_socket(&task, client_fd)) + .unwrap(); assert_eq!( client_local_after_connect, SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())) ); // getpeername should return server's address - let client_peer = task.do_getpeername(client_fd).unwrap(); + let client_peer = task + .do_getpeername(&typed_socket(&task, client_fd)) + .unwrap(); assert_eq!( client_peer, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Server hasn't connected, so getpeername should fail with ENOTCONN - let server_peer_result = task.do_getpeername(server_fd); + let server_peer_result = task.do_getpeername(&typed_socket(&task, server_fd)); assert_eq!(server_peer_result.unwrap_err(), Errno::ENOTCONN); close_socket(&task, server_fd); From a9223372cbb68092487fb0717acf0a85c9bedf7a Mon Sep 17 00:00:00 2001 From: Weiteng Chen Date: Sat, 5 Sep 2026 00:38:25 +0000 Subject: [PATCH 2/3] clean up code --- litebox_shim_linux/src/syscalls/file.rs | 79 +--- litebox_shim_linux/src/syscalls/net.rs | 485 +++++++++++------------- 2 files changed, 228 insertions(+), 336 deletions(-) diff --git a/litebox_shim_linux/src/syscalls/file.rs b/litebox_shim_linux/src/syscalls/file.rs index bfe751e47..8bf5bafdb 100644 --- a/litebox_shim_linux/src/syscalls/file.rs +++ b/litebox_shim_linux/src/syscalls/file.rs @@ -168,9 +168,6 @@ impl FilesState { } /// A raw fd resolved once into the subsystem that owns it. -/// -/// Holding the `Arc` keeps the resolution stable: a concurrent `close` makes subsequent -/// operations fail with `EBADF` rather than follow a reused raw slot to another file. pub(crate) enum AnyTypedFd { Fs(alloc::sync::Arc>), Network(alloc::sync::Arc>>), @@ -588,6 +585,8 @@ impl Task { }) }, ); + // For datagrams, the returned size represents the actual size of the message, + // which may be larger than the buffer size. result.map(|size| size.min(buf.borrow().len())) } @@ -2624,80 +2623,6 @@ mod tests { extern crate std; - #[test] - fn readv_does_not_follow_raw_fd_reuse() { - let task = crate::syscalls::tests::init_platform(None); - let replacement_path = "/readv_fd_reuse"; - let replacement = task - .sys_open(replacement_path, OFlags::CREAT | OFlags::WRONLY, Mode::RWXU) - .unwrap(); - task.sys_write(i32::try_from(replacement).unwrap(), b"BBBB", None) - .unwrap(); - task.sys_close(i32::try_from(replacement).unwrap()).unwrap(); - - let (read_fd, write_fd) = task.sys_pipe2(OFlags::empty()).unwrap(); - task.sys_write(i32::try_from(write_fd).unwrap(), b"AAAACCCC", None) - .unwrap(); - - let mut first = [0u8; 4]; - let mut second = [0u8; 4]; - let iovs = [ - IoReadVec { - iov_base: UserPtrMut::from_usize(first.as_mut_ptr().expose_provenance()), - iov_len: first.len(), - }, - IoReadVec { - iov_base: UserPtrMut::from_usize(second.as_mut_ptr().expose_provenance()), - iov_len: second.len(), - }, - ]; - let mut kernel_buffer = [0u8; 4]; - let call = Cell::new(0); - let read_fd = i32::try_from(read_fd).unwrap(); - - let result = task.with_typed_fd(read_fd, |fd| { - read_from_iovec::<_, crate::syscalls::tests::TestPlatform>( - &iovs, - &mut kernel_buffer, - |buf, _total| { - let size = task.do_read(fd, buf, None)?; - if call.replace(call.get() + 1) == 0 { - task.sys_close(read_fd).unwrap(); - let reopened = task - .sys_open(replacement_path, OFlags::RDONLY, Mode::empty()) - .unwrap(); - assert_eq!(i32::try_from(reopened).unwrap(), read_fd); - } - Ok(size) - }, - ) - }); - - assert_eq!(result, Ok(4)); - assert_eq!(&first, b"AAAA"); - assert_eq!(&second, &[0; 4]); - } - - #[test] - fn dup_does_not_follow_raw_fd_reuse() { - let task = crate::syscalls::tests::init_platform(None); - let raw_fd = task - .sys_open("/dev/stdin", OFlags::RDONLY, Mode::empty()) - .unwrap(); - let typed_fd = task.typed_fd(raw_fd.try_into().unwrap()).unwrap(); - - task.sys_close(raw_fd.try_into().unwrap()).unwrap(); - let replacement = task - .sys_open("/dev/stdin", OFlags::RDONLY, Mode::empty()) - .unwrap(); - assert_eq!(replacement, raw_fd); - - assert!(matches!( - task.do_dup(&typed_fd, OFlags::empty()), - Err(DupFdError::BadFd) - )); - } - #[test] fn write_to_iovec_returns_partial_after_later_error() { let first = b"first"; diff --git a/litebox_shim_linux/src/syscalls/net.rs b/litebox_shim_linux/src/syscalls/net.rs index 10ece50d3..dcb7cf073 100644 --- a/litebox_shim_linux/src/syscalls/net.rs +++ b/litebox_shim_linux/src/syscalls/net.rs @@ -2115,11 +2115,14 @@ mod tests { /// Helper to read SO_ERROR from a socket via getsockopt. /// Returns the errno integer value (0 means no error). - fn get_so_error(task: &TestTask, sockfd: u32) -> u32 { + fn get_so_error( + task: &TestTask, + sockfd: &crate::syscalls::file::AnyTypedFd, + ) -> u32 { let mut optval: u32 = 0xDEAD; let len = task .do_getsockopt( - &typed_socket(task, sockfd), + sockfd, SocketOptionName::Socket(SocketOption::ERROR), UserPtrMut::from_usize((&raw mut optval).cast::() as usize), core::mem::size_of::().trunc(), @@ -2163,7 +2166,7 @@ mod tests { test_trunc: bool, option: &'static str, ) { - let server = task + let raw_server = task .do_socket( AddressFamily::INET, SockType::Stream, @@ -2175,13 +2178,14 @@ mod tests { 0, ) .unwrap(); + let server = typed_socket(task, raw_server); let server_sockaddr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from(ip), port, ))); - task.do_bind(&typed_socket(task, server), server_sockaddr.clone()) + task.do_bind(&server, server_sockaddr.clone()) .expect("Failed to bind socket"); - task.do_listen(&typed_socket(task, server), 1) + task.do_listen(&server, 1) .expect("Failed to listen on socket"); // Create an epoll instance and register the server fd for EPOLLIN @@ -2189,7 +2193,7 @@ mod tests { .sys_epoll_create(litebox_common_linux::EpollCreateFlags::empty()) .expect("failed to create epoll"); let epfd = i32::try_from(epfd).unwrap(); - epoll_add(task, epfd, server, litebox::event::Events::IN); + epoll_add(task, epfd, raw_server, litebox::event::Events::IN); let buf = "Hello, world!"; let child_handle = std::thread::spawn(move || { @@ -2228,9 +2232,9 @@ mod tests { } let mut remote_addr = super::SocketAddress::default(); - let client_fd = task + let raw_client_fd = task .do_accept( - &typed_socket(task, server), + &server, Some(&mut remote_addr), if is_nonblocking { SockFlags::NONBLOCK @@ -2239,14 +2243,9 @@ mod tests { }, ) .expect("Failed to accept connection"); - assert_eq!( - server_sockaddr, - task.do_getsockname(&typed_socket(task, client_fd)).unwrap() - ); - assert_eq!( - remote_addr, - task.do_getpeername(&typed_socket(task, client_fd)).unwrap() - ); + let client_fd = typed_socket(task, raw_client_fd); + assert_eq!(server_sockaddr, task.do_getsockname(&client_fd).unwrap()); + assert_eq!(remote_addr, task.do_getpeername(&client_fd).unwrap()); let super::SocketAddress::Inet(SocketAddr::V4(remote_addr)) = remote_addr else { panic!("Expected IPv4 address"); }; @@ -2256,12 +2255,7 @@ mod tests { match option { "sendto" => { let n = task - .do_sendto( - &typed_socket(task, client_fd), - buf.as_bytes(), - SendFlags::empty(), - None, - ) + .do_sendto(&client_fd, buf.as_bytes(), SendFlags::empty(), None) .expect("Failed to send data"); assert_eq!(n, buf.len()); let output = child_handle @@ -2292,7 +2286,7 @@ mod tests { }; assert_eq!( task.sys_sendmsg( - i32::try_from(client_fd).unwrap(), + i32::try_from(raw_client_fd).unwrap(), UserPtr::from_usize(&raw const hdr as usize), SendFlags::empty(), ) @@ -2308,13 +2302,13 @@ mod tests { } "recvfrom" | "recvmsg" => { if is_nonblocking { - epoll_add(task, epfd, client_fd, litebox::event::Events::IN); + epoll_add(task, epfd, raw_client_fd, litebox::event::Events::IN); let mut events = [litebox_common_linux::EpollEvent { events: 0, data: 0 }; 2]; let n = epoll_wait(task, epfd, &mut events); for ev in &events[..n] { assert!(ev.events & litebox::event::Events::IN.bits() != 0); let fd = u32::try_from(ev.data).unwrap(); - assert_eq!(fd, client_fd); + assert_eq!(fd, raw_client_fd); } } let mut recv_buf = [0u8; 48]; @@ -2326,7 +2320,7 @@ mod tests { let n = match option { "recvfrom" => task .sys_recvfrom( - i32::try_from(client_fd).unwrap(), + i32::try_from(raw_client_fd).unwrap(), UserPtrMut::from_usize(recv_buf.as_mut_ptr() as usize), recv_buf.len(), flags, @@ -2345,7 +2339,7 @@ mod tests { msg_hdr.msg_iov = UserPtr::from_usize(iovec.as_ptr() as usize); msg_hdr.msg_iovlen = iovec.len(); let msg_ptr = UserPtrMut::from_usize(&raw mut msg_hdr as usize); - task.sys_recvmsg(i32::try_from(client_fd).unwrap(), msg_ptr, flags) + task.sys_recvmsg(i32::try_from(raw_client_fd).unwrap(), msg_ptr, flags) .expect("failed to recvmsg") } _ => unreachable!(), @@ -2361,8 +2355,8 @@ mod tests { _ => panic!("Unknown option"), } - close_socket(task, client_fd); - close_socket(task, server); + close_socket(task, raw_client_fd); + close_socket(task, raw_server); } fn test_tcp_socket_with_external_client(port: u16, is_nonblocking: bool, test_trunc: bool) { @@ -2433,17 +2427,18 @@ mod tests { #[test] fn test_tun_tcp_connection_refused() { let task = init_platform(Some(TUN_DEVICE_NAME)); - let socket_fd = task + let raw_socket_fd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create socket"); - let socket_fd2 = task - .sys_dup(i32::try_from(socket_fd).unwrap(), None, None) + let raw_socket_fd2 = task + .sys_dup(i32::try_from(raw_socket_fd).unwrap(), None, None) .unwrap(); + let socket_fd2 = typed_socket(&task, raw_socket_fd2); - close_socket(&task, socket_fd); + close_socket(&task, raw_socket_fd); let err = task .do_connect( - &typed_socket(&task, socket_fd2), + &socket_fd2, SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([10, 0, 0, 1]), SERVER_PORT, @@ -2452,11 +2447,11 @@ mod tests { .unwrap_err(); assert_eq!(err, litebox_common_linux::errno::Errno::ECONNREFUSED); - let so_err = get_so_error(&task, socket_fd2); + let so_err = get_so_error(&task, &socket_fd2); assert_eq!(so_err, i32::from(Errno::ECONNREFUSED).cast_unsigned()); // Second read should be cleared (self-clearing semantics) - assert_eq!(get_so_error(&task, socket_fd2), 0); + assert_eq!(get_so_error(&task, &socket_fd2), 0); } #[test] @@ -2477,17 +2472,18 @@ mod tests { std::thread::sleep(core::time::Duration::from_secs(1)); // Client socket - let client_fd = task + let raw_client_fd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create client socket"); + let client_fd = typed_socket(&task, raw_client_fd); let server_addr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([10, 0, 0, 1]), SERVER_PORT, ))); - task.do_connect(&typed_socket(&task, client_fd), server_addr) + task.do_connect(&client_fd, server_addr) .expect("failed to connect to server"); - let so_error = get_so_error(&task, client_fd); + let so_error = get_so_error(&task, &client_fd); assert_eq!( so_error, 0, "SO_ERROR should be 0 after successful connect, got {so_error}" @@ -2495,12 +2491,7 @@ mod tests { let buf = "Hello, world!"; let n = task - .do_sendto( - &typed_socket(&task, client_fd), - buf.as_bytes(), - SendFlags::empty(), - None, - ) + .do_sendto(&client_fd, buf.as_bytes(), SendFlags::empty(), None) .unwrap(); assert_eq!(n, buf.len()); @@ -2510,14 +2501,14 @@ mod tests { }; let optval = UserPtr::from_usize((&raw const linger).cast::() as usize); task.do_setsockopt( - &typed_socket(&task, client_fd), + &client_fd, SocketOptionName::Socket(SocketOption::LINGER), optval, core::mem::size_of::(), ) .expect("Failed to set SO_LINGER"); - close_socket(&task, client_fd); + close_socket(&task, raw_client_fd); let output = child_handle .join() @@ -2535,7 +2526,7 @@ mod tests { op: &str, ) { // Server socket and bind - let server_fd = task + let raw_server_fd = task .do_socket( AddressFamily::INET, SockType::Datagram, @@ -2547,16 +2538,16 @@ mod tests { litebox_common_linux::IPProtocol::UDP as u8, ) .expect("failed to create server socket"); + let server_fd = typed_socket(task, raw_server_fd); let server_addr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from(TUN_IP_ADDR), SERVER_PORT, ))); - task.do_bind(&typed_socket(task, server_fd), server_addr.clone()) + task.do_bind(&server_fd, server_addr.clone()) .expect("failed to bind server"); assert_eq!( server_addr, - task.do_getsockname(&typed_socket(task, server_fd)) - .expect("getsockname failed") + task.do_getsockname(&server_fd).expect("getsockname failed") ); // Create an epoll instance and register the server fd for EPOLLIN @@ -2564,7 +2555,7 @@ mod tests { .sys_epoll_create(litebox_common_linux::EpollCreateFlags::empty()) .expect("failed to create epoll"); let epfd = i32::try_from(epfd).unwrap(); - epoll_add(task, epfd, server_fd, litebox::event::Events::IN); + epoll_add(task, epfd, raw_server_fd, litebox::event::Events::IN); let msg = "Hello from client"; let mut child = std::process::Command::new("nc") @@ -2604,7 +2595,7 @@ mod tests { for ev in &events[..n] { assert!(ev.events & litebox::event::Events::IN.bits() != 0); let fd = u32::try_from(ev.data).unwrap(); - assert_eq!(fd, server_fd); + assert_eq!(fd, raw_server_fd); } } let recv_len = if test_trunc { @@ -2617,7 +2608,7 @@ mod tests { "recvfrom" => { let mut addrlen = core::mem::size_of::(); task.sys_recvfrom( - i32::try_from(server_fd).unwrap(), + i32::try_from(raw_server_fd).unwrap(), UserPtrMut::from_usize(recv_buf.as_mut_ptr() as usize), recv_len, recv_flags, @@ -2638,7 +2629,7 @@ mod tests { msg_hdr.msg_namelen = source_addr.len().trunc(); let msg_ptr = UserPtrMut::from_usize(&raw mut msg_hdr as usize); let n = task - .sys_recvmsg(i32::try_from(server_fd).unwrap(), msg_ptr, recv_flags) + .sys_recvmsg(i32::try_from(raw_server_fd).unwrap(), msg_ptr, recv_flags) .expect("recvmsg failed"); if test_trunc { let flags = msg_hdr.msg_flags; @@ -2670,7 +2661,7 @@ mod tests { }; assert_eq!(sender_addr.port(), CLIENT_PORT); - close_socket(task, server_fd); + close_socket(task, raw_server_fd); child.wait().expect("Failed to wait for client"); } @@ -2704,7 +2695,7 @@ mod tests { let task = init_platform(Some(TUN_DEVICE_NAME)); // Client socket and explicit bind - let client_fd = task + let raw_client_fd = task .do_socket( AddressFamily::INET, SockType::Datagram, @@ -2712,6 +2703,7 @@ mod tests { litebox_common_linux::IPProtocol::UDP as u8, ) .expect("failed to create client socket"); + let client_fd = typed_socket(&task, raw_client_fd); let server_addr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([127, 0, 0, 1]), @@ -2721,7 +2713,7 @@ mod tests { // Send from client to server let msg = "Hello without connect()"; task.do_sendto( - &typed_socket(&task, client_fd), + &client_fd, msg.as_bytes(), SendFlags::empty(), Some(server_addr.clone()), @@ -2729,42 +2721,37 @@ mod tests { .expect("failed to sendto"); // Client implicitly bound to an ephemeral port via sendto - let SocketAddress::Inet(client_addr) = task - .do_getsockname(&typed_socket(&task, client_fd)) - .expect("getsockname failed") + let SocketAddress::Inet(client_addr) = + task.do_getsockname(&client_fd).expect("getsockname failed") else { panic!("Expected Inet socket address"); }; assert_ne!(client_addr.port(), 0); // Client connects to server address - task.do_connect(&typed_socket(&task, client_fd), server_addr.clone()) + task.do_connect(&client_fd, server_addr.clone()) .expect("failed to connect"); // Now client can send without specifying addr let msg = "Hello with connect()"; - task.do_sendto( - &typed_socket(&task, client_fd), - msg.as_bytes(), - SendFlags::empty(), - None, - ) - .expect("failed to sendto"); + task.do_sendto(&client_fd, msg.as_bytes(), SendFlags::empty(), None) + .expect("failed to sendto"); - close_socket(&task, client_fd); + close_socket(&task, raw_client_fd); } #[test] fn test_tun_tcp_sockopt() { let task = init_platform(Some(TUN_DEVICE_NAME)); - let sockfd = task + let raw_sockfd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create socket"); + let sockfd = typed_socket(&task, raw_sockfd); let mut congestion_name = [0u8; 16]; let optlen = task .do_getsockopt( - &typed_socket(&task, sockfd), + &sockfd, SocketOptionName::TCP(TcpOption::CONGESTION), UserPtrMut::from_usize(congestion_name.as_mut_ptr() as usize), congestion_name.len().trunc(), @@ -2777,7 +2764,7 @@ mod tests { ); task.do_setsockopt( - &typed_socket(&task, sockfd), + &sockfd, SocketOptionName::TCP(TcpOption::CONGESTION), UserPtr::from_usize(congestion_name.as_ptr() as usize), optlen, @@ -2787,7 +2774,7 @@ mod tests { let congestion_name = b"cubic\0"; let err = task .do_setsockopt( - &typed_socket(&task, sockfd), + &sockfd, SocketOptionName::TCP(TcpOption::CONGESTION), UserPtr::from_usize(congestion_name.as_ptr() as usize), congestion_name.len(), @@ -2798,7 +2785,7 @@ mod tests { let val: u32 = 1; let optval = UserPtr::from_usize((&raw const val).cast::() as usize); task.do_setsockopt( - &typed_socket(&task, sockfd), + &sockfd, SocketOptionName::Socket(SocketOption::KEEPALIVE), optval, core::mem::size_of::(), @@ -2810,7 +2797,7 @@ mod tests { let optval_out = UserPtrMut::from_usize((&raw mut result).cast::() as usize); let len = task .do_getsockopt( - &typed_socket(&task, sockfd), + &sockfd, SocketOptionName::Socket(SocketOption::KEEPALIVE), optval_out, core::mem::size_of::().trunc(), @@ -2824,16 +2811,17 @@ mod tests { #[test] fn test_tun_tcp_so_error_network_unreachable() { let task = init_platform(Some(TUN_DEVICE_NAME)); - let sockfd = task + let raw_sockfd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create socket"); + let sockfd = typed_socket(&task, raw_sockfd); // Connect to an off-subnet IP (TEST-NET, 192.0.2.1). // smoltcp does not report errors when route table lookup fails. Instead, it just dicards the packets. // Our current implementation returns `ETIMEDOUT` instead of `ENETUNREACH`. let err = task .do_connect( - &typed_socket(&task, sockfd), + &sockfd, SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([192, 0, 2, 1]), SERVER_PORT, @@ -2842,10 +2830,10 @@ mod tests { .unwrap_err(); assert_eq!(err, Errno::ETIMEDOUT); - let so_err = get_so_error(&task, sockfd); + let so_err = get_so_error(&task, &sockfd); assert_eq!(so_err, i32::from(Errno::ETIMEDOUT).cast_unsigned()); - close_socket(&task, sockfd); + close_socket(&task, raw_sockfd); } #[test] @@ -2932,13 +2920,14 @@ mod unix_tests { addr: &str, flags: SockFlags, ) -> Result { - let server_fd = create_unix_socket(task, SockType::Stream, flags); + let raw_server_fd = create_unix_socket(task, SockType::Stream, flags); + let server_fd = typed_socket(task, raw_server_fd); task.do_bind( - &typed_socket(task, server_fd), + &server_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), )?; - task.do_listen(&typed_socket(task, server_fd), 1)?; - Ok(server_fd) + task.do_listen(&server_fd, 1)?; + Ok(raw_server_fd) } fn close_socket(task: &TestTask, fd: u32) { @@ -2958,13 +2947,12 @@ mod unix_tests { fn recvfrom( task: &TestTask, - sockfd: u32, + socket: &crate::syscalls::file::AnyTypedFd, buf: &mut [u8], flags: ReceiveFlags, source_addr: Option<&mut Option>, ) -> Result { - let socket = typed_socket(task, sockfd); - task.do_recvfrom(&socket, buf, flags, source_addr) + task.do_recvfrom(socket, buf, flags, source_addr) } fn ppoll(task: &TestTask, fd: u32, events: Events) { @@ -2995,20 +2983,22 @@ mod unix_tests { for _ in 0..10 { let server_path = "/unix_stream_socket_server.sock"; let client_path = "/unix_stream_socket_client.sock"; - let server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); - let client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd = typed_socket(&task, raw_server_fd); + let client_fd = typed_socket(&task, raw_client_fd); let server_addr = SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())); let client_addr = SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())); - task.do_bind(&typed_socket(&task, server_fd), server_addr.clone()) + task.do_bind(&server_fd, server_addr.clone()) .expect("server bind failed"); - task.do_bind(&typed_socket(&task, client_fd), client_addr.clone()) + task.do_bind(&client_fd, client_addr.clone()) .expect("client bind failed"); // send message from server to client let msg1 = "Hello from server"; let n = task .do_sendto( - &typed_socket(&task, server_fd), + &server_fd, msg1.as_bytes(), SendFlags::empty(), Some(client_addr.clone()), @@ -3020,7 +3010,7 @@ mod unix_tests { let mut source = None; let n = recvfrom( &task, - client_fd, + &client_fd, &mut buf, ReceiveFlags::empty(), Some(&mut source), @@ -3034,7 +3024,7 @@ mod unix_tests { let msg2 = "Hello from client"; let n = task .do_sendto( - &typed_socket(&task, client_fd), + &client_fd, msg2.as_bytes(), SendFlags::empty(), Some(server_addr), @@ -3046,7 +3036,7 @@ mod unix_tests { let mut source = None; let n = recvfrom( &task, - server_fd, + &server_fd, &mut buf, ReceiveFlags::empty(), Some(&mut source), @@ -3056,8 +3046,8 @@ mod unix_tests { assert_eq!(&buf[..n], b"Hello from client"); assert_eq!(source, Some(client_addr)); - close_socket(&task, server_fd); - close_socket(&task, client_fd); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_client_fd); task.sys_unlinkat(-1, server_path, AtFlags::empty()) .unwrap(); task.sys_unlinkat(-1, client_path, AtFlags::empty()) @@ -3071,55 +3061,44 @@ mod unix_tests { for _ in 0..10 { let addr = "/unix_stream_socket.sock"; - let server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let server_fd = typed_socket(&task, raw_server_fd); + let client_fd = typed_socket(&task, raw_client_fd); task.do_connect( - &typed_socket(&task, client_fd), + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); let mut peer_addr = SocketAddress::default(); - let server_conn = task - .do_accept( - &typed_socket(&task, server_fd), - Some(&mut peer_addr), - SockFlags::empty(), - ) + let raw_server_conn = task + .do_accept(&server_fd, Some(&mut peer_addr), SockFlags::empty()) .unwrap(); + let server_conn = typed_socket(&task, raw_server_conn); assert!(matches!( peer_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); let msg1 = "Hello, "; let n = task - .do_sendto( - &typed_socket(&task, server_conn), - msg1.as_bytes(), - SendFlags::empty(), - None, - ) + .do_sendto(&server_conn, msg1.as_bytes(), SendFlags::empty(), None) .expect("sendto failed"); assert_eq!(n, msg1.len()); let msg2 = "world!"; let n = task - .do_sendto( - &typed_socket(&task, server_conn), - msg2.as_bytes(), - SendFlags::empty(), - None, - ) + .do_sendto(&server_conn, msg2.as_bytes(), SendFlags::empty(), None) .expect("sendto failed"); assert_eq!(n, msg2.len()); let mut buf = [0u8; 64]; - let n = recvfrom(&task, client_fd, &mut buf, ReceiveFlags::empty(), None) + let n = recvfrom(&task, &client_fd, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(n, msg1.len() + msg2.len()); assert_eq!(&buf[..n], b"Hello, world!"); - close_socket(&task, server_fd); - close_socket(&task, client_fd); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_client_fd); task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); } } @@ -3127,56 +3106,60 @@ mod unix_tests { #[test] fn test_unix_stream_socket_refused() { let task = init_platform(None); - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client_fd = typed_socket(&task, raw_client_fd); let addr = "/unix_stream_socket_refused.sock"; let result = task.do_connect( - &typed_socket(&task, client_fd), + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert_eq!(result.unwrap_err(), Errno::ECONNREFUSED); - close_socket(&task, client_fd); + close_socket(&task, raw_client_fd); - let server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client_fd = typed_socket(&task, raw_client_fd); let result = task.do_connect( - &typed_socket(&task, client_fd), + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert!(result.is_ok()); // close the server socket - close_socket(&task, server_fd); + close_socket(&task, raw_server_fd); - let another_client = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_another_client = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let another_client = typed_socket(&task, raw_another_client); let result = task.do_connect( - &typed_socket(&task, another_client), + &another_client, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert_eq!(result.unwrap_err(), Errno::ECONNREFUSED); - close_socket(&task, another_client); - close_socket(&task, client_fd); + close_socket(&task, raw_another_client); + close_socket(&task, raw_client_fd); let addr = "/unix_stream_socket_refused2.sock"; - let server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client_fd = typed_socket(&task, raw_client_fd); // remove the sock file task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); let result = task.do_connect( - &typed_socket(&task, client_fd), + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert_eq!(result.unwrap_err(), Errno::ENOENT); - close_socket(&task, server_fd); - close_socket(&task, client_fd); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_client_fd); } fn test_multiple_unix_stream_connections(is_nonblocking: bool) { let task = init_platform(None); let addr = "/unix_multi_stream_socket.sock"; - let server_fd = create_unix_server_socket( + let raw_server_fd = create_unix_server_socket( &task, addr, if is_nonblocking { @@ -3186,11 +3169,12 @@ mod unix_tests { }, ) .unwrap(); + let server_fd = typed_socket(&task, raw_server_fd); let client = task.spawn_clone_for_test(move |task| { let mut client_fds = Vec::new(); for _ in 0..10 { - let client_fd = create_unix_socket( + let raw_client_fd = create_unix_socket( &task, SockType::Stream, if is_nonblocking { @@ -3199,43 +3183,39 @@ mod unix_tests { SockFlags::empty() }, ); + let client_fd = typed_socket(&task, raw_client_fd); if is_nonblocking { - ppoll(&task, server_fd, Events::OUT); + ppoll(&task, raw_server_fd, Events::OUT); } task.do_connect( - &typed_socket(&task, client_fd), + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); - client_fds.push(client_fd); + client_fds.push((raw_client_fd, client_fd)); } - for (i, client_fd) in client_fds.iter().enumerate() { + for (i, (_, client_fd)) in client_fds.iter().enumerate() { let msg = alloc::format!("message from connection {i}"); let n = task - .do_sendto( - &typed_socket(&task, *client_fd), - msg.as_bytes(), - SendFlags::empty(), - None, - ) + .do_sendto(client_fd, msg.as_bytes(), SendFlags::empty(), None) .expect("sendto failed"); assert_eq!(n, msg.len()); } - for client_fd in client_fds { - close_socket(&task, client_fd); + for (raw_client_fd, _) in client_fds { + close_socket(&task, raw_client_fd); } }); - let mut server_conn_fds = Vec::new(); + let mut raw_server_conn_fds = Vec::new(); for _ in 0..10 { if is_nonblocking { - ppoll(&task, server_fd, Events::IN); + ppoll(&task, raw_server_fd, Events::IN); } - let server_conn = task + let raw_server_conn_fd = task .do_accept( - &typed_socket(&task, server_fd), + &server_fd, None, if is_nonblocking { SockFlags::NONBLOCK @@ -3244,18 +3224,19 @@ mod unix_tests { }, ) .unwrap(); - server_conn_fds.push(server_conn); + raw_server_conn_fds.push(raw_server_conn_fd); } - for (i, server_conn_fd) in server_conn_fds.iter().enumerate() { + for (i, raw_server_conn_fd) in raw_server_conn_fds.iter().enumerate() { let msg = alloc::format!("message from connection {i}"); let mut buf = [0u8; 64]; if is_nonblocking { - ppoll(&task, *server_conn_fd, Events::IN); + ppoll(&task, *raw_server_conn_fd, Events::IN); } + let server_conn_fd = typed_socket(&task, *raw_server_conn_fd); let n = recvfrom( &task, - *server_conn_fd, + &server_conn_fd, &mut buf, ReceiveFlags::empty(), None, @@ -3265,10 +3246,10 @@ mod unix_tests { assert_eq!(&buf[..n], msg.as_bytes()); } - for server_conn_fd in server_conn_fds { - close_socket(&task, server_conn_fd); + for raw_server_conn_fd in raw_server_conn_fds { + close_socket(&task, raw_server_conn_fd); } - close_socket(&task, server_fd); + close_socket(&task, raw_server_fd); client.join().unwrap(); } @@ -3287,43 +3268,49 @@ mod unix_tests { let task = init_platform(None); for _ in 0..10 { let addr = "/unix_stream_socket_server.sock"; - let server1_fd = create_unix_server_socket(&task, addr, SockFlags::NONBLOCK).unwrap(); + let raw_server1_fd = + create_unix_server_socket(&task, addr, SockFlags::NONBLOCK).unwrap(); + let server1_fd = typed_socket(&task, raw_server1_fd); let err = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap_err(); assert_eq!(err, Errno::EADDRINUSE); // remove the socket file to allow another server to bind to the same address task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); - let server2_fd = create_unix_server_socket(&task, addr, SockFlags::NONBLOCK).unwrap(); + let raw_server2_fd = + create_unix_server_socket(&task, addr, SockFlags::NONBLOCK).unwrap(); + let server2_fd = typed_socket(&task, raw_server2_fd); - let client1_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_client1_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client1_fd = typed_socket(&task, raw_client1_fd); task.do_connect( - &typed_socket(&task, client1_fd), + &client1_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); // server one is still alive but cannot accept connections let err = task - .do_accept(&typed_socket(&task, server1_fd), None, SockFlags::empty()) + .do_accept(&server1_fd, None, SockFlags::empty()) .unwrap_err(); assert_eq!(err, Errno::EAGAIN); let conn_fd = task - .do_accept(&typed_socket(&task, server2_fd), None, SockFlags::empty()) + .do_accept(&server2_fd, None, SockFlags::empty()) .unwrap(); close_socket(&task, conn_fd); - close_socket(&task, client1_fd); + close_socket(&task, raw_client1_fd); // close server one and connect again - close_socket(&task, server1_fd); - let client2_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + close_socket(&task, raw_server1_fd); + let raw_client2_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client2_fd = typed_socket(&task, raw_client2_fd); task.do_connect( - &typed_socket(&task, client2_fd), + &client2_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); - close_socket(&task, client2_fd); - close_socket(&task, server2_fd); + close_socket(&task, raw_client2_fd); + close_socket(&task, raw_server2_fd); // still fail after we close the server let err = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap_err(); @@ -3338,32 +3325,35 @@ mod unix_tests { let task = init_platform(None); for _ in 0..10 { let addr = "/unix_datagram_socket_server.sock"; - let server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd = typed_socket(&task, raw_server_fd); task.do_bind( - &typed_socket(&task, server_fd), + &server_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); - let server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd2 = typed_socket(&task, raw_server_fd2); let err = task .do_bind( - &typed_socket(&task, server_fd2), + &server_fd2, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap_err(); assert_eq!(err, Errno::EADDRINUSE); task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); - let server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd2 = typed_socket(&task, raw_server_fd2); task.do_bind( - &typed_socket(&task, server_fd2), + &server_fd2, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); - close_socket(&task, server_fd); - close_socket(&task, server_fd2); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_server_fd2); task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); } } @@ -3381,16 +3371,19 @@ mod unix_tests { task.sys_socketpair(AddressFamily::UNIX as u32, ty_and_flags, 0, sv_mut_ptr) .unwrap(); - let sock1 = sv_ptr[0]; - let sock2 = sv_ptr[1]; + let raw_sock1 = sv_ptr[0]; + let raw_sock2 = sv_ptr[1]; + let sock1 = typed_socket(&task, raw_sock1); + let sock2 = typed_socket(&task, raw_sock2); // Receive on sock2 (from sock1) let receiver2 = task.spawn_clone_for_test(move |task| { let mut buf = [0u8; 64]; if is_nonblocking { - ppoll(&task, sock2, Events::IN); + ppoll(&task, raw_sock2, Events::IN); } - let n = recvfrom(&task, sock2, &mut buf, ReceiveFlags::empty(), None) + let sock2 = typed_socket(&task, raw_sock2); + let n = recvfrom(&task, &sock2, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(&buf[..n], b"Message from sock1"); }); @@ -3398,21 +3391,17 @@ mod unix_tests { std::thread::sleep(core::time::Duration::from_millis(100)); // Send from sock1 to sock2 let msg1 = "Message from sock1"; - task.do_sendto( - &typed_socket(&task, sock1), - msg1.as_bytes(), - SendFlags::empty(), - None, - ) - .expect("sendto failed"); + task.do_sendto(&sock1, msg1.as_bytes(), SendFlags::empty(), None) + .expect("sendto failed"); let receiver1 = task.spawn_clone_for_test(move |task| { // Receive on sock1 (from sock2) let mut buf = [0u8; 64]; if is_nonblocking { - ppoll(&task, sock1, Events::IN); + ppoll(&task, raw_sock1, Events::IN); } - let n = recvfrom(&task, sock1, &mut buf, ReceiveFlags::empty(), None) + let sock1 = typed_socket(&task, raw_sock1); + let n = recvfrom(&task, &sock1, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(&buf[..n], b"Message from sock2"); }); @@ -3420,17 +3409,12 @@ mod unix_tests { std::thread::sleep(core::time::Duration::from_millis(100)); // Send from sock2 to sock1 let msg2 = "Message from sock2"; - task.do_sendto( - &typed_socket(&task, sock2), - msg2.as_bytes(), - SendFlags::empty(), - None, - ) - .expect("sendto failed"); + task.do_sendto(&sock2, msg2.as_bytes(), SendFlags::empty(), None) + .expect("sendto failed"); std::thread::sleep(core::time::Duration::from_millis(500)); - close_socket(&task, sock1); - close_socket(&task, sock2); + close_socket(&task, raw_sock1); + close_socket(&task, raw_sock2); receiver2.join().unwrap(); receiver1.join().unwrap(); } @@ -3471,14 +3455,15 @@ mod unix_tests { fn unix_socket_recv_timeout(ty: SockType) { let task = init_platform(None); - let (sock1, _sock2) = task + let (raw_sock1, _raw_sock2) = task .do_socketpair(AddressFamily::UNIX, ty, SockFlags::empty(), 0) .expect("socketpair failed"); + let sock1 = typed_socket(&task, raw_sock1); let timeout = Duration::from_millis(200); let tv = litebox_common_linux::TimeVal::from(timeout); let optval = UserPtr::from_usize((&raw const tv).cast::() as usize); task.do_setsockopt( - &typed_socket(&task, sock1), + &sock1, SocketOptionName::Socket(SocketOption::RCVTIMEO), optval, core::mem::size_of::(), @@ -3486,7 +3471,7 @@ mod unix_tests { .expect("Failed to set SO_RCVTIMEO"); let mut buf = [0u8; 16]; let start = std::time::Instant::now(); - let err = recvfrom(&task, sock1, &mut buf, ReceiveFlags::empty(), None).unwrap_err(); + let err = recvfrom(&task, &sock1, &mut buf, ReceiveFlags::empty(), None).unwrap_err(); let elapsed = start.elapsed(); // Linux returns EAGAIN (not ETIMEDOUT) when SO_RCVTIMEO expires on a blocking recv. assert_eq!(err, Errno::EAGAIN); @@ -3507,24 +3492,23 @@ mod unix_tests { fn test_unix_stream_addr() { let task = init_platform(None); let server_path = "/unix_stream_sockname.sock"; - let server_fd = create_unix_server_socket(&task, server_path, SockFlags::empty()).unwrap(); + let raw_server_fd = + create_unix_server_socket(&task, server_path, SockFlags::empty()).unwrap(); + let server_fd = typed_socket(&task, raw_server_fd); // Server socket should have its bound address - let server_addr = task - .do_getsockname(&typed_socket(&task, server_fd)) - .unwrap(); + let server_addr = task.do_getsockname(&server_fd).unwrap(); assert_eq!( server_addr, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Create client and connect - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client_fd = typed_socket(&task, raw_client_fd); // Before connect, client should have unnamed address - let client_addr = task - .do_getsockname(&typed_socket(&task, client_fd)) - .unwrap(); + let client_addr = task.do_getsockname(&client_fd).unwrap(); assert!(matches!( client_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) @@ -3532,55 +3516,48 @@ mod unix_tests { // Connect client to server task.do_connect( - &typed_socket(&task, client_fd), + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())), ) .unwrap(); // After connect, client's getsockname should still be unnamed - let client_local_addr = task - .do_getsockname(&typed_socket(&task, client_fd)) - .unwrap(); + let client_local_addr = task.do_getsockname(&client_fd).unwrap(); assert!(matches!( client_local_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); // Client's getpeername should return server's address - let client_peer_addr = task - .do_getpeername(&typed_socket(&task, client_fd)) - .unwrap(); + let client_peer_addr = task.do_getpeername(&client_fd).unwrap(); assert_eq!( client_peer_addr, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Accept connection on server - let server_conn = task - .do_accept(&typed_socket(&task, server_fd), None, SockFlags::empty()) + let raw_server_conn = task + .do_accept(&server_fd, None, SockFlags::empty()) .unwrap(); + let server_conn = typed_socket(&task, raw_server_conn); // Server connection's local address should be the server's bound address - let server_conn_local = task - .do_getsockname(&typed_socket(&task, server_conn)) - .unwrap(); + let server_conn_local = task.do_getsockname(&server_conn).unwrap(); assert_eq!( server_conn_local, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Server connection's peer address should be unnamed (client didn't bind) - let server_conn_peer = task - .do_getpeername(&typed_socket(&task, server_conn)) - .unwrap(); + let server_conn_peer = task.do_getpeername(&server_conn).unwrap(); assert!(matches!( server_conn_peer, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); - close_socket(&task, client_fd); - close_socket(&task, server_conn); - close_socket(&task, server_fd); + close_socket(&task, raw_client_fd); + close_socket(&task, raw_server_conn); + close_socket(&task, raw_server_fd); task.sys_unlinkat(-1, server_path, AtFlags::empty()) .unwrap(); } @@ -3591,21 +3568,19 @@ mod unix_tests { let server_path = "/unix_datagram_sockname_server.sock"; let client_path = "/unix_datagram_sockname_client.sock"; - let server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); - let client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd = typed_socket(&task, raw_server_fd); + let client_fd = typed_socket(&task, raw_client_fd); // Before bind, both should have unnamed addresses - let server_addr = task - .do_getsockname(&typed_socket(&task, server_fd)) - .unwrap(); + let server_addr = task.do_getsockname(&server_fd).unwrap(); assert!(matches!( server_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); - let client_addr = task - .do_getsockname(&typed_socket(&task, client_fd)) - .unwrap(); + let client_addr = task.do_getsockname(&client_fd).unwrap(); assert!(matches!( client_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) @@ -3613,15 +3588,13 @@ mod unix_tests { // Bind server task.do_bind( - &typed_socket(&task, server_fd), + &server_fd, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())), ) .unwrap(); // After bind, server should have its bound address - let server_local = task - .do_getsockname(&typed_socket(&task, server_fd)) - .unwrap(); + let server_local = task.do_getsockname(&server_fd).unwrap(); assert_eq!( server_local, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) @@ -3629,15 +3602,13 @@ mod unix_tests { // Bind client task.do_bind( - &typed_socket(&task, client_fd), + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())), ) .unwrap(); // After bind, client should have its bound address - let client_local = task - .do_getsockname(&typed_socket(&task, client_fd)) - .unwrap(); + let client_local = task.do_getsockname(&client_fd).unwrap(); assert_eq!( client_local, SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())) @@ -3645,35 +3616,31 @@ mod unix_tests { // Connect client to server task.do_connect( - &typed_socket(&task, client_fd), + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())), ) .unwrap(); // After connect, getsockname should still return client's bound address - let client_local_after_connect = task - .do_getsockname(&typed_socket(&task, client_fd)) - .unwrap(); + let client_local_after_connect = task.do_getsockname(&client_fd).unwrap(); assert_eq!( client_local_after_connect, SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())) ); // getpeername should return server's address - let client_peer = task - .do_getpeername(&typed_socket(&task, client_fd)) - .unwrap(); + let client_peer = task.do_getpeername(&client_fd).unwrap(); assert_eq!( client_peer, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Server hasn't connected, so getpeername should fail with ENOTCONN - let server_peer_result = task.do_getpeername(&typed_socket(&task, server_fd)); + let server_peer_result = task.do_getpeername(&server_fd); assert_eq!(server_peer_result.unwrap_err(), Errno::ENOTCONN); - close_socket(&task, server_fd); - close_socket(&task, client_fd); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_client_fd); task.sys_unlinkat(-1, server_path, AtFlags::empty()) .unwrap(); task.sys_unlinkat(-1, client_path, AtFlags::empty()) From 9ef17ffe51298eabfaf2d9e951c37ba57575ab6d Mon Sep 17 00:00:00 2001 From: Weiteng Chen Date: Sat, 5 Sep 2026 00:50:10 +0000 Subject: [PATCH 3/3] clean up code --- litebox_shim_linux/src/syscalls/net.rs | 119 +++++++------------------ 1 file changed, 31 insertions(+), 88 deletions(-) diff --git a/litebox_shim_linux/src/syscalls/net.rs b/litebox_shim_linux/src/syscalls/net.rs index dcb7cf073..33cad6935 100644 --- a/litebox_shim_linux/src/syscalls/net.rs +++ b/litebox_shim_linux/src/syscalls/net.rs @@ -2285,12 +2285,8 @@ mod tests { h }; assert_eq!( - task.sys_sendmsg( - i32::try_from(raw_client_fd).unwrap(), - UserPtr::from_usize(&raw const hdr as usize), - SendFlags::empty(), - ) - .expect("Failed to sendmsg"), + task.do_sendmsg(&client_fd, &hdr, SendFlags::empty()) + .expect("Failed to sendmsg"), buf1.len() + buf2.len() ); let output = child_handle @@ -2319,14 +2315,7 @@ mod tests { }; let n = match option { "recvfrom" => task - .sys_recvfrom( - i32::try_from(raw_client_fd).unwrap(), - UserPtrMut::from_usize(recv_buf.as_mut_ptr() as usize), - recv_buf.len(), - flags, - None, - UserPtrMut::from_usize(0), - ) + .do_recvfrom(&client_fd, &mut recv_buf, flags, None) .expect("Failed to receive data"), "recvmsg" => { let iovec = [litebox_common_linux::IoVec { @@ -2853,42 +2842,6 @@ mod tests { close_socket(&task, socket_fd); close_socket(&task, socket_fd2); } - - #[test] - fn socket_from_raw_rejects_negative_fd() { - let task = init_platform(None); - assert!(matches!( - task.files.borrow().socket_from_raw(-1), - Err(Errno::EBADF) - )); - } - - #[test] - fn typed_socket_does_not_follow_raw_fd_reuse() { - let task = init_platform(None); - let socket_fd = task - .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) - .unwrap(); - let socket = task - .files - .borrow() - .socket_from_raw(i32::try_from(socket_fd).unwrap()) - .unwrap(); - - close_socket(&task, socket_fd); - let replacement = task - .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) - .unwrap(); - assert_eq!(replacement, socket_fd); - - let result = task.files.borrow().with_typed_socket( - &task.global, - &socket, - |fd| task.global.get_proxy(fd).map(|_| ()), - |_| unreachable!(), - ); - assert_eq!(result, Err(Errno::EBADF)); - } } #[cfg(test)] @@ -2945,16 +2898,6 @@ mod unix_tests { .unwrap() } - fn recvfrom( - task: &TestTask, - socket: &crate::syscalls::file::AnyTypedFd, - buf: &mut [u8], - flags: ReceiveFlags, - source_addr: Option<&mut Option>, - ) -> Result { - task.do_recvfrom(socket, buf, flags, source_addr) - } - fn ppoll(task: &TestTask, fd: u32, events: Events) { let fd = i32::try_from(fd).unwrap(); let mut pollfd = [litebox_common_linux::Pollfd { @@ -3008,14 +2951,14 @@ mod unix_tests { let mut buf = [0u8; 64]; let mut source = None; - let n = recvfrom( - &task, - &client_fd, - &mut buf, - ReceiveFlags::empty(), - Some(&mut source), - ) - .expect("recvfrom failed"); + let n = task + .do_recvfrom( + &client_fd, + &mut buf, + ReceiveFlags::empty(), + Some(&mut source), + ) + .expect("recvfrom failed"); assert_eq!(n, msg1.len()); assert_eq!(&buf[..n], b"Hello from server"); assert_eq!(source, Some(server_addr.clone())); @@ -3034,14 +2977,14 @@ mod unix_tests { let mut buf = [0u8; 64]; let mut source = None; - let n = recvfrom( - &task, - &server_fd, - &mut buf, - ReceiveFlags::empty(), - Some(&mut source), - ) - .expect("recvfrom failed"); + let n = task + .do_recvfrom( + &server_fd, + &mut buf, + ReceiveFlags::empty(), + Some(&mut source), + ) + .expect("recvfrom failed"); assert_eq!(n, msg2.len()); assert_eq!(&buf[..n], b"Hello from client"); assert_eq!(source, Some(client_addr)); @@ -3092,7 +3035,8 @@ mod unix_tests { assert_eq!(n, msg2.len()); let mut buf = [0u8; 64]; - let n = recvfrom(&task, &client_fd, &mut buf, ReceiveFlags::empty(), None) + let n = task + .do_recvfrom(&client_fd, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(n, msg1.len() + msg2.len()); assert_eq!(&buf[..n], b"Hello, world!"); @@ -3234,14 +3178,9 @@ mod unix_tests { ppoll(&task, *raw_server_conn_fd, Events::IN); } let server_conn_fd = typed_socket(&task, *raw_server_conn_fd); - let n = recvfrom( - &task, - &server_conn_fd, - &mut buf, - ReceiveFlags::empty(), - None, - ) - .expect("recvfrom failed"); + let n = task + .do_recvfrom(&server_conn_fd, &mut buf, ReceiveFlags::empty(), None) + .expect("recvfrom failed"); assert_eq!(n, msg.len()); assert_eq!(&buf[..n], msg.as_bytes()); } @@ -3383,7 +3322,8 @@ mod unix_tests { ppoll(&task, raw_sock2, Events::IN); } let sock2 = typed_socket(&task, raw_sock2); - let n = recvfrom(&task, &sock2, &mut buf, ReceiveFlags::empty(), None) + let n = task + .do_recvfrom(&sock2, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(&buf[..n], b"Message from sock1"); }); @@ -3401,7 +3341,8 @@ mod unix_tests { ppoll(&task, raw_sock1, Events::IN); } let sock1 = typed_socket(&task, raw_sock1); - let n = recvfrom(&task, &sock1, &mut buf, ReceiveFlags::empty(), None) + let n = task + .do_recvfrom(&sock1, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(&buf[..n], b"Message from sock2"); }); @@ -3471,7 +3412,9 @@ mod unix_tests { .expect("Failed to set SO_RCVTIMEO"); let mut buf = [0u8; 16]; let start = std::time::Instant::now(); - let err = recvfrom(&task, &sock1, &mut buf, ReceiveFlags::empty(), None).unwrap_err(); + let err = task + .do_recvfrom(&sock1, &mut buf, ReceiveFlags::empty(), None) + .unwrap_err(); let elapsed = start.elapsed(); // Linux returns EAGAIN (not ETIMEDOUT) when SO_RCVTIMEO expires on a blocking recv. assert_eq!(err, Errno::EAGAIN);