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; }