diff --git a/exec.c b/exec.c index e785178d73b54c145a912216efeed30c15902bbe..0a6ac67c84398daa6ef2873a4c6c508d74685547 100644 --- a/exec.c +++ b/exec.c @@ -43,6 +43,7 @@ #include "qemu.h" #else /* !CONFIG_USER_ONLY */ #include "hw/hw.h" +#include "qemu/log.h" #include "exec/memory.h" #include "exec/ioport.h" #include "sysemu/dma.h" @@ -3326,6 +3327,33 @@ static bool prepare_mmio_access(MemoryRegion *mr) return release_lock; } +/** + * flatview_access_allowed + * @mr: #MemoryRegion to be accessed + * @attrs: memory transaction attributes + * @addr: address within that memory region + * @len: the number of bytes to access + * + * Check if a memory transaction is allowed. + * + * Returns: true if transaction is allowed, false if denied. + */ +static bool flatview_access_allowed(MemoryRegion *mr, MemTxAttrs attrs, + hwaddr addr, hwaddr len) +{ + if (likely(!attrs.memory)) { + return true; + } + if (memory_region_is_ram(mr)) { + return true; + } + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to non-RAM device at " + "addr 0x%" HWADDR_PRIX ", size %" HWADDR_PRIu ", " + "region '%s'\n", addr, len, memory_region_name(mr)); + return false; +} + /* Called within RCU critical section. */ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, MemTxAttrs attrs, @@ -3339,7 +3367,10 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, bool release_lock = false; for (;;) { - if (!memory_access_is_direct(mr, true)) { + if (!flatview_access_allowed(mr, attrs, addr1, l)) { + result |= MEMTX_ACCESS_ERROR; + /* Keep going. */ + } else if (!memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); l = memory_access_size(mr, l, addr1); /* XXX: could force current_cpu to NULL to avoid @@ -3384,6 +3415,9 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, l = len; mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); + if (!flatview_access_allowed(mr, attrs, addr, len)) { + return MEMTX_ACCESS_ERROR; + } result = flatview_write_continue(fv, addr, attrs, buf, len, addr1, l, mr); @@ -3402,7 +3436,10 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, bool release_lock = false; for (;;) { - if (!memory_access_is_direct(mr, false)) { + if (!flatview_access_allowed(mr, attrs, addr1, l)) { + result |= MEMTX_ACCESS_ERROR; + /* Keep going. */ + } else if (!memory_access_is_direct(mr, false)) { /* I/O case */ release_lock |= prepare_mmio_access(mr); l = memory_access_size(mr, l, addr1); @@ -3444,6 +3481,9 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, l = len; mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); + if (!flatview_access_allowed(mr, attrs, addr, len)) { + return MEMTX_ACCESS_ERROR; + } return flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr); } diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 95d56f320845445175652ca31cd2fa863fcbfe5d..83d8ea9952e3a0672de1dd993f73554667244c80 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -400,7 +400,8 @@ static void mv88w8618_eth_realize(DeviceState *dev, Error **errp) mv88w8618_eth_state *s = MV88W8618_ETH(dev); s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); } static const VMStateDescription mv88w8618_eth_vmsd = { diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index eecda528008c8d885626d598fd35195bd138ead1..2475df78836073c7b8e7c38a9196eefea2c0c38e 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -450,7 +450,8 @@ static void aw_emac_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_aw_emac_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); fifo8_create(&s->rx_fifo, RX_FIFO_SIZE); diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 52205f36becf12f046f8845a00a638b32ed233f3..5c99ce3b0fffbdcd2587db540553f4913ea39fb5 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1565,7 +1565,8 @@ static void gem_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_gem_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); } static void gem_init(Object *obj) diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index a64da76bf3811fdf44ff60e648e6af45f1882636..69a8097a68460e011a146deab402a15da3232ce4 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -881,7 +881,8 @@ static void dp8393x_realize(DeviceState *dev, Error **errp) "dp8393x-regs", 0x40 << s->it_shift); s->nic = qemu_new_nic(&net_dp83932_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); diff --git a/hw/net/e1000.c b/hw/net/e1000.c index a41b5b116de6ae75cd6e4380122c976931ff12ac..4daa7a47cd7f742862d8510d64c3985752abb948 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1719,7 +1719,8 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) macaddr); d->nic = qemu_new_nic(&net_e1000_info, &d->conf, - object_get_typename(OBJECT(d)), dev->id, d); + object_get_typename(OBJECT(d)), dev->id, + &dev->mem_reentrancy_guard, d); qemu_format_nic_info_str(qemu_get_queue(d->nic), macaddr); diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 581f7d03d5cc933f3e63a669d6003676bbc59c36..13809171000d3f9118e0a744a309fc961a6820e2 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -323,7 +323,7 @@ e1000e_init_net_peer(E1000EState *s, PCIDevice *pci_dev, uint8_t *macaddr) int i; s->nic = qemu_new_nic(&net_e1000e_info, &s->conf, - object_get_typename(OBJECT(s)), dev->id, s); + object_get_typename(OBJECT(s)), dev->id, &dev->mem_reentrancy_guard, s); s->core.max_queue_num = s->conf.peers.queues - 1; diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 6607c9142d364eb451453c35fca71ed4bf3fd3ab..ed1a7f54f095807455531418d99884bcaed94fb3 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -1863,7 +1863,9 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) nic_reset(s); s->nic = qemu_new_nic(&net_eepro100_info, &s->conf, - object_get_typename(OBJECT(pci_dev)), pci_dev->qdev.id, s); + object_get_typename(OBJECT(pci_dev)), + pci_dev->qdev.id, + &pci_dev->qdev.mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); TRACE(OTHER, logout("%s\n", qemu_get_queue(s->nic)->info_str)); diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c index 4cfbf1135a60b354278b1c356b1a40264b08c898..af7cfb78614d2dfbd7ca5bfe63675155812fd1d7 100644 --- a/hw/net/etraxfs_eth.c +++ b/hw/net/etraxfs_eth.c @@ -625,7 +625,8 @@ static void etraxfs_eth_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_etraxfs_info, &s->conf, - object_get_typename(OBJECT(s)), dev->id, s); + object_get_typename(OBJECT(s)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->phy.read = tdk_read; diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 2a8b99a2e41dbdbb9d02899a3ac42c6ca95a20ec..d59e757d3b0edb021d0faa948a2a49179620f1a8 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -386,7 +386,8 @@ static void etsec_realize(DeviceState *dev, Error **errp) eTSEC *etsec = ETSEC_COMMON(dev); etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf, - object_get_typename(OBJECT(dev)), dev->id, etsec); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, etsec); qemu_format_nic_info_str(qemu_get_queue(etsec->nic), etsec->conf.macaddr.a); diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index d2cded5e942ebac614e1bf9b371bc876aad12dee..aad090b18a12ae1c980f4d3c61cc09c15e914334 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -1018,8 +1018,8 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf, - object_get_typename(OBJECT(dev)), DEVICE(dev)->id, - s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 404154ebbf72334268e8ed7ea55834a565ca867e..18fcbb743a7dcb685ebf7e04e9e90611caffc04d 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -1313,7 +1313,7 @@ static void imx_eth_realize(DeviceState *dev, Error **errp) s->nic = qemu_new_nic(&imx_eth_net_info, &s->conf, object_get_typename(OBJECT(dev)), - DEVICE(dev)->id, s); + DEVICE(dev)->id, &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index f1a1d2351e084e048221f3b80630ebb47dffa17d..c47e36790b426ffb5388a271c6d3abdf20da9686 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -1336,7 +1336,8 @@ static void lan9118_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_lan9118_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->eeprom[0] = 0xa5; for (i = 0; i < 6; i++) { diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 78468fad6b9d52e6d99eedbcf13a15fbd300ca01..876cef0fd0a480318a4d7ed333f4d61787ee1245 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -638,7 +638,8 @@ static void mcf_fec_realize(DeviceState *dev, Error **errp) mcf_fec_state *s = MCF_FEC_NET(dev); s->nic = qemu_new_nic(&net_mcf_fec_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index c5fbd8431fd6f3d56eb3bf5943b6864191bcc07e..9f9eea7fcc88621413740d2804f4148f2d490afc 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -248,7 +248,8 @@ static void mipsnet_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq); s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 3490e54c5a2b2456690f2f82d35aafae23a27957..1b016bb53b75886dea453a57707cebd77c50268d 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -73,7 +73,8 @@ static void isa_ne2000_realizefn(DeviceState *dev, Error **errp) ne2000_reset(s); s->nic = qemu_new_nic(&net_ne2000_isa_info, &s->c, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); } diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c index cb05744f3c3d2a9604d50a7e44e0e64c4b12e1b7..844f43f409be00b721167eac5173a3deb98ac0ca 100644 --- a/hw/net/ne2000-pci.c +++ b/hw/net/ne2000-pci.c @@ -67,7 +67,8 @@ static void pci_ne2000_realize(PCIDevice *pci_dev, Error **errp) s->nic = qemu_new_nic(&net_ne2000_info, &s->c, object_get_typename(OBJECT(pci_dev)), - pci_dev->qdev.id, s); + pci_dev->qdev.id, + &pci_dev->qdev.mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->c.macaddr.a); } diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index a5abb8df46c4440ab6879f6a6e3ef54d406993fc..3fde629df5ef3d35cb56b657868a65b84e05f3f5 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -732,7 +732,8 @@ static void sysbus_open_eth_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq); s->nic = qemu_new_nic(&net_open_eth_info, &s->conf, - object_get_typename(OBJECT(s)), dev->id, s); + object_get_typename(OBJECT(s)), dev->id, + &dev->mem_reentrancy_guard, s); } static void qdev_open_eth_reset(DeviceState *dev) diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index 9e8d267536d87fe41d0865a10e4c448cdeca361d..0ba54a23bfccb6253ca7053d9e03698bd20d6112 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -1717,7 +1717,8 @@ void pcnet_common_init(DeviceState *dev, PCNetState *s, NetClientInfo *info) s->poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pcnet_poll_timer, s); qemu_macaddr_default_if_unset(&s->conf.macaddr); - s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); + s->nic = qemu_new_nic(info, &s->conf, object_get_typename(OBJECT(dev)), + dev->id, &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); /* Initialize the PROM */ diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c index 4aa7da79b81dfc8e129b189e5ef9f0666eba9990..0cd3534fd0c48634516521d1a12f4797548d7c3b 100644 --- a/hw/net/rocker/rocker_fp.c +++ b/hw/net/rocker/rocker_fp.c @@ -238,8 +238,8 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name, port->conf.bootindex = -1; port->conf.peers = *peers; - port->nic = qemu_new_nic(&fp_port_info, &port->conf, - sw_name, NULL, port); + port->nic = qemu_new_nic(&fp_port_info, &port->conf, sw_name, NULL, + &DEVICE(r)->mem_reentrancy_guard, port); qemu_format_nic_info_str(qemu_get_queue(port->nic), port->conf.macaddr.a); diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 79584fbb1789f087c1fde3739388982e1e4563bc..2ed69e500627b076eeffcbcc0bac665f65f0cc3d 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -3396,7 +3396,8 @@ static void pci_rtl8139_realize(PCIDevice *dev, Error **errp) s->eeprom.contents[9] = s->conf.macaddr.a[4] | s->conf.macaddr.a[5] << 8; s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf, - object_get_typename(OBJECT(dev)), d->id, s); + object_get_typename(OBJECT(dev)), d->id, + &d->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->cplus_txbuffer = NULL; diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 4a612eebe95b7c05c7bf974ee256c4f264a2cc91..8f7b254218e40bc2c1d20c85dd92bf9109f4f1f8 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -778,7 +778,8 @@ static void smc91c111_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_smc91c111_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); /* ??? Save/restore. */ } diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index f162d490251c571004fa0eb466e90420a9a97891..2adf6c840ed6cde5edabf77599567573f59582da 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -326,7 +326,8 @@ static void spapr_vlan_realize(SpaprVioDevice *sdev, Error **errp) memcpy(&dev->perm_mac.a, &dev->nicconf.macaddr.a, sizeof(dev->perm_mac.a)); dev->nic = qemu_new_nic(&net_spapr_vlan_info, &dev->nicconf, - object_get_typename(OBJECT(sdev)), sdev->qdev.id, dev); + object_get_typename(OBJECT(sdev)), sdev->qdev.id, + &sdev->qdev.mem_reentrancy_guard, dev); qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); dev->rxp_timer = timer_new_us(QEMU_CLOCK_VIRTUAL, spapr_vlan_flush_rx_queue, diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 2f645bfb716562dc3b1d8b093c33835ccb1be3de..89772249bc0366dd9d95537ba05fbb8261af6aff 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -489,7 +489,8 @@ static void stellaris_enet_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/sungem.c b/hw/net/sungem.c index 37b62f62b8484c366b6460e057c8eacbb3789fb4..40c8c4e6f599221cbbaace3b938d4f507026ebf1 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -1358,7 +1358,7 @@ static void sungem_realize(PCIDevice *pci_dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_sungem_info, &s->conf, object_get_typename(OBJECT(dev)), - dev->id, s); + dev->id, &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index 8b8603e6962f92074dab715c289e581049e2cdd0..79f4716c7595c027f6137353558f7c7247f3a2f7 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -890,7 +890,8 @@ static void sunhme_realize(PCIDevice *pci_dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_sunhme_info, &s->conf, - object_get_typename(OBJECT(d)), d->id, s); + object_get_typename(OBJECT(d)), d->id, + &d->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 28e9cd52ff1529163872760064cf93534067425e..b7fc688b4feb9b5aaaacca6326d00b63757150c3 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -2774,10 +2774,12 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) * Happen when virtio_net_set_netclient_name has been called. */ n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, - n->netclient_type, n->netclient_name, n); + n->netclient_type, n->netclient_name, + &dev->mem_reentrancy_guard, n); } else { n->nic = qemu_new_nic(&net_virtio_info, &n->nic_conf, - object_get_typename(OBJECT(dev)), dev->id, n); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, n); } peer_test_vnet_hdr(n); diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index ecc4f5bcf000528ecb0850ba6d270b4bb099fce9..1a6eff7d87f4c803af658ddcba15bc12ba5bce80 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2062,7 +2062,7 @@ static void vmxnet3_net_init(VMXNET3State *s) s->nic = qemu_new_nic(&net_vmxnet3_info, &s->conf, object_get_typename(OBJECT(s)), - d->id, s); + d->id, &d->mem_reentrancy_guard, s); s->peer_has_vhdr = vmxnet3_peer_has_vnet_hdr(s); s->tx_sop = true; diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index ffb3b5898d661413fac48166726111886dc17856..cf0427e3e64e91a0fecf7bce2fe7eed808c799d6 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -295,7 +295,7 @@ static int net_init(struct XenLegacyDevice *xendev) } netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf, - "xen", NULL, netdev); + "xen", NULL, &xendev->qdev.mem_reentrancy_guard, netdev); snprintf(qemu_get_queue(netdev->nic)->info_str, sizeof(qemu_get_queue(netdev->nic)->info_str), diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index f496f7ed4c6c7951278c9c58e28f2e6590e20e35..91c98029c88202e607d6c043e48b52a6d6abcc2b 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -399,7 +399,8 @@ static void xgmac_enet_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xgmac_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); s->regs[XGMAC_ADDR_HIGH(0)] = (s->conf.macaddr.a[5] << 8) | diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index feeaca680eb7219c02ca7694dea5204ded4331cd..3139f030d6bb1fca38fab0bfe8d0cbf4142fadb1 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -970,7 +970,8 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); tdk_init(&s->TEMAC.phy); diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 8f3a8f8597c2caa05c9bacdab0a04d9cce28cccd..e927e6563fee35db99854847b8c5f4e7696962ee 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -233,7 +233,8 @@ static void xilinx_ethlite_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_xilinx_ethlite_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, s); + object_get_typename(OBJECT(dev)), dev->id, + &dev->mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); } diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 88bccf2b4cdcc7c92b46dbcf871a51f7ea252042..336923be1c3b198b90fa4b4b501e4227e747695c 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -1133,15 +1133,24 @@ static void lsi_execute_script(LSIState *s) uint32_t addr, addr_high; int opcode; int insn_processed = 0; + static int reentrancy_level; + + reentrancy_level++; s->istat1 |= LSI_ISTAT1_SRUN; again: - if (++insn_processed > LSI_MAX_INSN) { - /* Some windows drivers make the device spin waiting for a memory - location to change. If we have been executed a lot of code then - assume this is the case and force an unexpected device disconnect. - This is apparently sufficient to beat the drivers into submission. - */ + /* + * Some windows drivers make the device spin waiting for a memory location + * to change. If we have executed more than LSI_MAX_INSN instructions then + * assume this is the case and force an unexpected device disconnect. This + * is apparently sufficient to beat the drivers into submission. + * + * Another issue (CVE-2023-0330) can occur if the script is programmed to + * trigger itself again and again. Avoid this problem by stopping after + * being called multiple times in a reentrant way (8 is an arbitrary value + * which should be enough for all valid use cases). + */ + if (++insn_processed > LSI_MAX_INSN || reentrancy_level > 8) { if (!(s->sien0 & LSI_SIST0_UDC)) { qemu_log_mask(LOG_GUEST_ERROR, "lsi_scsi: inf. loop with UDC masked"); @@ -1149,6 +1158,7 @@ again: lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); lsi_disconnect(s); trace_lsi_execute_script_stop(); + reentrancy_level--; return; } insn = read_dword(s, s->dsp); @@ -1595,6 +1605,8 @@ again: } } trace_lsi_execute_script_stop(); + + reentrancy_level--; } static uint8_t lsi_reg_readb(LSIState *s, int offset) diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 889069dd5a2e8c6a6494bceeef4547a51481cf10..3b695193d61b20b1ed84a531d292029881a7b793 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1359,7 +1359,8 @@ static void usb_net_realize(USBDevice *dev, Error **errrp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_usbnet_info, &s->conf, - object_get_typename(OBJECT(s)), s->dev.qdev.id, s); + object_get_typename(OBJECT(s)), s->dev.qdev.id, + &s->dev.qdev.mem_reentrancy_guard, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); snprintf(s->usbstring_mac, sizeof(s->usbstring_mac), "%02x%02x%02x%02x%02x%02x", diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index d4a3477d71d3e079968a262287e0bc7a1a6c65c5..570e73c06fa17f62ed809b3b7f1add3165d45e3b 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -35,6 +35,14 @@ typedef struct MemTxAttrs { unsigned int secure:1; /* Memory access is usermode (unprivileged) */ unsigned int user:1; + /* + * Bus interconnect and peripherals can access anything (memories, + * devices) by default. By setting the 'memory' bit, bus transaction + * are restricted to "normal" memories (per the AMBA documentation) + * versus devices. Access to devices will be logged and rejected + * (see MEMTX_ACCESS_ERROR). + */ + unsigned int memory:1; /* Requester ID (for MSI for example) */ unsigned int requester_id:16; /* @@ -64,6 +72,7 @@ typedef struct MemTxAttrs { #define MEMTX_OK 0 #define MEMTX_ERROR (1U << 0) /* device returned an error */ #define MEMTX_DECODE_ERROR (1U << 1) /* nothing at that address */ +#define MEMTX_ACCESS_ERROR (1U << 2) /* access denied */ typedef uint32_t MemTxResult; #endif diff --git a/include/exec/memory.h b/include/exec/memory.h index dca8184277d354c855c12a31d41742a578b9474a..ad260ebbd604656f21d7b0066514525dbfcee03c 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -365,6 +365,8 @@ struct MemoryRegion { bool is_iommu; RAMBlock *ram_block; Object *owner; + /* owner as TYPE_DEVICE. Used for re-entrancy checks in MR access hotpath */ + DeviceState *dev; const MemoryRegionOps *ops; void *opaque; @@ -387,6 +389,9 @@ struct MemoryRegion { const char *name; unsigned ioeventfd_nb; MemoryRegionIoeventfd *ioeventfds; + + /* For devices designed to perform re-entrant IO into their own IO MRs */ + bool disable_reentrancy_guard; }; struct IOMMUMemoryRegion { diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 136df7774ca34361a610bff273a590ff8add8984..364771f3a2cd9d8017994fc4942b21ad1a508906 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -129,6 +129,10 @@ struct NamedGPIOList { QLIST_ENTRY(NamedGPIOList) node; }; +typedef struct { + bool engaged_in_io; +} MemReentrancyGuard; + /** * DeviceState: * @realized: Indicates whether the device has been fully constructed. @@ -153,6 +157,9 @@ struct DeviceState { int num_child_bus; int instance_id_alias; int alias_required_for_version; + + /* Is the device currently in mmio/pio/dma? Used to prevent re-entrancy */ + MemReentrancyGuard mem_reentrancy_guard; }; struct DeviceListener { diff --git a/include/net/net.h b/include/net/net.h index 5609b2ecba549bbd17061f0ddb22016ebd254649..d6533febd37651e0d29594db175e2d414d785995 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -5,6 +5,7 @@ #include "qapi/qapi-types-net.h" #include "net/queue.h" #include "migration/vmstate.h" +#include "hw/qdev-core.h" #define MAC_FMT "%02X:%02X:%02X:%02X:%02X:%02X" #define MAC_ARG(x) ((uint8_t *)(x))[0], ((uint8_t *)(x))[1], \ @@ -105,6 +106,7 @@ struct NetClientState { typedef struct NICState { NetClientState *ncs; NICConf *conf; + MemReentrancyGuard *reentrancy_guard; void *opaque; bool peer_deleted; } NICState; @@ -134,6 +136,7 @@ NICState *qemu_new_nic(NetClientInfo *info, NICConf *conf, const char *model, const char *name, + MemReentrancyGuard *reentrancy_guard, void *opaque); void qemu_del_nic(NICState *nic); NetClientState *qemu_get_subqueue(NICState *nic, int queue_index); diff --git a/memory.c b/memory.c index 5d8c9a9234fbe67424ccb902a6748f06d527e669..fa7053f9cb308be4e9842b7f0bbe047e8515580d 100644 --- a/memory.c +++ b/memory.c @@ -561,6 +561,18 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, access_size_max = 4; } + /* Do not allow more than one simultaneous access to a device's IO Regions */ + if (mr->dev && !mr->disable_reentrancy_guard && + !mr->ram_device && !mr->ram && !mr->rom_device && !mr->readonly) { + if (mr->dev->mem_reentrancy_guard.engaged_in_io) { + warn_report_once("Blocked re-entrant IO on MemoryRegion: " + "%s at addr: 0x%" HWADDR_PRIX, + memory_region_name(mr), addr); + return MEMTX_ACCESS_ERROR; + } + mr->dev->mem_reentrancy_guard.engaged_in_io = true; + } + /* FIXME: support unaligned access? */ access_size = MAX(MIN(size, access_size_max), access_size_min); access_mask = MAKE_64BIT_MASK(0, access_size * 8); @@ -575,6 +587,9 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, access_mask, attrs); } } + if (mr->dev) { + mr->dev->mem_reentrancy_guard.engaged_in_io = false; + } return r; } @@ -1155,6 +1170,7 @@ static void memory_region_do_init(MemoryRegion *mr, } mr->name = g_strdup(name); mr->owner = owner; + mr->dev = (DeviceState *) object_dynamic_cast(mr->owner, TYPE_DEVICE); mr->ram_block = NULL; if (name) { diff --git a/net/net.c b/net/net.c index 3b5631879c90eb0523db4de9471f69d4781a9a97..97b20044f969ecb38272b77b23389b4b0901950a 100644 --- a/net/net.c +++ b/net/net.c @@ -276,6 +276,7 @@ NICState *qemu_new_nic(NetClientInfo *info, NICConf *conf, const char *model, const char *name, + MemReentrancyGuard *reentrancy_guard, void *opaque) { NetClientState **peers = conf->peers.ncs; @@ -288,6 +289,7 @@ NICState *qemu_new_nic(NetClientInfo *info, nic = g_malloc0(info->size + sizeof(NetClientState) * queues); nic->ncs = (void *)nic + info->size; nic->conf = conf; + nic->reentrancy_guard = reentrancy_guard, nic->opaque = opaque; for (i = 0; i < queues; i++) { @@ -732,6 +734,7 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, int iovcnt, void *opaque) { + MemReentrancyGuard *owned_reentrancy_guard; NetClientState *nc = opaque; int ret; @@ -744,12 +747,24 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, return 0; } + if (nc->info->type != NET_CLIENT_DRIVER_NIC || + qemu_get_nic(nc)->reentrancy_guard->engaged_in_io) { + owned_reentrancy_guard = NULL; + } else { + owned_reentrancy_guard = qemu_get_nic(nc)->reentrancy_guard; + owned_reentrancy_guard->engaged_in_io = true; + } + if (nc->info->receive_iov && !(flags & QEMU_NET_PACKET_FLAG_RAW)) { ret = nc->info->receive_iov(nc, iov, iovcnt); } else { ret = nc_sendv_compat(nc, iov, iovcnt, flags); } + if (owned_reentrancy_guard) { + owned_reentrancy_guard->engaged_in_io = false; + } + if (ret == 0) { nc->receive_disabled = 1; }