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