
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