commit bf679f65284b834b455e8453d10616073ff58d2c
Author: Srikar Dronamraju <sri...@linux.vnet.ibm.com>
Date:   Tue Aug 18 18:15:49 2009 +0530

detach fix


Saw a Oops message 

BUG: unable to handle kernel NULL pointer dereference at 0000000000000018
IP: [<ffffffff8107a8cd>] get_utrace_lock+0x43/0x115

I figured out the reason for the oops message as "2nd parameter
(engine) to utrace_set_events as NULL."

Call sequence is as below:
- traced process exits resulting in gdb_utrace_report_death being
  called.
- gdb_utrace_report_death sets p->engine to NULL and returns
  UTRACE_DETACH.
- gdb calls release callback (proc_gdb_release() which results in 
        - utrace_set_events and utrace_control(..,UTRACE_DETACH) with
          engine being set to NULL  resulting in OOPS.

Also we were explicitly decrementing reference to engine even when we
havent explicitly incremented a reference.


This patch 
- Doesnt detach from release callback if the engine is already detached.
- removes explicit reference decrement.
- removes setting engine to NULL in gdb_utrace_report_death().

Signed-off-by: Srikar Dronamraju <sri...@linux.vnet.ibm.com>

diff --git a/kernel/utrace-gdb.c b/kernel/utrace-gdb.c
index f7833dc..8cf2b0f 100644
--- a/kernel/utrace-gdb.c
+++ b/kernel/utrace-gdb.c
@@ -497,8 +497,6 @@ u32 gdb_utrace_report_death(struct utrace_engine *engine,
         snprintf (p->stopcode, GDB_BUFMAX, "X%2x", (unsigned)(signal & 0xFF));
         push_output_packet (p, p->stopcode);
 
-        p->engine = NULL;
-
 #ifdef CONFIG_HAVE_UPROBES
         {
                 struct list_head *l;
@@ -1245,11 +1243,11 @@ static int proc_gdb_release(struct inode *inode, struct 
file *filp)
         }
 #endif /* CONFIG_HAVE_UPROBES */
 
-        if (task == NULL) {
+       if (task == NULL) {
                 /* The thread is already gone; report_death was already 
called. */
-                pr_debug ("gdb %d releasing old\n", p->target);
-        } else {
-                pr_debug ("gdb %d releasing current\n", p->target);
+               pr_debug("gdb %d releasing old\n", p->target);
+       } else if (p->at_quiesce_do != UTRACE_DETACH) {
+               pr_debug("gdb %d releasing current\n", p->target);
 
                 ret = utrace_set_events(task, p->engine, 0);
                 if (ret == -EINPROGRESS)
@@ -1259,8 +1257,6 @@ static int proc_gdb_release(struct inode *inode, struct 
file *filp)
                 ret = utrace_control(task, p->engine, UTRACE_DETACH); /* => 
RESUME */
                 if (ret == -EINPROGRESS)
                         ret = utrace_barrier(task, p->engine);
-                
-                utrace_engine_put (p->engine);
         }
 
         list_del(&p->link);

Reply via email to