NVMe: fix race condition in nvme_submit_sync_cmd()
authorJens Axboe <axboe@fb.com>
Fri, 12 Dec 2014 15:53:40 +0000 (08:53 -0700)
committerJens Axboe <axboe@fb.com>
Fri, 12 Dec 2014 15:53:40 +0000 (08:53 -0700)
If we have a race between the schedule timing out and the command
completing, we could have the task issuing the command exit
nvme_submit_sync_cmd() while the irq is running sync_completion().
If that happens, we could be corrupting memory, since the stack
that held 'cmdinfo' is no longer valid.

Fix this by always calling nvme_abort_cmd_info(). Once that call
completes, we know that we have either run sync_completion() if
the completion came in, or that we will never run it since we now
have special_completion() as the command callback handler.

Acked-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
drivers/block/nvme-core.c

index e92bdf4c68fcd50a19d84a2aae8f3d2eed10afc0..b1d5d87973157b4c6e4a70b757519c37b3460201 100644 (file)
@@ -804,12 +804,19 @@ static int nvme_submit_sync_cmd(struct request *req, struct nvme_command *cmd,
                nvme_finish_cmd(nvmeq, req->tag, NULL);
                set_current_state(TASK_RUNNING);
        }
-       schedule_timeout(timeout);
+       ret = schedule_timeout(timeout);
 
-       if (cmdinfo.status == -EINTR) {
-               nvme_abort_cmd_info(nvmeq, blk_mq_rq_to_pdu(req));
+       /*
+        * Ensure that sync_completion has either run, or that it will
+        * never run.
+        */
+       nvme_abort_cmd_info(nvmeq, blk_mq_rq_to_pdu(req));
+
+       /*
+        * We never got the completion
+        */
+       if (cmdinfo.status == -EINTR)
                return -EINTR;
-       }
 
        if (result)
                *result = cmdinfo.result;