New module 'safe-alloc'.
authorDavid Lutterkort <lutter@redhat.com>
Sat, 21 Feb 2009 10:08:37 +0000 (11:08 +0100)
committerBruno Haible <bruno@clisp.org>
Sat, 21 Feb 2009 10:08:37 +0000 (11:08 +0100)
ChangeLog
MODULES.html.sh
doc/gnulib.texi
doc/safe-alloc.texi [new file with mode: 0644]
lib/safe-alloc.c [new file with mode: 0644]
lib/safe-alloc.h [new file with mode: 0644]
m4/safe-alloc.m4 [new file with mode: 0644]
modules/safe-alloc [new file with mode: 0644]

index d54e782..da030f0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2009-02-21  David Lutterkort  <lutter@redhat.com>
+
+       New module 'safe-alloc'.
+       * lib/safe-alloc.h: New file.
+       * lib/safe-alloc.c: New file.
+       * m4/safe-alloc.m4: New file.
+       * modules/safe-alloc: New file.
+       * doc/safe-alloc.texi: New file.
+       * doc/gnulib.texi: Include it.
+       * MODULES.html.sh (Memory management functions <stdlib.h>): Add
+       safe-alloc.
+
 2009-02-18  Bruno Haible  <bruno@clisp.org>
 
        Fix link error on non-glibc systems.
index 0154242..6145234 100755 (executable)
@@ -1724,6 +1724,7 @@ func_all_modules ()
   func_module malloca
   func_module xmalloca
   func_module xmemdup0
+  func_module safe-alloc
   func_end_table
 
   element="Integer arithmetic functions <stdlib.h>"
index d9383f6..fc623b5 100644 (file)
@@ -5824,6 +5824,7 @@ This list of functions is sorted according to the header that declares them.
 * func::
 * warnings::
 * manywarnings::
+* Safe Allocation Macros::
 @end menu
 
 @node alloca
@@ -5914,6 +5915,7 @@ generated automatically.
 
 @include manywarnings.texi
 
+@include safe-alloc.texi
 
 @node GNU Free Documentation License
 @appendix GNU Free Documentation License
diff --git a/doc/safe-alloc.texi b/doc/safe-alloc.texi
new file mode 100644 (file)
index 0000000..bd39890
--- /dev/null
@@ -0,0 +1,83 @@
+@node Safe Allocation Macros
+@section Safe Allocation Macros
+
+The standard C library malloc/realloc/calloc/free APIs are prone to a
+number of common coding errors. The @code{safe-alloc} module provides
+macros that make it easier to avoid many of them. It still uses the
+standard C allocation functions behind the scenes.
+
+Some of the memory allocation mistakes that are commonly made are
+
+@itemize @bullet
+@item
+passing the incorrect number of bytes to @code{malloc}, especially
+when allocationg an array
+@item
+fail to check the return value of @code{malloc} and @code{realloc} for
+errors
+@item
+forget to fully initialize memory just allocated with @code{malloc}
+@item
+duplicate calls to @code{free} by forgetting to set the pointer
+variable to @code{NULL}
+@item
+leaking memory in calls to @code{realloc} when that call fails
+@end itemize
+
+The @code{safe-alloc} module addresses these problems in the following way:
+
+@itemize @bullet
+@item
+Define macros that wrap around the standard C allocation
+functions. That makes it possible to use the compiler's knowledge of
+the size of objects for allocation; it also allows setting pointers
+passed in as arguments when appropriate
+@item
+Use return values only for a success/fail error condition flag,
+and annotate them with GCC's @code{__warn_unused_result__}
+@item
+Use @code{calloc} in favor of @code{malloc}
+@end itemize
+
+@defmac {int} ALLOC (ptr)
+@findex ALLOC
+Allocate @code{sizeof(*ptr)} bytes of memory and store the address of
+allocated memory in @code{ptr}. Fill the newly allocated memory with
+zeros.
+
+Returns -1 on failure, 0 on success.
+@end defmac
+
+@defmac {int} ALLOC_N(ptr, count)
+@findex ALLOC_N
+Allocate an array of @code{count} elements, each @code{sizeof(*ptr)}
+bytes long and store the address of allocated memory in
+@code{ptr}. Fill the newly allocated memory with zeros.
+
+Returns -1 on failure, 0 on success.
+@end defmac
+
+@defmac {int} ALLOC_N_UNINITIALIZED(ptr, count)
+@findex ALLOC_N_UNINITIALIZED
+Allocate an array of @code{count} elements, each @code{sizeof(*ptr)}
+bytes long and store the address of allocated memory in
+@code{ptr}. The allocated memory is not initialized.
+
+Returns -1 on failure, 0 on success.
+@end defmac
+
+@defmac {int} REALLOC_N(ptr, count)
+@findex REALLOC_N
+Reallocate the memory pointedto by @code{ptr} to be big enough to hold
+at least @code{count} elements, each @code{sizeof(*ptr)} bytes long
+and store the address of allocated memory in @code{ptr}. If
+reallocation fails, the @code{ptr} is not modified.
+
+Returns -1 on failure, 0 on success.
+@end defmac
+
+@defmac {void} FREE(ptr)
+@findex FREE
+Free the memory stored in @code{ptr} and set @code{ptr} to
+@code{NULL}.
+@end defmac
diff --git a/lib/safe-alloc.c b/lib/safe-alloc.c
new file mode 100644 (file)
index 0000000..fca8754
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * safe-alloc.c: safer memory allocation
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * 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
+ *
+ */
+
+/* Written by Daniel Berrange <berrange@redhat.com>, 2008 */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <errno.h>
+
+#include "safe-alloc.h"
+
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+   to size arithmetic overflow.  S must be positive and N must be
+   nonnegative.  This is a macro, not an inline function, so that it
+   works correctly even when SIZE_MAX < N.
+
+   By gnulib convention, SIZE_MAX represents overflow in size
+   calculations, so the conservative dividend to use here is
+   SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+   However, malloc (SIZE_MAX) fails on all known hosts where
+   sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+   exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+   branch when S is known to be 1.
+
+   This is the same as xalloc_oversized from xalloc.h
+*/
+#define safe_alloc_oversized(n, s)                                      \
+  ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+
+
+/**
+ * safe_alloc_alloc_n:
+ * @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.
+ *
+ * Return -1 on failure to allocate, zero on success
+ */
+int
+safe_alloc_alloc_n (void *ptrptr, size_t size, size_t count, int zeroed)
+{
+  if (size == 0 || count == 0)
+    {
+      *(void **) ptrptr = NULL;
+      return 0;
+    }
+
+  if (safe_alloc_oversized (count, size))
+    {
+      errno = ENOMEM;
+      return -1;
+    }
+
+  if (zeroed)
+    *(void **) ptrptr = calloc (count, size);
+  else
+    *(void **) ptrptr = malloc (count * size);
+
+  if (*(void **) ptrptr == NULL)
+    return -1;
+  return 0;
+}
+
+/**
+ * safe_alloc_realloc_n:
+ * @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.
+ *
+ * Return -1 on failure to allocate, zero on success
+ */
+int
+safe_alloc_realloc_n (void *ptrptr, size_t size, size_t count)
+{
+  void *tmp;
+  if (size == 0 || count == 0)
+    {
+      free (*(void **) ptrptr);
+      *(void **) ptrptr = NULL;
+      return 0;
+    }
+  if (safe_alloc_oversized (count, size))
+    {
+      errno = ENOMEM;
+      return -1;
+    }
+  tmp = realloc (*(void **) ptrptr, size * count);
+  if (!tmp)
+    return -1;
+  *(void **) ptrptr = tmp;
+  return 0;
+}
diff --git a/lib/safe-alloc.h b/lib/safe-alloc.h
new file mode 100644 (file)
index 0000000..1b7847a
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * memory.c: safer memory allocation
+ *
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * 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
+ *
+ */
+
+/* Written by Daniel Berrange <berrange@redhat.com>, 2008 */
+
+#ifndef SAFE_ALLOC_H_
+# define SAFE_ALLOC_H_
+
+# include <stdlib.h>
+
+# ifndef ATTRIBUTE_RETURN_CHECK
+#  if __GNUC_PREREQ (3, 4)
+#   define ATTRIBUTE_RETURN_CHECK __attribute__((__warn_unused_result__))
+#  else
+#   define ATTRIBUTE_RETURN_CHECK
+#  endif
+# endif
+
+/* Don't call these directly - use the macros below */
+int
+safe_alloc_alloc_n (void *ptrptr, size_t size, size_t count, int zeroed)
+  ATTRIBUTE_RETURN_CHECK;
+
+int
+safe_alloc_realloc_n (void *ptrptr, size_t size, size_t count)
+  ATTRIBUTE_RETURN_CHECK;
+
+/**
+ * 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.
+ *
+ * Return -1 on failure to allocate, zero on success
+ */
+# define ALLOC(ptr)                                     \
+  safe_alloc_alloc_n (&(ptr), sizeof(*(ptr)), 1, 1)
+
+/**
+ * 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.
+ *
+ * Return -1 on failure, 0 on success
+ */
+# define ALLOC_N(ptr, count)                                    \
+  safe_alloc_alloc_n (&(ptr), sizeof(*(ptr)), (count), 1)
+
+/**
+ * ALLOC_N_UNINITIALIZED:
+ * @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'. Do not initialize the new memory at all.
+ *
+ * Return -1 on failure to allocate, zero on success
+ */
+# define ALLOC_N_UNINITIALIZED(ptr, count)                      \
+  safe_alloc_alloc_n (&(ptr), sizeof(*(ptr)), (count), 0)
+
+/**
+ * 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
+ *
+ * Return -1 on failure to reallocate, zero on success
+ */
+# define REALLOC_N(ptr, count)                                  \
+  safe_alloc_realloc_n (&(ptr), sizeof(*(ptr)), (count))
+
+/**
+ * FREE:
+ * @ptr: pointer holding address to be freed
+ *
+ * Free the memory stored in 'ptr' and update to point
+ * to NULL.
+ */
+# define FREE(ptr)                              \
+  do                                            \
+    {                                           \
+      free(ptr);                                \
+      (ptr) = NULL;                             \
+    }                                           \
+  while(0)
+
+#endif /* SAFE_ALLOC_H_ */
diff --git a/m4/safe-alloc.m4 b/m4/safe-alloc.m4
new file mode 100644 (file)
index 0000000..c8fff08
--- /dev/null
@@ -0,0 +1,9 @@
+# safe-alloc.m4 serial 1
+dnl Copyright (C) 2009 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+AC_DEFUN([gl_SAFE_ALLOC],
+[
+  AC_LIBOBJ([safe-alloc])
+])
diff --git a/modules/safe-alloc b/modules/safe-alloc
new file mode 100644 (file)
index 0000000..bffbecc
--- /dev/null
@@ -0,0 +1,21 @@
+Description:
+A set of macros to make calls to alloc/calloc/realloc safer.
+
+Files:
+lib/safe-alloc.h
+lib/safe-alloc.c
+m4/safe-alloc.m4
+
+configure.ac:
+gl_SAFE_ALLOC
+
+Makefile.am:
+
+Include:
+"safe-alloc.h"
+
+License:
+LGPLv2+
+
+Maintainer:
+David Lutterkort