>From 2b7d49a572d41ecf8da4ceb5eac35c0244315eb8 Mon Sep 17 00:00:00 2001 From: David Lutterkort 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 () 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 " 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 address@hidden 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 @@ address@hidden Safe Allocation Macros address@hidden 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 + address@hidden @bullet address@hidden +passing the incorrect number of bytes to @code{malloc}, especially +when allocationg an array address@hidden +fail to check the return value of @code{malloc} and @code{realloc} for +errors address@hidden +forget to fully initialize memory just allocated with @code{malloc} address@hidden +duplicate calls to @code{free} by forgetting to set the pointer +variable to @code{NULL} address@hidden +leaking memory in calls to @code{realloc} when that call fails address@hidden itemize + +The @code{safe-alloc} module addresses these problems in the following way: + address@hidden @bullet address@hidden +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 address@hidden +Use return values only for a success/fail error condition flag, +and annotate them with GCC's @code{__warn_unused_result__} address@hidden +Use @code{calloc} in favor of @code{malloc} address@hidden itemize + address@hidden {int} ALLOC (ptr) address@hidden 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. address@hidden defmac + address@hidden {int} ALLOC_N(ptr, count) address@hidden ALLOC_N +Allocate an array of @code{count} elements, each @code{sizeof(*ptr)} +bytes long and store the address of allocated memory in address@hidden Fill the newly allocated memory with zeros. + +Returns -1 on failure, 0 on success. address@hidden defmac + address@hidden {int} ALLOC_N_UNINITIALIZED(ptr, count) address@hidden ALLOC_N_UNINITIALIZED +Allocate an array of @code{count} elements, each @code{sizeof(*ptr)} +bytes long and store the address of allocated memory in address@hidden The allocated memory is not initialized. + +Returns -1 on failure, 0 on success. address@hidden defmac + address@hidden {int} REALLOC_N(ptr, count) address@hidden 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. address@hidden defmac + address@hidden {void} FREE(ptr) address@hidden FREE +Free the memory stored in @code{ptr} and set @code{ptr} to address@hidden address@hidden 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 + +#include +#include +#include + +#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 + +#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