From 1546a4a08f6a36db4bce92b8d695a48a92c0fa23 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 14 Oct 2023 12:44:38 +0200 Subject: [PATCH] Implement SYS_SENDFD and SKMSG_FOBTAINFD. --- src/scheme/mod.rs | 4 +++ src/scheme/user.rs | 63 ++++++++++++++++++++++++++++++++------------ src/syscall/debug.rs | 8 ++++++ src/syscall/fs.rs | 16 +++++++++++ src/syscall/mod.rs | 7 +++++ syscall | 2 +- 6 files changed, 82 insertions(+), 18 deletions(-) diff --git a/src/scheme/mod.rs b/src/scheme/mod.rs index 6418b8f023..6d6acd68ef 100644 --- a/src/scheme/mod.rs +++ b/src/scheme/mod.rs @@ -342,6 +342,10 @@ pub trait KernelScheme: Scheme + Send + Sync + 'static { Err(Error::new(EBADF)) } + fn ksendfd(&self, id: usize, desc: Arc>, flags: usize, arg: u64) -> Result { + Err(Error::new(EOPNOTSUPP)) + } + // TODO: This demonstrates why we need to transition away from a dyn trait. fn as_user_inner(&self) -> Option>> { None } } diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 86b322cdd3..944c7a444e 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -3,7 +3,8 @@ use alloc::sync::{Arc, Weak}; use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::vec::Vec; -use syscall::{SKMSG_FRETURNFD, CallerCtx, SKMSG_PROVIDE_MMAP, MAP_FIXED_NOREPLACE, MunmapFlags}; +use syscall::{SKMSG_FRETURNFD, CallerCtx, SKMSG_PROVIDE_MMAP, MAP_FIXED_NOREPLACE, MunmapFlags, SKMSG_FOBTAINFD, FobtainFdFlags}; +use core::mem::size_of; use core::num::NonZeroUsize; use core::sync::atomic::{AtomicBool, Ordering}; use core::{mem, usize}; @@ -12,7 +13,7 @@ use spin::{Mutex, RwLock}; use crate::context::context::HardBlockedReason; use crate::context::{self, Context, BorrowedHtBuf, Status}; -use crate::context::file::FileDescription; +use crate::context::file::{FileDescription, FileDescriptor}; use crate::context::memory::{AddrSpace, DANGLING, Grant, GrantFileRef, PageSpan, MmapMode, BorrowedFmapSource}; use crate::event; use crate::memory::Frame; @@ -43,7 +44,7 @@ pub struct UserInner { } enum State { - Waiting(Weak>), + Waiting { context: Weak>, fd: Option>> }, Responded(Response), Fmap(Weak>), Placeholder, @@ -95,7 +96,7 @@ impl UserInner { } pub fn call(&self, a: usize, b: usize, c: usize, d: usize) -> Result { - match self.call_extended(current_caller_ctx()?, [a, b, c, d])? { + match self.call_extended(current_caller_ctx()?, None, [a, b, c, d])? { Response::Regular(code) => Error::demux(code), Response::Fd(_) => { if a & SYS_RET_FILE == SYS_RET_FILE { @@ -107,8 +108,8 @@ impl UserInner { } } - pub fn call_extended(&self, ctx: CallerCtx, [a, b, c, d]: [usize; 4]) -> Result { - self.call_extended_inner(Packet { + pub fn call_extended(&self, ctx: CallerCtx, fd: Option>>, [a, b, c, d]: [usize; 4]) -> Result { + self.call_extended_inner(fd, Packet { id: self.next_id(), pid: ctx.pid, uid: ctx.uid, @@ -120,7 +121,7 @@ impl UserInner { }) } - fn call_extended_inner(&self, packet: Packet) -> Result { + fn call_extended_inner(&self, fd: Option>>, packet: Packet) -> Result { if self.unmounting.load(Ordering::SeqCst) { return Err(Error::new(ENODEV)); } @@ -132,7 +133,7 @@ impl UserInner { { let mut states = self.states.lock(); current_context.write().block("UserScheme::call"); - states.insert(id, State::Waiting(Arc::downgrade(¤t_context))); + states.insert(id, State::Waiting { context: Arc::downgrade(¤t_context), fd }); } self.todo.send(packet); @@ -149,7 +150,7 @@ impl UserInner { Entry::Vacant(_) => return Err(Error::new(EBADFD)), Entry::Occupied(mut o) => match mem::replace(o.get_mut(), State::Placeholder) { // spurious wakeup, TODO: EINTR - old_state @ State::Waiting(_) => { + old_state @ State::Waiting { .. } => { *o.get_mut() = old_state; continue; } @@ -395,7 +396,7 @@ impl UserInner { pub fn write(&self, buf: UserSliceRo) -> Result { let mut packets_read = 0; - for chunk in buf.in_exact_chunks(mem::size_of::()) { + for chunk in buf.in_exact_chunks(size_of::()) { match self.handle_packet(&unsafe { chunk.read_exact::()? }) { Ok(()) => packets_read += 1, Err(_) if packets_read > 0 => break, @@ -403,7 +404,7 @@ impl UserInner { } } - Ok(packets_read * mem::size_of::()) + Ok(packets_read * size_of::()) } pub fn request_fmap(&self, id: usize, offset: u64, required_page_count: usize, flags: MapFlags) -> Result<()> { log::info!("REQUEST FMAP"); @@ -444,6 +445,20 @@ impl UserInner { self.respond(packet.id, Response::Fd(desc))?; } + SKMSG_FOBTAINFD => { + let flags = FobtainFdFlags::from_bits(packet.d).ok_or(Error::new(EINVAL))?; + let description = match self.states.lock().get_mut(&packet.id).ok_or(Error::new(EINVAL))? { + State::Waiting { ref mut fd, .. } => fd.take().ok_or(Error::new(ENOENT))?, + _ => return Err(Error::new(ENOENT)), + }; + + if flags.contains(FobtainFdFlags::MANUAL_FD) { + context::current()?.read().insert_file(FileHandle::from(packet.c), FileDescriptor { description, cloexec: true }); + } else { + let fd = context::current()?.read().add_file(FileDescriptor { description, cloexec: true }).ok_or(Error::new(EMFILE))?; + UserSlice::wo(packet.c, size_of::())?.write_usize(fd.get())?; + } + } SKMSG_PROVIDE_MMAP => { log::info!("PROVIDE_MAP {:?}", packet); let offset = u64::from(packet.uid) | (u64::from(packet.gid) << 32); @@ -468,7 +483,7 @@ impl UserInner { return Err(Error::new(EBADFD)); } // invalid kernel to scheme call - old_state @ (State::Waiting(_) | State::Responded(_)) => { + old_state @ (State::Waiting { .. } | State::Responded(_)) => { *o.get_mut() = old_state; return Err(Error::new(EINVAL)); } @@ -511,7 +526,7 @@ impl UserInner { return Err(Error::new(EINVAL)); } - State::Waiting(context) => { + State::Waiting { context, .. } => { if let Some(context) = context.upgrade() { context.write().unblock(); *o.get_mut() = State::Responded(response); @@ -579,7 +594,7 @@ impl UserInner { (context.id, desc.description) }; - let response = self.call_extended_inner(Packet { + let response = self.call_extended_inner(None, Packet { id: self.next_id(), pid: pid.into(), a: KSMSG_MMAP_PREP, @@ -867,7 +882,7 @@ impl KernelScheme for UserScheme { fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.copy_and_capture_tail(path.as_bytes())?; - match inner.call_extended(ctx, [SYS_OPEN, address.base(), address.len(), flags])? { + match inner.call_extended(ctx, None, [SYS_OPEN, address.base(), address.len(), flags])? { Response::Regular(code) => Error::demux(code).map(OpenResult::SchemeLocal), Response::Fd(desc) => Ok(OpenResult::External(desc)), } @@ -875,7 +890,7 @@ impl KernelScheme for UserScheme { fn kdup(&self, file: usize, buf: UserSliceRo, ctx: CallerCtx) -> Result { let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; let address = inner.capture_user(buf)?; - let result = inner.call_extended(ctx, [SYS_DUP, file, address.base(), address.len()]); + let result = inner.call_extended(ctx, None, [SYS_DUP, file, address.base(), address.len()]); address.release()?; @@ -946,13 +961,27 @@ impl KernelScheme for UserScheme { #[cfg(not(target_pointer_width = "64"))] gid: 0, - }, [KSMSG_MUNMAP, number, size, flags.bits()])?; + }, None, [KSMSG_MUNMAP, number, size, flags.bits()])?; match res { Response::Regular(_) => Ok(()), Response::Fd(_) => Err(Error::new(EIO)), } } + fn ksendfd(&self, number: usize, desc: Arc>, flags: usize, arg: u64) -> Result { + let inner = self.inner.upgrade().ok_or(Error::new(ENODEV))?; + + let res = inner.call_extended(CallerCtx { + pid: context::context_id().into(), + uid: arg as u32, + gid: (arg >> 32) as u32, + }, Some(desc), [SYS_SENDFD, number, flags, 0])?; + + match res { + Response::Regular(res) => Ok(res), + Response::Fd(_) => Err(Error::new(EIO)), + } + } fn as_user_inner(&self) -> Option>> { Some(self.inner.upgrade().ok_or(Error::new(ENODEV))) diff --git a/src/syscall/debug.rs b/src/syscall/debug.rs index 1fb6472c6c..baed3fed85 100644 --- a/src/syscall/debug.rs +++ b/src/syscall/debug.rs @@ -69,6 +69,14 @@ pub fn format_call(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) - c, debug_buf(d, e).as_ref().map(|b| ByteStr(&*b)), ), + SYS_SENDFD => format!( + "sendfd({}, {}, {:#0x} {:#0x} {:#0x})", + b, + c, + d, + e, + f, + ), SYS_READ => format!( "read({}, {:#X}, {})", b, diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index 0751ef3ce0..80c0fef8ef 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -235,6 +235,22 @@ pub fn dup2(fd: FileHandle, new_fd: FileHandle, buf: UserSliceRo) -> Result Result { + let (scheme, number, desc_to_send) = { + let current_lock = context::current()?; + let current = current_lock.read(); + + // TODO: Ensure deadlocks can't happen + + let (scheme, number) = match current.get_file(socket).ok_or(Error::new(EBADF))?.description.read() { + ref desc => (desc.scheme, desc.number), + }; + let scheme = Arc::clone(scheme::schemes().get(scheme).ok_or(Error::new(ENODEV))?); + + (scheme, number, current.remove_file(fd).ok_or(Error::new(EBADF))?.description) + }; + scheme.ksendfd(number, desc_to_send, flags, arg) +} /// File descriptor controls pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result { diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 3ac81becaf..9db10062a7 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -99,6 +99,13 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack _ => match a { SYS_DUP => dup(fd, UserSlice::ro(c, d)?).map(FileHandle::into), SYS_DUP2 => dup2(fd, FileHandle::from(c), UserSlice::ro(d, e)?).map(FileHandle::into), + + #[cfg(target_pointer_width = "32")] + SYS_SENDFD => sendfd(fd, FileHandle::from(c), d, e as u64 | ((f as u64) << 32)), + + #[cfg(target_pointer_width = "64")] + SYS_SENDFD => sendfd(fd, FileHandle::from(c), d, e as u64), + SYS_LSEEK => file_op_generic(fd, |scheme, _, number| Ok(scheme.seek(number, c as isize, d)? as usize)), SYS_FCHMOD => file_op_generic(fd, |scheme, _, number| scheme.fchmod(number, c as u16)), SYS_FCHOWN => file_op_generic(fd, |scheme, _, number| scheme.fchown(number, c as u32, d as u32)), diff --git a/syscall b/syscall index f94a5a7dca..0200e818a4 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit f94a5a7dcafdcbcd8d43af586a637148515bc86a +Subproject commit 0200e818a4ba752c1869be9718eb7f090c19d65c