If the tracee sets PTRACE_O_TRACEVFORK and the tracee does vfork(),
the tracee stops inside do_fork() and reports PTRACE_EVENT_VFORK.

If we need to report PTRACE_EVENT_VFORK_DONE after that, ptrace_resume()
can use ptrace_wake_up() or just report the stacked event. We need to
wake up the tracee, it should pass wait_for_completion() and then stop
and report before return to user-mode.

So. change ptrace_resume() to do set_stop_code() + utrace_control(REPORT)
like ptrace_report_clone() does, we rely on ->report_quiesce() which must
notice ptrace_event_pending() and return UTRACE_STOP.

And inow we pass this test:

        #include <stdio.h>
        #include <unistd.h>
        #include <stdlib.h>
        #include <signal.h>
        #include <errno.h>
        #include <sys/wait.h>
        #include <sys/ptrace.h>
        #include <assert.h>

        #define WEVENT(s) ((s & 0xFF0000) >> 16)

        static unsigned long __step(int line, int pid, int o, int e)
        {
                int stat, cont = PTRACE_CONT;
                unsigned long msg;

                if (o == PTRACE_SYSCALL) {
                        cont = o;
                } else if (o) {
                        assert(0 == ptrace(PTRACE_SETOPTIONS, pid, 0, o));
                }

                assert(0 == ptrace(cont, pid, 0, 0));
                assert(waitpid(pid, &stat, __WALL) == pid);
        //      assert(WIFSTOPPED(stat));
        //      assert(WSTOPSIG(stat) == SIGTRAP);
                if (!WIFSTOPPED(stat) || WSTOPSIG(stat) != SIGTRAP)
                        { printf("ERR!! %3d: %06X\n", line, stat); exit(1); }
                assert(WEVENT(stat) == e);
                assert(0 == ptrace(PTRACE_GETEVENTMSG, pid, 0, &msg));

                return msg;
        }

        #define step(pid, o, e) __step(__LINE__, pid, o, e)

        #define T_FORK_A        0x01
        #define T_FORK_Z        0x02
        #define T_SYSCALL       0x04
        #define T_SIGNAL        0x08

        #define T_ALL   (T_FORK_A | T_FORK_Z | T_SYSCALL | T_SIGNAL)

        static void p_todo(int todo)
        {
                printf("todo: ");

                #define P(n)    if (todo & n) printf(#n " ")
                P(T_FORK_A); P(T_FORK_Z); P(T_SYSCALL); P(T_SIGNAL);
                #undef P

                if (todo & ~T_ALL) printf("ERR!!!");
                printf("\n");
        }

        static void tst(int todo)
        {
                int pid, cpid=-1, stat;
                long msg;

                p_todo(todo);

                pid = fork();
                if (!pid) {
                        int vp;

                        if (!(todo & T_SIGNAL))
                                signal(SIGCHLD, SIG_IGN);

                        assert(0 == ptrace(PTRACE_TRACEME, 0,0,0));
                        kill(getpid(), SIGSTOP);

                        vp = vfork();
                        exit(0x43);
                }

                assert(wait(&stat) == pid);
                assert(WIFSTOPPED(stat) && WSTOPSIG(stat) == SIGSTOP);

                if (todo & T_SYSCALL) {
                        msg = step(pid, PTRACE_SYSCALL, 0);
                        assert(msg == 0);
                }

                if (todo & T_FORK_A) {
                        cpid = step(pid, PTRACE_O_TRACEVFORK, 
PTRACE_EVENT_VFORK);
                        assert(waitpid(cpid, &stat, __WALL) == cpid);
                        assert(WIFSTOPPED(stat));
                        assert(WSTOPSIG(stat) == SIGSTOP);
                        assert(0 == ptrace(PTRACE_CONT, cpid, 0, 0));
                        assert(waitpid(cpid, &stat, __WALL) == cpid);
                        assert(WIFEXITED(stat));
                }

                if (todo & T_FORK_Z) {
                        msg = step(pid, PTRACE_O_TRACEVFORKDONE, 
PTRACE_EVENT_VFORK_DONE);
                        if (todo & T_FORK_A) assert(msg == cpid);
                }

                if (todo & T_SIGNAL) {
                        assert(0 == ptrace(PTRACE_CONT, pid, 0, 0));
                        assert(waitpid(pid, &stat, __WALL) == pid);
                        if (!(todo & T_FORK_A) && stat == 0x4300)
                                goto parent_exited_first;
                        assert(WIFSTOPPED(stat));
                        assert(WSTOPSIG(stat) == SIGCHLD);
                }

                if (todo & T_SYSCALL) {
                        msg = step(pid, PTRACE_SYSCALL, 0);
                        if (todo & T_FORK_A) assert(msg == cpid);
                }

                assert(0 == ptrace(PTRACE_CONT, pid, 0, 0));
                assert(waitpid(pid, &stat, __WALL) == pid);
                assert(WIFEXITED(stat));

        parent_exited_first:
                assert(waitpid(-1, &stat, __WALL) == -1);
                assert(errno == ECHILD);
        }

        int main(int argc, char* argv[])
        {
                int todo;

                if (argc > 1) {
                        todo = atoi(argv[1]);
                        tst(todo);
                } else {
                        int todo;
                        for (todo = 0; todo <= T_ALL; ++todo)
                                tst(todo);
                }

                return 0;
        }

---

 kernel/ptrace.c |   10 ++++++++++
 1 file changed, 10 insertions(+)

--- PU/kernel/ptrace.c~99_VFORK_THEN_VFORKDONE  2009-10-17 19:46:42.000000000 
+0200
+++ PU/kernel/ptrace.c  2009-10-17 19:50:54.000000000 +0200
@@ -975,11 +975,21 @@ static void do_ptrace_resume(struct utra
        case 0:
                // XXX: JCTL stop
                break;
+
+       case PTRACE_EVENT_VFORK:
+               if (context->options & PTRACE_O_TRACEVFORKDONE) {
+                       set_stop_code(context, PTRACE_EVENT_VFORK_DONE);
+                       utrace_control(tracee, engine, UTRACE_REPORT);
+                       return;
+               }
+               break;
+
        case PTRACE_EVENT_SYSCALL_ENTRY:
        case PTRACE_EVENT_SYSCALL_EXIT:
                if (data)
                        send_sig_info(data, SEND_SIG_PRIV, tracee);
                break;
+
        case PTRACE_EVENT_SIGNAL:
                context->signr = data;
                break;

Reply via email to