From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DD63E3C13EE; Mon, 31 Aug 2026 11:21:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175294; cv=none; b=ilRaTOYMQfbHZVk6C/eN0D5lafzcbfVgj6UBTCAh5q/f2QIRCKJX7QD+1vQ3fBvYssNxnKekJWenmGXlf3NTbJfRos2OePnE1VT5AC+nIvxkWUSsxLMkO+TqOS8w4IAKYAE1uzdkkZRyLnV73OW8lqP1EMPdzQKCFrdPLYx9ZAg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175294; c=relaxed/simple; bh=LUKVa7rLOIRndX/gkjeiUcPZzv/vThjjRyJ8HPrxUEY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=s26Ut1ytTnH52d/SKDgFBRs4bXRNWjd5qCgNvEUlo9GDQsoaI8P5RtIovQt3Qy2mqqP9vyz7b6c/6mEy45nWIuhLe7mqmg4WZo5qPB9BZ+kJXBsROh+bHUBgVEuelHYoYn++KfUuH89duz3rxLy3hhboH8tjf7jXbZbzwGM4ihk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ZE643WAM; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ZE643WAM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 19AF21F00A3D; Mon, 31 Aug 2026 11:21:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175290; bh=6Z2+FLpNeiWaTbE//chvaqQodcUI++k90sFFBLQ+Abw=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ZE643WAMPLg4MTGHOU4C0dLDTe0QVPdRqj7kJakLM+NH9DHX2pc3SM11lSlWhLgCC iDBdg1AzoiBHXFPIaFnyNmsYGpamvD7CV1XZ5oOUemsLIr/lkF43KaoT1T/OxFKmBJ dMA8b0nYIgjRhvDBDNekq7Yf6u6pVScR2xhuouVfaAF4mXuZ1ZsiWyDDG5x2/Iw00r PQoXb3Th66VGD3w7mP1OfUu+ci5jDitLnTKxhjlmtZdZ0sebG5TdMdPFMjBj2gX++p v6Mq8FNXPxDFDjY+B7BwcT2qe8fRc35rOYkJ4TuUBmt7sZUtrx5nMcwGJlbOzjrp/o qOiR9WEELTqAg== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:13 +0200 Subject: [PATCH 01/10] pid: add helpers to operate on a struct pid array Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-1-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=6155; i=brauner@kernel.org; h=from:subject:message-id; bh=LUKVa7rLOIRndX/gkjeiUcPZzv/vThjjRyJ8HPrxUEY=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd4gaLnbtuP451dMUx92797kZc7maXf3d/g+332+V 5u05xzU7ChlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZjIrC0M/6zid32f+OCAYWXe k0uJaof5s3q2d5y2ml2/JMRhwZNJ2scZGfqnhK3t0eVacN/xKTur+IXFjDHv5FLdYy0uTV3/0bN VixsA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 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) Reviewed-by: Alexander Mikhalitsyn --- 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 gf= p) return 0; } =20 +/* 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 >=3D PIDTYPE_MAX)) + return -EINVAL; + + for (enum pid_type type =3D PIDTYPE_PID; type <=3D last; type++) { + int ret =3D 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 =20 +#include +#include +#include #include #include #include @@ -92,6 +95,56 @@ static inline struct pid *get_pid(struct pid *pid) } =20 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 =3D PIDTYPE_PID; type <=3D last; type++) + dst[type] =3D get_pid(src[type]); +} + +static inline void __put_pids(struct pid **pids, enum pid_type last) +{ + for (enum pid_type type =3D PIDTYPE_PID; type <=3D last; type++) { + put_pid(pids[type]); + pids[type] =3D NULL; + } +} + +static inline void __swap_pids(struct pid **a, struct pid **b, + enum pid_type last) +{ + for (enum pid_type type =3D PIDTYPE_PID; type <=3D 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 =3D PIDTYPE_PID; type <=3D last; type++) + if (a[type] !=3D 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) !=3D 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 =20 +#include + enum pid_type { PIDTYPE_PID, PIDTYPE_TGID, @@ -10,6 +12,12 @@ enum pid_type { PIDTYPE_MAX, }; =20 +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) >=3D PIDTYPE_MAX)] + struct pid_namespace; extern struct pid_namespace init_pid_ns; =20 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 =20 #include +#include =20 struct coredump_params; =20 @@ -32,5 +33,9 @@ static inline int pidfs_register_pid(struct pid *pid) } =20 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(pi= ds)) =20 #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, en= um pid_type type) return pid; } =20 +/* 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 =3D PIDTYPE_PID; type <=3D last; type++) + pids[type] =3D 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 =3D PIDTYPE_PID; type <=3D last; type++) + pids[type] =3D 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), t= ask) + static inline struct pid *task_tgid(struct task_struct *task) { return task->signal->pids[PIDTYPE_TGID]; --=20 2.53.0 From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6253D3DDDBB; Mon, 31 Aug 2026 11:21:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175295; cv=none; b=aymOXKAfHDhE+4W1zuDwc1ta2Pu7wuFhFldCvwrpM3+1e4WDMmJCf7AhtT/ot4S9AVTx2+/YzP+ARX7vC2qXAEANUlLu9D5ITdWEMIqREMVPkM4Ar9lv1lq+Zo24eyj5YFcOtwFQAJHOVZj6A+YNqskgoL6lygUwz2pprlIhetE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175295; c=relaxed/simple; bh=8y7i7XdLNk6YD1ieQIQKQPtW6EPU/d87CtG5vVgfh1w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=B09Gt1u2RCLBrsDCA23037hUQNXddjbM1Txx/OgSaQ7fed7Wkz27kGUJ1b8u8TLUnYSi93SD1RvRlty+UxW9N5NGoN9a+SvGq479TZXmZOWyzWnfV24HRpLXSDM+9Q7keTws2rJ2bzKh8I3KkppZTlIRwrWlStWpkeCeDXJXdcw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FcYLf6t+; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FcYLf6t+" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B8ADA1F000E9; Mon, 31 Aug 2026 11:21:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175294; bh=f7vAua/SuxVzCWdPf5sdxe/QtAK3nj/ceXJVxPpgBGA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=FcYLf6t+LlM1BO9qm87ob0a3d8IwchOV8AYH8r1W4Zs9l9g/A4bDil//1y8tkQsXi gxPa0CsVXBy539tFp8UVMy095LDLpvrvXq+RidnHvadiVKRYDjOjb8Jpn4RfJltqSK /FNdhJ3zN/03Zzvqiu2YX1/4vaW4mAaTafk10QVm36wmJFyUwVQQQrqHX2ZOYtp/pi Z1Zeg5vnjM78BDbgx7S79JESiisWj62b0vFH7U4SLmnQH9o7UjTrM7BuABG2QrkX1g UGEjnGNrZexDKSQoKSrseDXYSJdvCo0RyiFqaTz8RQKcpajriLs2j2zLmjmRfIBzBp yQ+SWqWTG5yGw== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:14 +0200 Subject: [PATCH 02/10] af_unix: record the pid of the sending thread Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-2-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=6397; i=brauner@kernel.org; h=from:subject:message-id; bh=8y7i7XdLNk6YD1ieQIQKQPtW6EPU/d87CtG5vVgfh1w=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd6Y4/vmr6vwwQ0JtxdsMGh3PmKQdXJGaT6HhmgUz 70naTvSO0pZGMS4GGTFFFkc2k3C5ZbzVGw2ytSAmcPKBDKEgYtTACby+gEjw/Ttf/nmsCy1rXZa tMmxyEVy84cdld+Kp5Z0Be+VddabGM7I8Mft7QKdvTemCU4IXZ/uYcnz9lGo/tF79b+znB74GdU 7MwEA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 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) Reviewed-by: Alexander Mikhalitsyn --- 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 { }; =20 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 sock= et *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 =3D get_pid(pid); + scm->pid[PIDTYPE_TGID] =3D get_pid(pid); scm->creds.pid =3D pid_vnr(pid); scm->creds.uid =3D uid; scm->creds.gid =3D gid; @@ -77,8 +77,7 @@ static __inline__ void scm_set_cred(struct scm_cookie *sc= m, =20 static __inline__ void scm_destroy_cred(struct scm_cookie *scm) { - put_pid(scm->pid); - scm->pid =3D NULL; + put_pids(scm->pid); } =20 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); =20 static inline int scm_replace_pid(struct scm_cookie *scm, struct pid *pid) { + struct pid *thread_pid; int err; =20 /* drop all previous references */ @@ -158,7 +159,18 @@ static inline int scm_replace_pid(struct scm_cookie *s= cm, struct pid *pid) if (unlikely(err)) return err; =20 - scm->pid =3D pid; + /* A sender naming its own thread-group sends from the current thread. */ + if (pid =3D=3D task_tgid(current)) + thread_pid =3D task_pid(current); + else + thread_pid =3D pid; + + err =3D pidfs_register_pid(thread_pid); + if (unlikely(err)) + return err; + + scm->pid[PIDTYPE_TGID] =3D pid; + scm->pid[PIDTYPE_PID] =3D get_pid(thread_pid); scm->creds.pid =3D 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; =20 - if (!p->pid || pid_vnr(p->pid) !=3D creds.pid) { + if (!p->pid[PIDTYPE_TGID] || + pid_vnr(p->pid[PIDTYPE_TGID]) !=3D creds.pid) { struct pid *pid; err =3D -ESRCH; pid =3D find_get_pid(creds.pid); @@ -504,10 +517,10 @@ static void scm_pidfd_recv(struct msghdr *msg, struct= scm_cookie *scm) return; } =20 - if (!scm->pid) + if (!scm->pid[PIDTYPE_TGID]) return; =20 - pidfd =3D pidfd_prepare(scm->pid, PIDFD_STALE, &pidfd_file); + pidfd =3D pidfd_prepare(scm->pid[PIDTYPE_TGID], PIDFD_STALE, &pidfd_file); =20 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 =3D {}; =20 - swap(scm.pid, UNIXCB(skb).pid); + swap_pids(scm.pid, UNIXCB(skb).pid); =20 if (UNIXCB(skb).fp) unix_detach_fds(&scm, skb); @@ -1991,7 +1991,7 @@ static int unix_scm_to_skb(struct scm_cookie *scm, st= ruct sk_buff *skb, bool sen { int err =3D 0; =20 - UNIXCB(skb).pid =3D get_pid(scm->pid); + get_pids(UNIXCB(skb).pid, scm->pid); UNIXCB(skb).uid =3D scm->creds.uid; UNIXCB(skb).gid =3D scm->creds.gid; UNIXCB(skb).fp =3D NULL; @@ -2005,7 +2005,10 @@ static int unix_scm_to_skb(struct scm_cookie *scm, s= truct sk_buff *skb, bool sen =20 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 =3D pid_vnr(scm->pid[PIDTYPE_TGID]); + scm->creds.uid =3D UNIXCB(skb).uid; + scm->creds.gid =3D UNIXCB(skb).gid; unix_set_secdata(scm, skb); } =20 @@ -2025,20 +2028,20 @@ static void unix_skb_to_scm(struct sk_buff *skb, st= ruct 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; =20 if (unix_may_passcred(sk) || unix_may_passcred(other) || !other->sk_socket) { - struct pid *pid; int err; =20 - pid =3D task_tgid(current); - err =3D pidfs_register_pid(pid); - if (unlikely(err)) + get_task_pids(UNIXCB(skb).pid, current); + err =3D pidfs_register_pids(UNIXCB(skb).pid); + if (unlikely(err)) { + put_pids(UNIXCB(skb).pid); return err; + } =20 - UNIXCB(skb).pid =3D get_pid(pid); current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid); } =20 @@ -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 =3D=3D scm->pid && + return UNIXCB(skb).pid[PIDTYPE_TGID] =3D=3D 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 =20 +#include #include =20 #define UNIX_HASH_MOD (256 - 1) @@ -11,7 +12,7 @@ struct sock *unix_peer_get(struct sock *sk); =20 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 */ --=20 2.53.0 From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 325813E49C7; Mon, 31 Aug 2026 11:21:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175300; cv=none; b=KmDPw7a7pWb6SvUNJTN1SLrCUHBU6CogltzUSVQTd1zfCJZqOtfESK3R/IZLTiwJpivFfLrVdI2ukG0olGfaL+HWhuyUkadjgr4sfxsHD8s62zZ/7LWSb3CenW72FJgwJ9TCS/0Sb45noVba1wyxYJ9K/J3xrddWv6pNOsIG0Qg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175300; c=relaxed/simple; bh=NLa8uilUxAnPii04OppmXTDEn9mgtxxJISrmS/dAGEo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YqswZuxvpiWhOtlXxShKJ4Ocd75LRR1QPqZYUzO8GilF51Zvf7Hc9y3a4VLJtZOf74xs4SYGpAoSqIL1j7FgUSFYzYikk6/GRbHWc+/I/u2ORM0j/5jAiWRNqU32uIVn2i1ez6i12JfAslgMAmqfQxzUoQEMOhwUOwk+WUHfIhI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=B8UMdrYe; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="B8UMdrYe" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 754B71F00A3D; Mon, 31 Aug 2026 11:21:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175297; bh=GiJ2hHOWnwnvPkGxLjwnC23vL+oGkNd/f/J0V6WYS1k=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=B8UMdrYeA+jyz+nwijxsaePMtHarhde97Ghwhp9N8p7JcuSwUpXmAy+8p2o1d9QtK Eg6zGXp6BybUCY1lKCzIqz24c5wkn6ZkmRHmEzC9GQxDKvynOZzGCLk+fF3KVuu7V8 z5+WwEY7vObBj4q1xFWaYWoNCxNEYXQUz0nOndV9XTJmOGWjGEo5HQpw3dy0nlQJbx SDllx3Dgqwur51qF0CadnUeUHJZsM1RJY/lvy7jr3nTg1M5HIIcMaay2KStAuSj4n3 QBxB3ByyskPZZBVarw/sA70FdQ/0DPs+fLIweJlCRZQ1zIoM6opOByDUQ5AckIlzL1 H2Su527Patdqg== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:15 +0200 Subject: [PATCH 03/10] net: add SO_PASSPIDFD_THREAD to get a thread-specific SCM_PIDFD Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-3-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=9092; i=brauner@kernel.org; h=from:subject:message-id; bh=NLa8uilUxAnPii04OppmXTDEn9mgtxxJISrmS/dAGEo=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd7YV+Pv+rn0VsQT3/OpWUHfc4Ll2q89bd4516JR6 /I+TotPHaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABM5zMHwz9CpxNQmbs93K4ES VvO+Z45TIqT3M0+/d3fWX5HqqSevvmRkWBiU5/ZB4f/MTeemH5bkt/EyKF5Za81zRu//rmNFm/W FWAA= X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 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) Reviewed-by: Alexander Mikhalitsyn --- 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 @@ =20 #define SO_RIGHTS_NOTRUNC 85 =20 +#define SO_PASSPIDFD_THREAD 86 + #if !defined(__KERNEL__) =20 #if __BITS_PER_LONG =3D=3D 64 diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/a= sm/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 @@ =20 #define SO_RIGHTS_NOTRUNC 85 =20 +#define SO_PASSPIDFD_THREAD 86 + #if !defined(__KERNEL__) =20 #if __BITS_PER_LONG =3D=3D 64 diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/ua= pi/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 @@ =20 #define SO_RIGHTS_NOTRUNC 0x4053 =20 +#define SO_PASSPIDFD_THREAD 0x4054 + #if !defined(__KERNEL__) =20 #if __BITS_PER_LONG =3D=3D 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 @@ =20 #define SO_RIGHTS_NOTRUNC 0x005e =20 +#define SO_PASSPIDFD_THREAD 0x005f + #if !defined(__KERNEL__) =20 =20 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 so= ck *sk) return sk_is_unix(sk) && sk->sk_type =3D=3D SOCK_STREAM; } =20 +/* 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 =3D=3D AF_VSOCK; diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/s= ocket.h index 84ea7b92936e..d1e5c6de146d 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -152,6 +152,8 @@ =20 #define SO_RIGHTS_NOTRUNC 85 =20 +#define SO_PASSPIDFD_THREAD 86 + #if !defined(__KERNEL__) =20 #if __BITS_PER_LONG =3D=3D 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 =20 -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 =3D sk->sk_scm_pidfd_thread ? PIDTYPE_PID : PIDTYPE_TG= ID; + struct pid *pid =3D scm->pid[type]; struct file *pidfd_file =3D NULL; + unsigned int flags =3D PIDFD_STALE; int len, pidfd; =20 /* 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; } =20 - if (!scm->pid[PIDTYPE_TGID]) + if (!pid) return; =20 - pidfd =3D pidfd_prepare(scm->pid[PIDTYPE_TGID], PIDFD_STALE, &pidfd_file); + if (type =3D=3D PIDTYPE_PID) + flags |=3D PIDFD_THREAD; + + pidfd =3D pidfd_prepare(pid, flags, &pidfd_file); =20 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 m= sghdr *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 |=3D MSG_CTRUNC; =20 @@ -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)); } =20 - 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); =20 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 o= ptname, break; =20 case SO_PASSPIDFD: - if (sk_is_unix(sk)) + if (sk_is_unix(sk)) { + /* Mutually exclusive with SO_PASSPIDFD_THREAD. */ sk->sk_scm_pidfd =3D valbool; - else + if (valbool) + sk->sk_scm_pidfd_thread =3D 0; + } else { + ret =3D -EOPNOTSUPP; + } + break; + + case SO_PASSPIDFD_THREAD: + if (sk_is_unix(sk)) { + /* Mutually exclusive with SO_PASSPIDFD. */ + sk->sk_scm_pidfd_thread =3D valbool; + if (valbool) + sk->sk_scm_pidfd =3D 0; + } else { ret =3D -EOPNOTSUPP; + } break; =20 case SO_PASSRIGHTS: @@ -1892,6 +1907,13 @@ int sk_getsockopt(struct sock *sk, int level, int op= tname, v.val =3D sk->sk_scm_pidfd; break; =20 + case SO_PASSPIDFD_THREAD: + if (!sk_is_unix(sk)) + return -EOPNOTSUPP; + + v.val =3D 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) =20 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); } =20 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; } =20 -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] !=3D scm->pid[PIDTYPE_PID]) + return false; + return UNIXCB(skb).pid[PIDTYPE_TGID] =3D=3D 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_strea= m_read_state *state, =20 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 */ --=20 2.53.0 From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BBA3B3E5580; Mon, 31 Aug 2026 11:21:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175304; cv=none; b=Hc0WLorw1jIxA6apGdwY86p/Y3JfVuEPblXx3GM6PX6Q7vcuT+PND8wJo8ZELAXQzmD+Xgh63PKHUVIeeLEE8usST/ieEz/WCkFuhdxllnP7u9DFAKGbeczTuI3EXdVuECV+gbQPrBZkREPG7+GjPX1Lu5jZ7BgO1N9vX3bkPQk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175304; c=relaxed/simple; bh=2YnxsfrAF7HQBfWzTLMa2O/I/q1GmiD7jT9cB0DlWWk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=KGLrOP/IvG7bQriZ/iIbWJrCQqhsh/ePFZfEqFV19KQBpwqNheznYv26cEbJvNfeDORD64VICuZ7/Aa1FD92LovlPoISXY5nxXzcCk2Iz71nWDa/9InX3d1Nc4KUrN436LLQ6kjwMrZkAPqbI42vyld8yynOh//SEk+VOJtocjM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YQuoLR3P; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="YQuoLR3P" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 518F81F000E9; Mon, 31 Aug 2026 11:21:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175301; bh=2QGYk5eR/9VO1k3tAFlZXnTVM66BZOkvZZisq9g5IQA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=YQuoLR3P0IfY+colXyvxkX/zsBAbc9C0GMc3muciHXpMg563lwK5C3+FkSlJocBY6 SZwZHpi50jF0AzVOxf9ZRnSuFULuz8ecsUKD+Jaok2k1jqZSpZBW7V6CJMosNO4N+R QV2xb4zXH2Nd4C9ipFpOMEQqetUdg9c4+ksSUYVLExBPREjugDEbcS/0tDHn6iMJ1i oaS2riqHpQuNZAtC71JXMZc+KrpZGl1alF8zqUNOldZBJBOG29vJqRrpIfg+rpOJOv 2hMF5Esd2euaTL2DnhBQ/fiSJQO/k7bvgl3OphgKFeibQuu7c9CtE2oysVXIM12Qba H3yLoMhnxATeg== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:16 +0200 Subject: [PATCH 04/10] selftests/net: SO_PASSPIDFD_THREAD Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-4-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=7771; i=brauner@kernel.org; h=from:subject:message-id; bh=2YnxsfrAF7HQBfWzTLMa2O/I/q1GmiD7jT9cB0DlWWk=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd5o8/656pVwyQmMm0o6t85NfDlx+2RRw9Bpb7aIz NUtsdv6oKOUhUGMi0FWTJHFod0kXG45T8Vmo0wNmDmsTCBDGLg4BWAiP44x/A9mvFtb/Wsmc8R+ pu/cMxbxF+19vEBmjklWP+Oy2/Fbv+kxMvQ+PJSR/OWnxeFJRz64t/3g+FAu1cUpYnX6oeLCnuP ntvEAAA== X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Extend the af_unix scm_pidfd selftest with SO_PASSPIDFD_THREAD tests. Signed-off-by: Christian Brauner (Amutable) Reviewed-by: Alexander Mikhalitsyn --- 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/s= elftests/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 :=3D scm_rights_denial_lsm.bpf.o include ../../lib.mk include ../bpf.mk =20 +$(OUTPUT)/scm_pidfd: CFLAGS +=3D -pthread + $(OUTPUT)/scm_rights_denial_lsm: $(BPFOBJ) $(OUTPUT)/scm_rights_denial_lsm: CFLAGS +=3D -I$(SCRATCH_DIR)/include $(OUTPUT)/scm_rights_denial_lsm: LDLIBS +=3D -lelf -lz diff --git a/tools/testing/selftests/net/af_unix/scm_pidfd.c b/tools/testin= g/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 #include #include +#include #include #include #include @@ -27,6 +28,10 @@ #define SCM_PIDFD 0x04 #endif =20 +#ifndef SO_PASSPIDFD_THREAD +#define SO_PASSPIDFD_THREAD 86 +#endif + #define CHILD_EXIT_CODE_OK 123 =20 static void child_die() @@ -553,4 +558,268 @@ TEST_F(scm_pidfd, test) close(pfd); } =20 +struct thread_ids { + pid_t pid; + pid_t tid; +}; + +static void *send_ids_thread(void *arg) +{ + int fd =3D *(int *)arg; + struct thread_ids ids =3D { + .pid =3D getpid(), + .tid =3D gettid(), + }; + char sync; + + if (send(fd, &ids, sizeof(ids), 0) !=3D sizeof(ids)) + return (void *)1; + + /* stay alive until the receiver has looked at our pidfd */ + if (read(fd, &sync, 1) !=3D 1) + return (void *)1; + + return NULL; +} + +static void *send_ids_creds_thread(void *arg) +{ + int fd =3D *(int *)arg; + struct thread_ids ids =3D { + .pid =3D getpid(), + .tid =3D gettid(), + }; + struct ucred ucred =3D { + .pid =3D getpid(), + .uid =3D getuid(), + .gid =3D getgid(), + }; + char control[CMSG_SPACE(sizeof(ucred))] =3D { 0 }; + struct iovec iov; + struct msghdr msg =3D { 0 }; + struct cmsghdr *cmsg; + char sync; + + iov.iov_base =3D &ids; + iov.iov_len =3D sizeof(ids); + + msg.msg_iov =3D &iov; + msg.msg_iovlen =3D 1; + msg.msg_control =3D control; + msg.msg_controllen =3D sizeof(control); + + cmsg =3D CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level =3D SOL_SOCKET; + cmsg->cmsg_type =3D SCM_CREDENTIALS; + cmsg->cmsg_len =3D CMSG_LEN(sizeof(ucred)); + memcpy(CMSG_DATA(cmsg), &ucred, sizeof(ucred)); + + if (sendmsg(fd, &msg, 0) !=3D sizeof(ids)) + return (void *)1; + + if (read(fd, &sync, 1) !=3D 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) !=3D 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))] =3D { 0 }; + struct iovec iov; + struct msghdr msg =3D { 0 }; + struct cmsghdr *cmsg; + int pidfd =3D -1; + + iov.iov_base =3D ids; + iov.iov_len =3D sizeof(*ids); + + msg.msg_iov =3D &iov; + msg.msg_iovlen =3D 1; + msg.msg_control =3D control; + msg.msg_controllen =3D sizeof(control); + + if (recvmsg(fd, &msg, 0) !=3D sizeof(*ids)) { + log_err("recvmsg"); + return -1; + } + + if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) { + log_err("recvmsg: truncated"); + return -1; + } + + for (cmsg =3D CMSG_FIRSTHDR(&msg); cmsg !=3D NULL; + cmsg =3D CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level =3D=3D SOL_SOCKET && + cmsg->cmsg_type =3D=3D 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 =3D 0; + pid_t child; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sk)) + return -1; + + if (pipe(syncpipe)) + return -1; + + child =3D fork(); + if (child < 0) + return -1; + + if (child =3D=3D 0) { + close(sk[0]); + close(syncpipe[1]); + thread_client(sk[1], syncpipe[0], sender); + } + close(sk[1]); + close(syncpipe[0]); + + int on =3D 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) !=3D 1) + return -1; + close(syncpipe[1]); + + pidfd =3D recv_pidfd(sk[0], ids); + if (pidfd < 0) + return -1; + + info->mask =3D 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) !=3D 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 =3D sizeof(int); + int val =3D -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 --=20 2.53.0 From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5D9993DA7DC; Mon, 31 Aug 2026 11:21:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175306; cv=none; b=jJ5q+UHPtWonZwvRuT1HYVow/rJkenaSOytEtnX7BH0Y8Gz2FGl6pnoAdoteHm9KqMQBjxgFIwTZEqfspelBoRm28kvz3xZdsLfPHob5NFsX+Y8mMbmwCPd+gwG6YJxoqfHfPbSfu1mbDDqqGwccZRF5sGn8VdEEa6OIE0KTN5A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175306; c=relaxed/simple; bh=HEybP9kYZLYh95nXQVU4Ro2ZoH8yWd91e11nBo+cWPU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ugLaZj/apN6VjlPQtkTMh4YgrH0WvyYlbDXs/fuE5VvnFKUeawUhuESGHUeNP4VGO/jkn31KUbM7h4QY+EXh0ENvS9Hw6ArCyKEZQg4QgvLrL+KA473/YHDSAgm7PepO9QFKchnBpNIxqbON3z0FpKwE3kLmLHl071XFeeCpr/w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FLk/CHkr; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FLk/CHkr" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 116F91F00A3D; Mon, 31 Aug 2026 11:21:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175305; bh=PiV5BA+QJHtOFjV1KLxuRRKTBzeVLodZTa8VggNpLEM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=FLk/CHkrDGvPG8oCcoDtYwJDIX9UZzH3sXhtzSg7fkg6klfKR1l6cb+eBzQrGW0ui hl2/tpDfpMNJApn9RVeuB1eWZoaoo0OcGGS3Jkl7QV/Z4vsBFKn0uqALelLoZ7CyXN 9W52q2eH6cav+MLtR3/+iP9h3drcTe/WZ4ahijFW3cQHh49y+ua47yd5VKs8WJO2qZ B/Z+UyE8Ikx6StzdZVov0TtLPTavXqAwDKoXAxa/NQb5GP6OTDHKeCEHJHypGL7Rsu Tf6a3+jh+7UXyYx/+PXko4FdXtBkWgeOSn9wwxEQNYziyiBnTr3349Fz2HI//IlKYQ eYwfnUuqaYXkw== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:17 +0200 Subject: [PATCH 05/10] net: turn sk_peer_pid into an array indexed by pid type Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-5-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=8395; i=brauner@kernel.org; h=from:subject:message-id; bh=HEybP9kYZLYh95nXQVU4Ro2ZoH8yWd91e11nBo+cWPU=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd4ocO5KvKvSvnuhalM+l7ztlK0/VLtHljUwZxHf1 DuK2Qs+d5SyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEwkfDojwy+bNX38O65GHeVi e71nUWpBZ8DcHPHS3LqU1oQnKz1yyxkZTtW2HnZ7wXNm5mf3KhVRlvyTm8LldH1DhKc0vPHcM6W YHwA= X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 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) --- 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 * } =20 /* ... and validate that @sk_peer_pid matches @cprm.pid. */ - if (WARN_ON_ONCE(unix_peer(socket->sk)->sk_peer_pid !=3D cprm->pid)) + if (WARN_ON_ONCE(unix_peer(socket->sk)->sk_peer_pid[PIDTYPE_TGID] !=3D cp= rm->pid)) return false; =20 cprm->limit =3D 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; =20 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 sock= et *sock, /* Init peer information so it can be properly monitored */ if (!kern) { spin_lock(&sk->sk_peer_lock); - sk->sk_peer_pid =3D get_pid(task_tgid(current)); + sk->sk_peer_pid[PIDTYPE_TGID] =3D get_pid(task_tgid(current)); sk->sk_peer_cred =3D 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 =3D sk->sk_peer_pid; + old_pid =3D sk->sk_peer_pid[PIDTYPE_TGID]; old_cred =3D sk->sk_peer_cred; - sk->sk_peer_pid =3D get_pid(parent->sk_peer_pid); + sk->sk_peer_pid[PIDTYPE_TGID] =3D get_pid(parent->sk_peer_pid[PIDTYPE_TGI= D]); sk->sk_peer_cred =3D get_cred(parent->sk_peer_cred); spin_unlock(&sk->sk_peer_lock); =20 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, stru= ct sk_buff *skb) creds =3D &bt_cb(skb)->creds; =20 /* 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 =3D bt_sk(sk)->parent; else return; } =20 /* Check if scm_creds already set */ - if (creds->pid =3D=3D pid_vnr(sk->sk_peer_pid)) + if (creds->pid =3D=3D pid_vnr(sk->sk_peer_pid[PIDTYPE_TGID])) return; =20 memset(creds, 0, sizeof(*creds)); =20 - creds->pid =3D pid_vnr(sk->sk_peer_pid); + creds->pid =3D pid_vnr(sk->sk_peer_pid[PIDTYPE_TGID]); if (sk->sk_peer_cred) { creds->uid =3D sk->sk_peer_cred->uid; creds->gid =3D 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 =3D chan->data; =20 - return sk->sk_peer_pid; + return sk->sk_peer_pid[PIDTYPE_TGID]; } =20 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 opt= name, len =3D sizeof(peercred); =20 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); =20 if (copy_to_sockptr(optval, &peercred, len)) @@ -1947,7 +1948,7 @@ int sk_getsockopt(struct sock *sk, int level, int opt= name, len =3D sizeof(pidfd); =20 spin_lock(&sk->sk_peer_lock); - peer_pid =3D get_pid(sk->sk_peer_pid); + peer_pid =3D get_pid(sk->sk_peer_pid[PIDTYPE_TGID]); spin_unlock(&sk->sk_peer_lock); =20 if (!peer_pid) @@ -2401,7 +2402,7 @@ static void __sk_destruct(struct rcu_head *head) =20 /* 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); =20 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 s= ock *sk, kuid_t uid) sk->sk_frag.offset =3D 0; sk->sk_peek_off =3D -1; =20 - sk->sk_peer_pid =3D NULL; + memset(sk->sk_peer_pid, 0, sizeof(sk->sk_peer_pid)); sk->sk_peer_cred =3D NULL; spin_lock_init(&sk->sk_peer_lock); =20 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 embr= ion) } =20 struct unix_peercred { - struct pid *peer_pid; + DECLARE_PIDS(peer_pid, PIDTYPE_TGID); const struct cred *peer_cred; }; =20 @@ -749,7 +749,7 @@ static inline int prepare_peercred(struct unix_peercred= *peercred) pid =3D task_tgid(current); err =3D pidfs_register_pid(pid); if (likely(!err)) { - peercred->peer_pid =3D get_pid(pid); + peercred->peer_pid[PIDTYPE_TGID] =3D get_pid(pid); peercred->peer_cred =3D get_current_cred(); } return err; @@ -762,7 +762,7 @@ static void drop_peercred(struct unix_peercred *peercre= d) =20 might_sleep(); =20 - swap(peercred->peer_pid, pid); + swap(peercred->peer_pid[PIDTYPE_TGID], pid); swap(peercred->peer_cred, cred); =20 put_pid(pid); @@ -772,7 +772,7 @@ static void drop_peercred(struct unix_peercred *peercre= d) static inline void init_peercred(struct sock *sk, const struct unix_peercred *peercred) { - sk->sk_peer_pid =3D peercred->peer_pid; + sk->sk_peer_pid[PIDTYPE_TGID] =3D peercred->peer_pid[PIDTYPE_TGID]; sk->sk_peer_cred =3D peercred->peer_cred; } =20 @@ -782,12 +782,12 @@ static void update_peercred(struct sock *sk, struct u= nix_peercred *peercred) struct pid *old_pid; =20 spin_lock(&sk->sk_peer_lock); - old_pid =3D sk->sk_peer_pid; + old_pid =3D sk->sk_peer_pid[PIDTYPE_TGID]; old_cred =3D sk->sk_peer_cred; init_peercred(sk, peercred); spin_unlock(&sk->sk_peer_lock); =20 - peercred->peer_pid =3D old_pid; + peercred->peer_pid[PIDTYPE_TGID] =3D old_pid; peercred->peer_cred =3D old_cred; } =20 @@ -796,7 +796,7 @@ static void copy_peercred(struct sock *sk, struct sock = *peersk) lockdep_assert_held(&unix_sk(peersk)->lock); =20 spin_lock(&sk->sk_peer_lock); - sk->sk_peer_pid =3D get_pid(peersk->sk_peer_pid); + sk->sk_peer_pid[PIDTYPE_TGID] =3D get_pid(peersk->sk_peer_pid[PIDTYPE_TGI= D]); sk->sk_peer_cred =3D get_cred(peersk->sk_peer_cred); spin_unlock(&sk->sk_peer_lock); } --=20 2.53.0 From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 652C73EAC75; Mon, 31 Aug 2026 11:21:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175310; cv=none; b=CVnRMUsMbk5+WJdfb/DpCUJj7hZ5EQ1sHDM4ga9vh9+xI+6QKT8le1vswqu9Ht+92hMWN2razO+F2XVPjhCuMlqe482LwAgr7OXE1cH0M2byLK2Rir2CdJx6G+iNSqyrGkqfafPNmVEh0fqOy0lSVNXaCAnU/Z5XbhBafH8BBp4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175310; c=relaxed/simple; bh=yA/Xz27iNSl0I/FYFPwMCx9PJl0ttUYbsV6seW06gok=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jxByMPgFSB5mnPzNjygRjb+5NpbV+UfHj09Sftp5qRVY/2ZbFPJTBaYPQjbz5S8+sSpK7o5rtOa1E+ohFQvrsuxyINRgvNU92QtZa3VArSMgpiPPB1DeFIVLoc6wc9DBqEuV8MEyfI1xMhRknaE3e/ogf1T2OoK8ByIcW3aCViU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=oI+SfSZq; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="oI+SfSZq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B7B2D1F000E9; Mon, 31 Aug 2026 11:21:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175309; bh=kB/2xO2CtThMIOvpgDlR1yhv6emlsZPwXXBBmEolq/U=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=oI+SfSZqMHQZ3+XfjEAMf1x3Vdswu1UFns9ZT7xz+Vi+RTFp6KSdrQO23Q4jow4Oq fwX/W2m0Jf82Jla/VF9p0A1ABXkK3BuX2zbpksRLhDOgVqDwm3+AWzxKgA/4UsuArW gBoqCa0mRayCkfNOrM7Q8lIKzU5DWjYid0uRVaYYBQShlGl2bU7N7IaEHZUxcGppDj X913fEvgGEZuw3ewgX1gIEMzvHEwHJCpLtMuRS6lSjlAY3IgHr4lwOurgsA+EvFzs3 yng7tt8Z522of86L63wCyen+nJ77ZA/DCBM1qdleUqlwxrPdkMkEJwCd2yLlxoygT0 K8coXOeX3gaYA== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:18 +0200 Subject: [PATCH 06/10] af_unix: record the pid of the connecting thread Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-6-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=2771; i=brauner@kernel.org; h=from:subject:message-id; bh=yA/Xz27iNSl0I/FYFPwMCx9PJl0ttUYbsV6seW06gok=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd4otmjuhXNHep2aGMvapxSzt5vKbej9vuHQvur+r 4smmpsqdpSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEzEeCvD/9LrDm4Xt9w8JDrN zimJyetEwMvTJ5OnXD7NmnGtOWXpLg6Gv4KN7+s9U2f1xGtYHTob6BVQ8t5voXSp2eQdTxY5clb sZgYA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 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) Reviewed-by: Alexander Mikhalitsyn --- 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 { =20 static inline int prepare_peercred(struct unix_peercred *peercred) { - struct pid *pid; int err; =20 - pid =3D task_tgid(current); - err =3D pidfs_register_pid(pid); - if (likely(!err)) { - peercred->peer_pid[PIDTYPE_TGID] =3D get_pid(pid); - peercred->peer_cred =3D get_current_cred(); + get_task_pids(peercred->peer_pid, current); + err =3D pidfs_register_pids(peercred->peer_pid); + if (unlikely(err)) { + put_pids(peercred->peer_pid); + return err; } - return err; + + peercred->peer_cred =3D get_current_cred(); + return 0; } =20 static void drop_peercred(struct unix_peercred *peercred) { const struct cred *cred =3D NULL; - struct pid *pid =3D NULL; =20 might_sleep(); =20 - swap(peercred->peer_pid[PIDTYPE_TGID], pid); + put_pids(peercred->peer_pid); swap(peercred->peer_cred, cred); - - put_pid(pid); put_cred(cred); } =20 static inline void init_peercred(struct sock *sk, const struct unix_peercred *peercred) { - sk->sk_peer_pid[PIDTYPE_TGID] =3D peercred->peer_pid[PIDTYPE_TGID]; + memcpy(sk->sk_peer_pid, peercred->peer_pid, sizeof(sk->sk_peer_pid)); sk->sk_peer_cred =3D peercred->peer_cred; } =20 static void update_peercred(struct sock *sk, struct unix_peercred *peercre= d) { const struct cred *old_cred; - struct pid *old_pid; =20 spin_lock(&sk->sk_peer_lock); - old_pid =3D sk->sk_peer_pid[PIDTYPE_TGID]; + swap_pids(sk->sk_peer_pid, peercred->peer_pid); old_cred =3D sk->sk_peer_cred; - init_peercred(sk, peercred); + sk->sk_peer_cred =3D peercred->peer_cred; spin_unlock(&sk->sk_peer_lock); =20 - peercred->peer_pid[PIDTYPE_TGID] =3D old_pid; peercred->peer_cred =3D old_cred; } =20 @@ -796,7 +792,7 @@ static void copy_peercred(struct sock *sk, struct sock = *peersk) lockdep_assert_held(&unix_sk(peersk)->lock); =20 spin_lock(&sk->sk_peer_lock); - sk->sk_peer_pid[PIDTYPE_TGID] =3D get_pid(peersk->sk_peer_pid[PIDTYPE_TGI= D]); + get_pids(sk->sk_peer_pid, peersk->sk_peer_pid); sk->sk_peer_cred =3D get_cred(peersk->sk_peer_cred); spin_unlock(&sk->sk_peer_lock); } --=20 2.53.0 From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2E2D23CC7EA; Mon, 31 Aug 2026 11:21:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175314; cv=none; b=tHQkcCg8o54i8gqEzSZWXWi64RSdpOskyXAwi+yyX2EA/hMBTCqFAIGFUgVHYOnGsHXMoPtDRNXu0zUu6ICZOdKF7Mv4Rz1cnTk+1unYRWcMKDKyLNgDeHPeF4EM34b6TvcUXjPWaiqcDHwTVhXg/8aTQ13pdkvSEfiZtNBYlx0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175314; c=relaxed/simple; bh=qlgstd7gjMVLDTmM6iuC7d3Y90wNHKV1PsuqEuP5Q0k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RgNaKM58EzLuyDqPFdwmYWBZHbUVxPi+LgfhfCYpXTggPFChnXqBjW7pezde7xGXCSSIUulGqJkKhk5pwfKqNe4nu/cHazZ6KFlekN129C/FfzF+zMNa657m2SWBfPnf82crAOMo9GEYVRipU/J/XbqSo+P/9uiyojF3hsOC82U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=csUqez2o; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="csUqez2o" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8E6721F00A3D; Mon, 31 Aug 2026 11:21:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175312; bh=3fCLdlzZ/S81TJajr8WSen6qbEgo3zYGkQoCY1jFZCQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=csUqez2ozgqbRZc9ECjkfpHWc63h/cj5nBR794aKxzyUoyXaR/8d/5pko+ZGhJyfk nCOWMzhdfz2lwv6FWgzqZ2zVO3LYiwdnBCYFIIhZvdjDdIXokgHn0HDncSPcpaBSPG KtxN0Nm3KN/8F9gL4k4UUu2eL2PS2zmO1NlC5O0wA9rHuqLKKqyJcBBqcgt5DVJG+L XuEOGn174a6F14Ng1gPgOBMAmriW+ysEaPnEAnFxvKJF/5i1mC/2uRxkV8K+sZ+McQ XYasMUOw1OJMBzy0sjy9mrc/BBHBP+nAxqeyPbZ2npg/kzAyVWuAWCfABliM5NE1FN GX2pen6/WHfNg== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:19 +0200 Subject: [PATCH 07/10] net: add SO_PEERPIDFD_THREAD to get a thread-specific pidfd Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-7-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=5888; i=brauner@kernel.org; h=from:subject:message-id; bh=qlgstd7gjMVLDTmM6iuC7d3Y90wNHKV1PsuqEuP5Q0k=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd547nvDu1LbKktDlY6jZq+l/a6/0/OMtmmNZHkb0 LSjlL2mo5SFQYyLQVZMkcWh3SRcbjlPxWajTA2YOaxMIEMYuDgFYCK/LBn+afOWr2rT+eavKPt9 Xxib2ze/nG6G0nv5NuLTlFwXxxw9wshwzFNnzh0DrZac3m62TzM1cqYWmDfZnT3w5EbEvsMWHJL MAA== X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 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) Reviewed-by: Alexander Mikhalitsyn --- 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 @@ =20 #define SO_PASSPIDFD_THREAD 86 =20 +#define SO_PEERPIDFD_THREAD 87 + #if !defined(__KERNEL__) =20 #if __BITS_PER_LONG =3D=3D 64 diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/a= sm/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 @@ =20 #define SO_PASSPIDFD_THREAD 86 =20 +#define SO_PEERPIDFD_THREAD 87 + #if !defined(__KERNEL__) =20 #if __BITS_PER_LONG =3D=3D 64 diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/ua= pi/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 @@ =20 #define SO_PASSPIDFD_THREAD 0x4054 =20 +#define SO_PEERPIDFD_THREAD 0x4055 + #if !defined(__KERNEL__) =20 #if __BITS_PER_LONG =3D=3D 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 @@ =20 #define SO_PASSPIDFD_THREAD 0x005f =20 +#define SO_PEERPIDFD_THREAD 0x0060 + #if !defined(__KERNEL__) =20 =20 diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/s= ocket.h index d1e5c6de146d..56fed7ab27ab 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -154,6 +154,8 @@ =20 #define SO_PASSPIDFD_THREAD 86 =20 +#define SO_PEERPIDFD_THREAD 87 + #if !defined(__KERNEL__) =20 #if __BITS_PER_LONG =3D=3D 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 struc= t group_info *src) return 0; } =20 +/* 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 =3D NULL; + unsigned int flags =3D 0; + struct pid *peer_pid; + int pidfd; + + if (len > sizeof(pidfd)) + len =3D sizeof(pidfd); + + spin_lock(&sk->sk_peer_lock); + peer_pid =3D 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 =3D=3D AF_UNIX) + flags |=3D PIDFD_STALE; + if (type =3D=3D PIDTYPE_PID) + flags |=3D PIDFD_THREAD; + + pidfd =3D 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 o= ptname, } =20 case SO_PEERPIDFD: - { - struct pid *peer_pid; - struct file *pidfd_file =3D NULL; - unsigned int flags =3D 0; - int pidfd; - - if (len > sizeof(pidfd)) - len =3D sizeof(pidfd); - - spin_lock(&sk->sk_peer_lock); - peer_pid =3D 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 =3D=3D AF_UNIX) - flags =3D PIDFD_STALE; + return sk_getsockopt_peerpidfd(sk, PIDTYPE_TGID, optval, optlen, len); =20 - pidfd =3D 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; =20 - fd_install(pidfd, pidfd_file); - return 0; - } + return sk_getsockopt_peerpidfd(sk, PIDTYPE_PID, optval, optlen, len); =20 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 =3D=3D SOL_SOCKET) { switch (optname) { case SO_PEERPIDFD: + case SO_PEERPIDFD_THREAD: return true; default: return false; --=20 2.53.0 From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BB9D53E49C9; Mon, 31 Aug 2026 11:21:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175318; cv=none; b=DOOQhjJc0tLCxuUc4+XrhujuS9UfmSgf0dDJMqazhPd4UIIu2hlNt+aSTmiKcqJGw2D7MdWBQWq8idrwTcDrMV2lssEqgP1IOcZevK7NUa1vf8F79uoSLBr9VXIfpDKp4xrfVtdFsT9twyVvEeLNu/yF5MIlK1Eh80UbLwYISds= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175318; c=relaxed/simple; bh=93dvwazGQmyhpLGOG6YC1oHD3PgppRUiN9FyAp69QeA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gBplB78XQnTrECFDynNFoEhDOPEYBGdTH4iNZjhxmAQD0mQloEqXaoopPbd1FaSjpdJTPeLmGkiuWnqjgQl0BoYdInvEZbYzf9zk53aBswajn0r+AJ+c+jEhDb1DNZ1l6HJ5sITJWupqUU1OkEk3Vt8ju3PKycok0xQAfM7UyzI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ekNDTPCQ; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ekNDTPCQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4A6941F00ACA; Mon, 31 Aug 2026 11:21:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175316; bh=vvhqs/opFW1MtRaaw0rgUb+gCmDOYHPkin1TdpYv3Uk=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ekNDTPCQdWkEoSfNIbvVZXs2NUYC7fLMmBBvnOWaxsvJ+6lft/Go7ZNrqrufS1qxm B/VlzNwrEvTVOkN3FUr/fExTnqUjI1TYzfZQwzoqdnRDFLOdkmVUc7Rk8PPJXghIeU jfD0ZWDxJRtk4qXnrz3tCqfOWs51QLVCTLX9ZOOD5D24jN+sl6d/YxT92SYHRtkzJg QGOtq6RrHZaOyZixmn6MQgedIHjzJmjDVsZ+GbcWmdmvhdSPCo39AkbsUPSTTtDhz2 atGEsETzjwvTlPN44XbjHHrUWdr5TIMv+4GJIAeL9Aqm6lx0bygWRqz1TzOcw3av4j E2yXCEvPCmMGA== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:20 +0200 Subject: [PATCH 08/10] selftests/net: SO_PEERPIDFD_THREAD Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-8-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=3150; i=brauner@kernel.org; h=from:subject:message-id; bh=93dvwazGQmyhpLGOG6YC1oHD3PgppRUiN9FyAp69QeA=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd6omPdr8tFpGT13mZcY/On+Kp+0qWTDZ6azhmIPd iS2u0e/7yhlYRDjYpAVU2RxaDcJl1vOU7HZKFMDZg4rE8gQBi5OAZjImTZGhjlsX2+sdVq45P8j A59dVk/c7yX4KQdpc8tPteIrrDA362RkWOX34H+w2slS65fuTxLsOa7/Kzrfmix8863Ya+Nnq5b qMAMA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 Add tests for SO_PEERPIDFD_THREAD. Signed-off-by: Christian Brauner (Amutable) Reviewed-by: Alexander Mikhalitsyn --- 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/testin= g/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 =20 +#ifndef SO_PEERPIDFD_THREAD +#define SO_PEERPIDFD_THREAD 87 +#endif + #define CHILD_EXIT_CODE_OK 123 =20 static void child_die() @@ -822,4 +826,97 @@ TEST(scm_pidfd_thread_creds) EXPECT_EQ(ids.pid, info.tgid); } =20 +static void *peer_connect_thread(void *arg) +{ + struct sock_addr *sa =3D arg; + struct thread_ids ids =3D { + .pid =3D getpid(), + .tid =3D gettid(), + }; + int fd; + char sync; + + fd =3D 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) !=3D sizeof(ids)) + return (void *)1; + + /* stay alive until the server has looked at our pidfd */ + if (read(fd, &sync, 1) !=3D 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 =3D sizeof(pidfd); + + if (getsockopt(fd, SOL_SOCKET, optname, &pidfd, &len)) { + log_err("getsockopt(SO_PEERPIDFD*)"); + return -1; + } + + info->mask =3D 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 =3D 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 =3D 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 --=20 2.53.0 From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 700D83EFFA9; Mon, 31 Aug 2026 11:22:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175321; cv=none; b=QYh/FTb1Zo0XndWmapHrafl5UNkoDzZYEhQhDUY/MZY+Q3xuO33kWjjgmII5O4v6olL455G/ffjNXfRjXfMemgdRkmFuS6q5lzo7EtO0Pqq81EV3llw2Dhs1gzEzvAvthEYUMW/Yzr7PCUgtzjwB44J7HYJAoZhO2FO3QNPHR7Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175321; c=relaxed/simple; bh=VLssKX8GIOxfbHmQLMsVyfJ1wjYRa6l1ALugNjTkGP4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kFh32TwNKm9utzSmLq8fT8Ssz33klt1EbyBUw0zZwHYCBBMCzr8wMw5y9UJXdKyIkKi+K6ruTcYODDpL4UEOLw9UAuXRq8x/0K2kWHOQFFZEhGCMgXfp/EswmEeImVKs2Z7SICIqFeMZ+lg8z8dmvPu+kOWbgIIL6MTAEccrdjM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Wm+Gtv/M; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Wm+Gtv/M" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D3C6A1F00A3D; Mon, 31 Aug 2026 11:21:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175320; bh=6ELI+s/2g2iPQPGx3WRbVPVNLhrYyFGbu19t3cCh+FA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Wm+Gtv/MPvueJtqbljoCLIXCcW1ukR1bhLUz4uIJtVbZurSMQOGeYKw6V750lZJoe rv4NokgI+vj1urVOmSIDwTnexdZkm6ilevHzhH0qPs4+IjB8M/hY9IHYSqBI9Ifoho CU0y1TpuOty5IZrUQXvapC58baZvqYYYfePWgfNdAzvAfyjX5WMpcYxYuW+Y8ke/xw m3nXCrCAEn0rnEjvYcnD3ISNQpQBlIrCKxBRahAnZWSxjXO21x9cOUGXd1Kz5acbKb 23dgR42xBXL5rcsmh576zdFNEkvcsnVFdFevFukPilNxElT9+KQEvgMNmMWm5YYrvO 8DUh186wtk5Mw== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:21 +0200 Subject: [PATCH 09/10] pidfs: record the coredump on the dumping thread's pid too Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-9-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=4565; i=brauner@kernel.org; h=from:subject:message-id; bh=VLssKX8GIOxfbHmQLMsVyfJ1wjYRa6l1ALugNjTkGP4=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd648tabbNXT9c8l5tp/P/6venOwQF/Ukwe3BU6ff 3mf7ZxNRUcpC4MYF4OsmCKLQ7tJuNxynorNRpkaMHNYmUCGMHBxCsBEuHkZ/tkfuCixa3L/ea7Z Fr9XrMw1WF5gXzM1MWrOxvksEu3Ml78yMuy78OCRKhdzlU1+ZuRMu3AHrbV2pofZtLQ2xH10v1L iwgUA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 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) Reviewed-by: Alexander Mikhalitsyn --- 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 =3D task_tgid(current); + task_pids(cprm->pid, current); err =3D 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 =3D (struct coredump_params *)info->data; int err; =20 - if (cp->pid) { + if (cp->pid[PIDTYPE_TGID]) { struct file *pidfs_file __free(fput) =3D NULL; =20 - pidfs_file =3D pidfs_alloc_file(cp->pid, 0); + pidfs_file =3D pidfs_alloc_file(cp->pid[PIDTYPE_TGID], 0); if (IS_ERR(pidfs_file)) return PTR_ERR(pidfs_file); =20 + err =3D pidfs_register_pids(cp->pid); + if (err) + return err; + pidfs_coredump(cp); =20 /* @@ -695,12 +699,12 @@ static bool coredump_sock_connect(struct core_name *c= n, struct coredump_params * return false; =20 /* - * 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 =3D task_tgid(current); - retval =3D pidfs_register_pid(cprm->pid); + task_pids(cprm->pid, current); + retval =3D pidfs_register_pids(cprm->pid); if (retval) return false; =20 @@ -722,7 +726,7 @@ static bool coredump_sock_connect(struct core_name *cn,= struct coredump_params * } =20 /* ... and validate that @sk_peer_pid matches @cprm.pid. */ - if (WARN_ON_ONCE(unix_peer(socket->sk)->sk_peer_pid[PIDTYPE_TGID] !=3D cp= rm->pid)) + if (WARN_ON_ONCE(!pids_equal(unix_peer(socket->sk)->sk_peer_pid, cprm->pi= d))) return false; =20 cprm->limit =3D 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) } =20 #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 =3D cprm->pid; struct pidfs_attr *attr; =20 attr =3D 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 =3D PIDTYPE_PID; type <=3D pids_last(cprm->pid); = type++) + pidfs_coredump_pid(cprm->pid[type], cprm); +} #endif =20 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 #include #include +#include #include #include =20 @@ -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); }; =20 extern unsigned int core_file_note_size_limit; --=20 2.53.0 From nobody Sat Sep 26 19:34:28 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2B75B3F39E8; Mon, 31 Aug 2026 11:22:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175325; cv=none; b=j57QbVtIWLUq3w/Cp4oY2RE9pa5PvcydOzegqEk3lRgaohwrq55Xy3gf8oC1h3Sivbfnd2WBjAnU+N01pF/O80w4X1HNjGzs0aBNCR/jBXqTVGUQfWJV+WyxeaUElfSwTnTt2jld5DLMr31RxD00dM/u22TuVl3D3aWPX65TUpg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788175325; c=relaxed/simple; bh=/gLzXoWjA3OG6zZFZUdLwDoN7/YEvqPGa1o+7C0HII8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EY98I95t2e8jn9b9Gn1Jq1cOUdETJHnVHaFhYvv7YQNZdDNMhShlBj3jiqpCDQc00xMZzL2hsQPXPjodJoGyw54mFXM4FAocCfeT2KYO0qt6O/Zvuh4h41mxoW9pMQE9tT2YzvR9r07cf73H10VldOMd1zraOH4jr12HIzEyNeo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HZki8Ndf; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="HZki8Ndf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C90AE1F000E9; Mon, 31 Aug 2026 11:22:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788175324; bh=NIEk83whaboRl5hJs+zRgzVrnKeaoqMIlrMhqA/02pY=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=HZki8NdfPEwn3SskCfaffJOqWLTLl3BHdK/xcQeMuacl/cHeB7yRDUcW9zoTizBiD is2Yv9k7jTdD5DCUCmpYhNsZcnYmIYyVTNucF3/wsIBjCOp9HDXDhAr853cJIeResg i8LtlLuQ04XdfCeyupJfkvlshg4M5airlkvpd3frZs36esXbzwsjWrm5q83FTJ2UEN 0hCHkmZ4p7w5lX2KcsTl3540SVNz2Ps7v+aoNzxr69cTSdNVrG1nU6vDyAtuvhI8/b nqiKo4WSSkDNeQmMRSLoP/OhZX62AZ6dhHSauASzdTB0fG8dL2B0UxuIjl4DAw22iV bbfkX88fKvDCA== From: Christian Brauner Date: Mon, 31 Aug 2026 13:21:22 +0200 Subject: [PATCH 10/10] selftests/coredump: check the dumping thread's pidfd Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260831-work-unix-passpidfd-v1-10-70cbfda0c7ba@kernel.org> References: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> In-Reply-To: <20260831-work-unix-passpidfd-v1-0-70cbfda0c7ba@kernel.org> To: Jakub Kicinski , Kuniyuki Iwashima , Oleg Nesterov Cc: "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Willem de Bruijn , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Alexander Viro , Jan Kara , linux-fsdevel@vger.kernel.org, Alexander Mikhalitsyn , "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-db0b7 X-Developer-Signature: v=1; a=openpgp-sha256; l=9270; i=brauner@kernel.org; h=from:subject:message-id; bh=/gLzXoWjA3OG6zZFZUdLwDoN7/YEvqPGa1o+7C0HII8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWRNTd4Y16lWdLTW9PW9NwkNkpMbWl9vV+BSXfYqOOZoT 81pyUuTOkpZGMS4GGTFFFkc2k3C5ZbzVGw2ytSAmcPKBDKEgYtTACby7Q4jQ++GGQxJM9P+unD/ 2jYtY2v7ggUh71ceUUyxvHnmTILp2m0M/2OCK98cqYzemNEhJpV14U2I41PLHbdEe0z64ioWTWZ LZgcA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 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) Reviewed-by: Alexander Mikhalitsyn --- .../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/tool= s/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); } =20 +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 != =3D SIGSEGV) { + fprintf(stderr, "%s: coredump_signal=3D%d, expected SIGSEGV=3D%d\n", + what, info->coredump_signal, SIGSEGV); + return false; + } + + if (!(info->mask & PIDFD_INFO_COREDUMP_CODE) || info->coredump_code !=3D = SEGV_MAPERR) { + fprintf(stderr, "%s: coredump_code=3D%d, expected SEGV_MAPERR=3D%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 =3D {}; + int ipc_sockets[2]; + char c; + + ASSERT_TRUE(set_core_pattern("@/tmp/coredump.socket")); + + ret =3D socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); + ASSERT_EQ(ret, 0); + + pid_coredump_server =3D fork(); + ASSERT_GE(pid_coredump_server, 0); + if (pid_coredump_server =3D=3D 0) { + int fd_server =3D -1, fd_coredump =3D -1, fd_peer_pidfd =3D -1; + int fd_thread_pidfd =3D -1, fd_core_file =3D -1; + struct pidfd_info thread_info =3D {}; + int exit_code =3D EXIT_FAILURE; + + close(ipc_sockets[0]); + + fd_server =3D 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 =3D 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 =3D 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 =3D 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 !=3D info.tgid || thread_info.tgid !=3D info.tgid || + thread_info.pid =3D=3D 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 =3D 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 =3D 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 =3D=3D 0) + break; + + bytes_write =3D write(fd_core_file, buffer, bytes_read); + if (bytes_read !=3D bytes_write) { + fprintf(stderr, "socket_coredump_thread: core write %zd/%zd: %m\n", + bytes_read, bytes_write); + goto out; + } + } + + exit_code =3D EXIT_SUCCESS; + fprintf(stderr, "socket_coredump_thread: completed successfully\n"); +out: + if (fd_core_file >=3D 0) + close(fd_core_file); + if (fd_thread_pidfd >=3D 0) + close(fd_thread_pidfd); + if (fd_peer_pidfd >=3D 0) + close(fd_peer_pidfd); + if (fd_coredump >=3D 0) + close(fd_coredump); + if (fd_server >=3D 0) + close(fd_server); + _exit(exit_code); + } + self->pid_coredump_server =3D 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 =3D fork(); + ASSERT_GE(pid, 0); + if (pid =3D=3D 0) + crashing_child_thread(); + + pidfd =3D 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/testi= ng/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); =20 /* Inline helper that uses harness types */ diff --git a/tools/testing/selftests/coredump/coredump_test_helpers.c b/too= ls/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 #include #include +#include #include #include #include @@ -38,6 +39,10 @@ struct _fixture_coredump_data { =20 #define NUM_THREAD_SPAWN 128 =20 +#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 =3D *(volatile int *)NULL; } =20 +static void *crashing_thread(void *arg) +{ + int *p; + + (void)arg; + + /* crash on purpose with SEGV_MAPERR */ + p =3D mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (p =3D=3D MAP_FAILED) + return NULL; + munmap(p, PAGE_SIZE); + *p =3D 0; + + return NULL; +} + +void crashing_child_thread(void) +{ + pthread_t thread; + int i; + + for (i =3D 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; } =20 +int get_peer_pidfd_thread(int fd) +{ + int fd_peer_pidfd; + socklen_t fd_peer_pidfd_len =3D sizeof(fd_peer_pidfd); + int ret =3D getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD_THREAD, &fd_peer_pidf= d, + &fd_peer_pidfd_len); + if (ret < 0) { + fprintf(stderr, "%s: getsockopt(SO_PEERPIDFD_THREAD) failed: %m\n", __fu= nc__); + return -1; + } + fprintf(stderr, "%s: successfully retrieved pidfd %d\n", __func__, fd_pee= r_pidfd); + return fd_peer_pidfd; +} + bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info) { int ret; --=20 2.53.0