+++ /dev/null
-From 4cfbf1a112e17b071fcdae207e4f785c096a198c Mon Sep 17 00:00:00 2001
-From: Saul Wold <sgw@linux.intel.com>
-Date: Thu, 2 Jul 2020 09:46:12 -0700
-Subject: [PATCH] Notification of Death of arbitrary processes
-
-Note: this commit was copied from Titanium Cloud Rel2
-
-This exposes a new feature which may be called to request
-notification when an arbitrary process changes state. The
-caller specifies a pid, signal number, and event mask, and
-when that pid dies, or is stopped, or anything else that
-would normally cause a SIGCHLD, the kernel will send the
-specified signal to the caller if the event is in the event
-mask originally passed down. The siginfo_t struct will
-contain the same information as would be included with SIGCHLD.
-
-This is exposed to userspace via the prctl() call with the
-PR_DO_NOTIFY_TASK_STATE option.
-
-Signed-off-by: Jim Somerville <Jim.Somerville@windriver.com>
-Signed-off-by: Zhang Zhiguo <zhangzhg@neusoft.com>
-Signed-off-by: Shuicheng Lin <shuicheng.lin@intel.com>
-Signed-off-by: Saul Wold <sgw@linux.intel.com>
----
- include/linux/init_task.h | 9 ++
- include/linux/sched.h | 6 +
- include/uapi/linux/prctl.h | 18 +++
- init/Kconfig | 15 +++
- init/init_task.c | 1 +
- kernel/Makefile | 1 +
- kernel/death_notify.c | 228 +++++++++++++++++++++++++++++++++++++
- kernel/death_notify.h | 46 ++++++++
- kernel/exit.c | 6 +
- kernel/fork.c | 4 +
- kernel/signal.c | 11 ++
- kernel/sys.c | 8 ++
- 12 files changed, 353 insertions(+)
- create mode 100644 kernel/death_notify.c
- create mode 100644 kernel/death_notify.h
-
-diff --git a/include/linux/init_task.h b/include/linux/init_task.h
-index a7083a45a26c..1ad2341b3036 100644
---- a/include/linux/init_task.h
-+++ b/include/linux/init_task.h
-@@ -24,6 +24,15 @@
- extern struct files_struct init_files;
- extern struct fs_struct init_fs;
- extern struct nsproxy init_nsproxy;
-+
-+#ifdef CONFIG_SIGEXIT
-+#define INIT_SIGEXIT(tsk) \
-+ .notify = LIST_HEAD_INIT(tsk.notify), \
-+ .monitor = LIST_HEAD_INIT(tsk.monitor),
-+#else
-+#define INIT_SIGEXIT(tsk)
-+#endif
-+
- extern struct group_info init_groups;
- extern struct cred init_cred;
-
-diff --git a/include/linux/sched.h b/include/linux/sched.h
-index 9b35aff09f70..d6f5a2711b7d 100644
---- a/include/linux/sched.h
-+++ b/include/linux/sched.h
-@@ -1015,6 +1015,12 @@ struct task_struct {
- short il_prev;
- short pref_node_fork;
- #endif
-+#ifdef CONFIG_SIGEXIT
-+ /* list of processes to notify on death */
-+ struct list_head notify;
-+ /* list of outstanding monitor requests */
-+ struct list_head monitor;
-+#endif
- #ifdef CONFIG_NUMA_BALANCING
- int numa_scan_seq;
- unsigned int numa_scan_period;
-diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
-index b4875a93363a..d50c435a04ea 100644
---- a/include/uapi/linux/prctl.h
-+++ b/include/uapi/linux/prctl.h
-@@ -63,6 +63,24 @@
- # define PR_ENDIAN_LITTLE 1 /* True little endian mode */
- # define PR_ENDIAN_PPC_LITTLE 2 /* "PowerPC" pseudo little endian */
-
-+#ifdef CONFIG_SIGEXIT
-+#define PR_DO_NOTIFY_TASK_STATE 17 /* Set/get notification for task
-+ state changes */
-+
-+/* This is the data structure for requestion process death
-+ * (and other state change) information. Sig of -1 means
-+ * query, sig of 0 means deregistration, positive sig means
-+ * that you want to set it. sig and events are value-result
-+ * and will be updated with the previous values on every
-+ * successful call.
-+ */
-+struct task_state_notify_info {
-+ pid_t pid;
-+ int sig;
-+ unsigned int events;
-+};
-+#endif
-+
- /* Get/set process seccomp mode */
- #define PR_GET_SECCOMP 21
- #define PR_SET_SECCOMP 22
-diff --git a/init/Kconfig b/init/Kconfig
-index bdf4f284509b..8102ef3d2adb 100644
---- a/init/Kconfig
-+++ b/init/Kconfig
-@@ -1648,6 +1648,21 @@ config VM_EVENT_COUNTERS
- on EXPERT systems. /proc/vmstat will only show page counts
- if VM event counters are disabled.
-
-+config SIGEXIT
-+ bool "Notification of death of arbitrary processes"
-+ default n
-+ help
-+ When enabled this exposes a new feature which may be called to request
-+ notification when an arbitrary process changes state. The caller specifies
-+ a pid, signal number, and event mask, and when that pid dies, or is
-+ stopped, or anything else that would normally cause a SIGCHLD, the
-+ kernel will send the specified signal to the caller if the event is in
-+ the event mask originally passed down. The siginfo_t struct will
-+ contain the same information as would be included with SIGCHLD.
-+
-+ This is exposed to userspace via the prctl()
-+ call with the PR_DO_NOTIFY_TASK_STATE option
-+
- config SLUB_DEBUG
- default y
- bool "Enable SLUB debugging support" if EXPERT
-diff --git a/init/init_task.c b/init/init_task.c
-index 5aebe3be4d7c..d0891101ac7d 100644
---- a/init/init_task.c
-+++ b/init/init_task.c
-@@ -116,6 +116,7 @@ struct task_struct init_task
- .alloc_lock = __SPIN_LOCK_UNLOCKED(init_task.alloc_lock),
- .journal_info = NULL,
- INIT_CPU_TIMERS(init_task)
-+ INIT_SIGEXIT(init_task)
- .pi_lock = __RAW_SPIN_LOCK_UNLOCKED(init_task.pi_lock),
- .timer_slack_ns = 50000, /* 50 usec default slack */
- .thread_pid = &init_struct_pid,
-diff --git a/kernel/Makefile b/kernel/Makefile
-index 1ea0ba13a445..f839f425ca09 100644
---- a/kernel/Makefile
-+++ b/kernel/Makefile
-@@ -99,6 +99,7 @@ obj-$(CONFIG_TRACEPOINTS) += trace/
- obj-$(CONFIG_IRQ_WORK) += irq_work.o
- obj-$(CONFIG_CPU_PM) += cpu_pm.o
- obj-$(CONFIG_BPF) += bpf/
-+obj-$(CONFIG_SIGEXIT) += death_notify.o
-
- obj-$(CONFIG_PERF_EVENTS) += events/
-
-diff --git a/kernel/death_notify.c b/kernel/death_notify.c
-new file mode 100644
-index 000000000000..5819d35a2564
---- /dev/null
-+++ b/kernel/death_notify.c
-@@ -0,0 +1,228 @@
-+/*
-+ * kernel/death_notify.c, Process death notification support
-+ *
-+ * Copyright (c) 2006-2014 Wind River Systems, Inc.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-+ * See the GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+ *
-+ */
-+
-+#include <linux/errno.h>
-+#include <linux/signal.h>
-+#include <linux/sched.h>
-+#include <linux/sched/task.h>
-+#include <linux/slab.h>
-+#include <linux/prctl.h>
-+#include <linux/uaccess.h>
-+#include "death_notify.h"
-+
-+static void unlink_status_notifier(struct signotifier *n)
-+{
-+ list_del(&n->monitor_list);
-+ list_del(&n->notify_list);
-+ kfree(n);
-+}
-+
-+static void handle_already_monitoring(struct signotifier *node,
-+ struct task_state_notify_info *args,
-+ struct task_state_notify_info *oldargs)
-+{
-+ /* Store the old values */
-+ oldargs->sig = node->sig;
-+ oldargs->events = node->events;
-+
-+ /* We know that args->sig is 0 or a valid signal. */
-+ if (args->sig > 0) {
-+ /* Update the new values */
-+ node->sig = args->sig;
-+ node->events = args->events;
-+ } else if (!args->sig) {
-+ /* args->sig of 0 means to deregister */
-+ unlink_status_notifier(node);
-+ }
-+}
-+
-+static void setup_new_node(struct task_struct *p,
-+ struct signotifier *node,
-+ struct task_state_notify_info *args)
-+{
-+ node->notify_tsk = current;
-+ node->sig = args->sig;
-+ node->events = args->events;
-+
-+ /* Add this node to the list of notification requests
-+ * for the specified process.
-+ */
-+ list_add_tail(&node->notify_list, &p->notify);
-+
-+ /* Also add this node to the list of monitor requests
-+ * for the current process.
-+ */
-+ list_add_tail(&node->monitor_list, ¤t->monitor);
-+}
-+
-+/* Returns 0 if arguments are valid, 1 if they are not. */
-+static int invalid_args(struct task_state_notify_info *args)
-+{
-+ int ret = 1;
-+
-+ if (args->pid <= 0)
-+ goto out;
-+
-+ /* Sig of -1 implies query, sig of 0 implies deregistration.
-+ * Otherwise sig must be positive and within range.
-+ */
-+ if ((args->sig < -1) || (args->sig > _NSIG))
-+ goto out;
-+
-+ /* If positive sig, must have valid events. */
-+ if (args->sig > 0) {
-+ if (!args->events || (args->events >= (1 << (NSIGCHLD+1))))
-+ goto out;
-+ }
-+
-+ ret = 0;
-+out:
-+ return ret;
-+}
-+
-+/* Notify those registered for process state updates via do_notify_task_state().
-+ * If "del" is nonzero, the process is dying and we want to free
-+ * the nodes in the list as we go.
-+ *
-+ * Note: we only notify processes for events in which they have registered
-+ * interest.
-+ *
-+ * Must be called holding a lock on tasklist_lock.
-+ */
-+void do_notify_others(struct task_struct *tsk, struct kernel_siginfo *info)
-+{
-+ struct signotifier *node;
-+ unsigned int events;
-+
-+ /* This method of generating the event bit must be
-+ * matched in the userspace library.
-+ */
-+ events = 1 << (info->si_code & 0xFF);
-+
-+ list_for_each_entry(node, &tsk->notify, notify_list) {
-+ if (events & node->events) {
-+ info->si_signo = node->sig;
-+ group_send_sig_info(node->sig, info, node->notify_tsk, PIDTYPE_TGID);
-+ }
-+ }
-+}
-+
-+void release_notify_others(struct task_struct *p)
-+{
-+ struct signotifier *n, *t;
-+
-+ /* Need to clean up any outstanding requests where we
-+ * wanted to be notified when others died.
-+ */
-+ list_for_each_entry_safe(n, t, &p->monitor, monitor_list) {
-+ unlink_status_notifier(n);
-+ }
-+
-+ /* Also need to clean up any outstanding requests where others
-+ * wanted to be notified when we died.
-+ */
-+ list_for_each_entry_safe(n, t, &p->notify, notify_list) {
-+ unlink_status_notifier(n);
-+ }
-+}
-+
-+/* If the config is defined, then processes can call this routine
-+ * to request notification when the specified task's state changes.
-+ * On the death (or other state change) of the specified process,
-+ * we will send them the specified signal if the event is listed
-+ * in their event bitfield.
-+ *
-+ * A sig of 0 means that we want to deregister.
-+ *
-+ * The sig/events fields are value/result. On success we update them
-+ * to reflect what they were before the call.
-+ *
-+ * Returns error code on error, on success we return 0.
-+ */
-+int do_notify_task_state(unsigned long arg)
-+{
-+ int err;
-+ struct task_struct *p;
-+ struct signotifier *node, *tmp;
-+ struct task_state_notify_info args, oldargs;
-+
-+ if (copy_from_user(&args, (struct task_state_notify_info __user *)arg,
-+ sizeof(args)))
-+ return -EFAULT;
-+ oldargs.pid = args.pid;
-+
-+ /* Validate the arguments passed in. */
-+ err = -EINVAL;
-+ if (invalid_args(&args))
-+ goto out;
-+
-+ /* We must hold a write lock on tasklist_lock to add the notification
-+ * later on, and we need some lock on tasklist_lock for
-+ * find_task_by_pid(), so may as well take the write lock now.
-+ * Must use write_lock_irq().
-+ */
-+ write_lock_irq(&tasklist_lock);
-+
-+ err = -ESRCH;
-+ p = find_task_by_vpid(args.pid);
-+ if (!p)
-+ goto unlock_out;
-+
-+ /* Now we know pid exists, unlikely to fail. */
-+ err = 0;
-+
-+ /* Check if we're already monitoring the specified pid. If so, update
-+ * the monitoring parameters and return the old ones.
-+ */
-+ list_for_each_entry(tmp, &p->notify, notify_list) {
-+ if (tmp->notify_tsk == current) {
-+ handle_already_monitoring(tmp, &args, &oldargs);
-+ goto unlock_out;
-+ }
-+ }
-+
-+ /* If we get here, we're not currently monitoring the process. */
-+ oldargs.sig = 0;
-+ oldargs.events = 0;
-+
-+ /* If we wanted to set up a new monitor, do it now. If we didn't
-+ * manage to allocate memory for the new node, then we return
-+ * an appropriate error.
-+ */
-+ if (args.sig > 0) {
-+ node = kmalloc(sizeof(*node), GFP_ATOMIC);
-+ if (node)
-+ setup_new_node(p, node, &args);
-+ else
-+ err = -ENOMEM;
-+ }
-+
-+unlock_out:
-+ write_unlock_irq(&tasklist_lock);
-+
-+ /* Copy the old values back to caller. */
-+ if (copy_to_user((struct task_state_notify_info __user *)arg,
-+ &oldargs, sizeof(oldargs)))
-+ err = -EFAULT;
-+
-+out:
-+ return err;
-+}
-+
-diff --git a/kernel/death_notify.h b/kernel/death_notify.h
-new file mode 100644
-index 000000000000..14a0995b79af
---- /dev/null
-+++ b/kernel/death_notify.h
-@@ -0,0 +1,46 @@
-+/*
-+ * kernel/death_notify.h, Process death notification support
-+ *
-+ * Copyright (c) 2006-2014 Wind River Systems, Inc.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-+ * See the GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+ *
-+ */
-+#ifndef _KERNEL_DEATH_NOTIFY_H
-+#define _KERNEL_DEATH_NOTIFY_H
-+
-+#ifdef CONFIG_SIGEXIT
-+
-+struct signotifier {
-+ struct task_struct *notify_tsk;
-+ struct list_head notify_list;
-+ struct list_head monitor_list;
-+ int sig;
-+ unsigned int events;
-+};
-+
-+extern int do_notify_task_state(unsigned long arg);
-+extern void do_notify_others(struct task_struct *tsk,
-+ struct kernel_siginfo *info);
-+extern void release_notify_others(struct task_struct *p);
-+
-+#else /* !CONFIG_SIGEXIT */
-+
-+static inline void do_notify_others(struct task_struct *tsk,
-+ struct kernel_siginfo *info) {}
-+static inline void release_notify_others(struct task_struct *p) {}
-+
-+#endif /* CONFIG_SIGEXIT */
-+#endif
-+
-diff --git a/kernel/exit.c b/kernel/exit.c
-index 2166c2d92ddc..f35f3a5870a8 100644
---- a/kernel/exit.c
-+++ b/kernel/exit.c
-@@ -67,6 +67,9 @@
- #include <asm/unistd.h>
- #include <asm/pgtable.h>
- #include <asm/mmu_context.h>
-+#ifdef CONFIG_SIGEXIT
-+#include "death_notify.h"
-+#endif
-
- static void __unhash_process(struct task_struct *p, bool group_dead)
- {
-@@ -196,6 +199,9 @@ void release_task(struct task_struct *p)
- proc_flush_task(p);
-
- write_lock_irq(&tasklist_lock);
-+#ifdef CONFIG_SIGEXIT
-+ release_notify_others(p);
-+#endif
- ptrace_release_task(p);
- __exit_signal(p);
-
-diff --git a/kernel/fork.c b/kernel/fork.c
-index 17389e9935c4..4d79f261c365 100644
---- a/kernel/fork.c
-+++ b/kernel/fork.c
-@@ -1907,6 +1907,10 @@ static __latent_entropy struct task_struct *copy_process(
- p->sequential_io = 0;
- p->sequential_io_avg = 0;
- #endif
-+#ifdef CONFIG_SIGEXIT
-+ INIT_LIST_HEAD(&p->notify);
-+ INIT_LIST_HEAD(&p->monitor);
-+#endif
-
- /* Perform scheduler related setup. Assign this task to a CPU. */
- retval = sched_fork(clone_flags, p);
-diff --git a/kernel/signal.c b/kernel/signal.c
-index 57b7771e20d7..41126fa53102 100644
---- a/kernel/signal.c
-+++ b/kernel/signal.c
-@@ -51,6 +51,9 @@
- #include <asm/siginfo.h>
- #include <asm/cacheflush.h>
- #include "audit.h" /* audit_signal_info() */
-+#ifdef CONFIG_SIGEXIT
-+#include "death_notify.h"
-+#endif
-
- /*
- * SLAB caches for signal bits.
-@@ -1893,6 +1896,10 @@ bool do_notify_parent(struct task_struct *tsk, int sig)
- __wake_up_parent(tsk, tsk->parent);
- spin_unlock_irqrestore(&psig->siglock, flags);
-
-+#ifdef CONFIG_SIGEXIT
-+ do_notify_others(tsk, &info);
-+#endif
-+
- return autoreap;
- }
-
-@@ -1965,6 +1972,10 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,
- */
- __wake_up_parent(tsk, parent);
- spin_unlock_irqrestore(&sighand->siglock, flags);
-+
-+#ifdef CONFIG_SIGEXIT
-+ do_notify_others(tsk, &info);
-+#endif
- }
-
- static inline bool may_ptrace_stop(void)
-diff --git a/kernel/sys.c b/kernel/sys.c
-index f7eb62eceb24..9fadbbe9565f 100644
---- a/kernel/sys.c
-+++ b/kernel/sys.c
-@@ -72,6 +72,9 @@
- #include <asm/unistd.h>
-
- #include "uid16.h"
-+#ifdef CONFIG_SIGEXIT
-+#include "death_notify.h"
-+#endif
-
- #ifndef SET_UNALIGN_CTL
- # define SET_UNALIGN_CTL(a, b) (-EINVAL)
-@@ -2405,6 +2408,11 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
- else
- error = PR_MCE_KILL_DEFAULT;
- break;
-+#ifdef CONFIG_SIGEXIT
-+ case PR_DO_NOTIFY_TASK_STATE:
-+ error = do_notify_task_state(arg2);
-+ break;
-+#endif
- case PR_SET_MM:
- error = prctl_set_mm(arg2, arg3, arg4, arg5);
- break;
---
-2.17.1
-