From 7e490cdad6364c82b326d5d9251826c757e8f77b Mon Sep 17 00:00:00 2001 From: Laine Stump Date: Wed, 25 Sep 2019 12:42:51 -0400 Subject: [PATCH] conf: utility function to update entry in def->nets array A virDomainNetDef object in a domain's nets array might contain a virDomainHostdevDef, and when this is the case, the domain's hostdevs array will also have a pointer to this embedded hostdev (this is done so that internal functions that need to perform some operation on all hostdevs won't leave out the type='hostdev' network interfaces). When a network device was updated with virDomainUpdateDeviceFlags(), we were replacing the entry in the nets array (and free'ing the original) but forgetting about the pointer in the hostdevs array (which would then point to the now-free'd hostdev contained in the old net object.) This often resulted in a libvirtd crash. The solution is to add a function, virDomainNetUpdate(), called by qemuDomainUpdateDeviceConfig(), that updates the hostdevs array appropriately along with the nets array. Resolves: https://bugzilla.redhat.com/1558934 Signed-off-by: Laine Stump Reviewed-by: Michal Privoznik --- src/conf/domain_conf.c | 41 ++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 1 + src/libvirt_private.syms | 1 + src/lxc/lxc_driver.c | 6 ++++-- src/qemu/qemu_driver.c | 6 ++++-- 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c290baf953..7d83a3d1c6 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -17169,6 +17169,47 @@ virDomainNetRemove(virDomainDefPtr def, size_t i) return net; } + +int +virDomainNetUpdate(virDomainDefPtr def, + size_t netidx, + virDomainNetDefPtr newnet) +{ + size_t hostdevidx; + virDomainNetDefPtr oldnet = def->nets[netidx]; + virDomainHostdevDefPtr oldhostdev = virDomainNetGetActualHostdev(oldnet); + virDomainHostdevDefPtr newhostdev = virDomainNetGetActualHostdev(newnet); + + /* + * if newnet or oldnet has a valid hostdev*, we need to update the + * hostdevs list + */ + if (oldhostdev) { + for (hostdevidx = 0; hostdevidx < def->nhostdevs; hostdevidx++) { + if (def->hostdevs[hostdevidx] == oldhostdev) + break; + } + } + + if (oldhostdev && hostdevidx < def->nhostdevs) { + if (newhostdev) { + /* update existing entry in def->hostdevs */ + def->hostdevs[hostdevidx] = newhostdev; + } else { + /* delete oldhostdev from def->hostdevs */ + virDomainHostdevRemove(def, hostdevidx); + } + } else if (newhostdev) { + /* add newhostdev to end of def->hostdevs */ + if (VIR_APPEND_ELEMENT(def->hostdevs, def->nhostdevs, newhostdev) < 0) + return -1; + } + + def->nets[netidx] = newnet; + return 0; +} + + int virDomainControllerInsert(virDomainDefPtr def, virDomainControllerDefPtr controller) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 53bdee22fb..2884af49d8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3174,6 +3174,7 @@ virDomainNetDefPtr virDomainNetFind(virDomainDefPtr def, const char *device); virDomainNetDefPtr virDomainNetFindByName(virDomainDefPtr def, const char *ifname); bool virDomainHasNet(virDomainDefPtr def, virDomainNetDefPtr net); int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net); +int virDomainNetUpdate(virDomainDefPtr def, size_t netidx, virDomainNetDefPtr newnet); virDomainNetDefPtr virDomainNetRemove(virDomainDefPtr def, size_t i); void virDomainNetRemoveHostdev(virDomainDefPtr def, virDomainNetDefPtr net); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 287e63bffa..2b10a1030e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -508,6 +508,7 @@ virDomainNetSetModelString; virDomainNetTypeFromString; virDomainNetTypeSharesHostView; virDomainNetTypeToString; +virDomainNetUpdate; virDomainNostateReasonTypeFromString; virDomainNostateReasonTypeToString; virDomainObjAssignDef; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 5453f49064..cf1dd1428e 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -3486,8 +3486,10 @@ lxcDomainUpdateDeviceConfig(virDomainDefPtr vmdef, false) < 0) return -1; - virDomainNetDefFree(vmdef->nets[idx]); - vmdef->nets[idx] = net; + if (virDomainNetUpdate(vmdef, idx, net) < 0) + return -1; + + virDomainNetDefFree(oldDev.data.net); dev->data.net = NULL; ret = 0; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0c65414a1a..286f5bd984 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -8786,8 +8786,10 @@ qemuDomainUpdateDeviceConfig(virDomainDefPtr vmdef, false) < 0) return -1; - virDomainNetDefFree(vmdef->nets[pos]); - vmdef->nets[pos] = net; + if (virDomainNetUpdate(vmdef, pos, net)) + return -1; + + virDomainNetDefFree(oldDev.data.net); dev->data.net = NULL; break;