This is the mail archive of the libc-alpha@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]

[PATCH] Allocation buffers for NSS result construction


I'm proposing the attached patch to reorganize the way result buffers for the NSS lookup of functions are filled.

Comments?

Thanks,
Florian
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).

The commit converts the "network" handling in nss_dns to allocation
buffers because this is something for which test coverage already
exists.

2017-04-22  Florian Weimer  <fweimer@redhat.com>

	* malloc/Makefile (tests): Add tst-alloc_buffer.
	(routines): Add alloc_buffer_alloc_array, alloc_buffer_allocate,
	alloc_buffer_copy_bytes.
	* malloc/Versions (__libc_alloc_buffer_alloc_array)
	(__libc_alloc_buffer_allocate, __libc_alloc_buffer_copy_bytes):
	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/tst-alloc_buffer.c: Likewise.
	* resolv/Versions (__ns_name_ntop_buffer)
	(__ns_name_unpack_buffer): Export as GLIBC_PRIVATE.
	* resolv/ns_name.c (__ns_name_ntop_buffer): New function.
	(ns_name_ntop): Implement using __ns_name_ntop_buffer.
	(__ns_name_unpack_buffer): New function.
	(ns_name_unpack): Implement using __ns_name_unpack_buffer.
	* resolv/nss_dns/dns-network.c (_nss_dns_getnetbyname_r)
	(_nss_dns_getnetbyaddr_r): Adjust call to getanswer_r.
	(getanswer_r): Store result in an allocation buffer.  Use
	__ns_name_ntop_buffer instead of ns_name_ntop.  Remove superfluous
	call to res_dnok because ns_name_ntop output is always printable
	ASCII.

diff --git a/include/alloc_buffer.h b/include/alloc_buffer.h
new file mode 100644
index 0000000..386e49c
--- /dev/null
+++ b/include/alloc_buffer.h
@@ -0,0 +1,383 @@
+/* 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 recieve 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 = ...;
+
+     struct alloc_buffer buf = alloc_buffer_allocate
+       (double_array_size * sizeof (double) + int_array_size * sizeof (int));
+     _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.
+
+   The advantage over manual coding is that the computation of the
+   allocation size does not need an overflow check.  The 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,
+  };
+
+/* 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)
+{
+  return (struct alloc_buffer)
+    {
+      .__alloc_buffer_current = (uintptr_t) start,
+      .__alloc_buffer_end = (uintptr_t) start + size
+    };
+}
+
+/* Internal function.  See alloc_buffer_allocate below.  */
+struct alloc_buffer __libc_alloc_buffer_allocate (size_t size);
+libc_hidden_proto (__libc_alloc_buffer_allocate)
+
+/* Allocate a buffer of SIZE bytes using malloc.  The returned buffer
+   is in a failed state if malloc fails.  */
+static __always_inline
+struct alloc_buffer alloc_buffer_allocate (size_t size)
+{
+  return __libc_alloc_buffer_allocate (size);
+}
+
+/* Mark the buffer as failed.  */
+static inline void
+alloc_buffer_mark_failed (struct alloc_buffer *buf)
+{
+  buf->__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER;
+  buf->__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER;
+}
+
+/* Deallocate the buffer and mark it as failed.  The buffer must be in
+   its initial state; if data has been added to it, an invocation of
+   alloc_buffer_free results in undefined behavior.  This means that
+   callers need to make a copy of the buffer if they need to free it
+   later.  Deallocating a failed buffer is allowed; it has no
+   effect.  */
+static inline void
+alloc_buffer_free (struct alloc_buffer *buf)
+{
+  _Static_assert (__ALLOC_BUFFER_INVALID_POINTER == 0,
+		  "free can be called on __ALLOC_BUFFER_INVALID_POINTER");
+  free ((void *) buf->__alloc_buffer_current);
+  alloc_buffer_mark_failed (buf);
+}
+
+/* Return the remaining number of bytes in the buffer.  */
+static __always_inline 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
+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
+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 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 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 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);
+libc_hidden_proto (__libc_alloc_buffer_alloc_array)
+
+/* 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);
+libc_hidden_proto (__libc_alloc_buffer_copy_bytes)
+
+/* 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 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 *);
+libc_hidden_proto (__libc_alloc_buffer_copy_string)
+
+/* 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 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;
+}
+
+/* Internal function.  Set *RESULT to LEFT * RIGHT.  Return true if
+   the result overflowed.  */
+static inline bool
+__check_mul_overflow_size_t (size_t left, size_t right, size_t *result)
+{
+#if __GNUC__ >= 5
+  return __builtin_mul_overflow (left, right, result);
+#else
+  /* size_t is unsigned so the behavior on overflow is defined.  */
+  *result = left * right;
+  size_t half_size_t = ((size_t) 1) << (8 * sizeof (size_t) / 2);
+  if (__glibc_unlikely ((left | right) >= half_size_t))
+    {
+      if (__glibc_unlikely (right != 0 && *result / right != left))
+        return true;
+    }
+  return false;
+#endif
+}
+
+#endif /* _ALLOC_BUFFER_H */
diff --git a/malloc/Makefile b/malloc/Makefile
index e93b83b..2f7bb5a 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 \
@@ -49,7 +50,11 @@ test-srcs = tst-mtrace
 
 routines = malloc morecore mcheck mtrace obstack \
   scratch_buffer_grow scratch_buffer_grow_preserve \
-  scratch_buffer_set_array_size
+  scratch_buffer_set_array_size \
+  alloc_buffer_alloc_array \
+  alloc_buffer_allocate \
+  alloc_buffer_copy_bytes  \
+  alloc_buffer_copy_string \
 
 install-lib := libmcheck.a
 non-lib.a := libmcheck.a
diff --git a/malloc/Versions b/malloc/Versions
index e34ab17..361550b 100644
--- a/malloc/Versions
+++ b/malloc/Versions
@@ -74,5 +74,11 @@ libc {
     __libc_scratch_buffer_grow;
     __libc_scratch_buffer_grow_preserve;
     __libc_scratch_buffer_set_array_size;
+
+    # struct alloc_buffer support
+    __libc_alloc_buffer_alloc_array;
+    __libc_alloc_buffer_allocate;
+    __libc_alloc_buffer_copy_bytes;
+    __libc_alloc_buffer_copy_string;
   }
 }
diff --git a/malloc/alloc_buffer_alloc_array.c b/malloc/alloc_buffer_alloc_array.c
new file mode 100644
index 0000000..0172029
--- /dev/null
+++ b/malloc/alloc_buffer_alloc_array.c
@@ -0,0 +1,45 @@
+/* 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>
+
+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 = (current + align - 1) & ~(align - 1);
+  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..a9fd3f1
--- /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 *ptr = malloc (size);
+  if (ptr == NULL)
+    return (struct alloc_buffer)
+      {
+        .__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER,
+        .__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER
+      };
+  else
+    return alloc_buffer_create (ptr, 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/tst-alloc_buffer.c b/malloc/tst-alloc_buffer.c
new file mode 100644
index 0000000..362dae2
--- /dev/null
+++ b/malloc/tst-alloc_buffer.c
@@ -0,0 +1,663 @@
+/* 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));
+
+  {
+    struct alloc_buffer buf = alloc_buffer_allocate (1);
+    test_size_1 (buf);
+    alloc_buffer_free (&buf);
+  }
+
+  {
+    struct alloc_buffer buf = alloc_buffer_allocate (2);
+    test_size_2 (buf);
+    alloc_buffer_free (&buf);
+  }
+
+  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>
diff --git a/resolv/Versions b/resolv/Versions
index e561bce..aea43d4 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -79,6 +79,7 @@ libresolv {
     __ns_name_unpack; __ns_name_ntop;
     __ns_get16; __ns_get32;
     __libc_res_nquery; __libc_res_nsearch;
+    __ns_name_unpack_buffer; __ns_name_ntop_buffer;
   }
 }
 
diff --git a/resolv/ns_name.c b/resolv/ns_name.c
index 08a75e2..4cbb07d 100644
--- a/resolv/ns_name.c
+++ b/resolv/ns_name.c
@@ -1,3 +1,21 @@
+/* DNS name processing functions.
+ * Copyright (C) 1999-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/>.  */
+
 /*
  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
  * Copyright (c) 1996,1999 by Internet Software Consortium.
@@ -19,6 +37,7 @@
 
 #include <netinet/in.h>
 #include <arpa/nameser.h>
+#include <resolv/resolv-internal.h>
 
 #include <errno.h>
 #include <resolv.h>
@@ -26,6 +45,7 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <limits.h>
+#include <alloc_buffer.h>
 
 # define SPRINTF(x) ((size_t)sprintf x)
 
@@ -44,90 +64,94 @@ static int		labellen(const u_char *);
 
 /* Public. */
 
-/*%
- *	Convert an encoded domain name to printable ascii as per RFC1035.
+const char *
+__ns_name_ntop_buffer (struct alloc_buffer *pdst,
+		       const unsigned char *src)
+{
+  /* Make copy to help with aliasing analysis.  */
+  struct alloc_buffer dst = *pdst;
+  bool first = true;
+  while (true)
+    {
+      unsigned char n = *src;
+      ++src;
+      if (n == 0)
+	/* End of domain name.  */
+	break;
+      if (n > 63)
+	{
+	  /* Some kind of compression pointer.  This means that the
+	     name has not been unpacked.  */
+	  alloc_buffer_mark_failed (&dst);
+	  break;
+	}
+
+      /* Separate subsequent labels from their predecessor.  */
+      if (first)
+	first = false;
+      else
+	alloc_buffer_add_byte (&dst, '.');
+
+      for (int i = 0; i < n; ++i)
+	{
+	  unsigned char c = *src;
+	  ++src;
+	  if (special (c))
+	    {
+	      /* Non-decimal escape.  */
+	      char *p = alloc_buffer_alloc_bytes (&dst, 2);
+	      if (p == NULL)
+		{
+		  alloc_buffer_mark_failed (&dst);
+		  break;
+		}
+	      p[0] = '\\';
+	      p[1] = c;
+	    }
+	  else if (!printable (c))
+	    {
+	      /* Decimal escape.  */
+	      char *p = alloc_buffer_alloc_bytes (&dst, 4);
+	      if (p == NULL)
+		{
+		  alloc_buffer_mark_failed (pdst);
+		  break;
+		}
+	      p[0] = '\\';
+	      p[1] = '0' + (c / 100);
+	      p[2] = '0' + ((c % 100) / 10);
+	      p[3] = '0' + (c % 10);
+	    }
+	  else
+	    /* Regular character.  */
+	    alloc_buffer_add_byte (&dst, c);
+	}
+    }
+  if (first)
+    /* Root domain.  */
+    alloc_buffer_add_byte (&dst, '.');
+  alloc_buffer_add_byte (&dst, '\0');
+  if (alloc_buffer_has_failed (&dst))
+    {
+      alloc_buffer_mark_failed (pdst);
+      return NULL;
+    }
+  const char *start = alloc_buffer_next (&dst, char);
+  *pdst = dst;
+  return start;
+}
+libresolv_hidden_def (__ns_name_ntop_buffer)
 
- * return:
- *\li	Number of bytes written to buffer, or -1 (with errno set)
- *
- * notes:
- *\li	The root is returned as "."
- *\li	All other domains are returned in non absolute form
- */
 int
-ns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
+ns_name_ntop (const u_char *src, char *dst, size_t dstsiz)
 {
-	const u_char *cp;
-	char *dn, *eom;
-	u_char c;
-	u_int n;
-	int l;
-
-	cp = src;
-	dn = dst;
-	eom = dst + dstsiz;
-
-	while ((n = *cp++) != 0) {
-		if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
-			/* Some kind of compression pointer. */
-			__set_errno (EMSGSIZE);
-			return (-1);
-		}
-		if (dn != dst) {
-			if (dn >= eom) {
-				__set_errno (EMSGSIZE);
-				return (-1);
-			}
-			*dn++ = '.';
-		}
-		if ((l = labellen(cp - 1)) < 0) {
-			__set_errno (EMSGSIZE);
-			return(-1);
-		}
-		if (dn + l >= eom) {
-			__set_errno (EMSGSIZE);
-			return (-1);
-		}
-		for ((void)NULL; l > 0; l--) {
-			c = *cp++;
-			if (special(c)) {
-				if (dn + 1 >= eom) {
-					__set_errno (EMSGSIZE);
-					return (-1);
-				}
-				*dn++ = '\\';
-				*dn++ = (char)c;
-			} else if (!printable(c)) {
-				if (dn + 3 >= eom) {
-					__set_errno (EMSGSIZE);
-					return (-1);
-				}
-				*dn++ = '\\';
-				*dn++ = digits[c / 100];
-				*dn++ = digits[(c % 100) / 10];
-				*dn++ = digits[c % 10];
-			} else {
-				if (dn >= eom) {
-					__set_errno (EMSGSIZE);
-					return (-1);
-				}
-				*dn++ = (char)c;
-			}
-		}
-	}
-	if (dn == dst) {
-		if (dn >= eom) {
-			__set_errno (EMSGSIZE);
-			return (-1);
-		}
-		*dn++ = '.';
-	}
-	if (dn >= eom) {
-		__set_errno (EMSGSIZE);
-		return (-1);
-	}
-	*dn++ = '\0';
-	return (dn - dst);
+  struct alloc_buffer buf = alloc_buffer_create (dst, dstsiz);
+  if (__ns_name_ntop_buffer (&buf, src) == NULL)
+    {
+      __set_errno (EMSGSIZE);
+      return -1;
+    }
+  return alloc_buffer_next (&buf, void) - (const void *) dst;
 }
 libresolv_hidden_def (ns_name_ntop)
 strong_alias (ns_name_ntop, __ns_name_ntop)
@@ -301,6 +325,101 @@ ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz)
 	return (dn - dst);
 }
 
+size_t
+__ns_name_unpack_buffer (const unsigned char *msg, const unsigned char *eom,
+			 const unsigned char *src, struct alloc_buffer *pdst)
+{
+  const unsigned char *initial_src = src;
+  size_t consumed = 0;
+  struct alloc_buffer dst = *pdst;
+  if (src < msg || src >= eom)
+    alloc_buffer_mark_failed (&dst);
+  else
+    {
+      size_t packet_size = eom - msg;
+      /* Count the number of bytes processed by the loop below.  If we
+	 have covered the whole packet, this means we have a
+	 compression loop.  */
+      size_t processed = 0;
+      while (true)
+	{
+	  if (src == eom)
+	    {
+	      /* Missing NUL byte at end of domain name.  */
+	      alloc_buffer_mark_failed (&dst);
+	      break;
+	    }
+
+	  unsigned char b = *src;
+	  ++src;
+	  if (b == 0)
+	    /* End of domain name.  */
+	    {
+	      alloc_buffer_add_byte (&dst, '\0');
+	      if (consumed == 0)
+		consumed = src - initial_src;
+	      break;
+	    }
+	  else if (b <= 63)
+	    {
+	      /* Regular label.  */
+	      size_t remaining = eom - src;
+	      if (b > remaining)
+		{
+		  /* Input domain name is truncated.  */
+		  alloc_buffer_mark_failed (&dst);
+		  break;
+		}
+	      /* Include label length in copy.  */
+	      alloc_buffer_copy_bytes (&dst, src - 1, 1 + b);
+	      src += b;
+	      processed += 1 + b;
+	    }
+	  else if ((b & NS_CMPRSFLGS) == NS_CMPRSFLGS && src < eom)
+	    {
+	      /* Compression reference.  */
+	      unsigned char b2 = *src;
+	      unsigned offset = (b - NS_CMPRSFLGS) * 256 + b2;
+	      if (offset >= packet_size)
+		{
+		  /* Out-of-range compression reference.  */
+		  alloc_buffer_mark_failed (&dst);
+		  break;
+		}
+
+	      /* Record the position after the first compression
+		 reference.  */
+	      if (consumed == 0)
+		consumed = src + 1 - initial_src;
+
+	      /* Seek to the new position in the packet.  */
+	      src = msg + offset;
+
+	      /* Compression loop detection.  Account for the two
+		 bytes in the compression reference.  */
+	      processed += 2;
+	      if (processed >= packet_size)
+		{
+		  /* There is a compression loop.  */
+		  alloc_buffer_mark_failed (&dst);
+		  break;
+		}
+	    }
+	  else
+	    {
+	      /* Invalid label length, or truncated compression
+		 reference.  */
+	      alloc_buffer_mark_failed (&dst);
+	      break;
+	    }
+	}
+    }
+  if (alloc_buffer_has_failed (&dst))
+    consumed = 0;
+  *pdst = dst;
+  return consumed;
+}
+
 /*%
  *	Unpack a domain name from a message, source may be compressed.
  *
@@ -311,73 +430,14 @@ int
 ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
 	       u_char *dst, size_t dstsiz)
 {
-	const u_char *srcp, *dstlim;
-	u_char *dstp;
-	int n, len, checked, l;
-
-	len = -1;
-	checked = 0;
-	dstp = dst;
-	srcp = src;
-	dstlim = dst + dstsiz;
-	if (srcp < msg || srcp >= eom) {
-		__set_errno (EMSGSIZE);
-		return (-1);
-	}
-	/* Fetch next label in domain name. */
-	while ((n = *srcp++) != 0) {
-		/* Check for indirection. */
-		switch (n & NS_CMPRSFLGS) {
-		case 0:
-			/* Limit checks. */
-			if ((l = labellen(srcp - 1)) < 0) {
-				__set_errno (EMSGSIZE);
-				return(-1);
-			}
-			if (dstp + l + 1 >= dstlim || srcp + l >= eom) {
-				__set_errno (EMSGSIZE);
-				return (-1);
-			}
-			checked += l + 1;
-			*dstp++ = n;
-			memcpy(dstp, srcp, l);
-			dstp += l;
-			srcp += l;
-			break;
-
-		case NS_CMPRSFLGS:
-			if (srcp >= eom) {
-				__set_errno (EMSGSIZE);
-				return (-1);
-			}
-			if (len < 0)
-				len = srcp - src + 1;
-			srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
-			if (srcp < msg || srcp >= eom) {  /*%< Out of range. */
-				__set_errno (EMSGSIZE);
-				return (-1);
-			}
-			checked += 2;
-			/*
-			 * Check for loops in the compressed name;
-			 * if we've looked at the whole message,
-			 * there must be a loop.
-			 */
-			if (checked >= eom - msg) {
-				__set_errno (EMSGSIZE);
-				return (-1);
-			}
-			break;
-
-		default:
-			__set_errno (EMSGSIZE);
-			return (-1);			/*%< flag error */
-		}
-	}
-	*dstp = '\0';
-	if (len < 0)
-		len = srcp - src;
-	return (len);
+  struct alloc_buffer buf = alloc_buffer_create (dst, dstsiz);
+  size_t consumed = __ns_name_unpack_buffer (msg, eom, src, &buf);
+  if (alloc_buffer_has_failed (&buf) || consumed > INT_MAX)
+    {
+      __set_errno (EMSGSIZE);
+      return -1;
+    }
+  return consumed;
 }
 libresolv_hidden_def (ns_name_unpack)
 strong_alias (ns_name_unpack, __ns_name_unpack)
diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c
index 2be72d3..3c0ed82 100644
--- a/resolv/nss_dns/dns-network.c
+++ b/resolv/nss_dns/dns-network.c
@@ -67,6 +67,8 @@
 #include "nsswitch.h"
 #include <arpa/inet.h>
 #include <arpa/nameser.h>
+#include <alloc_buffer.h>
+#include <resolv/resolv-internal.h>
 
 /* Maximum number of aliases we allow.  */
 #define MAX_NR_ALIASES	48
@@ -95,8 +97,8 @@ typedef union querybuf
 
 /* Prototypes for local functions.  */
 static enum nss_status getanswer_r (const querybuf *answer, int anslen,
-				    struct netent *result, char *buffer,
-				    size_t buflen, int *errnop, int *h_errnop,
+				    struct netent *result, struct alloc_buffer,
+				    int *errnop, int *h_errnop,
 				    lookup_method net_i);
 
 
@@ -134,7 +136,8 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
 	? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
     }
 
-  status = getanswer_r (net_buffer.buf, anslen, result, buffer, buflen,
+  status = getanswer_r (net_buffer.buf, anslen, result,
+			alloc_buffer_create (buffer, buflen),
 			errnop, herrnop, BYNAME);
   if (net_buffer.buf != orig_net_buffer)
     free (net_buffer.buf);
@@ -211,7 +214,8 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
 	? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND;
     }
 
-  status = getanswer_r (net_buffer.buf, anslen, result, buffer, buflen,
+  status = getanswer_r (net_buffer.buf, anslen, result,
+			alloc_buffer_create (buffer, buflen),
 			errnop, herrnop, BYADDR);
   if (net_buffer.buf != orig_net_buffer)
     free (net_buffer.buf);
@@ -231,7 +235,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
 
 static enum nss_status
 getanswer_r (const querybuf *answer, int anslen, struct netent *result,
-	     char *buffer, size_t buflen, int *errnop, int *h_errnop,
+	     struct alloc_buffer buf, int *errnop, int *h_errnop,
 	     lookup_method net_i)
 {
   /*
@@ -248,16 +252,9 @@ getanswer_r (const querybuf *answer, int anslen, struct netent *result,
    *                 | Additional | RRs holding additional information
    *                 +------------+
    */
-  struct net_data
-  {
-    char *aliases[MAX_NR_ALIASES];
-    char linebuffer[0];
-  } *net_data;
 
-  uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct net_data);
-  buffer += pad;
-
-  if (__glibc_unlikely (buflen < sizeof (*net_data) + pad))
+  char **aliases = alloc_buffer_alloc_array (&buf, char *, MAX_NR_ALIASES);
+  if (__glibc_unlikely (aliases == NULL))
     {
       /* The buffer is too small.  */
     too_small:
@@ -265,22 +262,15 @@ getanswer_r (const querybuf *answer, int anslen, struct netent *result,
       *h_errnop = NETDB_INTERNAL;
       return NSS_STATUS_TRYAGAIN;
     }
-  buflen -= pad;
+  int alias_count = 0;
 
-  net_data = (struct net_data *) buffer;
-  int linebuflen = buflen - offsetof (struct net_data, linebuffer);
-  if (buflen - offsetof (struct net_data, linebuffer) != linebuflen)
-    linebuflen = INT_MAX;
   const unsigned char *end_of_message = &answer->buf[anslen];
   const HEADER *header_pointer = &answer->hdr;
   /* #/records in the answer section.  */
   int answer_count =  ntohs (header_pointer->ancount);
   /* #/entries in the question section.  */
   int question_count = ntohs (header_pointer->qdcount);
-  char *bp = net_data->linebuffer;
   const unsigned char *cp = &answer->buf[HFIXEDSZ];
-  char **alias_pointer;
-  int have_answer;
   u_char packtmp[NS_MAXCDNAME];
 
   if (question_count == 0)
@@ -312,29 +302,14 @@ getanswer_r (const querybuf *answer, int anslen, struct netent *result,
       cp += n + QFIXEDSZ;
     }
 
-  alias_pointer = result->n_aliases = &net_data->aliases[0];
-  *alias_pointer = NULL;
-  have_answer = 0;
-
   while (--answer_count >= 0 && cp < end_of_message)
     {
       int n = __ns_name_unpack (answer->buf, end_of_message, cp,
 				packtmp, sizeof packtmp);
-      if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
-	{
-	  if (errno == EMSGSIZE)
-	    goto too_small;
-
-	  n = -1;
-	}
-
-      if (n > 0 && bp[0] == '.')
-	bp[0] = '\0';
-
-      if (n < 0 || res_dnok (bp) == 0)
+      if (n == -1)
 	break;
+
       cp += n;
-
       if (end_of_message - cp < 10)
 	{
 	  __set_h_errno (NO_RECOVERY);
@@ -357,7 +332,10 @@ getanswer_r (const querybuf *answer, int anslen, struct netent *result,
 	{
 	  n = __ns_name_unpack (answer->buf, end_of_message, cp,
 				packtmp, sizeof packtmp);
-	  if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1)
+	  char *alias = NULL;
+	  if (n != -1
+	      && ((alias = (char *) __ns_name_ntop_buffer (&buf, packtmp))
+		  == NULL))
 	    {
 	      if (errno == EMSGSIZE)
 		goto too_small;
@@ -365,7 +343,7 @@ getanswer_r (const querybuf *answer, int anslen, struct netent *result,
 	      n = -1;
 	    }
 
-	  if (n < 0 || !res_hnok (bp))
+	  if (n < 0 || !res_hnok (alias))
 	    {
 	      /* XXX What does this mean?  The original form from bind
 		 returns NULL. Incrementing cp has no effect in any case.
@@ -374,35 +352,33 @@ getanswer_r (const querybuf *answer, int anslen, struct netent *result,
 	      return NSS_STATUS_UNAVAIL;
 	    }
 	  cp += rdatalen;
-         if (alias_pointer + 2 < &net_data->aliases[MAX_NR_ALIASES])
-           {
-             *alias_pointer++ = bp;
-             n = strlen (bp) + 1;
-             bp += n;
-             linebuflen -= n;
-             result->n_addrtype = class == C_IN ? AF_INET : AF_UNSPEC;
-             ++have_answer;
-           }
+	  if (alias_count + 1 < MAX_NR_ALIASES)
+	    {
+	      aliases[alias_count++] = alias;
+	      result->n_addrtype = class == C_IN ? AF_INET : AF_UNSPEC;
+	    }
 	}
       else
 	/* Skip over unknown record data.  */
 	cp += rdatalen;
     }
+  aliases[alias_count] = NULL;
 
-  if (have_answer)
+  if (alias_count > 0)
     {
-      *alias_pointer = NULL;
       switch (net_i)
 	{
 	case BYADDR:
-	  result->n_name = *result->n_aliases++;
+	  /* Use the first alias as the name.  */
+	  result->n_name = aliases[0];
+	  result->n_aliases = aliases + 1;
 	  result->n_net = 0L;
 	  return NSS_STATUS_SUCCESS;
 
 	case BYNAME:
 	  {
 	    char **ap;
-	    for (ap = result->n_aliases; *ap != NULL; ++ap)
+	    for (ap = aliases; *ap != NULL; ++ap)
 	      {
 		/* Check each alias name for being of the forms:
 		   4.3.2.1.in-addr.arpa		= net 1.2.3.4
@@ -455,6 +431,8 @@ getanswer_r (const querybuf *answer, int anslen, struct netent *result,
 		       2. This is not the droid we are looking for.  */
 		    if (!isdigit (*p) && !strcasecmp (p, "in-addr.arpa"))
 		      {
+			result->n_name = aliases[0];
+			result->n_aliases = aliases;
 			result->n_net = val;
 			return NSS_STATUS_SUCCESS;
 		      }
diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h
index 0d69ce1..f735a6b 100644
--- a/resolv/resolv-internal.h
+++ b/resolv/resolv-internal.h
@@ -56,4 +56,24 @@ enum
 int __res_nopt (res_state, int n0, unsigned char *buf, int buflen,
                 int anslen) attribute_hidden;
 
+struct alloc_buffer;
+
+/* Convert the expanded domain name at SRC from wire format to text
+   format.  Use storage in *DST.  Return a pointer to data in *DST, or
+   NULL on error (and put the allocation buffer into the failed
+   state).  */
+const char *__ns_name_ntop_buffer (struct alloc_buffer *, const u_char *)
+  __THROW;
+libresolv_hidden_proto (__ns_name_ntop_buffer)
+
+/* Decompress the domain name at SRC (within the DNS packet extending
+   from MSG to EOM) into the allocation buffer.  Returns the number of
+   bytes consumed from the input, or 0 on failure.  On failure, put
+   the allocation buffer into the failed state.  */
+size_t __ns_name_unpack_buffer (const unsigned char *msg,
+                                const unsigned char *eom,
+                                const unsigned char *src,
+                                struct alloc_buffer *) __THROW;
+libresolv_hidden_proto (__ns_name_unpack_buffer)
+
 #endif  /* _RESOLV_INTERNAL_H */

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