Move send_sigtrap() logic from ptrace_resume() to ptrace_report_signal(). ptrace_resume() sets ctx->signr = SIGTRAP and returns UTRACE_INTERRUPT. ptrace_report_signal() notices SIGTRAP, fills *info and reports the signal.
fill_sigtrap_info() mimics x86-specific send_sigtrap(), therefore: - it sets task->thread.trap_no/error_code under CONFIG_X86, what should it do in the #else case? - it sets info->si_addr = KSTK_EIP() which doesn't check user_mode_vm(). Hopefully this is OK? - with or without this patch utrace-ptrace assumes that PTRACE_SINGLESTEP after PTRACE_EVENT_SYSCALL_EXIT needs fill_sigtrap_info()'ed signal, this may break or fix !x86 machines, and I don't know how to check. Perhaps ptrace_resume(PTRACE_EVENT_SYSCALL_EXIT) needs ifdef's. User-visible changes: - unlike send_sigtrap()->force_sig_info() we don't unblock SIGTRAP or reset the handler - UTRACE_SIGNAL_REPORT synthesizes the "extra" SIGTRAP even if this signal is already penging these changes looks good (but see the next patch). However, any user-visible change is dangerous. For example, suppose a tracee reports SYSCALL_EXIT and the tracer does ptrace(PTRACE_SINGLESTEP, SIGTRAP). Before this change (or with upstream kernel) send_sigtrap() is "lost", because ptrace_report_syscall() does send_sig(SIGTRAP, current) first. With this patch we report TRAP_BRKPT and then the normal SIGTRAP. Also, we don't depend on dequeue_signal() which can dequeue another signr before SIGTRAP. Again, looks like a fix actually, but may break some test-case or stupid app. --- kernel/ptrace.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) --- PU/kernel/ptrace.c~127_OFFLOAD_SEND_SIGTRAP_TO_REPORT_SIGNAL 2009-11-02 00:31:30.000000000 +0100 +++ PU/kernel/ptrace.c 2009-11-02 05:31:46.000000000 +0100 @@ -439,6 +439,18 @@ static u32 ptrace_report_exec(enum utrac return UTRACE_STOP; } +static void fill_sigtrap_info(struct task_struct *task, siginfo_t *info) +{ +#ifdef CONFIG_X86 + task->thread.trap_no = 1; + task->thread.error_code = 0; +#endif + memset(info, 0, sizeof(*info)); + info->si_signo = SIGTRAP; + info->si_code = TRAP_BRKPT; + info->si_addr = (void __user*)KSTK_EIP(task); +} + static enum utrace_signal_action resume_signal(struct task_struct *task, struct ptrace_context *ctx, struct k_sigaction *return_ka) @@ -506,8 +518,15 @@ static u32 ptrace_report_signal(u32 acti } case UTRACE_SIGNAL_REPORT: - if (!ctx->siginfo) - return resume | UTRACE_SIGNAL_IGN; + if (!ctx->siginfo) { + if (!ctx->signr) + return resume | UTRACE_SIGNAL_IGN; + + WARN_ON(ctx->signr != SIGTRAP); + /* set by ptrace_resume(PTRACE_EVENT_SYSCALL_EXIT) */ + fill_sigtrap_info(task, info); + break; + } if (WARN_ON(ctx->siginfo != info)) return resume | UTRACE_SIGNAL_IGN; @@ -911,12 +930,12 @@ static int ptrace_resume(struct task_str case PTRACE_EVENT_SYSCALL_EXIT: if (action != UTRACE_RESUME) { - read_lock(&tasklist_lock); - if (tracee->sighand) - send_sigtrap(tracee, task_pt_regs(tracee), - 0, TRAP_BRKPT); - read_unlock(&tasklist_lock); - action = UTRACE_RESUME; + /* + * single-stepping. UTRACE_SIGNAL_REPORT will + * synthesize a trap to follow the syscall insn. + */ + ctx->signr = SIGTRAP; + action = UTRACE_INTERRUPT; } /* fallthrough */