@@ -643,11 +643,15 @@ static void forget_original_parent(struct task_struct *father,
reaper = find_new_reaper(father, reaper);
list_for_each_entry(p, &father->children, sibling) {
+ spin_lock(&p->sighand->siglock);
for_each_thread(p, t) {
RCU_INIT_POINTER(t->real_parent, reaper);
BUG_ON((!t->ptrace) != (rcu_access_pointer(t->parent) == father));
if (likely(!t->ptrace))
t->parent = t->real_parent;
+ }
+ spin_unlock(&p->sighand->siglock);
+ for_each_thread(p, t) {
if (t->pdeath_signal)
group_send_sig_info(t->pdeath_signal,
SEND_SIG_NOINFO, t,
@@ -2367,6 +2367,12 @@ static __latent_entropy struct task_struct *copy_process(
*/
write_lock_irq(&tasklist_lock);
+ klp_copy_process(p);
+
+ sched_core_fork(p);
+
+ spin_lock(¤t->sighand->siglock);
+
/* CLONE_PARENT re-uses the old parent */
if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) {
p->real_parent = current->real_parent;
@@ -2381,12 +2387,6 @@ static __latent_entropy struct task_struct *copy_process(
p->exit_signal = args->exit_signal;
}
- klp_copy_process(p);
-
- sched_core_fork(p);
-
- spin_lock(¤t->sighand->siglock);
-
/*
* Copy seccomp details explicitly here, in case they were changed
* before holding sighand lock.
@@ -123,13 +123,12 @@ void __ptrace_unlink(struct task_struct *child)
clear_task_syscall_work(child, SYSCALL_EMU);
#endif
+ spin_lock(&child->sighand->siglock);
child->parent = child->real_parent;
list_del_init(&child->ptrace_entry);
old_cred = child->ptracer_cred;
child->ptracer_cred = NULL;
put_cred(old_cred);
-
- spin_lock(&child->sighand->siglock);
child->ptrace = 0;
/*
* Clear all pending traps and TRAPPING. TRAPPING should be
@@ -441,15 +440,15 @@ static int ptrace_attach(struct task_struct *task, long request,
if (task->ptrace)
goto unlock_tasklist;
+ spin_lock(&task->sighand->siglock);
task->ptrace = flags;
ptrace_link(task, current);
/* SEIZE doesn't trap tracee on attach */
if (!seize)
- send_sig_info(SIGSTOP, SEND_SIG_PRIV, task);
+ send_signal_locked(SIGSTOP, SEND_SIG_PRIV, task, PIDTYPE_PID);
- spin_lock(&task->sighand->siglock);
/*
* If the task is already STOPPED, set JOBCTL_TRAP_STOP and
@@ -517,8 +516,10 @@ static int ptrace_traceme(void)
* pretend ->real_parent untraces us right after return.
*/
if (!ret && !(current->real_parent->flags & PF_EXITING)) {
+ spin_lock(¤t->sighand->siglock);
current->ptrace = PT_PTRACED;
ptrace_link(current, current->real_parent);
+ spin_unlock(¤t->sighand->siglock);
}
}
write_unlock_irq(&tasklist_lock);
The functions ptrace_stop and do_signal_stop have to drop siglock and grab tasklist_lock because the parent/child relation ship is guarded by tasklist_lock and not siglock. Simplify things by additionally guarding the parent/child relationship with siglock. This just requires a little bit of code motion. After this change tsk->parent, tsk->real_parent, tsk->ptracer_cred are all protected by tsk->siglock. The fields tsk->sibling and tsk->ptrace_entry are mostly protected by tsk->siglock. The field tsk->ptrace_entry is not protected by siglock when tsk->ptrace_entry is reused as the dead task list. The field tsk->sibling is not protected by siglock when children are reparented because their original parent dies. The field tsk->ptrace is protected by siglock except for the options which may change without siglock being held. Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> --- kernel/exit.c | 4 ++++ kernel/fork.c | 12 ++++++------ kernel/ptrace.c | 9 +++++---- 3 files changed, 15 insertions(+), 10 deletions(-)