exit_ptrace() && ptrace_report_signal() problems
Oleg Nesterov
oleg at redhat.com
Tue Sep 14 21:55:14 UTC 2010
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;
More information about the utrace-devel
mailing list