Merge remote-tracking branch 'pidfd/for-next'
[linux] / kernel / signal.c
index e1d7ad8..35cf384 100644 (file)
@@ -19,7 +19,9 @@
 #include <linux/sched/task.h>
 #include <linux/sched/task_stack.h>
 #include <linux/sched/cputime.h>
+#include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/proc_fs.h>
 #include <linux/tty.h>
 #include <linux/binfmts.h>
 #include <linux/coredump.h>
@@ -688,6 +690,48 @@ int dequeue_signal(struct task_struct *tsk, sigset_t *mask, kernel_siginfo_t *in
 }
 EXPORT_SYMBOL_GPL(dequeue_signal);
 
+static int dequeue_synchronous_signal(kernel_siginfo_t *info)
+{
+       struct task_struct *tsk = current;
+       struct sigpending *pending = &tsk->pending;
+       struct sigqueue *q, *sync = NULL;
+
+       /*
+        * Might a synchronous signal be in the queue?
+        */
+       if (!((pending->signal.sig[0] & ~tsk->blocked.sig[0]) & SYNCHRONOUS_MASK))
+               return 0;
+
+       /*
+        * Return the first synchronous signal in the queue.
+        */
+       list_for_each_entry(q, &pending->list, list) {
+               /* Synchronous signals have a postive si_code */
+               if ((q->info.si_code > SI_USER) &&
+                   (sigmask(q->info.si_signo) & SYNCHRONOUS_MASK)) {
+                       sync = q;
+                       goto next;
+               }
+       }
+       return 0;
+next:
+       /*
+        * Check if there is another siginfo for the same signal.
+        */
+       list_for_each_entry_continue(q, &pending->list, list) {
+               if (q->info.si_signo == sync->info.si_signo)
+                       goto still_pending;
+       }
+
+       sigdelset(&pending->signal, sync->info.si_signo);
+       recalc_sigpending();
+still_pending:
+       list_del_init(&sync->list);
+       copy_siginfo(info, &sync->info);
+       __sigqueue_free(sync);
+       return info->si_signo;
+}
+
 /*
  * Tell a process that it has a new active signal..
  *
@@ -1057,10 +1101,9 @@ static int __send_signal(int sig, struct kernel_siginfo *info, struct task_struc
 
        result = TRACE_SIGNAL_DELIVERED;
        /*
-        * Skip useless siginfo allocation for SIGKILL SIGSTOP,
-        * and kernel threads.
+        * Skip useless siginfo allocation for SIGKILL and kernel threads.
         */
-       if (sig_kernel_only(sig) || (t->flags & PF_KTHREAD))
+       if ((sig == SIGKILL) || (t->flags & PF_KTHREAD))
                goto out_set;
 
        /*
@@ -2394,6 +2437,11 @@ relock:
                goto relock;
        }
 
+       /* Has this task already been marked for death? */
+       ksig->info.si_signo = signr = SIGKILL;
+       if (signal_group_exit(signal))
+               goto fatal;
+
        for (;;) {
                struct k_sigaction *ka;
 
@@ -2407,7 +2455,15 @@ relock:
                        goto relock;
                }
 
-               signr = dequeue_signal(current, &current->blocked, &ksig->info);
+               /*
+                * Signals generated by the execution of an instruction
+                * need to be delivered before any other pending signals
+                * so that the instruction pointer in the signal stack
+                * frame points to the faulting instruction.
+                */
+               signr = dequeue_synchronous_signal(&ksig->info);
+               if (!signr)
+                       signr = dequeue_signal(current, &current->blocked, &ksig->info);
 
                if (!signr)
                        break; /* will return 0 */
@@ -2489,6 +2545,7 @@ relock:
                        continue;
                }
 
+       fatal:
                spin_unlock_irq(&sighand->siglock);
 
                /*
@@ -3397,7 +3454,7 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait_time64, compat_sigset_t __user *, uthese,
 }
 
 #ifdef CONFIG_COMPAT_32BIT_TIME
-COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, compat_sigset_t __user *, uthese,
+COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait_time32, compat_sigset_t __user *, uthese,
                struct compat_siginfo __user *, uinfo,
                struct old_timespec32 __user *, uts, compat_size_t, sigsetsize)
 {
@@ -3429,6 +3486,16 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, compat_sigset_t __user *, uthese,
 #endif
 #endif
 
+static inline void prepare_kill_siginfo(int sig, struct kernel_siginfo *info)
+{
+       clear_siginfo(info);
+       info->si_signo = sig;
+       info->si_errno = 0;
+       info->si_code = SI_USER;
+       info->si_pid = task_tgid_vnr(current);
+       info->si_uid = from_kuid_munged(current_user_ns(), current_uid());
+}
+
 /**
  *  sys_kill - send a signal to a process
  *  @pid: the PID of the process
@@ -3438,16 +3505,125 @@ SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
 {
        struct kernel_siginfo info;
 
-       clear_siginfo(&info);
-       info.si_signo = sig;
-       info.si_errno = 0;
-       info.si_code = SI_USER;
-       info.si_pid = task_tgid_vnr(current);
-       info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+       prepare_kill_siginfo(sig, &info);
 
        return kill_something_info(sig, &info, pid);
 }
 
+#ifdef CONFIG_PROC_FS
+/*
+ * Verify that the signaler and signalee either are in the same pid namespace
+ * or that the signaler's pid namespace is an ancestor of the signalee's pid
+ * namespace.
+ */
+static bool access_pidfd_pidns(struct pid *pid)
+{
+       struct pid_namespace *active = task_active_pid_ns(current);
+       struct pid_namespace *p = ns_of_pid(pid);
+
+       for (;;) {
+               if (!p)
+                       return false;
+               if (p == active)
+                       break;
+               p = p->parent;
+       }
+
+       return true;
+}
+
+static int copy_siginfo_from_user_any(kernel_siginfo_t *kinfo, siginfo_t *info)
+{
+#ifdef CONFIG_COMPAT
+       /*
+        * Avoid hooking up compat syscalls and instead handle necessary
+        * conversions here. Note, this is a stop-gap measure and should not be
+        * considered a generic solution.
+        */
+       if (in_compat_syscall())
+               return copy_siginfo_from_user32(
+                       kinfo, (struct compat_siginfo __user *)info);
+#endif
+       return copy_siginfo_from_user(kinfo, info);
+}
+
+/**
+ * sys_pidfd_send_signal - send a signal to a process through a task file
+ *                          descriptor
+ * @pidfd:  the file descriptor of the process
+ * @sig:    signal to be sent
+ * @info:   the signal info
+ * @flags:  future flags to be passed
+ *
+ * The syscall currently only signals via PIDTYPE_PID which covers
+ * kill(<positive-pid>, <signal>. It does not signal threads or process
+ * groups.
+ * In order to extend the syscall to threads and process groups the @flags
+ * argument should be used. In essence, the @flags argument will determine
+ * what is signaled and not the file descriptor itself. Put in other words,
+ * grouping is a property of the flags argument not a property of the file
+ * descriptor.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+SYSCALL_DEFINE4(pidfd_send_signal, int, pidfd, int, sig,
+               siginfo_t __user *, info, unsigned int, flags)
+{
+       int ret;
+       struct fd f;
+       struct pid *pid;
+       kernel_siginfo_t kinfo;
+
+       /* Enforce flags be set to 0 until we add an extension. */
+       if (flags)
+               return -EINVAL;
+
+       f = fdget_raw(pidfd);
+       if (!f.file)
+               return -EBADF;
+
+       /* Is this a pidfd? */
+       pid = tgid_pidfd_to_pid(f.file);
+       if (IS_ERR(pid)) {
+               ret = PTR_ERR(pid);
+               goto err;
+       }
+
+       ret = -EINVAL;
+       if (!access_pidfd_pidns(pid))
+               goto err;
+
+       if (info) {
+               ret = copy_siginfo_from_user_any(&kinfo, info);
+               if (unlikely(ret))
+                       goto err;
+
+               ret = -EINVAL;
+               if (unlikely(sig != kinfo.si_signo))
+                       goto err;
+
+               if ((task_pid(current) != pid) &&
+                   (kinfo.si_code >= 0 || kinfo.si_code == SI_TKILL)) {
+                       /* Only allow sending arbitrary signals to yourself. */
+                       ret = -EPERM;
+                       if (kinfo.si_code != SI_USER)
+                               goto err;
+
+                       /* Turn this into a regular kill signal. */
+                       prepare_kill_siginfo(sig, &kinfo);
+               }
+       } else {
+               prepare_kill_siginfo(sig, &kinfo);
+       }
+
+       ret = kill_pid_info(sig, &kinfo, pid);
+
+err:
+       fdput(f);
+       return ret;
+}
+#endif /* CONFIG_PROC_FS */
+
 static int
 do_send_specific(pid_t tgid, pid_t pid, int sig, struct kernel_siginfo *info)
 {