|
@@ -670,10 +670,34 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
|
|
|
|
|
|
void complete_vfork_done(struct task_struct *tsk)
|
|
|
{
|
|
|
- struct completion *vfork_done = tsk->vfork_done;
|
|
|
+ struct completion *vfork;
|
|
|
|
|
|
- tsk->vfork_done = NULL;
|
|
|
- complete(vfork_done);
|
|
|
+ task_lock(tsk);
|
|
|
+ vfork = tsk->vfork_done;
|
|
|
+ if (likely(vfork)) {
|
|
|
+ tsk->vfork_done = NULL;
|
|
|
+ complete(vfork);
|
|
|
+ }
|
|
|
+ task_unlock(tsk);
|
|
|
+}
|
|
|
+
|
|
|
+static int wait_for_vfork_done(struct task_struct *child,
|
|
|
+ struct completion *vfork)
|
|
|
+{
|
|
|
+ int killed;
|
|
|
+
|
|
|
+ freezer_do_not_count();
|
|
|
+ killed = wait_for_completion_killable(vfork);
|
|
|
+ freezer_count();
|
|
|
+
|
|
|
+ if (killed) {
|
|
|
+ task_lock(child);
|
|
|
+ child->vfork_done = NULL;
|
|
|
+ task_unlock(child);
|
|
|
+ }
|
|
|
+
|
|
|
+ put_task_struct(child);
|
|
|
+ return killed;
|
|
|
}
|
|
|
|
|
|
/* Please note the differences between mmput and mm_release.
|
|
@@ -717,7 +741,8 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm)
|
|
|
* If we're exiting normally, clear a user-space tid field if
|
|
|
* requested. We leave this alone when dying by signal, to leave
|
|
|
* the value intact in a core dump, and to save the unnecessary
|
|
|
- * trouble otherwise. Userland only wants this done for a sys_exit.
|
|
|
+ * trouble, say, a killed vfork parent shouldn't touch this mm.
|
|
|
+ * Userland only wants this done for a sys_exit.
|
|
|
*/
|
|
|
if (tsk->clear_child_tid) {
|
|
|
if (!(tsk->flags & PF_SIGNALED) &&
|
|
@@ -1551,6 +1576,7 @@ long do_fork(unsigned long clone_flags,
|
|
|
if (clone_flags & CLONE_VFORK) {
|
|
|
p->vfork_done = &vfork;
|
|
|
init_completion(&vfork);
|
|
|
+ get_task_struct(p);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1568,10 +1594,8 @@ long do_fork(unsigned long clone_flags,
|
|
|
ptrace_event(trace, nr);
|
|
|
|
|
|
if (clone_flags & CLONE_VFORK) {
|
|
|
- freezer_do_not_count();
|
|
|
- wait_for_completion(&vfork);
|
|
|
- freezer_count();
|
|
|
- ptrace_event(PTRACE_EVENT_VFORK_DONE, nr);
|
|
|
+ if (!wait_for_vfork_done(p, &vfork))
|
|
|
+ ptrace_event(PTRACE_EVENT_VFORK_DONE, nr);
|
|
|
}
|
|
|
} else {
|
|
|
nr = PTR_ERR(p);
|