]> xenbits.xensource.com Git - libvirt.git/commitdiff
memory: make it safer to expand arrays
authorEric Blake <eblake@redhat.com>
Fri, 13 Aug 2010 21:00:47 +0000 (15:00 -0600)
committerEric Blake <eblake@redhat.com>
Thu, 18 Nov 2010 19:11:43 +0000 (12:11 -0700)
* src/util/memory.h (VIR_REALLOC_N): Update docs.
(VIR_EXPAND_N, VIR_SHRINK_N): New macros.
(virAlloc, virAllocN, virReallocN, virAllocVar, virFree): Add some
gcc attributes.
* src/util/memory.c (virExpandN, virShrinkN): New functions.
(virReallocN): Update docs.
* src/libvirt_private.syms: Export new helpers.
* docs/hacking.html.in: Prefer newer interfaces over
VIR_REALLOC_N, since uninitialized memory can bite us.
* HACKING: Regenerate.

HACKING
docs/hacking.html.in
src/libvirt_private.syms
src/util/memory.c
src/util/memory.h

diff --git a/HACKING b/HACKING
index 17ad34479a099d91a28ac299d6a0b079b227b8bd..4a9516745a1ad5ef01027d64b79f9c185f42487f 100644 (file)
--- a/HACKING
+++ b/HACKING
@@ -280,9 +280,9 @@ Low level memory management
 Use of the malloc/free/realloc/calloc APIs is deprecated in the libvirt
 codebase, because they encourage a number of serious coding bugs and do not
 enable compile time verification of checks for NULL. Instead of these
-routines, use the macros from memory.h
+routines, use the macros from memory.h.
 
-- e.g. to allocate a single object:
+- To allocate a single object:
 
   virDomainPtr domain;
 
@@ -293,10 +293,10 @@ routines, use the macros from memory.h
 
 
 
-- e.g. to allocate an array of objects
+- To allocate an array of objects:
 
   virDomainPtr domains;
-  int ndomains = 10;
+  size_t ndomains = 10;
 
   if (VIR_ALLOC_N(domains, ndomains) < 0) {
       virReportOOMError();
@@ -305,7 +305,7 @@ routines, use the macros from memory.h
 
 
 
-- e.g. to allocate an array of object pointers
+- To allocate an array of object pointers:
 
   virDomainPtr *domains;
   int ndomains = 10;
@@ -317,18 +317,22 @@ routines, use the macros from memory.h
 
 
 
-- e.g. to re-allocate the array of domains to be longer
-
-  ndomains = 20
+- To re-allocate the array of domains to be longer:
 
-  if (VIR_REALLOC_N(domains, ndomains) < 0) {
+  if (VIR_EXPAND_N(domains, ndomains, 10) < 0) {
       virReportOOMError();
       return NULL;
   }
 
 
 
-- e.g. to free the domain
+- To trim an array of domains to have one less element:
+
+  VIR_SHRINK_N(domains, ndomains, 1);
+
+
+
+- To free the domain:
 
   VIR_FREE(domain);
 
@@ -344,7 +348,7 @@ code base to help avoiding double-closing of files or file descriptors, which
 is particulary dangerous in a multi-threaded applications. Instead of these
 APIs, use the macros from files.h
 
-- eg opening a file from a file descriptor
+- Open a file from a file descriptor:
 
   if ((file = VIR_FDOPEN(fd, "r")) == NULL) {
       virReportSystemError(errno, "%s",
@@ -355,7 +359,7 @@ APIs, use the macros from files.h
 
 
 
-- e.g. close a file descriptor
+- Close a file descriptor:
 
   if (VIR_CLOSE(fd) < 0) {
       virReportSystemError(errno, "%s", _("failed to close file"));
@@ -363,7 +367,7 @@ APIs, use the macros from files.h
 
 
 
-- eg close a file
+- Close a file:
 
   if (VIR_FCLOSE(file) < 0) {
       virReportSystemError(errno, "%s", _("failed to close file"));
@@ -371,8 +375,8 @@ APIs, use the macros from files.h
 
 
 
-- eg close a file or file descriptor in an error path, without losing the
-previous "errno" value
+- Close a file or file descriptor in an error path, without losing the previous
+"errno" value:
 
   VIR_FORCE_CLOSE(fd);
   VIR_FORCE_FCLOSE(file);
@@ -460,7 +464,7 @@ If there is a need for complex string concatenations, avoid using the usual
 sequence of malloc/strcpy/strcat/snprintf functions and make use of the
 virBuffer API described in buf.h
 
-eg typical usage is as follows:
+Typical usage is as follows:
 
   char *
   somefunction(...)
@@ -594,7 +598,7 @@ When using goto, please use one of these standard labels if it makes sense:
       error: A path only taken upon return with an error code
     cleanup: A path taken upon return with success code + optional error
   no_memory: A path only taken upon return with an OOM error code
-      retry: If needing to jump upwards (eg retry on EINTR)
+      retry: If needing to jump upwards (e.g., retry on EINTR)
 
 
 Libvirt committer guidelines
index e1b51856c74a48572b42e3b87e5f27704d7d8650..964904e3db3b4ba8585bbbd9d1dd11ca96bcecaa 100644 (file)
       Use of the malloc/free/realloc/calloc APIs is deprecated in the libvirt
       codebase, because they encourage a number of serious coding bugs and do
       not enable compile time verification of checks for NULL. Instead of these
-      routines, use the macros from memory.h
+      routines, use the macros from memory.h.
     </p>
 
     <ul>
-      <li><p>e.g. to allocate  a single object:</p>
+      <li><p>To allocate a single object:</p>
+
 <pre>
   virDomainPtr domain;
 
 </pre>
       </li>
 
-      <li><p>e.g. to allocate an array of objects</p>
+      <li><p>To allocate an array of objects:</p>
 <pre>
   virDomainPtr domains;
-  int ndomains = 10;
+  size_t ndomains = 10;
 
   if (VIR_ALLOC_N(domains, ndomains) &lt; 0) {
       virReportOOMError();
 </pre>
       </li>
 
-      <li><p>e.g. to allocate an array of object pointers</p>
+      <li><p>To allocate an array of object pointers:</p>
 <pre>
   virDomainPtr *domains;
   int ndomains = 10;
 </pre>
       </li>
 
-      <li><p>e.g. to re-allocate the array of domains to be longer</p>
+      <li><p>To re-allocate the array of domains to be longer:</p>
 <pre>
-  ndomains = 20
-
-  if (VIR_REALLOC_N(domains, ndomains) &lt; 0) {
+  if (VIR_EXPAND_N(domains, ndomains, 10) &lt; 0) {
       virReportOOMError();
       return NULL;
   }
 </pre>
       </li>
 
-      <li><p>e.g. to free the domain</p>
+      <li><p>To trim an array of domains to have one less element:</p>
+
+<pre>
+  VIR_SHRINK_N(domains, ndomains, 1);
+</pre></li>
+
+      <li><p>To free the domain:</p>
 <pre>
   VIR_FREE(domain);
 </pre>
     </p>
 
    <ul>
-      <li><p>eg opening a file from a file descriptor</p>
+      <li><p>Open a file from a file descriptor:</p>
 
 <pre>
   if ((file = VIR_FDOPEN(fd, "r")) == NULL) {
   /* fd is now invalid; only access the file using file variable */
 </pre></li>
 
-      <li><p>e.g. close a file descriptor</p>
+      <li><p>Close a file descriptor:</p>
 <pre>
   if (VIR_CLOSE(fd) &lt; 0) {
       virReportSystemError(errno, "%s", _("failed to close file"));
   }
 </pre></li>
 
-      <li><p>eg close a file</p>
+      <li><p>Close a file:</p>
 
 <pre>
   if (VIR_FCLOSE(file) &lt; 0) {
   }
 </pre></li>
 
-      <li><p>eg close a file or file descriptor in an error path, without losing
-             the previous <code>errno</code> value</p>
+      <li><p>Close a file or file descriptor in an error path, without losing
+             the previous <code>errno</code> value:</p>
 
 <pre>
   VIR_FORCE_CLOSE(fd);
       make use of the virBuffer API described in buf.h
     </p>
 
-    <p>eg typical usage is as follows:</p>
+    <p>Typical usage is as follows:</p>
 
 <pre>
   char *
       error: A path only taken upon return with an error code
     cleanup: A path taken upon return with success code + optional error
   no_memory: A path only taken upon return with an OOM error code
-      retry: If needing to jump upwards (eg retry on EINTR)
+      retry: If needing to jump upwards (e.g., retry on EINTR)
 </pre>
 
 
index 50ecebac64f2a3e3c333e9f6a8918c5938467daa..796559c597829a3945557ab47a02587f0a9bb988 100644 (file)
@@ -503,8 +503,10 @@ virLogUnlock;
 # memory.h
 virAlloc;
 virAllocN;
+virExpandN;
 virFree;
 virReallocN;
+virShrinkN;
 
 
 # network.h
index dd1216b7d4fec65cb75f3961d7a71651d2469fda..59685b30ef02e5f277a2aeeb47390468d5f46e71 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * memory.c: safer memory allocation
  *
+ * Copyright (C) 2010 Red Hat, Inc.
  * Copyright (C) 2008 Daniel P. Berrange
  *
  * This library is free software; you can redistribute it and/or
@@ -24,6 +25,7 @@
 #include <stddef.h>
 
 #include "memory.h"
+#include "ignore-value.h"
 
 
 #if TEST_OOM
@@ -141,7 +143,7 @@ int virAllocN(void *ptrptr, size_t size, size_t count)
  * '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.
+ * block. Any newly allocated memory in 'ptrptr' is uninitialized.
  *
  * Returns -1 on failure to allocate, zero on success
  */
@@ -164,6 +166,61 @@ int virReallocN(void *ptrptr, size_t size, size_t count)
     return 0;
 }
 
+/**
+ * virExpandN:
+ * @ptrptr: pointer to pointer for address of allocated memory
+ * @size: number of bytes per element
+ * @countptr: pointer to number of elements in array
+ * @add: number of elements to add
+ *
+ * Resize the block of memory in 'ptrptr' to be an array of
+ * '*countptr' + 'add' elements, each 'size' bytes in length.
+ * Update 'ptrptr' and 'countptr'  with the details of the newly
+ * allocated memory. On failure, 'ptrptr' and 'countptr' are not
+ * changed. Any newly allocated memory in 'ptrptr' is zero-filled.
+ *
+ * Returns -1 on failure to allocate, zero on success
+ */
+int virExpandN(void *ptrptr, size_t size, size_t *countptr, size_t add)
+{
+    int ret;
+
+    if (*countptr + add < *countptr) {
+        errno = ENOMEM;
+        return -1;
+    }
+    ret = virReallocN(ptrptr, size, *countptr + add);
+    if (ret == 0) {
+        memset(*(char **)ptrptr + (size * *countptr), 0, size * add);
+        *countptr += add;
+    }
+    return ret;
+}
+
+/**
+ * virShrinkN:
+ * @ptrptr: pointer to pointer for address of allocated memory
+ * @size: number of bytes per element
+ * @countptr: pointer to number of elements in array
+ * @remove: number of elements to remove
+ *
+ * Resize the block of memory in 'ptrptr' to be an array of
+ * '*countptr' - 'remove' elements, each 'size' bytes in length.
+ * Update 'ptrptr' and 'countptr'  with the details of the newly
+ * allocated memory. If 'remove' is larger than 'countptr', free
+ * the entire array.
+ */
+void virShrinkN(void *ptrptr, size_t size, size_t *countptr, size_t remove)
+{
+    if (remove < *countptr)
+        ignore_value(virReallocN(ptrptr, size, *countptr -= remove));
+    else {
+        virFree(ptrptr);
+        *countptr = 0;
+    }
+}
+
+
 /**
  * Vir_Alloc_Var:
  * @ptrptr: pointer to hold address of allocated memory
index 60e2be66f8a7be780b57d83ed0693643d3d600b0..98ac2b358209d6ff12b36ce1a7c8d1e9f641f79f 100644 (file)
 
 
 /* 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 virReallocN(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK;
+int virAlloc(void *ptrptr, size_t size) ATTRIBUTE_RETURN_CHECK
+    ATTRIBUTE_NONNULL(1);
+int virAllocN(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK
+    ATTRIBUTE_NONNULL(1);
+int virReallocN(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK
+    ATTRIBUTE_NONNULL(1);
+int virExpandN(void *ptrptr, size_t size, size_t *count, size_t add)
+    ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
+void virShrinkN(void *ptrptr, size_t size, size_t *count, size_t remove)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
 int virAllocVar(void *ptrptr,
                 size_t struct_size,
                 size_t element_size,
-                size_t count) ATTRIBUTE_RETURN_CHECK;
-void virFree(void *ptrptr);
+                size_t count) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1);
+void virFree(void *ptrptr) ATTRIBUTE_NONNULL(1);
 
 /**
  * VIR_ALLOC:
@@ -87,12 +94,44 @@ void virFree(void *ptrptr);
  *
  * 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
+ * 'ptr'. If 'ptr' grew, the added memory is uninitialized.
  *
  * Returns -1 on failure, 0 on success
  */
 # define VIR_REALLOC_N(ptr, count) virReallocN(&(ptr), sizeof(*(ptr)), (count))
 
+/**
+ * VIR_EXPAND_N:
+ * @ptr: pointer to hold address of allocated memory
+ * @count: variable tracking number of elements currently allocated
+ * @add: number of elements to add
+ *
+ * Re-allocate an array of 'count' elements, each sizeof(*ptr)
+ * bytes long, to be 'count' + 'add' elements long, then store the
+ * address of allocated memory in 'ptr' and the new size in 'count'.
+ * The new elements are filled with zero.
+ *
+ * Returns -1 on failure, 0 on success
+ */
+# define VIR_EXPAND_N(ptr, count, add) \
+    virExpandN(&(ptr), sizeof(*(ptr)), &(count), add)
+
+/**
+ * VIR_SHRINK_N:
+ * @ptr: pointer to hold address of allocated memory
+ * @count: variable tracking number of elements currently allocated
+ * @remove: number of elements to remove
+ *
+ * Re-allocate an array of 'count' elements, each sizeof(*ptr)
+ * bytes long, to be 'count' - 'remove' elements long, then store the
+ * address of allocated memory in 'ptr' and the new size in 'count'.
+ * If 'count' <= 'remove', the entire array is freed.
+ *
+ * No return value.
+ */
+# define VIR_SHRINK_N(ptr, count, remove) \
+    virShrinkN(&(ptr), sizeof(*(ptr)), &(count), remove)
+
 /*
  * VIR_ALLOC_VAR_OVERSIZED:
  * @M: size of base structure