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


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

GNU C Library master sources branch fw/out_buffer created. glibc-2.25-98-gddd21a2


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

The branch, fw/out_buffer has been created
        at  ddd21a211369658bc087cc5c56cd577bb8370857 (commit)

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

commit ddd21a211369658bc087cc5c56cd577bb8370857
Author: Florian Weimer <fweimer@redhat.com>
Date:   Fri Mar 10 20:45:25 2017 +0100

    WIP struct out_buffer

diff --git a/include/out_buffer.h b/include/out_buffer.h
new file mode 100644
index 0000000..4d6e15b
--- /dev/null
+++ b/include/out_buffer.h
@@ -0,0 +1,247 @@
+/* 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/>.  */
+
+/* TODOs:
+
+   More documentation, including usage scenarios:
+     (1) NSS code (with struct scratch_buffer), although this style is
+         not a good idea (callee should malloc if caller does not know
+         size).
+     (2) Compute allocation size of a buffer (without overflow
+         checking), allocate out_buffer, overflow checking while
+         writing to the buffer.  Timezone parser (time/tzfile.c)
+         contains a particularly interesting example.
+
+   out_buffer_init should be out_buffer_create and return the struct.
+
+   Add out_buffer_allocate which calls malloc (and returns the struct,
+   to help aliasing analysis).
+
+   Fix invalid pointer handling.  All pointers should point into the
+   buffer.  Use NULL for __OUT_BUFFER_INVALID_POINTER.  Make NULL
+   argument to out_buffer_init invalid, to resolve the
+   out_buffer_get_bytes ambiguity.
+
+   Add byte array copying functions.
+
+   Add string copying functions.
+
+   Maybe add string formatting functions.
+ */
+
+#ifndef _OUT_BUFFER_H
+#define _OUT_BUFFER_H
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/param.h>
+
+struct out_buffer
+{
+  uintptr_t __out_buffer_current;
+  uintptr_t __out_buffer_end;
+};
+
+enum
+  {
+    __OUT_BUFFER_INVALID_POINTER = 1,
+  };
+
+static inline void
+out_buffer_init (struct out_buffer *buf, void *start, size_t size)
+{
+  if (size > 0)
+    {
+      buf->__out_buffer_current = (uintptr_t) start;
+      buf->__out_buffer_end = buf->__out_buffer_current + size;
+    }
+  else
+    {
+      /* Avoid storing __OUT_BUFFER_INVALID_POINTER (marking the
+	 buffer as failed) for a zero-length buffer.  */
+      buf->__out_buffer_current = 0;
+      buf->__out_buffer_end = 0;
+    }
+}
+
+static __always_inline void *
+out_buffer_current (struct out_buffer *buf)
+{
+  return (void *) buf->__out_buffer_current;
+}
+
+/* Internal function.  Return the remaining number of bytes in the
+   buffer.  */
+static __always_inline size_t
+__out_buffer_remaining (const struct out_buffer *buf)
+{
+  return buf->__out_buffer_end - buf->__out_buffer_current;
+}
+
+/* Internal function.  Mark the buffer as failed. */
+static inline void
+out_buffer_mark_failed (struct out_buffer *buf)
+{
+  buf->__out_buffer_current = __OUT_BUFFER_INVALID_POINTER;
+  buf->__out_buffer_end = __OUT_BUFFER_INVALID_POINTER;
+}
+
+/* Return true if the buffer has been marked as failed.  */
+static inline bool
+out_buffer_has_failed (const struct out_buffer *buf)
+{
+  return buf->__out_buffer_current == __OUT_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
+out_buffer_add_byte (struct out_buffer *buf, unsigned char b)
+{
+  if (__glibc_likely (buf->__out_buffer_current < buf->__out_buffer_end))
+    {
+      *(unsigned char *) buf->__out_buffer_current = b;
+      ++buf->__out_buffer_current;
+    }
+  else
+   out_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.  */
+static inline void *
+out_buffer_get_bytes (struct out_buffer *buf, size_t length)
+{
+  if (__glibc_likely (length > 0))
+    {
+      if (length <= __out_buffer_remaining (buf))
+	{
+	  void *result = (void *) buf->__out_buffer_current;
+	  buf->__out_buffer_current += length;
+	  return result;
+	}
+      else
+	{
+	  out_buffer_mark_failed (buf);
+	  return NULL;
+	}
+    }
+  else if (out_buffer_has_failed (buf))
+    return NULL;
+  else
+    /* Non-null pointer to empty array.  */
+    return (void *) __OUT_BUFFER_INVALID_POINTER;
+}
+
+/* Internal function.  Statically assert that the type size is
+   constant and valid.  */
+static __always_inline size_t
+__out_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
+__out_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 *
+__out_buffer_get (struct out_buffer *buf, size_t size, size_t align)
+{
+  if (size == 1 && align == 1)
+    return out_buffer_get_bytes (buf, size);
+
+  size_t current = buf->__out_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->__out_buffer_end) /* Room in buffer.  */
+    {
+      buf->__out_buffer_current = new_current;
+      return (void *) aligned;
+    }
+  else
+    {
+      out_buffer_mark_failed (buf);
+      return NULL;
+    }
+}
+
+/* Obtain a TYPE * pointer to an object in BUF of TYPE.  Consume these
+   bytes from the buffer.  Return NULL and mark the buffer as failed
+   if if there is not enough room in the buffer, or if the buffer has
+   failed before.  */
+#define out_buffer_get(buf, type)                       \
+  ((type *) __out_buffer_get                            \
+   (buf, __out_buffer_assert_size (sizeof (type)),      \
+    __out_buffer_assert_align (__alignof__ (type))))
+
+
+/* Internal function.  Allocate an array.  COUNT_LIMIT is used
+   internally to check for multiplication overflow without a
+   division.  */
+void * __libc_out_buffer_get_array (struct out_buffer *buf,
+				    size_t size, size_t align,
+				    size_t count, size_t count_limit)
+  internal_function;
+libc_hidden_proto (__libc_out_buffer_get_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.  */
+#define out_buffer_get_array(buf, type, count)          \
+  ((type *) __libc_out_buffer_get_array			\
+   (buf, __out_buffer_assert_size (sizeof (type)),      \
+    __out_buffer_assert_align (__alignof__ (type)),     \
+    count, ((size_t) -1) / sizeof (type)))
+
+#endif /* _OUT_BUFFER_H */
diff --git a/malloc/Makefile b/malloc/Makefile
index e93b83b..3aca6fa 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-out_buffer \
 
 tests-static := \
 	 tst-interpose-static-nothread \
@@ -49,7 +50,7 @@ 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 out_buffer_get_array
 
 install-lib := libmcheck.a
 non-lib.a := libmcheck.a
diff --git a/malloc/Versions b/malloc/Versions
index f3c3d8a..779bc3d 100644
--- a/malloc/Versions
+++ b/malloc/Versions
@@ -72,5 +72,8 @@ libc {
     __libc_scratch_buffer_grow;
     __libc_scratch_buffer_grow_preserve;
     __libc_scratch_buffer_set_array_size;
+
+    # struct out_buffer support
+    __libc_out_buffer_get_array;
   }
 }
diff --git a/malloc/out_buffer_get_array.c b/malloc/out_buffer_get_array.c
new file mode 100644
index 0000000..0c09e33
--- /dev/null
+++ b/malloc/out_buffer_get_array.c
@@ -0,0 +1,50 @@
+/* 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 <out_buffer.h>
+
+void *
+internal_function
+__libc_out_buffer_get_array (struct out_buffer *buf,
+                             size_t element_size, size_t align,
+                             size_t count, size_t count_limit)
+{
+  /* Avoid spurious success if count == 0.  */
+  if (out_buffer_has_failed (buf))
+    return NULL;
+
+  size_t current = buf->__out_buffer_current;
+  /* The caller asserts that align is a power of two.  */
+  size_t aligned = (current + align - 1) & ~(align - 1);
+  size_t size = element_size * count;
+  size_t new_current = aligned + size;
+  if (count <= count_limit     /* Multiplication did not overflow.  */
+      && aligned >= current    /* No overflow in align step.  */
+      && new_current >= size   /* No overflow in size computation.  */
+      && new_current <= buf->__out_buffer_end) /* Room in buffer.  */
+    {
+      buf->__out_buffer_current = new_current;
+      return (void *) aligned;
+    }
+  else
+    {
+      out_buffer_mark_failed (buf);
+      return NULL;
+    }
+}
+libc_hidden_def (__libc_out_buffer_get_array)
diff --git a/malloc/tst-out_buffer.c b/malloc/tst-out_buffer.c
new file mode 100644
index 0000000..9139ae1
--- /dev/null
+++ b/malloc/tst-out_buffer.c
@@ -0,0 +1,480 @@
+/* Tests for struct out_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 <out_buffer.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <stdio.h>
+
+/* Return the start pointer of the buffer.  */
+#define START(buf) ((void *) (buf)->__out_buffer_current)
+
+/* Return true if PTR is sufficiently aligned for TYPE.  */
+#define IS_ALIGNED(ptr, type) \
+  ((((uintptr_t) ptr) & (__out_buffer_assert_align (__alignof (type)) -1)) \
+   == 0)
+
+/* Structure with non-power-of-two size.  */
+struct twelve
+{
+  uint32_t buffer[3];
+};
+_Static_assert (sizeof (struct twelve) == 12, "struct twelve");
+
+/* Check for success obtaining empty arrays.  Does not assume the
+   buffer is empty.  */
+static void
+test_empty_array (struct out_buffer refbuf)
+{
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_bytes (&buf, 0) != NULL);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_array (&buf, char, 0) != NULL);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_array (&buf, double, 0) != NULL);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_array (&buf, struct twelve, 0) != NULL);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+  }
+}
+
+/* Test allocation of impossibly large arrays.  */
+static void
+test_impossible_array (struct out_buffer refbuf)
+{
+  printf ("info: %s: current=0x%llx end=0x%llx\n",
+          __func__, (unsigned long long) refbuf.__out_buffer_current,
+          (unsigned long long) refbuf.__out_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];
+      printf ("info: %s: count=%zu\n", __func__, count);
+      {
+        struct out_buffer buf = refbuf;
+        TEST_VERIFY (out_buffer_get_bytes (&buf, count) == NULL);
+        TEST_VERIFY (out_buffer_has_failed (&buf));
+      }
+      {
+        struct out_buffer buf = refbuf;
+        TEST_VERIFY (out_buffer_get_array (&buf, char, count) == NULL);
+        TEST_VERIFY (out_buffer_has_failed (&buf));
+      }
+      {
+        struct out_buffer buf = refbuf;
+        TEST_VERIFY (out_buffer_get_array (&buf, short, count) == NULL);
+        TEST_VERIFY (out_buffer_has_failed (&buf));
+      }
+      {
+        struct out_buffer buf = refbuf;
+        TEST_VERIFY (out_buffer_get_array (&buf, double, count) == NULL);
+        TEST_VERIFY (out_buffer_has_failed (&buf));
+      }
+      {
+        struct out_buffer buf = refbuf;
+        TEST_VERIFY (out_buffer_get_array (&buf, struct twelve, count)
+                     == NULL);
+        TEST_VERIFY (out_buffer_has_failed (&buf));
+      }
+    }
+}
+
+/* Check for failure to obtain anything from a failed buffer.  */
+static void
+test_after_failure (struct out_buffer refbuf)
+{
+  TEST_VERIFY (out_buffer_has_failed (&refbuf));
+  {
+    struct out_buffer buf = refbuf;
+    out_buffer_add_byte (&buf, 17);
+    TEST_VERIFY (out_buffer_has_failed (&buf));
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, char) == NULL);
+    TEST_VERIFY (out_buffer_has_failed (&buf));
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, double) == NULL);
+    TEST_VERIFY (out_buffer_has_failed (&buf));
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, struct twelve) == NULL);
+    TEST_VERIFY (out_buffer_has_failed (&buf));
+  }
+
+  test_impossible_array (refbuf);
+  for (int count = 0; count <= 4; ++count)
+    {
+      {
+        struct out_buffer buf = refbuf;
+        TEST_VERIFY (out_buffer_get_bytes (&buf, count) == NULL);
+        TEST_VERIFY (out_buffer_has_failed (&buf));
+      }
+      {
+        struct out_buffer buf = refbuf;
+        TEST_VERIFY (out_buffer_get_array (&buf, char, count) == NULL);
+        TEST_VERIFY (out_buffer_has_failed (&buf));
+      }
+      {
+        struct out_buffer buf = refbuf;
+        TEST_VERIFY (out_buffer_get_array (&buf, double, count) == NULL);
+        TEST_VERIFY (out_buffer_has_failed (&buf));
+      }
+      {
+        struct out_buffer buf = refbuf;
+        TEST_VERIFY (out_buffer_get_array (&buf, struct twelve, count)
+                     == NULL);
+        TEST_VERIFY (out_buffer_has_failed (&buf));
+      }
+    }
+}
+
+static void
+test_empty (struct out_buffer refbuf)
+{
+  TEST_VERIFY (!out_buffer_has_failed (&refbuf));
+  test_empty_array (refbuf);
+  test_impossible_array (refbuf);
+
+  /* Failure to obtain non-empty objects.  */
+  {
+    struct out_buffer buf = refbuf;
+    out_buffer_add_byte (&buf, 17);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, char) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, double) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, struct twelve) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_array (&buf, char, 1) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_array (&buf, double, 1) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_array (&buf, struct twelve, 1) == NULL);
+    test_after_failure (buf);
+  }
+}
+
+static void
+test_size_1 (struct out_buffer refbuf)
+{
+  TEST_VERIFY (!out_buffer_has_failed (&refbuf));
+  test_empty_array (refbuf);
+  test_impossible_array (refbuf);
+
+  /* Success adding a single byte.  */
+  {
+    struct out_buffer buf = refbuf;
+    out_buffer_add_byte (&buf, 17);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+    test_empty (buf);
+  }
+  TEST_VERIFY (memcmp (START (&refbuf), "\x11", 1) == 0);
+  {
+    struct out_buffer buf = refbuf;
+    signed char *ptr = out_buffer_get (&buf, signed char);
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+    *ptr = 126;
+    test_empty (buf);
+  }
+  TEST_VERIFY (memcmp (START (&refbuf), "\176", 1) == 0);
+  {
+    struct out_buffer buf = refbuf;
+    char *ptr = out_buffer_get_array (&buf, char, 1);
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+    *ptr = (char) 253;
+    test_empty (buf);
+  }
+  TEST_VERIFY (memcmp (START (&refbuf), "\xfd", 1) == 0);
+
+  /* Failure with larger objects.  */
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, short) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, double) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, struct twelve) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_array (&buf, short, 1) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_array (&buf, double, 1) == NULL);
+    test_after_failure (buf);
+  }
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get_array (&buf, struct twelve, 1) == NULL);
+    test_after_failure (buf);
+  }
+}
+
+static void
+test_size_2 (struct out_buffer refbuf)
+{
+  TEST_VERIFY (!out_buffer_has_failed (&refbuf));
+  TEST_VERIFY (IS_ALIGNED (START (&refbuf), short));
+  test_empty_array (refbuf);
+  test_impossible_array (refbuf);
+
+  /* Success adding two bytes.  */
+  {
+    struct out_buffer buf = refbuf;
+    out_buffer_add_byte (&buf, '@');
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+    test_size_1 (buf);
+  }
+  TEST_VERIFY (memcmp (START (&refbuf), "@\xfd", 2) == 0);
+  {
+    struct out_buffer buf = refbuf;
+    signed char *ptr = out_buffer_get (&buf, signed char);
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+    *ptr = 'A';
+    test_size_1 (buf);
+  }
+  TEST_VERIFY (memcmp (START (&refbuf), "A\xfd", 2) == 0);
+  {
+    struct out_buffer buf = refbuf;
+    char *ptr = out_buffer_get_array (&buf, char, 1);
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+    *ptr = 'B';
+    test_size_1 (buf);
+  }
+  TEST_VERIFY (memcmp (START (&refbuf), "B\xfd", 2) == 0);
+  {
+    struct out_buffer buf = refbuf;
+    unsigned short *ptr = out_buffer_get (&buf, unsigned short);
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+    *ptr = htons (0x12f4);
+    test_empty (buf);
+  }
+  TEST_VERIFY (memcmp (START (&refbuf), "\x12\xf4", 2) == 0);
+  {
+    struct out_buffer buf = refbuf;
+    unsigned short *ptr = out_buffer_get_array (&buf, unsigned short, 1);
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+    *ptr = htons (0x13f5);
+    test_empty (buf);
+  }
+  TEST_VERIFY (memcmp (START (&refbuf), "\x13\xf5", 2) == 0);
+  {
+    struct out_buffer buf = refbuf;
+    char *ptr = out_buffer_get_array (&buf, char, 2);
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (!out_buffer_has_failed (&buf));
+    memcpy (ptr, "12", 2);
+    test_empty (buf);
+  }
+  TEST_VERIFY (memcmp (START (&refbuf), "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 out_buffer refbuf;
+  out_buffer_init (&refbuf, backing + 1, SIZE);
+
+  {
+    struct out_buffer buf = refbuf;
+    short *ptr = out_buffer_get_array (&buf, short, SIZE / sizeof (short));
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (IS_ALIGNED (ptr, short));
+    TEST_VERIFY (!out_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 out_buffer buf = refbuf;
+    uint32_t *ptr = out_buffer_get_array
+      (&buf, uint32_t, SIZE / sizeof (uint32_t));
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (IS_ALIGNED (ptr, uint32_t));
+    TEST_VERIFY (!out_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 out_buffer buf = refbuf;
+    struct twelve *ptr = out_buffer_get (&buf, struct twelve);
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
+    TEST_VERIFY (!out_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 out_buffer buf = refbuf;
+    double *ptr = out_buffer_get_array (&buf, double, 2);
+    TEST_VERIFY_EXIT (ptr != NULL);
+    TEST_VERIFY (IS_ALIGNED (ptr, double));
+    TEST_VERIFY (!out_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 out_buffer refbuf;
+  out_buffer_init (&refbuf, (void *) start, 16);
+  TEST_VERIFY (!out_buffer_has_failed (&refbuf));
+
+  struct __attribute__ ((aligned (256))) align256
+  {
+    int dymmy;
+  };
+
+  {
+    struct out_buffer buf = refbuf;
+    TEST_VERIFY (out_buffer_get (&buf, struct align256) == NULL);
+    test_after_failure (buf);
+  }
+  for (int count = 0; count < 3; ++count)
+    {
+      struct out_buffer buf = refbuf;
+      TEST_VERIFY (out_buffer_get_array (&buf, struct align256, count) == NULL);
+      test_after_failure (buf);
+    }
+}
+
+static struct out_buffer
+mkbuffer (void *ptr, size_t len)
+{
+  struct out_buffer buf;
+  out_buffer_init (&buf, ptr, len);
+  return buf;
+}
+
+static int
+do_test (void)
+{
+  test_empty (mkbuffer (NULL, 0));
+  test_empty (mkbuffer ((char *) "", 0));
+  test_empty (mkbuffer ((void *) 1, 0));
+
+  {
+    char *backing = xmalloc (1);
+    struct out_buffer buf;
+    out_buffer_init (&buf, backing, 1);
+    test_size_1 (buf);
+    free (backing);
+  }
+
+  {
+    char *backing = xmalloc (2);
+    struct out_buffer buf;
+    out_buffer_init (&buf, backing, 2);
+    test_size_2 (buf);
+    free (backing);
+  }
+
+  test_misaligned (0);
+  test_misaligned (0xc7);
+  test_misaligned (0xff);
+
+  test_large_misaligned ();
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/resolv/Versions b/resolv/Versions
index e561bce..9e77270 100644
--- a/resolv/Versions
+++ b/resolv/Versions
@@ -76,7 +76,7 @@ libresolv {
   }
   GLIBC_PRIVATE {
     # Needed in libnss_dns.
-    __ns_name_unpack; __ns_name_ntop;
+    __ns_name_unpack; __ns_name_ntop; __ns_name_ntop_buffer;
     __ns_get16; __ns_get32;
     __libc_res_nquery; __libc_res_nsearch;
   }
diff --git a/resolv/ns_name.c b/resolv/ns_name.c
index 0d76fe5..a681506 100644
--- a/resolv/ns_name.c
+++ b/resolv/ns_name.c
@@ -19,6 +19,7 @@
 
 #include <netinet/in.h>
 #include <arpa/nameser.h>
+#include <resolv/resolv-internal.h>
 
 #include <errno.h>
 #include <resolv.h>
@@ -26,6 +27,7 @@
 #include <ctype.h>
 #include <stdlib.h>
 #include <limits.h>
+#include <out_buffer.h>
 
 # define SPRINTF(x) ((size_t)sprintf x)
 
@@ -44,90 +46,90 @@ static int		labellen(const u_char *);
 
 /* Public. */
 
-/*%
- *	Convert an encoded domain name to printable ascii as per RFC1035.
-
- * 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)
+char *
+internal_function
+__ns_name_ntop_buffer (struct out_buffer *pdst,
+		       const unsigned char *src)
 {
-	const u_char *cp;
-	char *dn, *eom;
-	u_char c;
-	u_int n;
-	int l;
-
-	cp = src;
-	dn = dst;
-	eom = dst + dstsiz;
+  /* Make copy to help with aliasing analysis.  */
+  struct out_buffer dst = *pdst;
+  bool first = true;
+  while (true)
+    {
+      unsigned char n = *src;
+      ++src;
+      if (n == 0)
+	break;
+      if (n > 63)
+	{
+	  /* Some kind of compression pointer. */
+	  out_buffer_mark_failed (&dst);
+	  break;
+	}
 
-	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 (first)
+	first = false;
+      else
+	out_buffer_add_byte (&dst, '.');
+
+      for (int i = 0; i < n; ++i)
+	{
+	  unsigned char c = *src;
+	  ++src;
+	  if (special(c))
+	    {
+	      char *p = out_buffer_get_bytes (&dst, 2);
+	      if (p == NULL)
+		{
+		  out_buffer_mark_failed (&dst);
+		  break;
 		}
-		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);
+	      p[0] = '\\';
+	      p[1] = c;
+	    }
+	  else if (!printable(c))
+	    {
+	      char *p = out_buffer_get_bytes (&dst, 4);
+	      if (p == NULL)
+		{
+		  out_buffer_mark_failed (pdst);
+		  break;
 		}
-		*dn++ = '.';
+	      p[0] = '\\';
+	      p[1] = '0' + (c / 100);
+	      p[2] = '0' + ((c % 100) / 10);
+	      p[3] = '0' + (c % 10);
+	    }
+	  else
+	    out_buffer_add_byte (&dst, c);
 	}
-	if (dn >= eom) {
-		__set_errno (EMSGSIZE);
-		return (-1);
-	}
-	*dn++ = '\0';
-	return (dn - dst);
+    }
+  if (first)
+    /* Root domain.  */
+    out_buffer_add_byte (&dst, '.');
+  out_buffer_add_byte (&dst, '\0');
+  if (out_buffer_has_failed (&dst))
+    {
+      out_buffer_mark_failed (pdst);
+      return NULL;
+    }
+  char *start = out_buffer_current (pdst);
+  *pdst = dst;
+  return start;
+}
+libresolv_hidden_def (__ns_name_ntop_buffer)
+
+int
+ns_name_ntop (const u_char *src, char *dst, size_t dstsiz)
+{
+  struct out_buffer buf;
+  out_buffer_init (&buf, dst, dstsiz);
+  if (__ns_name_ntop_buffer (&buf, src) == NULL)
+    {
+      __set_errno (EMSGSIZE);
+      return -1;
+    }
+  return out_buffer_current (&buf) - (void *) dst;
 }
 libresolv_hidden_def (ns_name_ntop)
 strong_alias (ns_name_ntop, __ns_name_ntop)
diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c
index 45f7f18..1358710 100644
--- a/resolv/nss_dns/dns-network.c
+++ b/resolv/nss_dns/dns-network.c
@@ -66,6 +66,9 @@
 
 #include "nsswitch.h"
 #include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <out_buffer.h>
+#include <resolv/resolv-internal.h>
 
 /* Maximum number of aliases we allow.  */
 #define MAX_NR_ALIASES	48
@@ -92,17 +95,10 @@ typedef union querybuf
   u_char buf[MAXPACKET];
 } querybuf;
 
-/* These functions are defined in res_comp.c.  */
-#define NS_MAXCDNAME	255	/* maximum compressed domain name */
-extern int __ns_name_ntop (const u_char *, char *, size_t) __THROW;
-extern int __ns_name_unpack (const u_char *, const u_char *,
-			     const u_char *, u_char *, size_t) __THROW;
-
-
 /* 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 out_buffer,
+				    int *errnop, int *h_errnop,
 				    lookup_method net_i);
 
 
@@ -140,7 +136,9 @@ _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,
+  struct out_buffer outbuf;
+  out_buffer_init (&outbuf, buffer, buflen);
+  status = getanswer_r (net_buffer.buf, anslen, result, outbuf,
 			errnop, herrnop, BYNAME);
   if (net_buffer.buf != orig_net_buffer)
     free (net_buffer.buf);
@@ -217,7 +215,9 @@ _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,
+  struct out_buffer outbuf;
+  out_buffer_init (&outbuf, buffer, buflen);
+  status = getanswer_r (net_buffer.buf, anslen, result, outbuf,
 			errnop, herrnop, BYADDR);
   if (net_buffer.buf != orig_net_buffer)
     free (net_buffer.buf);
@@ -237,7 +237,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 out_buffer buf, int *errnop, int *h_errnop,
 	     lookup_method net_i)
 {
   /*
@@ -254,16 +254,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 = out_buffer_get_array (&buf, char *, MAX_NR_ALIASES);
+  if (__glibc_unlikely (aliases == NULL))
     {
       /* The buffer is too small.  */
     too_small:
@@ -271,21 +264,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];
 
@@ -318,32 +305,17 @@ 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 = dn_expand (answer->buf, end_of_message, cp, bp, linebuflen);
       int type, class;
-
-      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)
+      int n = __ns_name_unpack (answer->buf, end_of_message, cp,
+				packtmp, sizeof packtmp);
+      if (n == -1)
 	break;
-      cp += n;
 
+      cp += n;
       if (end_of_message - cp < 10)
 	{
 	  __set_h_errno (NO_RECOVERY);
@@ -365,7 +337,9 @@ 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 = __ns_name_ntop_buffer (&buf, packtmp)) == NULL)
 	    {
 	      if (errno == EMSGSIZE)
 		goto too_small;
@@ -373,7 +347,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.
@@ -382,12 +356,9 @@ 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])
+         if (alias_count + 1 < MAX_NR_ALIASES)
            {
-             *alias_pointer++ = bp;
-             n = strlen (bp) + 1;
-             bp += n;
-             linebuflen -= n;
+             aliases[alias_count++] = alias;
              result->n_addrtype = class == C_IN ? AF_INET : AF_UNSPEC;
              ++have_answer;
            }
@@ -399,7 +370,7 @@ getanswer_r (const querybuf *answer, int anslen, struct netent *result,
 
   if (have_answer)
     {
-      *alias_pointer = NULL;
+      aliases[alias_count] = NULL;
       switch (net_i)
 	{
 	case BYADDR:
diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h
index 99fc17c..0453906 100644
--- a/resolv/resolv-internal.h
+++ b/resolv/resolv-internal.h
@@ -32,4 +32,16 @@ res_use_inet6 (void)
   return _res.options & DEPRECATED_RES_USE_INET6;
 }
 
+struct out_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 sets errno to EMSGSIZE).  */
+char *__ns_name_ntop_buffer (struct out_buffer *, const u_char *)
+  __THROW internal_function;
+libresolv_hidden_proto (__ns_name_ntop_buffer)
+
+int __ns_name_unpack (const u_char *, const u_char *,
+                      const u_char *, u_char *, size_t) __THROW;
+
 #endif  /* _RESOLV_INTERNAL_H */

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


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


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