Add ptrace_context->signr. The tracer just sets context->signr = data and resumes the tracee, the tracee updates context->siginfo if needed.
When the tracee reports the signal it sets context->signr = info->si_signo. This matches the current behaviour, but perhaps it would be bettter to set ->signr = 0. This only matters if the tracee is killed. --- kernel/ptrace.c | 66 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 32 deletions(-) --- PU/kernel/ptrace.c~61_RESUME_SIGNAL_BY_TRACEE 2009-10-03 00:00:27.000000000 +0200 +++ PU/kernel/ptrace.c 2009-10-03 01:37:41.000000000 +0200 @@ -27,7 +27,8 @@ struct ptrace_context { int options; - siginfo_t *siginfo; + int signr; + siginfo_t *siginfo; int ev_name; int ev_code; @@ -262,23 +263,37 @@ static u32 ptrace_report_exec(enum utrac return UTRACE_STOP; } -/* - * XXX: This all is wrong/racy/crashable - */ -static void ptrace_resume_signal(struct ptrace_context* context, int data) +static enum utrace_signal_action resume_signal(struct task_struct *task, + int signr, siginfo_t *info, + struct k_sigaction *return_ka) { - siginfo_t *info = context->siginfo; - - if (WARN_ON(!info)) - return; - - if (info->si_signo != data) { - info->si_signo = data; - info->si_code = SI_USER; + /* Did the debugger cancel the sig? */ + if (!signr) + return UTRACE_SIGNAL_IGN; + /* + * Update the siginfo structure if the signal has changed. + * If the debugger wanted something specific in the siginfo + * then it should have updated *info via PTRACE_SETSIGINFO. + */ + if (info->si_signo != signr) { + info->si_signo = signr; info->si_errno = 0; - info->si_pid = task_pid_vnr(current); - info->si_uid = current_uid(); + info->si_code = SI_USER; + info->si_pid = task_pid_vnr(current->parent); + info->si_uid = task_uid(current->parent); } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(&task->blocked, signr)) { + send_sig_info(signr, info, task); + return UTRACE_SIGNAL_IGN; + } + + spin_lock_irq(&task->sighand->siglock); + *return_ka = task->sighand->action[signr - 1]; + spin_unlock_irq(&task->sighand->siglock); + + return UTRACE_SIGNAL_DELIVER; } static u32 ptrace_report_signal(u32 action, @@ -319,22 +334,8 @@ static u32 ptrace_report_signal(u32 acti return resume | UTRACE_SIGNAL_IGN; context->siginfo = NULL; - if (!info->si_signo) // debugger cancelled sig - return resume | UTRACE_SIGNAL_IGN; - /* - * If the (new) signal is now blocked, requeue it. - */ - if (sigismember(&task->blocked, info->si_signo)) { - send_sig_info(info->si_signo, info, task); - return resume | UTRACE_SIGNAL_IGN; - } - - spin_lock_irq(&task->sighand->siglock); - *return_ka = task->sighand->action[info->si_signo - 1]; - spin_unlock_irq(&task->sighand->siglock); - - return resume | UTRACE_SIGNAL_DELIVER; - + return resume | resume_signal(task, context->signr, + info, return_ka); default: WARN_ON(context->siginfo); context->siginfo = info; @@ -344,6 +345,7 @@ static u32 ptrace_report_signal(u32 acti context->ev_name = PTRACE_EVENT_SIGNAL; context->ev_code = info->si_signo; + context->signr = info->si_signo; return UTRACE_STOP | UTRACE_SIGNAL_IGN; } @@ -895,7 +897,7 @@ static void do_ptrace_resume(struct utra send_sig_info(data, SEND_SIG_PRIV, tracee); break; case PTRACE_EVENT_SIGNAL: - ptrace_resume_signal(context, data); + context->signr = data; break; }