[Stable-11.0.4 113/120] hw/nvme: cancel inflight requests on controller reset

Michael Tokarev <[email protected]>
Newsgroups gmane.comp.emulators.qemu.stable,gmane.comp.emulators.qemu
Message-ID <[email protected]>
From: Minwoo Im <[email protected]>

nvme_ctrl_reset() freed every SQ/CQ right after nvme_ns_drain(), which
only waits out requests on a per-namespace BlockBackend. That is safe
as long as the guest first tore down I/O queues gracefully (Delete
I/O SQ/CQ), since nvme_del_sq() already cancels and waits for
anything left on a queue before freeing it.

A reset that happens without that graceful sequence first (e.g. an
abrupt/asynchronous controller reset) can still have commands
inflight on blk_aio_*. Freeing sq/cq before those complete leaves
their completion callbacks (nvme_rw_cb() and friends) to run against
already-freed NvmeRequest/NvmeSQueue/NvmeCQueue memory via
nvme_enqueue_req_completion(), causing a use-after-free/segfault.

Run nvme_sq_cancel_inflight() over every queue in nvme_ctrl_reset()
before the free loops, so no in-flight blk_aio_* callback can fire
after sq/cq memory is freed.

Cc: [email protected]
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3398
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3883
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4068
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4072
Signed-off-by: Minwoo Im <[email protected]>
Signed-off-by: Klaus Jensen <[email protected]>
(cherry picked from commit 86f938333e2faae7c60ca66c0809f5495c59aa4b)
(Mjt: back-port across v11.0.0-2641-g9d378cab537d "hw/nvme: add basic live migration support"
 which changed by-queue loops upper bound from `n->params.max_ioqpairs + 1'
 to `n->num_queues')
Signed-off-by: Michael Tokarev <[email protected]>

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 857dc4fa1d5..b39072673d3 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -7944,6 +7944,18 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst)
         nvme_ns_drain(ns);
     }
 
+    /*
+     * Cancel and wait out every inflight command on every queue first. A
+     * reset is not required to be preceded by the guest's graceful
+     * Delete I/O SQ/CQ sequence, so sq/cq must not be freed below while a
+     * blk_aio_* completion for them could still be in flight.
+     */
+    for (i = 0; i < n->params.max_ioqpairs + 1; i++) {
+        if (n->sq[i] != NULL) {
+            nvme_sq_cancel_inflight(n->sq[i], NVME_CMD_ABORT_SQ_DEL);
+        }
+    }
+
     for (i = 0; i < n->params.max_ioqpairs + 1; i++) {
         if (n->sq[i] != NULL) {
             nvme_free_sq(n->sq[i], n);
-- 
2.47.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.