@samitouri / QOSamiQemu / commits / 86f938333e

hw/nvme: cancel inflight requests on controller reset

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: qemu-stable@nongnu.org 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 <minwoo.im@samsung.com> Signed-off-by: Klaus Jensen <k.jensen@samsung.com>

Minwoo Im committed Jul 29, 2026 at 19:34 UTC 86f938333e2faae7c60ca66c0809f5495c59aa4b
1 file changed +12
hw/nvme/ctrl.c
+12
@@ -8040,6 +8040,18 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst)
8040 nvme_ns_drain(ns);
8041 }
8042
8043 + /*
8044 + * Cancel and wait out every inflight command on every queue first. A
8045 + * reset is not required to be preceded by the guest's graceful
8046 + * Delete I/O SQ/CQ sequence, so sq/cq must not be freed below while a
8047 + * blk_aio_* completion for them could still be in flight.
8048 + */
8049 + for (i = 0; i < n->num_queues; i++) {
8050 + if (n->sq[i] != NULL) {
8051 + nvme_sq_cancel_inflight(n->sq[i], NVME_CMD_ABORT_SQ_DEL);
8052 + }
8053 + }
8054 +
8055 for (i = 0; i < n->num_queues; i++) {
8056 if (n->sq[i] != NULL) {
8057 nvme_free_sq(n->sq[i], n);