From nobody Fri Sep 25 19:19:12 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 DB01A525A9B; Wed, 9 Sep 2026 10:43:16 +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=1788950599; cv=none; b=lSmMv0fIPVaWrfxm1yRHJM2K8Cz/kIjGxIOdmmiAjA6fbWyCSLJias8lBb2GxA5V69/6zZfe+feO8aItqi0WhnvN3VhjkigCtXRNuEX/4tQ4NGYpNNwh3KwpXYJLBSlkbE4NcsDV5i8t6n0rWRmrqK5/uL9lrF5Sr6yzInf1i6g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950599; c=relaxed/simple; bh=ljH4PmcbMqpxzI86GDJjrCdMsccf/UZG0sz37cuLEAk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SYelDyWn8NwkOu+E2r4Wgq+hY11V83zylXqiCudkm69+uhr+YnaQA0xtR5iYn/k8vzZ0MUJO0BLl0hA1T/cTtSkQvNBpDZ2NCigDpQFoZrXz91+Ps4sycbNhDC93De7zoFIp5d+/Yz1ZnS8Vo8gi1H51YIbZeH1gPbJPj9xJzn0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=gBfKKfcS; 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="gBfKKfcS" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 91BE31F00A3E; Wed, 9 Sep 2026 10:43:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950596; bh=XPk7U7FG+rJ3Spd9W56GP3BfliuT1Dhikqca+FPI3jM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=gBfKKfcSwtz7fRRp+OG5YaWqyypP5uDJpv4SeOjej1Zpz5t8acWvcA4pek3bxDvs8 AZuKOTn7PKQFQYXcPx4Z3SM+OmqJwjCyGyBmnzDfz/nfxKKLQYLGDoADL7f0rProos MUaUCV7oqlKuXx37mHInL0HQgyIu6rBT81xMZ5b4ntfNr45kj24d8mhN+PXGFWUWZ+ 6M2PUVrEtHopUFmVUaCK6TmEDxVtc2aMm1lX1kYgRSdYBPOKc0aBGDKbmBELa+jC3W iCpjRT88ZjF5yNNlgt556bdD3eYUlH6q1RRX0PMrQR1BYPhGIediWGa7h5JHsC8Q8+ yhj1rSVZ/kZ3A== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:00 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-1-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=6917; i=brauner@kernel.org; h=from:subject:message-id; bh=ljH4PmcbMqpxzI86GDJjrCdMsccf/UZG0sz37cuLEAk=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLCeJpP6rWy5QruasBfDBdtl69M3T3zDmjYn/PZsE ef8pXklHaUsDGJcDLJiiiwO7Sbhcst5KjYbZWrAzGFlAhnCwMUpABOZ1M7wv9TfZ25uve/Ng6Y2 a3Y3XlpVv9C8+g5b/Rx9Pf6VV07yHWJk6Pqv/ce0Y/dzV+9/846HxaUu2ZKV6+EkfcOy70jHvph qTgA= 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 Reviewed-by: Kuniyuki Iwashima --- fs/pidfs.c | 16 +++++++++++ include/linux/pid.h | 53 +++++++++++++++++++++++++++++++= ++++ include/linux/pid_types.h | 8 ++++++ include/linux/pidfs.h | 5 ++++ include/linux/sched/signal.h | 18 ++++++++++++ tools/lib/python/kdoc/xforms_lists.py | 1 + 6 files changed, 101 insertions(+) diff --git a/fs/pidfs.c b/fs/pidfs.c index a6a643f15d08..586af2e5811c 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -1070,6 +1070,22 @@ int pidfs_register_pid_gfp(struct pid *pid, gfp_t 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]; diff --git a/tools/lib/python/kdoc/xforms_lists.py b/tools/lib/python/kdoc/= xforms_lists.py index e3dda2fe8a53..e792785a15ec 100644 --- a/tools/lib/python/kdoc/xforms_lists.py +++ b/tools/lib/python/kdoc/xforms_lists.py @@ -45,6 +45,7 @@ class CTransforms: (CMatch("DECLARE_HASHTABLE"), r"unsigned long \1[1 << ((\2) - 1)]"= ), (CMatch("DECLARE_KFIFO"), r"\2 *\1"), (CMatch("DECLARE_KFIFO_PTR"), r"\2 *\1"), + (CMatch("DECLARE_PIDS"), r"struct pid *\1[(\2) + 1]"), (CMatch("(?:__)?DECLARE_FLEX_ARRAY"), r"\1 \2[]"), (CMatch("DEFINE_DMA_UNMAP_ADDR"), r"dma_addr_t \1"), (CMatch("DEFINE_DMA_UNMAP_LEN"), r"__u32 \1"), --=20 2.53.0 From nobody Fri Sep 25 19:19:12 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 463A452E067; Wed, 9 Sep 2026 10:43:20 +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=1788950602; cv=none; b=jl/vHySOVug+Z9WxsgbMFOzgNHptGUJzSmNz7cCSmIPZpuzTPp5GKVMcTXduwPy0aiLturdqRWRbbxEcX/oqIArZX2pLEeDlFtnhRNT4FSUYsmMuhF0tyvVkVnj1vETY8SSCFHL2SOGBbmFtCPGpNhBIy37NnO/OWnPe34Xo8xE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950602; c=relaxed/simple; bh=8y7i7XdLNk6YD1ieQIQKQPtW6EPU/d87CtG5vVgfh1w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DTPcDM6PAa4sbSHCBejiZMlar8IhlpVUuA4SCQ06/B60faFP+0Me+AW/cpanZ5JLNJIYNM/Q83LS5ciUwo4KYiFSfQEWQqrzxiiR7xeigFmWtCjfGffwE3L7zW3NeDBo3y5TdX2d46zwUsj50a72e9lYKXHV0ereFN/9aymr850= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ZzvzD49m; 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="ZzvzD49m" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 06FF11F00A3A; Wed, 9 Sep 2026 10:43:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950600; bh=f7vAua/SuxVzCWdPf5sdxe/QtAK3nj/ceXJVxPpgBGA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=ZzvzD49mk62KpzwTK8hJpNzjIR9J0eZSyM1GfjpKXIqwOwPXaGbVaH8aAACY0m95e VFPIpw31Wk7uSdXPLkZvKzMTqr4va6PtPhFkHVnugnLWymME/QnILL+hmVGnCjgEUl ScDgehELF85bOtToFhiMR5bYNSdp8FKwygKQyVBQlTCziniurO8ZsCL1E131kXGqBQ yXvLcEjO77rmzFpowX6OPdAIfZ9V4MFaNj37GNGrvCE7SFeoiLmj6tlo5tGBKytD4+ iAahxU+9hgkkxPQswJVQ+gDTUqX9MAU79g8ivZMKFUB3nZKZeYIHhuO7oaJygQE0Ma ET8WmHgdSdy7w== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:01 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-2-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLAWjlDJub24PlmG/Q5Hhn2J2VEvE5UwRn2rjceW7 ltltfxQRykLgxgXg6yYIotDu0m43HKeis1GmRowc1iZQIYwcHEKwEQazRn++5u7z7nuqvrNZb/9 lddFE3Q4znvfFLdK2n+vRPDR/5Aj3gx/5bV4WLYWLtNU5dSvPqEgeObwJZ+5X+r2mHqwe6qF+XQ wAwA= 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 Fri Sep 25 19:19:12 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 D15D452FE3C; Wed, 9 Sep 2026 10:43:23 +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=1788950605; cv=none; b=tFwChJbzXAWa66M4ThmXiMrTTZ3g+XJIaeLFGEkxzV4R3L8JpOImG8Y4wSTbxDOw0VT4VO8Nodo3uG2cYzRkNEEWRgxp4PxJtICrZ9cfEjZ1IKx4ZqznVN52VkNCyyIotbk37l2HZKeM+CM8OUlcOa2p5fqFu0OfIjbIYlNQ1dk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950605; c=relaxed/simple; bh=NLa8uilUxAnPii04OppmXTDEn9mgtxxJISrmS/dAGEo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=dNW8HTsb9UGtGvj7AsJdxIIZ0AF/nuNIpeUrXEz4iODUdmOP3VI6k5A6bZoL14FPUzVaYCXtrDr2f2LaAQW6HJ49MmOb3QPMzPNFwvzpyz5xCa6SdfsDErzTLnFrvMm+QoKE1zxJ3OIOLcROac+e+epcIX+VDNBdt1IPQ+4voSg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QUJNjcgD; 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="QUJNjcgD" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6ECBA1F00A3E; Wed, 9 Sep 2026 10:43:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950603; bh=GiJ2hHOWnwnvPkGxLjwnC23vL+oGkNd/f/J0V6WYS1k=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=QUJNjcgD3I41Kznw8yajpqMAfuIquGma9K1eglykkYYbeLEIu2q+vmR2+aIk6phZa BL/aKG3/6NpDb6fFqWsEzW86Rrm/4MEHgDL3MIPyv+le3LZGwaclNJspIhRotHzBxp jMXJv2+fQj6aueZK5js/0WIroy9mbDYhxs/9JPRViqXS4j5VJfDSaE1ghKlEZjT+NU giZCPS18pRjpnEJ1LVXwT2WlYLIjuM4e30VP0AnR1HKam8LlJSsBntL1fd8Akw16Ze ANTi9xILZ75JsW6ZY2KAIQzyKDr6Nyo1DCaMwMj3y1LMCYO3T+OuRau+qgI9yXXNxb EWBP3cCUyXFMA== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:02 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-3-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLCeyvdld/p944CveZXW07imGic9a7pp8GuzpoTzF O53EetOdZSyMIhxMciKKbI4tJuEyy3nqdhslKkBM4eVCWQIAxenAEyk9R3D/6SORZG8VRkHTZOu vbX5eXKiUEmjjpL1cbZ2SRaPR/UvvBj+R01NdXrS9+LMK8/jJhE3D2keuiRo0Kp+8N5t72dirbM /sgAA 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 Fri Sep 25 19:19:12 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 49F3B52D2C4; Wed, 9 Sep 2026 10:43:27 +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=1788950608; cv=none; b=Qd2fi5+vUAVMEPkurQx+jRFqB0URNM08PPeo48riVlghD0jn0UTypvEJl9/e2QRm3Nt/S71ZHmugx7h5KA5fngBgI1cMrTAFqn8E1hvQujcRPtcCEnMndt+Nk6JpnIHUteyIFjU982UbuHUsKVgtyy/Xw6glWEBXHiIGa5tpz0E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950608; c=relaxed/simple; bh=2YnxsfrAF7HQBfWzTLMa2O/I/q1GmiD7jT9cB0DlWWk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AvTu9Ov/xgamCuHd8sSa9n2XgYwWiOiVjwcze4UM/Sr8z0mdVTBjCmnfxK4kR/XLjMMgIS1ypzxTmK9UQTcHRW2lniiE/kK9C6/OkEKIVYw1quetbJMJ8POYAWYGjE383PLwYXsL9ps5KyKHCdzQt9gJaZS9eXohyfKG5LU3GNo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=oLpzQOge; 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="oLpzQOge" Received: by smtp.kernel.org (Postfix) with ESMTPSA id F3AE81F00A3D; Wed, 9 Sep 2026 10:43:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950607; bh=2QGYk5eR/9VO1k3tAFlZXnTVM66BZOkvZZisq9g5IQA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=oLpzQOgefLCEmc5Kzk35bB4HXYq9vemi5qlqr8+SBZaXZb3YDpYbNNbHtj2qVYuLy 6amkiQnF2NyBOZWoaTXH+yXEXykGYZL5VhFNm5HJF4l1LH2R6JOnT5yzXYiUxYiBB0 HORQi7yWzu5BNZHz/2l1/Fm63vXtMkCtHupkA6MBosQGWocfdq0lQVvNgTEHy5uN5Q t+4E+vXL7XCzdXl/8uLdbuebVzCSdn26n/DR6QwC2eZiv/InkaYacMwu2Ii4hbqqTt NDS+zKpqa3psoktkoZvjNz2N+oarQPO7IaqVWXBOMDIQB1Id2m3sx/PrwGjB++J/Op MVkZt8YS6sIlQ== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:03 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-4-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLA+v/XnxLD2SOmre1jOmN45Zn9u47zszPt9LxkyV h+X2/n3UkcpC4MYF4OsmCKLQ7tJuNxynorNRpkaMHNYmUCGMHBxCsBEjq5m+B9kbOR0ILvSVJjh 11f/rdqieteV9poJv9qkzH6DMdxryipGhoX656cd+mrOdWoJq9dqgabtVvfTJZUqCkscNCpfvFr +mAcA 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 Fri Sep 25 19:19:12 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 7D889530E17; Wed, 9 Sep 2026 10:43: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=1788950611; cv=none; b=B2/lFSsJK0T8d64Tx342ll/pWB4wrf5IUcT15HKgy8gD5e0DjFxkaAcb1/xgDbXGXQB2bx5G5EW0qQ6DxaBKOR+3npO+dAkB0HCy5tP32n9xTNYNRP0wvDDO5vnb09fJY7MRmgKjeG25BG5Mq6UneBe1ZDyaLOpJYPfNI6UUZEI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950611; c=relaxed/simple; bh=trmlci8ytu5GtClHy2fE/8jjzJ54lgp57DpFWz7F3U8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=n8lwQyTIijJgl7Fn3R2DjZD9kMWt06B4tzXuXz3Mq6rm1xfZX1Dh7eQkExIJ6hY4dYfymM5UK8VVV3QDK5MjQIB5/GgovcHKybNicN9c8S8yeocgZv2Wf/XmrUMSDFMe8pdZNGKqDdIFftpnTI8SxxEhshi7e0NWCt1zJUyvAfc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lPs916Xi; 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="lPs916Xi" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 687F41F00A3E; Wed, 9 Sep 2026 10:43:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950610; bh=/PpvRThi6XFIVO4bLU1C9lPev50SJCiayR4l2shNQMY=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=lPs916XiI/YpwFvHAOX5aTsQNEMIMQWqATsmox7H+BY6n3csTh0WmS0VGlT6I486K 6whL1pBKUZfP/nJJJqORqz/vINsjiRGyLt27Z4xKTykcFf+2N632rFre+QMeGm6BDQ zkmiFJNGeEJJNqX9x48Rqsa+Ce4Rthbb22kZTWNCQ4l+CGAx+mkz/5VmM5f3SUIwEu 1U+JNU5ERULDX5ohGw022XDVCScJPlgFXKUUQO4lzybUYWsQV5pZWbZaNZbMOSYrYF of1BsLRRMCOHi1KX4lhjnGk0CmOtPclYvi1RzYF2341HC3qJjtozpFIthCfnR8HeGq DNVHYwqbCOLsw== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:04 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-5-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=9030; i=brauner@kernel.org; h=from:subject:message-id; bh=trmlci8ytu5GtClHy2fE/8jjzJ54lgp57DpFWz7F3U8=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLB+6FbhorVzUroV24VFwQ/edt68df/8zAvP/Xh3m e6ovOR2tKOUhUGMi0FWTJHFod0kXG45T8Vmo0wNmDmsTCBDGLg4BWAiwgyMDO9OWotcZ8/Q3yOf 2mfhaHPSQfe8193K0FUZHLd0V+VN+8rIcKJT/n1Dv6Ho272GSWL6MRxPb63dv7Zs29E/RhuXqz+ OYQAA 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) Reviewed-by: Alexander Mikhalitsyn --- fs/coredump.c | 2 +- include/net/sock.h | 4 ++-- include/trace/events/landlock.h | 2 +- net/bluetooth/af_bluetooth.c | 6 +++--- net/bluetooth/hci_sock.c | 8 ++++---- net/bluetooth/l2cap_sock.c | 2 +- net/core/sock.c | 9 +++++---- net/unix/af_unix.c | 14 +++++++------- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index 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/include/trace/events/landlock.h b/include/trace/events/landloc= k.h index f82588f6f90e..762e721b1d9c 100644 --- a/include/trace/events/landlock.h +++ b/include/trace/events/landlock.h @@ -943,7 +943,7 @@ TRACE_EVENT(landlock_deny_scope_abstract_unix_socket, * these hooks, so this READ_ONCE() is safe; sun_path is the * reliable identifier. */ - peer_pid =3D READ_ONCE(peer->sk_peer_pid); + peer_pid =3D READ_ONCE(peer->sk_peer_pid[PIDTYPE_TGID]); __entry->peer_pid =3D peer_pid ? pid_nr(peer_pid) : 0; __assign_str(sun_path); ), diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 411d66f24393..7758e9ea3848 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -161,7 +161,7 @@ struct sock *bt_sock_alloc(struct net *net, struct 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 Fri Sep 25 19:19:12 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 04D4F531AF2; Wed, 9 Sep 2026 10:43: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=1788950615; cv=none; b=VjUDuAMNvRR8XzJNAbrc7Jyug+epAj/Qrq+H8izIGdzUxuB1z621fyD1m2cyahaR3v4hbdTt4eOa/Jj4q0d0YCYFnpIfpRkod7jPz+NM5FnE0nSXbIUCSaOwDiCtNASY7q/1LyKHs38Huvi3dYmQ9HIwhIUAi4I3A5ZLUPXbqwY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950615; c=relaxed/simple; bh=yA/Xz27iNSl0I/FYFPwMCx9PJl0ttUYbsV6seW06gok=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=E+saSnn2bPRZVQ+3oEazh0QdnNC4ylmscvZFiQ6TpBoAqEUBpAvzmcwZ8aouoZZW3GqNzVdOg3TRP3Ozm08twop3G7kY0v5cYjyn2yIXx5rOEbtYuIqjInPO7z2nxHfHMyLKUngvKeqkHDyOfoNKiPeLx4aLBMhH/KGEYY/M1V8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CYQ1sIJZ; 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="CYQ1sIJZ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D66D61F00A3D; Wed, 9 Sep 2026 10:43:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950613; bh=kB/2xO2CtThMIOvpgDlR1yhv6emlsZPwXXBBmEolq/U=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=CYQ1sIJZqdnhrYrtfbyKXxc32LGB5xcXKp8w4WX6tBJa3QmXaM0bgD2Mgzlr1o/oz ScQxwBF3b6S+b9y+vjQ+4m0EI3kpCzaFLR9EVhBHaQFs+G0sy4RLkcebrILYEPNVRu 0XE51zOlubTDWl1GIWWpmyvqhe9I5CaQ9xfiuIFHKoC3T1IseTy0o5csrxcheTpPhU srtEUa4UuuYRhjCa7lQch4ZZxSVTllAws9waYubL9wtWhp+gfPNtvsjWxdkOfrsbif GxzO4+ARhXCBZ2iChHNs/wqFNBtjzHmbKBwzkylk++ItVxZRYwG4u7CsHB4LEMq2dX vQ+LUf4fJTU/Q== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:05 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-6-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLDpTtspvaRmQx2zUW3LxFy/g0uvFak7h1+9tmNdT Z9pifmhjlIWBjEuBlkxRRaHdpNwueU8FZuNMjVg5rAygQxh4OIUgIm8/8rwTy2hTUlpsmvg3yM/ H7AxVETnTswKSxcU/XhKTCuy2/7eNIZ/xkER068f/e1h5NfFV339vP0aRo9Pn1dELIgxv3W+oq2 JCwA= 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 Fri Sep 25 19:19:12 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 899885328D8; Wed, 9 Sep 2026 10:43: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=1788950619; cv=none; b=lHyQ8sa5BrXj/BBYJnJIIFzJFDbnHOSBWy5mAM1pAEzl6M5u5GJkDz1imK9tC9RZ8Sq36ScupgfiTophNb4ENc+PLjdfiE1tRhuzvSHw8iACoKyjkd7QJOqYhzfzAg4qP3s2RvTh9tqJRpiCbftQfj1XgokS0O97Kmr6CBmk3lg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950619; c=relaxed/simple; bh=qlgstd7gjMVLDTmM6iuC7d3Y90wNHKV1PsuqEuP5Q0k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=knb2Y/iFrmm1ffMNJL3cglIfMs5rrhFK6t3scPgwmJvxTEkJ4s6dCDsTpfme3DP3ywEa4acvw8vwSgQAFcVIyt4t5X8jCkPEf9FGnMxGiO70Vu+aaLLvH0/W45DUuWu24aqCsf+fKe1pU/X/QdpNX+SVEGuzPd2b3K7v6uEd2aA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=OGEcaCkK; 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="OGEcaCkK" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 56DD11F00A3A; Wed, 9 Sep 2026 10:43:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950617; bh=3fCLdlzZ/S81TJajr8WSen6qbEgo3zYGkQoCY1jFZCQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=OGEcaCkKyGFzA+TEVjtaBUvPM1FyUQXkYG7BHEzejyJq7FVVHP37jbKSguX2v+HVJ sZmfWqKQFUE6LqtpuhhcmTA/tPdyuEX3veWsAY4rAhGZP86aQK3iBjITFySEQaS1l2 IfDqpEHJB+jeSyljPK9B9wMuMv0qn3TXNTUU80QdxdY7OEOA698q44wnN0TqYFkwhe 6FMxVFMHZW2h1kmCE68s89u6MCHWVaHxaJj83y9ny/hKO13OTIxVmRAvYemt+im+QO MUJbIsJCcW39CAkbQ+xiOtheWNi9XKQIT9vGWiPYk38w55K73u6LRrs6i0sBwOVfbL uevsfn8ZlRYVQ== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:06 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-7-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLBxPxkU2tSZIDtdvEn9lzJDUWf148cGP0pfBEuUR 97Q+lvaUcrCIMbFICumyOLQbhIut5ynYrNRpgbMHFYmkCEMXJwCMJHv4owMvb8P+czqeZ0zvWOK 9KkD78wu3cmaJLrp4K07q290Ve+OvszwT+1n6OeXrAq/zI6lTP2ovL7Wly8ggXN2YuFC/R3hD9d N5AUA 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 Fri Sep 25 19:19:12 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 10D92534445; Wed, 9 Sep 2026 10:43:40 +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=1788950622; cv=none; b=QJtV86eI2pSkg3u6j9QZkzSg7eXTddO+xFadlM8NIzcF1FxSIpz6mRjtzo2dc3DgwgV3xLps8c/FvgsZBhMmWbYk1tq97sU1F9/atzqejjB6HmthxTK+24Ip4+5rU5hp/kXrvhjSew17Vzfgb+U6zNA3cHWW2fVFKULqOsZwqa8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950622; c=relaxed/simple; bh=93dvwazGQmyhpLGOG6YC1oHD3PgppRUiN9FyAp69QeA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jp2HT+uBuMbUT0hgdhhMIbCkTvXb+PCWqPOhyvmJ9QxVw7xC364KOh/2JLxqFr5dphliOHeVTBXwMrLeQ80yltbeAD0ZgMUdi+qzd0fm6QKqt/7oTSZd1jAf6GTb7znhcrB5rwquxPDZYSR3BcQ3i6+32OMutWAHVKmwZOfAZXM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=eQPpa1/O; 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="eQPpa1/O" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CD1E31F00A3E; Wed, 9 Sep 2026 10:43:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950620; bh=vvhqs/opFW1MtRaaw0rgUb+gCmDOYHPkin1TdpYv3Uk=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=eQPpa1/Ocf/0CMF3tUn+eMHV1rZW5aao7LswwDQP+yJCEegBUQBEdjqtcDhO1Qub6 1sWZ70wLrntXkLWsz++kcAdYVgZbiknIzpF3bLij6Xys8tFwqTM7emSjVxHQ728ApK PqY0arlq5mpQyCc4kWNAzxGjI7IOc4Z+a7quM6Mz8zOztRNXuROfawNQwZEp1SN11D BljePL275l3HPEO3Uz5CaXxg9yi546zKa7qb1Ajti8nl6bycV0v24sfLiS9kysAeDE /YF68RJf+7TJhCef0Kahzh1RHo5QpaqqmdI91DjIV9wJ2YVvNN07/4WO4Q2DVJ3cqx JSqQIoA5zfk4Q== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:07 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-8-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLCZl/m5ZPXC2/9X2NxRbtjvWnDOaMnMXP/IpslFV 4tuxu9Z21HKwiDGxSArpsji0G4SLrecp2KzUaYGzBxWJpAhDFycAjCRxXMY/hktYF31IsjktPO1 u3Vs7KFreXW3Mxov2fGAz+P79w0//vUxMpy4FyTK+XWHjEzL8c+W26Ilti9ky+Jh95zWYBqyoGL xOgYA 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 Fri Sep 25 19:19:12 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 5BB75538D88; Wed, 9 Sep 2026 10:43:44 +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=1788950625; cv=none; b=aF3VIiymdCpiJxJCVrp0BwwB73nN0kkh+nXGHs2r9FgrHkzKNmGVk2V9KTPkHJZDNjPD3b2fOXN+4t4c4ifPlFXOcC4/1IDhRux4Q0bWXFlDH93YAf4X1wqsFS/NE7q0Q068mkPlZZ2vUV4b6FFUo2YEWd0TclnPeNcxXeej2uY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950625; c=relaxed/simple; bh=VLssKX8GIOxfbHmQLMsVyfJ1wjYRa6l1ALugNjTkGP4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=enJVXo5+g21MfnKKjWkgX7Xq6+6px1xAe7D60QdBkCiMTxdLk9skRhlEzdQ4bUXUUUkwhgHmBo/x0q7zqIGzjez0Zp/98JUakn/B061cR6wRnu3KqiZWF65AuJyHtnxXV5sloj1Bw3uJ9iR/3Makz7dCusrFlW0CKoZx9IWLWaY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TIyQQ+cm; 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="TIyQQ+cm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 480051F00A3F; Wed, 9 Sep 2026 10:43:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950624; bh=6ELI+s/2g2iPQPGx3WRbVPVNLhrYyFGbu19t3cCh+FA=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=TIyQQ+cmKhWrJPmuZsUoCc2szSntRphjeMDWKQDLlwbKszRuGfHgsB3fCXrBth2rM 7+LhES2zWAsBBLwA9o+b8O4qkw0ZYFEi0Z2r/FcYGJmoxCz+wlySjOp48vQL+pwzPN N12tpTL8av8R1xhGx4JLYG5nGOBlVE+QsI+cmNBxRpV+dA9s7uGL/oiiLmbZNZJ3Bt EFNp06BtFSuRVj6/8kTwO6tPPEb4MPLyma1BGH2NZKIcwVxfOrwBa5qqQC0ZCJ8UBF Gc/gg4gsEzg7XEEcR1UjydLJ+2dhYhthWmol/PsCT5kBt7LM+xH+DoknkjJCoLsfOa xc/4wWFWzHojQ== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:08 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-9-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLCpP1nYLVAownT3ifr3LU/4b3u8+z9fTvPJ/5NXb yTMNlrzuqOUhUGMi0FWTJHFod0kXG45T8Vmo0wNmDmsTCBDGLg4BWAi22QZGe7+XPuw9vb3khCB WdnK+/b5f3q9TuH/vnVcpcsuqvAW+Gcw/OG1Nti2K+P93ZtX6nJC57Mc5vv1+W6/3aGc734FJae +TeEFAA== 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 Fri Sep 25 19:19:12 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 0DAA553A8B2; Wed, 9 Sep 2026 10:43:47 +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=1788950629; cv=none; b=bw0eP06KTst9DvxeLmig5b5qZthuj43MHzMXcUlba6ENroLjSss8DwL34FYjQ72VXNurglqpyt3k+XV6BN4jwkwH72Fh9HDJv1KMdhQddRbp1kld4VzMkXn1BpDXksoiqmpOOfV/HBgW7Y9sE78G5RgjHCDrvdCgYr0SpkS+v2c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788950629; c=relaxed/simple; bh=/gLzXoWjA3OG6zZFZUdLwDoN7/YEvqPGa1o+7C0HII8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZeSQb5Uzr19Bk8LHAYVI0o2jmeEZnmmLTR/KSCWXGZTI4jUUhhbFEMV8ou3xwR/9q0lNVog+N2BfFw4tuLPoe9OS3XslFqU0xnopEz9OkIe/u+sVbL8/aW8WF9LTeYamHc9lMRjtgXfq3bGkLRef2uaxfI1fc1QiuIQq+SZtiXA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=a6e8GuOo; 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="a6e8GuOo" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B3ECD1F00A3D; Wed, 9 Sep 2026 10:43:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788950627; bh=NIEk83whaboRl5hJs+zRgzVrnKeaoqMIlrMhqA/02pY=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=a6e8GuOob0c3r0uCCbMXYlitT5GBhvZ9n3uHJTVta4b26Z+91MMdOas5kbSRH79f4 UKQfJsMFswpb4zOnvKW3PDJjs6lEt1+5ODGqupmbMLeHnFEHmyz6fREokb7CRyUN0O INi20+3jzav1/lwMKKbcvFiF9/qpRHjKmcXMLqIEfqwwfhNrt5aCaxzA2S9Ic1+fz+ dtW67af8ei5lDcXY275lsp+w5TqxBZX5Z2kApcJe8cGIY8CMMnIfg1L3Kd0SQWdYxi vCa3gtbyWiBXzwhIZy2GyRa8oLm58dKi1u+ddSnvYtIshCFfKOFP9p4wqugBIBvQCh TvjQEhlQDsU4A== From: Christian Brauner Date: Wed, 09 Sep 2026 12:43:09 +0200 Subject: [PATCH v2 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: <20260909-work-unix-passpidfd-v2-10-7bd342abb2d1@kernel.org> References: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@kernel.org> In-Reply-To: <20260909-work-unix-passpidfd-v2-0-7bd342abb2d1@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=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQttLCx2Zymf+7Gicc/y99dOCV9dlnLz7SH/2+c/XV99 1uxI4GzF3eUsjCIcTHIiimyOLSbhMst56nYbJSpATOHlQlkCAMXpwBMhEeEkWHePs/LtyLWs5t4 2Jz8XWoRt/j5/AtZKUVzre/5Kd7ctuwDw39Pt30JxcKLn0XezklZZNvgmSB+7L1aO/PExm8Jp0/ e4WEFAA== 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