[libvirt] [PATCH] virnwfilterbindingobj: Introduce and use virNWFilterBindingObjStealDef

John Ferlan jferlan at redhat.com
Wed Mar 20 13:39:20 UTC 2019



On 3/20/19 9:11 AM, Michal Privoznik wrote:
> On 3/20/19 12:17 PM, John Ferlan wrote:
>>
>>
>> On 3/20/19 6:31 AM, Michal Privoznik wrote:
>>> https://bugzilla.redhat.com/show_bug.cgi?id=1686927
>>>
>>> When trying to create a nwfilter binding via
>>> nwfilterBindingCreateXML() we may encounter a crash. The sequence
>>> of functions called is as follows:
>>>
>>> 1) nwfilterBindingCreateXML() parses the XML and calls
>>> virNWFilterBindingObjListAdd() which calls
>>> virNWFilterBindingObjListAddLocked()
>>>
>>> 2) Here, @binding is not found because binding->remove is set.
>>>
>>> 3) Therefore, controls continue with creating new @binding,
>>> setting its def to the one from 1) and adding it to the hash
>>> table.
>>>
>>> 4) This fails, because the binding is still in the hash table
>>> (duplicate key is detected).
>>>
>>> 5) The control jumps to 'error' label where
>>> virNWFilterBindingObjEndAPI() is called which frees the binding
>>> definition passed.
>>>
>>> 6) Error is propagated to the caller, which calls
>>> virNWFilterBindingDefFree() over the definition again.
>>>
>>> The solution is to unset binding->def in case of failure so it's
>>> not freed in step 5).
>>>
>>> Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
>>> ---
>>>
>>> Technically, this is a v2 of:
>>>
>>> https://www.redhat.com/archives/libvir-list/2019-March/msg01209.html
>>>
>>> But since this one implements different approach than v1 I'm not marking
>>> it as such.
>>>
>>>   src/conf/virnwfilterbindingobj.c     | 10 ++++++++++
>>>   src/conf/virnwfilterbindingobj.h     |  3 +++
>>>   src/conf/virnwfilterbindingobjlist.c |  4 ++++
>>>   src/libvirt_private.syms             |  1 +
>>>   4 files changed, 18 insertions(+)
>>>
>>
>> Why wouldn't your other series take care of this in a more efficient
>> way?, e.g. patch 3/3:
>>
>> https://www.redhat.com/archives/libvir-list/2019-March/msg01321.html
>>
> 
> These are separate issues really. virHashAddEntry() can fail in more
> cases than just 'Duplicate key'. For instance, on OOM. The patches you
> reference help us prevent tickling this bug in case of 'Duplicate key'
> but not really for OOM.
> 

Ah... right... you could update the commit message above to indicate key
error or oom error (just for posterity)

>> Isn't the problem that we can get to the virNWFilterBindingObjSetDef and
>> virNWFilterBindingObjListAddObjLocked because of the same portdevname
>> key name which causes the virHashAddEntry to fail.
>>
>> If we fail after virNWFilterBindingObjListFindByPortDevLocked because
>> the other patch recognized that the @binding was in the process of being
>> removed, then we'd never create a new @obj with our new @def that had
>> the duplicated portdevname key.
> 
> Again, patch you refer to will only prevent from tickling this bug.
> 

Right - just trying to complete my thought.

>>
>>
>>> diff --git a/src/conf/virnwfilterbindingobj.c
>>> b/src/conf/virnwfilterbindingobj.c
>>> index 23978d4207..68afb9c434 100644
>>> --- a/src/conf/virnwfilterbindingobj.c
>>> +++ b/src/conf/virnwfilterbindingobj.c
>>> @@ -88,6 +88,16 @@
>>> virNWFilterBindingObjSetDef(virNWFilterBindingObjPtr obj,
>>>   }
>>>     +virNWFilterBindingDefPtr
>>> +virNWFilterBindingObjStealDef(virNWFilterBindingObjPtr obj)
>>> +{
>>> +    virNWFilterBindingDefPtr def;
>>> +
>>> +    VIR_STEAL_PTR(def, obj->def);
>>> +    return def;
>>> +}
>>> +
>>> +
>>>   bool
>>>   virNWFilterBindingObjGetRemoving(virNWFilterBindingObjPtr obj)
>>>   {
>>> diff --git a/src/conf/virnwfilterbindingobj.h
>>> b/src/conf/virnwfilterbindingobj.h
>>> index 8e5fbee35f..b26bb3c8ec 100644
>>> --- a/src/conf/virnwfilterbindingobj.h
>>> +++ b/src/conf/virnwfilterbindingobj.h
>>> @@ -39,6 +39,9 @@ void
>>>   virNWFilterBindingObjSetDef(virNWFilterBindingObjPtr obj,
>>>                               virNWFilterBindingDefPtr def);
>>>   +virNWFilterBindingDefPtr
>>> +virNWFilterBindingObjStealDef(virNWFilterBindingObjPtr obj);
>>> +
>>>   bool
>>>   virNWFilterBindingObjGetRemoving(virNWFilterBindingObjPtr obj);
>>>   diff --git a/src/conf/virnwfilterbindingobjlist.c
>>> b/src/conf/virnwfilterbindingobjlist.c
>>> index 06ccbf53af..4ee2c1b194 100644
>>> --- a/src/conf/virnwfilterbindingobjlist.c
>>> +++ b/src/conf/virnwfilterbindingobjlist.c
>>> @@ -167,6 +167,7 @@
>>> virNWFilterBindingObjListAddLocked(virNWFilterBindingObjListPtr
>>> bindings,
>>>                                      virNWFilterBindingDefPtr def)
>>>   {
>>>       virNWFilterBindingObjPtr binding;
>>> +    bool stealDef = false;
>>>         /* See if a binding with matching portdev already exists */
>>>       if ((binding = virNWFilterBindingObjListFindByPortDevLocked(
>>> @@ -181,6 +182,7 @@
>>> virNWFilterBindingObjListAddLocked(virNWFilterBindingObjListPtr
>>> bindings,
>>>           goto error;
>>>         virNWFilterBindingObjSetDef(binding, def);
>>> +    stealDef = true;
>>>         if (virNWFilterBindingObjListAddObjLocked(bindings, binding)
>>> < 0)
>>>           goto error;
>>> @@ -188,6 +190,8 @@
>>> virNWFilterBindingObjListAddLocked(virNWFilterBindingObjListPtr
>>> bindings,
>>>       return binding;
>>>      error:
>>> +    if (stealDef)
>>> +        virNWFilterBindingObjStealDef(binding);
>>
>> I think this is no different than modifying the failure path for
>> virNWFilterBindingObjListAddObjLocked to clear out @def acknowledging
>> our caller would free @def upon failure return.
>>
>>     if !AddObj() {
>>        ObjSet(binding, NULL)
>>        goto error
>>     }
> 
> As said in review for v1, virNWFilterBindingObjSetDef() calls
> virNWFilterBindingDefFree(binding->def) and only after that it sets
> binding->def to desired value. This approach won't help.
> 

Ah right - mind block. Trying to recall other places and the order of
when setting ->def occurs - for some reason I recall it always occurred
after the Hash insertion was successful, hence the subsequent comment of
changing order. That's more of a consistency type thing.

I'm fine with this approach and see Jano already R-By'd it. Didn't want
to leave you hanging with any unresolved questions/concerns.

John

>>
>> Alternatively, reorder the code to do the ObjSetDef after successful
>> return from virNWFilterBindingObjListAddLocked which would take
>> def->portdevname as a "const char *key" type parameter; however, there
>> could be some affect with virNWFilterBindingObjListLoadStatus
>> processing. In particular how virNWFilterBindingObjParseXML sets
>> obj->def...
> 
> That's an alternative approach. There are two (or more) ways to fix this
> bug. I've chosen one of them. What advantages does the other have that
> it should be done that way?
> 
>>
>> BTW: When I read the create a StealDef I was thinking more in the terms
>> of "if binding is in the process of being removed", then use the
>> StealDef to return the current @binding->def so it could be Free'd and
>> clear the removing bit. There may need to be some obj ref counting magic
>> to make sure some other thread in the process of deleting the object
>> doesn't do something bad or logic in that thread to recognize the @obj
>> is no longer being deleted. I didn't dig into the removing code and
>> there's not enough coffee in me yet to think more clearly about that.
> 
> In general, I don't think such logic is desired. Just look at my
> reproducer (1/3 from the linked series). One thread is doing 'destroy'
> the other is doing 'create'. There can't be any logic that would make
> both APIs happy, can it? One of the pair has to fail.
> 
> Michal




More information about the libvir-list mailing list