From 4db9832ce58dbd51fdf4c6b58d7cea445ee8195a Mon Sep 17 00:00:00 2001
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Fri, 20 Sep 2013 13:59:08 +0200
Subject: [PATCH] virtio-bus: cleanup plug/unplug interface

Right now we have these pairs:

- virtio_bus_plug_device/virtio_bus_destroy_device.  The first
  takes a VirtIODevice, the second takes a VirtioBusState

- device_plugged/device_unplug callbacks in the VirtioBusClass
  (here it's just the naming that is inconsistent)

- virtio_bus_destroy_device is not called by anyone (and since
  it calls qdev_free, it would be called by the proxies---but
  then the callback is useless since the proxies can do whatever
  they want before calling virtio_bus_destroy_device)

And there is a k->init but no k->exit, hence virtio_device_exit is
overwritten by subclasses (except virtio-9p).  This cleans it up by:

- renaming the device_unplug callback to device_unplugged

- renaming virtio_bus_plug_device to virtio_bus_device_plugged,
  matching the callback name

- renaming virtio_bus_destroy_device to virtio_bus_device_unplugged,
  removing the qdev_free, making it take a VirtIODevice and calling it
  from virtio_device_exit

- adding a k->exit callback

virtio_device_exit is still overwritten, the next patches will fix that.

Cc: qemu-stable@nongnu.org
Acked-by: Andreas Faerber <afaerber@suse.de>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 5e96f5d2f8d2696ef7d2d8d7282c18fa6023470b)
[BR: Fix and/or infrastructure for BSC#1020427 CVE-2016-9602]
Signed-off-by: Bruce Rogers <brogers@suse.com>
---
 hw/virtio-bus.c | 24 +++++++++++-------------
 hw/virtio-bus.h |  6 +++---
 hw/virtio.c     | 18 +++++++++++++++++-
 hw/virtio.h     |  1 +
 4 files changed, 32 insertions(+), 17 deletions(-)

diff --git a/hw/virtio-bus.c b/hw/virtio-bus.c
index 6045d8ad86..617c8e9514 100644
--- a/hw/virtio-bus.c
+++ b/hw/virtio-bus.c
@@ -37,8 +37,8 @@ do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0)
 #define DPRINTF(fmt, ...) do { } while (0)
 #endif
 
-/* Plug the VirtIODevice */
-int virtio_bus_plug_device(VirtIODevice *vdev)
+/* A VirtIODevice is being plugged */
+int virtio_bus_device_plugged(VirtIODevice *vdev)
 {
     DeviceState *qdev = DEVICE(vdev);
     BusState *qbus = BUS(qdev_get_parent_bus(qdev));
@@ -81,21 +81,19 @@ void virtio_bus_reset(VirtioBusState *bus)
     }
 }
 
-/* Destroy the VirtIODevice */
-void virtio_bus_destroy_device(VirtioBusState *bus)
+/* A VirtIODevice is being unplugged */
+void virtio_bus_device_unplugged(VirtIODevice *vdev)
 {
-    DeviceState *qdev;
-    BusState *qbus = BUS(bus);
-    VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
+    DeviceState *qdev = DEVICE(vdev);
+    BusState *qbus = BUS(qdev_get_parent_bus(qdev));
+    VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(qbus);
+
     DPRINTF("%s: remove device.\n", qbus->name);
 
-    if (bus->vdev != NULL) {
-        if (klass->device_unplug != NULL) {
-            klass->device_unplug(qbus->parent);
+    if (vdev != NULL) {
+        if (klass->device_unplugged != NULL) {
+            klass->device_unplugged(qbus->parent);
         }
-        qdev = DEVICE(bus->vdev);
-        qdev_free(qdev);
-        bus->vdev = NULL;
     }
 }
 
diff --git a/hw/virtio-bus.h b/hw/virtio-bus.h
index 7584a0e6ae..6cfe2b95bc 100644
--- a/hw/virtio-bus.h
+++ b/hw/virtio-bus.h
@@ -61,7 +61,7 @@ typedef struct VirtioBusClass {
      * transport independent exit function.
      * This is called by virtio-bus just before the device is unplugged.
      */
-    void (*device_unplug)(DeviceState *d);
+    void (*device_unplugged)(DeviceState *d);
 } VirtioBusClass;
 
 struct VirtioBusState {
@@ -76,9 +76,9 @@ struct VirtioBusState {
     VirtIOBindings bindings;
 };
 
-int virtio_bus_plug_device(VirtIODevice *vdev);
+int virtio_bus_device_plugged(VirtIODevice *vdev);
 void virtio_bus_reset(VirtioBusState *bus);
-void virtio_bus_destroy_device(VirtioBusState *bus);
+void virtio_bus_device_unplugged(VirtIODevice *bus);
 /* Get the device id of the plugged device. */
 uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus);
 /* Get the config_len field of the plugged device. */
diff --git a/hw/virtio.c b/hw/virtio.c
index 1f98141323..be13d63754 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -1154,7 +1154,23 @@ static int virtio_device_init(DeviceState *qdev)
     if (k->init(vdev) < 0) {
         return -1;
     }
-    virtio_bus_plug_device(vdev);
+    virtio_bus_device_plugged(vdev);
+    return 0;
+}
+
+static int virtio_device_exit(DeviceState *qdev)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
+    VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(qdev);
+
+    virtio_bus_device_unplugged(vdev);
+    if (k->exit) {
+        k->exit(vdev);
+    }
+    if (vdev->bus_name) {
+        g_free(vdev->bus_name);
+        vdev->bus_name = NULL;
+    }
     return 0;
 }
 
diff --git a/hw/virtio.h b/hw/virtio.h
index 2912bc5818..1dbdf435ab 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -165,6 +165,7 @@ typedef struct VirtioDeviceClass {
     /* This is what a VirtioDevice must implement */
     DeviceClass parent;
     int (*init)(VirtIODevice *vdev);
+    void (*exit)(VirtIODevice *vdev);
     uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features);
     uint32_t (*bad_features)(VirtIODevice *vdev);
     void (*set_features)(VirtIODevice *vdev, uint32_t val);
