Blame SOURCES/glibc-rh677316-alloc_buffer.patch

fa3bfd
Backport of the following upstream commits:
fa3bfd
fa3bfd
commit d54bb9b1d3fd25779fba2c403003c5097ba9af73
fa3bfd
Author: Tulio Magno Quites Machado Filho <tuliom@linux.vnet.ibm.com>
fa3bfd
Date:   Mon Jun 26 09:55:41 2017 -0300
fa3bfd
fa3bfd
    Prevent an implicit int promotion in malloc/tst-alloc_buffer.c
fa3bfd
fa3bfd
commit 4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec
fa3bfd
Author: Florian Weimer <fweimer@redhat.com>
fa3bfd
Date:   Wed Jun 21 22:43:57 2017 +0200
fa3bfd
fa3bfd
    Implement allocation buffers for internal use
fa3bfd
fa3bfd
diff --git a/include/alloc_buffer.h b/include/alloc_buffer.h
fa3bfd
new file mode 100644
fa3bfd
index 0000000000000000..d668a60d66c5b076
fa3bfd
--- /dev/null
fa3bfd
+++ b/include/alloc_buffer.h
fa3bfd
@@ -0,0 +1,367 @@
fa3bfd
+/* Allocation from a fixed-size buffer.
fa3bfd
+   Copyright (C) 2017 Free Software Foundation, Inc.
fa3bfd
+   This file is part of the GNU C Library.
fa3bfd
+
fa3bfd
+   The GNU C Library is free software; you can redistribute it and/or
fa3bfd
+   modify it under the terms of the GNU Lesser General Public
fa3bfd
+   License as published by the Free Software Foundation; either
fa3bfd
+   version 2.1 of the License, or (at your option) any later version.
fa3bfd
+
fa3bfd
+   The GNU C Library is distributed in the hope that it will be useful,
fa3bfd
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
fa3bfd
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
fa3bfd
+   Lesser General Public License for more details.
fa3bfd
+
fa3bfd
+   You should have received a copy of the GNU Lesser General Public
fa3bfd
+   License along with the GNU C Library; if not, see
fa3bfd
+   <http://www.gnu.org/licenses/>.  */
fa3bfd
+
fa3bfd
+/* Allocation buffers are used to carve out sub-allocations from a
fa3bfd
+   larger allocation.  Their primary application is in writing NSS
fa3bfd
+   modules, which receive a caller-allocated buffer in which they are
fa3bfd
+   expected to store variable-length results:
fa3bfd
+
fa3bfd
+     void *buffer = ...;
fa3bfd
+     size_t buffer_size = ...;
fa3bfd
+
fa3bfd
+     struct alloc_buffer buf = alloc_buffer_create (buffer, buffer_size);
fa3bfd
+     result->gr_name = alloc_buffer_copy_string (&buf, name);
fa3bfd
+
fa3bfd
+     // Allocate a list of group_count groups and copy strings into it.
fa3bfd
+     char **group_list = alloc_buffer_alloc_array
fa3bfd
+       (&buf, char *, group_count  + 1);
fa3bfd
+     if (group_list == NULL)
fa3bfd
+       return ...; // Request a larger buffer.
fa3bfd
+     for (int i = 0; i < group_count; ++i)
fa3bfd
+       group_list[i] = alloc_buffer_copy_string (&buf, group_list_src[i]);
fa3bfd
+     group_list[group_count] = NULL;
fa3bfd
+     ...
fa3bfd
+
fa3bfd
+     if (alloc_buffer_has_failed (&buf))
fa3bfd
+       return ...; // Request a larger buffer.
fa3bfd
+     result->gr_mem = group_list;
fa3bfd
+     ...
fa3bfd
+
fa3bfd
+   Note that it is not necessary to check the results of individual
fa3bfd
+   allocation operations if the returned pointer is not dereferenced.
fa3bfd
+   Allocation failure is sticky, so one check using
fa3bfd
+   alloc_buffer_has_failed at the end covers all previous failures.
fa3bfd
+
fa3bfd
+   A different use case involves combining multiple heap allocations
fa3bfd
+   into a single, large one.  In the following example, an array of
fa3bfd
+   doubles and an array of ints is allocated:
fa3bfd
+
fa3bfd
+     size_t double_array_size = ...;
fa3bfd
+     size_t int_array_size = ...;
fa3bfd
+
fa3bfd
+     void *heap_ptr;
fa3bfd
+     struct alloc_buffer buf = alloc_buffer_allocate
fa3bfd
+       (double_array_size * sizeof (double) + int_array_size * sizeof (int),
fa3bfd
+        &heap_ptr);
fa3bfd
+     _Static_assert (__alignof__ (double) >= __alignof__ (int),
fa3bfd
+                     "no padding after double array");
fa3bfd
+     double *double_array = alloc_buffer_alloc_array
fa3bfd
+       (&buf, double, double_array_size);
fa3bfd
+     int *int_array = alloc_buffer_alloc_array (&buf, int, int_array_size);
fa3bfd
+     if (alloc_buffer_has_failed (&buf))
fa3bfd
+       return ...; // Report error.
fa3bfd
+     ...
fa3bfd
+     free (heap_ptr);
fa3bfd
+
fa3bfd
+   The advantage over manual coding is that the computation of the
fa3bfd
+   allocation size does not need an overflow check.  In case of an
fa3bfd
+   overflow, one of the subsequent allocations from the buffer will
fa3bfd
+   fail.  The initial size computation is checked for consistency at
fa3bfd
+   run time, too.  */
fa3bfd
+
fa3bfd
+#ifndef _ALLOC_BUFFER_H
fa3bfd
+#define _ALLOC_BUFFER_H
fa3bfd
+
fa3bfd
+#include <inttypes.h>
fa3bfd
+#include <stdbool.h>
fa3bfd
+#include <stddef.h>
fa3bfd
+#include <stdlib.h>
fa3bfd
+#include <sys/param.h>
fa3bfd
+
fa3bfd
+/* struct alloc_buffer objects refer to a region of bytes in memory of a
fa3bfd
+   fixed size.  The functions below can be used to allocate single
fa3bfd
+   objects and arrays from this memory region, or write to its end.
fa3bfd
+   On allocation failure (or if an attempt to write beyond the end of
fa3bfd
+   the buffer with one of the copy functions), the buffer enters a
fa3bfd
+   failed state.
fa3bfd
+
fa3bfd
+   struct alloc_buffer objects can be copied.  The backing buffer will
fa3bfd
+   be shared, but the current write position will be independent.
fa3bfd
+
fa3bfd
+   Conceptually, the memory region consists of a current write pointer
fa3bfd
+   and a limit, beyond which the write pointer cannot move.  */
fa3bfd
+struct alloc_buffer
fa3bfd
+{
fa3bfd
+  /* uintptr_t is used here to simplify the alignment code, and to
fa3bfd
+     avoid issues undefined subtractions if the buffer covers more
fa3bfd
+     than half of the address space (which would result in differences
fa3bfd
+     which could not be represented as a ptrdiff_t value).  */
fa3bfd
+  uintptr_t __alloc_buffer_current;
fa3bfd
+  uintptr_t __alloc_buffer_end;
fa3bfd
+};
fa3bfd
+
fa3bfd
+enum
fa3bfd
+  {
fa3bfd
+    /* The value for the __alloc_buffer_current member which marks the
fa3bfd
+       buffer as invalid (together with a zero-length buffer).  */
fa3bfd
+    __ALLOC_BUFFER_INVALID_POINTER = 0,
fa3bfd
+  };
fa3bfd
+
fa3bfd
+/* Internal function.  Terminate the process using __libc_fatal.  */
fa3bfd
+void __libc_alloc_buffer_create_failure (void *start, size_t size);
fa3bfd
+
fa3bfd
+/* Create a new allocation buffer.  The byte range from START to START
fa3bfd
+   + SIZE - 1 must be valid, and the allocation buffer allocates
fa3bfd
+   objects from that range.  If START is NULL (so that SIZE must be
fa3bfd
+   0), the buffer is marked as failed immediately.  */
fa3bfd
+static inline struct alloc_buffer
fa3bfd
+alloc_buffer_create (void *start, size_t size)
fa3bfd
+{
fa3bfd
+  uintptr_t current = (uintptr_t) start;
fa3bfd
+  uintptr_t end = (uintptr_t) start + size;
fa3bfd
+  if (end < current)
fa3bfd
+    __libc_alloc_buffer_create_failure (start, size);
fa3bfd
+  return (struct alloc_buffer) { current, end };
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Internal function.  See alloc_buffer_allocate below.  */
fa3bfd
+struct alloc_buffer __libc_alloc_buffer_allocate (size_t size, void **pptr)
fa3bfd
+  __attribute__ ((nonnull (2)));
fa3bfd
+
fa3bfd
+/* Allocate a buffer of SIZE bytes using malloc.  The returned buffer
fa3bfd
+   is in a failed state if malloc fails.  *PPTR points to the start of
fa3bfd
+   the buffer and can be used to free it later, after the returned
fa3bfd
+   buffer has been freed.  */
fa3bfd
+static __always_inline __attribute__ ((nonnull (2)))
fa3bfd
+struct alloc_buffer alloc_buffer_allocate (size_t size, void **pptr)
fa3bfd
+{
fa3bfd
+  return __libc_alloc_buffer_allocate (size, pptr);
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Mark the buffer as failed.  */
fa3bfd
+static inline void __attribute__ ((nonnull (1)))
fa3bfd
+alloc_buffer_mark_failed (struct alloc_buffer *buf)
fa3bfd
+{
fa3bfd
+  buf->__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER;
fa3bfd
+  buf->__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER;
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Return the remaining number of bytes in the buffer.  */
fa3bfd
+static __always_inline __attribute__ ((nonnull (1))) size_t
fa3bfd
+alloc_buffer_size (const struct alloc_buffer *buf)
fa3bfd
+{
fa3bfd
+  return buf->__alloc_buffer_end - buf->__alloc_buffer_current;
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Return true if the buffer has been marked as failed.  */
fa3bfd
+static inline bool __attribute__ ((nonnull (1)))
fa3bfd
+alloc_buffer_has_failed (const struct alloc_buffer *buf)
fa3bfd
+{
fa3bfd
+  return buf->__alloc_buffer_current == __ALLOC_BUFFER_INVALID_POINTER;
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Add a single byte to the buffer (consuming the space for this
fa3bfd
+   byte).  Mark the buffer as failed if there is not enough room.  */
fa3bfd
+static inline void __attribute__ ((nonnull (1)))
fa3bfd
+alloc_buffer_add_byte (struct alloc_buffer *buf, unsigned char b)
fa3bfd
+{
fa3bfd
+  if (__glibc_likely (buf->__alloc_buffer_current < buf->__alloc_buffer_end))
fa3bfd
+    {
fa3bfd
+      *(unsigned char *) buf->__alloc_buffer_current = b;
fa3bfd
+      ++buf->__alloc_buffer_current;
fa3bfd
+    }
fa3bfd
+  else
fa3bfd
+    alloc_buffer_mark_failed (buf);
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Obtain a pointer to LENGTH bytes in BUF, and consume these bytes.
fa3bfd
+   NULL is returned if there is not enough room, and the buffer is
fa3bfd
+   marked as failed, or if the buffer has already failed.
fa3bfd
+   (Zero-length allocations from an empty buffer which has not yet
fa3bfd
+   failed succeed.)  */
fa3bfd
+static inline __attribute__ ((nonnull (1))) void *
fa3bfd
+alloc_buffer_alloc_bytes (struct alloc_buffer *buf, size_t length)
fa3bfd
+{
fa3bfd
+  if (length <= alloc_buffer_size (buf))
fa3bfd
+    {
fa3bfd
+      void *result = (void *) buf->__alloc_buffer_current;
fa3bfd
+      buf->__alloc_buffer_current += length;
fa3bfd
+      return result;
fa3bfd
+    }
fa3bfd
+  else
fa3bfd
+    {
fa3bfd
+      alloc_buffer_mark_failed (buf);
fa3bfd
+      return NULL;
fa3bfd
+    }
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Internal function.  Statically assert that the type size is
fa3bfd
+   constant and valid.  */
fa3bfd
+static __always_inline size_t
fa3bfd
+__alloc_buffer_assert_size (size_t size)
fa3bfd
+{
fa3bfd
+  if (!__builtin_constant_p (size))
fa3bfd
+    {
fa3bfd
+      __errordecl (error, "type size is not constant");
fa3bfd
+      error ();
fa3bfd
+    }
fa3bfd
+  else if (size == 0)
fa3bfd
+    {
fa3bfd
+      __errordecl (error, "type size is zero");
fa3bfd
+      error ();
fa3bfd
+    }
fa3bfd
+  return size;
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Internal function.  Statically assert that the type alignment is
fa3bfd
+   constant and valid.  */
fa3bfd
+static __always_inline size_t
fa3bfd
+__alloc_buffer_assert_align (size_t align)
fa3bfd
+{
fa3bfd
+  if (!__builtin_constant_p (align))
fa3bfd
+    {
fa3bfd
+      __errordecl (error, "type alignment is not constant");
fa3bfd
+      error ();
fa3bfd
+    }
fa3bfd
+  else if (align == 0)
fa3bfd
+    {
fa3bfd
+      __errordecl (error, "type alignment is zero");
fa3bfd
+      error ();
fa3bfd
+    }
fa3bfd
+  else if (!powerof2 (align))
fa3bfd
+    {
fa3bfd
+      __errordecl (error, "type alignment is not a power of two");
fa3bfd
+      error ();
fa3bfd
+    }
fa3bfd
+  return align;
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Internal function.  Obtain a pointer to an object.  */
fa3bfd
+static inline __attribute__ ((nonnull (1))) void *
fa3bfd
+__alloc_buffer_alloc (struct alloc_buffer *buf, size_t size, size_t align)
fa3bfd
+{
fa3bfd
+  if (size == 1 && align == 1)
fa3bfd
+    return alloc_buffer_alloc_bytes (buf, size);
fa3bfd
+
fa3bfd
+  size_t current = buf->__alloc_buffer_current;
fa3bfd
+  size_t aligned = roundup (current, align);
fa3bfd
+  size_t new_current = aligned + size;
fa3bfd
+  if (aligned >= current        /* No overflow in align step.  */
fa3bfd
+      && new_current >= size    /* No overflow in size computation.  */
fa3bfd
+      && new_current <= buf->__alloc_buffer_end) /* Room in buffer.  */
fa3bfd
+    {
fa3bfd
+      buf->__alloc_buffer_current = new_current;
fa3bfd
+      return (void *) aligned;
fa3bfd
+    }
fa3bfd
+  else
fa3bfd
+    {
fa3bfd
+      alloc_buffer_mark_failed (buf);
fa3bfd
+      return NULL;
fa3bfd
+    }
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Obtain a TYPE * pointer to an object in BUF of TYPE.  Consume these
fa3bfd
+   bytes from the buffer.  Return NULL and mark the buffer as failed
fa3bfd
+   if if there is not enough room in the buffer, or if the buffer has
fa3bfd
+   failed before.  */
fa3bfd
+#define alloc_buffer_alloc(buf, type)				\
fa3bfd
+  ((type *) __alloc_buffer_alloc				\
fa3bfd
+   (buf, __alloc_buffer_assert_size (sizeof (type)),		\
fa3bfd
+    __alloc_buffer_assert_align (__alignof__ (type))))
fa3bfd
+
fa3bfd
+/* Internal function.  Obtain a pointer to an object which is
fa3bfd
+   subsequently added.  */
fa3bfd
+static inline const __attribute__ ((nonnull (1))) void *
fa3bfd
+__alloc_buffer_next (struct alloc_buffer *buf, size_t align)
fa3bfd
+{
fa3bfd
+  if (align == 1)
fa3bfd
+    return (const void *) buf->__alloc_buffer_current;
fa3bfd
+
fa3bfd
+  size_t current = buf->__alloc_buffer_current;
fa3bfd
+  size_t aligned = roundup (current, align);
fa3bfd
+  if (aligned >= current        /* No overflow in align step.  */
fa3bfd
+      && aligned <= buf->__alloc_buffer_end) /* Room in buffer.  */
fa3bfd
+    {
fa3bfd
+      buf->__alloc_buffer_current = aligned;
fa3bfd
+      return (const void *) aligned;
fa3bfd
+    }
fa3bfd
+  else
fa3bfd
+    {
fa3bfd
+      alloc_buffer_mark_failed (buf);
fa3bfd
+      return NULL;
fa3bfd
+    }
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Like alloc_buffer_alloc, but do not advance the pointer beyond the
fa3bfd
+   object (so a subseqent call to alloc_buffer_next or
fa3bfd
+   alloc_buffer_alloc returns the same pointer).  Note that the buffer
fa3bfd
+   is still aligned according to the requirements of TYPE.  The effect
fa3bfd
+   of this function is similar to allocating a zero-length array from
fa3bfd
+   the buffer.  */
fa3bfd
+#define alloc_buffer_next(buf, type)				\
fa3bfd
+  ((const type *) __alloc_buffer_next				\
fa3bfd
+   (buf, __alloc_buffer_assert_align (__alignof__ (type))))
fa3bfd
+
fa3bfd
+/* Internal function.  Allocate an array.  */
fa3bfd
+void * __libc_alloc_buffer_alloc_array (struct alloc_buffer *buf,
fa3bfd
+					size_t size, size_t align,
fa3bfd
+					size_t count)
fa3bfd
+  __attribute__ ((nonnull (1)));
fa3bfd
+
fa3bfd
+/* Obtain a TYPE * pointer to an array of COUNT objects in BUF of
fa3bfd
+   TYPE.  Consume these bytes from the buffer.  Return NULL and mark
fa3bfd
+   the buffer as failed if if there is not enough room in the buffer,
fa3bfd
+   or if the buffer has failed before.  (Zero-length allocations from
fa3bfd
+   an empty buffer which has not yet failed succeed.)  */
fa3bfd
+#define alloc_buffer_alloc_array(buf, type, count)       \
fa3bfd
+  ((type *) __libc_alloc_buffer_alloc_array		 \
fa3bfd
+   (buf, __alloc_buffer_assert_size (sizeof (type)),	 \
fa3bfd
+    __alloc_buffer_assert_align (__alignof__ (type)),	 \
fa3bfd
+    count))
fa3bfd
+
fa3bfd
+/* Internal function.  See alloc_buffer_copy_bytes below.  */
fa3bfd
+struct alloc_buffer __libc_alloc_buffer_copy_bytes (struct alloc_buffer,
fa3bfd
+						    const void *, size_t)
fa3bfd
+  __attribute__ ((nonnull (2)));
fa3bfd
+
fa3bfd
+/* Copy SIZE bytes starting at SRC into the buffer.  If there is not
fa3bfd
+   enough room in the buffer, the buffer is marked as failed.  No
fa3bfd
+   alignment of the buffer is performed.  */
fa3bfd
+static inline __attribute__ ((nonnull (1, 2))) void
fa3bfd
+alloc_buffer_copy_bytes (struct alloc_buffer *buf, const void *src, size_t size)
fa3bfd
+{
fa3bfd
+  *buf = __libc_alloc_buffer_copy_bytes (*buf, src, size);
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Internal function.  See alloc_buffer_copy_string below.  */
fa3bfd
+struct alloc_buffer __libc_alloc_buffer_copy_string (struct alloc_buffer,
fa3bfd
+						     const char *)
fa3bfd
+  __attribute__ ((nonnull (2)));
fa3bfd
+
fa3bfd
+/* Copy the string at SRC into the buffer, including its null
fa3bfd
+   terminator.  If there is not enough room in the buffer, the buffer
fa3bfd
+   is marked as failed.  Return a pointer to the string.  */
fa3bfd
+static inline __attribute__ ((nonnull (1, 2))) char *
fa3bfd
+alloc_buffer_copy_string (struct alloc_buffer *buf, const char *src)
fa3bfd
+{
fa3bfd
+  char *result = (char *) buf->__alloc_buffer_current;
fa3bfd
+  *buf = __libc_alloc_buffer_copy_string (*buf, src);
fa3bfd
+  if (alloc_buffer_has_failed (buf))
fa3bfd
+    result = NULL;
fa3bfd
+  return result;
fa3bfd
+}
fa3bfd
+
fa3bfd
+#ifndef _ISOMAC
fa3bfd
+libc_hidden_proto (__libc_alloc_buffer_alloc_array)
fa3bfd
+libc_hidden_proto (__libc_alloc_buffer_allocate)
fa3bfd
+libc_hidden_proto (__libc_alloc_buffer_copy_bytes)
fa3bfd
+libc_hidden_proto (__libc_alloc_buffer_copy_string)
fa3bfd
+libc_hidden_proto (__libc_alloc_buffer_create_failure)
fa3bfd
+#endif
fa3bfd
+
fa3bfd
+#endif /* _ALLOC_BUFFER_H */
fa3bfd
diff --git a/malloc/Makefile b/malloc/Makefile
fa3bfd
index 63fc3291dcc4077a..db4b1f921d56da32 100644
fa3bfd
--- a/malloc/Makefile
fa3bfd
+++ b/malloc/Makefile
fa3bfd
@@ -37,6 +37,7 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
fa3bfd
 	 tst-dynarray \
fa3bfd
 	 tst-dynarray-fail \
fa3bfd
 	 tst-dynarray-at-fail \
fa3bfd
+	 tst-alloc_buffer \
fa3bfd
 
fa3bfd
 tests-static := \
fa3bfd
 	 tst-interpose-static-nothread \
fa3bfd
@@ -55,6 +56,11 @@ routines = malloc morecore mcheck mtrace obstack \
fa3bfd
   dynarray_finalize \
fa3bfd
   dynarray_resize \
fa3bfd
   dynarray_resize_clear \
fa3bfd
+  alloc_buffer_alloc_array \
fa3bfd
+  alloc_buffer_allocate \
fa3bfd
+  alloc_buffer_copy_bytes  \
fa3bfd
+  alloc_buffer_copy_string \
fa3bfd
+  alloc_buffer_create_failure \
fa3bfd
 
fa3bfd
 
fa3bfd
 install-lib := libmcheck.a
fa3bfd
diff --git a/malloc/Versions b/malloc/Versions
fa3bfd
index 16f9dab418a4e3f6..0b7b690bbbdd1c2c 100644
fa3bfd
--- a/malloc/Versions
fa3bfd
+++ b/malloc/Versions
fa3bfd
@@ -79,5 +79,12 @@ libc {
fa3bfd
     __libc_dynarray_finalize;
fa3bfd
     __libc_dynarray_resize;
fa3bfd
     __libc_dynarray_resize_clear;
fa3bfd
+
fa3bfd
+    # struct alloc_buffer support
fa3bfd
+    __libc_alloc_buffer_alloc_array;
fa3bfd
+    __libc_alloc_buffer_allocate;
fa3bfd
+    __libc_alloc_buffer_copy_bytes;
fa3bfd
+    __libc_alloc_buffer_copy_string;
fa3bfd
+    __libc_alloc_buffer_create_failure;
fa3bfd
   }
fa3bfd
 }
fa3bfd
diff --git a/malloc/alloc_buffer_alloc_array.c b/malloc/alloc_buffer_alloc_array.c
fa3bfd
new file mode 100644
fa3bfd
index 0000000000000000..68e14da8dd4e545c
fa3bfd
--- /dev/null
fa3bfd
+++ b/malloc/alloc_buffer_alloc_array.c
fa3bfd
@@ -0,0 +1,47 @@
fa3bfd
+/* Array allocation from a fixed-size buffer.
fa3bfd
+   Copyright (C) 2017 Free Software Foundation, Inc.
fa3bfd
+   This file is part of the GNU C Library.
fa3bfd
+
fa3bfd
+   The GNU C Library is free software; you can redistribute it and/or
fa3bfd
+   modify it under the terms of the GNU Lesser General Public
fa3bfd
+   License as published by the Free Software Foundation; either
fa3bfd
+   version 2.1 of the License, or (at your option) any later version.
fa3bfd
+
fa3bfd
+   The GNU C Library is distributed in the hope that it will be useful,
fa3bfd
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
fa3bfd
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
fa3bfd
+   Lesser General Public License for more details.
fa3bfd
+
fa3bfd
+   You should have received a copy of the GNU Lesser General Public
fa3bfd
+   License along with the GNU C Library; if not, see
fa3bfd
+   <http://www.gnu.org/licenses/>.  */
fa3bfd
+
fa3bfd
+#include <alloc_buffer.h>
fa3bfd
+#include <malloc-internal.h>
fa3bfd
+#include <libc-pointer-arith.h>
fa3bfd
+
fa3bfd
+void *
fa3bfd
+__libc_alloc_buffer_alloc_array (struct alloc_buffer *buf, size_t element_size,
fa3bfd
+                                 size_t align, size_t count)
fa3bfd
+{
fa3bfd
+  size_t current = buf->__alloc_buffer_current;
fa3bfd
+  /* The caller asserts that align is a power of two.  */
fa3bfd
+  size_t aligned = ALIGN_UP (current, align);
fa3bfd
+  size_t size;
fa3bfd
+  bool overflow = check_mul_overflow_size_t (element_size, count, &size);
fa3bfd
+  size_t new_current = aligned + size;
fa3bfd
+  if (!overflow                /* Multiplication did not overflow.  */
fa3bfd
+      && aligned >= current    /* No overflow in align step.  */
fa3bfd
+      && new_current >= size   /* No overflow in size computation.  */
fa3bfd
+      && new_current <= buf->__alloc_buffer_end) /* Room in buffer.  */
fa3bfd
+    {
fa3bfd
+      buf->__alloc_buffer_current = new_current;
fa3bfd
+      return (void *) aligned;
fa3bfd
+    }
fa3bfd
+  else
fa3bfd
+    {
fa3bfd
+      alloc_buffer_mark_failed (buf);
fa3bfd
+      return NULL;
fa3bfd
+    }
fa3bfd
+}
fa3bfd
+libc_hidden_def (__libc_alloc_buffer_alloc_array)
fa3bfd
diff --git a/malloc/alloc_buffer_allocate.c b/malloc/alloc_buffer_allocate.c
fa3bfd
new file mode 100644
fa3bfd
index 0000000000000000..cbde72b842f0b5d1
fa3bfd
--- /dev/null
fa3bfd
+++ b/malloc/alloc_buffer_allocate.c
fa3bfd
@@ -0,0 +1,36 @@
fa3bfd
+/* Allocate a fixed-size allocation buffer using malloc.
fa3bfd
+   Copyright (C) 2017 Free Software Foundation, Inc.
fa3bfd
+   This file is part of the GNU C Library.
fa3bfd
+
fa3bfd
+   The GNU C Library is free software; you can redistribute it and/or
fa3bfd
+   modify it under the terms of the GNU Lesser General Public
fa3bfd
+   License as published by the Free Software Foundation; either
fa3bfd
+   version 2.1 of the License, or (at your option) any later version.
fa3bfd
+
fa3bfd
+   The GNU C Library is distributed in the hope that it will be useful,
fa3bfd
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
fa3bfd
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
fa3bfd
+   Lesser General Public License for more details.
fa3bfd
+
fa3bfd
+   You should have received a copy of the GNU Lesser General Public
fa3bfd
+   License along with the GNU C Library; if not, see
fa3bfd
+   <http://www.gnu.org/licenses/>.  */
fa3bfd
+
fa3bfd
+#include <alloc_buffer.h>
fa3bfd
+
fa3bfd
+#include <stdlib.h>
fa3bfd
+
fa3bfd
+struct alloc_buffer
fa3bfd
+__libc_alloc_buffer_allocate (size_t size, void **pptr)
fa3bfd
+{
fa3bfd
+  *pptr = malloc (size);
fa3bfd
+  if (*pptr == NULL)
fa3bfd
+    return (struct alloc_buffer)
fa3bfd
+      {
fa3bfd
+        .__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER,
fa3bfd
+        .__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER
fa3bfd
+      };
fa3bfd
+  else
fa3bfd
+    return alloc_buffer_create (*pptr, size);
fa3bfd
+}
fa3bfd
+libc_hidden_def (__libc_alloc_buffer_allocate)
fa3bfd
diff --git a/malloc/alloc_buffer_copy_bytes.c b/malloc/alloc_buffer_copy_bytes.c
fa3bfd
new file mode 100644
fa3bfd
index 0000000000000000..66196f1520c6fc84
fa3bfd
--- /dev/null
fa3bfd
+++ b/malloc/alloc_buffer_copy_bytes.c
fa3bfd
@@ -0,0 +1,34 @@
fa3bfd
+/* Copy an array of bytes into the buffer.
fa3bfd
+   Copyright (C) 2017 Free Software Foundation, Inc.
fa3bfd
+   This file is part of the GNU C Library.
fa3bfd
+
fa3bfd
+   The GNU C Library is free software; you can redistribute it and/or
fa3bfd
+   modify it under the terms of the GNU Lesser General Public
fa3bfd
+   License as published by the Free Software Foundation; either
fa3bfd
+   version 2.1 of the License, or (at your option) any later version.
fa3bfd
+
fa3bfd
+   The GNU C Library is distributed in the hope that it will be useful,
fa3bfd
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
fa3bfd
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
fa3bfd
+   Lesser General Public License for more details.
fa3bfd
+
fa3bfd
+   You should have received a copy of the GNU Lesser General Public
fa3bfd
+   License along with the GNU C Library; if not, see
fa3bfd
+   <http://www.gnu.org/licenses/>.  */
fa3bfd
+
fa3bfd
+#include <alloc_buffer.h>
fa3bfd
+
fa3bfd
+#include <string.h>
fa3bfd
+
fa3bfd
+/* This function works on a copy of the buffer object, so that it can
fa3bfd
+   remain non-addressable in the caller.  */
fa3bfd
+struct alloc_buffer
fa3bfd
+__libc_alloc_buffer_copy_bytes (struct alloc_buffer buf,
fa3bfd
+                                const void *src, size_t len)
fa3bfd
+{
fa3bfd
+  void *ptr = alloc_buffer_alloc_bytes (&buf, len);
fa3bfd
+  if (ptr != NULL)
fa3bfd
+    memcpy (ptr, src, len);
fa3bfd
+  return buf;
fa3bfd
+}
fa3bfd
+libc_hidden_def (__libc_alloc_buffer_copy_bytes)
fa3bfd
diff --git a/malloc/alloc_buffer_copy_string.c b/malloc/alloc_buffer_copy_string.c
fa3bfd
new file mode 100644
fa3bfd
index 0000000000000000..77c0023d510b43be
fa3bfd
--- /dev/null
fa3bfd
+++ b/malloc/alloc_buffer_copy_string.c
fa3bfd
@@ -0,0 +1,30 @@
fa3bfd
+/* Copy a string into the allocation buffer.
fa3bfd
+   Copyright (C) 2017 Free Software Foundation, Inc.
fa3bfd
+   This file is part of the GNU C Library.
fa3bfd
+
fa3bfd
+   The GNU C Library is free software; you can redistribute it and/or
fa3bfd
+   modify it under the terms of the GNU Lesser General Public
fa3bfd
+   License as published by the Free Software Foundation; either
fa3bfd
+   version 2.1 of the License, or (at your option) any later version.
fa3bfd
+
fa3bfd
+   The GNU C Library is distributed in the hope that it will be useful,
fa3bfd
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
fa3bfd
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
fa3bfd
+   Lesser General Public License for more details.
fa3bfd
+
fa3bfd
+   You should have received a copy of the GNU Lesser General Public
fa3bfd
+   License along with the GNU C Library; if not, see
fa3bfd
+   <http://www.gnu.org/licenses/>.  */
fa3bfd
+
fa3bfd
+#include <alloc_buffer.h>
fa3bfd
+
fa3bfd
+#include <string.h>
fa3bfd
+
fa3bfd
+/* This function works on a copy of the buffer object, so that it can
fa3bfd
+   remain non-addressable in the caller.  */
fa3bfd
+struct alloc_buffer
fa3bfd
+__libc_alloc_buffer_copy_string (struct alloc_buffer buf, const char *src)
fa3bfd
+{
fa3bfd
+  return __libc_alloc_buffer_copy_bytes (buf, src, strlen (src) + 1);
fa3bfd
+}
fa3bfd
+libc_hidden_def (__libc_alloc_buffer_copy_string)
fa3bfd
diff --git a/malloc/alloc_buffer_create_failure.c b/malloc/alloc_buffer_create_failure.c
fa3bfd
new file mode 100644
fa3bfd
index 0000000000000000..5ffba22f7b1e0dc5
fa3bfd
--- /dev/null
fa3bfd
+++ b/malloc/alloc_buffer_create_failure.c
fa3bfd
@@ -0,0 +1,31 @@
fa3bfd
+/* Terminate the process as the result of an invalid allocation buffer.
fa3bfd
+   Copyright (C) 2017 Free Software Foundation, Inc.
fa3bfd
+   This file is part of the GNU C Library.
fa3bfd
+
fa3bfd
+   The GNU C Library is free software; you can redistribute it and/or
fa3bfd
+   modify it under the terms of the GNU Lesser General Public
fa3bfd
+   License as published by the Free Software Foundation; either
fa3bfd
+   version 2.1 of the License, or (at your option) any later version.
fa3bfd
+
fa3bfd
+   The GNU C Library is distributed in the hope that it will be useful,
fa3bfd
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
fa3bfd
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
fa3bfd
+   Lesser General Public License for more details.
fa3bfd
+
fa3bfd
+   You should have received a copy of the GNU Lesser General Public
fa3bfd
+   License along with the GNU C Library; if not, see
fa3bfd
+   <http://www.gnu.org/licenses/>.  */
fa3bfd
+
fa3bfd
+#include <alloc_buffer.h>
fa3bfd
+#include <stdio.h>
fa3bfd
+
fa3bfd
+void
fa3bfd
+__libc_alloc_buffer_create_failure (void *start, size_t size)
fa3bfd
+{
fa3bfd
+  char buf[200];
fa3bfd
+  __snprintf (buf, sizeof (buf), "Fatal glibc error: "
fa3bfd
+              "invalid allocation buffer of size %zu\n",
fa3bfd
+              size);
fa3bfd
+  __libc_fatal (buf);
fa3bfd
+}
fa3bfd
+libc_hidden_def (__libc_alloc_buffer_create_failure)
fa3bfd
diff --git a/malloc/tst-alloc_buffer.c b/malloc/tst-alloc_buffer.c
fa3bfd
new file mode 100644
fa3bfd
index 0000000000000000..9b2bd2046a12c0f2
fa3bfd
--- /dev/null
fa3bfd
+++ b/malloc/tst-alloc_buffer.c
fa3bfd
@@ -0,0 +1,665 @@
fa3bfd
+/* Tests for struct alloc_buffer.
fa3bfd
+   Copyright (C) 2017 Free Software Foundation, Inc.
fa3bfd
+   This file is part of the GNU C Library.
fa3bfd
+
fa3bfd
+   The GNU C Library is free software; you can redistribute it and/or
fa3bfd
+   modify it under the terms of the GNU Lesser General Public
fa3bfd
+   License as published by the Free Software Foundation; either
fa3bfd
+   version 2.1 of the License, or (at your option) any later version.
fa3bfd
+
fa3bfd
+   The GNU C Library is distributed in the hope that it will be useful,
fa3bfd
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
fa3bfd
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
fa3bfd
+   Lesser General Public License for more details.
fa3bfd
+
fa3bfd
+   You should have received a copy of the GNU Lesser General Public
fa3bfd
+   License along with the GNU C Library; if not, see
fa3bfd
+   <http://www.gnu.org/licenses/>.  */
fa3bfd
+
fa3bfd
+#include <arpa/inet.h>
fa3bfd
+#include <alloc_buffer.h>
fa3bfd
+#include <stdio.h>
fa3bfd
+#include <stdlib.h>
fa3bfd
+#include <string.h>
fa3bfd
+#include <support/check.h>
fa3bfd
+#include <support/support.h>
fa3bfd
+#include <support/test-driver.h>
fa3bfd
+
fa3bfd
+/* Return true if PTR is sufficiently aligned for TYPE.  */
fa3bfd
+#define IS_ALIGNED(ptr, type) \
fa3bfd
+  ((((uintptr_t) ptr) & (__alloc_buffer_assert_align (__alignof (type)) - 1)) \
fa3bfd
+   == 0)
fa3bfd
+
fa3bfd
+/* Structure with non-power-of-two size.  */
fa3bfd
+struct twelve
fa3bfd
+{
fa3bfd
+  uint32_t buffer[3] __attribute__ ((aligned (4)));
fa3bfd
+};
fa3bfd
+_Static_assert (sizeof (struct twelve) == 12, "struct twelve");
fa3bfd
+_Static_assert (__alignof__ (struct twelve) == 4, "struct twelve");
fa3bfd
+
fa3bfd
+/* Check for success obtaining empty arrays.  Does not assume the
fa3bfd
+   buffer is empty.  */
fa3bfd
+static void
fa3bfd
+test_empty_array (struct alloc_buffer refbuf)
fa3bfd
+{
fa3bfd
+  bool refbuf_failed = alloc_buffer_has_failed (&refbuf);
fa3bfd
+  if (test_verbose)
fa3bfd
+    printf ("info: %s: current=0x%llx end=0x%llx refbuf_failed=%d\n",
fa3bfd
+            __func__, (unsigned long long) refbuf.__alloc_buffer_current,
fa3bfd
+            (unsigned long long) refbuf.__alloc_buffer_end, refbuf_failed);
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY ((alloc_buffer_alloc_bytes (&buf, 0) == NULL)
fa3bfd
+                 == refbuf_failed);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY ((alloc_buffer_alloc_array (&buf, char, 0) == NULL)
fa3bfd
+                 == refbuf_failed);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
fa3bfd
+  }
fa3bfd
+  /* The following tests can fail due to the need for aligning the
fa3bfd
+     returned pointer.  */
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    bool expect_failure = refbuf_failed
fa3bfd
+      || !IS_ALIGNED (alloc_buffer_next (&buf, void), double);
fa3bfd
+    double *ptr = alloc_buffer_alloc_array (&buf, double, 0);
fa3bfd
+    TEST_VERIFY (IS_ALIGNED (ptr, double));
fa3bfd
+    TEST_VERIFY ((ptr == NULL) == expect_failure);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    bool expect_failure = refbuf_failed
fa3bfd
+      || !IS_ALIGNED (alloc_buffer_next (&buf, void), struct twelve);
fa3bfd
+    struct twelve *ptr = alloc_buffer_alloc_array (&buf, struct twelve, 0);
fa3bfd
+    TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
fa3bfd
+    TEST_VERIFY ((ptr == NULL) == expect_failure);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
fa3bfd
+  }
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Test allocation of impossibly large arrays.  */
fa3bfd
+static void
fa3bfd
+test_impossible_array (struct alloc_buffer refbuf)
fa3bfd
+{
fa3bfd
+  if (test_verbose)
fa3bfd
+    printf ("info: %s: current=0x%llx end=0x%llx\n",
fa3bfd
+            __func__, (unsigned long long) refbuf.__alloc_buffer_current,
fa3bfd
+            (unsigned long long) refbuf.__alloc_buffer_end);
fa3bfd
+  static const size_t counts[] =
fa3bfd
+    { SIZE_MAX, SIZE_MAX - 1, SIZE_MAX - 2, SIZE_MAX - 3, SIZE_MAX - 4,
fa3bfd
+      SIZE_MAX / 2, SIZE_MAX / 2 + 1, SIZE_MAX / 2 - 1, 0};
fa3bfd
+
fa3bfd
+  for (int i = 0; counts[i] != 0; ++i)
fa3bfd
+    {
fa3bfd
+      size_t count = counts[i];
fa3bfd
+      if (test_verbose)
fa3bfd
+        printf ("info: %s: count=%zu\n", __func__, count);
fa3bfd
+      {
fa3bfd
+        struct alloc_buffer buf = refbuf;
fa3bfd
+        TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
fa3bfd
+        TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+      }
fa3bfd
+      {
fa3bfd
+        struct alloc_buffer buf = refbuf;
fa3bfd
+        TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
fa3bfd
+        TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+      }
fa3bfd
+      {
fa3bfd
+        struct alloc_buffer buf = refbuf;
fa3bfd
+        TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, count) == NULL);
fa3bfd
+        TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+      }
fa3bfd
+      {
fa3bfd
+        struct alloc_buffer buf = refbuf;
fa3bfd
+        TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
fa3bfd
+        TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+      }
fa3bfd
+      {
fa3bfd
+        struct alloc_buffer buf = refbuf;
fa3bfd
+        TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
fa3bfd
+                     == NULL);
fa3bfd
+        TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+      }
fa3bfd
+    }
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Check for failure to obtain anything from a failed buffer.  */
fa3bfd
+static void
fa3bfd
+test_after_failure (struct alloc_buffer refbuf)
fa3bfd
+{
fa3bfd
+  if (test_verbose)
fa3bfd
+    printf ("info: %s: current=0x%llx end=0x%llx\n",
fa3bfd
+            __func__, (unsigned long long) refbuf.__alloc_buffer_current,
fa3bfd
+            (unsigned long long) refbuf.__alloc_buffer_end);
fa3bfd
+  TEST_VERIFY (alloc_buffer_has_failed (&refbuf));
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    alloc_buffer_add_byte (&buf, 17);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+  }
fa3bfd
+
fa3bfd
+  test_impossible_array (refbuf);
fa3bfd
+  for (int count = 0; count <= 4; ++count)
fa3bfd
+    {
fa3bfd
+      {
fa3bfd
+        struct alloc_buffer buf = refbuf;
fa3bfd
+        TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
fa3bfd
+        TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+      }
fa3bfd
+      {
fa3bfd
+        struct alloc_buffer buf = refbuf;
fa3bfd
+        TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
fa3bfd
+        TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+      }
fa3bfd
+      {
fa3bfd
+        struct alloc_buffer buf = refbuf;
fa3bfd
+        TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
fa3bfd
+        TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+      }
fa3bfd
+      {
fa3bfd
+        struct alloc_buffer buf = refbuf;
fa3bfd
+        TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
fa3bfd
+                     == NULL);
fa3bfd
+        TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+      }
fa3bfd
+    }
fa3bfd
+}
fa3bfd
+
fa3bfd
+static void
fa3bfd
+test_empty (struct alloc_buffer refbuf)
fa3bfd
+{
fa3bfd
+  TEST_VERIFY (alloc_buffer_size (&refbuf) == 0);
fa3bfd
+  if (alloc_buffer_next (&refbuf, void) != NULL)
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
fa3bfd
+  test_empty_array (refbuf);
fa3bfd
+  test_impossible_array (refbuf);
fa3bfd
+
fa3bfd
+  /* Failure to obtain non-empty objects.  */
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    alloc_buffer_add_byte (&buf, 17);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, 1) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+}
fa3bfd
+
fa3bfd
+static void
fa3bfd
+test_size_1 (struct alloc_buffer refbuf)
fa3bfd
+{
fa3bfd
+  TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
fa3bfd
+  TEST_VERIFY (alloc_buffer_size (&refbuf) == 1);
fa3bfd
+  test_empty_array (refbuf);
fa3bfd
+  test_impossible_array (refbuf);
fa3bfd
+
fa3bfd
+  /* Success adding a single byte.  */
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    alloc_buffer_add_byte (&buf, 17);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    test_empty (buf);
fa3bfd
+  }
fa3bfd
+  TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x11", 1) == 0);
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    signed char *ptr = alloc_buffer_alloc (&buf, signed char);
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    *ptr = 126;
fa3bfd
+    test_empty (buf);
fa3bfd
+  }
fa3bfd
+  TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\176", 1) == 0);
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    *ptr = (char) 253;
fa3bfd
+    test_empty (buf);
fa3bfd
+  }
fa3bfd
+  TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\xfd", 1) == 0);
fa3bfd
+
fa3bfd
+  /* Failure with larger objects.  */
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, short) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, 1) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+}
fa3bfd
+
fa3bfd
+static void
fa3bfd
+test_size_2 (struct alloc_buffer refbuf)
fa3bfd
+{
fa3bfd
+  TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
fa3bfd
+  TEST_VERIFY (alloc_buffer_size (&refbuf) == 2);
fa3bfd
+  TEST_VERIFY (IS_ALIGNED (alloc_buffer_next (&refbuf, void), short));
fa3bfd
+  test_empty_array (refbuf);
fa3bfd
+  test_impossible_array (refbuf);
fa3bfd
+
fa3bfd
+  /* Success adding two bytes.  */
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    alloc_buffer_add_byte (&buf, '@');
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    test_size_1 (buf);
fa3bfd
+  }
fa3bfd
+  TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "@\xfd", 2) == 0);
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    signed char *ptr = alloc_buffer_alloc (&buf, signed char);
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    *ptr = 'A';
fa3bfd
+    test_size_1 (buf);
fa3bfd
+  }
fa3bfd
+  TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "A\xfd", 2) == 0);
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    *ptr = 'B';
fa3bfd
+    test_size_1 (buf);
fa3bfd
+  }
fa3bfd
+  TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "B\xfd", 2) == 0);
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    unsigned short *ptr = alloc_buffer_alloc (&buf, unsigned short);
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    *ptr = htons (0x12f4);
fa3bfd
+    test_empty (buf);
fa3bfd
+  }
fa3bfd
+  TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x12\xf4", 2) == 0);
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    unsigned short *ptr = alloc_buffer_alloc_array (&buf, unsigned short, 1);
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    *ptr = htons (0x13f5);
fa3bfd
+    test_empty (buf);
fa3bfd
+  }
fa3bfd
+  TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x13\xf5", 2) == 0);
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    char *ptr = alloc_buffer_alloc_array (&buf, char, 2);
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    memcpy (ptr, "12", 2);
fa3bfd
+    test_empty (buf);
fa3bfd
+  }
fa3bfd
+  TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "12", 2) == 0);
fa3bfd
+}
fa3bfd
+
fa3bfd
+static void
fa3bfd
+test_misaligned (char pad)
fa3bfd
+{
fa3bfd
+  enum { SIZE = 23 };
fa3bfd
+  char *backing = xmalloc (SIZE + 2);
fa3bfd
+  backing[0] = ~pad;
fa3bfd
+  backing[SIZE + 1] = pad;
fa3bfd
+  struct alloc_buffer refbuf = alloc_buffer_create (backing + 1, SIZE);
fa3bfd
+
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    short *ptr = alloc_buffer_alloc_array (&buf, short, SIZE / sizeof (short));
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (IS_ALIGNED (ptr, short));
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    for (int i = 0; i < SIZE / sizeof (short); ++i)
fa3bfd
+      ptr[i] = htons (0xff01 + i);
fa3bfd
+    TEST_VERIFY (memcmp (ptr,
fa3bfd
+                         "\xff\x01\xff\x02\xff\x03\xff\x04"
fa3bfd
+                         "\xff\x05\xff\x06\xff\x07\xff\x08"
fa3bfd
+                         "\xff\x09\xff\x0a\xff\x0b", 22) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    uint32_t *ptr = alloc_buffer_alloc_array
fa3bfd
+      (&buf, uint32_t, SIZE / sizeof (uint32_t));
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (IS_ALIGNED (ptr, uint32_t));
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    for (int i = 0; i < SIZE / sizeof (uint32_t); ++i)
fa3bfd
+      ptr[i] = htonl (0xf1e2d301 + i);
fa3bfd
+    TEST_VERIFY (memcmp (ptr,
fa3bfd
+                         "\xf1\xe2\xd3\x01\xf1\xe2\xd3\x02"
fa3bfd
+                         "\xf1\xe2\xd3\x03\xf1\xe2\xd3\x04"
fa3bfd
+                         "\xf1\xe2\xd3\x05", 20) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    struct twelve *ptr = alloc_buffer_alloc (&buf, struct twelve);
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    ptr->buffer[0] = htonl (0x11223344);
fa3bfd
+    ptr->buffer[1] = htonl (0x55667788);
fa3bfd
+    ptr->buffer[2] = htonl (0x99aabbcc);
fa3bfd
+    TEST_VERIFY (memcmp (ptr,
fa3bfd
+                         "\x11\x22\x33\x44"
fa3bfd
+                         "\x55\x66\x77\x88"
fa3bfd
+                         "\x99\xaa\xbb\xcc", 12) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    static const double nums[] = { 1, 2 };
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    double *ptr = alloc_buffer_alloc_array (&buf, double, 2);
fa3bfd
+    TEST_VERIFY_EXIT (ptr != NULL);
fa3bfd
+    TEST_VERIFY (IS_ALIGNED (ptr, double));
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    ptr[0] = nums[0];
fa3bfd
+    ptr[1] = nums[1];
fa3bfd
+    TEST_VERIFY (memcmp (ptr, nums, sizeof (nums)) == 0);
fa3bfd
+  }
fa3bfd
+
fa3bfd
+  /* Verify that padding was not overwritten.  */
fa3bfd
+  TEST_VERIFY (backing[0] == (char) ~pad);
fa3bfd
+  TEST_VERIFY (backing[SIZE + 1] == pad);
fa3bfd
+  free (backing);
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Check that overflow during alignment is handled properly.  */
fa3bfd
+static void
fa3bfd
+test_large_misaligned (void)
fa3bfd
+{
fa3bfd
+  uintptr_t minus1 = -1;
fa3bfd
+  uintptr_t start = minus1 & ~0xfe;
fa3bfd
+  struct alloc_buffer refbuf = alloc_buffer_create ((void *) start, 16);
fa3bfd
+  TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
fa3bfd
+
fa3bfd
+  struct __attribute__ ((aligned (256))) align256
fa3bfd
+  {
fa3bfd
+    int dymmy;
fa3bfd
+  };
fa3bfd
+
fa3bfd
+  {
fa3bfd
+    struct alloc_buffer buf = refbuf;
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc (&buf, struct align256) == NULL);
fa3bfd
+    test_after_failure (buf);
fa3bfd
+  }
fa3bfd
+  for (int count = 0; count < 3; ++count)
fa3bfd
+    {
fa3bfd
+      struct alloc_buffer buf = refbuf;
fa3bfd
+      TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct align256, count)
fa3bfd
+                   == NULL);
fa3bfd
+      test_after_failure (buf);
fa3bfd
+    }
fa3bfd
+}
fa3bfd
+
fa3bfd
+/* Check behavior of large allocations.  */
fa3bfd
+static void
fa3bfd
+test_large (void)
fa3bfd
+{
fa3bfd
+  {
fa3bfd
+    /* Allocation which wraps around.  */
fa3bfd
+    struct alloc_buffer buf = { 1, SIZE_MAX };
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, SIZE_MAX) == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+  }
fa3bfd
+
fa3bfd
+  {
fa3bfd
+    /* Successful very large allocation.  */
fa3bfd
+    struct alloc_buffer buf = { 1, SIZE_MAX };
fa3bfd
+    uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
fa3bfd
+      (&buf, char, SIZE_MAX - 1);
fa3bfd
+    TEST_VERIFY (val == 1);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    test_empty (buf);
fa3bfd
+  }
fa3bfd
+
fa3bfd
+  {
fa3bfd
+    typedef char __attribute__ ((aligned (2))) char2;
fa3bfd
+
fa3bfd
+    /* Overflow in array size computation.   */
fa3bfd
+    struct alloc_buffer buf = { 1, SIZE_MAX };
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, SIZE_MAX - 1) == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+
fa3bfd
+    /* Successful allocation after alignment.  */
fa3bfd
+    buf = (struct alloc_buffer) { 1, SIZE_MAX };
fa3bfd
+    uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
fa3bfd
+      (&buf, char2, SIZE_MAX - 2);
fa3bfd
+    TEST_VERIFY (val == 2);
fa3bfd
+    test_empty (buf);
fa3bfd
+
fa3bfd
+    /* Alignment behavior near the top of the address space.  */
fa3bfd
+    buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
fa3bfd
+    TEST_VERIFY (alloc_buffer_next (&buf, char2) == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+    buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, 0) == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+  }
fa3bfd
+
fa3bfd
+  {
fa3bfd
+    typedef short __attribute__ ((aligned (2))) short2;
fa3bfd
+
fa3bfd
+    /* Test overflow in size computation.  */
fa3bfd
+    struct alloc_buffer buf = { 1, SIZE_MAX };
fa3bfd
+    TEST_VERIFY (alloc_buffer_alloc_array (&buf, short2, SIZE_MAX / 2)
fa3bfd
+                 == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+
fa3bfd
+    /* A slightly smaller array fits within the allocation.  */
fa3bfd
+    buf = (struct alloc_buffer) { 2, SIZE_MAX - 1 };
fa3bfd
+    uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
fa3bfd
+      (&buf, short2, SIZE_MAX / 2 - 1);
fa3bfd
+    TEST_VERIFY (val == 2);
fa3bfd
+    test_empty (buf);
fa3bfd
+  }
fa3bfd
+}
fa3bfd
+
fa3bfd
+static void
fa3bfd
+test_copy_bytes (void)
fa3bfd
+{
fa3bfd
+  char backing[4];
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    alloc_buffer_copy_bytes (&buf, "1", 1);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (alloc_buffer_size (&buf) == 3);
fa3bfd
+    TEST_VERIFY (memcmp (backing, "1@@@", 4) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    alloc_buffer_copy_bytes (&buf, "12", 3);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (alloc_buffer_size (&buf) == 1);
fa3bfd
+    TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    alloc_buffer_copy_bytes (&buf, "1234", 4);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (alloc_buffer_size (&buf) == 0);
fa3bfd
+    TEST_VERIFY (memcmp (backing, "1234", 4) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    alloc_buffer_copy_bytes (&buf, "1234", 5);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    alloc_buffer_copy_bytes (&buf, "1234", -1);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
fa3bfd
+  }
fa3bfd
+}
fa3bfd
+
fa3bfd
+static void
fa3bfd
+test_copy_string (void)
fa3bfd
+{
fa3bfd
+  char backing[4];
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    const char *p = alloc_buffer_copy_string (&buf, "");
fa3bfd
+    TEST_VERIFY (p == backing);
fa3bfd
+    TEST_VERIFY (strcmp (p, "") == 0);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (alloc_buffer_size (&buf) == 3);
fa3bfd
+    TEST_VERIFY (memcmp (backing, "\0@@@", 4) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    const char *p = alloc_buffer_copy_string (&buf, "1");
fa3bfd
+    TEST_VERIFY (p == backing);
fa3bfd
+    TEST_VERIFY (strcmp (p, "1") == 0);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (alloc_buffer_size (&buf) == 2);
fa3bfd
+    TEST_VERIFY (memcmp (backing, "1\0@@", 4) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    const char *p = alloc_buffer_copy_string (&buf, "12");
fa3bfd
+    TEST_VERIFY (p == backing);
fa3bfd
+    TEST_VERIFY (strcmp (p, "12") == 0);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (alloc_buffer_size (&buf) == 1);
fa3bfd
+    TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    const char *p = alloc_buffer_copy_string (&buf, "123");
fa3bfd
+    TEST_VERIFY (p == backing);
fa3bfd
+    TEST_VERIFY (strcmp (p, "123") == 0);
fa3bfd
+    TEST_VERIFY (!alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (alloc_buffer_size (&buf) == 0);
fa3bfd
+    TEST_VERIFY (memcmp (backing, "123", 4) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    TEST_VERIFY (alloc_buffer_copy_string (&buf, "1234") == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
fa3bfd
+  }
fa3bfd
+  {
fa3bfd
+    memset (backing, '@', sizeof (backing));
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
fa3bfd
+    TEST_VERIFY (alloc_buffer_copy_string (&buf, "12345") == NULL);
fa3bfd
+    TEST_VERIFY (alloc_buffer_has_failed (&buf));
fa3bfd
+    TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
fa3bfd
+  }
fa3bfd
+}
fa3bfd
+
fa3bfd
+static int
fa3bfd
+do_test (void)
fa3bfd
+{
fa3bfd
+  test_empty (alloc_buffer_create (NULL, 0));
fa3bfd
+  test_empty (alloc_buffer_create ((char *) "", 0));
fa3bfd
+  test_empty (alloc_buffer_create ((void *) 1, 0));
fa3bfd
+
fa3bfd
+  {
fa3bfd
+    void *ptr = (void *) "";    /* Cannot be freed. */
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_allocate (1, &ptr);
fa3bfd
+    test_size_1 (buf);
fa3bfd
+    free (ptr);                 /* Should have been overwritten.  */
fa3bfd
+  }
fa3bfd
+
fa3bfd
+  {
fa3bfd
+    void *ptr= (void *) "";     /* Cannot be freed.  */
fa3bfd
+    struct alloc_buffer buf = alloc_buffer_allocate (2, &ptr);
fa3bfd
+    test_size_2 (buf);
fa3bfd
+    free (ptr);                 /* Should have been overwritten.  */
fa3bfd
+  }
fa3bfd
+
fa3bfd
+  test_misaligned (0);
fa3bfd
+  test_misaligned (0xc7);
fa3bfd
+  test_misaligned (0xff);
fa3bfd
+
fa3bfd
+  test_large_misaligned ();
fa3bfd
+  test_large ();
fa3bfd
+  test_copy_bytes ();
fa3bfd
+  test_copy_string ();
fa3bfd
+
fa3bfd
+  return 0;
fa3bfd
+}
fa3bfd
+
fa3bfd
+#include <support/test-driver.c>