@samitouri / QOSamiQemu / commits / a2fbf1785b

hw/ide/ahci: clear cur_cmd when the command list is unmapped

ahci_unmap_clb_address() drops the CLB mapping but leaves cur_cmd pointing into it. The cancel added by commit d9f78431d8 covers the buffered reads, and ide_cancel_dma_sync() drains bus->dma->aiocb, but neither reaches IDEState::pio_aiocb: a PIO write started before the guest cleared PxCMD.ST completes afterwards and runs its second DRQ phase against the stale header. That is harmless while the CLB is direct RAM, because unmapping it changes nothing. It is a use-after-free once PxCLB points at an MMIO region, where address_space_map() hands out a bounce buffer that dma_memory_unmap() then frees. Clear cur_cmd after the cancel, so nothing reachable from a later completion still refers to the freed mapping. Reported-by: Katherine Leaver <katherine.j.leaver@gmail.com> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3719 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4043 Cc: John Snow <jsnow@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Denis V. Lunev <den@openvz.org>

Denis V. Lunev committed Aug 17, 2026 at 23:44 UTC a2fbf1785b7a464d61c0f60c4ad0a86d82925e5d
1 file changed +6
hw/ide/ahci.c
+6
@@ -743,6 +743,12 @@ static void ahci_unmap_clb_address(AHCIDevice *ad)
743 /* Cancel in-flight reads that would complete against a cleared cur_cmd. */
744 ide_cancel_dma_sync(ide_bus_active_if(&ad->port));
745
746 + /*
747 + * Whatever survives the cancel must not be left pointing into the
748 + * mapping this function is about to drop.
749 + */
750 + ad->cur_cmd = NULL;
751 +
752 if (ad->lst == NULL) {
753 trace_ahci_unmap_clb_address_null(ad->hba, ad->port_no);
754 return;