After updating the virBuffer APIs to protect against improper usage I have been thinking about how we might provider safer memory allocation APIs with protection against common usage errors and compile time validation of checks for failure. The standard C library malloc/realloc/calloc/free APIs have just about the worst possible design, positively encouraging serious application coding errors. There are at least 7 common problems I can think of at the moment, perhaps more.... 1. malloc() - pass incorrect number of bytes 2. malloc() - fail to check the return value 3. malloc() - use uninitialized memory 4. free() - double free by not setting pointer to NULL 5. realloc() - fail to check the return value 6. realloc() - fail to re-assign the pointer with new address 7. realloc() - accidental overwrite of original pointer with NULL on failure, leaking memory Many applications / libraries wrap these in their own functions, but typically fail to address one or more these problems. eg glib re-definitions try to address point 2 by having g_malloc() call abort() on failure, but then re-introduce the problem by adding g_try_malloc() gpointer g_malloc (gulong n_bytes) G_GNUC_MALLOC; gpointer g_try_malloc (gulong n_bytes) G_GNUC_MALLOC; Simiarly it tries address point 6, with the annotation, but this still leaves open the failure to check for NULL in point 1. gpointer g_realloc (gpointer mem, gulong n_bytes) G_GNUC_WARN_UNUSED_RESULT; gpointer g_try_realloc (gpointer mem, gulong n_bytes) G_GNUC_WARN_UNUSED_RESULT; And the g_free wrapper doesn't help with the double free issue at all: void g_free (gpointer mem); All 7 of the errors listed early could be avoided and/or checked at compile time if the APIs used a different calling convention. 1. For any given pointer the compiler knows how many bytes need to be allocated for a single instance of it. ie sizeof(*(ptr)). Since C has no data type representing a data type, this cannot be done with a simple function - it needs a (trivial) macro. 2. The __attribute__((__warn_unused_result__)) annotation causes the compiler to warn if an application doesn't do something with a return value. With the standard malloc() API this doesn't help because you always assign the return value to a pointer. The core problem here is that the API encodes the error condition as a special case of the actual data returned. This can be addressed by separating the two. The return value should simply be bi-state success or fail code and the return data passed back via an pointer to a pointer parameter. 3. The memory allocated by malloc() is not initialized to zero. For that a separate calloc() function is provided. This leaves open the possiblity of an app mistakenly using the wrong variant. Or consider an app using malloc() and explicitly initializing all struct members. Some time later a new member is added and now the developer needs to find all malloc() calls wrt to that struct and explicitly initilize the new member. It would be safer to always have malloc zero all memory, even though it has a small performance impact, the net win in safety is probably worth it. 4. Since free() takes the pointer to be freed directly it cannot reset the caller's original pointer back to NULL. This can be addressed if a pointer to the pointer is passed instead. The computational cost of the often redundant assignment to NULL is negligable given the safety benefit 5, 6 & 7. As with problem 2, these problems are all caused by the fact that the error condition is special case of the return data value. The impact of these is typically far worse because it often results in a buffer overflow and the complex rules for calling realloc() are hard to remember. So the core requirements of a safer API for allocation are: - Use return values *only* for the success/fail error condition - Annotate all the return values with __warn_unused_result__ - Pass a pointer to the pointer into all functions - Define macros around all functions to automatically do the sizeof(*(ptr)) Passing a pointer to a pointer gets a little tedious because of the need to write '&(foo)' instead of just 'foo', and is actually introduces a new problem of the caller accidentally passing merely a pointer, instead of a pointer to a pointer. This is a minor problem though because it will be identified on the very first run of the code. In addition, since we're defining macros around every function we can have the macro also convert from ptr to &(ptr) for us, avoiding this potential error: So the primary application usage would be via a set of macros: VIR_ALLOC(ptr) VIR_ALLOC_N(ptr, count) VIR_REALLOC(ptr) VIR_REALLOC_N(ptr, count) VIR_FREE(ptr) These call through to the underlying APIs: int virAlloc(void *, size_t bytes) int virAllocN(void *, size_t bytes, size_t count) int virRealloc(void *, size_t bytes) int virReallocN(void *, size_t bytes, size_t count) int virFree(void *) Note that although we're passing a pointer to a pointer into all these, the first param is still just 'void *' and not 'void **'. This is because 'void *' is defined to hold any type of pointer, and using 'void **' causes gcc to complain bitterly about strict aliasing violations. Internally the impls of these methods will safely cast to 'void **' when deferencing the pointer. All 4 of Alloc/Realloc functions will have __warn_unused_result__ annotation so the caller is forced to check the return value for failure, validated at compile time generating a warning (or fatal compile error with -Werror). So to wire up the macros to the APIs: #define VIR_ALLOC(ptr) virAlloc(&(ptr), sizeof(*(ptr))) #define VIR_ALLOC_N(ptr, count) virAllocN(&(ptr), sizeof(*(ptr)), (count)) #define VIR_REALLOC(ptr) virRealloc(&(ptr), sizeof(*(ptr))) #define VIR_REALLOC_N(ptr, count) virReallocN(&(ptr), sizeof(*(ptr)), (count)) #define VIR_FREE(ptr) virFree(&(ptr)) In particular notice how we're using the compiler to decide how many bytes to allocate for each variable here, thus guarenteeing the correct size. Finally here's an illustration of how these APis would be used in practice. - Allocating a new variable: virDomainPtr domain; if (VIR_ALLOC(domain) < 0) { __virRaiseError(VIR_ERROR_NO_MEMORY) return NULL; } Looking at the macro, this pass '&domain' into virAlloc() which initalizes it with address of memory sizeof(*domain) bytes long. - Allocating an array of domains virDomainPtr domains; int ndomains = 10; if (VIR_ALLOC_N(domains, ndomains) < 0) { __virRaiseError(VIR_ERROR_NO_MEMORY) return NULL; } - Allocating an array of domain pointers virDomainPtr *domains; int ndomains = 10; if (VIR_ALLOC_N(domains, ndomains) < 0) { __virRaiseError(VIR_ERROR_NO_MEMORY) return NULL; } - Re-allocate the array of domains to be longer ndomains = 20 if (VIR_REALLOC_N(domains, ndomains) < 0) { __virRaiseError(VIR_ERROR_NO_MEMORY) return NULL; } - Free the domain VIR_FREE(domain); The attached patch implements these ideas in a memory.h and memory.c file and updates 'hash.c' to illustrate their usage. If we were to replace every single instance of malloc/calloc/free/realloc with these calls the automated builds which run with -Werror would quickly tell us of any places where we forget to check memory allocations for failure, and we'd make code like from capabilities.c: if ((guest->arch.defaultInfo.machines = calloc(nmachines, sizeof(*guest->arch.defaultInfo.machines))) == NULL) return -1; char **migrateTrans; if ((migrateTrans = realloc(caps->host.migrateTrans, sizeof(*migrateTrans) * (caps->host.nmigrateTrans+1))) == NULL) return -1; caps->host.migrateTrans = migrateTrans; Much less ugly: if (VIR_ALLOC_N(guest->arch.defaultInfo.machines, nmachines) < 0) return -1; if (VIR_REALLOC(migrateTrans, caps->host.nmigrateTrans+1) < 0) return -1; So we've addressed all 7 common error scenarios with malloc/free/realloc/caller and produced easier to read code too. The cleaner realloc() API is particularly appealing. This does all seem a little bit too good to be true - I'm wondering why other apps/libraries don't use this style of allocation functions already ? Perhaps someone can find nasty flaws in this approach, but hopefuly not... hash.c | 113 ++++++++++++++++++++++-------------------------- memory.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ memory.h | 95 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 294 insertions(+), 60 deletions(-) Regards, Daniel. Index: memory.h =================================================================== RCS file: memory.h diff -N memory.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ memory.h 27 Apr 2008 19:04:28 -0000 @@ -0,0 +1,95 @@ +/* + * memory.c: safer memory allocation + * + * Copyright (C) 2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#ifndef __VIR_MEMORY_H_ +#define __VIR_MEMORY_H_ + +#include "internal.h" + +/* Don't call these directly - use the macros below */ +int virAlloc(void *ptrptr, size_t size) ATTRIBUTE_RETURN_CHECK; +int virAllocN(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK; +int virRealloc(void *ptrptr, size_t size) ATTRIBUTE_RETURN_CHECK; +int virReallocN(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK; +void virFree(void *ptrptr); + + +/** + * VIR_ALLOC: + * @ptr: pointer to hold address of allocated memory + * + * Allocate sizeof(*ptr) bytes of memory and store + * the address of allocated memory in 'ptr'. Fill the + * newly allocated memory with zeros. + * + * Returns -1 on failure, 0 on success + */ +#define VIR_ALLOC(ptr) virAlloc(&(ptr), sizeof(*(ptr))) + +/** + * VIR_ALLOC_N: + * @ptr: pointer to hold address of allocated memory + * @count: number of elements to allocate + * + * Allocate an array of 'count' elements, each sizeof(*ptr) + * bytes long and store the address of allocated memory in + * 'ptr'. Fill the newly allocated memory with zeros. + * + * Returns -1 on failure, 0 on success + */ +#define VIR_ALLOC_N(ptr, count) virAllocN(&(ptr), sizeof(*(ptr)), (count)) + +/** + * VIR_REALLOC: + * @ptr: pointer to hold address of allocated memory + * + * Re-allocate to sizeof(*ptr) bytes of memory and store + * the address of allocated memory in 'ptr'. Fill the + * newly allocated memory with zeros + * + * Returns -1 on failure, 0 on success + */ +#define VIR_REALLOC(ptr) virRealloc(&(ptr), sizeof(*(ptr))) + +/** + * VIR_REALLOC_N: + * @ptr: pointer to hold address of allocated memory + * @count: number of elements to allocate + * + * Re-allocate an array of 'count' elements, each sizeof(*ptr) + * bytes long and store the address of allocated memory in + * 'ptr'. Fill the newly allocated memory with zeros + * + * Returns -1 on failure, 0 on success + */ +#define VIR_REALLOC_N(ptr, count) virReallocN(&(ptr), sizeof(*(ptr)), (count)) + +/** + * VIR_FREE: + * @ptr: pointer holding address to be freed + * + * Free the memory stored in 'ptr' and update to point + * to NULL. + */ +#define VIR_FREE(ptr) virFree(&(ptr)) + +#endif /* __VIR_MEMORY_H_ */ Index: memory.c =================================================================== RCS file: memory.c diff -N memory.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ memory.c 27 Apr 2008 19:04:28 -0000 @@ -0,0 +1,146 @@ +/* + * memory.c: safer memory allocation + * + * Copyright (C) 2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdlib.h> + +#include "memory.h" + +/** + * virAlloc: + * @ptrptr: pointer to pointer for address of allocated memory + * @size: number of bytes to allocate + * + * Allocate 'size' bytes of memory. Return the address of the + * allocated memory in 'ptrptr'. The newly allocated memory is + * filled with zeros. + * + * Returns -1 on failure to allocate, zero on success + */ +int virAlloc(void *ptrptr, size_t size) +{ + if (size == 0) { + *(void **)ptrptr = NULL; + return 0; + } + + *(void **)ptrptr = calloc(1, size); + if (*(void **)ptrptr == NULL) + return -1; + return 0; +} + +/** + * virAllocN: + * @ptrptr: pointer to pointer for address of allocated memory + * @size: number of bytes to allocate + * @count: number of elements to allocate + * + * Allocate an array of memory 'count' elements long, + * each with 'size' bytes. Return the address of the + * allocated memory in 'ptrptr'. The newly allocated + * memory is filled with zeros. + * + * Returns -1 on failure to allocate, zero on success + */ +int virAllocN(void *ptrptr, size_t size, size_t count) +{ + if (size == 0 || count == 0) { + *(void **)ptrptr = NULL; + return 0; + } + + *(void**)ptrptr = calloc(count, size); + if (*(void**)ptrptr == NULL) + return -1; + return 0; +} + +/** + * virRealloc: + * @ptrptr: pointer to pointer for address of allocated memory + * @size: number of bytes to allocate + * + * Resize the block of memory in 'ptrptr' to be 'size' bytes + * in length. Update 'ptrptr' with the address of the newly + * allocated memory. On failure, 'ptrptr' is not changed and + * still points to the original memory block. The newly + * allocated memory is filled with zeros. + * + * Returns -1 on failure to allocate, zero on success + */ +int virRealloc(void *ptrptr, size_t size) +{ + void *tmp; + if (size == 0) { + free(*(void **)ptrptr); + *(void **)ptrptr = NULL; + return 0; + } + + tmp = realloc(*(void**)ptrptr, size); + if (!tmp) + return -1; + *(void**)ptrptr = tmp; + return 0; +} + +/** + * virReallocN: + * @ptrptr: pointer to pointer for address of allocated memory + * @size: number of bytes to allocate + * @count: number of elements in array + * + * Resize the block of memory in 'ptrptr' to be an array of + * 'count' elements, each 'size' bytes in length. Update 'ptrptr' + * with the address of the newly allocated memory. On failure, + * 'ptrptr' is not changed and still points to the original memory + * block. The newly allocated memory is filled with zeros. + * + * Returns -1 on failure to allocate, zero on success + */ +int virReallocN(void *ptrptr, size_t size, size_t count) +{ + void *tmp; + if (size == 0 || count == 0) { + free(*(void **)ptrptr); + *(void **)ptrptr = NULL; + return 0; + } + tmp = realloc(*(void**)ptrptr, size * count); + if (!tmp) + return -1; + *(void**)ptrptr = tmp; + return 0; +} + +/** + * virFree: + * @ptrptr: pointer to pointer for address of memory to be freed + * + * Release the chunk of memory in the pointer pointed to by + * the 'ptrptr' variable. After release, 'ptrptr' will be + * updated to point to NULL. + */ +void virFree(void *ptrptr) +{ + free(*(void**)ptrptr); + *(void**)ptrptr = NULL; +} Index: hash.c =================================================================== RCS file: /data/cvs/libvirt/src/hash.c,v retrieving revision 1.37 diff -u -p -r1.37 hash.c --- hash.c 18 Apr 2008 08:33:23 -0000 1.37 +++ hash.c 27 Apr 2008 19:04:28 -0000 @@ -25,6 +25,7 @@ #include <libxml/threads.h> #include "internal.h" #include "hash.h" +#include "memory.h" #define MAX_HASH_LEN 8 @@ -85,22 +86,22 @@ virHashComputeKey(virHashTablePtr table, virHashTablePtr virHashCreate(int size) { - virHashTablePtr table; + virHashTablePtr table = NULL; if (size <= 0) size = 256; - table = malloc(sizeof(*table)); - if (table) { - table->size = size; - table->nbElems = 0; - table->table = calloc(1, size * sizeof(*(table->table))); - if (table->table) { - return (table); - } - free(table); + if (VIR_ALLOC(table) < 0) + return NULL; + + table->size = size; + table->nbElems = 0; + if (VIR_ALLOC_N(table->table, size) < 0) { + VIR_FREE(table); + return NULL; } - return (NULL); + + return table; } /** @@ -136,8 +137,7 @@ virHashGrow(virHashTablePtr table, int s if (oldtable == NULL) return (-1); - table->table = calloc(1, size * sizeof(*(table->table))); - if (table->table == NULL) { + if (VIR_ALLOC_N(table->table, size) < 0) { table->table = oldtable; return (-1); } @@ -170,7 +170,7 @@ virHashGrow(virHashTablePtr table, int s if (table->table[key].valid == 0) { memcpy(&(table->table[key]), iter, sizeof(virHashEntry)); table->table[key].next = NULL; - free(iter); + VIR_FREE(iter); } else { iter->next = table->table[key].next; table->table[key].next = iter; @@ -184,7 +184,7 @@ virHashGrow(virHashTablePtr table, int s } } - free(oldtable); + VIR_FREE(oldtable); #ifdef DEBUG_GROW xmlGenericError(xmlGenericErrorContext, @@ -225,19 +225,19 @@ virHashFree(virHashTablePtr table, virHa next = iter->next; if ((f != NULL) && (iter->payload != NULL)) f(iter->payload, iter->name); - free(iter->name); + VIR_FREE(iter->name); iter->payload = NULL; if (!inside_table) - free(iter); + VIR_FREE(iter); nbElems--; inside_table = 0; iter = next; } inside_table = 0; } - free(table->table); + VIR_FREE(table->table); } - free(table); + VIR_FREE(table); } /** @@ -281,8 +281,7 @@ virHashAddEntry(virHashTablePtr table, c if (insert == NULL) { entry = &(table->table[key]); } else { - entry = malloc(sizeof(*entry)); - if (entry == NULL) + if (VIR_ALLOC(entry) < 0) return (-1); } @@ -354,8 +353,7 @@ virHashUpdateEntry(virHashTablePtr table if (insert == NULL) { entry = &(table->table[key]); } else { - entry = malloc(sizeof(*entry)); - if (entry == NULL) + if (VIR_ALLOC(entry) < 0) return (-1); } @@ -451,10 +449,10 @@ virHashRemoveEntry(virHashTablePtr table if ((f != NULL) && (entry->payload != NULL)) f(entry->payload, entry->name); entry->payload = NULL; - free(entry->name); + VIR_FREE(entry->name); if (prev) { prev->next = entry->next; - free(entry); + VIR_FREE(entry); } else { if (entry->next == NULL) { entry->valid = 0; @@ -462,7 +460,7 @@ virHashRemoveEntry(virHashTablePtr table entry = entry->next; memcpy(&(table->table[key]), entry, sizeof(virHashEntry)); - free(entry); + VIR_FREE(entry); } } table->nbElems--; @@ -535,11 +533,11 @@ int virHashRemoveSet(virHashTablePtr tab if (iter(entry->payload, entry->name, data)) { count++; f(entry->payload, entry->name); - free(entry->name); + VIR_FREE(entry->name); table->nbElems--; if (prev) { prev->next = entry->next; - free(entry); + VIR_FREE(entry); entry = prev; } else { if (entry->next == NULL) { @@ -549,7 +547,7 @@ int virHashRemoveSet(virHashTablePtr tab entry = entry->next; memcpy(&(table->table[i]), entry, sizeof(virHashEntry)); - free(entry); + VIR_FREE(entry); entry = &(table->table[i]); continue; } @@ -689,8 +687,7 @@ virConnectPtr virGetConnect(void) { virConnectPtr ret; - ret = calloc(1, sizeof(*ret)); - if (ret == NULL) { + if (VIR_ALLOC(ret) < 0) { virHashError(NULL, VIR_ERR_NO_MEMORY, _("allocating connection")); goto failed; } @@ -729,7 +726,7 @@ failed: virHashFree(ret->storageVols, (virHashDeallocator) virStorageVolFreeName); pthread_mutex_destroy(&ret->lock); - free(ret); + VIR_FREE(ret); } return(NULL); } @@ -759,11 +756,11 @@ virReleaseConnect(virConnectPtr conn) { if (__lastErr.conn == conn) __lastErr.conn = NULL; - free(conn->name); + VIR_FREE(conn->name); pthread_mutex_unlock(&conn->lock); pthread_mutex_destroy(&conn->lock); - free(conn); + VIR_FREE(conn); } /** @@ -824,8 +821,7 @@ __virGetDomain(virConnectPtr conn, const ret = (virDomainPtr) virHashLookup(conn->domains, name); /* TODO check the UUID */ if (ret == NULL) { - ret = (virDomainPtr) calloc(1, sizeof(*ret)); - if (ret == NULL) { + if (VIR_ALLOC(ret) < 0) { virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating domain")); goto error; } @@ -854,8 +850,8 @@ __virGetDomain(virConnectPtr conn, const error: pthread_mutex_unlock(&conn->lock); if (ret != NULL) { - free(ret->name ); - free(ret); + VIR_FREE(ret->name); + VIR_FREE(ret); } return(NULL); } @@ -888,8 +884,8 @@ virReleaseDomain(virDomainPtr domain) { __lastErr.dom = NULL; domain->magic = -1; domain->id = -1; - free(domain->name); - free(domain); + VIR_FREE(domain->name); + VIR_FREE(domain); DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs); conn->refs--; @@ -962,8 +958,7 @@ __virGetNetwork(virConnectPtr conn, cons ret = (virNetworkPtr) virHashLookup(conn->networks, name); /* TODO check the UUID */ if (ret == NULL) { - ret = (virNetworkPtr) calloc(1, sizeof(*ret)); - if (ret == NULL) { + if (VIR_ALLOC(ret) < 0) { virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating network")); goto error; } @@ -991,8 +986,8 @@ __virGetNetwork(virConnectPtr conn, cons error: pthread_mutex_unlock(&conn->lock); if (ret != NULL) { - free(ret->name ); - free(ret); + VIR_FREE(ret->name); + VIR_FREE(ret); } return(NULL); } @@ -1025,8 +1020,8 @@ virReleaseNetwork(virNetworkPtr network) __lastErr.net = NULL; network->magic = -1; - free(network->name); - free(network); + VIR_FREE(network->name); + VIR_FREE(network); DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs); conn->refs--; @@ -1100,8 +1095,7 @@ __virGetStoragePool(virConnectPtr conn, ret = (virStoragePoolPtr) virHashLookup(conn->storagePools, name); /* TODO check the UUID */ if (ret == NULL) { - ret = (virStoragePoolPtr) calloc(1, sizeof(*ret)); - if (ret == NULL) { + if (VIR_ALLOC(ret) < 0) { virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage pool")); goto error; } @@ -1129,8 +1123,8 @@ __virGetStoragePool(virConnectPtr conn, error: pthread_mutex_unlock(&conn->lock); if (ret != NULL) { - free(ret->name); - free(ret); + VIR_FREE(ret->name); + VIR_FREE(ret); } return(NULL); } @@ -1159,8 +1153,8 @@ virReleaseStoragePool(virStoragePoolPtr _("pool missing from connection hash table")); pool->magic = -1; - free(pool->name); - free(pool); + VIR_FREE(pool->name); + VIR_FREE(pool); DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs); conn->refs--; @@ -1232,8 +1226,7 @@ __virGetStorageVol(virConnectPtr conn, c ret = (virStorageVolPtr) virHashLookup(conn->storageVols, key); if (ret == NULL) { - ret = (virStorageVolPtr) calloc(1, sizeof(*ret)); - if (ret == NULL) { + if (VIR_ALLOC(ret) < 0) { virHashError(conn, VIR_ERR_NO_MEMORY, _("allocating storage vol")); goto error; } @@ -1266,9 +1259,9 @@ __virGetStorageVol(virConnectPtr conn, c error: pthread_mutex_unlock(&conn->lock); if (ret != NULL) { - free(ret->name); - free(ret->pool); - free(ret); + VIR_FREE(ret->name); + VIR_FREE(ret->pool); + VIR_FREE(ret); } return(NULL); } @@ -1297,9 +1290,9 @@ virReleaseStorageVol(virStorageVolPtr vo _("vol missing from connection hash table")); vol->magic = -1; - free(vol->name); - free(vol->pool); - free(vol); + VIR_FREE(vol->name); + VIR_FREE(vol->pool); + VIR_FREE(vol); DEBUG("unref connection %p %s %d", conn, conn->name, conn->refs); conn->refs--; -- |: Red Hat, Engineering, Boston -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list