* [PATCH v4 01/10] pid: add helpers to operate on a struct pid array
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-05 15:09 ` [PATCH v4 02/10] af_unix: record the pid of the sending thread Andrew Halaney
` (10 subsequent siblings)
11 siblings, 0 replies; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet, Alexander Mikhalitsyn
From: Christian Brauner <brauner@kernel.org>
We're about to extend af_unix sockets and coredump code with the ability
to hand out thread-specific pidfds. Add a few simple helpers that allow
to operate on multiple struct pids up to PIDTYPE_MAX with automatic
bounds checking.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
fs/pidfs.c | 16 +++++++++++
include/linux/pid.h | 53 +++++++++++++++++++++++++++++++++++
include/linux/pid_types.h | 8 ++++++
include/linux/pidfs.h | 5 ++++
include/linux/sched/signal.h | 18 ++++++++++++
tools/lib/python/kdoc/xforms_lists.py | 1 +
6 files changed, 101 insertions(+)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index a6a643f15d08..586af2e5811c 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -1070,6 +1070,22 @@ int pidfs_register_pid_gfp(struct pid *pid, gfp_t gfp)
return 0;
}
+/* Register the pids up to pid type @last of @pids in pidfs. */
+int __pidfs_register_pids(struct pid *const *pids, enum pid_type last)
+{
+ if (WARN_ON_ONCE(last >= PIDTYPE_MAX))
+ return -EINVAL;
+
+ for (enum pid_type type = PIDTYPE_PID; type <= last; type++) {
+ int ret = pidfs_register_pid(pids[type]);
+
+ if (unlikely(ret))
+ return ret;
+ }
+
+ return 0;
+}
+
static struct dentry *pidfs_stash_dentry(struct dentry **stashed,
struct dentry *dentry)
{
diff --git a/include/linux/pid.h b/include/linux/pid.h
index ddaef0bbc8ba..87635d0cc1f7 100644
--- a/include/linux/pid.h
+++ b/include/linux/pid.h
@@ -2,6 +2,9 @@
#ifndef _LINUX_PID_H
#define _LINUX_PID_H
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/minmax.h>
#include <linux/pid_types.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
@@ -92,6 +95,56 @@ static inline struct pid *get_pid(struct pid *pid)
}
extern void put_pid(struct pid *pid);
+
+/*
+ * Helpers for arrays of struct pid indexed by pid type declared with
+ * DECLARE_PIDS(). The array covers PIDTYPE_PID up to the pid type it
+ * was declared with and the helpers take that bound from the array.
+ */
+static inline void __get_pids(struct pid **dst, struct pid *const *src,
+ enum pid_type last)
+{
+ for (enum pid_type type = PIDTYPE_PID; type <= last; type++)
+ dst[type] = get_pid(src[type]);
+}
+
+static inline void __put_pids(struct pid **pids, enum pid_type last)
+{
+ for (enum pid_type type = PIDTYPE_PID; type <= last; type++) {
+ put_pid(pids[type]);
+ pids[type] = NULL;
+ }
+}
+
+static inline void __swap_pids(struct pid **a, struct pid **b,
+ enum pid_type last)
+{
+ for (enum pid_type type = PIDTYPE_PID; type <= last; type++)
+ swap(a[type], b[type]);
+}
+
+static inline bool __pids_equal(struct pid *const *a, struct pid *const *b,
+ enum pid_type last)
+{
+ for (enum pid_type type = PIDTYPE_PID; type <= last; type++)
+ if (a[type] != b[type])
+ return false;
+ return true;
+}
+
+/* The last pid type an array declared with DECLARE_PIDS() covers. */
+#define pids_last(pids) \
+ ((enum pid_type)(ARRAY_SIZE(pids) - 1 + \
+ BUILD_BUG_ON_ZERO(ARRAY_SIZE(pids) > PIDTYPE_MAX)))
+
+#define __pids_last2(a, b) \
+ (pids_last(a) + BUILD_BUG_ON_ZERO(ARRAY_SIZE(a) != ARRAY_SIZE(b)))
+
+#define get_pids(dst, src) __get_pids(dst, src, __pids_last2(dst, src))
+#define put_pids(pids) __put_pids(pids, pids_last(pids))
+#define swap_pids(a, b) __swap_pids(a, b, __pids_last2(a, b))
+#define pids_equal(a, b) __pids_equal(a, b, __pids_last2(a, b))
+
extern struct task_struct *pid_task(struct pid *pid, enum pid_type);
static inline bool pid_has_task(struct pid *pid, enum pid_type type)
{
diff --git a/include/linux/pid_types.h b/include/linux/pid_types.h
index c2aee1d91dcf..3302690a2a28 100644
--- a/include/linux/pid_types.h
+++ b/include/linux/pid_types.h
@@ -2,6 +2,8 @@
#ifndef _LINUX_PID_TYPES_H
#define _LINUX_PID_TYPES_H
+#include <linux/build_bug.h>
+
enum pid_type {
PIDTYPE_PID,
PIDTYPE_TGID,
@@ -10,6 +12,12 @@ enum pid_type {
PIDTYPE_MAX,
};
+struct pid;
+
+/* An array of struct pid indexed by pid type, PIDTYPE_PID up to @last. */
+#define DECLARE_PIDS(name, last) \
+ struct pid *name[(last) + 1 + BUILD_BUG_ON_ZERO((last) >= PIDTYPE_MAX)]
+
struct pid_namespace;
extern struct pid_namespace init_pid_ns;
diff --git a/include/linux/pidfs.h b/include/linux/pidfs.h
index 0abf7da9ab23..3c1e82f1a369 100644
--- a/include/linux/pidfs.h
+++ b/include/linux/pidfs.h
@@ -3,6 +3,7 @@
#define _LINUX_PID_FS_H
#include <linux/gfp_types.h>
+#include <linux/pid.h>
struct coredump_params;
@@ -32,5 +33,9 @@ static inline int pidfs_register_pid(struct pid *pid)
}
void pidfs_free_pid(struct pid *pid);
+int __pidfs_register_pids(struct pid *const *pids, enum pid_type last);
+
+/* Register the pids of an array declared with DECLARE_PIDS(). */
+#define pidfs_register_pids(pids) __pidfs_register_pids(pids, pids_last(pids))
#endif /* _LINUX_PID_FS_H */
diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index d45a5476b97d..3149cdb976c6 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -674,6 +674,24 @@ struct pid *task_pid_type(struct task_struct *task, enum pid_type type)
return pid;
}
+/* Fill @pids with the pid types of @task up to @last, without references. */
+static inline void __task_pids(struct pid **pids, enum pid_type last,
+ struct task_struct *task)
+{
+ for (enum pid_type type = PIDTYPE_PID; type <= last; type++)
+ pids[type] = task_pid_type(task, type);
+}
+
+static inline void __get_task_pids(struct pid **pids, enum pid_type last,
+ struct task_struct *task)
+{
+ for (enum pid_type type = PIDTYPE_PID; type <= last; type++)
+ pids[type] = get_pid(task_pid_type(task, type));
+}
+
+#define task_pids(pids, task) __task_pids(pids, pids_last(pids), task)
+#define get_task_pids(pids, task) __get_task_pids(pids, pids_last(pids), task)
+
static inline struct pid *task_tgid(struct task_struct *task)
{
return task->signal->pids[PIDTYPE_TGID];
diff --git a/tools/lib/python/kdoc/xforms_lists.py b/tools/lib/python/kdoc/xforms_lists.py
index e3dda2fe8a53..e792785a15ec 100644
--- a/tools/lib/python/kdoc/xforms_lists.py
+++ b/tools/lib/python/kdoc/xforms_lists.py
@@ -45,6 +45,7 @@ class CTransforms:
(CMatch("DECLARE_HASHTABLE"), r"unsigned long \1[1 << ((\2) - 1)]"),
(CMatch("DECLARE_KFIFO"), r"\2 *\1"),
(CMatch("DECLARE_KFIFO_PTR"), r"\2 *\1"),
+ (CMatch("DECLARE_PIDS"), r"struct pid *\1[(\2) + 1]"),
(CMatch("(?:__)?DECLARE_FLEX_ARRAY"), r"\1 \2[]"),
(CMatch("DEFINE_DMA_UNMAP_ADDR"), r"dma_addr_t \1"),
(CMatch("DEFINE_DMA_UNMAP_LEN"), r"__u32 \1"),
--
2.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH v4 02/10] af_unix: record the pid of the sending thread
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
2026-10-05 15:09 ` [PATCH v4 01/10] pid: add helpers to operate on a struct pid array Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-08 4:35 ` Kuniyuki Iwashima
2026-10-05 15:09 ` [PATCH v4 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD Andrew Halaney
` (9 subsequent siblings)
11 siblings, 1 reply; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet, Alexander Mikhalitsyn
From: Christian Brauner <brauner@kernel.org>
Currently only the struct pid of the thread-group leader is recorded.
The identity of the actual thread that sent the message or is connected
to a given socket cannot be retrieved.
Add the plumbing to make it possible to retrieve a pidfd for the sender.
Nothing uses the thread-specific struct pid yet. No functional changes.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
include/net/scm.h | 10 +++++-----
net/core/scm.c | 21 +++++++++++++++++----
net/unix/af_unix.c | 21 +++++++++++----------
net/unix/af_unix.h | 3 ++-
4 files changed, 35 insertions(+), 20 deletions(-)
diff --git a/include/net/scm.h b/include/net/scm.h
index 86ae6bc109ec..b68a143f42de 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -42,7 +42,7 @@ struct scm_fp_list {
};
struct scm_cookie {
- struct pid *pid; /* Skb credentials */
+ DECLARE_PIDS(pid, PIDTYPE_TGID); /* Skb credentials by pid type */
struct scm_fp_list *fp; /* Passed files */
struct scm_creds creds; /* Skb credentials */
#ifdef CONFIG_SECURITY_NETWORK
@@ -69,7 +69,6 @@ static __inline__ void unix_get_peersec_dgram(struct socket *sock, struct scm_co
static __inline__ void scm_set_cred(struct scm_cookie *scm,
struct pid *pid, kuid_t uid, kgid_t gid)
{
- scm->pid = get_pid(pid);
scm->creds.pid = pid_vnr(pid);
scm->creds.uid = uid;
scm->creds.gid = gid;
@@ -77,8 +76,7 @@ static __inline__ void scm_set_cred(struct scm_cookie *scm,
static __inline__ void scm_destroy_cred(struct scm_cookie *scm)
{
- put_pid(scm->pid);
- scm->pid = NULL;
+ put_pids(scm->pid);
}
static __inline__ void scm_destroy(struct scm_cookie *scm)
@@ -94,8 +92,10 @@ static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
memset(scm, 0, sizeof(*scm));
scm->creds.uid = INVALID_UID;
scm->creds.gid = INVALID_GID;
- if (forcecreds)
+ if (forcecreds) {
+ scm->pid[PIDTYPE_TGID] = get_pid(task_tgid(current));
scm_set_cred(scm, task_tgid(current), current_uid(), current_gid());
+ }
unix_get_peersec_dgram(sock, scm);
if (msg->msg_controllen <= 0)
return 0;
diff --git a/net/core/scm.c b/net/core/scm.c
index f0d44ecdb11f..9b9e119c353a 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -149,6 +149,7 @@ EXPORT_SYMBOL(__scm_destroy);
static inline int scm_replace_pid(struct scm_cookie *scm, struct pid *pid)
{
+ struct pid *thread_pid;
int err;
/* drop all previous references */
@@ -158,7 +159,18 @@ static inline int scm_replace_pid(struct scm_cookie *scm, struct pid *pid)
if (unlikely(err))
return err;
- scm->pid = pid;
+ /* A sender naming its own thread-group sends from the current thread. */
+ if (pid == task_tgid(current))
+ thread_pid = task_pid(current);
+ else
+ thread_pid = pid;
+
+ err = pidfs_register_pid(thread_pid);
+ if (unlikely(err))
+ return err;
+
+ scm->pid[PIDTYPE_TGID] = pid;
+ scm->pid[PIDTYPE_PID] = get_pid(thread_pid);
scm->creds.pid = pid_vnr(pid);
return 0;
}
@@ -207,7 +219,8 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
if (err)
goto error;
- if (!p->pid || pid_vnr(p->pid) != creds.pid) {
+ if (!p->pid[PIDTYPE_TGID] ||
+ pid_vnr(p->pid[PIDTYPE_TGID]) != creds.pid) {
struct pid *pid;
err = -ESRCH;
pid = find_get_pid(creds.pid);
@@ -504,10 +517,10 @@ static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
return;
}
- if (!scm->pid)
+ if (!scm->pid[PIDTYPE_TGID])
return;
- pidfd = pidfd_prepare(scm->pid, PIDFD_STALE, &pidfd_file);
+ pidfd = pidfd_prepare(scm->pid[PIDTYPE_TGID], PIDFD_STALE, &pidfd_file);
if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
if (pidfd_file) {
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 42cffeafc8c1..64e720bd289b 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1973,7 +1973,7 @@ static void unix_destruct_scm(struct sk_buff *skb)
{
struct scm_cookie scm = {};
- swap(scm.pid, UNIXCB(skb).pid);
+ swap_pids(scm.pid, UNIXCB(skb).pid);
if (UNIXCB(skb).fp)
unix_detach_fds(&scm, skb);
@@ -1991,7 +1991,7 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
{
int err = 0;
- UNIXCB(skb).pid = get_pid(scm->pid);
+ get_pids(UNIXCB(skb).pid, scm->pid);
UNIXCB(skb).uid = scm->creds.uid;
UNIXCB(skb).gid = scm->creds.gid;
UNIXCB(skb).fp = NULL;
@@ -2005,7 +2005,8 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
static void unix_skb_to_scm(struct sk_buff *skb, struct scm_cookie *scm)
{
- scm_set_cred(scm, UNIXCB(skb).pid, UNIXCB(skb).uid, UNIXCB(skb).gid);
+ get_pids(scm->pid, UNIXCB(skb).pid);
+ scm_set_cred(scm, UNIXCB(skb).pid[PIDTYPE_TGID], UNIXCB(skb).uid, UNIXCB(skb).gid);
unix_set_secdata(scm, skb);
}
@@ -2025,20 +2026,20 @@ static void unix_skb_to_scm(struct sk_buff *skb, struct scm_cookie *scm)
static int unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk,
const struct sock *other)
{
- if (UNIXCB(skb).pid)
+ if (UNIXCB(skb).pid[PIDTYPE_TGID])
return 0;
if (unix_may_passcred(sk) || unix_may_passcred(other) ||
!other->sk_socket) {
- struct pid *pid;
int err;
- pid = task_tgid(current);
- err = pidfs_register_pid(pid);
- if (unlikely(err))
+ get_task_pids(UNIXCB(skb).pid, current);
+ err = pidfs_register_pids(UNIXCB(skb).pid);
+ if (unlikely(err)) {
+ put_pids(UNIXCB(skb).pid);
return err;
+ }
- UNIXCB(skb).pid = get_pid(pid);
current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
}
@@ -2048,7 +2049,7 @@ static int unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk,
static bool unix_skb_scm_eq(struct sk_buff *skb,
struct scm_cookie *scm)
{
- return UNIXCB(skb).pid == scm->pid &&
+ return UNIXCB(skb).pid[PIDTYPE_TGID] == scm->pid[PIDTYPE_TGID] &&
uid_eq(UNIXCB(skb).uid, scm->creds.uid) &&
gid_eq(UNIXCB(skb).gid, scm->creds.gid) &&
unix_secdata_eq(scm, skb);
diff --git a/net/unix/af_unix.h b/net/unix/af_unix.h
index 8119dbeef3a3..402742895acc 100644
--- a/net/unix/af_unix.h
+++ b/net/unix/af_unix.h
@@ -2,6 +2,7 @@
#ifndef __AF_UNIX_H
#define __AF_UNIX_H
+#include <linux/pid_types.h>
#include <linux/uidgid.h>
#define UNIX_HASH_MOD (256 - 1)
@@ -11,7 +12,7 @@
struct sock *unix_peer_get(struct sock *sk);
struct unix_skb_parms {
- struct pid *pid; /* skb credentials */
+ DECLARE_PIDS(pid, PIDTYPE_TGID); /* skb credentials by pid type */
kuid_t uid;
kgid_t gid;
struct scm_fp_list *fp; /* Passed files */
--
2.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v4 02/10] af_unix: record the pid of the sending thread
2026-10-05 15:09 ` [PATCH v4 02/10] af_unix: record the pid of the sending thread Andrew Halaney
@ 2026-10-08 4:35 ` Kuniyuki Iwashima
0 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-08 4:35 UTC (permalink / raw)
To: Andrew Halaney
Cc: Jakub Kicinski, Oleg Nesterov, David S. Miller, Paolo Abeni,
Simon Horman, Willem de Bruijn, netdev, linux-kernel,
Alexander Viro, Jan Kara, linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable),
Eric Dumazet
On Mon, Oct 5, 2026 at 8:09 AM Andrew Halaney <andrew@amutable.com> wrote:
>
> From: Christian Brauner <brauner@kernel.org>
>
> Currently only the struct pid of the thread-group leader is recorded.
> The identity of the actual thread that sent the message or is connected
> to a given socket cannot be retrieved.
>
> Add the plumbing to make it possible to retrieve a pidfd for the sender.
> Nothing uses the thread-specific struct pid yet. No functional changes.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
> Signed-off-by: Andrew Halaney <andrew@amutable.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
2026-10-05 15:09 ` [PATCH v4 01/10] pid: add helpers to operate on a struct pid array Andrew Halaney
2026-10-05 15:09 ` [PATCH v4 02/10] af_unix: record the pid of the sending thread Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-08 4:36 ` Kuniyuki Iwashima
2026-10-05 15:09 ` [PATCH v4 04/10] selftests/net: SO_PASSPIDFD_THREAD Andrew Halaney
` (8 subsequent siblings)
11 siblings, 1 reply; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet
From: Christian Brauner <brauner@kernel.org>
Currently, SCM_PIDFD carries a pidfd for the thread-group leader. A
broker or the coredump server cannot learn the identity of the specific
thread that sent a given message. Now that both struct pids are recorded
a receiver can ask for the specific identity it needs.
So add SO_PASSPIDFD_THREAD as a sibling of SO_PASSPIDFD.
SO_PASSPIDFD_THREAD delivers an SCM_PIDFD_THREAD, i.e. scoped
to the specific thread sending. This is completely independent of
SO_PASSPIDFD, meaning you can use both options and get both cmsgs.
On SOCK_STREAM sockets recvmsg() only stops merging data at a thread
boundary when the receiver asked for a thread pidfd. For SO_PASSCRED and
SO_PASSPIDFD receivers all threads of one process remain a single
writer.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Co-developed-by: Andrew Halaney <andrew@amutable.com>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
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/linux/socket.h | 9 +++++----
include/net/sock.h | 4 +++-
include/uapi/asm-generic/socket.h | 2 ++
net/core/scm.c | 23 +++++++++++++++++------
net/core/sock.c | 14 ++++++++++++++
net/unix/af_unix.c | 12 +++++++++---
10 files changed, 58 insertions(+), 14 deletions(-)
diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
index 946a5fad2691..bb3d534826bb 100644
--- a/arch/alpha/include/uapi/asm/socket.h
+++ b/arch/alpha/include/uapi/asm/socket.h
@@ -157,6 +157,8 @@
#define SO_RIGHTS_NOTRUNC 85
+#define SO_PASSPIDFD_THREAD 86
+
#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 f1641dde135f..269badcaa086 100644
--- a/arch/mips/include/uapi/asm/socket.h
+++ b/arch/mips/include/uapi/asm/socket.h
@@ -168,6 +168,8 @@
#define SO_RIGHTS_NOTRUNC 85
+#define SO_PASSPIDFD_THREAD 86
+
#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 f3a3815c7dc2..313aee10a52c 100644
--- a/arch/parisc/include/uapi/asm/socket.h
+++ b/arch/parisc/include/uapi/asm/socket.h
@@ -149,6 +149,8 @@
#define SO_RIGHTS_NOTRUNC 0x4053
+#define SO_PASSPIDFD_THREAD 0x4054
+
#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 7907f3b1f0ee..bd3e69bcce7a 100644
--- a/arch/sparc/include/uapi/asm/socket.h
+++ b/arch/sparc/include/uapi/asm/socket.h
@@ -150,6 +150,8 @@
#define SO_RIGHTS_NOTRUNC 0x005e
+#define SO_PASSPIDFD_THREAD 0x005f
+
#if !defined(__KERNEL__)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 5a5eb1250103..b17fdb7d38a7 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -189,10 +189,11 @@ static inline size_t msg_data_left(const struct msghdr *msg)
/* "Socket"-level control message types: */
-#define SCM_RIGHTS 0x01 /* rw: access rights (array of int) */
-#define SCM_CREDENTIALS 0x02 /* rw: struct ucred */
-#define SCM_SECURITY 0x03 /* rw: security label */
-#define SCM_PIDFD 0x04 /* ro: pidfd (int) */
+#define SCM_RIGHTS 0x01 /* rw: access rights (array of int) */
+#define SCM_CREDENTIALS 0x02 /* rw: struct ucred */
+#define SCM_SECURITY 0x03 /* rw: security label */
+#define SCM_PIDFD 0x04 /* ro: pidfd (int) */
+#define SCM_PIDFD_THREAD 0x05 /* ro: thread pidfd (int) */
struct ucred {
__u32 pid;
diff --git a/include/net/sock.h b/include/net/sock.h
index 60ea55dc1885..2c4f754b498b 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -356,6 +356,7 @@ struct sk_filter;
* @sk_scm_security: flagged by SO_PASSSEC to recv SCM_SECURITY
* @sk_scm_pidfd: flagged by SO_PASSPIDFD to recv SCM_PIDFD
* @sk_scm_rights: flagged by SO_PASSRIGHTS to recv SCM_RIGHTS
+ * @sk_scm_pidfd_thread: flagged by SO_PASSPIDFD_THREAD to recv a thread SCM_PIDFD
* @sk_scm_unused: unused flags for scm_recv()
* @ns_tracker: tracker for netns reference
* @sk_user_frags: xarray of pages the user is holding a reference on.
@@ -562,7 +563,8 @@ struct sock {
sk_scm_security : 1,
sk_scm_pidfd : 1,
sk_scm_rights : 1,
- sk_scm_unused : 4;
+ sk_scm_pidfd_thread : 1,
+ sk_scm_unused : 3;
};
};
u8 sk_clockid;
diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
index 84ea7b92936e..d1e5c6de146d 100644
--- a/include/uapi/asm-generic/socket.h
+++ b/include/uapi/asm-generic/socket.h
@@ -152,6 +152,8 @@
#define SO_RIGHTS_NOTRUNC 85
+#define SO_PASSPIDFD_THREAD 86
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
diff --git a/net/core/scm.c b/net/core/scm.c
index 9b9e119c353a..00025579d53e 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -499,11 +499,16 @@ static bool scm_has_secdata(struct sock *sk)
}
#endif
-static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
+static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm,
+ enum pid_type type, int cmsg_type)
{
+ unsigned int flags = PIDFD_STALE;
struct file *pidfd_file = NULL;
+ struct pid *pid;
int len, pidfd;
+ pid = scm->pid[type];
+
/* put_cmsg() doesn't return an error if CMSG is truncated,
* that's why we need to opencode these checks here.
*/
@@ -517,12 +522,15 @@ static void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm)
return;
}
- if (!scm->pid[PIDTYPE_TGID])
+ if (!pid)
return;
- pidfd = pidfd_prepare(scm->pid[PIDTYPE_TGID], PIDFD_STALE, &pidfd_file);
+ if (type == PIDTYPE_PID)
+ flags |= PIDFD_THREAD;
- if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
+ pidfd = pidfd_prepare(pid, flags, &pidfd_file);
+
+ if (put_cmsg(msg, SOL_SOCKET, cmsg_type, sizeof(int), &pidfd)) {
if (pidfd_file) {
put_unused_fd(pidfd);
fput(pidfd_file);
@@ -540,7 +548,7 @@ static bool __scm_recv_common(struct sock *sk, struct msghdr *msg,
{
if (!msg->msg_control) {
if (sk->sk_scm_credentials || sk->sk_scm_pidfd ||
- scm->fp || scm_has_secdata(sk))
+ sk->sk_scm_pidfd_thread || scm->fp || scm_has_secdata(sk))
msg->msg_flags |= MSG_CTRUNC;
scm_destroy(scm);
@@ -587,7 +595,10 @@ void scm_recv_unix(struct socket *sock, struct msghdr *msg,
}
if (sock->sk->sk_scm_pidfd)
- scm_pidfd_recv(msg, scm);
+ scm_pidfd_recv(msg, scm, PIDTYPE_TGID, SCM_PIDFD);
+
+ if (sock->sk->sk_scm_pidfd_thread)
+ scm_pidfd_recv(msg, scm, PIDTYPE_PID, SCM_PIDFD_THREAD);
scm_destroy_cred(scm);
}
diff --git a/net/core/sock.c b/net/core/sock.c
index e8551df8330f..c6f33fcbea43 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1578,6 +1578,13 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
ret = -EOPNOTSUPP;
break;
+ case SO_PASSPIDFD_THREAD:
+ if (sk_is_unix(sk))
+ sk->sk_scm_pidfd_thread = valbool;
+ else
+ ret = -EOPNOTSUPP;
+ break;
+
case SO_PASSRIGHTS:
if (sk_is_unix(sk))
sk->sk_scm_rights = valbool;
@@ -1893,6 +1900,13 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
v.val = sk->sk_scm_pidfd;
break;
+ case SO_PASSPIDFD_THREAD:
+ if (!sk_is_unix(sk))
+ return -EOPNOTSUPP;
+
+ v.val = sk->sk_scm_pidfd_thread;
+ break;
+
case SO_PASSRIGHTS:
if (!sk_is_unix(sk))
return -EOPNOTSUPP;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 64e720bd289b..e44afb059abf 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -803,7 +803,8 @@ static void copy_peercred(struct sock *sk, struct sock *peersk)
static bool unix_may_passcred(const struct sock *sk)
{
- return sk->sk_scm_credentials || sk->sk_scm_pidfd;
+ return sk->sk_scm_credentials || sk->sk_scm_pidfd ||
+ sk->sk_scm_pidfd_thread;
}
static int unix_listen(struct socket *sock, int backlog)
@@ -2046,9 +2047,14 @@ static int unix_maybe_add_creds(struct sk_buff *skb, const struct sock *sk,
return 0;
}
-static bool unix_skb_scm_eq(struct sk_buff *skb,
+static bool unix_skb_scm_eq(const struct sock *sk, struct sk_buff *skb,
struct scm_cookie *scm)
{
+ /* Only a thread pidfd receiver can tell threads of one process apart. */
+ if (sk->sk_scm_pidfd_thread &&
+ UNIXCB(skb).pid[PIDTYPE_PID] != scm->pid[PIDTYPE_PID])
+ return false;
+
return UNIXCB(skb).pid[PIDTYPE_TGID] == scm->pid[PIDTYPE_TGID] &&
uid_eq(UNIXCB(skb).uid, scm->creds.uid) &&
gid_eq(UNIXCB(skb).gid, scm->creds.gid) &&
@@ -3031,7 +3037,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
if (check_creds) {
/* Never glue messages from different writers */
- if (!unix_skb_scm_eq(skb, &scm))
+ if (!unix_skb_scm_eq(sk, skb, &scm))
break;
} else if (unix_may_passcred(sk)) {
/* Copy credentials */
--
2.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v4 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD
2026-10-05 15:09 ` [PATCH v4 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD Andrew Halaney
@ 2026-10-08 4:36 ` Kuniyuki Iwashima
0 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-08 4:36 UTC (permalink / raw)
To: Andrew Halaney
Cc: Jakub Kicinski, Oleg Nesterov, David S. Miller, Paolo Abeni,
Simon Horman, Willem de Bruijn, netdev, linux-kernel,
Alexander Viro, Jan Kara, linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable),
Eric Dumazet
On Mon, Oct 5, 2026 at 8:09 AM Andrew Halaney <andrew@amutable.com> wrote:
>
> From: Christian Brauner <brauner@kernel.org>
>
> Currently, SCM_PIDFD carries a pidfd for the thread-group leader. A
> broker or the coredump server cannot learn the identity of the specific
> thread that sent a given message. Now that both struct pids are recorded
> a receiver can ask for the specific identity it needs.
>
> So add SO_PASSPIDFD_THREAD as a sibling of SO_PASSPIDFD.
> SO_PASSPIDFD_THREAD delivers an SCM_PIDFD_THREAD, i.e. scoped
> to the specific thread sending. This is completely independent of
> SO_PASSPIDFD, meaning you can use both options and get both cmsgs.
>
> On SOCK_STREAM sockets recvmsg() only stops merging data at a thread
> boundary when the receiver asked for a thread pidfd. For SO_PASSCRED and
> SO_PASSPIDFD receivers all threads of one process remain a single
> writer.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> Co-developed-by: Andrew Halaney <andrew@amutable.com>
> Signed-off-by: Andrew Halaney <andrew@amutable.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 04/10] selftests/net: SO_PASSPIDFD_THREAD
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
` (2 preceding siblings ...)
2026-10-05 15:09 ` [PATCH v4 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-08 5:03 ` Kuniyuki Iwashima
2026-10-05 15:09 ` [PATCH v4 05/10] net: turn sk_peer_pid into an array indexed by pid type Andrew Halaney
` (7 subsequent siblings)
11 siblings, 1 reply; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet
From: Christian Brauner <brauner@kernel.org>
Extend the af_unix scm_pidfd selftest with SO_PASSPIDFD_THREAD tests.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Co-developed-by: Andrew Halaney <andrew@amutable.com>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
tools/testing/selftests/net/af_unix/Makefile | 2 +
tools/testing/selftests/net/af_unix/scm_pidfd.c | 505 ++++++++++++++++++++++++
2 files changed, 507 insertions(+)
diff --git a/tools/testing/selftests/net/af_unix/Makefile b/tools/testing/selftests/net/af_unix/Makefile
index a66f10fb0c23..45b841758f1b 100644
--- a/tools/testing/selftests/net/af_unix/Makefile
+++ b/tools/testing/selftests/net/af_unix/Makefile
@@ -23,6 +23,8 @@ TEST_GEN_FILES := scm_rights_denial_lsm.bpf.o
include ../../lib.mk
include ../bpf.mk
+$(OUTPUT)/scm_pidfd: CFLAGS += -pthread
+
$(OUTPUT)/scm_rights_denial_lsm: $(BPFOBJ)
$(OUTPUT)/scm_rights_denial_lsm: CFLAGS += -I$(SCRATCH_DIR)/include
$(OUTPUT)/scm_rights_denial_lsm: LDLIBS += -lelf -lz
diff --git a/tools/testing/selftests/net/af_unix/scm_pidfd.c b/tools/testing/selftests/net/af_unix/scm_pidfd.c
index 2c18b92a2603..d73ae9d27a48 100644
--- a/tools/testing/selftests/net/af_unix/scm_pidfd.c
+++ b/tools/testing/selftests/net/af_unix/scm_pidfd.c
@@ -10,6 +10,7 @@
#include <unistd.h>
#include <string.h>
#include <errno.h>
+#include <pthread.h>
#include <sys/un.h>
#include <sys/signal.h>
#include <sys/types.h>
@@ -27,6 +28,14 @@
#define SCM_PIDFD 0x04
#endif
+#ifndef SCM_PIDFD_THREAD
+#define SCM_PIDFD_THREAD 0x05
+#endif
+
+#ifndef SO_PASSPIDFD_THREAD
+#define SO_PASSPIDFD_THREAD 86
+#endif
+
#define CHILD_EXIT_CODE_OK 123
static void child_die()
@@ -553,4 +562,500 @@ TEST_F(scm_pidfd, test)
close(pfd);
}
+struct thread_ids {
+ pid_t pid;
+ pid_t tid;
+};
+
+#define MAX_WRITERS 2
+
+/* Used by writers to signal they've written, so we can sequence multiple writers */
+static int seq_pipe[2];
+
+static void *send_ids_thread(void *arg)
+{
+ int fd = *(int *)arg;
+ struct thread_ids ids = {
+ .pid = getpid(),
+ .tid = gettid(),
+ };
+ char sync;
+
+ if (send(fd, &ids, sizeof(ids), 0) != sizeof(ids))
+ return (void *)1;
+
+ /* let the client start the next writer, so the queue order is known */
+ if (write(seq_pipe[1], "1", 1) != 1)
+ return (void *)1;
+
+ /* stay alive until the receiver has looked at our pidfd */
+ if (read(fd, &sync, 1) != 1)
+ return (void *)1;
+
+ return NULL;
+}
+
+static void *send_ids_creds_thread(void *arg)
+{
+ int fd = *(int *)arg;
+ struct thread_ids ids = {
+ .pid = getpid(),
+ .tid = gettid(),
+ };
+ struct ucred ucred = {
+ .pid = getpid(),
+ .uid = getuid(),
+ .gid = getgid(),
+ };
+ char control[CMSG_SPACE(sizeof(ucred))] = { 0 };
+ struct iovec iov;
+ struct msghdr msg = { 0 };
+ struct cmsghdr *cmsg;
+ char sync;
+
+ iov.iov_base = &ids;
+ iov.iov_len = sizeof(ids);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_CREDENTIALS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(ucred));
+ memcpy(CMSG_DATA(cmsg), &ucred, sizeof(ucred));
+
+ if (sendmsg(fd, &msg, 0) != sizeof(ids))
+ return (void *)1;
+
+ /* signal we've written our info */
+ if (write(seq_pipe[1], "1", 1) != 1)
+ return (void *)1;
+
+ /* stay alive until the receiver has looked at our pidfd */
+ if (read(fd, &sync, 1) != 1)
+ return (void *)1;
+
+ return NULL;
+}
+
+/*
+ * Runs @nwriters copies of @sender, each in its own non-leader thread, one
+ * at a time so the queue order is known, then tells the parent on @ackfd
+ * that everything is queued.
+ */
+static void thread_client(int fd, int syncfd, int ackfd,
+ void *(*sender)(void *), int nwriters)
+{
+ pthread_t writers[MAX_WRITERS];
+ void *ret;
+ char sync;
+ int i;
+
+ /* wait until the receiver enabled the options it wants */
+ if (read(syncfd, &sync, 1) != 1)
+ child_die();
+
+ for (i = 0; i < nwriters; i++) {
+ if (pthread_create(&writers[i], NULL, sender, &fd))
+ child_die();
+
+ if (read(seq_pipe[0], &sync, 1) != 1)
+ child_die();
+ }
+
+ if (write(ackfd, "1", 1) != 1)
+ child_die();
+
+ for (i = 0; i < nwriters; i++)
+ if (pthread_join(writers[i], &ret) || ret)
+ child_die();
+
+ exit(0);
+}
+
+struct pidfd_msg {
+ ssize_t len;
+ struct thread_ids ids;
+ struct pidfd_info tgid_info;
+ struct pidfd_info thread_info;
+ int tgid_flags;
+ int thread_flags;
+ bool have_tgid;
+ bool have_thread;
+};
+
+static int get_pidfd_info(int pidfd, struct pidfd_info *info)
+{
+ info->mask = PIDFD_INFO_PID;
+ if (ioctl(pidfd, PIDFD_GET_INFO, info)) {
+ log_err("ioctl(PIDFD_GET_INFO)");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* One recvmsg(), reporting how many bytes came back and what the pidfd
+ * cmsgs that came with them say.
+ */
+static int read_pidfd_msg(int fd, struct pidfd_msg *out)
+{
+ char control[CMSG_SPACE(sizeof(int)) * 2] = { 0 };
+ int tgid_pidfd = -1, thread_pidfd = -1;
+ struct thread_ids buf[2] = { 0 };
+ struct msghdr msg = { 0 };
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ out->len = recvmsg(fd, &msg, 0);
+ if (out->len < 0) {
+ log_err("recvmsg");
+ return -1;
+ }
+
+ if (msg.msg_flags & MSG_CTRUNC) {
+ log_err("recvmsg: control truncated");
+ return -1;
+ }
+
+ out->ids = buf[0];
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET)
+ continue;
+
+ if (cmsg->cmsg_type == SCM_PIDFD)
+ memcpy(&tgid_pidfd, CMSG_DATA(cmsg), sizeof(tgid_pidfd));
+ else if (cmsg->cmsg_type == SCM_PIDFD_THREAD)
+ memcpy(&thread_pidfd, CMSG_DATA(cmsg), sizeof(thread_pidfd));
+ }
+
+ out->have_tgid = tgid_pidfd >= 0;
+ out->have_thread = thread_pidfd >= 0;
+
+ if (out->have_tgid) {
+ if (get_pidfd_info(tgid_pidfd, &out->tgid_info))
+ return -1;
+ out->tgid_flags = fcntl(tgid_pidfd, F_GETFL);
+ close(tgid_pidfd);
+ }
+
+ if (out->have_thread) {
+ if (get_pidfd_info(thread_pidfd, &out->thread_info))
+ return -1;
+ out->thread_flags = fcntl(thread_pidfd, F_GETFL);
+ close(thread_pidfd);
+ }
+
+ return 0;
+}
+
+/*
+ * Runs @nwriters threads of one child process against a SOCK_STREAM pair,
+ * all of them queued before the parent reads. SO_PASSPIDFD is set when
+ * @want_tgid is given, SO_PASSPIDFD_THREAD when @want_thread is. @nread
+ * reads are returned in @out.
+ */
+static int pidfd_flow(void *(*sender)(void *), int nwriters, bool want_tgid,
+ bool want_thread, struct pidfd_msg *out, int nread)
+{
+ int child_status = 0;
+ int syncpipe[2];
+ int ackpipe[2];
+ int sk[2];
+ int on = 1;
+ char sync;
+ pid_t child;
+ int i;
+
+ if (nwriters > MAX_WRITERS)
+ return -1;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sk))
+ return -1;
+ if (pipe(syncpipe) || pipe(ackpipe) || pipe(seq_pipe))
+ return -1;
+
+ child = fork();
+ if (child < 0)
+ return -1;
+
+ if (child == 0) {
+ close(sk[0]);
+ close(syncpipe[1]);
+ close(ackpipe[0]);
+ thread_client(sk[1], syncpipe[0], ackpipe[1], sender, nwriters);
+ }
+ close(sk[1]);
+ close(syncpipe[0]);
+ close(ackpipe[1]);
+ close(seq_pipe[0]);
+ close(seq_pipe[1]);
+
+ if (want_tgid &&
+ setsockopt(sk[0], SOL_SOCKET, SO_PASSPIDFD, &on, sizeof(on))) {
+ log_err("Failed to set SO_PASSPIDFD");
+ return -1;
+ }
+
+ if (want_thread &&
+ setsockopt(sk[0], SOL_SOCKET, SO_PASSPIDFD_THREAD, &on, sizeof(on))) {
+ log_err("Failed to set SO_PASSPIDFD_THREAD");
+ return -1;
+ }
+
+ /* let the child know the options are set, it can write now */
+ if (write(syncpipe[1], "1", 1) != 1)
+ return -1;
+ close(syncpipe[1]);
+
+ /* wait until every writer has queued its message */
+ if (read(ackpipe[0], &sync, 1) != 1)
+ return -1;
+ close(ackpipe[0]);
+
+ for (i = 0; i < nread; i++)
+ if (read_pidfd_msg(sk[0], &out[i]))
+ return -1;
+
+ /* release the writers */
+ for (i = 0; i < nwriters; i++)
+ if (write(sk[0], "x", 1) != 1)
+ return -1;
+ close(sk[0]);
+
+ waitpid(child, &child_status, 0);
+ if (!WIFEXITED(child_status) || WEXITSTATUS(child_status))
+ return -1;
+
+ return 0;
+}
+
+static int sockopt_set(int fd, int optname, int val)
+{
+ return setsockopt(fd, SOL_SOCKET, optname, &val, sizeof(val));
+}
+
+static int sockopt_get(int fd, int optname)
+{
+ socklen_t len = sizeof(int);
+ int val = -1;
+
+ if (getsockopt(fd, SOL_SOCKET, optname, &val, &len))
+ return -1;
+
+ return val;
+}
+
+TEST(scm_pidfd_setsockopt_values)
+{
+ int sk[2];
+
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sk));
+
+ /* Verify that the options are truly set independently */
+ ASSERT_EQ(0, sockopt_set(sk[0], SO_PASSPIDFD_THREAD, 1));
+ ASSERT_EQ(1, sockopt_get(sk[0], SO_PASSPIDFD_THREAD));
+ ASSERT_EQ(0, sockopt_get(sk[0], SO_PASSPIDFD));
+
+ ASSERT_EQ(0, sockopt_set(sk[0], SO_PASSPIDFD, 1));
+ ASSERT_EQ(1, sockopt_get(sk[0], SO_PASSPIDFD));
+ ASSERT_EQ(1, sockopt_get(sk[0], SO_PASSPIDFD_THREAD));
+
+ /* Verify that the options are truly cleared independently */
+ ASSERT_EQ(0, sockopt_set(sk[0], SO_PASSPIDFD, 0));
+ ASSERT_EQ(0, sockopt_get(sk[0], SO_PASSPIDFD));
+ ASSERT_EQ(1, sockopt_get(sk[0], SO_PASSPIDFD_THREAD));
+
+ ASSERT_EQ(0, sockopt_set(sk[0], SO_PASSPIDFD_THREAD, 0));
+ ASSERT_EQ(0, sockopt_get(sk[0], SO_PASSPIDFD_THREAD));
+ ASSERT_EQ(0, sockopt_get(sk[0], SO_PASSPIDFD));
+
+ close(sk[0]);
+ close(sk[1]);
+}
+
+/* A receiver that only asked about the process cannot tell the two
+ * threads apart, so their writes are glued into one read.
+ */
+TEST(scm_pidfd_stream_glues_threads)
+{
+ struct pidfd_msg msg[1] = { 0 };
+
+ ASSERT_EQ(0, pidfd_flow(send_ids_thread, 2, true, false, msg, 1));
+ EXPECT_EQ(sizeof(struct thread_ids) * 2, msg[0].len);
+
+ EXPECT_TRUE(msg[0].have_tgid);
+ EXPECT_FALSE(msg[0].have_thread);
+ EXPECT_EQ(msg[0].ids.pid, msg[0].tgid_info.pid);
+ EXPECT_EQ(msg[0].ids.pid, msg[0].tgid_info.tgid);
+}
+
+/* A receiver asking for both thread and process pidfd should get a unique
+ * msg for each thread writing
+ */
+TEST(scm_pidfd_thread_stream_splits_on_threads)
+{
+ struct pidfd_msg msg[2] = { 0 };
+ int i;
+
+ ASSERT_EQ(0, pidfd_flow(send_ids_thread, 2, true, true, msg, 2));
+
+ for (i = 0; i < 2; i++) {
+ EXPECT_EQ(sizeof(struct thread_ids), msg[i].len);
+ EXPECT_TRUE(msg[i].have_tgid);
+ EXPECT_TRUE(msg[i].have_thread);
+
+ /* Make sure the info makes sense for the pidfd type */
+ EXPECT_EQ(msg[i].ids.pid, msg[i].tgid_info.pid);
+ EXPECT_EQ(msg[i].ids.pid, msg[i].tgid_info.tgid);
+ EXPECT_EQ(msg[i].ids.tid, msg[i].thread_info.pid);
+ EXPECT_EQ(msg[i].ids.pid, msg[i].thread_info.tgid);
+ EXPECT_NE(msg[i].ids.pid, msg[i].ids.tid);
+ }
+
+ /* Make sure we really got unique threads per message */
+ EXPECT_NE(msg[0].ids.tid, msg[1].ids.tid);
+ EXPECT_EQ(msg[0].ids.pid, msg[1].ids.pid);
+ EXPECT_EQ(msg[0].tgid_info.pid, msg[1].tgid_info.pid);
+ EXPECT_NE(msg[0].thread_info.pid, msg[1].thread_info.pid);
+}
+
+/* Sends from the thread-group leader */
+static int leader_flow(bool want_thread, struct pidfd_msg *out)
+{
+ struct thread_ids ids = {
+ .pid = getpid(),
+ .tid = gettid(),
+ };
+ int sk[2];
+ int on = 1;
+ int ret;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sk))
+ return -1;
+
+ if (setsockopt(sk[0], SOL_SOCKET, SO_PASSPIDFD, &on, sizeof(on))) {
+ log_err("Failed to set SO_PASSPIDFD");
+ return -1;
+ }
+
+ if (want_thread &&
+ setsockopt(sk[0], SOL_SOCKET, SO_PASSPIDFD_THREAD, &on, sizeof(on))) {
+ log_err("Failed to set SO_PASSPIDFD_THREAD");
+ return -1;
+ }
+
+ if (send(sk[1], &ids, sizeof(ids), 0) != sizeof(ids)) {
+ log_err("send");
+ return -1;
+ }
+
+ ret = read_pidfd_msg(sk[0], out);
+
+ close(sk[0]);
+ close(sk[1]);
+
+ return ret;
+}
+
+TEST(scm_pidfd_leader_sender)
+{
+ struct pidfd_msg msg = { 0 };
+
+ ASSERT_EQ(getpid(), gettid());
+
+ ASSERT_EQ(0, leader_flow(false, &msg));
+ ASSERT_TRUE(msg.have_tgid);
+ EXPECT_FALSE(msg.have_thread);
+
+ EXPECT_EQ(getpid(), msg.tgid_info.pid);
+ EXPECT_EQ(getpid(), msg.tgid_info.tgid);
+ EXPECT_FALSE(msg.tgid_flags & O_EXCL);
+}
+
+TEST(scm_pidfd_thread_leader_sender)
+{
+ struct pidfd_msg msg = { 0 };
+
+ ASSERT_EQ(getpid(), gettid());
+
+ ASSERT_EQ(0, leader_flow(true, &msg));
+ ASSERT_TRUE(msg.have_tgid);
+ ASSERT_TRUE(msg.have_thread);
+
+ EXPECT_EQ(getpid(), msg.tgid_info.pid);
+ EXPECT_EQ(getpid(), msg.tgid_info.tgid);
+ EXPECT_EQ(getpid(), msg.thread_info.pid);
+ EXPECT_EQ(getpid(), msg.thread_info.tgid);
+
+ EXPECT_FALSE(msg.tgid_flags & O_EXCL);
+ EXPECT_TRUE(msg.thread_flags & O_EXCL);
+}
+
+TEST(scm_pidfd_thread_and_group)
+{
+ struct pidfd_msg msg = { 0 };
+
+ ASSERT_EQ(0, pidfd_flow(send_ids_thread, 1, true, true, &msg, 1));
+ ASSERT_NE(msg.ids.pid, msg.ids.tid);
+ ASSERT_TRUE(msg.have_tgid);
+ ASSERT_TRUE(msg.have_thread);
+
+ EXPECT_EQ(msg.ids.pid, msg.tgid_info.pid);
+ EXPECT_EQ(msg.ids.pid, msg.tgid_info.tgid);
+
+ EXPECT_EQ(msg.ids.tid, msg.thread_info.pid);
+ EXPECT_EQ(msg.ids.pid, msg.thread_info.tgid);
+}
+
+TEST(scm_pidfd_thread)
+{
+ struct pidfd_msg msg = { 0 };
+
+ ASSERT_EQ(0, pidfd_flow(send_ids_thread, 1, false, true, &msg, 1));
+ ASSERT_NE(msg.ids.pid, msg.ids.tid);
+ ASSERT_TRUE(msg.have_thread);
+ EXPECT_FALSE(msg.have_tgid);
+ EXPECT_EQ(msg.ids.tid, msg.thread_info.pid);
+ EXPECT_EQ(msg.ids.pid, msg.thread_info.tgid);
+}
+
+TEST(scm_pidfd_thread_group)
+{
+ struct pidfd_msg msg = { 0 };
+
+ ASSERT_EQ(0, pidfd_flow(send_ids_thread, 1, true, false, &msg, 1));
+ ASSERT_NE(msg.ids.pid, msg.ids.tid);
+ ASSERT_TRUE(msg.have_tgid);
+ EXPECT_FALSE(msg.have_thread);
+ EXPECT_EQ(msg.ids.pid, msg.tgid_info.pid);
+ EXPECT_EQ(msg.ids.pid, msg.tgid_info.tgid);
+}
+
+TEST(scm_pidfd_thread_creds)
+{
+ struct pidfd_msg msg = { 0 };
+
+ ASSERT_EQ(0, pidfd_flow(send_ids_creds_thread, 1, true, true, &msg, 1));
+ ASSERT_NE(msg.ids.pid, msg.ids.tid);
+ ASSERT_TRUE(msg.have_tgid);
+ ASSERT_TRUE(msg.have_thread);
+ EXPECT_EQ(msg.ids.pid, msg.tgid_info.pid);
+ EXPECT_EQ(msg.ids.pid, msg.tgid_info.tgid);
+ EXPECT_EQ(msg.ids.tid, msg.thread_info.pid);
+ EXPECT_EQ(msg.ids.pid, msg.thread_info.tgid);
+}
+
TEST_HARNESS_MAIN
--
2.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v4 04/10] selftests/net: SO_PASSPIDFD_THREAD
2026-10-05 15:09 ` [PATCH v4 04/10] selftests/net: SO_PASSPIDFD_THREAD Andrew Halaney
@ 2026-10-08 5:03 ` Kuniyuki Iwashima
0 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-08 5:03 UTC (permalink / raw)
To: Andrew Halaney
Cc: Jakub Kicinski, Oleg Nesterov, David S. Miller, Paolo Abeni,
Simon Horman, Willem de Bruijn, netdev, linux-kernel,
Alexander Viro, Jan Kara, linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable),
Eric Dumazet
On Mon, Oct 5, 2026 at 8:09 AM Andrew Halaney <andrew@amutable.com> wrote:
>
> From: Christian Brauner <brauner@kernel.org>
>
> Extend the af_unix scm_pidfd selftest with SO_PASSPIDFD_THREAD tests.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> Co-developed-by: Andrew Halaney <andrew@amutable.com>
> Signed-off-by: Andrew Halaney <andrew@amutable.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 05/10] net: turn sk_peer_pid into an array indexed by pid type
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
` (3 preceding siblings ...)
2026-10-05 15:09 ` [PATCH v4 04/10] selftests/net: SO_PASSPIDFD_THREAD Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-05 15:09 ` [PATCH v4 06/10] af_unix: record the pid of the connecting thread Andrew Halaney
` (6 subsequent siblings)
11 siblings, 0 replies; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet, Alexander Mikhalitsyn
From: Christian Brauner <brauner@kernel.org>
Currently only the struct pid of the thread-group leader is recorded
for a socket's peer. To make room for the struct pid of the thread that
called connect(), listen() or socketpair() turn sk_peer_pid into an
array indexed by pid type. All users, including bluetooth and the
coredump socket, keep using the PIDTYPE_TGID slot.
Nothing fills the PIDTYPE_PID slot yet. No functional changes.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
fs/coredump.c | 2 +-
include/net/sock.h | 4 ++--
include/trace/events/landlock.h | 2 +-
net/bluetooth/af_bluetooth.c | 6 +++---
net/bluetooth/hci_sock.c | 8 ++++----
net/bluetooth/l2cap_sock.c | 2 +-
net/core/sock.c | 9 +++++----
net/unix/af_unix.c | 14 +++++++-------
8 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/fs/coredump.c b/fs/coredump.c
index 6114839f5178..9b267d3c0ed7 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -722,7 +722,7 @@ static bool coredump_sock_connect(struct core_name *cn, struct coredump_params *
}
/* ... and validate that @sk_peer_pid matches @cprm.pid. */
- if (WARN_ON_ONCE(unix_peer(socket->sk)->sk_peer_pid != cprm->pid))
+ if (WARN_ON_ONCE(unix_peer(socket->sk)->sk_peer_pid[PIDTYPE_TGID] != cprm->pid))
return false;
cprm->limit = RLIM_INFINITY;
diff --git a/include/net/sock.h b/include/net/sock.h
index 2c4f754b498b..77dfb170d3c8 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -301,7 +301,7 @@ struct sk_filter;
* @sk_type: socket type (%SOCK_STREAM, etc)
* @sk_protocol: which protocol this socket belongs in this network family
* @sk_peer_lock: lock protecting @sk_peer_pid and @sk_peer_cred
- * @sk_peer_pid: &struct pid for this socket's peer
+ * @sk_peer_pid: &struct pid for this socket's peer, by pid type
* @sk_peer_cred: %SO_PEERCRED setting
* @sk_rcvlowat: %SO_RCVLOWAT setting
* @sk_rcvtimeo: %SO_RCVTIMEO setting
@@ -546,7 +546,7 @@ struct sock {
u64 sk_ino;
spinlock_t sk_peer_lock;
int sk_bind_phc;
- struct pid *sk_peer_pid;
+ DECLARE_PIDS(sk_peer_pid, PIDTYPE_TGID);
const struct cred *sk_peer_cred;
ktime_t sk_stamp;
diff --git a/include/trace/events/landlock.h b/include/trace/events/landlock.h
index 3a43638c9bc2..9e172ea22d95 100644
--- a/include/trace/events/landlock.h
+++ b/include/trace/events/landlock.h
@@ -1037,7 +1037,7 @@ TRACE_EVENT(landlock_deny_scope_abstract_unix_socket,
* updates. The peer socket keeps a reference to sk_peer_pid
* through pid_nr(); sun_path is the reliable identifier.
*/
- peer_pid = READ_ONCE(peer->sk_peer_pid);
+ peer_pid = READ_ONCE(peer->sk_peer_pid[PIDTYPE_TGID]);
__entry->peer_pid = peer_pid ? pid_nr(peer_pid) : 0;
__assign_str(sun_path);
),
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 411d66f24393..7758e9ea3848 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -161,7 +161,7 @@ struct sock *bt_sock_alloc(struct net *net, struct socket *sock,
/* Init peer information so it can be properly monitored */
if (!kern) {
spin_lock(&sk->sk_peer_lock);
- sk->sk_peer_pid = get_pid(task_tgid(current));
+ sk->sk_peer_pid[PIDTYPE_TGID] = get_pid(task_tgid(current));
sk->sk_peer_cred = get_current_cred();
spin_unlock(&sk->sk_peer_lock);
}
@@ -235,9 +235,9 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh)
* socket is allocated by the kernel.
*/
spin_lock(&sk->sk_peer_lock);
- old_pid = sk->sk_peer_pid;
+ old_pid = sk->sk_peer_pid[PIDTYPE_TGID];
old_cred = sk->sk_peer_cred;
- sk->sk_peer_pid = get_pid(parent->sk_peer_pid);
+ sk->sk_peer_pid[PIDTYPE_TGID] = get_pid(parent->sk_peer_pid[PIDTYPE_TGID]);
sk->sk_peer_cred = get_cred(parent->sk_peer_cred);
spin_unlock(&sk->sk_peer_lock);
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 6d56c77741e1..4c40068ba5fb 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -284,21 +284,21 @@ static void hci_sock_copy_creds(struct sock *sk, struct sk_buff *skb)
creds = &bt_cb(skb)->creds;
/* Check if peer credentials is set */
- if (!sk->sk_peer_pid) {
+ if (!sk->sk_peer_pid[PIDTYPE_TGID]) {
/* Check if parent peer credentials is set */
- if (bt_sk(sk)->parent && bt_sk(sk)->parent->sk_peer_pid)
+ if (bt_sk(sk)->parent && bt_sk(sk)->parent->sk_peer_pid[PIDTYPE_TGID])
sk = bt_sk(sk)->parent;
else
return;
}
/* Check if scm_creds already set */
- if (creds->pid == pid_vnr(sk->sk_peer_pid))
+ if (creds->pid == pid_vnr(sk->sk_peer_pid[PIDTYPE_TGID]))
return;
memset(creds, 0, sizeof(*creds));
- creds->pid = pid_vnr(sk->sk_peer_pid);
+ creds->pid = pid_vnr(sk->sk_peer_pid[PIDTYPE_TGID]);
if (sk->sk_peer_cred) {
creds->uid = sk->sk_peer_cred->uid;
creds->gid = sk->sk_peer_cred->gid;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 1194c37e466f..872d8fb31b6f 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1890,7 +1890,7 @@ static struct pid *l2cap_sock_get_peer_pid_cb(struct l2cap_chan *chan)
{
struct sock *sk = chan->data;
- return sk->sk_peer_pid;
+ return sk->sk_peer_pid[PIDTYPE_TGID];
}
static void l2cap_sock_suspend_cb(struct l2cap_chan *chan)
diff --git a/net/core/sock.c b/net/core/sock.c
index c6f33fcbea43..f8436e494138 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1921,7 +1921,8 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
len = sizeof(peercred);
spin_lock(&sk->sk_peer_lock);
- cred_to_ucred(sk->sk_peer_pid, sk->sk_peer_cred, &peercred);
+ cred_to_ucred(sk->sk_peer_pid[PIDTYPE_TGID], sk->sk_peer_cred,
+ &peercred);
spin_unlock(&sk->sk_peer_lock);
if (copy_to_sockptr(optval, &peercred, len))
@@ -1940,7 +1941,7 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
len = sizeof(pidfd);
spin_lock(&sk->sk_peer_lock);
- peer_pid = get_pid(sk->sk_peer_pid);
+ peer_pid = get_pid(sk->sk_peer_pid[PIDTYPE_TGID]);
spin_unlock(&sk->sk_peer_lock);
if (!peer_pid)
@@ -2394,7 +2395,7 @@ static void __sk_destruct(struct rcu_head *head)
/* We do not need to acquire sk->sk_peer_lock, we are the last user. */
put_cred(sk->sk_peer_cred);
- put_pid(sk->sk_peer_pid);
+ put_pids(sk->sk_peer_pid);
if (likely(sk->sk_net_refcnt)) {
put_net_track(net, &sk->ns_tracker);
@@ -3799,7 +3800,7 @@ void sock_init_data_uid(struct socket *sock, struct sock *sk, kuid_t uid)
sk->sk_frag.offset = 0;
sk->sk_peek_off = -1;
- sk->sk_peer_pid = NULL;
+ memset(sk->sk_peer_pid, 0, sizeof(sk->sk_peer_pid));
sk->sk_peer_cred = NULL;
spin_lock_init(&sk->sk_peer_lock);
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index e44afb059abf..4e571c5a0e16 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -737,7 +737,7 @@ static void unix_release_sock(struct sock *sk, int embrion)
}
struct unix_peercred {
- struct pid *peer_pid;
+ DECLARE_PIDS(peer_pid, PIDTYPE_TGID);
const struct cred *peer_cred;
};
@@ -749,7 +749,7 @@ static inline int prepare_peercred(struct unix_peercred *peercred)
pid = task_tgid(current);
err = pidfs_register_pid(pid);
if (likely(!err)) {
- peercred->peer_pid = get_pid(pid);
+ peercred->peer_pid[PIDTYPE_TGID] = get_pid(pid);
peercred->peer_cred = get_current_cred();
}
return err;
@@ -762,7 +762,7 @@ static void drop_peercred(struct unix_peercred *peercred)
might_sleep();
- swap(peercred->peer_pid, pid);
+ swap(peercred->peer_pid[PIDTYPE_TGID], pid);
swap(peercred->peer_cred, cred);
put_pid(pid);
@@ -772,7 +772,7 @@ static void drop_peercred(struct unix_peercred *peercred)
static inline void init_peercred(struct sock *sk,
const struct unix_peercred *peercred)
{
- sk->sk_peer_pid = peercred->peer_pid;
+ sk->sk_peer_pid[PIDTYPE_TGID] = peercred->peer_pid[PIDTYPE_TGID];
sk->sk_peer_cred = peercred->peer_cred;
}
@@ -782,12 +782,12 @@ static void update_peercred(struct sock *sk, struct unix_peercred *peercred)
struct pid *old_pid;
spin_lock(&sk->sk_peer_lock);
- old_pid = sk->sk_peer_pid;
+ old_pid = sk->sk_peer_pid[PIDTYPE_TGID];
old_cred = sk->sk_peer_cred;
init_peercred(sk, peercred);
spin_unlock(&sk->sk_peer_lock);
- peercred->peer_pid = old_pid;
+ peercred->peer_pid[PIDTYPE_TGID] = old_pid;
peercred->peer_cred = old_cred;
}
@@ -796,7 +796,7 @@ static void copy_peercred(struct sock *sk, struct sock *peersk)
lockdep_assert_held(&unix_sk(peersk)->lock);
spin_lock(&sk->sk_peer_lock);
- sk->sk_peer_pid = get_pid(peersk->sk_peer_pid);
+ sk->sk_peer_pid[PIDTYPE_TGID] = get_pid(peersk->sk_peer_pid[PIDTYPE_TGID]);
sk->sk_peer_cred = get_cred(peersk->sk_peer_cred);
spin_unlock(&sk->sk_peer_lock);
}
--
2.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH v4 06/10] af_unix: record the pid of the connecting thread
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
` (4 preceding siblings ...)
2026-10-05 15:09 ` [PATCH v4 05/10] net: turn sk_peer_pid into an array indexed by pid type Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-05 15:09 ` [PATCH v4 07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd Andrew Halaney
` (5 subsequent siblings)
11 siblings, 0 replies; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet, Alexander Mikhalitsyn
From: Christian Brauner <brauner@kernel.org>
Currently only the struct pid of the thread-group leader is recorded.
The identity of the actual thread that connected to a given socket
cannot be retrieved.
Add the plumbing to make it possible to retrieve a pidfd for the
connecting thread. Nothing uses the thread-specific struct pid yet. No
functional changes.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
net/unix/af_unix.c | 30 +++++++++++++-----------------
1 file changed, 13 insertions(+), 17 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 4e571c5a0e16..b7d62e61d9c4 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -743,51 +743,47 @@ struct unix_peercred {
static inline int prepare_peercred(struct unix_peercred *peercred)
{
- struct pid *pid;
int err;
- pid = task_tgid(current);
- err = pidfs_register_pid(pid);
- if (likely(!err)) {
- peercred->peer_pid[PIDTYPE_TGID] = get_pid(pid);
- peercred->peer_cred = get_current_cred();
+ get_task_pids(peercred->peer_pid, current);
+ err = pidfs_register_pids(peercred->peer_pid);
+ if (unlikely(err)) {
+ put_pids(peercred->peer_pid);
+ return err;
}
- return err;
+
+ peercred->peer_cred = get_current_cred();
+ return 0;
}
static void drop_peercred(struct unix_peercred *peercred)
{
const struct cred *cred = NULL;
- struct pid *pid = NULL;
might_sleep();
- swap(peercred->peer_pid[PIDTYPE_TGID], pid);
+ put_pids(peercred->peer_pid);
swap(peercred->peer_cred, cred);
-
- put_pid(pid);
put_cred(cred);
}
static inline void init_peercred(struct sock *sk,
const struct unix_peercred *peercred)
{
- sk->sk_peer_pid[PIDTYPE_TGID] = peercred->peer_pid[PIDTYPE_TGID];
+ memcpy(sk->sk_peer_pid, peercred->peer_pid, sizeof(sk->sk_peer_pid));
sk->sk_peer_cred = peercred->peer_cred;
}
static void update_peercred(struct sock *sk, struct unix_peercred *peercred)
{
const struct cred *old_cred;
- struct pid *old_pid;
spin_lock(&sk->sk_peer_lock);
- old_pid = sk->sk_peer_pid[PIDTYPE_TGID];
+ swap_pids(sk->sk_peer_pid, peercred->peer_pid);
old_cred = sk->sk_peer_cred;
- init_peercred(sk, peercred);
+ sk->sk_peer_cred = peercred->peer_cred;
spin_unlock(&sk->sk_peer_lock);
- peercred->peer_pid[PIDTYPE_TGID] = old_pid;
peercred->peer_cred = old_cred;
}
@@ -796,7 +792,7 @@ static void copy_peercred(struct sock *sk, struct sock *peersk)
lockdep_assert_held(&unix_sk(peersk)->lock);
spin_lock(&sk->sk_peer_lock);
- sk->sk_peer_pid[PIDTYPE_TGID] = get_pid(peersk->sk_peer_pid[PIDTYPE_TGID]);
+ get_pids(sk->sk_peer_pid, peersk->sk_peer_pid);
sk->sk_peer_cred = get_cred(peersk->sk_peer_cred);
spin_unlock(&sk->sk_peer_lock);
}
--
2.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH v4 07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
` (5 preceding siblings ...)
2026-10-05 15:09 ` [PATCH v4 06/10] af_unix: record the pid of the connecting thread Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-05 15:09 ` [PATCH v4 08/10] selftests/net: SO_PEERPIDFD_THREAD Andrew Halaney
` (4 subsequent siblings)
11 siblings, 0 replies; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet, Alexander Mikhalitsyn
From: Christian Brauner <brauner@kernel.org>
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>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
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 f8436e494138..3c25d66ba31a 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1736,6 +1736,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 is prepared for this.
+ */
+ if (sk_is_unix(sk))
+ 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)
{
@@ -1931,45 +1975,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;
+ return sk_getsockopt_peerpidfd(sk, PIDTYPE_TGID, optval, optlen, len);
- if (len > sizeof(pidfd))
- len = sizeof(pidfd);
+ case SO_PEERPIDFD_THREAD:
+ /* Only AF_UNIX records the peer's connecting thread. */
+ if (!sk_is_unix(sk))
+ return -EOPNOTSUPP;
- 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;
-
- 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;
- }
+ 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 b7d62e61d9c4..a78e8ef3e0b0 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1057,6 +1057,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.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH v4 08/10] selftests/net: SO_PEERPIDFD_THREAD
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
` (6 preceding siblings ...)
2026-10-05 15:09 ` [PATCH v4 07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-08 5:05 ` Kuniyuki Iwashima
2026-10-05 15:09 ` [PATCH v4 09/10] pidfs: record the coredump on the dumping thread's pid too Andrew Halaney
` (3 subsequent siblings)
11 siblings, 1 reply; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet, Alexander Mikhalitsyn
From: Christian Brauner <brauner@kernel.org>
Add tests for SO_PEERPIDFD_THREAD.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
tools/testing/selftests/net/af_unix/scm_pidfd.c | 97 +++++++++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/tools/testing/selftests/net/af_unix/scm_pidfd.c b/tools/testing/selftests/net/af_unix/scm_pidfd.c
index d73ae9d27a48..cbe53fab44fc 100644
--- a/tools/testing/selftests/net/af_unix/scm_pidfd.c
+++ b/tools/testing/selftests/net/af_unix/scm_pidfd.c
@@ -36,6 +36,10 @@
#define SO_PASSPIDFD_THREAD 86
#endif
+#ifndef SO_PEERPIDFD_THREAD
+#define SO_PEERPIDFD_THREAD 87
+#endif
+
#define CHILD_EXIT_CODE_OK 123
static void child_die()
@@ -1058,4 +1062,97 @@ TEST(scm_pidfd_thread_creds)
EXPECT_EQ(msg.ids.pid, msg.thread_info.tgid);
}
+static void *peer_connect_thread(void *arg)
+{
+ struct sock_addr *sa = arg;
+ struct thread_ids ids = {
+ .pid = getpid(),
+ .tid = gettid(),
+ };
+ int fd;
+ char sync;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return (void *)1;
+
+ if (connect(fd, (struct sockaddr *)&sa->listen_addr, sa->addrlen))
+ return (void *)1;
+
+ if (send(fd, &ids, sizeof(ids), 0) != sizeof(ids))
+ return (void *)1;
+
+ /* stay alive until the server has looked at our pidfd */
+ if (read(fd, &sync, 1) != 1)
+ return (void *)1;
+
+ close(fd);
+ return NULL;
+}
+
+static int peer_pidfd_info(int fd, int optname, struct pidfd_info *info)
+{
+ int pidfd;
+ socklen_t len = sizeof(pidfd);
+
+ if (getsockopt(fd, SOL_SOCKET, optname, &pidfd, &len)) {
+ log_err("getsockopt(SO_PEERPIDFD*)");
+ return -1;
+ }
+
+ info->mask = PIDFD_INFO_PID;
+ if (ioctl(pidfd, PIDFD_GET_INFO, info)) {
+ log_err("ioctl(PIDFD_GET_INFO)");
+ close(pidfd);
+ return -1;
+ }
+
+ close(pidfd);
+ return 0;
+}
+
+/* SO_PEERPIDFD_THREAD returns a pidfd for the peer's connecting thread. */
+TEST(so_peerpidfd_thread)
+{
+ struct sock_addr sa;
+ struct thread_ids ids;
+ struct pidfd_info info;
+ pthread_t thread;
+ void *tret;
+ int server, cfd;
+
+ server = socket(AF_UNIX, SOCK_STREAM, 0);
+ ASSERT_LE(0, server);
+
+ fill_sockaddr(&sa, true);
+ ASSERT_EQ(0, bind(server, (struct sockaddr *)&sa.listen_addr, sa.addrlen));
+ ASSERT_EQ(0, listen(server, 1));
+
+ ASSERT_EQ(0, pthread_create(&thread, NULL, peer_connect_thread, &sa));
+
+ cfd = accept(server, NULL, NULL);
+ ASSERT_LE(0, cfd);
+
+ ASSERT_EQ(sizeof(ids), recv(cfd, &ids, sizeof(ids), MSG_WAITALL));
+ ASSERT_NE(ids.pid, ids.tid);
+
+ /* SO_PEERPIDFD refers to the peer's thread-group. */
+ ASSERT_EQ(0, peer_pidfd_info(cfd, SO_PEERPIDFD, &info));
+ EXPECT_EQ(ids.pid, info.pid);
+ EXPECT_EQ(ids.pid, info.tgid);
+
+ /* SO_PEERPIDFD_THREAD refers to the connecting thread. */
+ ASSERT_EQ(0, peer_pidfd_info(cfd, SO_PEERPIDFD_THREAD, &info));
+ EXPECT_EQ(ids.tid, info.pid);
+ EXPECT_EQ(ids.pid, info.tgid);
+
+ /* release the connecting thread */
+ ASSERT_EQ(1, write(cfd, "x", 1));
+ ASSERT_EQ(0, pthread_join(thread, &tret));
+ ASSERT_EQ(NULL, tret);
+
+ close(cfd);
+ close(server);
+}
+
TEST_HARNESS_MAIN
--
2.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v4 08/10] selftests/net: SO_PEERPIDFD_THREAD
2026-10-05 15:09 ` [PATCH v4 08/10] selftests/net: SO_PEERPIDFD_THREAD Andrew Halaney
@ 2026-10-08 5:05 ` Kuniyuki Iwashima
0 siblings, 0 replies; 18+ messages in thread
From: Kuniyuki Iwashima @ 2026-10-08 5:05 UTC (permalink / raw)
To: Andrew Halaney
Cc: Jakub Kicinski, Oleg Nesterov, David S. Miller, Paolo Abeni,
Simon Horman, Willem de Bruijn, netdev, linux-kernel,
Alexander Viro, Jan Kara, linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable),
Eric Dumazet
On Mon, Oct 5, 2026 at 8:10 AM Andrew Halaney <andrew@amutable.com> wrote:
>
> From: Christian Brauner <brauner@kernel.org>
>
> Add tests for SO_PEERPIDFD_THREAD.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
> Signed-off-by: Andrew Halaney <andrew@amutable.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 09/10] pidfs: record the coredump on the dumping thread's pid too
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
` (7 preceding siblings ...)
2026-10-05 15:09 ` [PATCH v4 08/10] selftests/net: SO_PEERPIDFD_THREAD Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-05 15:09 ` [PATCH v4 10/10] selftests/coredump: check the dumping thread's pidfd Andrew Halaney
` (2 subsequent siblings)
11 siblings, 0 replies; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet, Alexander Mikhalitsyn
From: Christian Brauner <brauner@kernel.org>
If a thread-group coredumps only the thread-group leader pidfd will
return coredump information. A pidfd for the thread that took the fatal
signal cannot be used to retrieve it.
Record both the thread-group leader and the specific thread that took
the signal and register both in pidfs. Mark both the thread-group leader
and the specific thread with the coredump information so retrieval works
for both pidfds.
Now that both SO_PEERPIDFD and SO_PEERPIDFD_THREAD are available it's
easy to get the coredump information for the specific thread.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
fs/coredump.c | 22 +++++++++++++---------
fs/pidfs.c | 11 +++++++++--
include/linux/coredump.h | 4 +++-
3 files changed, 25 insertions(+), 12 deletions(-)
diff --git a/fs/coredump.c b/fs/coredump.c
index 9b267d3c0ed7..870ad720b215 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -454,7 +454,7 @@ static bool coredump_parse(struct core_name *cn, struct coredump_params *cprm,
* leader we know that the thread-group leader
* cannot be reaped until @current has exited.
*/
- cprm->pid = task_tgid(current);
+ task_pids(cprm->pid, current);
err = cn_printf(cn, "%d", COREDUMP_PIDFD_NUMBER);
break;
}
@@ -626,13 +626,17 @@ static int umh_coredump_setup(struct subprocess_info *info, struct cred *new)
struct coredump_params *cp = (struct coredump_params *)info->data;
int err;
- if (cp->pid) {
+ if (cp->pid[PIDTYPE_TGID]) {
struct file *pidfs_file __free(fput) = NULL;
- pidfs_file = pidfs_alloc_file(cp->pid, 0);
+ pidfs_file = pidfs_alloc_file(cp->pid[PIDTYPE_TGID], 0);
if (IS_ERR(pidfs_file))
return PTR_ERR(pidfs_file);
+ err = pidfs_register_pids(cp->pid);
+ if (err)
+ return err;
+
pidfs_coredump(cp);
/*
@@ -695,12 +699,12 @@ static bool coredump_sock_connect(struct core_name *cn, struct coredump_params *
return false;
/*
- * Set the thread-group leader pid which is used for the peer
- * credentials during connect() below. Then immediately register
- * it in pidfs...
+ * Set the pids of the dumping thread and its thread-group leader
+ * which are used for the peer credentials during connect() below.
+ * Then immediately register them in pidfs...
*/
- cprm->pid = task_tgid(current);
- retval = pidfs_register_pid(cprm->pid);
+ task_pids(cprm->pid, current);
+ retval = pidfs_register_pids(cprm->pid);
if (retval)
return false;
@@ -722,7 +726,7 @@ static bool coredump_sock_connect(struct core_name *cn, struct coredump_params *
}
/* ... and validate that @sk_peer_pid matches @cprm.pid. */
- if (WARN_ON_ONCE(unix_peer(socket->sk)->sk_peer_pid[PIDTYPE_TGID] != cprm->pid))
+ if (WARN_ON_ONCE(!pids_equal(unix_peer(socket->sk)->sk_peer_pid, cprm->pid)))
return false;
cprm->limit = RLIM_INFINITY;
diff --git a/fs/pidfs.c b/fs/pidfs.c
index 586af2e5811c..29299b2c7ca7 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -793,9 +793,9 @@ void pidfs_exit(struct task_struct *tsk)
}
#ifdef CONFIG_COREDUMP
-void pidfs_coredump(const struct coredump_params *cprm)
+static void pidfs_coredump_pid(struct pid *pid,
+ const struct coredump_params *cprm)
{
- struct pid *pid = cprm->pid;
struct pidfs_attr *attr;
attr = READ_ONCE(pid->attr);
@@ -814,6 +814,13 @@ void pidfs_coredump(const struct coredump_params *cprm)
smp_wmb();
set_bit(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask);
}
+
+void pidfs_coredump(const struct coredump_params *cprm)
+{
+ /* The dumping thread's pidfd reports the coredump as well. */
+ for (enum pid_type type = PIDTYPE_PID; type <= pids_last(cprm->pid); type++)
+ pidfs_coredump_pid(cprm->pid[type], cprm);
+}
#endif
static struct vfsmount *pidfs_mnt __ro_after_init;
diff --git a/include/linux/coredump.h b/include/linux/coredump.h
index 7b38ee2e7913..0bbb7de6a402 100644
--- a/include/linux/coredump.h
+++ b/include/linux/coredump.h
@@ -5,6 +5,7 @@
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/fs.h>
+#include <linux/pid_types.h>
#include <linux/sched/coredump.h>
#include <asm/siginfo.h>
@@ -32,7 +33,8 @@ struct coredump_params {
int vma_count;
size_t vma_data_size;
struct core_vma_metadata *vma_meta;
- struct pid *pid;
+ /* Dumping thread and its thread-group leader by pid type. */
+ DECLARE_PIDS(pid, PIDTYPE_TGID);
};
extern unsigned int core_file_note_size_limit;
--
2.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH v4 10/10] selftests/coredump: check the dumping thread's pidfd
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
` (8 preceding siblings ...)
2026-10-05 15:09 ` [PATCH v4 09/10] pidfs: record the coredump on the dumping thread's pid too Andrew Halaney
@ 2026-10-05 15:09 ` Andrew Halaney
2026-10-12 9:00 ` [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
2026-10-12 15:59 ` Paolo Abeni
11 siblings, 0 replies; 18+ messages in thread
From: Andrew Halaney @ 2026-10-05 15:09 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Paolo Abeni, Simon Horman, Willem de Bruijn,
netdev, linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Andrew Halaney, Eric Dumazet, Alexander Mikhalitsyn
From: Christian Brauner <brauner@kernel.org>
Crash from a non-leader thread and verify that the pidfd from
SO_PEERPIDFD_THREAD on the coredump socket refers to that thread and
reports the coredump like the thread-group leader's pidfd does.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io>
Signed-off-by: Andrew Halaney <andrew@amutable.com>
---
.../selftests/coredump/coredump_socket_test.c | 175 +++++++++++++++++++++
tools/testing/selftests/coredump/coredump_test.h | 2 +
.../selftests/coredump/coredump_test_helpers.c | 49 ++++++
3 files changed, 226 insertions(+)
diff --git a/tools/testing/selftests/coredump/coredump_socket_test.c b/tools/testing/selftests/coredump/coredump_socket_test.c
index 422728f632ca..ec73bb690bbc 100644
--- a/tools/testing/selftests/coredump/coredump_socket_test.c
+++ b/tools/testing/selftests/coredump/coredump_socket_test.c
@@ -592,6 +592,181 @@ TEST_F(coredump, socket_coredump_signal_sigsegv)
wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
}
+static bool check_coredump_info(const struct pidfd_info *info, const char *what)
+{
+ if (!(info->mask & PIDFD_INFO_COREDUMP)) {
+ fprintf(stderr, "%s: PIDFD_INFO_COREDUMP not set in mask\n", what);
+ return false;
+ }
+
+ if (!(info->coredump_mask & PIDFD_COREDUMPED)) {
+ fprintf(stderr, "%s: PIDFD_COREDUMPED not set in coredump_mask\n", what);
+ return false;
+ }
+
+ if (!(info->mask & PIDFD_INFO_COREDUMP_SIGNAL) || info->coredump_signal != SIGSEGV) {
+ fprintf(stderr, "%s: coredump_signal=%d, expected SIGSEGV=%d\n",
+ what, info->coredump_signal, SIGSEGV);
+ return false;
+ }
+
+ if (!(info->mask & PIDFD_INFO_COREDUMP_CODE) || info->coredump_code != SEGV_MAPERR) {
+ fprintf(stderr, "%s: coredump_code=%d, expected SEGV_MAPERR=%d\n",
+ what, info->coredump_code, SEGV_MAPERR);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Test: PIDFD_INFO_COREDUMP on the dumping thread's pidfd
+ *
+ * Crash from a non-leader thread and verify that the pidfd from
+ * SO_PEERPIDFD_THREAD refers to that thread and reports the coredump
+ * like the thread-group leader's pidfd from SO_PEERPIDFD does.
+ */
+TEST_F(coredump, socket_coredump_thread)
+{
+ int pidfd, ret, status;
+ pid_t pid, pid_coredump_server;
+ struct pidfd_info info = {};
+ int ipc_sockets[2];
+ char c;
+
+ ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket"));
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets);
+ ASSERT_EQ(ret, 0);
+
+ pid_coredump_server = fork();
+ ASSERT_GE(pid_coredump_server, 0);
+ if (pid_coredump_server == 0) {
+ int fd_server = -1, fd_coredump = -1, fd_peer_pidfd = -1;
+ int fd_thread_pidfd = -1, fd_core_file = -1;
+ struct pidfd_info thread_info = {};
+ int exit_code = EXIT_FAILURE;
+
+ close(ipc_sockets[0]);
+
+ fd_server = create_and_listen_unix_socket("/tmp/coredump.socket");
+ if (fd_server < 0) {
+ fprintf(stderr, "socket_coredump_thread: listen socket failed: %m\n");
+ goto out;
+ }
+
+ if (write_nointr(ipc_sockets[1], "1", 1) < 0) {
+ fprintf(stderr, "socket_coredump_thread: ipc write failed: %m\n");
+ goto out;
+ }
+
+ close(ipc_sockets[1]);
+
+ fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC);
+ if (fd_coredump < 0) {
+ fprintf(stderr, "socket_coredump_thread: accept4 failed: %m\n");
+ goto out;
+ }
+
+ fd_peer_pidfd = get_peer_pidfd(fd_coredump);
+ if (fd_peer_pidfd < 0) {
+ fprintf(stderr, "socket_coredump_thread: get_peer_pidfd failed\n");
+ goto out;
+ }
+
+ fd_thread_pidfd = get_peer_pidfd_thread(fd_coredump);
+ if (fd_thread_pidfd < 0) {
+ fprintf(stderr, "socket_coredump_thread: get_peer_pidfd_thread failed\n");
+ goto out;
+ }
+
+ if (!get_pidfd_info(fd_peer_pidfd, &info) ||
+ !get_pidfd_info(fd_thread_pidfd, &thread_info)) {
+ fprintf(stderr, "socket_coredump_thread: get_pidfd_info failed\n");
+ goto out;
+ }
+
+ /* The peer is the thread-group leader, the dumping thread is not. */
+ if (info.pid != info.tgid || thread_info.tgid != info.tgid ||
+ thread_info.pid == thread_info.tgid) {
+ fprintf(stderr, "socket_coredump_thread: unexpected ids %d/%d and %d/%d\n",
+ info.pid, info.tgid, thread_info.pid, thread_info.tgid);
+ goto out;
+ }
+
+ if (!check_coredump_info(&info, "SO_PEERPIDFD") ||
+ !check_coredump_info(&thread_info, "SO_PEERPIDFD_THREAD"))
+ goto out;
+
+ fd_core_file = open_coredump_tmpfile(self->fd_tmpfs_detached);
+ if (fd_core_file < 0) {
+ fprintf(stderr, "socket_coredump_thread: core tmpfile failed: %m\n");
+ goto out;
+ }
+
+ for (;;) {
+ char buffer[4096];
+ ssize_t bytes_read, bytes_write;
+
+ bytes_read = read(fd_coredump, buffer, sizeof(buffer));
+ if (bytes_read < 0) {
+ fprintf(stderr, "socket_coredump_thread: core read failed: %m\n");
+ goto out;
+ }
+
+ if (bytes_read == 0)
+ break;
+
+ bytes_write = write(fd_core_file, buffer, bytes_read);
+ if (bytes_read != bytes_write) {
+ fprintf(stderr, "socket_coredump_thread: core write %zd/%zd: %m\n",
+ bytes_read, bytes_write);
+ goto out;
+ }
+ }
+
+ exit_code = EXIT_SUCCESS;
+ fprintf(stderr, "socket_coredump_thread: completed successfully\n");
+out:
+ if (fd_core_file >= 0)
+ close(fd_core_file);
+ if (fd_thread_pidfd >= 0)
+ close(fd_thread_pidfd);
+ if (fd_peer_pidfd >= 0)
+ close(fd_peer_pidfd);
+ if (fd_coredump >= 0)
+ close(fd_coredump);
+ if (fd_server >= 0)
+ close(fd_server);
+ _exit(exit_code);
+ }
+ self->pid_coredump_server = pid_coredump_server;
+
+ EXPECT_EQ(close(ipc_sockets[1]), 0);
+ ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1);
+ EXPECT_EQ(close(ipc_sockets[0]), 0);
+
+ pid = fork();
+ ASSERT_GE(pid, 0);
+ if (pid == 0)
+ crashing_child_thread();
+
+ pidfd = sys_pidfd_open(pid, 0);
+ ASSERT_GE(pidfd, 0);
+
+ waitpid(pid, &status, 0);
+ ASSERT_TRUE(WIFSIGNALED(status));
+ ASSERT_EQ(WTERMSIG(status), SIGSEGV);
+ ASSERT_TRUE(WCOREDUMP(status));
+
+ ASSERT_TRUE(get_pidfd_info(pidfd, &info));
+ ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP));
+ ASSERT_TRUE(!!(info.coredump_mask & PIDFD_COREDUMPED));
+ ASSERT_EQ(info.coredump_signal, SIGSEGV);
+
+ wait_and_check_coredump_server(pid_coredump_server, _metadata, self);
+}
+
/*
* Test: PIDFD_INFO_COREDUMP_SIGNAL via simple socket coredump with SIGABRT
*
diff --git a/tools/testing/selftests/coredump/coredump_test.h b/tools/testing/selftests/coredump/coredump_test.h
index ed47f01fa53c..4212656e31f0 100644
--- a/tools/testing/selftests/coredump/coredump_test.h
+++ b/tools/testing/selftests/coredump/coredump_test.h
@@ -27,10 +27,12 @@ FIXTURE(coredump)
/* Shared helper function declarations */
void *do_nothing(void *arg);
void crashing_child(void);
+void crashing_child_thread(void);
int create_detached_tmpfs(void);
int create_and_listen_unix_socket(const char *path);
bool set_core_pattern(const char *pattern);
int get_peer_pidfd(int fd);
+int get_peer_pidfd_thread(int fd);
bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info);
/* Inline helper that uses harness types */
diff --git a/tools/testing/selftests/coredump/coredump_test_helpers.c b/tools/testing/selftests/coredump/coredump_test_helpers.c
index 2a20faf9cb0a..36306069f62e 100644
--- a/tools/testing/selftests/coredump/coredump_test_helpers.c
+++ b/tools/testing/selftests/coredump/coredump_test_helpers.c
@@ -13,6 +13,7 @@
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
+#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
@@ -38,6 +39,10 @@ struct _fixture_coredump_data {
#define NUM_THREAD_SPAWN 128
+#ifndef SO_PEERPIDFD_THREAD
+#define SO_PEERPIDFD_THREAD 87
+#endif
+
void *do_nothing(void *arg)
{
(void)arg;
@@ -59,6 +64,36 @@ void crashing_child(void)
i = *(volatile int *)NULL;
}
+static void *crashing_thread(void *arg)
+{
+ int *p;
+
+ (void)arg;
+
+ /* crash on purpose with SEGV_MAPERR */
+ p = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (p == MAP_FAILED)
+ return NULL;
+ munmap(p, PAGE_SIZE);
+ *p = 0;
+
+ return NULL;
+}
+
+void crashing_child_thread(void)
+{
+ pthread_t thread;
+ int i;
+
+ for (i = 0; i < NUM_THREAD_SPAWN; ++i)
+ pthread_create(&thread, NULL, do_nothing, NULL);
+
+ /* crash from a non-leader thread */
+ pthread_create(&thread, NULL, crashing_thread, NULL);
+ pause();
+}
+
int create_detached_tmpfs(void)
{
int fd_context, fd_tmpfs;
@@ -138,6 +173,20 @@ int get_peer_pidfd(int fd)
return fd_peer_pidfd;
}
+int get_peer_pidfd_thread(int fd)
+{
+ int fd_peer_pidfd;
+ socklen_t fd_peer_pidfd_len = sizeof(fd_peer_pidfd);
+ int ret = getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD_THREAD, &fd_peer_pidfd,
+ &fd_peer_pidfd_len);
+ if (ret < 0) {
+ fprintf(stderr, "%s: getsockopt(SO_PEERPIDFD_THREAD) failed: %m\n", __func__);
+ return -1;
+ }
+ fprintf(stderr, "%s: successfully retrieved pidfd %d\n", __func__, fd_peer_pidfd);
+ return fd_peer_pidfd;
+}
+
bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info)
{
int ret;
--
2.55.0
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v4 00/10] net: support thread-specific pidfds for send and connect
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
` (9 preceding siblings ...)
2026-10-05 15:09 ` [PATCH v4 10/10] selftests/coredump: check the dumping thread's pidfd Andrew Halaney
@ 2026-10-12 9:00 ` Christian Brauner
2026-10-12 15:59 ` Paolo Abeni
11 siblings, 0 replies; 18+ messages in thread
From: Christian Brauner @ 2026-10-12 9:00 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov, Andrew Halaney
Cc: Christian Brauner, David S. Miller, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn, Eric Dumazet
On Mon, 05 Oct 2026 10:09:30 -0500, Andrew Halaney wrote:
> SO_PASSPIDFD/SCM_PIDFD and SO_PEERPIDFD allow to retrieve a pidfd for
> the thread-group leader. The coredump server using the coredump socket
> cannot get a handle on the task that took the signal and is writing the
> coredump easily. Workloads interested in per-thread authentification
> have similar problems.
>
> Add SO_PASSPIDFD_THREAD/SCM_PIDFD_THREAD and SO_PEERPIDFD_THREAD. We
> record the sending and the connecting thread in addition to the
> thread-group leader.
>
> [...]
Ok, stable branch it is as discussed.
---
Applied to the vfs-7.4.shared.net-next.pidfd branch of the vfs/vfs.git tree.
Patches in the vfs-7.4.shared.net-next.pidfd branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.
Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs-7.4.shared.net-next.pidfd
[01/10] pid: add helpers to operate on a struct pid array
https://git.kernel.org/vfs/vfs/c/fd0349aadbb9
[02/10] af_unix: record the pid of the sending thread
https://git.kernel.org/vfs/vfs/c/c48ae5bcfc35
[03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD
https://git.kernel.org/vfs/vfs/c/be3a5e06b962
[04/10] selftests/net: SO_PASSPIDFD_THREAD
https://git.kernel.org/vfs/vfs/c/4e2446c83f38
[05/10] net: turn sk_peer_pid into an array indexed by pid type
https://git.kernel.org/vfs/vfs/c/95d9b7369665
[06/10] af_unix: record the pid of the connecting thread
https://git.kernel.org/vfs/vfs/c/1592d89361e9
[07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd
https://git.kernel.org/vfs/vfs/c/e10899b2a098
[08/10] selftests/net: SO_PEERPIDFD_THREAD
https://git.kernel.org/vfs/vfs/c/02e658dffe50
[09/10] pidfs: record the coredump on the dumping thread's pid too
https://git.kernel.org/vfs/vfs/c/3209535cd39e
[10/10] selftests/coredump: check the dumping thread's pidfd
https://git.kernel.org/vfs/vfs/c/1f47b275d5d5
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v4 00/10] net: support thread-specific pidfds for send and connect
2026-10-05 15:09 [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Andrew Halaney
` (10 preceding siblings ...)
2026-10-12 9:00 ` [PATCH v4 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
@ 2026-10-12 15:59 ` Paolo Abeni
2026-10-12 18:14 ` Andrew Halaney
11 siblings, 1 reply; 18+ messages in thread
From: Paolo Abeni @ 2026-10-12 15:59 UTC (permalink / raw)
To: Andrew Halaney, Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Simon Horman, Willem de Bruijn, netdev,
linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Eric Dumazet
On 10/5/26 17:09, Andrew Halaney wrote:
> SO_PASSPIDFD/SCM_PIDFD and SO_PEERPIDFD allow to retrieve a pidfd for
> the thread-group leader. The coredump server using the coredump socket
> cannot get a handle on the task that took the signal and is writing the
> coredump easily. Workloads interested in per-thread authentification
> have similar problems.
>
> Add SO_PASSPIDFD_THREAD/SCM_PIDFD_THREAD and SO_PEERPIDFD_THREAD. We
> record the sending and the connecting thread in addition to the
> thread-group leader.
>
> SO_PASSPIDFD_THREAD functions like SO_PASSPIDFD but instead of an
> SCM_PIDFD message for the thread-group leader, SCM_PIDFD_THREAD sends a
> pidfd for the specific thread. SO_PASSPIDFD / SO_PASSPIDFD_THREAD are
> independent, allowing callers to get both styles of pidfd back in their
> cmsg.
>
> SO_PEERPIDFD_THREAD allows to retrieve a pidfd for the specific thread
> that called connect(), listen(), or socketpair().
>
> pidfs_coredump() now also stamps the dumping thread's struct pid so a
> pidfd for that thread reports the coredump like the pidfd of the
> thread-group leader does.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> Signed-off-by: Andrew Halaney <andrew@amutable.com>
>
> ---
> Changes in v4:
> - Moved get_pid() out of scm_set_cred() into scm_send() (Kuniyuki)
> - Dropped unnecessary helps (Kuniyuki)
> - Rebased on v7.3-r6
> - Collected Kuniyuki's RB tags
> - Link to v3: https://lore.kernel.org/r/20260928-work-unix-passpidfd-v3-0-532018a91b21@amutable.com
Out of sheer ignorance, I don't see the shared, stable tree where we
can pull from, can you please point that out?
I think that this late in the cycle, the series could go via Christian's
tree only and we could get it back after the merge window. WDYT?
Thanks,
Paolo
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v4 00/10] net: support thread-specific pidfds for send and connect
2026-10-12 15:59 ` Paolo Abeni
@ 2026-10-12 18:14 ` Andrew Halaney
0 siblings, 0 replies; 18+ messages in thread
From: Andrew Halaney @ 2026-10-12 18:14 UTC (permalink / raw)
To: Paolo Abeni
Cc: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov,
David S. Miller, Simon Horman, Willem de Bruijn, netdev,
linux-kernel, Alexander Viro, Jan Kara, linux-fsdevel,
Alexander Mikhalitsyn, Christian Brauner (Amutable),
Eric Dumazet
On Mon, Oct 12, 2026 at 05:59:17PM +0200, Paolo Abeni wrote:
> On 10/5/26 17:09, Andrew Halaney wrote:
> > SO_PASSPIDFD/SCM_PIDFD and SO_PEERPIDFD allow to retrieve a pidfd for
> > the thread-group leader. The coredump server using the coredump socket
> > cannot get a handle on the task that took the signal and is writing the
> > coredump easily. Workloads interested in per-thread authentification
> > have similar problems.
> >
> > Add SO_PASSPIDFD_THREAD/SCM_PIDFD_THREAD and SO_PEERPIDFD_THREAD. We
> > record the sending and the connecting thread in addition to the
> > thread-group leader.
> >
> > SO_PASSPIDFD_THREAD functions like SO_PASSPIDFD but instead of an
> > SCM_PIDFD message for the thread-group leader, SCM_PIDFD_THREAD sends a
> > pidfd for the specific thread. SO_PASSPIDFD / SO_PASSPIDFD_THREAD are
> > independent, allowing callers to get both styles of pidfd back in their
> > cmsg.
> >
> > SO_PEERPIDFD_THREAD allows to retrieve a pidfd for the specific thread
> > that called connect(), listen(), or socketpair().
> >
> > pidfs_coredump() now also stamps the dumping thread's struct pid so a
> > pidfd for that thread reports the coredump like the pidfd of the
> > thread-group leader does.
> >
> > Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> > Signed-off-by: Andrew Halaney <andrew@amutable.com>
> >
> > ---
> > Changes in v4:
> > - Moved get_pid() out of scm_set_cred() into scm_send() (Kuniyuki)
> > - Dropped unnecessary helps (Kuniyuki)
> > - Rebased on v7.3-r6
> > - Collected Kuniyuki's RB tags
> > - Link to v3: https://lore.kernel.org/r/20260928-work-unix-passpidfd-v3-0-532018a91b21@amutable.com
> Out of sheer ignorance, I don't see the shared, stable tree where we
> can pull from, can you please point that out?
Here ya go!
https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git/log/?h=vfs-7.4.shared.net-next.pidfd
>
> I think that this late in the cycle, the series could go via Christian's
> tree only and we could get it back after the merge window. WDYT?
This works for me at least, for what its worth.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 18+ messages in thread