Blob Blame History Raw
From 0309361905e9b9ee674b740610f49ae273862939 Mon Sep 17 00:00:00 2001
Message-Id: <0309361905e9b9ee674b740610f49ae273862939.1377873641.git.jdenemar@redhat.com>
From: Peter Krempa <pkrempa@redhat.com>
Date: Thu, 29 Aug 2013 19:21:11 +0200
Subject: [PATCH] qemu: Remove hostdev entry when freeing the depending network
 entry

When using a <interface type="network"> that points to a network with
hostdev forwarding mode a hostdev alias is created for the network. This
allias is inserted into the hostdev list, but is backed with a part of
the network object that it is connected to.

When a VM is being stopped qemuProcessStop() calls
networkReleaseActualDevice() which eventually frees the memory for the
hostdev object. Afterwards when the domain definition is being freed by
virDomainDefFree() an invalid pointer is accessed by
virDomainHostdevDefFree() and may cause a crash of the daemon.

This patch removes the entry in the hostdev list before freeing the
depending memory to avoid this issue.

Resolves:   https://bugzilla.redhat.com/show_bug.cgi?id=1002669 [7.0]
            https://bugzilla.redhat.com/show_bug.cgi?id=1000973 [6.5]
(cherry picked from commit 50348e6edfa10ddb61929bf95a1c4820a9614e19)
---
 src/conf/domain_conf.c   | 26 ++++++++++++++++++--------
 src/conf/domain_conf.h   |  1 +
 src/libvirt_private.syms |  1 +
 src/qemu/qemu_hotplug.c  |  5 +++++
 src/qemu/qemu_process.c  |  2 ++
 5 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 01f9bd0..1a20232 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -9936,26 +9936,36 @@ virDomainNetFindIdx(virDomainDefPtr def, virDomainNetDefPtr net)
     return matchidx;
 }
 
-virDomainNetDefPtr
-virDomainNetRemove(virDomainDefPtr def, size_t i)
-{
-    virDomainNetDefPtr net = def->nets[i];
 
+void
+virDomainNetRemoveHostdev(virDomainDefPtr def,
+                          virDomainNetDefPtr net)
+{
     if (net->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
         /* hostdev net devices are normally also be in the hostdevs
          * array, but might have already been removed by the time we
          * get here.
          */
         virDomainHostdevDefPtr hostdev = &net->data.hostdev.def;
-        size_t h;
+        size_t i;
 
-        for (h = 0; h < def->nhostdevs; h++) {
-            if (def->hostdevs[h] == hostdev) {
-                virDomainHostdevRemove(def, h);
+        for (i = 0; i < def->nhostdevs; i++) {
+            if (def->hostdevs[i] == hostdev) {
+                virDomainHostdevRemove(def, i);
                 break;
             }
         }
     }
+}
+
+
+virDomainNetDefPtr
+virDomainNetRemove(virDomainDefPtr def, size_t i)
+{
+    virDomainNetDefPtr net = def->nets[i];
+
+    virDomainNetRemoveHostdev(def, net);
+
     if (def->nnets > 1) {
         memmove(def->nets + i,
                 def->nets + i + 1,
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 536fc96..29ef0f8 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2360,6 +2360,7 @@ int virDomainNetFindIdx(virDomainDefPtr def, virDomainNetDefPtr net);
 virDomainNetDefPtr virDomainNetFind(virDomainDefPtr def, const char *device);
 int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net);
 virDomainNetDefPtr virDomainNetRemove(virDomainDefPtr def, size_t i);
+void virDomainNetRemoveHostdev(virDomainDefPtr def, virDomainNetDefPtr net);
 
 int virDomainHostdevInsert(virDomainDefPtr def, virDomainHostdevDefPtr hostdev);
 virDomainHostdevDefPtr
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index faa67ad..e6c23e4 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -302,6 +302,7 @@ virDomainNetGetActualVirtPortProfile;
 virDomainNetGetActualVlan;
 virDomainNetInsert;
 virDomainNetRemove;
+virDomainNetRemoveHostdev;
 virDomainNetTypeToString;
 virDomainNostateReasonTypeFromString;
 virDomainNostateReasonTypeToString;
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 7a6946e..cbe7072 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -940,6 +940,8 @@ cleanup:
                                virDomainNetGetActualBridgeName(net), net->ifname));
         }
 
+        virDomainNetRemoveHostdev(vm->def, net);
+
         networkReleaseActualDevice(net);
     }
 
@@ -1978,6 +1980,9 @@ qemuDomainChangeNet(virQEMUDriverPtr driver,
         /* the changes above warrant replacing olddev with newdev in
          * the domain's nets list.
          */
+
+        /* this function doesn't work with HOSTDEV networks yet, thus
+         * no need to change the pointer in the hostdev structure */
         networkReleaseActualDevice(olddev);
         virDomainNetDefFree(olddev);
         /* move newdev into the nets list, and NULL it out from the
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 0dccac3..6a28356 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -4234,6 +4234,8 @@ void qemuProcessStop(virQEMUDriverPtr driver,
                                        virDomainNetGetActualBridgeName(net),
                                        net->ifname));
 
+        /* kick the device out of the hostdev list too */
+        virDomainNetRemoveHostdev(def, net);
         networkReleaseActualDevice(net);
     }
 
-- 
1.8.3.2