1 1.1 christos /* Safe automatic memory allocation. 2 1.1.1.2 christos Copyright (C) 2003, 2006-2007, 2009-2022 Free Software Foundation, Inc. 3 1.1 christos Written by Bruno Haible <bruno (at) clisp.org>, 2003, 2018. 4 1.1 christos 5 1.1.1.2 christos This file is free software: you can redistribute it and/or modify 6 1.1.1.2 christos it under the terms of the GNU Lesser General Public License as 7 1.1.1.2 christos published by the Free Software Foundation; either version 2.1 of the 8 1.1.1.2 christos License, or (at your option) any later version. 9 1.1 christos 10 1.1.1.2 christos This file is distributed in the hope that it will be useful, 11 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of 12 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 1.1.1.2 christos GNU Lesser General Public License for more details. 14 1.1 christos 15 1.1.1.2 christos You should have received a copy of the GNU Lesser General Public License 16 1.1.1.2 christos along with this program. If not, see <https://www.gnu.org/licenses/>. */ 17 1.1 christos 18 1.1 christos #define _GL_USE_STDLIB_ALLOC 1 19 1.1 christos #include <config.h> 20 1.1 christos 21 1.1 christos /* Specification. */ 22 1.1 christos #include "malloca.h" 23 1.1 christos 24 1.1.1.2 christos #include "idx.h" 25 1.1.1.2 christos #include "intprops.h" 26 1.1 christos #include "verify.h" 27 1.1 christos 28 1.1 christos /* The speed critical point in this file is freea() applied to an alloca() 29 1.1 christos result: it must be fast, to match the speed of alloca(). The speed of 30 1.1 christos mmalloca() and freea() in the other case are not critical, because they 31 1.1 christos are only invoked for big memory sizes. 32 1.1 christos Here we use a bit in the address as an indicator, an idea by Ondej Blka. 33 1.1 christos malloca() can return three types of pointers: 34 1.1 christos - Pointers 0 mod 2*sa_alignment_max come from stack allocation. 35 1.1 christos - Pointers sa_alignment_max mod 2*sa_alignment_max come from heap 36 1.1 christos allocation. 37 1.1 christos - NULL comes from a failed heap allocation. */ 38 1.1 christos 39 1.1 christos /* Type for holding very small pointer differences. */ 40 1.1 christos typedef unsigned char small_t; 41 1.1 christos /* Verify that it is wide enough. */ 42 1.1 christos verify (2 * sa_alignment_max - 1 <= (small_t) -1); 43 1.1 christos 44 1.1 christos void * 45 1.1 christos mmalloca (size_t n) 46 1.1 christos { 47 1.1 christos #if HAVE_ALLOCA 48 1.1 christos /* Allocate one more word, used to determine the address to pass to freea(), 49 1.1 christos and room for the alignment sa_alignment_max mod 2*sa_alignment_max. */ 50 1.1.1.2 christos uintptr_t alignment2_mask = 2 * sa_alignment_max - 1; 51 1.1.1.2 christos int plus = sizeof (small_t) + alignment2_mask; 52 1.1.1.2 christos idx_t nplus; 53 1.1.1.2 christos if (!INT_ADD_WRAPV (n, plus, &nplus) && !xalloc_oversized (nplus, 1)) 54 1.1 christos { 55 1.1 christos char *mem = (char *) malloc (nplus); 56 1.1 christos 57 1.1 christos if (mem != NULL) 58 1.1 christos { 59 1.1.1.2 christos uintptr_t umem = (uintptr_t)mem, umemplus; 60 1.1.1.2 christos /* The INT_ADD_WRAPV avoids signed integer overflow on 61 1.1.1.2 christos theoretical platforms where UINTPTR_MAX <= INT_MAX. */ 62 1.1.1.2 christos INT_ADD_WRAPV (umem, sizeof (small_t) + sa_alignment_max - 1, 63 1.1.1.2 christos &umemplus); 64 1.1.1.2 christos idx_t offset = ((umemplus & ~alignment2_mask) 65 1.1.1.2 christos + sa_alignment_max - umem); 66 1.1.1.2 christos void *vp = mem + offset; 67 1.1.1.2 christos small_t *p = vp; 68 1.1 christos /* Here p >= mem + sizeof (small_t), 69 1.1 christos and p <= mem + sizeof (small_t) + 2 * sa_alignment_max - 1 70 1.1 christos hence p + n <= mem + nplus. 71 1.1 christos So, the memory range [p, p+n) lies in the allocated memory range 72 1.1 christos [mem, mem + nplus). */ 73 1.1.1.2 christos p[-1] = offset; 74 1.1 christos /* p sa_alignment_max mod 2*sa_alignment_max. */ 75 1.1 christos return p; 76 1.1 christos } 77 1.1 christos } 78 1.1 christos /* Out of memory. */ 79 1.1 christos return NULL; 80 1.1 christos #else 81 1.1 christos # if !MALLOC_0_IS_NONNULL 82 1.1 christos if (n == 0) 83 1.1 christos n = 1; 84 1.1 christos # endif 85 1.1 christos return malloc (n); 86 1.1 christos #endif 87 1.1 christos } 88 1.1 christos 89 1.1 christos #if HAVE_ALLOCA 90 1.1 christos void 91 1.1 christos freea (void *p) 92 1.1 christos { 93 1.1 christos /* Check argument. */ 94 1.1 christos if ((uintptr_t) p & (sa_alignment_max - 1)) 95 1.1 christos { 96 1.1 christos /* p was not the result of a malloca() call. Invalid argument. */ 97 1.1 christos abort (); 98 1.1 christos } 99 1.1 christos /* Determine whether p was a non-NULL pointer returned by mmalloca(). */ 100 1.1 christos if ((uintptr_t) p & sa_alignment_max) 101 1.1 christos { 102 1.1 christos void *mem = (char *) p - ((small_t *) p)[-1]; 103 1.1 christos free (mem); 104 1.1 christos } 105 1.1 christos } 106 1.1 christos #endif 107 1.1 christos 108 1.1 christos /* 109 1.1 christos * Hey Emacs! 110 1.1 christos * Local Variables: 111 1.1 christos * coding: utf-8 112 1.1 christos * End: 113 1.1 christos */ 114