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