[libvirt] [PATCH] conf: utility function to update entry in def->nets array

Laine Stump posted 1 patch 4 years, 7 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/libvirt tags/patchew/20190925165732.24807-1-laine@redhat.com
Test syntax-check passed
src/conf/domain_conf.c   | 41 ++++++++++++++++++++++++++++++++++++++++
src/conf/domain_conf.h   |  1 +
src/libvirt_private.syms |  1 +
src/qemu/qemu_driver.c   |  6 ++++--
4 files changed, 47 insertions(+), 2 deletions(-)
[libvirt] [PATCH] conf: utility function to update entry in def->nets array
Posted by Laine Stump 4 years, 7 months ago
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 <laine@redhat.com>

---

(I actually think that it was a bad idea to have this "reach over"
pointer in the hostdevs array (I can say that, since it was my idea
:-), and want to eliminate it in favor of using an iterator function
for all operations that need to do something to all hostdevs, but this
bug exists on old, maintained branches, and I wouldn't want to require
backporting anything that disruptive to a stable branch.)

Also, I was unable to reproduce the crashes described in the bug
report using current upstream code, but could witness (by adding a
breakpoint in gdb) the stale pointer when running without this patch,
and the correct pointer when running with the patch)

 src/conf/domain_conf.c   | 41 ++++++++++++++++++++++++++++++++++++++++
 src/conf/domain_conf.h   |  1 +
 src/libvirt_private.syms |  1 +
 src/qemu/qemu_driver.c   |  6 ++++--
 4 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index d638c455d0..2875598eb0 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -17109,6 +17109,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 57ce5b95dc..c91e8841ee 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -3157,6 +3157,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 39812227aa..d1534871a9 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/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 0753904472..5f63c4f51e 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;
 
-- 
2.21.0

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] conf: utility function to update entry in def->nets array
Posted by Michal Privoznik 4 years, 7 months ago
On 9/25/19 6:57 PM, Laine Stump wrote:
> 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 <laine@redhat.com>
> 
> ---
> 
> (I actually think that it was a bad idea to have this "reach over"
> pointer in the hostdevs array (I can say that, since it was my idea
> :-), and want to eliminate it in favor of using an iterator function
> for all operations that need to do something to all hostdevs, but this
> bug exists on old, maintained branches, and I wouldn't want to require
> backporting anything that disruptive to a stable branch.)
> 
> Also, I was unable to reproduce the crashes described in the bug
> report using current upstream code, but could witness (by adding a
> breakpoint in gdb) the stale pointer when running without this patch,
> and the correct pointer when running with the patch)

Agreed, refactoring the whole approach would require a significant 
amount of work and it's not worth it only to fix a crasher.

> 
>   src/conf/domain_conf.c   | 41 ++++++++++++++++++++++++++++++++++++++++
>   src/conf/domain_conf.h   |  1 +
>   src/libvirt_private.syms |  1 +
>   src/qemu/qemu_driver.c   |  6 ++++--
>   4 files changed, 47 insertions(+), 2 deletions(-)
> 

> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index 0753904472..5f63c4f51e 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;
>   
> 

The same code pattern occurrs in lxcDomainUpdateDeviceConfig() so you 
may want to fix it the same way as you're doing here. With that,

Reviewed-by: Michal Privoznik <mprivozn@redhat.com>

Michal

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] conf: utility function to update entry in def->nets array
Posted by Laine Stump 4 years, 7 months ago
On 9/26/19 3:28 AM, Michal Privoznik wrote:
> On 9/25/19 6:57 PM, Laine Stump wrote:
>
>> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
>> index 0753904472..5f63c4f51e 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;
>>
>
> The same code pattern occurrs in lxcDomainUpdateDeviceConfig() so you 
> may want to fix it the same way as you're doing here.


Sure. I try to keep changes in sync for all the hypervisors that support 
any particular operation, but sometimes I'm too focused on eliminating 
my specific problem and forget.


--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] conf: utility function to update entry in def->nets array
Posted by Laine Stump 4 years, 7 months ago
On 9/26/19 11:51 AM, Laine Stump wrote:
> On 9/26/19 3:28 AM, Michal Privoznik wrote:
>> On 9/25/19 6:57 PM, Laine Stump wrote:
>>
>>> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
>>> index 0753904472..5f63c4f51e 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;
>>>
>>
>> The same code pattern occurrs in lxcDomainUpdateDeviceConfig() so you 
>> may want to fix it the same way as you're doing here.
>
>
> Sure. I try to keep changes in sync for all the hypervisors that 
> support any particular operation, but sometimes I'm too focused on 
> eliminating my specific problem and forget.


... although to be fair, lxc doesn't support <interface type='hostdev'> 
(or <hostdev mode='subsystem' type='pci'> at all, which is what's 
implied by <interface type='hostdev'>).


Still, it doesn't harm anything to use the common function (which 
reduces to just replacing the nets entry

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list