diff options
Diffstat (limited to 'gl/malloca.c')
| -rw-r--r-- | gl/malloca.c | 165 |
1 files changed, 64 insertions, 101 deletions
diff --git a/gl/malloca.c b/gl/malloca.c index 311be569..f055b1e5 100644 --- a/gl/malloca.c +++ b/gl/malloca.c | |||
| @@ -1,19 +1,19 @@ | |||
| 1 | /* Safe automatic memory allocation. | 1 | /* Safe automatic memory allocation. |
| 2 | Copyright (C) 2003, 2006-2007, 2009-2013 Free Software Foundation, Inc. | 2 | Copyright (C) 2003, 2006-2007, 2009-2023 Free Software Foundation, Inc. |
| 3 | Written by Bruno Haible <bruno@clisp.org>, 2003. | 3 | Written by Bruno Haible <bruno@clisp.org>, 2003, 2018. |
| 4 | 4 | ||
| 5 | This program is free software; you can redistribute it and/or modify | 5 | This file is free software: you can redistribute it and/or modify |
| 6 | it under the terms of the GNU General Public License as published by | 6 | it under the terms of the GNU Lesser General Public License as |
| 7 | the Free Software Foundation; either version 3, or (at your option) | 7 | published by the Free Software Foundation; either version 2.1 of the |
| 8 | any later version. | 8 | License, or (at your option) any later version. |
| 9 | 9 | ||
| 10 | This program is distributed in the hope that it will be useful, | 10 | This file is distributed in the hope that it will be useful, |
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | GNU General Public License for more details. | 13 | GNU Lesser General Public License for more details. |
| 14 | 14 | ||
| 15 | You should have received a copy of the GNU General Public License | 15 | You should have received a copy of the GNU Lesser General Public License |
| 16 | along with this program; if not, see <http://www.gnu.org/licenses/>. */ | 16 | along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| 17 | 17 | ||
| 18 | #define _GL_USE_STDLIB_ALLOC 1 | 18 | #define _GL_USE_STDLIB_ALLOC 1 |
| 19 | #include <config.h> | 19 | #include <config.h> |
| @@ -21,82 +21,56 @@ | |||
| 21 | /* Specification. */ | 21 | /* Specification. */ |
| 22 | #include "malloca.h" | 22 | #include "malloca.h" |
| 23 | 23 | ||
| 24 | #include <stdint.h> | 24 | #include <stdckdint.h> |
| 25 | 25 | ||
| 26 | #include "verify.h" | 26 | #include "idx.h" |
| 27 | 27 | ||
| 28 | /* The speed critical point in this file is freea() applied to an alloca() | 28 | /* The speed critical point in this file is freea() applied to an alloca() |
| 29 | result: it must be fast, to match the speed of alloca(). The speed of | 29 | result: it must be fast, to match the speed of alloca(). The speed of |
| 30 | mmalloca() and freea() in the other case are not critical, because they | 30 | mmalloca() and freea() in the other case are not critical, because they |
| 31 | are only invoked for big memory sizes. */ | 31 | are only invoked for big memory sizes. |
| 32 | 32 | Here we use a bit in the address as an indicator, an idea by Ondřej Bílka. | |
| 33 | #if HAVE_ALLOCA | 33 | malloca() can return three types of pointers: |
| 34 | 34 | - Pointers ≡ 0 mod 2*sa_alignment_max come from stack allocation. | |
| 35 | /* Store the mmalloca() results in a hash table. This is needed to reliably | 35 | - Pointers ≡ sa_alignment_max mod 2*sa_alignment_max come from heap |
| 36 | distinguish a mmalloca() result and an alloca() result. | 36 | allocation. |
| 37 | 37 | - NULL comes from a failed heap allocation. */ | |
| 38 | Although it is possible that the same pointer is returned by alloca() and | 38 | |
| 39 | by mmalloca() at different times in the same application, it does not lead | 39 | /* Type for holding very small pointer differences. */ |
| 40 | to a bug in freea(), because: | 40 | typedef unsigned char small_t; |
| 41 | - Before a pointer returned by alloca() can point into malloc()ed memory, | 41 | /* Verify that it is wide enough. */ |
| 42 | the function must return, and once this has happened the programmer must | 42 | static_assert (2 * sa_alignment_max - 1 <= (small_t) -1); |
| 43 | not call freea() on it anyway. | ||
| 44 | - Before a pointer returned by mmalloca() can point into the stack, it | ||
| 45 | must be freed. The only function that can free it is freea(), and | ||
| 46 | when freea() frees it, it also removes it from the hash table. */ | ||
| 47 | |||
| 48 | #define MAGIC_NUMBER 0x1415fb4a | ||
| 49 | #define MAGIC_SIZE sizeof (int) | ||
| 50 | /* This is how the header info would look like without any alignment | ||
| 51 | considerations. */ | ||
| 52 | struct preliminary_header { void *next; int magic; }; | ||
| 53 | /* But the header's size must be a multiple of sa_alignment_max. */ | ||
| 54 | #define HEADER_SIZE \ | ||
| 55 | (((sizeof (struct preliminary_header) + sa_alignment_max - 1) / sa_alignment_max) * sa_alignment_max) | ||
| 56 | union header { | ||
| 57 | void *next; | ||
| 58 | struct { | ||
| 59 | char room[HEADER_SIZE - MAGIC_SIZE]; | ||
| 60 | int word; | ||
| 61 | } magic; | ||
| 62 | }; | ||
| 63 | verify (HEADER_SIZE == sizeof (union header)); | ||
| 64 | /* We make the hash table quite big, so that during lookups the probability | ||
| 65 | of empty hash buckets is quite high. There is no need to make the hash | ||
| 66 | table resizable, because when the hash table gets filled so much that the | ||
| 67 | lookup becomes slow, it means that the application has memory leaks. */ | ||
| 68 | #define HASH_TABLE_SIZE 257 | ||
| 69 | static void * mmalloca_results[HASH_TABLE_SIZE]; | ||
| 70 | |||
| 71 | #endif | ||
| 72 | 43 | ||
| 73 | void * | 44 | void * |
| 74 | mmalloca (size_t n) | 45 | mmalloca (size_t n) |
| 75 | { | 46 | { |
| 76 | #if HAVE_ALLOCA | 47 | #if HAVE_ALLOCA |
| 77 | /* Allocate one more word, that serves as an indicator for malloc()ed | 48 | /* Allocate one more word, used to determine the address to pass to freea(), |
| 78 | memory, so that freea() of an alloca() result is fast. */ | 49 | and room for the alignment ≡ sa_alignment_max mod 2*sa_alignment_max. */ |
| 79 | size_t nplus = n + HEADER_SIZE; | 50 | uintptr_t alignment2_mask = 2 * sa_alignment_max - 1; |
| 80 | 51 | int plus = sizeof (small_t) + alignment2_mask; | |
| 81 | if (nplus >= n) | 52 | idx_t nplus; |
| 53 | if (!ckd_add (&nplus, n, plus) && !xalloc_oversized (nplus, 1)) | ||
| 82 | { | 54 | { |
| 83 | void *p = malloc (nplus); | 55 | char *mem = (char *) malloc (nplus); |
| 84 | 56 | ||
| 85 | if (p != NULL) | 57 | if (mem != NULL) |
| 86 | { | 58 | { |
| 87 | size_t slot; | 59 | uintptr_t umem = (uintptr_t)mem, umemplus; |
| 88 | union header *h = p; | 60 | /* The ckd_add avoids signed integer overflow on |
| 89 | 61 | theoretical platforms where UINTPTR_MAX <= INT_MAX. */ | |
| 90 | p = h + 1; | 62 | ckd_add (&umemplus, umem, sizeof (small_t) + sa_alignment_max - 1); |
| 91 | 63 | idx_t offset = ((umemplus & ~alignment2_mask) | |
| 92 | /* Put a magic number into the indicator word. */ | 64 | + sa_alignment_max - umem); |
| 93 | h->magic.word = MAGIC_NUMBER; | 65 | void *vp = mem + offset; |
| 94 | 66 | small_t *p = vp; | |
| 95 | /* Enter p into the hash table. */ | 67 | /* Here p >= mem + sizeof (small_t), |
| 96 | slot = (uintptr_t) p % HASH_TABLE_SIZE; | 68 | and p <= mem + sizeof (small_t) + 2 * sa_alignment_max - 1 |
| 97 | h->next = mmalloca_results[slot]; | 69 | hence p + n <= mem + nplus. |
| 98 | mmalloca_results[slot] = p; | 70 | So, the memory range [p, p+n) lies in the allocated memory range |
| 99 | 71 | [mem, mem + nplus). */ | |
| 72 | p[-1] = offset; | ||
| 73 | /* p ≡ sa_alignment_max mod 2*sa_alignment_max. */ | ||
| 100 | return p; | 74 | return p; |
| 101 | } | 75 | } |
| 102 | } | 76 | } |
| @@ -115,35 +89,24 @@ mmalloca (size_t n) | |||
| 115 | void | 89 | void |
| 116 | freea (void *p) | 90 | freea (void *p) |
| 117 | { | 91 | { |
| 118 | /* mmalloca() may have returned NULL. */ | 92 | /* Check argument. */ |
| 119 | if (p != NULL) | 93 | if ((uintptr_t) p & (sa_alignment_max - 1)) |
| 120 | { | 94 | { |
| 121 | /* Attempt to quickly distinguish the mmalloca() result - which has | 95 | /* p was not the result of a malloca() call. Invalid argument. */ |
| 122 | a magic indicator word - and the alloca() result - which has an | 96 | abort (); |
| 123 | uninitialized indicator word. It is for this test that sa_increment | 97 | } |
| 124 | additional bytes are allocated in the alloca() case. */ | 98 | /* Determine whether p was a non-NULL pointer returned by mmalloca(). */ |
| 125 | if (((int *) p)[-1] == MAGIC_NUMBER) | 99 | if ((uintptr_t) p & sa_alignment_max) |
| 126 | { | 100 | { |
| 127 | /* Looks like a mmalloca() result. To see whether it really is one, | 101 | void *mem = (char *) p - ((small_t *) p)[-1]; |
| 128 | perform a lookup in the hash table. */ | 102 | free (mem); |
| 129 | size_t slot = (uintptr_t) p % HASH_TABLE_SIZE; | ||
| 130 | void **chain = &mmalloca_results[slot]; | ||
| 131 | for (; *chain != NULL;) | ||
| 132 | { | ||
| 133 | union header *h = p; | ||
| 134 | if (*chain == p) | ||
| 135 | { | ||
| 136 | /* Found it. Remove it from the hash table and free it. */ | ||
| 137 | union header *p_begin = h - 1; | ||
| 138 | *chain = p_begin->next; | ||
| 139 | free (p_begin); | ||
| 140 | return; | ||
| 141 | } | ||
| 142 | h = *chain; | ||
| 143 | chain = &h[-1].next; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | /* At this point, we know it was not a mmalloca() result. */ | ||
| 147 | } | 103 | } |
| 148 | } | 104 | } |
| 149 | #endif | 105 | #endif |
| 106 | |||
| 107 | /* | ||
| 108 | * Hey Emacs! | ||
| 109 | * Local Variables: | ||
| 110 | * coding: utf-8 | ||
| 111 | * End: | ||
| 112 | */ | ||
