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.stoa...@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

Reply via email to