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