util/ralloc: Add helpers for growing zero-initialized memory
authorJason Ekstrand <jason@jlekstrand.net>
Thu, 9 May 2019 23:52:44 +0000 (18:52 -0500)
committerJason Ekstrand <jason@jlekstrand.net>
Tue, 14 May 2019 17:30:22 +0000 (12:30 -0500)
Unfortunately, we can't quite follow the standard C conventions for
these because ralloc doesn't know the sizes of pointers.

Reviewed-by: Eric Anholt <eric@anholt.net>
src/util/ralloc.c
src/util/ralloc.h

index fc35661996d77010620c1f7fa29593387d9ab6e2..0d20223d98f8a9d8c128913267b46ea503fb8244 100644 (file)
@@ -197,6 +197,21 @@ reralloc_size(const void *ctx, void *ptr, size_t size)
    return resize(ptr, size);
 }
 
+void *
+rerzalloc_size(const void *ctx, void *ptr, size_t old_size, size_t new_size)
+{
+   if (unlikely(ptr == NULL))
+      return rzalloc_size(ctx, new_size);
+
+   assert(ralloc_parent(ptr) == ctx);
+   ptr = resize(ptr, new_size);
+
+   if (new_size > old_size)
+      memset((char *)ptr + old_size, 0, new_size - old_size);
+
+   return ptr;
+}
+
 void *
 ralloc_array_size(const void *ctx, size_t size, unsigned count)
 {
@@ -224,6 +239,16 @@ reralloc_array_size(const void *ctx, void *ptr, size_t size, unsigned count)
    return reralloc_size(ctx, ptr, size * count);
 }
 
+void *
+rerzalloc_array_size(const void *ctx, void *ptr, size_t size,
+                     unsigned old_count, unsigned new_count)
+{
+   if (new_count > SIZE_MAX/size)
+      return NULL;
+
+   return rerzalloc_size(ctx, ptr, size * old_count, size * new_count);
+}
+
 void
 ralloc_free(void *ptr)
 {
index b4f372dfe9b00948ebf98caba30d573376e5836d..857ca5f797f06fec121857ef4722686cffa3ae98 100644 (file)
@@ -121,6 +121,23 @@ void *rzalloc_size(const void *ctx, size_t size) MALLOCLIKE;
  */
 void *reralloc_size(const void *ctx, void *ptr, size_t size);
 
+/**
+ * Resize a ralloc-managed array, preserving data and initializing any newly
+ * allocated data to zero.
+ *
+ * Similar to \c realloc.  Unlike C89, passing 0 for \p size does not free the
+ * memory.  Instead, it resizes it to a 0-byte ralloc context, just like
+ * calling ralloc_size(ctx, 0).  This is different from talloc.
+ *
+ * \param ctx        The context to use for new allocation.  If \p ptr != NULL,
+ *                   it must be the same as ralloc_parent(\p ptr).
+ * \param ptr        Pointer to the memory to be resized.  May be NULL.
+ * \param old_size   The amount of memory in the previous allocation, in bytes.
+ * \param new_size   The amount of memory to allocate, in bytes.
+ */
+void *rerzalloc_size(const void *ctx, void *ptr,
+                     size_t old_size, size_t new_size);
+
 /// \defgroup array Array Allocators @{
 
 /**
@@ -177,6 +194,28 @@ void *reralloc_size(const void *ctx, void *ptr, size_t size);
 #define reralloc(ctx, ptr, type, count) \
    ((type *) reralloc_array_size(ctx, ptr, sizeof(type), count))
 
+/**
+ * \def rerzalloc(ctx, ptr, type, count)
+ * Resize a ralloc-managed array, preserving data and initializing any newly
+ * allocated data to zero.
+ *
+ * Similar to \c realloc.  Unlike C89, passing 0 for \p size does not free the
+ * memory.  Instead, it resizes it to a 0-byte ralloc context, just like
+ * calling ralloc_size(ctx, 0).  This is different from talloc.
+ *
+ * More than a convenience function, this also checks for integer overflow when
+ * multiplying \c sizeof(type) and \p count.  This is necessary for security.
+ *
+ * \param ctx        The context to use for new allocation.  If \p ptr != NULL,
+ *                   it must be the same as ralloc_parent(\p ptr).
+ * \param ptr        Pointer to the array to be resized.  May be NULL.
+ * \param type       The element type.
+ * \param old_count  The number of elements in the previous allocation.
+ * \param new_count  The number of elements to allocate.
+ */
+#define rerzalloc(ctx, ptr, type, old_count, new_count) \
+   ((type *) rerzalloc_array_size(ctx, ptr, sizeof(type), old_count, new_count))
+
 /**
  * Allocate memory for an array chained off the given context.
  *
@@ -217,6 +256,29 @@ void *rzalloc_array_size(const void *ctx, size_t size, unsigned count) MALLOCLIK
  */
 void *reralloc_array_size(const void *ctx, void *ptr, size_t size,
                          unsigned count);
+
+/**
+ * Resize a ralloc-managed array, preserving data and initializing any newly
+ * allocated data to zero.
+ *
+ * Similar to \c realloc.  Unlike C89, passing 0 for \p size does not free the
+ * memory.  Instead, it resizes it to a 0-byte ralloc context, just like
+ * calling ralloc_size(ctx, 0).  This is different from talloc.
+ *
+ * More than a convenience function, this also checks for integer overflow when
+ * multiplying \c sizeof(type) and \p count.  This is necessary for security.
+ *
+ * \param ctx        The context to use for new allocation.  If \p ptr != NULL,
+ *                   it must be the same as ralloc_parent(\p ptr).
+ * \param ptr        Pointer to the array to be resized.  May be NULL.
+ * \param size       The size of an individual element.
+ * \param old_count  The number of elements in the previous allocation.
+ * \param new_count  The number of elements to allocate.
+ *
+ * \return True unless allocation failed.
+ */
+void *rerzalloc_array_size(const void *ctx, void *ptr, size_t size,
+                          unsigned old_count, unsigned new_count);
 /// @}
 
 /**