aboutsummaryrefslogtreecommitdiff
path: root/malloc
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2017-06-21 22:43:57 +0200
committerFlorian Weimer <fweimer@redhat.com>2017-06-21 22:43:57 +0200
commit4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec (patch)
treebfc9688f34dfc949e8da77d0ec05b684ff64e86f /malloc
parent11ffcacb64a939c10cfc713746b8ec88837f5c4a (diff)
downloadglibc-4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec.tar
glibc-4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec.tar.gz
glibc-4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec.tar.bz2
glibc-4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec.zip
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).
Diffstat (limited to 'malloc')
-rw-r--r--malloc/Makefile6
-rw-r--r--malloc/Versions8
-rw-r--r--malloc/alloc_buffer_alloc_array.c47
-rw-r--r--malloc/alloc_buffer_allocate.c36
-rw-r--r--malloc/alloc_buffer_copy_bytes.c34
-rw-r--r--malloc/alloc_buffer_copy_string.c30
-rw-r--r--malloc/alloc_buffer_create_failure.c31
-rw-r--r--malloc/tst-alloc_buffer.c665
8 files changed, 856 insertions, 1 deletions
diff --git a/malloc/Makefile b/malloc/Makefile
index 14c13f17dc..b50de7cd6c 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -33,6 +33,7 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
tst-mallocfork2 \
tst-interpose-nothread \
tst-interpose-thread \
+ tst-alloc_buffer \
tests-static := \
tst-interpose-static-nothread \
@@ -63,6 +64,11 @@ routines = malloc morecore mcheck mtrace obstack reallocarray \
dynarray_finalize \
dynarray_resize \
dynarray_resize_clear \
+ alloc_buffer_alloc_array \
+ alloc_buffer_allocate \
+ alloc_buffer_copy_bytes \
+ alloc_buffer_copy_string \
+ alloc_buffer_create_failure \
install-lib := libmcheck.a
non-lib.a := libmcheck.a
diff --git a/malloc/Versions b/malloc/Versions
index 5b543069b3..2357cff3da 100644
--- a/malloc/Versions
+++ b/malloc/Versions
@@ -76,7 +76,6 @@ libc {
__libc_scratch_buffer_grow_preserve;
__libc_scratch_buffer_set_array_size;
-
# Internal name for reallocarray
__libc_reallocarray;
@@ -86,5 +85,12 @@ libc {
__libc_dynarray_finalize;
__libc_dynarray_resize;
__libc_dynarray_resize_clear;
+
+ # struct alloc_buffer support
+ __libc_alloc_buffer_alloc_array;
+ __libc_alloc_buffer_allocate;
+ __libc_alloc_buffer_copy_bytes;
+ __libc_alloc_buffer_copy_string;
+ __libc_alloc_buffer_create_failure;
}
}
diff --git a/malloc/alloc_buffer_alloc_array.c b/malloc/alloc_buffer_alloc_array.c
new file mode 100644
index 0000000000..68e14da8dd
--- /dev/null
+++ b/malloc/alloc_buffer_alloc_array.c
@@ -0,0 +1,47 @@
+/* Array allocation from a fixed-size buffer.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <alloc_buffer.h>
+#include <malloc-internal.h>
+#include <libc-pointer-arith.h>
+
+void *
+__libc_alloc_buffer_alloc_array (struct alloc_buffer *buf, size_t element_size,
+ size_t align, size_t count)
+{
+ size_t current = buf->__alloc_buffer_current;
+ /* The caller asserts that align is a power of two. */
+ size_t aligned = ALIGN_UP (current, align);
+ size_t size;
+ bool overflow = check_mul_overflow_size_t (element_size, count, &size);
+ size_t new_current = aligned + size;
+ if (!overflow /* Multiplication did not overflow. */
+ && aligned >= current /* No overflow in align step. */
+ && new_current >= size /* No overflow in size computation. */
+ && new_current <= buf->__alloc_buffer_end) /* Room in buffer. */
+ {
+ buf->__alloc_buffer_current = new_current;
+ return (void *) aligned;
+ }
+ else
+ {
+ alloc_buffer_mark_failed (buf);
+ return NULL;
+ }
+}
+libc_hidden_def (__libc_alloc_buffer_alloc_array)
diff --git a/malloc/alloc_buffer_allocate.c b/malloc/alloc_buffer_allocate.c
new file mode 100644
index 0000000000..cbde72b842
--- /dev/null
+++ b/malloc/alloc_buffer_allocate.c
@@ -0,0 +1,36 @@
+/* Allocate a fixed-size allocation buffer using malloc.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <alloc_buffer.h>
+
+#include <stdlib.h>
+
+struct alloc_buffer
+__libc_alloc_buffer_allocate (size_t size, void **pptr)
+{
+ *pptr = malloc (size);
+ if (*pptr == NULL)
+ return (struct alloc_buffer)
+ {
+ .__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER,
+ .__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER
+ };
+ else
+ return alloc_buffer_create (*pptr, size);
+}
+libc_hidden_def (__libc_alloc_buffer_allocate)
diff --git a/malloc/alloc_buffer_copy_bytes.c b/malloc/alloc_buffer_copy_bytes.c
new file mode 100644
index 0000000000..66196f1520
--- /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 0000000000..77c0023d51
--- /dev/null
+++ b/malloc/alloc_buffer_copy_string.c
@@ -0,0 +1,30 @@
+/* Copy a string into the allocation buffer.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <alloc_buffer.h>
+
+#include <string.h>
+
+/* This function works on a copy of the buffer object, so that it can
+ remain non-addressable in the caller. */
+struct alloc_buffer
+__libc_alloc_buffer_copy_string (struct alloc_buffer buf, const char *src)
+{
+ return __libc_alloc_buffer_copy_bytes (buf, src, strlen (src) + 1);
+}
+libc_hidden_def (__libc_alloc_buffer_copy_string)
diff --git a/malloc/alloc_buffer_create_failure.c b/malloc/alloc_buffer_create_failure.c
new file mode 100644
index 0000000000..5ffba22f7b
--- /dev/null
+++ b/malloc/alloc_buffer_create_failure.c
@@ -0,0 +1,31 @@
+/* Terminate the process as the result of an invalid allocation buffer.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <alloc_buffer.h>
+#include <stdio.h>
+
+void
+__libc_alloc_buffer_create_failure (void *start, size_t size)
+{
+ char buf[200];
+ __snprintf (buf, sizeof (buf), "Fatal glibc error: "
+ "invalid allocation buffer of size %zu\n",
+ size);
+ __libc_fatal (buf);
+}
+libc_hidden_def (__libc_alloc_buffer_create_failure)
diff --git a/malloc/tst-alloc_buffer.c b/malloc/tst-alloc_buffer.c
new file mode 100644
index 0000000000..1c143999c7
--- /dev/null
+++ b/malloc/tst-alloc_buffer.c
@@ -0,0 +1,665 @@
+/* Tests for struct alloc_buffer.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <arpa/inet.h>
+#include <alloc_buffer.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+
+/* Return true if PTR is sufficiently aligned for TYPE. */
+#define IS_ALIGNED(ptr, type) \
+ ((((uintptr_t) ptr) & (__alloc_buffer_assert_align (__alignof (type)) - 1)) \
+ == 0)
+
+/* Structure with non-power-of-two size. */
+struct twelve
+{
+ uint32_t buffer[3] __attribute__ ((aligned (4)));
+};
+_Static_assert (sizeof (struct twelve) == 12, "struct twelve");
+_Static_assert (__alignof__ (struct twelve) == 4, "struct twelve");
+
+/* Check for success obtaining empty arrays. Does not assume the
+ buffer is empty. */
+static void
+test_empty_array (struct alloc_buffer refbuf)
+{
+ bool refbuf_failed = alloc_buffer_has_failed (&refbuf);
+ if (test_verbose)
+ printf ("info: %s: current=0x%llx end=0x%llx refbuf_failed=%d\n",
+ __func__, (unsigned long long) refbuf.__alloc_buffer_current,
+ (unsigned long long) refbuf.__alloc_buffer_end, refbuf_failed);
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY ((alloc_buffer_alloc_bytes (&buf, 0) == NULL)
+ == refbuf_failed);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY ((alloc_buffer_alloc_array (&buf, char, 0) == NULL)
+ == refbuf_failed);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
+ }
+ /* The following tests can fail due to the need for aligning the
+ returned pointer. */
+ {
+ struct alloc_buffer buf = refbuf;
+ bool expect_failure = refbuf_failed
+ || !IS_ALIGNED (alloc_buffer_next (&buf, void), double);
+ double *ptr = alloc_buffer_alloc_array (&buf, double, 0);
+ TEST_VERIFY (IS_ALIGNED (ptr, double));
+ TEST_VERIFY ((ptr == NULL) == expect_failure);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ bool expect_failure = refbuf_failed
+ || !IS_ALIGNED (alloc_buffer_next (&buf, void), struct twelve);
+ struct twelve *ptr = alloc_buffer_alloc_array (&buf, struct twelve, 0);
+ TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
+ TEST_VERIFY ((ptr == NULL) == expect_failure);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
+ }
+}
+
+/* Test allocation of impossibly large arrays. */
+static void
+test_impossible_array (struct alloc_buffer refbuf)
+{
+ if (test_verbose)
+ printf ("info: %s: current=0x%llx end=0x%llx\n",
+ __func__, (unsigned long long) refbuf.__alloc_buffer_current,
+ (unsigned long long) refbuf.__alloc_buffer_end);
+ static const size_t counts[] =
+ { SIZE_MAX, SIZE_MAX - 1, SIZE_MAX - 2, SIZE_MAX - 3, SIZE_MAX - 4,
+ SIZE_MAX / 2, SIZE_MAX / 2 + 1, SIZE_MAX / 2 - 1, 0};
+
+ for (int i = 0; counts[i] != 0; ++i)
+ {
+ size_t count = counts[i];
+ if (test_verbose)
+ printf ("info: %s: count=%zu\n", __func__, count);
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
+ == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ }
+}
+
+/* Check for failure to obtain anything from a failed buffer. */
+static void
+test_after_failure (struct alloc_buffer refbuf)
+{
+ if (test_verbose)
+ printf ("info: %s: current=0x%llx end=0x%llx\n",
+ __func__, (unsigned long long) refbuf.__alloc_buffer_current,
+ (unsigned long long) refbuf.__alloc_buffer_end);
+ TEST_VERIFY (alloc_buffer_has_failed (&refbuf));
+ {
+ struct alloc_buffer buf = refbuf;
+ alloc_buffer_add_byte (&buf, 17);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+
+ test_impossible_array (refbuf);
+ for (int count = 0; count <= 4; ++count)
+ {
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
+ == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ }
+}
+
+static void
+test_empty (struct alloc_buffer refbuf)
+{
+ TEST_VERIFY (alloc_buffer_size (&refbuf) == 0);
+ if (alloc_buffer_next (&refbuf, void) != NULL)
+ TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
+ test_empty_array (refbuf);
+ test_impossible_array (refbuf);
+
+ /* Failure to obtain non-empty objects. */
+ {
+ struct alloc_buffer buf = refbuf;
+ alloc_buffer_add_byte (&buf, 17);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, 1) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
+ test_after_failure (buf);
+ }
+}
+
+static void
+test_size_1 (struct alloc_buffer refbuf)
+{
+ TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
+ TEST_VERIFY (alloc_buffer_size (&refbuf) == 1);
+ test_empty_array (refbuf);
+ test_impossible_array (refbuf);
+
+ /* Success adding a single byte. */
+ {
+ struct alloc_buffer buf = refbuf;
+ alloc_buffer_add_byte (&buf, 17);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x11", 1) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ signed char *ptr = alloc_buffer_alloc (&buf, signed char);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = 126;
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\176", 1) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = (char) 253;
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\xfd", 1) == 0);
+
+ /* Failure with larger objects. */
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, short) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, 1) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
+ test_after_failure (buf);
+ }
+}
+
+static void
+test_size_2 (struct alloc_buffer refbuf)
+{
+ TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
+ TEST_VERIFY (alloc_buffer_size (&refbuf) == 2);
+ TEST_VERIFY (IS_ALIGNED (alloc_buffer_next (&refbuf, void), short));
+ test_empty_array (refbuf);
+ test_impossible_array (refbuf);
+
+ /* Success adding two bytes. */
+ {
+ struct alloc_buffer buf = refbuf;
+ alloc_buffer_add_byte (&buf, '@');
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ test_size_1 (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "@\xfd", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ signed char *ptr = alloc_buffer_alloc (&buf, signed char);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = 'A';
+ test_size_1 (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "A\xfd", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = 'B';
+ test_size_1 (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "B\xfd", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ unsigned short *ptr = alloc_buffer_alloc (&buf, unsigned short);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = htons (0x12f4);
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x12\xf4", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ unsigned short *ptr = alloc_buffer_alloc_array (&buf, unsigned short, 1);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = htons (0x13f5);
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x13\xf5", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ char *ptr = alloc_buffer_alloc_array (&buf, char, 2);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ memcpy (ptr, "12", 2);
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "12", 2) == 0);
+}
+
+static void
+test_misaligned (char pad)
+{
+ enum { SIZE = 23 };
+ char *backing = xmalloc (SIZE + 2);
+ backing[0] = ~pad;
+ backing[SIZE + 1] = pad;
+ struct alloc_buffer refbuf = alloc_buffer_create (backing + 1, SIZE);
+
+ {
+ struct alloc_buffer buf = refbuf;
+ short *ptr = alloc_buffer_alloc_array (&buf, short, SIZE / sizeof (short));
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, short));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ for (int i = 0; i < SIZE / sizeof (short); ++i)
+ ptr[i] = htons (0xff01 + i);
+ TEST_VERIFY (memcmp (ptr,
+ "\xff\x01\xff\x02\xff\x03\xff\x04"
+ "\xff\x05\xff\x06\xff\x07\xff\x08"
+ "\xff\x09\xff\x0a\xff\x0b", 22) == 0);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ uint32_t *ptr = alloc_buffer_alloc_array
+ (&buf, uint32_t, SIZE / sizeof (uint32_t));
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, uint32_t));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ for (int i = 0; i < SIZE / sizeof (uint32_t); ++i)
+ ptr[i] = htonl (0xf1e2d301 + i);
+ TEST_VERIFY (memcmp (ptr,
+ "\xf1\xe2\xd3\x01\xf1\xe2\xd3\x02"
+ "\xf1\xe2\xd3\x03\xf1\xe2\xd3\x04"
+ "\xf1\xe2\xd3\x05", 20) == 0);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ struct twelve *ptr = alloc_buffer_alloc (&buf, struct twelve);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ ptr->buffer[0] = htonl (0x11223344);
+ ptr->buffer[1] = htonl (0x55667788);
+ ptr->buffer[2] = htonl (0x99aabbcc);
+ TEST_VERIFY (memcmp (ptr,
+ "\x11\x22\x33\x44"
+ "\x55\x66\x77\x88"
+ "\x99\xaa\xbb\xcc", 12) == 0);
+ }
+ {
+ static const double nums[] = { 1, 2 };
+ struct alloc_buffer buf = refbuf;
+ double *ptr = alloc_buffer_alloc_array (&buf, double, 2);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, double));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ ptr[0] = nums[0];
+ ptr[1] = nums[1];
+ TEST_VERIFY (memcmp (ptr, nums, sizeof (nums)) == 0);
+ }
+
+ /* Verify that padding was not overwritten. */
+ TEST_VERIFY (backing[0] == ~pad);
+ TEST_VERIFY (backing[SIZE + 1] == pad);
+ free (backing);
+}
+
+/* Check that overflow during alignment is handled properly. */
+static void
+test_large_misaligned (void)
+{
+ uintptr_t minus1 = -1;
+ uintptr_t start = minus1 & ~0xfe;
+ struct alloc_buffer refbuf = alloc_buffer_create ((void *) start, 16);
+ TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
+
+ struct __attribute__ ((aligned (256))) align256
+ {
+ int dymmy;
+ };
+
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, struct align256) == NULL);
+ test_after_failure (buf);
+ }
+ for (int count = 0; count < 3; ++count)
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct align256, count)
+ == NULL);
+ test_after_failure (buf);
+ }
+}
+
+/* Check behavior of large allocations. */
+static void
+test_large (void)
+{
+ {
+ /* Allocation which wraps around. */
+ struct alloc_buffer buf = { 1, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, SIZE_MAX) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+
+ {
+ /* Successful very large allocation. */
+ struct alloc_buffer buf = { 1, SIZE_MAX };
+ uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
+ (&buf, char, SIZE_MAX - 1);
+ TEST_VERIFY (val == 1);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ test_empty (buf);
+ }
+
+ {
+ typedef char __attribute__ ((aligned (2))) char2;
+
+ /* Overflow in array size computation. */
+ struct alloc_buffer buf = { 1, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, SIZE_MAX - 1) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+
+ /* Successful allocation after alignment. */
+ buf = (struct alloc_buffer) { 1, SIZE_MAX };
+ uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
+ (&buf, char2, SIZE_MAX - 2);
+ TEST_VERIFY (val == 2);
+ test_empty (buf);
+
+ /* Alignment behavior near the top of the address space. */
+ buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_next (&buf, char2) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, 0) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+
+ {
+ typedef short __attribute__ ((aligned (2))) short2;
+
+ /* Test overflow in size computation. */
+ struct alloc_buffer buf = { 1, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, short2, SIZE_MAX / 2)
+ == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+
+ /* A slightly smaller array fits within the allocation. */
+ buf = (struct alloc_buffer) { 2, SIZE_MAX - 1 };
+ uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
+ (&buf, short2, SIZE_MAX / 2 - 1);
+ TEST_VERIFY (val == 2);
+ test_empty (buf);
+ }
+}
+
+static void
+test_copy_bytes (void)
+{
+ char backing[4];
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "1", 1);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 3);
+ TEST_VERIFY (memcmp (backing, "1@@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "12", 3);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 1);
+ TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "1234", 4);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 0);
+ TEST_VERIFY (memcmp (backing, "1234", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "1234", 5);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "1234", -1);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
+ }
+}
+
+static void
+test_copy_string (void)
+{
+ char backing[4];
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ const char *p = alloc_buffer_copy_string (&buf, "");
+ TEST_VERIFY (p == backing);
+ TEST_VERIFY (strcmp (p, "") == 0);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 3);
+ TEST_VERIFY (memcmp (backing, "\0@@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ const char *p = alloc_buffer_copy_string (&buf, "1");
+ TEST_VERIFY (p == backing);
+ TEST_VERIFY (strcmp (p, "1") == 0);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 2);
+ TEST_VERIFY (memcmp (backing, "1\0@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ const char *p = alloc_buffer_copy_string (&buf, "12");
+ TEST_VERIFY (p == backing);
+ TEST_VERIFY (strcmp (p, "12") == 0);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 1);
+ TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ const char *p = alloc_buffer_copy_string (&buf, "123");
+ TEST_VERIFY (p == backing);
+ TEST_VERIFY (strcmp (p, "123") == 0);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 0);
+ TEST_VERIFY (memcmp (backing, "123", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ TEST_VERIFY (alloc_buffer_copy_string (&buf, "1234") == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ TEST_VERIFY (alloc_buffer_copy_string (&buf, "12345") == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
+ }
+}
+
+static int
+do_test (void)
+{
+ test_empty (alloc_buffer_create (NULL, 0));
+ test_empty (alloc_buffer_create ((char *) "", 0));
+ test_empty (alloc_buffer_create ((void *) 1, 0));
+
+ {
+ void *ptr = (void *) ""; /* Cannot be freed. */
+ struct alloc_buffer buf = alloc_buffer_allocate (1, &ptr);
+ test_size_1 (buf);
+ free (ptr); /* Should have been overwritten. */
+ }
+
+ {
+ void *ptr= (void *) ""; /* Cannot be freed. */
+ struct alloc_buffer buf = alloc_buffer_allocate (2, &ptr);
+ test_size_2 (buf);
+ free (ptr); /* Should have been overwritten. */
+ }
+
+ test_misaligned (0);
+ test_misaligned (0xc7);
+ test_misaligned (0xff);
+
+ test_large_misaligned ();
+ test_large ();
+ test_copy_bytes ();
+ test_copy_string ();
+
+ return 0;
+}
+
+#include <support/test-driver.c>