bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 8/8] safe-alloc: fix pointer implementation


From: Paul Eggert
Subject: [PATCH 8/8] safe-alloc: fix pointer implementation
Date: Sun, 18 Apr 2021 21:01:59 -0700

The old implementation assumed that all pointers use the same
internal representation, but the C standard doesn’t guarantee
this.  Use void * (pointer) not void ** (pointer-to-pointer) for
the internal functions’ API.  The internal functions now return
NULL if and only if they failed, and the macros translate that
into -1 or 0 to satisfy the existing API.
* doc/safe-alloc.texi (Safe Allocation Macros): Mention overflow.
* lib/safe-alloc.c: Major rewrite.  Now this simply
defines SAFE_ALLOC_INLINE and includes safe-alloc.h.
* lib/safe-alloc.h: Include stddef.h, not stdlib.h.
(SAFE_ALLOC_INLINE): New macro; use Gnulib inline function style.
(safe_alloc_realloc_n): New API, which passes and returns
the pointer, and which returns NULL if and only if failure occurs.
(safe_alloc_check): New function.
(ALLOC, ALLOC_N, ALLOC_N_UNINITIALIZED, REALLOC_N):
Redo using the new API for internal functions, and using calloc
which is good enough since it’s GNU-compatible now.
(FREE): Expand to an expression rather than merely to something
that needs a following ‘;’ to become a statement.
* modules/safe-alloc (Depends-on): Add calloc-gnu.
---
 ChangeLog           | 22 +++++++++++
 doc/safe-alloc.texi |  2 +
 lib/safe-alloc.c    | 90 +-------------------------------------------
 lib/safe-alloc.h    | 91 +++++++++++++++++++++++++--------------------
 modules/safe-alloc  |  1 +
 5 files changed, 76 insertions(+), 130 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index ede998670..2c7edd59a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
 2021-04-18  Paul Eggert  <eggert@cs.ucla.edu>
 
+       safe-alloc: fix pointer implementation
+       The old implementation assumed that all pointers use the same
+       internal representation, but the C standard doesn’t guarantee
+       this.  Use void * (pointer) not void ** (pointer-to-pointer) for
+       the internal functions’ API.  The internal functions now return
+       NULL if and only if they failed, and the macros translate that
+       into -1 or 0 to satisfy the existing API.
+       * doc/safe-alloc.texi (Safe Allocation Macros): Mention overflow.
+       * lib/safe-alloc.c: Major rewrite.  Now this simply
+       defines SAFE_ALLOC_INLINE and includes safe-alloc.h.
+       * lib/safe-alloc.h: Include stddef.h, not stdlib.h.
+       (SAFE_ALLOC_INLINE): New macro; use Gnulib inline function style.
+       (safe_alloc_realloc_n): New API, which passes and returns
+       the pointer, and which returns NULL if and only if failure occurs.
+       (safe_alloc_check): New function.
+       (ALLOC, ALLOC_N, ALLOC_N_UNINITIALIZED, REALLOC_N):
+       Redo using the new API for internal functions, and using calloc
+       which is good enough since it’s GNU-compatible now.
+       (FREE): Expand to an expression rather than merely to something
+       that needs a following ‘;’ to become a statement.
+       * modules/safe-alloc (Depends-on): Add calloc-gnu.
+
        calloc-gnu: now LGPLv2+
        * modules/calloc-gnu (License): Change from GPL to LGPLv2+.
        The old value was evidently a longstanding typo, and calloc
diff --git a/doc/safe-alloc.texi b/doc/safe-alloc.texi
index d40ec65b6..e896e2598 100644
--- a/doc/safe-alloc.texi
+++ b/doc/safe-alloc.texi
@@ -13,6 +13,8 @@ Some of the memory allocation mistakes that are commonly made 
are
 passing the incorrect number of bytes to @code{malloc}, especially
 when allocating an array,
 @item
+unchecked integer overflow when calculating array sizes,
+@item
 fail to check the return value of @code{malloc} and @code{realloc} for
 errors,
 @item
diff --git a/lib/safe-alloc.c b/lib/safe-alloc.c
index 116ac4371..df061f9ef 100644
--- a/lib/safe-alloc.c
+++ b/lib/safe-alloc.c
@@ -1,91 +1,3 @@
-/* safe-alloc.c: safer memory allocation
-
-   Copyright (C) 2009-2021 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify it
-   under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 3 of the License, or any
-   later version.
-
-   This program 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 General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
-
-/* Written by Daniel Berrange <berrange@redhat.com>, 2008 */
-
 #include <config.h>
-
-/* Specification.  */
+#define SAFE_ALLOC_INLINE _GL_EXTERN_INLINE
 #include "safe-alloc.h"
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <errno.h>
-
-
-/**
- * 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
-    *(void **) ptrptr = reallocarray (NULL, 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;
-    }
-  tmp = reallocarray (*(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
index 1c655734e..7de424f3c 100644
--- a/lib/safe-alloc.h
+++ b/lib/safe-alloc.h
@@ -15,76 +15,89 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
-/* Written by Daniel Berrange <berrange@redhat.com>, 2008 */
+/* Written by Daniel Berrange and Paul Eggert.  */
 
 #ifndef SAFE_ALLOC_H_
-# define SAFE_ALLOC_H_
+#define SAFE_ALLOC_H_
 
-# include <stdlib.h>
+#include <stdlib.h>
 
-/* Don't call these directly - use the macros below */
-int
-safe_alloc_alloc_n (void *ptrptr, size_t size, size_t count, int zeroed)
-  _GL_ATTRIBUTE_NODISCARD;
+#ifndef _GL_INLINE_HEADER_BEGIN
+   #error "Please include config.h first."
+#endif
+_GL_INLINE_HEADER_BEGIN
+#ifndef SAFE_ALLOC_INLINE
+# define SAFE_ALLOC_INLINE _GL_INLINE
+#endif
 
-int
-safe_alloc_realloc_n (void *ptrptr, size_t size, size_t count)
-  _GL_ATTRIBUTE_NODISCARD;
+/* Don't call these directly - use the macros below.  */
+SAFE_ALLOC_INLINE void *
+safe_alloc_realloc_n (void *ptr, size_t count, size_t size)
+{
+  if (count == 0 || size == 0)
+    count = size = 1;
+  return reallocarray (ptr, count, size);
+}
+SAFE_ALLOC_INLINE int _GL_ATTRIBUTE_NODISCARD
+safe_alloc_check (void *ptr)
+{
+  /* Return 0 if the allocation was successful, -1 otherwise.  */
+  return -!ptr;
+}
 
 /**
  * ALLOC:
- * @ptr: pointer to hold address of allocated memory
+ * @ptr: pointer to allocated memory
  *
- * Allocate sizeof(*ptr) bytes of memory and store
- * the address of allocated memory in 'ptr'. Fill the
+ * 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
+ * Return -1 on failure to allocate, zero on success.
  */
-# define ALLOC(ptr)                                     \
-  safe_alloc_alloc_n (&(ptr), sizeof (*(ptr)), 1, 1)
+#define ALLOC(ptr) ALLOC_N (ptr, 1)
 
 /**
  * ALLOC_N:
- * @ptr: pointer to hold address of allocated memory
+ * @ptr: pointer to allocated memory
  * @count: number of elements to allocate
  *
- * Allocate an array of 'count' elements, each sizeof(*ptr)
+ * 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'.  Fill the newly allocated memory with zeros.
  *
- * Return -1 on failure, 0 on success
+ * Return -1 on failure, 0 on success.
  */
-# define ALLOC_N(ptr, count)                                    \
-  safe_alloc_alloc_n (&(ptr), sizeof (*(ptr)), (count), 1)
+#define ALLOC_N(ptr, count) \
+  safe_alloc_check ((ptr) = calloc (count, sizeof *(ptr)))
 
 /**
  * ALLOC_N_UNINITIALIZED:
- * @ptr: pointer to hold address of allocated memory
+ * @ptr: pointer to allocated memory
  * @count: number of elements to allocate
  *
- * Allocate an array of 'count' elements, each sizeof(*ptr)
+ * 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.
+ * 'ptr'.  Do not initialize the new memory at all.
  *
- * Return -1 on failure to allocate, zero on success
+ * Return -1 on failure to allocate, zero on success.
  */
-# define ALLOC_N_UNINITIALIZED(ptr, count)                      \
-  safe_alloc_alloc_n (&(ptr), sizeof (*(ptr)), (count), 0)
+#define ALLOC_N_UNINITIALIZED(ptr, count) \
+  safe_alloc_check ((ptr) = safe_alloc_realloc_n (NULL, count, sizeof *(ptr)))
 
 /**
  * REALLOC_N:
- * @ptr: pointer to hold address of allocated memory
+ * @ptr: pointer to allocated memory
  * @count: number of elements to allocate
  *
- * Re-allocate an array of 'count' elements, each sizeof(*ptr)
+ * 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'.  Fill the newly allocated memory with zeros.
  *
- * Return -1 on failure to reallocate, zero on success
+ * Return -1 on failure to reallocate, zero on success.
  */
-# define REALLOC_N(ptr, count)                                  \
-  safe_alloc_realloc_n (&(ptr), sizeof (*(ptr)), (count))
+#define REALLOC_N(ptr, count) \
+  safe_alloc_check ((ptr) = safe_alloc_realloc_n (ptr, count, sizeof *(ptr)))
 
 /**
  * FREE:
@@ -93,12 +106,8 @@ safe_alloc_realloc_n (void *ptrptr, size_t size, size_t 
count)
  * Free the memory stored in 'ptr' and update to point
  * to NULL.
  */
-# define FREE(ptr)                              \
-  do                                            \
-    {                                           \
-      free (ptr);                               \
-      (ptr) = NULL;                             \
-    }                                           \
-  while (0)
+#define FREE(ptr) ((void) (free (ptr), (ptr) = NULL))
+
+_GL_INLINE_HEADER_END
 
 #endif /* SAFE_ALLOC_H_ */
diff --git a/modules/safe-alloc b/modules/safe-alloc
index 9453b49ee..180aa8a2d 100644
--- a/modules/safe-alloc
+++ b/modules/safe-alloc
@@ -7,6 +7,7 @@ lib/safe-alloc.c
 m4/safe-alloc.m4
 
 Depends-on:
+calloc-gnu
 reallocarray
 
 configure.ac:
-- 
2.27.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]