It is useful to be able to utilise the pidfd mechanism to reference the
current thread or process (from a userland point of view - thread group
leader from the kernel's point of view).
Therefore introduce PIDFD_SELF_THREAD to refer to the current thread, and
PIDFD_SELF_THREAD_GROUP to refer to the current thread group leader.
For convenience and to avoid confusion from userland's perspective we alias
these:
* PIDFD_SELF is an alias for PIDFD_SELF_THREAD - This is nearly always what
the user will want to use, as they would find it surprising if for
instance fd's were unshared()'d and they wanted to invoke pidfd_getfd()
and that failed.
* PIDFD_SELF_PROCESS is an alias for PIDFD_SELF_THREAD_GROUP - Most users
have no concept of thread groups or what a thread group leader is, and
from userland's perspective and nomenclature this is what userland
considers to be a process.
We adjust pidfd_get_task() and the pidfd_send_signal() system call with
specific handling for this, implementing this functionality for
process_madvise(), process_mrelease() (albeit, using it here wouldn't
really make sense) and pidfd_send_signal().
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
include/uapi/linux/pidfd.h | 24 +++++++++
kernel/pid.c | 24 +++++++--
kernel/signal.c | 106 ++++++++++++++++++++++---------------
3 files changed, 107 insertions(+), 47 deletions(-)
diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h
index 4540f6301b8c..e0abd0b18841 100644
--- a/include/uapi/linux/pidfd.h
+++ b/include/uapi/linux/pidfd.h
@@ -23,6 +23,30 @@
#define PIDFD_INFO_SIZE_VER0 64 /* sizeof first published struct */
+/*
+ * The concept of process and threads in userland and the kernel is a confusing
+ * one - within the kernel every thread is a 'task' with its own individual PID,
+ * however from userland's point of view threads are grouped by a single PID,
+ * which is that of the 'thread group leader', typically the first thread
+ * spawned.
+ *
+ * To cut the Gideon knot, for internal kernel usage, we refer to
+ * PIDFD_SELF_THREAD to refer to the current thread (or task from a kernel
+ * perspective), and PIDFD_SELF_THREAD_GROUP to refer to the current thread
+ * group leader...
+ */
+#define PIDFD_SELF_THREAD -10000 /* Current thread. */
+#define PIDFD_SELF_THREAD_GROUP -20000 /* Current thread group leader. */
+
+/*
+ * ...and for userland we make life simpler - PIDFD_SELF refers to the current
+ * thread, PIDFD_SELF_PROCESS refers to the process thread group leader.
+ *
+ * For nearly all practical uses, a user will want to use PIDFD_SELF.
+ */
+#define PIDFD_SELF PIDFD_SELF_THREAD
+#define PIDFD_SELF_PROCESS PIDFD_SELF_THREAD_GROUP
+
struct pidfd_info {
/*
* This mask is similar to the request_mask in statx(2).
diff --git a/kernel/pid.c b/kernel/pid.c
index 3a10a7b6fcf8..1d2fc59d64fc 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -564,15 +564,29 @@ struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags)
*/
struct task_struct *pidfd_get_task(int pidfd, unsigned int *flags)
{
- unsigned int f_flags;
+ unsigned int f_flags = 0;
struct pid *pid;
struct task_struct *task;
+ enum pid_type type;
- pid = pidfd_get_pid(pidfd, &f_flags);
- if (IS_ERR(pid))
- return ERR_CAST(pid);
+ switch (pidfd) {
+ case PIDFD_SELF_THREAD:
+ type = PIDTYPE_PID;
+ pid = get_task_pid(current, type);
+ break;
+ case PIDFD_SELF_THREAD_GROUP:
+ type = PIDTYPE_TGID;
+ pid = get_task_pid(current, type);
+ break;
+ default:
+ pid = pidfd_get_pid(pidfd, &f_flags);
+ if (IS_ERR(pid))
+ return ERR_CAST(pid);
+ type = PIDTYPE_TGID;
+ break;
+ }
- task = get_pid_task(pid, PIDTYPE_TGID);
+ task = get_pid_task(pid, type);
put_pid(pid);
if (!task)
return ERR_PTR(-ESRCH);
diff --git a/kernel/signal.c b/kernel/signal.c
index a2afd54303f0..1e8f792f88de 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -4009,6 +4009,46 @@ static struct pid *pidfd_to_pid(const struct file *file)
(PIDFD_SIGNAL_THREAD | PIDFD_SIGNAL_THREAD_GROUP | \
PIDFD_SIGNAL_PROCESS_GROUP)
+static int do_pidfd_send_signal(struct pid *pid, int sig, enum pid_type type,
+ siginfo_t __user *info, unsigned int flags)
+{
+ kernel_siginfo_t kinfo;
+
+ switch (flags) {
+ case PIDFD_SIGNAL_THREAD:
+ type = PIDTYPE_PID;
+ break;
+ case PIDFD_SIGNAL_THREAD_GROUP:
+ type = PIDTYPE_TGID;
+ break;
+ case PIDFD_SIGNAL_PROCESS_GROUP:
+ type = PIDTYPE_PGID;
+ break;
+ }
+
+ if (info) {
+ int ret = copy_siginfo_from_user_any(&kinfo, info);
+
+ if (unlikely(ret))
+ return ret;
+
+ if (unlikely(sig != kinfo.si_signo))
+ return -EINVAL;
+
+ /* Only allow sending arbitrary signals to yourself. */
+ if ((task_pid(current) != pid || type > PIDTYPE_TGID) &&
+ (kinfo.si_code >= 0 || kinfo.si_code == SI_TKILL))
+ return -EPERM;
+ } else {
+ prepare_kill_siginfo(sig, &kinfo, type);
+ }
+
+ if (type == PIDTYPE_PGID)
+ return kill_pgrp_info(sig, &kinfo, pid);
+
+ return kill_pid_info_type(sig, &kinfo, pid, type);
+}
+
/**
* sys_pidfd_send_signal - Signal a process through a pidfd
* @pidfd: file descriptor of the process
@@ -4028,7 +4068,6 @@ SYSCALL_DEFINE4(pidfd_send_signal, int, pidfd, int, sig,
{
int ret;
struct pid *pid;
- kernel_siginfo_t kinfo;
enum pid_type type;
/* Enforce flags be set to 0 until we add an extension. */
@@ -4040,54 +4079,37 @@ SYSCALL_DEFINE4(pidfd_send_signal, int, pidfd, int, sig,
return -EINVAL;
CLASS(fd, f)(pidfd);
- if (fd_empty(f))
- return -EBADF;
- /* Is this a pidfd? */
- pid = pidfd_to_pid(fd_file(f));
- if (IS_ERR(pid))
- return PTR_ERR(pid);
+ switch (pidfd) {
+ case PIDFD_SELF_THREAD:
+ pid = get_task_pid(current, PIDTYPE_PID);
+ type = PIDTYPE_PID;
+ break;
+ case PIDFD_SELF_THREAD_GROUP:
+ pid = get_task_pid(current, PIDTYPE_TGID);
+ type = PIDTYPE_TGID;
+ break;
+ default:
+ if (fd_empty(f))
+ return -EBADF;
- if (!access_pidfd_pidns(pid))
- return -EINVAL;
+ /* Is this a pidfd? */
+ pid = pidfd_to_pid(fd_file(f));
+ if (IS_ERR(pid))
+ return PTR_ERR(pid);
- switch (flags) {
- case 0:
+ if (!access_pidfd_pidns(pid))
+ return -EINVAL;
/* Infer scope from the type of pidfd. */
if (fd_file(f)->f_flags & PIDFD_THREAD)
type = PIDTYPE_PID;
else
type = PIDTYPE_TGID;
break;
- case PIDFD_SIGNAL_THREAD:
- type = PIDTYPE_PID;
- break;
- case PIDFD_SIGNAL_THREAD_GROUP:
- type = PIDTYPE_TGID;
- break;
- case PIDFD_SIGNAL_PROCESS_GROUP:
- type = PIDTYPE_PGID;
- break;
- }
-
- if (info) {
- ret = copy_siginfo_from_user_any(&kinfo, info);
- if (unlikely(ret))
- return ret;
-
- if (unlikely(sig != kinfo.si_signo))
- return -EINVAL;
-
- /* Only allow sending arbitrary signals to yourself. */
- if ((task_pid(current) != pid || type > PIDTYPE_TGID) &&
- (kinfo.si_code >= 0 || kinfo.si_code == SI_TKILL))
- return -EPERM;
- } else {
- prepare_kill_siginfo(sig, &kinfo, type);
}
- if (type == PIDTYPE_PGID)
- return kill_pgrp_info(sig, &kinfo, pid);
- else
- return kill_pid_info_type(sig, &kinfo, pid, type);
+ ret = do_pidfd_send_signal(pid, sig, type, info, flags);
+ if (fd_empty(f))
+ put_pid(pid);
+ return ret;
}
--
2.48.1
On Thu, Jan 30, 2025 at 08:40:26PM +0000, Lorenzo Stoakes <lorenzo.stoakes@oracle.com> wrote: > > Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> > --- > include/uapi/linux/pidfd.h | 24 +++++++++ > kernel/pid.c | 24 +++++++-- > kernel/signal.c | 106 ++++++++++++++++++++++--------------- > 3 files changed, 107 insertions(+), 47 deletions(-) Practical idea, thanks. > diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h > + * To cut the Gideon knot, for internal kernel usage, we refer to A nit https://en.wikipedia.org/wiki/Gordian_Knot (if still applicable) Michal
On Tue, Feb 11, 2025 at 04:24:07PM +0100, Michal Koutný wrote: > On Thu, Jan 30, 2025 at 08:40:26PM +0000, Lorenzo Stoakes <lorenzo.stoakes@oracle.com> wrote: > > > > Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> > > --- > > include/uapi/linux/pidfd.h | 24 +++++++++ > > kernel/pid.c | 24 +++++++-- > > kernel/signal.c | 106 ++++++++++++++++++++++--------------- > > 3 files changed, 107 insertions(+), 47 deletions(-) > > Practical idea, thanks. Thanks! > > > diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h > > + * To cut the Gideon knot, for internal kernel usage, we refer to > > A nit > https://en.wikipedia.org/wiki/Gordian_Knot > > (if still applicable) MY GOD. Hahaha. How embarrassing. God knows how 'Gideon' ended up there. Apologies to all, I appear to have had a senior moment there... Feel free to correct Christian, unless we want to leave this as an Easter Egg? :P > > Michal
On Tue, Feb 11, 2025 at 03:45:20PM +0000, Lorenzo Stoakes wrote: > On Tue, Feb 11, 2025 at 04:24:07PM +0100, Michal Koutný wrote: > > On Thu, Jan 30, 2025 at 08:40:26PM +0000, Lorenzo Stoakes <lorenzo.stoakes@oracle.com> wrote: > > > > > > Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> > > > --- > > > include/uapi/linux/pidfd.h | 24 +++++++++ > > > kernel/pid.c | 24 +++++++-- > > > kernel/signal.c | 106 ++++++++++++++++++++++--------------- > > > 3 files changed, 107 insertions(+), 47 deletions(-) > > > > Practical idea, thanks. > > Thanks! > > > > > > diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h > > > + * To cut the Gideon knot, for internal kernel usage, we refer to > > > > A nit > > https://en.wikipedia.org/wiki/Gordian_Knot > > > > (if still applicable) > > MY GOD. Hahaha. How embarrassing. God knows how 'Gideon' ended up > there. Apologies to all, I appear to have had a senior moment there... > > Feel free to correct Christian, unless we want to leave this as an Easter > Egg? :P Everybody knows it's the "quotidian knot" that's the most challenging.
On Thu, Jan 30, 2025 at 08:40:26PM +0000, Lorenzo Stoakes wrote: > It is useful to be able to utilise the pidfd mechanism to reference the > current thread or process (from a userland point of view - thread group > leader from the kernel's point of view). > > Therefore introduce PIDFD_SELF_THREAD to refer to the current thread, and > PIDFD_SELF_THREAD_GROUP to refer to the current thread group leader. > > For convenience and to avoid confusion from userland's perspective we alias > these: > > * PIDFD_SELF is an alias for PIDFD_SELF_THREAD - This is nearly always what > the user will want to use, as they would find it surprising if for > instance fd's were unshared()'d and they wanted to invoke pidfd_getfd() > and that failed. > > * PIDFD_SELF_PROCESS is an alias for PIDFD_SELF_THREAD_GROUP - Most users > have no concept of thread groups or what a thread group leader is, and > from userland's perspective and nomenclature this is what userland > considers to be a process. > > We adjust pidfd_get_task() and the pidfd_send_signal() system call with > specific handling for this, implementing this functionality for > process_madvise(), process_mrelease() (albeit, using it here wouldn't > really make sense) and pidfd_send_signal(). > > Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> Reviewed-by: Shakeel Butt <shakeel.butt@linux.dev>
© 2016 - 2025 Red Hat, Inc.