Commit c257745248 for qemu.org
commit c257745248b02fd468619b4b1fdbbade5344191d
Author: Haotian Jiang <jianghaotian.sunday@gmail.com>
Date: Tue Jul 21 14:09:41 2026 +0800
hw/audio/intel-hda: restrict all DMA engine paths to memories
CVE-2021-3611 (commit 79fa99831d) restricted the DMA engine to memories
by setting attrs.memory=true, but only applied this to intel_hda_response.
Three other DMA engine access points still use MEMTXATTRS_UNSPECIFIED,
allowing a malicious guest to trigger DMA-to-self-MMIO reentry:
- intel_hda_xfer (line 398): called from the audio timer callback
(hda_codec_xfer -> bus->xfer), so the MemReentrancyGuard does not
fire (engaged_in_io is false outside MMIO dispatch). A guest that
points a BDL entry at the HDA controller's own MMIO BAR can write
audio samples to device registers, triggering whandler side effects
such as starting/stopping streams or injecting codec commands via
CORBWP.
- intel_hda_parse_bdl (line 478): uses pci_dma_read which hardcodes
MEMTXATTRS_UNSPECIFIED. A guest-controlled BDL base address can
point at controller MMIO, allowing the DMA engine to read device
registers as BDL descriptors.
- intel_hda_corb_run (line 333): ldl_le_pci_dma reads the CORB ring
with MEMTXATTRS_UNSPECIFIED, allowing the DMA engine to read
controller MMIO as CORB entries.
Fix all three by passing {.memory = true} explicitly, matching the
fix already applied to intel_hda_response. For intel_hda_parse_bdl,
replace pci_dma_read with pci_dma_rw to pass the controlled attrs.
Fixes: 79fa99831d ("hw/audio/intel-hda: Restrict DMA engine to memories (not MMIO devices)")
Reported-by: Haotian Jiang of Tencent Security (Yunding Lab) <jianghaotian.sunday@gmail.com>
Signed-off-by: Haotian Jiang <jianghaotian.sunday@gmail.com>
Cc: qemu-stable@nongnu.org
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Message-ID: <20260721060941.2989396-1-jianghaotian.sunday@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
index d7c2c3c2fd..3d361a4976 100644
--- a/hw/audio/intel-hda.c
+++ b/hw/audio/intel-hda.c
@@ -305,6 +305,7 @@ static int intel_hda_send_command(IntelHDAState *d, uint32_t verb)
static void intel_hda_corb_run(IntelHDAState *d)
{
+ const MemTxAttrs attrs = { .memory = true };
hwaddr addr;
uint32_t rp, verb;
@@ -330,7 +331,7 @@ static void intel_hda_corb_run(IntelHDAState *d)
rp = (d->corb_rp + 1) & 0xff;
addr = intel_hda_addr(d->corb_lbase, d->corb_ubase);
- ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, MEMTXATTRS_UNSPECIFIED);
+ ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, attrs);
d->corb_rp = rp;
dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __func__, rp, verb);
@@ -395,7 +396,7 @@ static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t res
static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
uint8_t *buf, uint32_t len)
{
- const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
+ const MemTxAttrs attrs = { .memory = true };
HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus);
IntelHDAState *d = container_of(bus, IntelHDAState, codecs);
hwaddr addr;
@@ -466,6 +467,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st)
{
+ const MemTxAttrs attrs = { .memory = true };
hwaddr addr;
uint8_t buf[16];
uint32_t i;
@@ -475,7 +477,8 @@ static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st)
g_free(st->bpl);
st->bpl = g_new(bpl, st->bentries);
for (i = 0; i < st->bentries; i++, addr += 16) {
- pci_dma_read(&d->pci, addr, buf, 16);
+ pci_dma_rw(&d->pci, addr, buf, 16,
+ DMA_DIRECTION_TO_DEVICE, attrs);
st->bpl[i].addr = le64_to_cpu(*(uint64_t *)buf);
st->bpl[i].len = le32_to_cpu(*(uint32_t *)(buf + 8));
st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12));