As I said, I greatly misread asm code, ->report_syscall_exit() can be
called without utrace_resume/utrace_get_signal. We need the stack of
stop events. Not the stack in fact, array.

Incomplete. The code needs cleanup/refactoring.

In particular, ptrace_report_clone() case is still not right. We can't
check PTRACE_O_TRACEVFORKDONE in do_fork() path, it can be set later.

But it passes clone/vfork tests. Btw, compared to vanilla kernel, the
following tests fail:

        FAIL: attach-wait-on-stopped
        FAIL: step-into-handler
        FAIL: step-through-sigret
        FAIL: detach-stopped
        FAIL: detach-parting-signal

And note that "detach-stopped" should not succeed in fact, it needs
the "detach always wakes up the child" logic which was removed.

---

 kernel/ptrace.c |  168 ++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 109 insertions(+), 59 deletions(-)

--- PU/kernel/ptrace.c~33_EV_ARRAY      2009-09-14 04:17:07.000000000 +0200
+++ PU/kernel/ptrace.c  2009-09-14 07:43:43.000000000 +0200
@@ -24,18 +24,53 @@
 #include <linux/syscalls.h>
 #include <linux/uaccess.h>
 
-typedef void (*resume_stopped_t)(struct utrace_engine *,
+typedef void (*resume_func_t)(struct utrace_engine *,
                                        struct task_struct*, long);
 
+struct ptrace_event {
+       int             ev_code;
+       unsigned long   ev_message;
+       resume_func_t   ev_resume;
+};
+
 struct ptrace_context {
        int options;
 
-       int stopped_code;
-       resume_stopped_t resume_stopped;
-
-       bool o_did_clone_vfork; // XXX: move into ->options
+       struct ptrace_event ev_array[2];
+       unsigned int ev_first, ev_last;
 };
 
+static inline bool ev_empty(struct ptrace_context *context)
+{
+       return context->ev_first == context->ev_last;
+}
+
+static inline struct ptrace_event *__ev_at(struct ptrace_context *context,
+                                               unsigned int idx)
+{
+       return context->ev_array + (idx % ARRAY_SIZE(context->ev_array));
+}
+
+static inline struct ptrace_event *ev_current(struct ptrace_context *context)
+{
+       WARN_ON(ev_empty(context));
+       return __ev_at(context, context->ev_first);
+}
+
+static inline struct ptrace_event *ev_pop(struct ptrace_context *context)
+{
+       WARN_ON(ev_empty(context));
+       return __ev_at(context, context->ev_first++);
+}
+
+static inline struct ptrace_event *ev_push(struct ptrace_context *context)
+{
+       struct ptrace_event *ev = __ev_at(context, context->ev_last++);
+       WARN_ON(context->ev_last - context->ev_first > 
ARRAY_SIZE(context->ev_array));
+       memset(ev, 0, sizeof(*ev));
+       return ev;
+}
+
 static inline struct ptrace_context *
 ptrace_context(struct utrace_engine *engine)
 {
@@ -60,6 +95,8 @@ static int ptrace_attach_task(struct tas
 static void ptrace_abort_attach(struct task_struct *tracee);
 static void ptrace_wake_up(struct utrace_engine *engine,
                struct task_struct *tracee, enum utrace_resume_action action);
+static void do_ptrace_notify_stop(struct ptrace_context *context,
+                                       struct task_struct *tracee);
 
 static void ptrace_detach_task(struct task_struct *child, int sig)
 {
@@ -231,26 +268,9 @@ static void ptrace_resume_vfork_done(str
         ptrace_wake_up(engine, tracee, UTRACE_RESUME);
 }
 
-static bool ck_o_tracevforkdone(struct utrace_engine *engine,
-                               struct task_struct *tracee)
-{
-       struct ptrace_context *context = ptrace_context(engine);
-
-       if (!(context->o_did_clone_vfork &&
-            (context->options & PTRACE_O_TRACEVFORKDONE)))
-               return false;
-
-       context->resume_stopped = ptrace_resume_vfork_done;
-       context->stopped_code = (PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP;
-
-       utrace_control(tracee, engine, UTRACE_REPORT);
-       return true;
-}
-
 static void ptrace_resume_clone(struct utrace_engine *engine,
                                struct task_struct *tracee, long data)
 {
-       ck_o_tracevforkdone(engine, tracee);
        ptrace_wake_up(engine, tracee, UTRACE_RESUME);
 }
 
@@ -261,18 +281,16 @@ static u32 ptrace_report_clone(enum utra
                               struct task_struct *child)
 {
        struct ptrace_context *context = ptrace_context(engine);
-       int event;
+       int event, ret = UTRACE_RESUME;
 
-       WARN_ON(context->resume_stopped);
-       WARN_ON(context->stopped_code);
+       WARN_ON(!ev_empty(context));
 
+       // XXX: WRONG!!!
        if (clone_flags & CLONE_UNTRACED)
-               return UTRACE_RESUME;
+               return ret;
 
        event = 0;
-       context->o_did_clone_vfork = false;
        if (clone_flags & CLONE_VFORK) {
-               context->o_did_clone_vfork = true;
                if (context->options & PTRACE_O_TRACEVFORK)
                        event = PTRACE_EVENT_VFORK;
        } else if ((clone_flags & CSIGNAL) != SIGCHLD) {
@@ -291,16 +309,27 @@ static u32 ptrace_report_clone(enum utra
 
        // XXX: child->pid is wrong! use tracer's pid_ns
        if (event) {
-               parent->ptrace_message = child->pid;
-               context->resume_stopped = ptrace_resume_clone;
-               context->stopped_code = (event << 8) | SIGTRAP;
+               struct ptrace_event *ev = ev_push(context);
 
-               return UTRACE_STOP;
-       } else if (ck_o_tracevforkdone(engine, parent)) {
-               parent->ptrace_message = child->pid;
+               ev->ev_message = child->pid;
+               ev->ev_resume = ptrace_resume_clone;
+               ev->ev_code = (event << 8) | SIGTRAP;
+
+               ret = UTRACE_STOP;
        }
 
-       return UTRACE_RESUME;
+       if ((clone_flags & CLONE_VFORK) &&
+           (context->options & PTRACE_O_TRACEVFORKDONE)) {
+               struct ptrace_event *ev = ev_push(context);
+
+               ev->ev_message = child->pid;
+               ev->ev_resume = ptrace_resume_vfork_done;
+               ev->ev_code = (PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP;
+
+               ret = UTRACE_STOP;
+       }
+
+       return ret;
 }
 
 static u32 ptrace_report_syscall(u32 action, struct utrace_engine *engine,
@@ -328,12 +357,19 @@ static u32 ptrace_report_syscall_entry(u
        return ptrace_report_syscall(UTRACE_SYSCALL_RUN, engine, task);
 }
 
+// XXX: move this all into ptrace_resume()
 static void ptrace_wake_up(struct utrace_engine *engine,
                                struct task_struct *tracee,
                                enum utrace_resume_action action)
 {
+       struct ptrace_context *context = ptrace_context(engine);
        unsigned long flags;
 
+       if (!ev_empty(context)) {
+               do_ptrace_notify_stop(context, tracee);
+               return;
+       }
+
        /* preserve the compatibility bug */
        if (!lock_task_sighand(tracee, &flags))
                return;
@@ -366,16 +402,12 @@ static u32 ptrace_report_syscall_exit(en
                                      struct pt_regs *regs)
 {
        struct ptrace_context *context = ptrace_context(engine);
+       struct ptrace_event *ev;
 
-       // XXX: temporary debugging check...
-       if (!fatal_signal_pending(task)) {
-               WARN_ON(context->resume_stopped);
-               WARN_ON(context->stopped_code);
-       }
-
-       context->resume_stopped = ptrace_resume_syscall;
-       context->stopped_code = (context->options & PTRACE_O_TRACESYSGOOD) ?
-                               (SIGTRAP | 0x80) : SIGTRAP;
+       ev = ev_push(context);
+       ev->ev_resume = ptrace_resume_syscall;
+       ev->ev_code = (context->options & PTRACE_O_TRACESYSGOOD) ?
+                       (SIGTRAP | 0x80) : SIGTRAP;
        return UTRACE_STOP;
 }
 
@@ -440,7 +472,8 @@ static u32 ptrace_report_signal(u32 acti
 {
        struct ptrace_context *context = ptrace_context(engine);
 
-       if (context->stopped_code) {
+       if (!ev_empty(context)) {
+               WARN_ON(!ev_current(context)->ev_code && 
!fatal_signal_pending(task));
                action = utrace_signal_action(action);
                WARN_ON(action != UTRACE_SIGNAL_REPORT);
                return action | UTRACE_STOP;
@@ -487,8 +520,10 @@ static u32 ptrace_report_quiesce(u32 act
 {
        struct ptrace_context *context = ptrace_context(engine);
 
-       if (context->stopped_code)
+       if (!ev_empty(context)) {
+               WARN_ON(!ev_current(context)->ev_code && 
!fatal_signal_pending(task));
                return UTRACE_STOP;
+       }
 
        if (event == 0) {
                task->last_siginfo = NULL;
@@ -954,6 +989,24 @@ static int ptrace_setsiginfo(struct task
 #define is_sysemu_singlestep(request)  0
 #endif
 
+static void do_ptrace_notify_stop(struct ptrace_context *context,
+                                       struct task_struct *tracee)
+{
+       struct ptrace_event *ev = ev_current(context);
+
+       if (ev->ev_message)
+               tracee->ptrace_message = ev->ev_message;
+       tracee->exit_code = ev->ev_code;
+
+       // XXX: for debug only
+       WARN_ON(ev->ev_code == 0);
+       ev->ev_code = 0;
+
+       read_lock(&tasklist_lock);
+       do_notify_parent_cldstop(tracee, CLD_TRAPPED);
+       read_unlock(&tasklist_lock);
+}
+
 void ptrace_notify_stop(struct task_struct *tracee)
 {
        struct utrace_engine *engine;
@@ -968,16 +1021,11 @@ void ptrace_notify_stop(struct task_stru
        }
 
        context = ptrace_context(engine);
-       if (context->stopped_code) {
-               tracee->exit_code = context->stopped_code;
-               context->stopped_code = 0;
-
-               read_lock(&tasklist_lock);
-               do_notify_parent_cldstop(tracee, CLD_TRAPPED);
-               read_unlock(&tasklist_lock);
+       if (!ev_empty(context)) {
+               do_ptrace_notify_stop(context, tracee);
+               return;
        }
 
-       // XXX: will die soon
        if (ptrace_resume_action(tracee) == UTRACE_STOP) {
                read_lock(&tasklist_lock);
                do_notify_parent_cldstop(tracee, CLD_TRAPPED);
@@ -1034,11 +1082,13 @@ static int ptrace_resume(struct task_str
        if (!ret) {
                int event;
 
-               if (context->resume_stopped) {
-                       resume_stopped_t resume = context->resume_stopped;
-                       context->resume_stopped = NULL;
-                       WARN_ON(context->stopped_code);
-                       resume(engine, child, data);
+               if (!ev_empty(context)) {
+                       struct ptrace_event *ev = ev_pop(context);
+
+                       WARN_ON(ev->ev_code); // XXX: debug
+                       ev->ev_resume(engine, child, data);
+                       ev->ev_resume = NULL; // XXX: debug
+
                        utrace_engine_put(engine);
                        return 0;
                }

Reply via email to