On 07/14/2017 08:26 AM, Michal Privoznik wrote:
On 07/14/2017 02:01 PM, John Ferlan wrote:
>
>
> On 07/14/2017 04:26 AM, Michal Privoznik wrote:
>> On 07/13/2017 06:54 PM, John Ferlan wrote:
>>>
>>>
>>> On 07/11/2017 11:52 AM, Michal Privoznik wrote:
>>>> On 06/03/2017 03:27 PM, John Ferlan wrote:
>>>>> Since the virSecretObjListAdd technically consumes @def on success,
>>>>> the secretDefineXML should fetch the @def from the object afterwards
>>>>> and manage as an @objdef and set @def = NULL immediately.
>>>>
>>>> Really? virSecretObjListAdd sets ->def pointer in the object, but it
>>>> doesn't touch the definition otherwise. So I think a caller is safe
to
>>>> continue using the pointer.
>>>>
>>>> Michal
>>>>
>>>
>>> Let's consider the code before my change...
>>>
>>> @def is added to the @obj
>>>
>>> "Something" causes us to jump to the "restore_backup:"
label and @backup
>>> == NULL.
>>>
>>> That means virSecretObjListRemove runs and because @obj has @def it ends
>>> up calling virSecretDefFree
>>
>> The only way this can happen is when @obj has refcnt == 1. Because then
>> unref() calls dispose() which calls virSecretDefFree(). However, @obj
>> will have at least 2 references when entering restore_backup label. In
>> virSecretObjListAdd() when creating the new object via virSecretObjNew()
>> obj has refcnt = 1, and then we ref the object again. But wait a second:
>> if the object is already in both of the hash tables we return that
>> reference and don't increase the refcnt! So in the end,
>> virSecretObjListAdd() can return an object with refcnt == 1 and refcnt
>> == 2. This is obviously wrong and root cause of the problem you are
>> seeing. As I describe in the other e-mail, this breaks refcounting and
>> needs to be fixed.
Coffee is strong this morning or light has dawned on marblehead.
Let me go back to the "existing" code for a moment where @def isn't set
to NULL when we get to restore_backup and call ObjListRemove
After Add, we have R=2,L=1
After Remove, we have R=1
fall into cleanup:
Call virSecretDefFree(def) where @def != NULL, so it free's it
Call virSecretObjEndAPI(&obj) which calls Unref, setting R=0 and calling
virSecretDefFree(obj->def)... But wait, we already did that because we
allowed the DefFree(def) to free something it didn't own.
>>
>> Michal
>>
>
> Ah - I see what's happened - in my mind I'm already at the next patch
> where the else has a virObjectUnref(obj) after the ListRemove call and
> thus falling into cleanup and doing a virSecretDefFree would have been
> bad if @def was not NULL.
>
> I don't understand the "But wait a second: if the object is already in
> both of the hash tables we return that reference and don't increase the
> refcnt! "
>
> When we leave ObjListAdd - the refcnt should include 1 for New and 1 for
> the HashTable, so it should be 2. This is where I contend domainobj's
> have it wonky (or wrong) because if the Remove is called each HashRemove
> will decrement the refcnt by 1. But all the callers there "know" this
> and thus "choose" to use just Unlock at times rather than EndAPI. When
> they use EndAPI, they always will Ref the object prior which IMO causes
> too much thinking/knowledge of the consumer.
Oh, you're right. I misread the code. So the virSecretObjListAdd()
should return an object with 3 references. Two are for the two hash
tables object is in, third is for the caller to use and later free by
calling EndAPI.
Object is only in one hash table at this point in time, so the return is
ref = 2, lock = 1. One ref is for the object allocation and one ref for
the table.
IMO, when EndAPI is called, it's an indication that the caller is done
with the obj; however, because the obj is in a hash table, the other ref
remains. I've always considered EndAPI an indication that we're done
with our reference to the object whether that was from an Add or a
Lookup, both of which should return with an incremented refcnt and a
locked object. As an aside, Add should also increment for each hash
table we're in because Remove will decrement for each. That's where I
think the domainobj code is wrong.
>
> I'll go read/respond to your 8/8 reply in a moment - the caffeine is
> starting to work through the morning haze...
>
> I understand you object to the virSecretObjGetDef call as unnecessary;
I don't care that much. I just find it surprising that introducing new
variable (which I have to remember anyway when reading the code) is
considered as more readable than dereferencing directly. Moreover, the
Levensthein distance between the two is just 2 ;-)
As long as someone remain vigilant that @def is set to NULL at some
point in the code after Add but before cleanup:, then we're good. But on
the off chance that it doesn't (which I've shown above), then we're not
in a happy state.
> however, what if I use VIR_STEAL_PTR.
How?
Instead of:
if (!(obj = virSecretObjListAdd(driver->secrets, def,
driver->configDir, &backup)))
goto cleanup;
def = NULL;
objdef = virSecretObjGetDef(obj);
it'd be
if (!(obj = virSecretObjListAdd(driver->secrets, def,
driver->configDir, &backup)))
goto cleanup;
VIR_STEAL_PTR(objdef, def);
John
> In the long run it's protection
> against needing to appropriately place the def = NULL much later in this
> code because we know the object owns it, but we wanted to use it and not
> create another temporary. It protects against some future adjustment
> that doesn't account for @def isn't owned by us and jumps to cleanup
> free'ing @def when we don't own it.
>
> John
>
Michal