From: Denis V. Lunev <[email protected]> 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 <[email protected]> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3719 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4043 Cc: John Snow <[email protected]> Cc: Philippe Mathieu-Daudé <[email protected]> Signed-off-by: Denis V. Lunev <[email protected]> --- hw/ide/ahci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 995b40efd5..4c138b0c51 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -743,6 +743,12 @@ static void ahci_unmap_clb_address(AHCIDevice *ad) /* Cancel in-flight reads that would complete against a cleared cur_cmd. */ ide_cancel_dma_sync(ide_bus_active_if(&ad->port)); + /* + * Whatever survives the cancel must not be left pointing into the + * mapping this function is about to drop. + */ + ad->cur_cmd = NULL; + if (ad->lst == NULL) { trace_ahci_unmap_clb_address_null(ad->hba, ad->port_no); return; -- 2.53.0
