On 09/14, Oleg Nesterov wrote: > > On 09/13, Roland McGrath wrote: > > > > Locks just between the tracer and ptrace_report_signal are not bad. > > OK, but let me think a bit more. I'd really like to avoid adding the > new lock to avoid the very unlikely race with the exiting tracer.
Oh, please take a look at this (untested) patch. But I think I should make another attempt, probably spinlock (->siglock ?) would be cleaner. Oleg. --- kstub/kernel/ptrace-utrace.c~9_EXIT_PTRACE_CAN_LOSE_SIG 2010-08-24 14:31:17.000000000 +0200 +++ kstub/kernel/ptrace-utrace.c 2010-09-14 23:37:59.000000000 +0200 @@ -67,6 +67,7 @@ struct ptrace_context { #define PT_UTRACED 0x00001000 #define PTRACE_O_SYSEMU 0x100 +#define PTRACE_O_REUSABLE 0x200 #define PTRACE_EVENT_SYSCALL (1 << 16) #define PTRACE_EVENT_SIGTRAP (2 << 16) @@ -115,7 +116,7 @@ ptrace_reuse_engine(struct task_struct * return engine; ctx = ptrace_context(engine); - if (unlikely(ctx->resume == UTRACE_DETACH)) { + if (unlikely(ctx->options == PTRACE_O_REUSABLE)) { /* * Try to reuse this self-detaching engine. * The only caller which can hit this case is ptrace_attach(), @@ -246,24 +247,48 @@ static void ptrace_detach_task(struct ta */ bool voluntary = (sig >= 0); struct utrace_engine *engine = ptrace_lookup_engine(tracee); + struct ptrace_context *ctx = ptrace_context(engine); enum utrace_resume_action action = UTRACE_DETACH; if (unlikely(IS_ERR(engine))) return; - if (sig) { - struct ptrace_context *ctx = ptrace_context(engine); + if (!voluntary) { + int err; + + ctx->resume = UTRACE_DETACH; + /* synchronize with ptrace_report_signal() */ + do { + err = utrace_barrier(tracee, engine); + } while (err == -ERESTARTSYS); + + if (!err) { + /* + * The tracee has already reported a signal. If we + * see ->siginfo != NULL it is safe to mark this ctx + * as reusable and resume the tracee. We can race + * with ptrace_report_signal(UTRACE_SIGNAL_REPORT) + * already in progress but this doesn't matter, it + * must see ->resume = UTRACE_DETACH. + */ + if (ctx->siginfo) { + smp_wmb(); + ctx->options = PTRACE_O_REUSABLE; + action = UTRACE_RESUME; + } + } + } else if (sig) { switch (get_stop_event(ctx)) { case PTRACE_EVENT_SYSCALL: - if (voluntary) - send_sig_info(sig, SEND_SIG_PRIV, tracee); + send_sig_info(sig, SEND_SIG_PRIV, tracee); break; case PTRACE_EVENT_SIGNAL: - if (voluntary) - ctx->signr = sig; + ctx->signr = sig; ctx->resume = UTRACE_DETACH; + smp_wmb(); + ctx->options = PTRACE_O_REUSABLE; action = UTRACE_RESUME; break; } @@ -410,6 +435,9 @@ static u32 ptrace_report_syscall_exit(u3 return UTRACE_STOP; if (ctx->resume != UTRACE_RESUME) { + if (ctx->resume == UTRACE_DETACH) + return UTRACE_RESUME; + WARN_ON(ctx->resume != UTRACE_BLOCKSTEP && ctx->resume != UTRACE_SINGLESTEP); ctx->resume = UTRACE_RESUME; @@ -502,6 +530,9 @@ static u32 ptrace_report_signal(u32 acti ctx->siginfo = NULL; if (resume != UTRACE_RESUME) { + if (resume == UTRACE_DETACH) + return action; + WARN_ON(resume != UTRACE_BLOCKSTEP && resume != UTRACE_SINGLESTEP); @@ -531,6 +562,11 @@ static u32 ptrace_report_signal(u32 acti } WARN_ON(ctx->siginfo); + + /* Raced with the exiting tracer ? */ + if (resume == UTRACE_DETACH) + return action; + ctx->siginfo = info; /* * ctx->siginfo points to the caller's stack. @@ -759,7 +795,7 @@ void exit_ptrace(struct task_struct *tra static int ptrace_set_options(struct task_struct *tracee, struct utrace_engine *engine, long data) { - BUILD_BUG_ON(PTRACE_O_MASK & PTRACE_O_SYSEMU); + BUILD_BUG_ON(PTRACE_O_MASK & (PTRACE_O_SYSEMU | PTRACE_O_REUSABLE)); ptrace_set_events(tracee, engine, data & PTRACE_O_MASK); return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;