aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/ptrace.h13
-rw-r--r--include/linux/tracehook.h28
-rw-r--r--kernel/exit.c21
3 files changed, 50 insertions, 12 deletions
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index dae6d85520f..ed69c03692d 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -176,6 +176,19 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
}
}
+/**
+ * ptrace_release_task - final ptrace-related cleanup of a zombie being reaped
+ * @task: task in %EXIT_DEAD state
+ *
+ * Called with write_lock(&tasklist_lock) held.
+ */
+static inline void ptrace_release_task(struct task_struct *task)
+{
+ BUG_ON(!list_empty(&task->ptraced));
+ ptrace_unlink(task);
+ BUG_ON(!list_empty(&task->ptrace_entry));
+}
+
#ifndef force_successful_syscall_return
/*
* System call handlers that, upon successful completion, need to return a
diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
index 830e6e16097..9a5b3be2503 100644
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -228,4 +228,32 @@ static inline void tracehook_report_vfork_done(struct task_struct *child,
ptrace_event(PT_TRACE_VFORK_DONE, PTRACE_EVENT_VFORK_DONE, pid);
}
+/**
+ * tracehook_prepare_release_task - task is being reaped, clean up tracing
+ * @task: task in %EXIT_DEAD state
+ *
+ * This is called in release_task() just before @task gets finally reaped
+ * and freed. This would be the ideal place to remove and clean up any
+ * tracing-related state for @task.
+ *
+ * Called with no locks held.
+ */
+static inline void tracehook_prepare_release_task(struct task_struct *task)
+{
+}
+
+/**
+ * tracehook_finish_release_task - task is being reaped, clean up tracing
+ * @task: task in %EXIT_DEAD state
+ *
+ * This is called in release_task() when @task is being in the middle of
+ * being reaped. After this, there must be no tracing entanglements.
+ *
+ * Called with write_lock_irq(&tasklist_lock) held.
+ */
+static inline void tracehook_finish_release_task(struct task_struct *task)
+{
+ ptrace_release_task(task);
+}
+
#endif /* <linux/tracehook.h> */
diff --git a/kernel/exit.c b/kernel/exit.c
index c3691cbc220..da28745f7c3 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -163,27 +163,17 @@ static void delayed_put_task_struct(struct rcu_head *rhp)
put_task_struct(container_of(rhp, struct task_struct, rcu));
}
-/*
- * Do final ptrace-related cleanup of a zombie being reaped.
- *
- * Called with write_lock(&tasklist_lock) held.
- */
-static void ptrace_release_task(struct task_struct *p)
-{
- BUG_ON(!list_empty(&p->ptraced));
- ptrace_unlink(p);
- BUG_ON(!list_empty(&p->ptrace_entry));
-}
void release_task(struct task_struct * p)
{
struct task_struct *leader;
int zap_leader;
repeat:
+ tracehook_prepare_release_task(p);
atomic_dec(&p->user->processes);
proc_flush_task(p);
write_lock_irq(&tasklist_lock);
- ptrace_release_task(p);
+ tracehook_finish_release_task(p);
__exit_signal(p);
/*
@@ -205,6 +195,13 @@ repeat:
* that case.
*/
zap_leader = task_detached(leader);
+
+ /*
+ * This maintains the invariant that release_task()
+ * only runs on a task in EXIT_DEAD, just for sanity.
+ */
+ if (zap_leader)
+ leader->exit_state = EXIT_DEAD;
}
write_unlock_irq(&tasklist_lock);