This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GNU C Library master sources branch master updated. glibc-2.25-547-g4dd8e7c


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, master has been updated
       via  4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec (commit)
      from  11ffcacb64a939c10cfc713746b8ec88837f5c4a (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec

commit 4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec
Author: Florian Weimer <fweimer@redhat.com>
Date:   Wed Jun 21 22:43:57 2017 +0200

    Implement allocation buffers for internal use
    
    This commit adds fixed-size allocation buffers.  The primary use
    case is in NSS modules, where dynamically sized data is stored
    in a fixed-size buffer provided by the caller.
    
    Other uses include a replacement of mempcpy cascades (which is
    safer due to the size checking inherent to allocation buffers).

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

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                            |   17 +
 include/alloc_buffer.h               |  367 +++++++++++++++++++
 malloc/Makefile                      |    6 +
 malloc/Versions                      |    8 +-
 malloc/alloc_buffer_alloc_array.c    |   47 +++
 malloc/alloc_buffer_allocate.c       |   36 ++
 malloc/alloc_buffer_copy_bytes.c     |   34 ++
 malloc/alloc_buffer_copy_string.c    |   30 ++
 malloc/alloc_buffer_create_failure.c |   31 ++
 malloc/tst-alloc_buffer.c            |  665 ++++++++++++++++++++++++++++++++++
 10 files changed, 1240 insertions(+), 1 deletions(-)
 create mode 100644 include/alloc_buffer.h
 create mode 100644 malloc/alloc_buffer_alloc_array.c
 create mode 100644 malloc/alloc_buffer_allocate.c
 create mode 100644 malloc/alloc_buffer_copy_bytes.c
 create mode 100644 malloc/alloc_buffer_copy_string.c
 create mode 100644 malloc/alloc_buffer_create_failure.c
 create mode 100644 malloc/tst-alloc_buffer.c


hooks/post-receive
-- 
GNU C Library master sources


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]