You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1307 lines
46 KiB
1307 lines
46 KiB
Backport of the following upstream commits: |
|
|
|
commit d54bb9b1d3fd25779fba2c403003c5097ba9af73 |
|
Author: Tulio Magno Quites Machado Filho <tuliom@linux.vnet.ibm.com> |
|
Date: Mon Jun 26 09:55:41 2017 -0300 |
|
|
|
Prevent an implicit int promotion in malloc/tst-alloc_buffer.c |
|
|
|
commit 4dd8e7c0ce5ecc7f65e33e60ad2f717b31de32ec |
|
Author: Florian Weimer <fweimer@redhat.com> |
|
Date: Wed Jun 21 22:43:57 2017 +0200 |
|
|
|
Implement allocation buffers for internal use |
|
|
|
diff --git a/include/alloc_buffer.h b/include/alloc_buffer.h |
|
new file mode 100644 |
|
index 0000000000000000..d668a60d66c5b076 |
|
--- /dev/null |
|
+++ b/include/alloc_buffer.h |
|
@@ -0,0 +1,367 @@ |
|
+/* 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 receive 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 = ...; |
|
+ |
|
+ void *heap_ptr; |
|
+ struct alloc_buffer buf = alloc_buffer_allocate |
|
+ (double_array_size * sizeof (double) + int_array_size * sizeof (int), |
|
+ &heap_ptr); |
|
+ _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. |
|
+ ... |
|
+ free (heap_ptr); |
|
+ |
|
+ The advantage over manual coding is that the computation of the |
|
+ allocation size does not need an overflow check. In case of an |
|
+ overflow, one of the subsequent allocations from the buffer will |
|
+ fail. The initial size computation is checked for consistency at |
|
+ run time, too. */ |
|
+ |
|
+#ifndef _ALLOC_BUFFER_H |
|
+#define _ALLOC_BUFFER_H |
|
+ |
|
+#include <inttypes.h> |
|
+#include <stdbool.h> |
|
+#include <stddef.h> |
|
+#include <stdlib.h> |
|
+#include <sys/param.h> |
|
+ |
|
+/* struct alloc_buffer objects refer to a region of bytes in memory of a |
|
+ fixed size. The functions below can be used to allocate single |
|
+ objects and arrays from this memory region, or write to its end. |
|
+ On allocation failure (or if an attempt to write beyond the end of |
|
+ the buffer with one of the copy functions), the buffer enters a |
|
+ failed state. |
|
+ |
|
+ struct alloc_buffer objects can be copied. The backing buffer will |
|
+ be shared, but the current write position will be independent. |
|
+ |
|
+ Conceptually, the memory region consists of a current write pointer |
|
+ and a limit, beyond which the write pointer cannot move. */ |
|
+struct alloc_buffer |
|
+{ |
|
+ /* uintptr_t is used here to simplify the alignment code, and to |
|
+ avoid issues undefined subtractions if the buffer covers more |
|
+ than half of the address space (which would result in differences |
|
+ which could not be represented as a ptrdiff_t value). */ |
|
+ uintptr_t __alloc_buffer_current; |
|
+ uintptr_t __alloc_buffer_end; |
|
+}; |
|
+ |
|
+enum |
|
+ { |
|
+ /* The value for the __alloc_buffer_current member which marks the |
|
+ buffer as invalid (together with a zero-length buffer). */ |
|
+ __ALLOC_BUFFER_INVALID_POINTER = 0, |
|
+ }; |
|
+ |
|
+/* Internal function. Terminate the process using __libc_fatal. */ |
|
+void __libc_alloc_buffer_create_failure (void *start, size_t size); |
|
+ |
|
+/* 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) |
|
+{ |
|
+ uintptr_t current = (uintptr_t) start; |
|
+ uintptr_t end = (uintptr_t) start + size; |
|
+ if (end < current) |
|
+ __libc_alloc_buffer_create_failure (start, size); |
|
+ return (struct alloc_buffer) { current, end }; |
|
+} |
|
+ |
|
+/* Internal function. See alloc_buffer_allocate below. */ |
|
+struct alloc_buffer __libc_alloc_buffer_allocate (size_t size, void **pptr) |
|
+ __attribute__ ((nonnull (2))); |
|
+ |
|
+/* Allocate a buffer of SIZE bytes using malloc. The returned buffer |
|
+ is in a failed state if malloc fails. *PPTR points to the start of |
|
+ the buffer and can be used to free it later, after the returned |
|
+ buffer has been freed. */ |
|
+static __always_inline __attribute__ ((nonnull (2))) |
|
+struct alloc_buffer alloc_buffer_allocate (size_t size, void **pptr) |
|
+{ |
|
+ return __libc_alloc_buffer_allocate (size, pptr); |
|
+} |
|
+ |
|
+/* Mark the buffer as failed. */ |
|
+static inline void __attribute__ ((nonnull (1))) |
|
+alloc_buffer_mark_failed (struct alloc_buffer *buf) |
|
+{ |
|
+ buf->__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER; |
|
+ buf->__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER; |
|
+} |
|
+ |
|
+/* Return the remaining number of bytes in the buffer. */ |
|
+static __always_inline __attribute__ ((nonnull (1))) 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 __attribute__ ((nonnull (1))) |
|
+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 __attribute__ ((nonnull (1))) |
|
+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 __attribute__ ((nonnull (1))) 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 __attribute__ ((nonnull (1))) void * |
|
+__alloc_buffer_alloc (struct alloc_buffer *buf, size_t size, size_t align) |
|
+{ |
|
+ if (size == 1 && align == 1) |
|
+ return alloc_buffer_alloc_bytes (buf, size); |
|
+ |
|
+ size_t current = buf->__alloc_buffer_current; |
|
+ size_t aligned = roundup (current, align); |
|
+ size_t new_current = aligned + size; |
|
+ if (aligned >= current /* No overflow in align step. */ |
|
+ && new_current >= size /* No overflow in size computation. */ |
|
+ && new_current <= buf->__alloc_buffer_end) /* Room in buffer. */ |
|
+ { |
|
+ buf->__alloc_buffer_current = new_current; |
|
+ return (void *) aligned; |
|
+ } |
|
+ else |
|
+ { |
|
+ alloc_buffer_mark_failed (buf); |
|
+ return NULL; |
|
+ } |
|
+} |
|
+ |
|
+/* Obtain a TYPE * pointer to an object in BUF of TYPE. Consume these |
|
+ bytes from the buffer. Return NULL and mark the buffer as failed |
|
+ if if there is not enough room in the buffer, or if the buffer has |
|
+ failed before. */ |
|
+#define alloc_buffer_alloc(buf, type) \ |
|
+ ((type *) __alloc_buffer_alloc \ |
|
+ (buf, __alloc_buffer_assert_size (sizeof (type)), \ |
|
+ __alloc_buffer_assert_align (__alignof__ (type)))) |
|
+ |
|
+/* Internal function. Obtain a pointer to an object which is |
|
+ subsequently added. */ |
|
+static inline const __attribute__ ((nonnull (1))) 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) |
|
+ __attribute__ ((nonnull (1))); |
|
+ |
|
+/* 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) |
|
+ __attribute__ ((nonnull (2))); |
|
+ |
|
+/* 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 __attribute__ ((nonnull (1, 2))) 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 *) |
|
+ __attribute__ ((nonnull (2))); |
|
+ |
|
+/* 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 __attribute__ ((nonnull (1, 2))) 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; |
|
+} |
|
+ |
|
+#ifndef _ISOMAC |
|
+libc_hidden_proto (__libc_alloc_buffer_alloc_array) |
|
+libc_hidden_proto (__libc_alloc_buffer_allocate) |
|
+libc_hidden_proto (__libc_alloc_buffer_copy_bytes) |
|
+libc_hidden_proto (__libc_alloc_buffer_copy_string) |
|
+libc_hidden_proto (__libc_alloc_buffer_create_failure) |
|
+#endif |
|
+ |
|
+#endif /* _ALLOC_BUFFER_H */ |
|
diff --git a/malloc/Makefile b/malloc/Makefile |
|
index 63fc3291dcc4077a..db4b1f921d56da32 100644 |
|
--- a/malloc/Makefile |
|
+++ b/malloc/Makefile |
|
@@ -37,6 +37,7 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \ |
|
tst-dynarray \ |
|
tst-dynarray-fail \ |
|
tst-dynarray-at-fail \ |
|
+ tst-alloc_buffer \ |
|
|
|
tests-static := \ |
|
tst-interpose-static-nothread \ |
|
@@ -55,6 +56,11 @@ routines = malloc morecore mcheck mtrace obstack \ |
|
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 |
|
diff --git a/malloc/Versions b/malloc/Versions |
|
index 16f9dab418a4e3f6..0b7b690bbbdd1c2c 100644 |
|
--- a/malloc/Versions |
|
+++ b/malloc/Versions |
|
@@ -79,5 +79,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 0000000000000000..68e14da8dd4e545c |
|
--- /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 0000000000000000..cbde72b842f0b5d1 |
|
--- /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 0000000000000000..66196f1520c6fc84 |
|
--- /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 0000000000000000..77c0023d510b43be |
|
--- /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 0000000000000000..5ffba22f7b1e0dc5 |
|
--- /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 0000000000000000..9b2bd2046a12c0f2 |
|
--- /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] == (char) ~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>
|
|
|