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]

Re: [PATCH] Allocation buffers for NSS result construction



On 22/04/2017 16:47, Florian Weimer wrote:
> I'm proposing the attached patch to reorganize the way result buffers for the NSS lookup of functions are filled.
> 
> Comments?
> 
> Thanks,
> Florian
> 
> alloc_buffer.patch
> 
> 
> 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.

I would prefer to split the patch in two, one for the alloc_buffer adition
and another one for its use in NSS result construction.

> 
> 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.  */

A new line maybe?

> +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
> +    };
> +}

Should we add an overflow test for sanity tests?

> +
> +/* Internal function.  See alloc_buffer_allocate below.  */
> +struct alloc_buffer __libc_alloc_buffer_allocate (size_t size);
> +libc_hidden_proto (__libc_alloc_buffer_allocate)

I am getting this while trying to build malloc/tst-alloc_buffer.o:

../include/alloc_buffer.h:125:1: error: return type defaults to ‘int’ [-Werror=implicit-int]
 libc_hidden_proto (__libc_alloc_buffer_allocate)

This is due 7c3018f9 (Suppress internal declarations for most of the
testsuite) which suppress libc_hidden_proto for testsuite.  I think we
need either to make them empty macros in this case (move their definition
outside the _ISOMAC) or move the libc_hidden_proto to another internal
header.

> +
> +/* 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);
> +}

No need to cast to void *.

> +
> +/* 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;
> +    }
> +}

Maybe use/add the check_add_wrapv_size_t from my char_array patch (also
for the other occurences)?

> +
> +/* 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))))

I would prefer to use a static inline function to type check, but we
can like with it (same for other occurencies).

> +
> +/* 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
> +}

This was pushed already on malloc/malloc-internal.h.

> +
> +#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);

Maybe use ALIGN_UP?

> +  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;

I am not sure how safe is convert a ptrdiff_t to a int at this point.  Does
it worth add a check for it?

>  }
>  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]