* [PATCH 00/10] net: support thread-specific pidfds for send and connect
@ 2026-08-31 11:21 Christian Brauner
2026-08-31 11:21 ` [PATCH 01/10] pid: add helpers to operate on a struct pid array Christian Brauner
` (9 more replies)
0 siblings, 10 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
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 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 sends a pidfd
for the specific thread. SO_PASSPIDFD_THREAD is mutually exclusive with
SO_PASSPIDFD. The last one set takes precedence and disables the other
one. Both SO_PASSCRED and SO_PASSPIDFD receivers see one writer per
process as before.
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>
---
Christian Brauner (10):
pid: add helpers to operate on a struct pid array
af_unix: record the pid of the sending thread
net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD
selftests/net: SO_PASSPIDFD_THREAD
net: turn sk_peer_pid into an array indexed by pid type
af_unix: record the pid of the connecting thread
net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd
selftests/net: SO_PEERPIDFD_THREAD
pidfs: record the coredump on the dumping thread's pid too
selftests/coredump: check the dumping thread's pidfd
arch/alpha/include/uapi/asm/socket.h | 4 +
arch/mips/include/uapi/asm/socket.h | 4 +
arch/parisc/include/uapi/asm/socket.h | 4 +
arch/sparc/include/uapi/asm/socket.h | 4 +
fs/coredump.c | 22 +-
fs/pidfs.c | 27 +-
include/linux/coredump.h | 4 +-
include/linux/pid.h | 53 +++
include/linux/pid_types.h | 8 +
include/linux/pidfs.h | 5 +
include/linux/sched/signal.h | 18 +
include/net/scm.h | 7 +-
include/net/sock.h | 14 +-
include/uapi/asm-generic/socket.h | 4 +
net/bluetooth/af_bluetooth.c | 6 +-
net/bluetooth/hci_sock.c | 8 +-
net/bluetooth/l2cap_sock.c | 2 +-
net/core/scm.c | 36 +-
net/core/sock.c | 120 ++++---
net/unix/af_unix.c | 67 ++--
net/unix/af_unix.h | 3 +-
.../selftests/coredump/coredump_socket_test.c | 175 ++++++++++
tools/testing/selftests/coredump/coredump_test.h | 2 +
.../selftests/coredump/coredump_test_helpers.c | 49 +++
tools/testing/selftests/net/af_unix/Makefile | 2 +
tools/testing/selftests/net/af_unix/scm_pidfd.c | 366 +++++++++++++++++++++
26 files changed, 905 insertions(+), 109 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260825-work-unix-passpidfd-086a1ec09e8a
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 01/10] pid: add helpers to operate on a struct pid array
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
2026-08-31 11:21 ` [PATCH 02/10] af_unix: record the pid of the sending thread Christian Brauner
` (8 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
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>
---
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 +++++++++++++++
5 files changed, 100 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 584ae88b435e..9444b47789a0 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -677,6 +677,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];
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 02/10] af_unix: record the pid of the sending thread
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
2026-08-31 11:21 ` [PATCH 01/10] pid: add helpers to operate on a struct pid array Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
2026-08-31 11:21 ` [PATCH 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD Christian Brauner
` (7 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
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>
---
include/net/scm.h | 7 +++----
net/core/scm.c | 21 +++++++++++++++++----
net/unix/af_unix.c | 23 +++++++++++++----------
net/unix/af_unix.h | 3 ++-
4 files changed, 35 insertions(+), 19 deletions(-)
diff --git a/include/net/scm.h b/include/net/scm.h
index 86ae6bc109ec..aa7d15c5fc27 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,7 @@ 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->pid[PIDTYPE_TGID] = get_pid(pid);
scm->creds.pid = pid_vnr(pid);
scm->creds.uid = uid;
scm->creds.gid = gid;
@@ -77,8 +77,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)
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 13f9926bf205..011af84e3626 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,10 @@ 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->creds.pid = pid_vnr(scm->pid[PIDTYPE_TGID]);
+ scm->creds.uid = UNIXCB(skb).uid;
+ scm->creds.gid = UNIXCB(skb).gid;
unix_set_secdata(scm, skb);
}
@@ -2025,20 +2028,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 +2051,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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
2026-08-31 11:21 ` [PATCH 01/10] pid: add helpers to operate on a struct pid array Christian Brauner
2026-08-31 11:21 ` [PATCH 02/10] af_unix: record the pid of the sending thread Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
2026-08-31 11:21 ` [PATCH 04/10] selftests/net: SO_PASSPIDFD_THREAD Christian Brauner
` (6 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
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. Either option
makes recvmsg() deliver an SCM_PIDFD. SO_PASSPIDFD sends a pidfd for the
thread-group leader and SO_PASSPIDFD_THREAD sends a pidfd for the
specific thread.
The two options are mutually exclusive. Enabling one switches the other
off, so getsockopt() always reports which of the two is active.
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>
---
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/net/sock.h | 10 +++++++++-
include/uapi/asm-generic/socket.h | 2 ++
net/core/scm.c | 19 +++++++++++++------
net/core/sock.c | 26 ++++++++++++++++++++++++--
net/unix/af_unix.c | 11 ++++++++---
9 files changed, 64 insertions(+), 12 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/net/sock.h b/include/net/sock.h
index 51185222aac2..fc09c92e8a83 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;
@@ -2986,6 +2988,12 @@ static inline bool sk_is_stream_unix(const struct sock *sk)
return sk_is_unix(sk) && sk->sk_type == SOCK_STREAM;
}
+/* SO_PASSPIDFD or SO_PASSPIDFD_THREAD asked for an SCM_PIDFD. */
+static inline bool sk_scm_pidfd_wanted(const struct sock *sk)
+{
+ return sk->sk_scm_pidfd || sk->sk_scm_pidfd_thread;
+}
+
static inline bool sk_is_vsock(const struct sock *sk)
{
return sk->sk_family == AF_VSOCK;
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..d69768414af4 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -499,9 +499,13 @@ 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 sock *sk, struct msghdr *msg,
+ struct scm_cookie *scm)
{
+ enum pid_type type = sk->sk_scm_pidfd_thread ? PIDTYPE_PID : PIDTYPE_TGID;
+ struct pid *pid = scm->pid[type];
struct file *pidfd_file = NULL;
+ unsigned int flags = PIDFD_STALE;
int len, pidfd;
/* put_cmsg() doesn't return an error if CMSG is truncated,
@@ -517,10 +521,13 @@ 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;
+
+ pidfd = pidfd_prepare(pid, flags, &pidfd_file);
if (put_cmsg(msg, SOL_SOCKET, SCM_PIDFD, sizeof(int), &pidfd)) {
if (pidfd_file) {
@@ -539,7 +546,7 @@ static bool __scm_recv_common(struct sock *sk, struct msghdr *msg,
struct scm_cookie *scm, int flags)
{
if (!msg->msg_control) {
- if (sk->sk_scm_credentials || sk->sk_scm_pidfd ||
+ if (sk->sk_scm_credentials || sk_scm_pidfd_wanted(sk) ||
scm->fp || scm_has_secdata(sk))
msg->msg_flags |= MSG_CTRUNC;
@@ -586,8 +593,8 @@ void scm_recv_unix(struct socket *sock, struct msghdr *msg,
scm_detach_fds(msg, scm, READ_ONCE(u->scm_rights_notrunc));
}
- if (sock->sk->sk_scm_pidfd)
- scm_pidfd_recv(msg, scm);
+ if (sk_scm_pidfd_wanted(sock->sk))
+ scm_pidfd_recv(sock->sk, msg, scm);
scm_destroy_cred(scm);
}
diff --git a/net/core/sock.c b/net/core/sock.c
index 1ad41904db25..f9615b0de10e 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1571,10 +1571,25 @@ int sk_setsockopt(struct sock *sk, int level, int optname,
break;
case SO_PASSPIDFD:
- if (sk_is_unix(sk))
+ if (sk_is_unix(sk)) {
+ /* Mutually exclusive with SO_PASSPIDFD_THREAD. */
sk->sk_scm_pidfd = valbool;
- else
+ if (valbool)
+ sk->sk_scm_pidfd_thread = 0;
+ } else {
+ ret = -EOPNOTSUPP;
+ }
+ break;
+
+ case SO_PASSPIDFD_THREAD:
+ if (sk_is_unix(sk)) {
+ /* Mutually exclusive with SO_PASSPIDFD. */
+ sk->sk_scm_pidfd_thread = valbool;
+ if (valbool)
+ sk->sk_scm_pidfd = 0;
+ } else {
ret = -EOPNOTSUPP;
+ }
break;
case SO_PASSRIGHTS:
@@ -1892,6 +1907,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 011af84e3626..468a9c479b87 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -803,7 +803,7 @@ 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_scm_pidfd_wanted(sk);
}
static int unix_listen(struct socket *sock, int backlog)
@@ -2048,9 +2048,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) &&
@@ -3029,7 +3034,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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 04/10] selftests/net: SO_PASSPIDFD_THREAD
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
` (2 preceding siblings ...)
2026-08-31 11:21 ` [PATCH 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
2026-08-31 11:21 ` [PATCH 05/10] net: turn sk_peer_pid into an array indexed by pid type Christian Brauner
` (5 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
Extend the af_unix scm_pidfd selftest with SO_PASSPIDFD_THREAD tests.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
tools/testing/selftests/net/af_unix/Makefile | 2 +
tools/testing/selftests/net/af_unix/scm_pidfd.c | 269 ++++++++++++++++++++++++
2 files changed, 271 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..019c48e1cdcd 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,10 @@
#define SCM_PIDFD 0x04
#endif
+#ifndef SO_PASSPIDFD_THREAD
+#define SO_PASSPIDFD_THREAD 86
+#endif
+
#define CHILD_EXIT_CODE_OK 123
static void child_die()
@@ -553,4 +558,268 @@ TEST_F(scm_pidfd, test)
close(pfd);
}
+struct thread_ids {
+ pid_t pid;
+ pid_t tid;
+};
+
+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;
+
+ /* 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;
+
+ if (read(fd, &sync, 1) != 1)
+ return (void *)1;
+
+ return NULL;
+}
+
+static void thread_client(int fd, int syncfd, void *(*sender)(void *))
+{
+ pthread_t thread;
+ void *ret;
+ char sync;
+
+ /* wait until the receiver enabled SO_PASSPIDFD */
+ if (read(syncfd, &sync, 1) != 1)
+ child_die();
+
+ if (pthread_create(&thread, NULL, sender, &fd))
+ child_die();
+
+ if (pthread_join(thread, &ret) || ret)
+ child_die();
+
+ exit(0);
+}
+
+static int recv_pidfd(int fd, struct thread_ids *ids)
+{
+ char control[CMSG_SPACE(sizeof(int))] = { 0 };
+ struct iovec iov;
+ struct msghdr msg = { 0 };
+ struct cmsghdr *cmsg;
+ int pidfd = -1;
+
+ 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);
+
+ if (recvmsg(fd, &msg, 0) != sizeof(*ids)) {
+ log_err("recvmsg");
+ return -1;
+ }
+
+ if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
+ log_err("recvmsg: truncated");
+ return -1;
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_PIDFD)
+ memcpy(&pidfd, CMSG_DATA(cmsg), sizeof(pidfd));
+ }
+
+ return pidfd;
+}
+
+static int thread_pidfd_flow(void *(*sender)(void *), int optname,
+ struct thread_ids *ids, struct pidfd_info *info)
+{
+ int sk[2];
+ int syncpipe[2];
+ int pidfd;
+ int child_status = 0;
+ pid_t child;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sk))
+ return -1;
+
+ if (pipe(syncpipe))
+ return -1;
+
+ child = fork();
+ if (child < 0)
+ return -1;
+
+ if (child == 0) {
+ close(sk[0]);
+ close(syncpipe[1]);
+ thread_client(sk[1], syncpipe[0], sender);
+ }
+ close(sk[1]);
+ close(syncpipe[0]);
+
+ int on = 1;
+
+ if (setsockopt(sk[0], SOL_SOCKET, optname, &on, sizeof(on))) {
+ log_err("Failed to set pidfd passing option");
+ return -1;
+ }
+
+ if (write(syncpipe[1], "1", 1) != 1)
+ return -1;
+ close(syncpipe[1]);
+
+ pidfd = recv_pidfd(sk[0], ids);
+ if (pidfd < 0)
+ 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);
+
+ /* release the sending thread */
+ 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));
+
+ 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));
+
+ /* The option set last wins. */
+ ASSERT_EQ(0, sockopt_set(sk[0], SO_PASSPIDFD, 1));
+ ASSERT_EQ(1, sockopt_get(sk[0], SO_PASSPIDFD));
+ ASSERT_EQ(0, sockopt_get(sk[0], SO_PASSPIDFD_THREAD));
+
+ 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));
+
+ /* Disabling one option leaves the other alone. */
+ ASSERT_EQ(0, sockopt_set(sk[0], SO_PASSPIDFD, 0));
+ 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));
+ ASSERT_EQ(0, sockopt_get(sk[0], SO_PASSPIDFD_THREAD));
+
+ close(sk[0]);
+ close(sk[1]);
+}
+
+TEST(scm_pidfd_thread)
+{
+ struct thread_ids ids;
+ struct pidfd_info info;
+
+ ASSERT_EQ(0, thread_pidfd_flow(send_ids_thread, SO_PASSPIDFD_THREAD,
+ &ids, &info));
+ ASSERT_NE(ids.pid, ids.tid);
+ EXPECT_EQ(ids.tid, info.pid);
+ EXPECT_EQ(ids.pid, info.tgid);
+}
+
+TEST(scm_pidfd_thread_group)
+{
+ struct thread_ids ids;
+ struct pidfd_info info;
+
+ ASSERT_EQ(0, thread_pidfd_flow(send_ids_thread, SO_PASSPIDFD,
+ &ids, &info));
+ ASSERT_NE(ids.pid, ids.tid);
+ EXPECT_EQ(ids.pid, info.pid);
+ EXPECT_EQ(ids.pid, info.tgid);
+}
+
+TEST(scm_pidfd_thread_creds)
+{
+ struct thread_ids ids;
+ struct pidfd_info info;
+
+ ASSERT_EQ(0, thread_pidfd_flow(send_ids_creds_thread, SO_PASSPIDFD_THREAD,
+ &ids, &info));
+ ASSERT_NE(ids.pid, ids.tid);
+ EXPECT_EQ(ids.tid, info.pid);
+ EXPECT_EQ(ids.pid, info.tgid);
+}
+
TEST_HARNESS_MAIN
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 05/10] net: turn sk_peer_pid into an array indexed by pid type
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
` (3 preceding siblings ...)
2026-08-31 11:21 ` [PATCH 04/10] selftests/net: SO_PASSPIDFD_THREAD Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
2026-09-02 0:20 ` Jakub Kicinski
2026-08-31 11:21 ` [PATCH 06/10] af_unix: record the pid of the connecting thread Christian Brauner
` (4 subsequent siblings)
9 siblings, 1 reply; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
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>
---
fs/coredump.c | 2 +-
include/net/sock.h | 4 ++--
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 +++++++-------
7 files changed, 23 insertions(+), 22 deletions(-)
diff --git a/fs/coredump.c b/fs/coredump.c
index ac3cd74808c6..71a0093ada1b 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 fc09c92e8a83..67b743bab220 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/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 070ca388f9ac..91e4738eabdb 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -273,21 +273,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 f9615b0de10e..6ada7e7eb7d7 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1928,7 +1928,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))
@@ -1947,7 +1948,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)
@@ -2401,7 +2402,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);
@@ -3797,7 +3798,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 468a9c479b87..335abd23c9bf 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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 06/10] af_unix: record the pid of the connecting thread
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
` (4 preceding siblings ...)
2026-08-31 11:21 ` [PATCH 05/10] net: turn sk_peer_pid into an array indexed by pid type Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
2026-08-31 11:21 ` [PATCH 07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd Christian Brauner
` (3 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
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>
---
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 335abd23c9bf..d01ee76c8026 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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
` (5 preceding siblings ...)
2026-08-31 11:21 ` [PATCH 06/10] af_unix: record the pid of the connecting thread Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
2026-08-31 11:21 ` [PATCH 08/10] selftests/net: SO_PEERPIDFD_THREAD Christian Brauner
` (2 subsequent siblings)
9 siblings, 0 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
SO_PEERPIDFD hands out a pidfd for the thread-group that called
connect() or socketpair(). Enable workloads such as the coredump server
or a broker to get a pidfd of the specific thread that connected to the
socket.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
arch/alpha/include/uapi/asm/socket.h | 2 +
arch/mips/include/uapi/asm/socket.h | 2 +
arch/parisc/include/uapi/asm/socket.h | 2 +
arch/sparc/include/uapi/asm/socket.h | 2 +
include/uapi/asm-generic/socket.h | 2 +
net/core/sock.c | 87 ++++++++++++++++++++---------------
net/unix/af_unix.c | 1 +
7 files changed, 61 insertions(+), 37 deletions(-)
diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h
index bb3d534826bb..5d3524c26b2b 100644
--- a/arch/alpha/include/uapi/asm/socket.h
+++ b/arch/alpha/include/uapi/asm/socket.h
@@ -159,6 +159,8 @@
#define SO_PASSPIDFD_THREAD 86
+#define SO_PEERPIDFD_THREAD 87
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h
index 269badcaa086..245a43f52fb1 100644
--- a/arch/mips/include/uapi/asm/socket.h
+++ b/arch/mips/include/uapi/asm/socket.h
@@ -170,6 +170,8 @@
#define SO_PASSPIDFD_THREAD 86
+#define SO_PEERPIDFD_THREAD 87
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h
index 313aee10a52c..f23710e1c671 100644
--- a/arch/parisc/include/uapi/asm/socket.h
+++ b/arch/parisc/include/uapi/asm/socket.h
@@ -151,6 +151,8 @@
#define SO_PASSPIDFD_THREAD 0x4054
+#define SO_PEERPIDFD_THREAD 0x4055
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64
diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h
index bd3e69bcce7a..b35b25bdefc2 100644
--- a/arch/sparc/include/uapi/asm/socket.h
+++ b/arch/sparc/include/uapi/asm/socket.h
@@ -152,6 +152,8 @@
#define SO_PASSPIDFD_THREAD 0x005f
+#define SO_PEERPIDFD_THREAD 0x0060
+
#if !defined(__KERNEL__)
diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h
index d1e5c6de146d..56fed7ab27ab 100644
--- a/include/uapi/asm-generic/socket.h
+++ b/include/uapi/asm-generic/socket.h
@@ -154,6 +154,8 @@
#define SO_PASSPIDFD_THREAD 86
+#define SO_PEERPIDFD_THREAD 87
+
#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
diff --git a/net/core/sock.c b/net/core/sock.c
index 6ada7e7eb7d7..cb2ffd329bc6 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1743,6 +1743,50 @@ static int groups_to_user(sockptr_t dst, const struct group_info *src)
return 0;
}
+/* Hand out a pidfd for @type of the socket's peer via SO_PEERPIDFD*. */
+static int sk_getsockopt_peerpidfd(struct sock *sk, enum pid_type type,
+ sockptr_t optval, sockptr_t optlen, int len)
+{
+ struct file *pidfd_file = NULL;
+ unsigned int flags = 0;
+ struct pid *peer_pid;
+ int pidfd;
+
+ if (len > sizeof(pidfd))
+ len = sizeof(pidfd);
+
+ spin_lock(&sk->sk_peer_lock);
+ peer_pid = get_pid(sk->sk_peer_pid[type]);
+ spin_unlock(&sk->sk_peer_lock);
+
+ if (!peer_pid)
+ return -ENODATA;
+
+ /* The use of PIDFD_STALE requires stashing of struct pid on pidfs
+ * with pidfs_register_pid() and only AF_UNIX were prepared for this.
+ */
+ if (sk->sk_family == AF_UNIX)
+ flags |= PIDFD_STALE;
+ if (type == PIDTYPE_PID)
+ flags |= PIDFD_THREAD;
+
+ pidfd = pidfd_prepare(peer_pid, flags, &pidfd_file);
+ put_pid(peer_pid);
+ if (pidfd < 0)
+ return pidfd;
+
+ if (copy_to_sockptr(optval, &pidfd, len) ||
+ copy_to_sockptr(optlen, &len, sizeof(int))) {
+ put_unused_fd(pidfd);
+ fput(pidfd_file);
+
+ return -EFAULT;
+ }
+
+ fd_install(pidfd, pidfd_file);
+ return 0;
+}
+
int sk_getsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, sockptr_t optlen)
{
@@ -1938,45 +1982,14 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
}
case SO_PEERPIDFD:
- {
- struct pid *peer_pid;
- struct file *pidfd_file = NULL;
- unsigned int flags = 0;
- int pidfd;
-
- if (len > sizeof(pidfd))
- len = sizeof(pidfd);
-
- spin_lock(&sk->sk_peer_lock);
- peer_pid = get_pid(sk->sk_peer_pid[PIDTYPE_TGID]);
- spin_unlock(&sk->sk_peer_lock);
-
- if (!peer_pid)
- return -ENODATA;
-
- /* The use of PIDFD_STALE requires stashing of struct pid
- * on pidfs with pidfs_register_pid() and only AF_UNIX
- * were prepared for this.
- */
- if (sk->sk_family == AF_UNIX)
- flags = PIDFD_STALE;
+ return sk_getsockopt_peerpidfd(sk, PIDTYPE_TGID, optval, optlen, len);
- pidfd = pidfd_prepare(peer_pid, flags, &pidfd_file);
- put_pid(peer_pid);
- if (pidfd < 0)
- return pidfd;
-
- if (copy_to_sockptr(optval, &pidfd, len) ||
- copy_to_sockptr(optlen, &len, sizeof(int))) {
- put_unused_fd(pidfd);
- fput(pidfd_file);
-
- return -EFAULT;
- }
+ case SO_PEERPIDFD_THREAD:
+ /* Only AF_UNIX records the peer's connecting thread. */
+ if (!sk_is_unix(sk))
+ return -EOPNOTSUPP;
- fd_install(pidfd, pidfd_file);
- return 0;
- }
+ return sk_getsockopt_peerpidfd(sk, PIDTYPE_PID, optval, optlen, len);
case SO_PEERGROUPS:
{
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index d01ee76c8026..563d9827c5cc 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1056,6 +1056,7 @@ static bool unix_bpf_bypass_getsockopt(int level, int optname)
if (level == SOL_SOCKET) {
switch (optname) {
case SO_PEERPIDFD:
+ case SO_PEERPIDFD_THREAD:
return true;
default:
return false;
--
2.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 08/10] selftests/net: SO_PEERPIDFD_THREAD
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
` (6 preceding siblings ...)
2026-08-31 11:21 ` [PATCH 07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
2026-08-31 11:21 ` [PATCH 09/10] pidfs: record the coredump on the dumping thread's pid too Christian Brauner
2026-08-31 11:21 ` [PATCH 10/10] selftests/coredump: check the dumping thread's pidfd Christian Brauner
9 siblings, 0 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
Add tests for SO_PEERPIDFD_THREAD.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
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 019c48e1cdcd..bbbd5e7b4fa7 100644
--- a/tools/testing/selftests/net/af_unix/scm_pidfd.c
+++ b/tools/testing/selftests/net/af_unix/scm_pidfd.c
@@ -32,6 +32,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()
@@ -822,4 +826,97 @@ TEST(scm_pidfd_thread_creds)
EXPECT_EQ(ids.pid, 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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 09/10] pidfs: record the coredump on the dumping thread's pid too
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
` (7 preceding siblings ...)
2026-08-31 11:21 ` [PATCH 08/10] selftests/net: SO_PEERPIDFD_THREAD Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
2026-08-31 11:21 ` [PATCH 10/10] selftests/coredump: check the dumping thread's pidfd Christian Brauner
9 siblings, 0 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
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>
---
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 71a0093ada1b..b5ff4b3e1831 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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 10/10] selftests/coredump: check the dumping thread's pidfd
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
` (8 preceding siblings ...)
2026-08-31 11:21 ` [PATCH 09/10] pidfs: record the coredump on the dumping thread's pid too Christian Brauner
@ 2026-08-31 11:21 ` Christian Brauner
9 siblings, 0 replies; 12+ messages in thread
From: Christian Brauner @ 2026-08-31 11:21 UTC (permalink / raw)
To: Jakub Kicinski, Kuniyuki Iwashima, Oleg Nesterov
Cc: David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, netdev, linux-kernel, Alexander Viro, Jan Kara,
linux-fsdevel, Alexander Mikhalitsyn,
Christian Brauner (Amutable)
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>
---
.../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.53.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 05/10] net: turn sk_peer_pid into an array indexed by pid type
2026-08-31 11:21 ` [PATCH 05/10] net: turn sk_peer_pid into an array indexed by pid type Christian Brauner
@ 2026-09-02 0:20 ` Jakub Kicinski
0 siblings, 0 replies; 12+ messages in thread
From: Jakub Kicinski @ 2026-09-02 0:20 UTC (permalink / raw)
To: Christian Brauner
Cc: Kuniyuki Iwashima, Oleg Nesterov, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Willem de Bruijn, netdev, linux-kernel,
Alexander Viro, Jan Kara, linux-fsdevel, Alexander Mikhalitsyn
On Mon, 31 Aug 2026 13:21:17 +0200 Christian Brauner wrote:
> 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.
This patch suffers from missing kdoc and general inability to build:
Warning: include/net/sock.h:606 struct member 'PIDTYPE_TGID' not described in 'sock'
Warning: include/net/sock.h:606 Excess struct member 'sk_peer_pid' description in 'sock' (did you mean one of: '#define sk_portpair __sk_common.skc_portpair', 'sk_peer_cred', 'sk_peer_lock', 'sk_peek_off')
include/trace/events/landlock.h:946:13: error: incompatible pointer types assigning to 'struct pid *' from 'struct pid *const volatile *' [-Wincompatible-pointer-types]
What's the merging path for these? Judging by the To: perhaps you
expect us to take these directly?
--
pw-bot: cr
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-09-02 0:20 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 11:21 [PATCH 00/10] net: support thread-specific pidfds for send and connect Christian Brauner
2026-08-31 11:21 ` [PATCH 01/10] pid: add helpers to operate on a struct pid array Christian Brauner
2026-08-31 11:21 ` [PATCH 02/10] af_unix: record the pid of the sending thread Christian Brauner
2026-08-31 11:21 ` [PATCH 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD Christian Brauner
2026-08-31 11:21 ` [PATCH 04/10] selftests/net: SO_PASSPIDFD_THREAD Christian Brauner
2026-08-31 11:21 ` [PATCH 05/10] net: turn sk_peer_pid into an array indexed by pid type Christian Brauner
2026-09-02 0:20 ` Jakub Kicinski
2026-08-31 11:21 ` [PATCH 06/10] af_unix: record the pid of the connecting thread Christian Brauner
2026-08-31 11:21 ` [PATCH 07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd Christian Brauner
2026-08-31 11:21 ` [PATCH 08/10] selftests/net: SO_PEERPIDFD_THREAD Christian Brauner
2026-08-31 11:21 ` [PATCH 09/10] pidfs: record the coredump on the dumping thread's pid too Christian Brauner
2026-08-31 11:21 ` [PATCH 10/10] selftests/coredump: check the dumping thread's pidfd Christian Brauner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox