diff options
author | Florian Weimer <fweimer@redhat.com> | 2015-04-07 11:03:43 +0200 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2015-04-07 11:03:43 +0200 |
commit | cfcfd4614b8b01b2782ac4dcafb21d14d74d5184 (patch) | |
tree | 3083fbe60cc7edf3211f4c2d3da04a0c9e35fbd0 /malloc | |
parent | 9e8c0381bbc6faf08cfc584e3848dd265d0d7e1a (diff) | |
download | glibc-cfcfd4614b8b01b2782ac4dcafb21d14d74d5184.tar.gz glibc-cfcfd4614b8b01b2782ac4dcafb21d14d74d5184.tar.xz glibc-cfcfd4614b8b01b2782ac4dcafb21d14d74d5184.zip |
Add struct scratch_buffer and its internal helper functions
These will be used from NSS modules, so they have to be exported.
Diffstat (limited to 'malloc')
-rw-r--r-- | malloc/Makefile | 6 | ||||
-rw-r--r-- | malloc/Versions | 5 | ||||
-rw-r--r-- | malloc/scratch_buffer_grow.c | 52 | ||||
-rw-r--r-- | malloc/scratch_buffer_grow_preserve.c | 62 | ||||
-rw-r--r-- | malloc/scratch_buffer_set_array_size.c | 59 | ||||
-rw-r--r-- | malloc/tst-scratch_buffer.c | 155 |
6 files changed, 337 insertions, 2 deletions
diff --git a/malloc/Makefile b/malloc/Makefile index 5f68a792e8..9e7112a5bf 100644 --- a/malloc/Makefile +++ b/malloc/Makefile @@ -27,10 +27,12 @@ headers := $(dist-headers) obstack.h mcheck.h tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \ tst-mallocstate tst-mcheck tst-mallocfork tst-trim1 \ tst-malloc-usable tst-realloc tst-posix_memalign \ - tst-pvalloc tst-memalign tst-mallopt + tst-pvalloc tst-memalign tst-mallopt tst-scratch_buffer test-srcs = tst-mtrace -routines = malloc morecore mcheck mtrace obstack +routines = malloc morecore mcheck mtrace obstack \ + scratch_buffer_grow scratch_buffer_grow_preserve \ + scratch_buffer_set_array_size install-lib := libmcheck.a non-lib.a := libmcheck.a diff --git a/malloc/Versions b/malloc/Versions index 7ca9bdf25f..f3c3d8a093 100644 --- a/malloc/Versions +++ b/malloc/Versions @@ -67,5 +67,10 @@ libc { # Internal destructor hook for libpthread. __libc_thread_freeres; + + # struct scratch_buffer support + __libc_scratch_buffer_grow; + __libc_scratch_buffer_grow_preserve; + __libc_scratch_buffer_set_array_size; } } diff --git a/malloc/scratch_buffer_grow.c b/malloc/scratch_buffer_grow.c new file mode 100644 index 0000000000..3f27e0e03c --- /dev/null +++ b/malloc/scratch_buffer_grow.c @@ -0,0 +1,52 @@ +/* Variable-sized buffer with on-stack default allocation. + Copyright (C) 2015 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 <scratch_buffer.h> +#include <errno.h> + +bool +__libc_scratch_buffer_grow (struct scratch_buffer *buffer) +{ + void *new_ptr; + size_t new_length = buffer->length * 2; + + /* Discard old buffer. */ + scratch_buffer_free (buffer); + + /* Check for overflow. */ + if (__glibc_likely (new_length >= buffer->length)) + new_ptr = malloc (new_length); + else + { + __set_errno (ENOMEM); + new_ptr = NULL; + } + + if (__glibc_unlikely (new_ptr == NULL)) + { + /* Buffer must remain valid to free. */ + scratch_buffer_init (buffer); + return false; + } + + /* Install new heap-based buffer. */ + buffer->data = new_ptr; + buffer->length = new_length; + return true; +} +libc_hidden_def (__libc_scratch_buffer_grow); diff --git a/malloc/scratch_buffer_grow_preserve.c b/malloc/scratch_buffer_grow_preserve.c new file mode 100644 index 0000000000..f2edb49bda --- /dev/null +++ b/malloc/scratch_buffer_grow_preserve.c @@ -0,0 +1,62 @@ +/* Variable-sized buffer with on-stack default allocation. + Copyright (C) 2015 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 <scratch_buffer.h> +#include <errno.h> + +bool +__libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer) +{ + size_t new_length = 2 * buffer->length; + void *new_ptr; + + if (buffer->data == buffer->__space) + { + /* Move buffer to the heap. No overflow is possible because + buffer->length describes a small buffer on the stack. */ + new_ptr = malloc (new_length); + if (new_ptr == NULL) + return false; + memcpy (new_ptr, buffer->__space, buffer->length); + } + else + { + /* Buffer was already on the heap. Check for overflow. */ + if (__glibc_likely (new_length >= buffer->length)) + new_ptr = realloc (buffer->data, new_length); + else + { + __set_errno (ENOMEM); + new_ptr = NULL; + } + + if (__glibc_unlikely (new_ptr == NULL)) + { + /* Deallocate, but buffer must remain valid to free. */ + free (buffer->data); + scratch_buffer_init (buffer); + return false; + } + } + + /* Install new heap-based buffer. */ + buffer->data = new_ptr; + buffer->length = new_length; + return true; +} +libc_hidden_def (__libc_scratch_buffer_grow_preserve); diff --git a/malloc/scratch_buffer_set_array_size.c b/malloc/scratch_buffer_set_array_size.c new file mode 100644 index 0000000000..a00b520eef --- /dev/null +++ b/malloc/scratch_buffer_set_array_size.c @@ -0,0 +1,59 @@ +/* Variable-sized buffer with on-stack default allocation. + Copyright (C) 2015 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 <scratch_buffer.h> +#include <errno.h> + +bool +__libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer, + size_t nelem, size_t size) +{ + size_t new_length = nelem * size; + + /* Avoid overflow check if both values are small. */ + if ((nelem | size) >> (sizeof (size_t) * CHAR_BIT / 2) != 0 + && nelem != 0 && size != new_length / nelem) + { + /* Overflow. Discard the old buffer, but it must remain valid + to free. */ + scratch_buffer_free (buffer); + scratch_buffer_init (buffer); + __set_errno (ENOMEM); + return false; + } + + if (new_length <= buffer->length) + return true; + + /* Discard old buffer. */ + scratch_buffer_free (buffer); + + char *new_ptr = malloc (new_length); + if (new_ptr == NULL) + { + /* Buffer must remain valid to free. */ + scratch_buffer_init (buffer); + return false; + } + + /* Install new heap-based buffer. */ + buffer->data = new_ptr; + buffer->length = new_length; + return true; +} +libc_hidden_def (__libc_scratch_buffer_set_array_size); diff --git a/malloc/tst-scratch_buffer.c b/malloc/tst-scratch_buffer.c new file mode 100644 index 0000000000..dcae5120dd --- /dev/null +++ b/malloc/tst-scratch_buffer.c @@ -0,0 +1,155 @@ +/* + Copyright (C) 2015 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 <scratch_buffer.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +static bool +unchanged_array_size (struct scratch_buffer *buf, size_t a, size_t b) +{ + size_t old_length = buf->length; + if (!scratch_buffer_set_array_size (buf, a, b)) + { + printf ("scratch_buffer_set_array_size failed: %zu %zu\n", + a, b); + return false; + } + if (old_length != buf->length) + { + printf ("scratch_buffer_set_array_size did not preserve size: %zu %zu\n", + a, b); + return false; + } + return true; +} + +static bool +array_size_must_fail (size_t a, size_t b) +{ + for (int pass = 0; pass < 2; ++pass) + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + if (pass > 0) + if (!scratch_buffer_grow (&buf)) + { + printf ("scratch_buffer_grow in array_size_must_fail failed\n"); + return false; + } + if (scratch_buffer_set_array_size (&buf, a, b)) + { + printf ("scratch_buffer_set_array_size passed: %d %zu %zu\n", + pass, a, b); + return false; + } + if (buf.data != buf.__space) + { + printf ("scratch_buffer_set_array_size did not free: %d %zu %zu\n", + pass, a, b); + return false; + } + } + return true; +} + +static int +do_test (void) +{ + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + memset (buf.data, ' ', buf.length); + scratch_buffer_free (&buf); + } + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + memset (buf.data, ' ', buf.length); + size_t old_length = buf.length; + scratch_buffer_grow (&buf); + if (buf.length <= old_length) + { + printf ("scratch_buffer_grow did not enlarge buffer\n"); + return 1; + } + memset (buf.data, ' ', buf.length); + scratch_buffer_free (&buf); + } + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + memset (buf.data, '@', buf.length); + strcpy (buf.data, "prefix"); + size_t old_length = buf.length; + scratch_buffer_grow_preserve (&buf); + if (buf.length <= old_length) + { + printf ("scratch_buffer_grow_preserve did not enlarge buffer\n"); + return 1; + } + if (strcmp (buf.data, "prefix") != 0) + { + printf ("scratch_buffer_grow_preserve did not copy buffer\n"); + return 1; + } + for (unsigned i = 7; i < old_length; ++i) + if (((char *)buf.data)[i] != '@') + { + printf ("scratch_buffer_grow_preserve did not copy buffer (%u)\n", + i); + return 1; + } + scratch_buffer_free (&buf); + } + { + struct scratch_buffer buf; + scratch_buffer_init (&buf); + for (int pass = 0; pass < 4; ++pass) + { + if (!(unchanged_array_size (&buf, 0, 0) + && unchanged_array_size (&buf, 1, 0) + && unchanged_array_size (&buf, 0, 1) + && unchanged_array_size (&buf, -1, 0) + && unchanged_array_size (&buf, 0, -1) + && unchanged_array_size (&buf, 1ULL << 16, 0) + && unchanged_array_size (&buf, 0, 1ULL << 16) + && unchanged_array_size (&buf, 1ULL << 32, 0) + && unchanged_array_size (&buf, 0, 1ULL << 32))) + return 1; + if (!scratch_buffer_grow (&buf)) + { + printf ("scratch_buffer_grow_failed (pass %d)\n", pass); + } + } + scratch_buffer_free (&buf); + } + { + if (!(array_size_must_fail (-1, 1) + && array_size_must_fail (-1, -1) + && array_size_must_fail (1, -1) + && array_size_must_fail (((size_t)-1) / 4, 4) + && array_size_must_fail (4, ((size_t)-1) / 4))) + return 1; + } + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" |