1 /*
2 * Copyright (c) 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 <cstdlib>
30 #include <cstdint>
31 #include <cinttypes>
32
33 #include "mpdecimal.h"
34
35 #include "decimal.hh"
36 #include "test.hh"
37
38
39 /******************************************************************************/
40 /* Exceptions */
41 /******************************************************************************/
42
43 namespace test {
what() const44 const char *Failure::what() const noexcept { return m.what(); }
45 } // namespace test
46
47
48 /******************************************************************************/
49 /* Functions */
50 /******************************************************************************/
51
52 namespace test {
53 void
assert_true(const char * file,const int64_t line,const bool p)54 assert_true(const char *file, const int64_t line, const bool p)
55 {
56 if (!(p)) {
57 raise(file, line, "assertion failed (expected true, got false)");
58 }
59 }
60
61 void
assert_false(const char * file,const int64_t line,const bool p)62 assert_false(const char *file, const int64_t line, const bool p)
63 {
64 if (p) {
65 raise(file, line, "assertion failed (expected false, got true)");
66 }
67 }
68 } // namespace test
69
70
71 /******************************************************************************/
72 /* Primary allocation functions (normal or offset) */
73 /******************************************************************************/
74
75 static const size_t OFFSET = 16;
76
77 #ifdef MPD_CONFIG_64
78 static const size_t alloc_limit = 0x4000000000000ULL;
79 #else
80 static thread_local size_t alloc_limit = SIZE_MAX;
81 #endif
82
83 /* malloc with upper limits */
84 static void *
malloc_ceil(size_t size)85 malloc_ceil(size_t size)
86 {
87 if (size > alloc_limit) {
88 return nullptr;
89 }
90
91 return malloc(size);
92 }
93
94 static void *
calloc_ceil(size_t nmemb,size_t size)95 calloc_ceil(size_t nmemb, size_t size)
96 {
97 if (nmemb > alloc_limit / size) {
98 return nullptr;
99 }
100
101 return calloc(nmemb, size);
102 }
103
104 static void *
realloc_ceil(void * ptr,size_t size)105 realloc_ceil(void *ptr, size_t size)
106 {
107 if (size > alloc_limit) {
108 return nullptr;
109 }
110
111 return realloc(ptr, size);
112 }
113
114 static void
free_ceil(void * ptr)115 free_ceil(void *ptr)
116 {
117 free(ptr);
118 }
119
120 /* custom malloc with an offset and upper limits */
121 static void *
malloc_offset(size_t size)122 malloc_offset(size_t size)
123 {
124 if (size == 0 || size > SIZE_MAX - OFFSET) {
125 return nullptr;
126 }
127
128 char *ptr = (char *)malloc_ceil(OFFSET + size);
129
130 return ptr ? ptr + OFFSET : nullptr;
131 }
132
133 static void *
calloc_offset(size_t nmemb,size_t size)134 calloc_offset(size_t nmemb, size_t size)
135 {
136 if (nmemb == 0 || size == 0 || size > SIZE_MAX - OFFSET) {
137 return nullptr;
138 }
139
140 char *ptr = (char *)calloc_ceil(nmemb, OFFSET + size);
141
142 return ptr ? ptr + OFFSET : nullptr;
143 }
144
145 static void *
realloc_offset(void * ptr,size_t size)146 realloc_offset(void *ptr, size_t size)
147 {
148 if (size == 0 || size > SIZE_MAX - OFFSET) {
149 return nullptr;
150 }
151
152 char *c = (char *)ptr - OFFSET;
153 char *p = (char *)realloc_ceil(c, OFFSET + size);
154
155 return p ? p + OFFSET : nullptr;
156 }
157
158 static void
free_offset(void * ptr)159 free_offset(void *ptr)
160 {
161 free((char *)ptr - OFFSET);
162 }
163
164 /* active set of primary allocation functions */
165 static void *(* test_mallocfunc)(size_t size) = malloc_ceil;
166 static void *(* test_callocfunc)(size_t nmemb, size_t size) = calloc_ceil;
167 static void *(* test_reallocfunc)(void *ptr, size_t size) = realloc_ceil;
168 static void (* test_freefunc)(void *ptr) = free_ceil;
169
170
171 /******************************************************************************/
172 /* Secondary allocation functions (count or failure mode) */
173 /******************************************************************************/
174
175 static bool enable_check_alloc = false;
176 static thread_local uint64_t alloc_fail = UINT64_MAX;
177 static thread_local uint64_t alloc_idx = 0;
178
179 static void *
malloc_fail(size_t size)180 malloc_fail(size_t size)
181 {
182 if (++alloc_idx >= alloc_fail) {
183 return nullptr;
184 }
185
186 return test_mallocfunc(size);
187 }
188
189 static void *
calloc_fail(size_t nmemb,size_t size)190 calloc_fail(size_t nmemb, size_t size)
191 {
192 if (++alloc_idx >= alloc_fail) {
193 return nullptr;
194 }
195
196 return test_callocfunc(nmemb, size);
197 }
198
199 static void *
realloc_fail(void * ptr,size_t size)200 realloc_fail(void *ptr, size_t size)
201 {
202 if (++alloc_idx >= alloc_fail) {
203 return nullptr;
204 }
205
206 return test_reallocfunc(ptr, size);
207 }
208
209 namespace test {
210
211 void
init_alloc(bool custom_alloc,bool check_alloc)212 init_alloc(bool custom_alloc, bool check_alloc)
213 {
214 static bool initialized = false;
215
216 if (initialized) {
217 fputs("mpd_init_alloc: error: cannot initialize twice\n", stderr);
218 exit(EXIT_FAILURE);
219 }
220 initialized = true;
221
222 /* initialization for the main thread */
223 #ifdef MPD_CONFIG_32
224 alloc_limit = SIZE_MAX;
225 #endif
226 alloc_fail = UINT64_MAX;
227 alloc_idx = 0;
228
229 enable_check_alloc = check_alloc;
230
231 if (custom_alloc) {
232 test_mallocfunc = malloc_offset;
233 test_callocfunc = calloc_offset;
234 test_reallocfunc = realloc_offset;
235 test_freefunc = free_offset;
236 }
237
238 mpd_mallocfunc = malloc_fail;
239 mpd_callocfunc = calloc_fail;
240 mpd_reallocfunc = realloc_fail;
241 mpd_free = test_freefunc;
242 }
243
244 #ifdef MPD_CONFIG_32
245 void
set_alloc_limit(size_t size)246 set_alloc_limit(size_t size)
247 {
248 alloc_limit = size;
249 }
250 #endif
251
252 void
set_alloc(decimal::Context & ctx)253 set_alloc(decimal::Context &ctx)
254 {
255 ctx.traps(MPD_Malloc_error);
256 alloc_idx = 0;
257 alloc_fail = UINT64_MAX;
258 }
259
260 void
set_alloc_fail(decimal::Context & ctx,uint64_t n)261 set_alloc_fail(decimal::Context &ctx, uint64_t n)
262 {
263 if (enable_check_alloc) {
264 ctx.traps(MPD_Malloc_error);
265 alloc_idx = 0;
266 alloc_fail = n;
267 }
268 }
269
270 } /* namespace test */
271