If the tracer sets PTRACE_O_TRACEVFORKDONE but not PTRACE_O_TRACEVFORK,
clone(CLONE_VFORK) should be reported too, so in this case event must
be PTRACE_EVENT_VFORK_DONE.

But we can't return UTRACE_STOP. Since CLONE_VFORK is set, utrace will
actually stop the tracee in tracehook_report_clone_complete() path, this
is not we want. We return UTRACE_RESUME which ensures ->report_quiesce()
will notice ptrace_event_pending() and return UTRACE_STOP later, before
return to user-mode.

Small problem. PTRACE_O_TRACEVFORKDONE doesn't imply auto-attach, so we
have to check it before ptrace_clone_attach(). TODO: cleanup this !!!

As expected, this fixes tests/o_tracevforkdone again but the code is still
wrong.

---

 kernel/ptrace.c |    9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

--- PU/kernel/ptrace.c~97_CLONE_VFORKDONE       2009-10-17 17:28:10.000000000 
+0200
+++ PU/kernel/ptrace.c  2009-10-17 19:37:45.000000000 +0200
@@ -199,6 +199,8 @@ static u32 ptrace_report_clone(enum utra
        if (clone_flags & CLONE_VFORK) {
                if (context->options & PTRACE_O_TRACEVFORK)
                        event = PTRACE_EVENT_VFORK;
+               else if (context->options & PTRACE_O_TRACEVFORKDONE)
+                       event = PTRACE_EVENT_VFORK_DONE;
        } else if ((clone_flags & CSIGNAL) != SIGCHLD) {
                if (context->options & PTRACE_O_TRACECLONE)
                        event = PTRACE_EVENT_CLONE;
@@ -209,7 +211,7 @@ static u32 ptrace_report_clone(enum utra
         * Any of these reports implies auto-attaching the new child.
         * So does CLONE_PTRACE, even with no event to report.
         */
-       if (event || (clone_flags & CLONE_PTRACE))
+       if ((event && event != PTRACE_EVENT_VFORK_DONE) || (clone_flags & 
CLONE_PTRACE))
                ptrace_clone_attach(parent, child, context->options);
 
        if (!event || (clone_flags & CLONE_UNTRACED))
@@ -218,7 +220,10 @@ static u32 ptrace_report_clone(enum utra
        set_stop_code(context, event);
        context->eventmsg = child->pid;
 
-       return UTRACE_STOP;
+       if (event == PTRACE_EVENT_VFORK_DONE)
+               return UTRACE_REPORT;
+       else
+               return UTRACE_STOP;
 }
 
 static inline void set_syscall_code(struct ptrace_context *context, int event)

Reply via email to