[PATCH 0/5] ptrace-utrace: fix exit_ptrace() vs ptrace_report_signal() races

2010-12-10 Thread Oleg Nesterov
In short: exit_ptrace()-ptrace_detach_task() is very wrong when it
tries to detach the !stopped tracee, we can not trust get_stop_event()
in this case.

This means that in the case like

ptrace(PTRACE_CONT, ..., SIGXXX);
exit(); // --- calls ptrace_detach_task()

the tracee can miss SIGXXX if ptrace_detach_task() does
utrace_control(UTRACE_DETACH) before the tracee calls -report_signal().

5/5 is the actual fix. 1-4 are preparations to simplify the review
and document the changes.

Oleg.



[PATCH 3/5] ptrace-utrace: don't assume resume != UTRACE_RESUME means stepping

2010-12-10 Thread Oleg Nesterov
No functional changes. Currently ptrace_report_syscall_exit() and
ptrace_report_signal(UTRACE_SIGNAL_HANDLER) can see either UTRACE_RESUME
or UTRACE_BLOCKSTEP/UTRACE_SINGLESTEP, but we are going to change this.
Change the code to not assume that resume != UTRACE_RESUME means stepping.
Add the trivial helper, is_step_resume(), which does the check.

Signed-off-by: Oleg Nesterov o...@redhat.com
---

 kernel/ptrace-utrace.c |   15 +++
 1 file changed, 7 insertions(+), 8 deletions(-)

--- kstub/kernel/ptrace-utrace.c~3_resume_step_helper   2010-09-20 
03:53:31.0 +0200
+++ kstub/kernel/ptrace-utrace.c2010-09-20 03:53:31.0 +0200
@@ -418,6 +418,11 @@ static u32 ptrace_report_syscall_entry(u
return UTRACE_SYSCALL_RUN | UTRACE_STOP;
 }
 
+static inline bool is_step_resume(enum utrace_resume_action resume)
+{
+   return resume == UTRACE_BLOCKSTEP || resume == UTRACE_SINGLESTEP;
+}
+
 static u32 ptrace_report_syscall_exit(u32 action, struct utrace_engine *engine,
  struct pt_regs *regs)
 {
@@ -426,10 +431,7 @@ static u32 ptrace_report_syscall_exit(u3
if (ptrace_event_pending(ctx))
return UTRACE_STOP;
 
-   if (ctx-resume != UTRACE_RESUME) {
-   WARN_ON(ctx-resume != UTRACE_BLOCKSTEP 
-   ctx-resume != UTRACE_SINGLESTEP);
-
+   if (is_step_resume(ctx-resume)) {
ctx-signr = SIGTRAP;
return UTRACE_INTERRUPT;
}
@@ -517,10 +519,7 @@ static u32 ptrace_report_signal(u32 acti
if (WARN_ON(ctx-siginfo))
ctx-siginfo = NULL;
 
-   if (resume != UTRACE_RESUME) {
-   WARN_ON(resume != UTRACE_BLOCKSTEP 
-   resume != UTRACE_SINGLESTEP);
-
+   if (is_step_resume(resume)) {
set_stop_code(ctx, PTRACE_EVENT_SIGTRAP);
return UTRACE_STOP | UTRACE_SIGNAL_IGN;
}



[PATCH 5/5] ptrace-utrace: fix exit_ptrace() vs ptrace_report_signal() races

2010-12-10 Thread Oleg Nesterov
Finally, the actual fix.

ptrace_detach_task(sig = -1) is very buggy. Somehow I completely forgot
that implicit detach can race with the running tracee. Depending on how
exactly it races with ptrace_report_signal() we can have the following
problems:

1) If the tracer exits right after ptrace(PTRACE_CONT, SIGXXX)
   the tracee can miss this signal.

2) the tracee can report a signal and stop after it was already
   detached.

Change ptrace_detach_task(sig = -1) to set ctx-resume = UTRACE_DETACH
before anything else, change ptrace_report_signal() to check ctx-resume
before reporting a signal. This fixes the 2nd problem.

To fix the 1st problem, change !explicit case to check -siginfo != NULL
instead of relying on get_stop_event() which can't work with the running
tracee.

Reported-by: Glen Johnson bugpr...@us.ibm.com
Signed-off-by: Oleg Nesterov o...@redhat.com
---

 kernel/ptrace-utrace.c |   41 ++---
 1 file changed, 34 insertions(+), 7 deletions(-)

--- kstub/kernel/ptrace-utrace.c~5_implicit_detach_races2010-09-20 
03:53:32.0 +0200
+++ kstub/kernel/ptrace-utrace.c2010-09-20 03:53:32.0 +0200
@@ -262,7 +262,7 @@ static void ptrace_detach_task(struct ta
 * If true, the caller is PTRACE_DETACH, otherwise
 * the tracer detaches implicitly during exit.
 */
-   bool voluntary = (sig = 0);
+   bool explicit = (sig = 0);
struct utrace_engine *engine = ptrace_lookup_engine(tracee);
enum utrace_resume_action action = UTRACE_DETACH;
struct ptrace_context *ctx;
@@ -272,24 +272,46 @@ static void ptrace_detach_task(struct ta
 
ctx = ptrace_context(engine);
 
-   if (sig) {
+   if (!explicit) {
+   int err;
 
+   /*
+* We are going to detach, the tracee can be running.
+* Ensure ptrace_report_signal() won't report a signal.
+*/
+   ctx-resume = UTRACE_DETACH;
+   err = utrace_barrier_uninterruptible(tracee, engine);
+
+   if (!err  ctx-siginfo) {
+   /*
+* The tracee has already reported a signal
+* before utrace_barrier().
+*
+* Resume it like we do in PTRACE_EVENT_SIGNAL
+* case below. The difference is that we can race
+* with ptrace_report_signal() if the tracee is
+* running but this doesn't matter. In any case
+* UTRACE_SIGNAL_REPORT must be pending and it
+* can return nothing but UTRACE_DETACH.
+*/
+   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;
action = UTRACE_RESUME;
break;
}
}
 
-   ptrace_wake_up(tracee, engine, action, voluntary);
+   ptrace_wake_up(tracee, engine, action, explicit);
 
if (action != UTRACE_DETACH)
ctx-options = PTRACE_O_DETACHED;
@@ -553,6 +575,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.



Re: [PATCH 0/5] ptrace-utrace: fix exit_ptrace() vs ptrace_report_signal() races

2010-12-10 Thread Roland McGrath
I've merged these patches to the utrace-ptrace branch, now merged up to
2.6.37-rc5, and also the 2.6.34 and 2.6.35 backport branches.  The 2.6.33
backport branch is no longer being maintained.  I didn't update the 2.6.36
backport branch and probably won't maintain it unless some Fedora release
starts using that kernel version (rawhide is already on 2.6.37-rc5 now).


Thanks,
Roland