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