Hi,

attached is a patch for a new module 'safe-alloc'. The module originated
with libvirt[1] and was originally written by Dan Berrange. I touched it
up for inclusion into gnulib. I've found them useful for some of my
projects, and thought that putting them into gnulib would be much better
than simply copying files around.

The module provides a few macros that help avoid common mistakes and
annoyances when using 'raw' malloc and friends. A fuller explanation
from the commit message for the patch:

These macros address a number of shortcomings in the usual C library
allocation functions:
  * Return value and error indication are conflated, making compile
    time checks for proper error checking (atribute __warn_unused_result__)
    useless
  * It is easy to allocate the wrong number of bytes, even though
    the compiler knows or could at least help in allocating the right
    number
  * Use of uninitialized memory (using malloc instead of calloc)
  * Double frees
  * Incorrect handling of the pointer for realloc, leading either to
    memory leaks or referencing invalid memory

A more complete explanation of the above can be found in the post
  "safer memory allocation APIs with compile time checking"
at
  http://www.redhat.com/archives/libvir-list/2008-April/msg00372.html

Using this module still allows using malloc/calloc/realloc directly, but
it offers an alternative that makes committing the above errors at least
harder, if not impossible.

David

[1] http://libvirt.org/
>From 2b7d49a572d41ecf8da4ceb5eac35c0244315eb8 Mon Sep 17 00:00:00 2001
From: David Lutterkort <lut...@redhat.com>
Date: Thu, 12 Feb 2009 09:41:11 -0800
Subject: [PATCH] Macros to make the use of malloc/calloc/realloc safer

These macros address a number of shortcomings in the usual C library
allocation functions:
  * Return value and error indication are conflated, making compile
    time checks for proper error checking (atribute __warn_unused_result__)
    useless
  * It is easy to allocate the wrong number of bytes, even though
    the compiler knows or could at least help in allocating the right
    number
  * Use of uninitialized memory (using malloc instead of calloc)
  * Double frees
  * Incorrect handling of the pointer for realloc, leading either to
    memory leaks or referencing invalid memory

A more complete explanation of the above can be found in the post
  "safer memory allocation APIs with compile time checking"
at
  http://www.redhat.com/archives/libvir-list/2008-April/msg00372.html

Using this module still allows using malloc/calloc/realloc directly, but
it offers an alternative that makes committing the above errors at least
harder, if not impossible.

This code was originally writen by Dan Berrange (<berra...@redhat.com>) for
libvirt and slightly adapted for gnulib.
---
 MODULES.html.sh     |    1 +
 doc/gnulib.texi     |    2 +
 doc/safe-alloc.texi |   83 ++++++++++++++++++++++++++++++++++
 lib/safe-alloc.c    |  122 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/safe-alloc.h    |  113 +++++++++++++++++++++++++++++++++++++++++++++++
 m4/safe-alloc.m4    |    9 ++++
 modules/safe-alloc  |   21 +++++++++
 7 files changed, 351 insertions(+), 0 deletions(-)
 create mode 100644 doc/safe-alloc.texi
 create mode 100644 lib/safe-alloc.c
 create mode 100644 lib/safe-alloc.h
 create mode 100644 m4/safe-alloc.m4
 create mode 100644 modules/safe-alloc

diff --git a/MODULES.html.sh b/MODULES.html.sh
index 0154242..6145234 100755
--- a/MODULES.html.sh
+++ b/MODULES.html.sh
@@ -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>"
diff --git a/doc/gnulib.texi b/doc/gnulib.texi
index d9383f6..fc623b5 100644
--- a/doc/gnulib.texi
+++ b/doc/gnulib.texi
@@ -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
index 0000000..bd39890
--- /dev/null
+++ b/doc/safe-alloc.texi
@@ -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
index 0000000..640eeb7
--- /dev/null
+++ b/lib/safe-alloc.c
@@ -0,0 +1,122 @@
+/*
+ * safe-alloc.c: safer memory allocation
+ *
+ * Copyright (C) 2008, 2009 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 <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.  */
+# define xalloc_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 (zeroed)
+    {
+      *(void **) ptrptr = calloc (count, size);
+    }
+  else
+    {
+      if (xalloc_oversized (count, size))
+        {
+          errno = ENOMEM;
+          return -1;
+        }
+      *(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 (xalloc_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
index 0000000..6e42512
--- /dev/null
+++ b/lib/safe-alloc.h
@@ -0,0 +1,113 @@
+/*
+ * memory.c: safer memory allocation
+ *
+ * Copyright (C) 2008, 2009 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 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
index 0000000..c8fff08
--- /dev/null
+++ b/m4/safe-alloc.m4
@@ -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
index 0000000..bffbecc
--- /dev/null
+++ b/modules/safe-alloc
@@ -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
-- 
1.6.0.6

Reply via email to