[PATCH 07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd
From: Christian Brauner <brauner@kernel.org>
Date: 2026-08-31 11:21:52
Also in:
linux-fsdevel, lkml
Subsystem:
alpha port, generic include/asm header files, mips, networking [general], networking [sockets], networking [unix sockets], parisc architecture, sparc + ultrasparc (sparc/sparc64), the rest · Maintainers:
Richard Henderson, Matt Turner, Magnus Lindholm, Arnd Bergmann, Thomas Bogendoerfer, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kuniyuki Iwashima, Willem de Bruijn, "James E.J. Bottomley", Helge Deller, Andreas Larsson, Linus Torvalds
SO_PEERPIDFD hands out a pidfd for the thread-group that called connect() or socketpair(). Enable workloads such as the coredump server or a broker to get a pidfd of the specific thread that connected to the socket. Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> --- arch/alpha/include/uapi/asm/socket.h | 2 + arch/mips/include/uapi/asm/socket.h | 2 + arch/parisc/include/uapi/asm/socket.h | 2 + arch/sparc/include/uapi/asm/socket.h | 2 + include/uapi/asm-generic/socket.h | 2 + net/core/sock.c | 87 ++++++++++++++++++++--------------- net/unix/af_unix.c | 1 + 7 files changed, 61 insertions(+), 37 deletions(-)
diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
index bb3d534826bb..5d3524c26b2b 100644
--- a/arch/alpha/include/uapi/asm/socket.h
+++ b/arch/alpha/include/uapi/asm/socket.h@@ -159,6 +159,8 @@ #define SO_PASSPIDFD_THREAD 86 +#define SO_PEERPIDFD_THREAD 87 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64
diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
index 269badcaa086..245a43f52fb1 100644
--- a/arch/mips/include/uapi/asm/socket.h
+++ b/arch/mips/include/uapi/asm/socket.h@@ -170,6 +170,8 @@ #define SO_PASSPIDFD_THREAD 86 +#define SO_PEERPIDFD_THREAD 87 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64
diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
index 313aee10a52c..f23710e1c671 100644
--- a/arch/parisc/include/uapi/asm/socket.h
+++ b/arch/parisc/include/uapi/asm/socket.h@@ -151,6 +151,8 @@ #define SO_PASSPIDFD_THREAD 0x4054 +#define SO_PEERPIDFD_THREAD 0x4055 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64
diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
index bd3e69bcce7a..b35b25bdefc2 100644
--- a/arch/sparc/include/uapi/asm/socket.h
+++ b/arch/sparc/include/uapi/asm/socket.h@@ -152,6 +152,8 @@ #define SO_PASSPIDFD_THREAD 0x005f +#define SO_PEERPIDFD_THREAD 0x0060 + #if !defined(__KERNEL__)
diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
index d1e5c6de146d..56fed7ab27ab 100644
--- a/include/uapi/asm-generic/socket.h
+++ b/include/uapi/asm-generic/socket.h@@ -154,6 +154,8 @@ #define SO_PASSPIDFD_THREAD 86 +#define SO_PEERPIDFD_THREAD 87 + #if !defined(__KERNEL__) #if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
diff --git a/net/core/sock.c b/net/core/sock.c
index 6ada7e7eb7d7..cb2ffd329bc6 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c@@ -1743,6 +1743,50 @@ static int groups_to_user(sockptr_t dst, const struct group_info *src) return 0; } +/* Hand out a pidfd for @type of the socket's peer via SO_PEERPIDFD*. */ +static int sk_getsockopt_peerpidfd(struct sock *sk, enum pid_type type, + sockptr_t optval, sockptr_t optlen, int len) +{ + struct file *pidfd_file = NULL; + unsigned int flags = 0; + struct pid *peer_pid; + int pidfd; + + if (len > sizeof(pidfd)) + len = sizeof(pidfd); + + spin_lock(&sk->sk_peer_lock); + peer_pid = get_pid(sk->sk_peer_pid[type]); + spin_unlock(&sk->sk_peer_lock); + + if (!peer_pid) + return -ENODATA; + + /* The use of PIDFD_STALE requires stashing of struct pid on pidfs + * with pidfs_register_pid() and only AF_UNIX were prepared for this. + */ + if (sk->sk_family == AF_UNIX) + flags |= PIDFD_STALE; + if (type == PIDTYPE_PID) + flags |= PIDFD_THREAD; + + pidfd = pidfd_prepare(peer_pid, flags, &pidfd_file); + put_pid(peer_pid); + if (pidfd < 0) + return pidfd; + + if (copy_to_sockptr(optval, &pidfd, len) || + copy_to_sockptr(optlen, &len, sizeof(int))) { + put_unused_fd(pidfd); + fput(pidfd_file); + + return -EFAULT; + } + + fd_install(pidfd, pidfd_file); + return 0; +} + int sk_getsockopt(struct sock *sk, int level, int optname, sockptr_t optval, sockptr_t optlen) {
@@ -1938,45 +1982,14 @@ int sk_getsockopt(struct sock *sk, int level, int optname, } case SO_PEERPIDFD: - { - struct pid *peer_pid; - struct file *pidfd_file = NULL; - unsigned int flags = 0; - int pidfd; - - if (len > sizeof(pidfd)) - len = sizeof(pidfd); - - spin_lock(&sk->sk_peer_lock); - peer_pid = get_pid(sk->sk_peer_pid[PIDTYPE_TGID]); - spin_unlock(&sk->sk_peer_lock); - - if (!peer_pid) - return -ENODATA; - - /* The use of PIDFD_STALE requires stashing of struct pid - * on pidfs with pidfs_register_pid() and only AF_UNIX - * were prepared for this. - */ - if (sk->sk_family == AF_UNIX) - flags = PIDFD_STALE; + return sk_getsockopt_peerpidfd(sk, PIDTYPE_TGID, optval, optlen, len); - pidfd = pidfd_prepare(peer_pid, flags, &pidfd_file); - put_pid(peer_pid); - if (pidfd < 0) - return pidfd; - - if (copy_to_sockptr(optval, &pidfd, len) || - copy_to_sockptr(optlen, &len, sizeof(int))) { - put_unused_fd(pidfd); - fput(pidfd_file); - - return -EFAULT; - } + case SO_PEERPIDFD_THREAD: + /* Only AF_UNIX records the peer's connecting thread. */ + if (!sk_is_unix(sk)) + return -EOPNOTSUPP; - fd_install(pidfd, pidfd_file); - return 0; - } + return sk_getsockopt_peerpidfd(sk, PIDTYPE_PID, optval, optlen, len); case SO_PEERGROUPS: {
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index d01ee76c8026..563d9827c5cc 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c@@ -1056,6 +1056,7 @@ static bool unix_bpf_bypass_getsockopt(int level, int optname) if (level == SOL_SOCKET) { switch (optname) { case SO_PEERPIDFD: + case SO_PEERPIDFD_THREAD: return true; default: return false;
--
2.53.0