vio(4): poll device status after issuing device reset.
authordv <dv@openbsd.org>
Mon, 15 Jan 2024 02:35:23 +0000 (02:35 +0000)
committerdv <dv@openbsd.org>
Mon, 15 Jan 2024 02:35:23 +0000 (02:35 +0000)
The virtio spec says a driver "should" wait for a device to report
a clear device status after performing a reset. In some hypervisors,
this doesn't matter as the vcpu's io instruction emulation and
virtio network device emulation happen serially in the same thread.
In hypervisors like vmd(8), device reset happens asynchronously and
the driver can't assume the device is ready.

This race condition results in mbuf pool corruption, causing panics.

Bug reported and reproduced by bluhm@. Root cause found and diff
from sf@. ok dv@ and committed on sf@'s behalf with his permission.

sys/dev/fdt/virtio_mmio.c
sys/dev/pci/virtio_pci.c

index 4f1e9eb..4d92508 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: virtio_mmio.c,v 1.11 2023/05/29 08:13:35 sf Exp $     */
+/*     $OpenBSD: virtio_mmio.c,v 1.12 2024/01/15 02:35:23 dv Exp $     */
 /*     $NetBSD: virtio.c,v 1.3 2011/11/02 23:05:52 njoly Exp $ */
 
 /*
@@ -200,11 +200,19 @@ virtio_mmio_set_status(struct virtio_softc *vsc, int status)
        struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
        int old = 0;
 
-       if (status != 0)
+       if (status == 0) {
+               bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_STATUS,
+                   0);
+               while (bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+                   VIRTIO_MMIO_STATUS) != 0) {
+                       CPU_BUSY_CYCLE();
+               }
+       } else  {
                old = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
-                                      VIRTIO_MMIO_STATUS);
-       bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_STATUS,
-                         status|old);
+                   VIRTIO_MMIO_STATUS);
+               bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_STATUS,
+                   status|old);
+       }
 }
 
 int
index 398dc96..36a62c1 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: virtio_pci.c,v 1.35 2023/07/07 10:23:39 patrick Exp $ */
+/*     $OpenBSD: virtio_pci.c,v 1.36 2024/01/15 02:35:23 dv Exp $      */
 /*     $NetBSD: virtio.c,v 1.3 2011/11/02 23:05:52 njoly Exp $ */
 
 /*
@@ -282,15 +282,29 @@ virtio_pci_set_status(struct virtio_softc *vsc, int status)
        int old = 0;
 
        if (sc->sc_sc.sc_version_1) {
-               if (status != 0)
+               if (status == 0) {
+                       CWRITE(sc, device_status, 0);
+                       while (CREAD(sc, device_status) != 0) {
+                               CPU_BUSY_CYCLE();
+                       }
+               } else {
                        old = CREAD(sc, device_status);
-               CWRITE(sc, device_status, status|old);
+                       CWRITE(sc, device_status, status|old);
+               }
        } else {
-               if (status != 0)
+               if (status == 0) {
+                       bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+                           VIRTIO_CONFIG_DEVICE_STATUS, status|old);
+                       while (bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+                           VIRTIO_CONFIG_DEVICE_STATUS) != 0) {
+                               CPU_BUSY_CYCLE();
+                       }
+               } else {
                        old = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
                            VIRTIO_CONFIG_DEVICE_STATUS);
-               bus_space_write_1(sc->sc_iot, sc->sc_ioh,
-                   VIRTIO_CONFIG_DEVICE_STATUS, status|old);
+                       bus_space_write_1(sc->sc_iot, sc->sc_ioh,
+                           VIRTIO_CONFIG_DEVICE_STATUS, status|old);
+               }
        }
 }