dlm: fix plock use-after-free
authorDavid Teigland <teigland@redhat.com>
Thu, 18 Jun 2009 18:20:24 +0000 (13:20 -0500)
committerDavid Teigland <teigland@redhat.com>
Thu, 18 Jun 2009 18:42:42 +0000 (13:42 -0500)
Fix a regression from the original addition of nfs lock support
586759f03e2e9031ac5589912a51a909ed53c30a.  When a synchronous
(non-nfs) plock completes, the waiting thread will wake up and
free the op struct.  This races with the user thread in
dev_write() which goes on to read the op's callback field to
check if the lock is async and needs a callback.  This check
can happen on the freed op.  The fix is to note the callback
value before the op can be freed.

Signed-off-by: David Teigland <teigland@redhat.com>
fs/dlm/plock.c

index 894a32d438d5094bb9f1882825f5f01f12f096d7..16f682e26c07e49493e8a667a6b36cf0cf9f6bb6 100644 (file)
@@ -353,7 +353,7 @@ static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
 {
        struct dlm_plock_info info;
        struct plock_op *op;
-       int found = 0;
+       int found = 0, do_callback = 0;
 
        if (count != sizeof(info))
                return -EINVAL;
@@ -366,21 +366,24 @@ static ssize_t dev_write(struct file *file, const char __user *u, size_t count,
 
        spin_lock(&ops_lock);
        list_for_each_entry(op, &recv_list, list) {
-               if (op->info.fsid == info.fsid && op->info.number == info.number &&
+               if (op->info.fsid == info.fsid &&
+                   op->info.number == info.number &&
                    op->info.owner == info.owner) {
+                       struct plock_xop *xop = (struct plock_xop *)op;
                        list_del_init(&op->list);
-                       found = 1;
-                       op->done = 1;
                        memcpy(&op->info, &info, sizeof(info));
+                       if (xop->callback)
+                               do_callback = 1;
+                       else
+                               op->done = 1;
+                       found = 1;
                        break;
                }
        }
        spin_unlock(&ops_lock);
 
        if (found) {
-               struct plock_xop *xop;
-               xop = (struct plock_xop *)op;
-               if (xop->callback)
+               if (do_callback)
                        dlm_plock_callback(op);
                else
                        wake_up(&recv_wq);