This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: [PATCH] Allocation buffers for NSS result construction
- From: Adhemerval Zanella <adhemerval dot zanella at linaro dot org>
- To: libc-alpha at sourceware dot org
- Date: Fri, 16 Jun 2017 11:42:34 -0300
- Subject: Re: [PATCH] Allocation buffers for NSS result construction
- Authentication-results: sourceware.org; auth=none
- References: <2526706e-18e7-1c30-84b3-812663c43b98@redhat.com>
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 */
>