null_blk: fix use-after-free problem
authorMike Krinkin <krinkin.m.u@gmail.com>
Sun, 19 Jul 2015 06:53:17 +0000 (09:53 +0300)
committerJens Axboe <axboe@fb.com>
Wed, 22 Jul 2015 19:30:20 +0000 (13:30 -0600)
end_cmd finishes request associated with nullb_cmd struct, so we
should save pointer to request_queue in a local variable before
calling end_cmd.

The problem was causes general protection fault with slab poisoning
enabled.

Fixes: 8b70f45e2eb2 ("null_blk: restart request processing on completion handler")
Tested-by: Akinobu Mita <akinobu.mita@gmail.com>
Signed-off-by: Mike Krinkin <krinkin.m.u@gmail.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
drivers/block/null_blk.c

index 69de41a87b74311b2b7478fb0226b8bc253c6ebc..3177b245d2bdf63e821a12a4c0f18cbab1b16229 100644 (file)
@@ -240,19 +240,19 @@ static enum hrtimer_restart null_cmd_timer_expired(struct hrtimer *timer)
        while ((entry = llist_del_all(&cq->list)) != NULL) {
                entry = llist_reverse_order(entry);
                do {
+                       struct request_queue *q = NULL;
+
                        cmd = container_of(entry, struct nullb_cmd, ll_list);
                        entry = entry->next;
+                       if (cmd->rq)
+                               q = cmd->rq->q;
                        end_cmd(cmd);
 
-                       if (cmd->rq) {
-                               struct request_queue *q = cmd->rq->q;
-
-                               if (!q->mq_ops && blk_queue_stopped(q)) {
-                                       spin_lock(q->queue_lock);
-                                       if (blk_queue_stopped(q))
-                                               blk_start_queue(q);
-                                       spin_unlock(q->queue_lock);
-                               }
+                       if (q && !q->mq_ops && blk_queue_stopped(q)) {
+                               spin_lock(q->queue_lock);
+                               if (blk_queue_stopped(q))
+                                       blk_start_queue(q);
+                               spin_unlock(q->queue_lock);
                        }
                } while (entry);
        }