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