From patchwork Mon Mar 2 09:59:37 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 445005 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 7B97114015A for ; Mon, 2 Mar 2015 20:59:55 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass reason="1024-bit key; unprotected key" header.d=sourceware.org header.i=@sourceware.org header.b=C9RZNBgg; dkim-adsp=none (unprotected policy); dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:message-id:date:from:mime-version:to:cc :subject:references:in-reply-to:content-type; q=dns; s=default; b= DCSrk1iiSNZDH/kTYGZ15uFPhQz/w38O7RkgI2o4CBCO8+G1/KCPQJh10saQh8QU W7i8bUwX6i1rFCm/byG84+6Lt9ajIJOV1UxJ2nQ9RBAn766+zYE4Y96tv2b/DcAu JFyhweq63MvCnq0sIuSIJ7OIoVPJGhL5L3USN8C3+xg= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:message-id:date:from:mime-version:to:cc :subject:references:in-reply-to:content-type; s=default; bh=rgFT QNWF3tGPu9P/zRDfmlowPUU=; b=C9RZNBggXNj62U1Jo9DcYrd+MgtgT+fU4FXw V3M/roNT2XWXzGoDWOvYdlrTYUjlvaCbpIJeTW+yBrtHo6tRdvJsarKQT3dzIwc/ QTopTboPfO40cUH5RODP7334PSz5wqNMcMeLFxOjGzkLDraOyqZg0OJwnG08eyIq luHLUWs= Received: (qmail 49831 invoked by alias); 2 Mar 2015 09:59:48 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 49818 invoked by uid 89); 2 Mar 2015 09:59:47 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.6 required=5.0 tests=AWL, BAYES_50, SPF_HELO_PASS, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Message-ID: <54F43489.6050302@redhat.com> Date: Mon, 02 Mar 2015 10:59:37 +0100 From: Florian Weimer User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.5.0 MIME-Version: 1.0 To: Andreas Schwab CC: libc-alpha@sourceware.org Subject: Re: [PATCH 06/25] Add struct scratch_buffer and its internal helper functions References: <7a6fe503fb764beee3d5b89662d3bbf65242161c.1425285061.git.fweimer@redhat.com> <54F4301E.7090704@redhat.com> In-Reply-To: On 03/02/2015 10:45 AM, Andreas Schwab wrote: > Florian Weimer writes: > >> See the commit message. nscd and some of the NSS modules need these >> symbols. > > Then use GLIBC_PRIVATE. Oh, right, sorry about that. Okay with that change? From 50297a6c876e60cbd8296b0d587146a87f4148d1 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Sun, 1 Mar 2015 15:28:11 +0100 Subject: [PATCH] Add struct scratch_buffer and its internal helper functions These will be used from NSS modules, so they have to be exported. --- include/scratch_buffer.h | 133 ++++++++++++++++++++++++++++ malloc/Makefile | 6 +- malloc/Versions | 5 ++ malloc/scratch_buffer_grow.c | 52 +++++++++++ malloc/scratch_buffer_grow_preserve.c | 65 ++++++++++++++ malloc/scratch_buffer_set_array_size.c | 61 +++++++++++++ malloc/tst-scratch_buffer.c | 155 +++++++++++++++++++++++++++++++++ 7 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 include/scratch_buffer.h create mode 100644 malloc/scratch_buffer_grow.c create mode 100644 malloc/scratch_buffer_grow_preserve.c create mode 100644 malloc/scratch_buffer_set_array_size.c create mode 100644 malloc/tst-scratch_buffer.c diff --git a/include/scratch_buffer.h b/include/scratch_buffer.h new file mode 100644 index 0000000..57117b6 --- /dev/null +++ b/include/scratch_buffer.h @@ -0,0 +1,133 @@ +/* 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 + . */ + +#pragma once + +/* Scratch buffers with a default stack allocation and fallback to + heap allocation. It is expected that this function is used in this + way: + + struct scratch_buffer tmpbuf; + scratch_buffer_init (&tmpbuf); + + while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length)) + if (!scratch_buffer_grow (&tmpbuf)) + return -1; + + scratch_buffer_free (&tmpbuf); + return 0; + + The allocation functions (scratch_buffer_grow, + scratch_buffer_grow_preserve, scratch_buffer_set_array_size) make + sure that the heap allocation, if any, is freed, so that the code + above does not have a memory leak. The buffer still remains in a + state that can be deallocated using scratch_buffer_free, so a loop + like this is valid as well: + + struct scratch_buffer tmpbuf; + scratch_buffer_init (&tmpbuf); + + while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length)) + if (!scratch_buffer_grow (&tmpbuf)) + break; + + scratch_buffer_free (&tmpbuf); + + The non-inlined functions are implemented in such a way that it is + possible to change the size of the pre-allocated buffer without + impacting ABI. +*/ + +#include +#include + +#define SCRATCH_BUFFER_ALIGNMENT \ + __attribute__ ((aligned (__alignof__ (union {void *p; double d;})))) + +/* Scratch buffer. Must be initialized with scratch_buffer_init + before its use. */ +struct scratch_buffer { + void *data; /* Pointer to the beginning of the scratch area. */ + size_t length; /* Allocated space at the data pointer, in bytes. */ + char __space[1024 - sizeof (size_t)] SCRATCH_BUFFER_ALIGNMENT; +} SCRATCH_BUFFER_ALIGNMENT; + +/* Initializes *BUFFER so that BUFFER->data points to BUFFER->__space + and BUFFER->length reflects the available space. */ +static inline void +scratch_buffer_init (struct scratch_buffer *buffer) + __attribute__ ((always_inline)); +static inline void +scratch_buffer_init (struct scratch_buffer *buffer) +{ + buffer->data = buffer->__space; + buffer->length = sizeof (buffer->__space); +} + +/* Deallocates *BUFFER (if it was heap-allocated). */ +static inline void +scratch_buffer_free (struct scratch_buffer *buffer) +{ + if (buffer->data != buffer->__space) + free (buffer->data); +} + +/* Grows *BUFFER by some arbitrary amount. The buffer contents is NOT + preserved. Returns true on success, fails on allocation failure + (in which case the old buffer is freed). On success, the new + buffer is slightly larger (by at least 16 bytes) than the previous + size. On failure, *BUFFER is deallocated, but remains in a + free-able state. */ +bool __libc_scratch_buffer_grow (struct scratch_buffer *buffer); +libc_hidden_proto (__libc_scratch_buffer_grow) + +/* Alias for __libc_scratch_buffer_grow. */ +static inline bool +scratch_buffer_grow (struct scratch_buffer *buffer) +{ + return __glibc_likely (__libc_scratch_buffer_grow (buffer)); +} + +/* Like __libc_scratch_buffer_grow, but preserves the old buffer + contents on success, as a prefix of the new buffer. */ +bool __libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer); +libc_hidden_proto (__libc_scratch_buffer_grow_preserve) + +/* Alias for __libc_scratch_buffer_grow_preserve. */ +static inline bool +scratch_buffer_grow_preserve (struct scratch_buffer *buffer) +{ + return __glibc_likely (__libc_scratch_buffer_grow_preserve (buffer)); +} + +/* Grows *BUFFER so that it can store at least NELEM elemnts of SIZE + bytes. The buffer contents is NOT preserved. Returns true on + success, fails on allocation failure (in which case the old buffer + is freed, but *BUFFER remains a free-able state). */ +bool __libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer, + size_t nelem, size_t size); +libc_hidden_proto (__libc_scratch_buffer_set_array_size) + +/* Alias for __libc_scratch_set_array_size. */ +static inline bool +scratch_buffer_set_array_size (struct scratch_buffer *buffer, + size_t nelem, size_t size) +{ + return __glibc_likely (__libc_scratch_buffer_set_array_size + (buffer, nelem, size)); +} diff --git a/malloc/Makefile b/malloc/Makefile index 5f68a79..9e7112a 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 7ca9bdf..f3c3d8a 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 0000000..6621393 --- /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 + . */ + +#include +#include + +bool +__libc_scratch_buffer_grow (struct scratch_buffer *buffer) +{ + size_t new_length = buffer->length * 2; + + /* Discard old buffer. */ + scratch_buffer_free (buffer); + + /* Check for overflow. */ + if (__glibc_unlikely (new_length < buffer->length)) + { + /* Buffer must remain valid to free. */ + scratch_buffer_init (buffer); + __set_errno (ENOMEM); + return false; + } + + void *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_grow); diff --git a/malloc/scratch_buffer_grow_preserve.c b/malloc/scratch_buffer_grow_preserve.c new file mode 100644 index 0000000..3abbd56 --- /dev/null +++ b/malloc/scratch_buffer_grow_preserve.c @@ -0,0 +1,65 @@ +/* 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 + . */ + +#include +#include + +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. We use + buffer->length instead of sizeof (buffer->__space) to avoid + making the size of struct scratch_buffer part of the ABI. */ + 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_unlikely (new_length < buffer->length)) + { + /* Deallocate, but buffer must remain valid to free. */ + free (buffer->data); + scratch_buffer_init (buffer); + __set_errno (ENOMEM); + return false; + } + new_ptr = realloc (buffer->data, new_length); + if (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 0000000..7786baf --- /dev/null +++ b/malloc/scratch_buffer_set_array_size.c @@ -0,0 +1,61 @@ +/* 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 + . */ + +#include +#include + +bool +__libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer, + size_t nelem, size_t size) +{ + size_t size_max_square_root = ((size_t)1) << (sizeof (size_t) * 4); + /* Avoid overflow check if both values are small. */ + if (nelem >= size_max_square_root || size >= size_max_square_root) + { + if (nelem != 0 && size > SIZE_MAX / nelem) + { + /* Discard the old buffer, but it must remain valid to + free. */ + scratch_buffer_free (buffer); + scratch_buffer_init (buffer); + __set_errno (ENOMEM); + return false; + } + } + + size_t new_length = nelem * size; + 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 0000000..dcae512 --- /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 + . */ + +#include +#include +#include +#include + +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" -- 2.1.0