Branch data Line data Source code
1 : : /* Safe automatic memory allocation.
2 : : Copyright (C) 2003, 2006-2007, 2009-2012 Free Software Foundation, Inc.
3 : : Written by Bruno Haible <bruno@clisp.org>, 2003.
4 : :
5 : : This program is free software; you can redistribute it and/or modify
6 : : it under the terms of the GNU General Public License as published by
7 : : the Free Software Foundation; either version 3, or (at your option)
8 : : any later version.
9 : :
10 : : This program is distributed in the hope that it will be useful,
11 : : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : : GNU General Public License for more details.
14 : :
15 : : You should have received a copy of the GNU General Public License
16 : : along with this program; if not, see <http://www.gnu.org/licenses/>. */
17 : :
18 : : #define _GL_USE_STDLIB_ALLOC 1
19 : : #include <config.h>
20 : :
21 : : /* Specification. */
22 : : #include "malloca.h"
23 : :
24 : : #include <stdint.h>
25 : :
26 : : #include "verify.h"
27 : :
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
30 : : mmalloca() and freea() in the other case are not critical, because they
31 : : are only invoked for big memory sizes. */
32 : :
33 : : #if HAVE_ALLOCA
34 : :
35 : : /* Store the mmalloca() results in a hash table. This is needed to reliably
36 : : distinguish a mmalloca() result and an alloca() result.
37 : :
38 : : Although it is possible that the same pointer is returned by alloca() and
39 : : by mmalloca() at different times in the same application, it does not lead
40 : : to a bug in freea(), because:
41 : : - Before a pointer returned by alloca() can point into malloc()ed memory,
42 : : the function must return, and once this has happened the programmer must
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; char room[MAGIC_SIZE]; };
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 : : struct header { void *next; char room[HEADER_SIZE - sizeof (struct preliminary_header) + MAGIC_SIZE]; };
57 : : verify (HEADER_SIZE == sizeof (struct header));
58 : : /* We make the hash table quite big, so that during lookups the probability
59 : : of empty hash buckets is quite high. There is no need to make the hash
60 : : table resizable, because when the hash table gets filled so much that the
61 : : lookup becomes slow, it means that the application has memory leaks. */
62 : : #define HASH_TABLE_SIZE 257
63 : : static void * mmalloca_results[HASH_TABLE_SIZE];
64 : :
65 : : #endif
66 : :
67 : : void *
68 : 250000 : mmalloca (size_t n)
69 : : {
70 : : #if HAVE_ALLOCA
71 : : /* Allocate one more word, that serves as an indicator for malloc()ed
72 : : memory, so that freea() of an alloca() result is fast. */
73 : 250000 : size_t nplus = n + HEADER_SIZE;
74 : :
75 [ + - ]: 250000 : if (nplus >= n)
76 : : {
77 : 250000 : char *p = (char *) malloc (nplus);
78 : :
79 [ + - ]: 250000 : if (p != NULL)
80 : : {
81 : : size_t slot;
82 : :
83 : 250000 : p += HEADER_SIZE;
84 : :
85 : : /* Put a magic number into the indicator word. */
86 : 250000 : ((int *) p)[-1] = MAGIC_NUMBER;
87 : :
88 : : /* Enter p into the hash table. */
89 : 250000 : slot = (uintptr_t) p % HASH_TABLE_SIZE;
90 : 250000 : ((struct header *) (p - HEADER_SIZE))->next = mmalloca_results[slot];
91 : 250000 : mmalloca_results[slot] = p;
92 : :
93 : 250000 : return p;
94 : : }
95 : : }
96 : : /* Out of memory. */
97 : 250000 : return NULL;
98 : : #else
99 : : # if !MALLOC_0_IS_NONNULL
100 : : if (n == 0)
101 : : n = 1;
102 : : # endif
103 : : return malloc (n);
104 : : #endif
105 : : }
106 : :
107 : : #if HAVE_ALLOCA
108 : : void
109 : 500000 : freea (void *p)
110 : : {
111 : : /* mmalloca() may have returned NULL. */
112 [ + - ]: 500000 : if (p != NULL)
113 : : {
114 : : /* Attempt to quickly distinguish the mmalloca() result - which has
115 : : a magic indicator word - and the alloca() result - which has an
116 : : uninitialized indicator word. It is for this test that sa_increment
117 : : additional bytes are allocated in the alloca() case. */
118 [ + + ]: 500000 : if (((int *) p)[-1] == MAGIC_NUMBER)
119 : : {
120 : : /* Looks like a mmalloca() result. To see whether it really is one,
121 : : perform a lookup in the hash table. */
122 : 250000 : size_t slot = (uintptr_t) p % HASH_TABLE_SIZE;
123 : 250000 : void **chain = &mmalloca_results[slot];
124 [ + - ]: 250000 : for (; *chain != NULL;)
125 : : {
126 [ + - ]: 250000 : if (*chain == p)
127 : : {
128 : : /* Found it. Remove it from the hash table and free it. */
129 : 250000 : char *p_begin = (char *) p - HEADER_SIZE;
130 : 250000 : *chain = ((struct header *) p_begin)->next;
131 : 250000 : free (p_begin);
132 : 500000 : return;
133 : : }
134 : 0 : chain = &((struct header *) ((char *) *chain - HEADER_SIZE))->next;
135 : : }
136 : : }
137 : : /* At this point, we know it was not a mmalloca() result. */
138 : : }
139 : : }
140 : : #endif
|