Skip to content

Commit 9d5c65e

Browse files
committed
Add sockopt::PeerPidfd (SO_PEERPIDFD) sockopt support to socket::sockopt
The existing PeerCredentials (SO_PEERCRED) sockopt provides the PID of the process connected to a socket, but PIDs are inherently racy, leading to confused deputy problems. The pidfd mechanism solves this by using a FD to refer to a process which the kernel can keep proper track of. This patch implements Get/Set<OwnedFd> to be able to receive a FD from getsockopt and implements sockopt::PeerPidfd (SO_PEERPIDFD) returning an OwnedFd representing the process connected to the socket.
1 parent ea012be commit 9d5c65e

File tree

3 files changed

+118
-0
lines changed

3 files changed

+118
-0
lines changed

changelog/2620.added.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `PeerPidfd` (`SO_PEERPIDFD`) to `socket::sockopt` for Linux

src/sys/socket/sockopt.rs

+89
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use libc::{self, c_int, c_void, socklen_t};
1010
use std::ffi::CString;
1111
use std::ffi::{CStr, OsStr, OsString};
1212
use std::mem::{self, MaybeUninit};
13+
use std::os::fd::OwnedFd;
1314
use std::os::unix::ffi::OsStrExt;
1415
#[cfg(any(linux_android, target_os = "illumos"))]
1516
use std::os::unix::io::{AsFd, AsRawFd};
@@ -192,6 +193,12 @@ macro_rules! sockopt_impl {
192193
$name, GetOnly, $level, $flag, usize, $crate::sys::socket::sockopt::GetUsize);
193194
};
194195

196+
($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, OwnedFd) =>
197+
{
198+
sockopt_impl!($(#[$attr])*
199+
$name, GetOnly, $level, $flag, OwnedFd, $crate::sys::socket::sockopt::GetOwnedFd);
200+
};
201+
195202
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
196203
sockopt_impl!($(#[$attr])*
197204
$name, SetOnly, $level, $flag, bool, $crate::sys::socket::sockopt::SetBool);
@@ -207,6 +214,12 @@ macro_rules! sockopt_impl {
207214
$name, SetOnly, $level, $flag, usize, $crate::sys::socket::sockopt::SetUsize);
208215
};
209216

217+
($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, OwnedFd) =>
218+
{
219+
sockopt_impl!($(#[$attr])*
220+
$name, SetOnly, $level, $flag, OwnedFd, $crate::sys::socket::sockopt::SetOwnedFd);
221+
};
222+
210223
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
211224
sockopt_impl!($(#[$attr])*
212225
$name, Both, $level, $flag, bool, $crate::sys::socket::sockopt::GetBool, $crate::sys::socket::sockopt::SetBool);
@@ -222,6 +235,11 @@ macro_rules! sockopt_impl {
222235
$name, Both, $level, $flag, usize, $crate::sys::socket::sockopt::GetUsize, $crate::sys::socket::sockopt::SetUsize);
223236
};
224237

238+
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, OwnedFd) => {
239+
sockopt_impl!($(#[$attr])*
240+
$name, Both, $level, $flag, OwnedFd, $crate::sys::socket::sockopt::GetOwnedFd, $crate::sys::socket::sockopt::SetOwnedFd);
241+
};
242+
225243
($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
226244
OsString<$array:ty>) =>
227245
{
@@ -662,6 +680,15 @@ sockopt_impl!(
662680
libc::SO_PEERCRED,
663681
super::UnixCredentials
664682
);
683+
#[cfg(target_os = "linux")]
684+
sockopt_impl!(
685+
/// Return the pidfd of the foreign process connected to this socket.
686+
PeerPidfd,
687+
GetOnly,
688+
libc::SOL_SOCKET,
689+
libc::SO_PEERPIDFD,
690+
OwnedFd
691+
);
665692
#[cfg(target_os = "freebsd")]
666693
#[cfg(feature = "net")]
667694
sockopt_impl!(
@@ -1804,6 +1831,68 @@ impl<'a> Set<'a, usize> for SetUsize {
18041831
}
18051832
}
18061833

1834+
1835+
/// Getter for a `OwnedFd` value.
1836+
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
1837+
#[doc(hidden)]
1838+
#[derive(Clone, Copy, Debug)]
1839+
pub struct GetOwnedFd {
1840+
len: socklen_t,
1841+
val: MaybeUninit<c_int>,
1842+
}
1843+
1844+
impl Get<OwnedFd> for GetOwnedFd {
1845+
fn uninit() -> Self {
1846+
GetOwnedFd {
1847+
len: mem::size_of::<c_int>() as socklen_t,
1848+
val: MaybeUninit::uninit(),
1849+
}
1850+
}
1851+
1852+
fn ffi_ptr(&mut self) -> *mut c_void {
1853+
self.val.as_mut_ptr().cast()
1854+
}
1855+
1856+
fn ffi_len(&mut self) -> *mut socklen_t {
1857+
&mut self.len
1858+
}
1859+
1860+
unsafe fn assume_init(self) -> OwnedFd {
1861+
use std::os::fd::{FromRawFd, RawFd};
1862+
1863+
assert_eq!(
1864+
self.len as usize,
1865+
mem::size_of::<c_int>(),
1866+
"invalid getsockopt implementation"
1867+
);
1868+
unsafe { OwnedFd::from_raw_fd(self.val.assume_init() as RawFd) }
1869+
}
1870+
}
1871+
1872+
/// Setter for an `OwnedFd` value.
1873+
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
1874+
#[doc(hidden)]
1875+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1876+
pub struct SetOwnedFd {
1877+
val: c_int,
1878+
}
1879+
1880+
impl<'a> Set<'a, OwnedFd> for SetOwnedFd {
1881+
fn new(val: &'a OwnedFd) -> SetOwnedFd {
1882+
use std::os::fd::AsRawFd;
1883+
1884+
SetOwnedFd { val: val.as_raw_fd() as c_int }
1885+
}
1886+
1887+
fn ffi_ptr(&self) -> *const c_void {
1888+
&self.val as *const c_int as *const c_void
1889+
}
1890+
1891+
fn ffi_len(&self) -> socklen_t {
1892+
mem::size_of_val(&self.val) as socklen_t
1893+
}
1894+
}
1895+
18071896
/// Getter for a `OsString` value.
18081897
// Hide the docs, because it's an implementation detail of `sockopt_impl!`
18091898
#[doc(hidden)]

test/sys/test_sockopt.rs

+28
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,34 @@ fn can_get_peercred_on_unix_socket() {
753753
assert_ne!(a_cred.pid(), 0);
754754
}
755755

756+
#[cfg(linux_android)]
757+
fn pid_from_pidfd(pidfd: OwnedFd) -> u32 {
758+
use std::fs::read_to_string;
759+
760+
let fd = pidfd.as_raw_fd();
761+
let fdinfo = read_to_string(format!("/proc/self/fdinfo/{fd}")).unwrap();
762+
let pidline = fdinfo.split('\n').find(|s| s.starts_with("Pid:")).unwrap();
763+
pidline.split('\t').next_back().unwrap().parse().unwrap()
764+
}
765+
766+
#[cfg(linux_android)]
767+
#[test]
768+
fn can_get_peerpidfd_on_unix_socket() {
769+
use nix::sys::socket::{socketpair, sockopt, SockFlag, SockType};
770+
771+
let (a, b) = socketpair(
772+
AddressFamily::Unix,
773+
SockType::Stream,
774+
None,
775+
SockFlag::empty(),
776+
)
777+
.unwrap();
778+
let a_pid = pid_from_pidfd(getsockopt(&a, sockopt::PeerPidfd).unwrap());
779+
let b_pid = pid_from_pidfd(getsockopt(&b, sockopt::PeerPidfd).unwrap());
780+
assert_eq!(a_pid, b_pid);
781+
assert_ne!(a_pid, 0);
782+
}
783+
756784
#[test]
757785
fn is_socket_type_unix() {
758786
use nix::sys::socket::{socketpair, sockopt, SockFlag, SockType};

0 commit comments

Comments
 (0)