From e4c5c36be9fb58ee78e558e72762e8450bcb5ba2 Mon Sep 17 00:00:00 2001 From: jiangjianfeng Date: Wed, 11 Jun 2025 08:28:05 +0000 Subject: [PATCH] Support SO_PEERCRED & SO_PEERGROUPS --- kernel/libs/aster-rights/src/lib.rs | 1 + kernel/src/net/socket/options/mod.rs | 4 +- kernel/src/net/socket/unix/cred.rs | 87 +++++++++++++++++++ kernel/src/net/socket/unix/mod.rs | 2 + .../src/net/socket/unix/stream/connected.rs | 11 ++- kernel/src/net/socket/unix/stream/init.rs | 12 ++- kernel/src/net/socket/unix/stream/listener.rs | 14 ++- kernel/src/net/socket/unix/stream/socket.rs | 55 +++++++++++- kernel/src/net/socket/util/options.rs | 13 ++- kernel/src/process/credentials/group.rs | 5 ++ kernel/src/process/credentials/user.rs | 5 ++ kernel/src/process/posix_thread/mod.rs | 7 +- kernel/src/syscall/getsockopt.rs | 13 ++- kernel/src/util/net/options/mod.rs | 12 +-- kernel/src/util/net/options/socket.rs | 49 ++++++++++- kernel/src/util/net/options/utils.rs | 15 ++++ 16 files changed, 282 insertions(+), 23 deletions(-) create mode 100644 kernel/src/net/socket/unix/cred.rs diff --git a/kernel/libs/aster-rights/src/lib.rs b/kernel/libs/aster-rights/src/lib.rs index 96546ad6b..f2d9fa136 100644 --- a/kernel/libs/aster-rights/src/lib.rs +++ b/kernel/libs/aster-rights/src/lib.rs @@ -52,6 +52,7 @@ typeflags::typeflags! { pub type Full = TRightSet; pub type ReadOp = TRights![Read]; pub type WriteOp = TRights![Write]; +pub type ReadDupOp = TRights![Read, Dup]; pub type FullOp = TRights![Read, Write, Dup]; /// Wrapper for TRights, used to bypass an error message from the Rust compiler, diff --git a/kernel/src/net/socket/options/mod.rs b/kernel/src/net/socket/options/mod.rs index 434ca092a..9eeed359f 100644 --- a/kernel/src/net/socket/options/mod.rs +++ b/kernel/src/net/socket/options/mod.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 use super::util::LingerOption; -use crate::{impl_socket_options, prelude::*}; +use crate::{impl_socket_options, net::socket::unix::CUserCred, prelude::*, process::Gid}; mod macros; @@ -21,7 +21,9 @@ impl_socket_options!( pub struct Priority(i32); pub struct Linger(LingerOption); pub struct KeepAlive(bool); + pub struct PeerCred(CUserCred); pub struct AcceptConn(bool); pub struct SendBufForce(u32); pub struct RecvBufForce(u32); + pub struct PeerGroups(Arc<[Gid]>); ); diff --git a/kernel/src/net/socket/unix/cred.rs b/kernel/src/net/socket/unix/cred.rs new file mode 100644 index 000000000..6e8544e6e --- /dev/null +++ b/kernel/src/net/socket/unix/cred.rs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MPL-2.0 + +use aster_rights::{Dup, Read, ReadDupOp, ReadOp, TRights}; +use aster_rights_proc::require; + +use crate::{ + prelude::*, + process::{posix_thread::AsPosixThread, Credentials, Gid, Pid, Uid}, +}; + +pub(super) struct SocketCred { + pid: Pid, + cred: Credentials, +} + +impl SocketCred { + pub(super) fn new_current() -> Self { + let pid = current!().pid(); + let cred = current_thread!().as_posix_thread().unwrap().credentials(); + + Self { pid, cred } + } +} + +impl SocketCred { + pub(super) fn new_current() -> Self { + let pid = current!().pid(); + let cred = current_thread!() + .as_posix_thread() + .unwrap() + .credentials_dup(); + + Self { pid, cred } + } +} + +impl SocketCred { + #[require(R > Read)] + pub(super) fn to_c_user_cred(&self) -> CUserCred { + CUserCred { + pid: self.pid, + uid: self.cred.euid(), + gid: self.cred.egid(), + } + } + + #[require(R > Read)] + pub(super) fn groups(&self) -> Arc<[Gid]> { + self.cred.groups().iter().cloned().collect() + } + + #[require(R > R1)] + pub(super) fn restrict(self) -> SocketCred { + let Self { pid, cred } = self; + SocketCred { + pid, + cred: cred.restrict(), + } + } + + #[require(R > Dup)] + pub(super) fn dup(&self) -> Self { + Self { + pid: self.pid, + cred: self.cred.dup(), + } + } +} + +/// Reference: . +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +pub struct CUserCred { + pid: Pid, + uid: Uid, + gid: Gid, +} + +impl CUserCred { + pub(in crate::net) const fn new_unknown() -> Self { + Self { + pid: 0, + uid: Uid::INVALID, + gid: Gid::INVALID, + } + } +} diff --git a/kernel/src/net/socket/unix/mod.rs b/kernel/src/net/socket/unix/mod.rs index 5a9c6332c..d135bba83 100644 --- a/kernel/src/net/socket/unix/mod.rs +++ b/kernel/src/net/socket/unix/mod.rs @@ -1,9 +1,11 @@ // SPDX-License-Identifier: MPL-2.0 mod addr; +mod cred; mod ns; mod stream; pub use addr::UnixSocketAddr; +pub use cred::CUserCred; pub use stream::UnixStreamSocket; pub(super) use stream::UNIX_STREAM_DEFAULT_BUF_SIZE; diff --git a/kernel/src/net/socket/unix/stream/connected.rs b/kernel/src/net/socket/unix/stream/connected.rs index 52519396e..34c12055d 100644 --- a/kernel/src/net/socket/unix/stream/connected.rs +++ b/kernel/src/net/socket/unix/stream/connected.rs @@ -4,7 +4,7 @@ use crate::{ events::IoEvents, fs::utils::{Endpoint, EndpointState}, net::socket::{ - unix::{addr::UnixSocketAddrBound, UnixSocketAddr}, + unix::{addr::UnixSocketAddrBound, cred::SocketCred, UnixSocketAddr}, util::SockShutdownCmd, }, prelude::*, @@ -19,6 +19,7 @@ pub(super) struct Connected { inner: Endpoint, reader: Mutex>, writer: Mutex>, + peer_cred: SocketCred, } impl Connected { @@ -27,6 +28,8 @@ impl Connected { peer_addr: Option, state: EndpointState, peer_state: EndpointState, + cred: SocketCred, + peer_cred: SocketCred, ) -> (Connected, Connected) { let (this_writer, peer_reader) = RingBuffer::new(UNIX_STREAM_DEFAULT_BUF_SIZE).split(); let (peer_writer, this_reader) = RingBuffer::new(UNIX_STREAM_DEFAULT_BUF_SIZE).split(); @@ -46,11 +49,13 @@ impl Connected { inner: this_inner, reader: Mutex::new(this_reader), writer: Mutex::new(this_writer), + peer_cred, }; let peer = Connected { inner: peer_inner, reader: Mutex::new(peer_reader), writer: Mutex::new(peer_writer), + peer_cred: cred, }; (this, peer) @@ -144,6 +149,10 @@ impl Connected { pub(super) fn cloned_pollee(&self) -> Pollee { self.inner.this_end().state.cloned_pollee() } + + pub(super) fn peer_cred(&self) -> &SocketCred { + &self.peer_cred + } } impl Drop for Connected { diff --git a/kernel/src/net/socket/unix/stream/init.rs b/kernel/src/net/socket/unix/stream/init.rs index dff846946..a696b2fca 100644 --- a/kernel/src/net/socket/unix/stream/init.rs +++ b/kernel/src/net/socket/unix/stream/init.rs @@ -2,6 +2,8 @@ use core::sync::atomic::{AtomicBool, Ordering}; +use aster_rights::ReadOp; + use super::{ connected::Connected, listener::Listener, @@ -11,7 +13,10 @@ use crate::{ events::IoEvents, fs::utils::EndpointState, net::socket::{ - unix::addr::{UnixSocketAddr, UnixSocketAddrBound}, + unix::{ + addr::{UnixSocketAddr, UnixSocketAddrBound}, + cred::SocketCred, + }, util::SockShutdownCmd, }, prelude::*, @@ -48,6 +53,7 @@ impl Init { self, peer_addr: UnixSocketAddrBound, pollee: Pollee, + peer_cred: SocketCred, ) -> (Connected, Connected) { let Init { addr, @@ -56,11 +62,15 @@ impl Init { } = self; pollee.invalidate(); + + let cred = SocketCred::::new_current(); let (this_conn, peer_conn) = Connected::new_pair( addr, Some(peer_addr), EndpointState::new(pollee, is_read_shutdown.into_inner()), EndpointState::new(Pollee::new(), is_write_shutdown.into_inner()), + cred, + peer_cred, ); (this_conn, peer_conn) diff --git a/kernel/src/net/socket/unix/stream/listener.rs b/kernel/src/net/socket/unix/stream/listener.rs index f631d5bec..e25b14790 100644 --- a/kernel/src/net/socket/unix/stream/listener.rs +++ b/kernel/src/net/socket/unix/stream/listener.rs @@ -2,6 +2,7 @@ use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use aster_rights::ReadDupOp; use ostd::sync::WaitQueue; use super::{ @@ -16,6 +17,7 @@ use crate::{ net::socket::{ unix::{ addr::{UnixSocketAddrBound, UnixSocketAddrKey}, + cred::SocketCred, stream::socket::OptionSet, }, util::{SockShutdownCmd, SocketAddr}, @@ -87,6 +89,10 @@ impl Listener { pub(super) fn check_io_events(&self) -> IoEvents { self.backlog.check_io_events() } + + pub(super) fn cred(&self) -> &SocketCred { + &self.backlog.listener_cred + } } impl Drop for Listener { @@ -146,6 +152,7 @@ pub(super) struct Backlog { backlog: AtomicUsize, incoming_conns: SpinLock>>, wait_queue: WaitQueue, + listener_cred: SocketCred, } impl Backlog { @@ -162,6 +169,7 @@ impl Backlog { backlog: AtomicUsize::new(backlog), incoming_conns: SpinLock::new(incoming_sockets), wait_queue: WaitQueue::new(), + listener_cred: SocketCred::::new_current(), } } @@ -248,7 +256,11 @@ impl Backlog { )); } - let (client_conn, server_conn) = init.into_connected(self.addr.clone(), pollee); + let (client_conn, server_conn) = init.into_connected( + self.addr.clone(), + pollee, + self.listener_cred.dup().restrict(), + ); incoming_conns.push_back(server_conn); self.pollee.notify(IoEvents::IN); diff --git a/kernel/src/net/socket/unix/stream/socket.rs b/kernel/src/net/socket/unix/stream/socket.rs index 79bd90ae5..56b3489b5 100644 --- a/kernel/src/net/socket/unix/stream/socket.rs +++ b/kernel/src/net/socket/unix/stream/socket.rs @@ -2,6 +2,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; +use aster_rights::ReadDupOp; use takeable::Takeable; use super::{ @@ -12,10 +13,11 @@ use super::{ use crate::{ events::IoEvents, fs::{file_handle::FileLike, utils::EndpointState}, + match_sock_option_mut, net::socket::{ - options::SocketOption, + options::{PeerCred, PeerGroups, SocketOption}, private::SocketPrivate, - unix::UnixSocketAddr, + unix::{cred::SocketCred, CUserCred, UnixSocketAddr}, util::{ options::{GetSocketLevelOption, SetSocketLevelOption, SocketOptionSet}, MessageHeader, SendRecvFlags, SockShutdownCmd, SocketAddr, @@ -23,7 +25,10 @@ use crate::{ Socket, }, prelude::*, - process::signal::{PollHandle, Pollable, Pollee}, + process::{ + signal::{PollHandle, Pollable, Pollee}, + Gid, + }, util::{MultiRead, MultiWrite}, }; @@ -115,6 +120,24 @@ impl State { State::Connected(connected) => connected.is_write_shutdown(), } } + + pub(self) fn peer_cred(&self) -> Option { + match self { + Self::Init(_) => None, + Self::Listen(listener) => Some(listener.cred().to_c_user_cred()), + Self::Connected(connected) => Some(connected.peer_cred().to_c_user_cred()), + } + } + + pub(self) fn peer_groups(&self) -> Result> { + match self { + State::Init(_) => { + return_errno_with_message!(Errno::ENODATA, "the socket does not have peer groups") + } + State::Listen(listener) => Ok(listener.cred().groups()), + State::Connected(connected) => Ok(connected.peer_cred().groups()), + } + } } #[derive(Clone, Debug)] @@ -136,11 +159,15 @@ impl UnixStreamSocket { } pub fn new_pair(is_nonblocking: bool) -> (Arc, Arc) { + let cred = SocketCred::::new_current(); + let (conn_a, conn_b) = Connected::new_pair( None, None, EndpointState::default(), EndpointState::default(), + cred.dup().restrict(), + cred.restrict(), ); let options = OptionSet::new(); ( @@ -378,6 +405,12 @@ impl Socket for UnixStreamSocket { let state = self.state.read(); let options = self.options.read(); + // Deal with UNIX-socket-specific socket-level options + match do_unix_getsockopt(option, state.as_ref()) { + Err(err) if err.error() == Errno::ENOPROTOOPT => (), + res => return res, + } + // Deal with socket-level options match options.socket.get_option(option, state.as_ref()) { Err(err) if err.error() == Errno::ENOPROTOOPT => (), @@ -409,6 +442,22 @@ impl Socket for UnixStreamSocket { } } +fn do_unix_getsockopt(option: &mut dyn SocketOption, state: &State) -> Result<()> { + match_sock_option_mut!(option, { + socket_peer_cred: PeerCred => { + let peer_cred = state.peer_cred().unwrap_or_else(CUserCred::new_unknown); + socket_peer_cred.set(peer_cred); + }, + socket_peer_groups: PeerGroups => { + let groups = state.peer_groups()?; + socket_peer_groups.set(groups); + }, + _ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to get is not unix socket specific") + }); + + Ok(()) +} + impl GetSocketLevelOption for State { fn is_listening(&self) -> bool { matches!(self, Self::Listen(_)) diff --git a/kernel/src/net/socket/util/options.rs b/kernel/src/net/socket/util/options.rs index 8806d8d48..de045e77d 100644 --- a/kernel/src/net/socket/util/options.rs +++ b/kernel/src/net/socket/util/options.rs @@ -11,10 +11,10 @@ use crate::{ match_sock_option_mut, match_sock_option_ref, net::socket::{ options::{ - AcceptConn, KeepAlive, Linger, Priority, RecvBuf, RecvBufForce, ReuseAddr, ReusePort, - SendBuf, SendBufForce, SocketOption, + AcceptConn, KeepAlive, Linger, PeerCred, PeerGroups, Priority, RecvBuf, RecvBufForce, + ReuseAddr, ReusePort, SendBuf, SendBufForce, SocketOption, }, - unix::UNIX_STREAM_DEFAULT_BUF_SIZE, + unix::{CUserCred, UNIX_STREAM_DEFAULT_BUF_SIZE}, }, prelude::*, process::{credentials::capabilities::CapSet, posix_thread::AsPosixThread}, @@ -114,6 +114,10 @@ impl SocketOptionSet { let keep_alive = self.keep_alive(); socket_keepalive.set(keep_alive); }, + socket_peer_cred: PeerCred => { + let peer_cred = CUserCred::new_unknown(); + socket_peer_cred.set(peer_cred); + }, socket_accept_conn: AcceptConn => { let is_listening = socket.is_listening(); socket_accept_conn.set(is_listening); @@ -128,6 +132,9 @@ impl SocketOptionSet { let recv_buf = self.recv_buf(); socket_recvbuf_force.set(recv_buf); }, + _socket_peer_groups: PeerGroups => { + return_errno_with_message!(Errno::ENODATA, "the socket does not have peer groups"); + }, _ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to get is unknown") }); Ok(()) diff --git a/kernel/src/process/credentials/group.rs b/kernel/src/process/credentials/group.rs index a42d4418f..557111865 100644 --- a/kernel/src/process/credentials/group.rs +++ b/kernel/src/process/credentials/group.rs @@ -11,6 +11,11 @@ use crate::prelude::*; pub struct Gid(u32); impl Gid { + /// The invalid GID, typically used to indicate that no valid GID is found when returning to user space. + /// + /// Reference: . + pub const INVALID: Gid = Gid(u32::MAX); + pub const fn new(gid: u32) -> Self { Self(gid) } diff --git a/kernel/src/process/credentials/user.rs b/kernel/src/process/credentials/user.rs index c8ac36e09..35843607b 100644 --- a/kernel/src/process/credentials/user.rs +++ b/kernel/src/process/credentials/user.rs @@ -13,6 +13,11 @@ pub struct Uid(u32); const ROOT_UID: u32 = 0; impl Uid { + /// The invalid UID, typically used to indicate that no valid UID is found when returning to user space. + /// + /// Reference: . + pub const INVALID: Uid = Self::new(u32::MAX); + pub const fn new_root() -> Self { Self(ROOT_UID) } diff --git a/kernel/src/process/posix_thread/mod.rs b/kernel/src/process/posix_thread/mod.rs index 700c9a7e3..042a3f963 100644 --- a/kernel/src/process/posix_thread/mod.rs +++ b/kernel/src/process/posix_thread/mod.rs @@ -2,7 +2,7 @@ use core::sync::atomic::{AtomicU32, Ordering}; -use aster_rights::{ReadOp, WriteOp}; +use aster_rights::{ReadDupOp, ReadOp, WriteOp}; use ostd::sync::{RoArc, Waker}; use super::{ @@ -291,6 +291,11 @@ impl PosixThread { self.credentials.dup().restrict() } + /// Gets the duplicatable read-only credentials of the thread. + pub fn credentials_dup(&self) -> Credentials { + self.credentials.dup().restrict() + } + /// Gets the write-only credentials of the current thread. /// /// It is illegal to mutate the credentials from a thread other than the diff --git a/kernel/src/syscall/getsockopt.rs b/kernel/src/syscall/getsockopt.rs index 07be9795d..ffedd099a 100644 --- a/kernel/src/syscall/getsockopt.rs +++ b/kernel/src/syscall/getsockopt.rs @@ -16,8 +16,8 @@ pub fn sys_getsockopt( ctx: &Context, ) -> Result { let level = CSocketOptionLevel::try_from(level).map_err(|_| Errno::EOPNOTSUPP)?; - if optval == 0 || optlen_addr == 0 { - return_errno_with_message!(Errno::EINVAL, "optval or optlen_addr is null pointer"); + if optlen_addr == 0 { + return_errno_with_message!(Errno::EINVAL, "optlen_addr is null pointer"); } let user_space = ctx.user_space(); @@ -34,7 +34,14 @@ pub fn sys_getsockopt( socket.get_option(raw_option.as_sock_option_mut())?; - let write_len = raw_option.write_to_user(optval, optlen)?; + let write_len = { + let mut new_opt_len = optlen; + let res = raw_option.write_to_user(optval, &mut new_opt_len); + if new_opt_len != optlen { + user_space.write_val(optlen_addr, &new_opt_len)?; + } + res? + }; user_space.write_val(optlen_addr, &(write_len as u32))?; Ok(SyscallReturn::Return(0)) diff --git a/kernel/src/util/net/options/mod.rs b/kernel/src/util/net/options/mod.rs index da49f8283..117bc16c2 100644 --- a/kernel/src/util/net/options/mod.rs +++ b/kernel/src/util/net/options/mod.rs @@ -67,7 +67,7 @@ use self::{socket::new_socket_option, tcp::new_tcp_option}; pub trait RawSocketOption: SocketOption { fn read_from_user(&mut self, addr: Vaddr, max_len: u32) -> Result<()>; - fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result; + fn write_to_user(&self, addr: Vaddr, max_len: &mut u32) -> Result; fn as_sock_option_mut(&mut self) -> &mut dyn SocketOption; @@ -87,11 +87,11 @@ macro_rules! impl_raw_socket_option { Ok(()) } - fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result { + fn write_to_user(&self, addr: Vaddr, max_len: &mut u32) -> Result { use $crate::util::net::options::utils::WriteToUser; let output = self.get().unwrap(); - output.write_to_user(addr, max_len) + output.write_to_user(addr, *max_len) } fn as_sock_option_mut(&mut self) -> &mut dyn SocketOption { @@ -114,11 +114,11 @@ macro_rules! impl_raw_sock_option_get_only { return_errno_with_message!(Errno::ENOPROTOOPT, "the option is getter-only"); } - fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result { + fn write_to_user(&self, addr: Vaddr, max_len: &mut u32) -> Result { use $crate::util::net::options::utils::WriteToUser; let output = self.get().unwrap(); - output.write_to_user(addr, max_len) + output.write_to_user(addr, *max_len) } fn as_sock_option_mut(&mut self) -> &mut dyn SocketOption { @@ -145,7 +145,7 @@ macro_rules! impl_raw_sock_option_set_only { Ok(()) } - fn write_to_user(&self, _addr: Vaddr, _max_len: u32) -> Result { + fn write_to_user(&self, _addr: Vaddr, _max_len: &mut u32) -> Result { return_errno_with_message!(Errno::ENOPROTOOPT, "the option is setter-only"); } diff --git a/kernel/src/util/net/options/socket.rs b/kernel/src/util/net/options/socket.rs index 0eb2b9b51..fd3de31e2 100644 --- a/kernel/src/util/net/options/socket.rs +++ b/kernel/src/util/net/options/socket.rs @@ -2,12 +2,13 @@ use super::RawSocketOption; use crate::{ - impl_raw_sock_option_get_only, impl_raw_socket_option, + current_userspace, impl_raw_sock_option_get_only, impl_raw_socket_option, net::socket::options::{ - AcceptConn, Error, KeepAlive, Linger, Priority, RecvBuf, RecvBufForce, ReuseAddr, - ReusePort, SendBuf, SendBufForce, SocketOption, + AcceptConn, Error, KeepAlive, Linger, PeerCred, PeerGroups, Priority, RecvBuf, + RecvBufForce, ReuseAddr, ReusePort, SendBuf, SendBufForce, SocketOption, }, prelude::*, + process::Gid, }; /// Socket level options. @@ -33,9 +34,15 @@ enum CSocketOptionName { LINGER = 13, BSDCOMPAT = 14, REUSEPORT = 15, + PASSCRED = 16, + PEERCRED = 17, + ATTACH_FILTER = 26, + DETACH_FILTER = 27, ACCPETCONN = 30, + PEERSEC = 31, SNDBUFFORCE = 32, RCVBUFFORCE = 33, + PEERGROUPS = 59, RCVTIMEO_NEW = 66, SNDTIMEO_NEW = 67, } @@ -51,9 +58,11 @@ pub fn new_socket_option(name: i32) -> Result> { CSocketOptionName::PRIORITY => Ok(Box::new(Priority::new())), CSocketOptionName::LINGER => Ok(Box::new(Linger::new())), CSocketOptionName::KEEPALIVE => Ok(Box::new(KeepAlive::new())), + CSocketOptionName::PEERCRED => Ok(Box::new(PeerCred::new())), CSocketOptionName::ACCPETCONN => Ok(Box::new(AcceptConn::new())), CSocketOptionName::SNDBUFFORCE => Ok(Box::new(SendBufForce::new())), CSocketOptionName::RCVBUFFORCE => Ok(Box::new(RecvBufForce::new())), + CSocketOptionName::PEERGROUPS => Ok(Box::new(PeerGroups::new())), _ => return_errno_with_message!(Errno::ENOPROTOOPT, "unsupported socket-level option"), } } @@ -66,6 +75,40 @@ impl_raw_socket_option!(ReusePort); impl_raw_socket_option!(Priority); impl_raw_socket_option!(Linger); impl_raw_socket_option!(KeepAlive); +impl_raw_sock_option_get_only!(PeerCred); impl_raw_sock_option_get_only!(AcceptConn); impl_raw_socket_option!(SendBufForce); impl_raw_socket_option!(RecvBufForce); + +// SO_PEERGROUPS is a read-only option. However, calling setsockopt on SO_PEERGROUPS will return EINVAL +// instead of ENOPROTOOPT like other options. Therefore, we manually implement `RawSocketOption` for it. +impl RawSocketOption for PeerGroups { + fn read_from_user(&mut self, _addr: Vaddr, _max_len: u32) -> Result<()> { + return_errno_with_message!(Errno::EINVAL, "the option is getter-only"); + } + + fn write_to_user(&self, addr: Vaddr, buffer_len: &mut u32) -> Result { + let groups = self.get().unwrap(); + + let old_len = *buffer_len; + *buffer_len = (groups.len() * core::mem::size_of::()) as u32; + if old_len < *buffer_len { + return_errno_with_message!(Errno::ERANGE, "the buffer is too small"); + } + + for (i, gid) in groups.iter().enumerate() { + let dst = addr + i * core::mem::size_of::(); + current_userspace!().write_val(dst, gid)?; + } + + Ok(*buffer_len as usize) + } + + fn as_sock_option_mut(&mut self) -> &mut dyn SocketOption { + self + } + + fn as_sock_option(&self) -> &dyn SocketOption { + self + } +} diff --git a/kernel/src/util/net/options/utils.rs b/kernel/src/util/net/options/utils.rs index 3eb3e3cd1..d9b116bdb 100644 --- a/kernel/src/util/net/options/utils.rs +++ b/kernel/src/util/net/options/utils.rs @@ -6,6 +6,7 @@ use crate::{ current_userspace, net::socket::{ ip::{options::IpTtl, stream_options::CongestionControl}, + unix::CUserCred, util::LingerOption, }, prelude::*, @@ -225,3 +226,17 @@ impl From for LingerOption { LingerOption::new(is_on, timeout) } } + +impl WriteToUser for CUserCred { + fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result { + let write_len = core::mem::size_of::(); + + if (max_len as usize) < write_len { + return_errno_with_message!(Errno::EINVAL, "max_len is too short"); + }; + + current_userspace!().write_val(addr, self)?; + + Ok(write_len) + } +}