[libvirt] [PATCH v3 0/3] More virsecretobj changes to prepare for common object

v2: https://www.redhat.com/archives/libvir-list/2017-July/msg00520.html Changes in v3: Former patch 1, dropped Former patch 2, pushed Former patch 3, adjusted: -> Use @objDef instead of @objdef -> Handle the if (backup) leak shown during review Former patch 4, split -> Patch 3 handles the virsecretobj call to virSecretObjListRemove -> Patch 4 handles the secret_driver calls to virSecretObjListRemove John Ferlan (3): secret: Properly handle @def after virSecretObjAdd in driver secret: Fix memory leak in virSecretLoad secret: Handle object list removal and deletion properly src/conf/virsecretobj.c | 14 ++++++-------- src/secret/secret_driver.c | 31 ++++++++++++++++++------------- 2 files changed, 24 insertions(+), 21 deletions(-) -- 2.9.4

Since the virSecretObjListAdd technically consumes @def on success, the secretDefineXML should set @def = NULL immediately and process the remaining calls using a new @objDef variable. We can use use VIR_STEAL_PTR since we know the Add function just stores @def in obj->def. Because we steal @def into @objDef, if we jump to restore_backup: and @backup is set, then we need to ensure the @def would be free'd properly, so we'll steal it back from @objDef. For the other condition this fixes a double free of @def if the code had jumped to @backup == NULL thus calling virSecretObjListRemove without setting @def = NULL. In this case, the subsequent call to DefFree would succeed and free @def; however, the call to EndAPI would also call DefFree because the Unref done would be the last one for the @obj meaning the obj->def would be used to call DefFree, but it's already been free'd because @def wasn't managed right within this error path. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/secret/secret_driver.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 30124b4..8defa46 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -210,6 +210,7 @@ secretDefineXML(virConnectPtr conn, { virSecretPtr ret = NULL; virSecretObjPtr obj = NULL; + virSecretDefPtr objDef; virSecretDefPtr backup = NULL; virSecretDefPtr def; virObjectEventPtr event = NULL; @@ -225,8 +226,9 @@ secretDefineXML(virConnectPtr conn, if (!(obj = virSecretObjListAdd(driver->secrets, def, driver->configDir, &backup))) goto cleanup; + VIR_STEAL_PTR(objDef, def); - if (!def->isephemeral) { + if (!objDef->isephemeral) { if (backup && backup->isephemeral) { if (virSecretObjSaveData(obj) < 0) goto restore_backup; @@ -248,28 +250,27 @@ secretDefineXML(virConnectPtr conn, /* Saved successfully - drop old values */ virSecretDefFree(backup); - event = virSecretEventLifecycleNew(def->uuid, - def->usage_type, - def->usage_id, + event = virSecretEventLifecycleNew(objDef->uuid, + objDef->usage_type, + objDef->usage_id, VIR_SECRET_EVENT_DEFINED, 0); ret = virGetSecret(conn, - def->uuid, - def->usage_type, - def->usage_id); - def = NULL; + objDef->uuid, + objDef->usage_type, + objDef->usage_id); goto cleanup; restore_backup: /* If we have a backup, then secret was defined before, so just restore - * the backup. The current def will be handled below. - * Otherwise, this is a new secret, thus remove it. - */ - if (backup) + * the backup; otherwise, this is a new secret, thus remove it. */ + if (backup) { virSecretObjSetDef(obj, backup); - else + VIR_STEAL_PTR(def, objDef); + } else { virSecretObjListRemove(driver->secrets, obj); + } cleanup: virSecretDefFree(def); -- 2.9.4

If the virSecretLoadValue fails, the code jumped to cleanup without setting @ret = obj, thus calling virSecretObjListRemove which only accounts for the object reference related to adding the object to the list during virSecretObjListAdd, but does not account for the reference to the object itself as the return of @ret would be NULL so the caller wouldn't call virSecretObjEndAPI on the object recently added thus reducing the refcnt to zero. This patch will perform the ObjListRemove in the failure path of virSecretLoadValue and Unref @obj in order to perform clean up and return @obj as NULL. The @def will be freed as part of the virObjectUnref. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/conf/virsecretobj.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/conf/virsecretobj.c b/src/conf/virsecretobj.c index 850fdaf..aa994ba 100644 --- a/src/conf/virsecretobj.c +++ b/src/conf/virsecretobj.c @@ -914,7 +914,6 @@ virSecretLoad(virSecretObjListPtr secrets, { virSecretDefPtr def = NULL; virSecretObjPtr obj = NULL; - virSecretObjPtr ret = NULL; if (!(def = virSecretDefParseFile(path))) goto cleanup; @@ -926,16 +925,15 @@ virSecretLoad(virSecretObjListPtr secrets, goto cleanup; def = NULL; - if (virSecretLoadValue(obj) < 0) - goto cleanup; - - ret = obj; - obj = NULL; + if (virSecretLoadValue(obj) < 0) { + virSecretObjListRemove(secrets, obj); + virObjectUnref(obj); + obj = NULL; + } cleanup: - virSecretObjListRemove(secrets, obj); virSecretDefFree(def); - return ret; + return obj; } -- 2.9.4

Rather than rely on virSecretObjEndAPI to make the final virObjectUnref after the call to virSecretObjListRemove, be more explicit by calling virObjectUnref and setting @obj to NULL for secretUndefine and in the error path of secretDefineXML. Calling EndAPI will end up calling Unlock on an already unlocked object which has indeteriminate results (usually an ignored error). The virSecretObjEndAPI will both Unref and Unlock the object; however, the virSecretObjListRemove would have already Unlock'd the object so calling Unlock again is incorrect. Once the virSecretObjListRemove is called all that's left is to Unref our interest since that's the corrollary to the virSecretObjListAdd which returned our ref interest plus references for each hash table in which the object resides. In math terms, after an Add there's 2 refs on the object (1 for the object and 1 for the list). After calling Remove there's just 1 ref on the object. For the Add callers, calling EndAPI removes the ref for the object and unlocks it, but since it's in a list the other 1 remains. Signed-off-by: John Ferlan <jferlan@redhat.com> --- src/secret/secret_driver.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 8defa46..d833a86 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -270,6 +270,8 @@ secretDefineXML(virConnectPtr conn, VIR_STEAL_PTR(def, objDef); } else { virSecretObjListRemove(driver->secrets, obj); + virObjectUnref(obj); + obj = NULL; } cleanup: @@ -410,6 +412,8 @@ secretUndefine(virSecretPtr secret) virSecretObjDeleteData(obj); virSecretObjListRemove(driver->secrets, obj); + virObjectUnref(obj); + obj = NULL; ret = 0; -- 2.9.4

On Tue, Jul 25, 2017 at 09:21:52AM -0400, John Ferlan wrote:
v2: https://www.redhat.com/archives/libvir-list/2017-July/msg00520.html
Changes in v3:
Former patch 1, dropped Former patch 2, pushed Former patch 3, adjusted: -> Use @objDef instead of @objdef -> Handle the if (backup) leak shown during review Former patch 4, split -> Patch 3 handles the virsecretobj call to virSecretObjListRemove -> Patch 4 handles the secret_driver calls to virSecretObjListRemove
John Ferlan (3): secret: Properly handle @def after virSecretObjAdd in driver secret: Fix memory leak in virSecretLoad secret: Handle object list removal and deletion properly
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
participants (2)
-
John Ferlan
-
Pavel Hrdina