[libvirt] [PATCH v2] storage: Avoid memory leak on metadata fetching
Jiri Denemark
jdenemar at redhat.com
Thu Jul 14 12:23:31 UTC 2011
On Thu, Jul 14, 2011 at 12:56:26 +0200, Michal Privoznik wrote:
> Getting metadata on storage allocates a memory (path) which need to
> be freed after use otherwise it gets leaked. This means after use of
> virStorageFileGetMetadataFromFD or virStorageFileGetMetadata one
> must call virStorageFileFreeMetadata to free it. This function frees
> structure internals and structure itself.
> ---
> diff to v1:
> -Eric's review suggestions taken in
>
> src/conf/domain_conf.c | 17 ++++++++---
> src/libvirt_private.syms | 1 +
> src/storage/storage_backend_fs.c | 53 +++++++++++++++++++++----------------
> src/util/storage_file.c | 16 +++++++++++
> src/util/storage_file.h | 2 +
> 5 files changed, 61 insertions(+), 28 deletions(-)
>
> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
> index 39e59b9..c89c7c6 100644
> --- a/src/conf/domain_conf.c
> +++ b/src/conf/domain_conf.c
> @@ -11055,6 +11055,12 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk,
> int ret = -1;
> size_t depth = 0;
> char *nextpath = NULL;
> + virStorageFileMetadata *meta;
> +
> + if (VIR_ALLOC(meta) < 0) {
> + virReportOOMError();
> + return ret;
> + }
>
> if (!disk->src || disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK)
> return 0;
You leak the allocated meta here. Let's move the allocation code after this
second if statement.
> @@ -11084,7 +11090,6 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk,
> paths = virHashCreate(5, NULL);
>
> do {
> - virStorageFileMetadata meta;
> const char *path = nextpath ? nextpath : disk->src;
> int fd;
>
> @@ -11112,7 +11117,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk,
> }
> }
>
> - if (virStorageFileGetMetadataFromFD(path, fd, format, &meta) < 0) {
> + if (virStorageFileGetMetadataFromFD(path, fd, format, meta) < 0) {
> VIR_FORCE_CLOSE(fd);
> goto cleanup;
> }
> @@ -11126,16 +11131,17 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk,
> goto cleanup;
>
> depth++;
> - nextpath = meta.backingStore;
> + VIR_FREE(nextpath);
> + nextpath = meta->backingStore;
You need to set meta->backingStore = NULL here, otherwise...
>
> /* Stop iterating if we reach a non-file backing store */
> - if (nextpath && !meta.backingStoreIsFile) {
> + if (nextpath && !meta->backingStoreIsFile) {
> VIR_DEBUG("Stopping iteration on non-file backing store: %s",
> nextpath);
> break;
> }
>
> - format = meta.backingStoreFormat;
> + format = meta->backingStoreFormat;
>
> if (format == VIR_STORAGE_FILE_AUTO &&
> !allowProbing)
> @@ -11151,6 +11157,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk,
> cleanup:
> virHashFree(paths);
> VIR_FREE(nextpath);
> + virStorageFileFreeMetadata(meta);
... the memory block referenced from meta->backingStore may be freed twice
here, once in nextpath and once in meta->backingStore.
>
> return ret;
> }
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index 3237d18..e06c7fc 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -940,6 +940,7 @@ virStorageGenerateQcowPassphrase;
> # storage_file.h
> virStorageFileFormatTypeFromString;
> virStorageFileFormatTypeToString;
> +virStorageFileFreeMetadata;
> virStorageFileGetMetadata;
> virStorageFileGetMetadataFromFD;
> virStorageFileIsSharedFS;
> diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
> index b30e01e..821efb7 100644
> --- a/src/storage/storage_backend_fs.c
> +++ b/src/storage/storage_backend_fs.c
> @@ -61,8 +61,13 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
> unsigned long long *capacity,
> virStorageEncryptionPtr *encryption)
> {
> - int fd, ret;
> - virStorageFileMetadata meta;
> + int fd = -1, ret = -1;
Personally, I prefer
int fd = -1;
int ret = -1;
> + virStorageFileMetadata *meta;
> +
> + if (VIR_ALLOC(meta) < 0) {
> + virReportOOMError();
> + return ret;
> + }
>
> *backingStore = NULL;
> *backingStoreFormat = VIR_STORAGE_FILE_AUTO;
> @@ -71,36 +76,33 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
>
> if ((ret = virStorageBackendVolOpenCheckMode(target->path,
> VIR_STORAGE_VOL_FS_REFRESH_FLAGS)) < 0)
> - return ret; /* Take care to propagate ret, it is not always -1 */
> + goto error; /* Take care to propagate ret, it is not always -1 */
> fd = ret;
>
> if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd,
> allocation,
> capacity)) < 0) {
> - VIR_FORCE_CLOSE(fd);
> - return ret;
> + goto error;
> }
>
> - memset(&meta, 0, sizeof(meta));
> -
> if ((target->format = virStorageFileProbeFormatFromFD(target->path, fd)) < 0) {
> - VIR_FORCE_CLOSE(fd);
> - return -1;
> + ret = 1;
Looks like a typo here, it should be
ret = -1;
> + goto error;
> }
>
> if (virStorageFileGetMetadataFromFD(target->path, fd,
> target->format,
> - &meta) < 0) {
> - VIR_FORCE_CLOSE(fd);
> - return -1;
> + meta) < 0) {
> + ret = -1;
> + goto error;
> }
>
> VIR_FORCE_CLOSE(fd);
>
> - if (meta.backingStore) {
> - *backingStore = meta.backingStore;
> - meta.backingStore = NULL;
> - if (meta.backingStoreFormat == VIR_STORAGE_FILE_AUTO) {
> + if (meta->backingStore) {
> + *backingStore = meta->backingStore;
> + meta->backingStore = NULL;
> + if (meta->backingStoreFormat == VIR_STORAGE_FILE_AUTO) {
> if ((ret = virStorageFileProbeFormat(*backingStore)) < 0) {
> /* If the backing file is currently unavailable, only log an error,
> * but continue. Returning -1 here would disable the whole storage
> @@ -114,18 +116,17 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
> ret = 0;
> }
> } else {
> - *backingStoreFormat = meta.backingStoreFormat;
> + *backingStoreFormat = meta->backingStoreFormat;
> ret = 0;
> }
> } else {
> - VIR_FREE(meta.backingStore);
> ret = 0;
> }
>
> - if (capacity && meta.capacity)
> - *capacity = meta.capacity;
> + if (capacity && meta->capacity)
> + *capacity = meta->capacity;
>
> - if (encryption != NULL && meta.encrypted) {
> + if (encryption != NULL && meta->encrypted) {
> if (VIR_ALLOC(*encryption) < 0) {
> virReportOOMError();
> goto cleanup;
> @@ -147,11 +148,17 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
> */
> }
>
> + virStorageFileFreeMetadata(meta);
> +
> return ret;
>
> +error:
> + VIR_FORCE_CLOSE(fd);
> +
> cleanup:
> - VIR_FREE(*backingStore);
> - return -1;
> + virStorageFileFreeMetadata(meta);
> + return ret;
> +
To avoid code duplication, this could have been
...
cleanup:
virStorageFileFreeMetadata(meta);
return ret;
error:
VIR_FORCE_CLOSE(fd);
goto cleanup;
But it's just one line, so not a big deal (unless the cleanup code is
expanded, in which case it's easier to forgot to update both chunks).
...
> @@ -912,6 +916,18 @@ virStorageFileGetMetadata(const char *path,
> return ret;
> }
>
> +/**
> + * virStorageFileFreeMetadata:
> + *
> + * Free pointers in passed structure, but not memory used by
> + * structure itself.
> + */
> +void ATTRIBUTE_NONNULL(1)
> +virStorageFileFreeMetadata(virStorageFileMetadata *meta)
> +{
> + VIR_FREE(meta->backingStore);
> + VIR_FREE(meta);
> +}
We like having free-like functions to work with NULL arguments, shouldn't we
follow that habit here as well?
> diff --git a/src/util/storage_file.h b/src/util/storage_file.h
> index f1bdd02..b362796 100644
> --- a/src/util/storage_file.h
> +++ b/src/util/storage_file.h
> @@ -70,6 +70,8 @@ int virStorageFileGetMetadataFromFD(const char *path,
> int format,
> virStorageFileMetadata *meta);
>
> +void virStorageFileFreeMetadata(virStorageFileMetadata meta);
> +
On the other hand, if we insist on having ATTRIBUTE_NONNULL, we should say
that in the header file.
Jirka
More information about the libvir-list
mailing list