• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2008-2024 Stefan Krah. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 
28 #include <stdbool.h>
29 #include <stdlib.h>
30 
31 #include "mpdecimal.h"
32 #include "test.h"
33 
34 
35 /******************************************************************************/
36 /*               Primary allocation functions (normal or offset)              */
37 /******************************************************************************/
38 
39 static const size_t OFFSET = 16;
40 
41 #ifdef MPD_CONFIG_64
42 static const size_t alloc_limit = 0x4000000000000ULL;
43 #else
44 static size_t alloc_limit = SIZE_MAX;
45 #endif
46 
47 /* malloc with upper limits */
48 static void *
malloc_ceil(size_t size)49 malloc_ceil(size_t size)
50 {
51     if (size > alloc_limit) {
52         return NULL;
53     }
54 
55     return malloc(size);
56 }
57 
58 static void *
calloc_ceil(size_t nmemb,size_t size)59 calloc_ceil(size_t nmemb, size_t size)
60 {
61     if (nmemb > alloc_limit / size) {
62         return NULL;
63     }
64 
65     return calloc(nmemb, size);
66 }
67 
68 static void *
realloc_ceil(void * ptr,size_t size)69 realloc_ceil(void *ptr, size_t size)
70 {
71     if (size > alloc_limit) {
72         return NULL;
73     }
74 
75     return realloc(ptr, size);
76 }
77 
78 static void
free_ceil(void * ptr)79 free_ceil(void *ptr)
80 {
81     free(ptr);
82 }
83 
84 /* custom malloc with an offset and upper limits */
85 static void *
malloc_offset(size_t size)86 malloc_offset(size_t size)
87 {
88     if (size == 0 || size > SIZE_MAX - OFFSET) {
89         return NULL;
90     }
91 
92     char *ptr = malloc_ceil(OFFSET + size);
93 
94     return ptr ? ptr + OFFSET : NULL;
95 }
96 
97 static void *
calloc_offset(size_t nmemb,size_t size)98 calloc_offset(size_t nmemb, size_t size)
99 {
100     if (nmemb == 0 || size == 0 || size > SIZE_MAX - OFFSET) {
101         return NULL;
102     }
103 
104     char *ptr = calloc_ceil(nmemb, OFFSET + size);
105 
106     return ptr ? ptr + OFFSET : NULL;
107 }
108 
109 static void *
realloc_offset(void * ptr,size_t size)110 realloc_offset(void *ptr, size_t size)
111 {
112     if (size == 0 || size > SIZE_MAX - OFFSET) {
113         return NULL;
114     }
115 
116     char *c = (char *)ptr - OFFSET;
117     char *p = realloc_ceil(c, OFFSET + size);
118 
119     return p ? p + OFFSET : NULL;
120 }
121 
122 static void
free_offset(void * ptr)123 free_offset(void *ptr)
124 {
125     free((char *)ptr - OFFSET);
126 }
127 
128 /* active set of primary allocation functions */
129 static void *(* test_mallocfunc)(size_t size) = malloc_ceil;
130 static void *(* test_callocfunc)(size_t nmemb, size_t size) = calloc_ceil;
131 static void *(* test_reallocfunc)(void *ptr, size_t size) = realloc_ceil;
132 static void (* test_freefunc)(void *ptr) = free_ceil;
133 
134 
135 /******************************************************************************/
136 /*            Secondary allocation functions (count or failure mode)          */
137 /******************************************************************************/
138 
139 static bool enable_check_alloc = false;
140 int alloc_count;
141 int alloc_fail;
142 int alloc_idx;
143 
144 static void *
malloc_count(size_t size)145 malloc_count(size_t size)
146 {
147     ++alloc_count;
148     return test_mallocfunc(size);
149 }
150 
151 static void *
calloc_count(size_t nmemb,size_t size)152 calloc_count(size_t nmemb, size_t size)
153 {
154     ++alloc_count;
155     return test_callocfunc(nmemb, size);
156 }
157 
158 static void *
realloc_count(void * ptr,size_t size)159 realloc_count(void *ptr, size_t size)
160 {
161     ++alloc_count;
162     return test_reallocfunc(ptr, size);
163 }
164 
165 static void *
malloc_fail(size_t size)166 malloc_fail(size_t size)
167 {
168     if (++alloc_idx >= alloc_fail) {
169         return NULL;
170     }
171 
172     return test_mallocfunc(size);
173 }
174 
175 static void *
calloc_fail(size_t nmemb,size_t size)176 calloc_fail(size_t nmemb, size_t size)
177 {
178     if (++alloc_idx >= alloc_fail) {
179         return NULL;
180     }
181 
182     return test_callocfunc(nmemb, size);
183 }
184 
185 static void *
realloc_fail(void * ptr,size_t size)186 realloc_fail(void *ptr, size_t size)
187 {
188     if (++alloc_idx >= alloc_fail) {
189         return NULL;
190     }
191 
192     return test_reallocfunc(ptr, size);
193 }
194 
195 
196 /******************************************************************************/
197 /*                                 Public API                                 */
198 /******************************************************************************/
199 
200 /* choose primary allocation functions at program start */
201 void
mpd_init_alloc(bool custom_alloc,bool check_alloc)202 mpd_init_alloc(bool custom_alloc, bool check_alloc)
203 {
204     static bool initialized = false;
205 
206     if (initialized) {
207         fputs("mpd_init_alloc: error: cannot initialize twice\n", stderr);
208         exit(EXIT_FAILURE);
209     }
210     initialized = true;
211 
212     enable_check_alloc = check_alloc;
213 
214     if (custom_alloc) {
215         test_mallocfunc = malloc_offset;
216         test_callocfunc = calloc_offset;
217         test_reallocfunc = realloc_offset;
218         test_freefunc = free_offset;
219     }
220 
221     mpd_mallocfunc = test_mallocfunc;
222     mpd_callocfunc = test_callocfunc;
223     mpd_reallocfunc = test_reallocfunc;
224     mpd_free = test_freefunc;
225 }
226 
227 #ifdef MPD_CONFIG_32
228 void
mpd_set_alloc_limit(size_t size)229 mpd_set_alloc_limit(size_t size)
230 {
231     alloc_limit = size;
232 }
233 #endif
234 
235 void
mpd_set_alloc(mpd_context_t * ctx)236 mpd_set_alloc(mpd_context_t *ctx)
237 {
238     mpd_mallocfunc = test_mallocfunc;
239     mpd_callocfunc = test_callocfunc;
240     mpd_reallocfunc = test_reallocfunc;
241     mpd_free = test_freefunc;
242 
243     ctx->traps = MPD_Malloc_error;
244 }
245 
246 void
mpd_set_alloc_count(mpd_context_t * ctx)247 mpd_set_alloc_count(mpd_context_t *ctx)
248 {
249     mpd_mallocfunc = malloc_count;
250     mpd_callocfunc = calloc_count;
251     mpd_reallocfunc = realloc_count;
252     mpd_free = test_freefunc;
253 
254     ctx->traps = MPD_Malloc_error;
255     alloc_count = 0;
256 }
257 
258 void
mpd_set_alloc_fail(mpd_context_t * ctx)259 mpd_set_alloc_fail(mpd_context_t *ctx)
260 {
261     if (enable_check_alloc) {
262         mpd_mallocfunc = malloc_fail;
263         mpd_callocfunc = calloc_fail;
264         mpd_reallocfunc = realloc_fail;
265         mpd_free = test_freefunc;
266 
267         ctx->traps = 0;
268         alloc_idx = 0;
269     }
270 }
271