1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright © 2020 Keith Packard
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * 3. Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #define _DEFAULT_SOURCE
37 #include <stdlib.h>
38 #include <malloc.h>
39 #include <string.h>
40 #include <stdio.h>
41 #include <stdint.h>
42
43 #define NUM_MALLOC 128
44 #define MAX_ALLOC 1024
45
46 static uint8_t *blocks[NUM_MALLOC];
47 static size_t block_size[NUM_MALLOC];
48 static uint8_t block_data[NUM_MALLOC];
49
50 static int order[NUM_MALLOC];
51
52 static int
randint(int max)53 randint(int max)
54 {
55 /* not evenly distributed, but it's good enough */
56 return random() % max;
57 }
58
59 static void
shuffle_order(void)60 shuffle_order(void)
61 {
62 int i;
63 for (i = 0; i < NUM_MALLOC; i++)
64 order[i] = i;
65 for (i = 0; i < NUM_MALLOC - 1; i++) {
66 int j = randint(NUM_MALLOC - 1 - i) + i + 1;
67
68 int t = order[i];
69 order[i] = order[j];
70 order[j] = t;
71 }
72 }
73
74 static void
fill_block(int i)75 fill_block(int i)
76 {
77 block_data[i] = randint(256);
78 if (blocks[i])
79 memset(blocks[i], block_data[i], block_size[i]);
80 }
81
82 static void
reset_block(int i)83 reset_block(int i)
84 {
85 blocks[i] = NULL;
86 block_data[i] = 0;
87 block_size[i] = 0;
88 }
89
90 static void
reset_blocks(void)91 reset_blocks(void)
92 {
93 int i;
94
95 for (i = 0; i < NUM_MALLOC; i++)
96 reset_block(i);
97 }
98
99 static int
check_block(char * which,int i)100 check_block(char *which, int i)
101 {
102 size_t size = block_size[i];
103 uint8_t data = block_data[i];
104 uint8_t *block = blocks[i];
105 size_t j;
106
107 if (!block)
108 return 0;
109 for (j = 0; j < size; j++)
110 if (block[j] != data) {
111 printf("%s: wrong data in block %d at %u\n",
112 which, i, (unsigned) j);
113 return 1;
114 }
115 return 0;
116 }
117
118 static int
check_blocks(char * which)119 check_blocks(char *which)
120 {
121 int i;
122 int result = 0;
123
124 for (i = 0; i < NUM_MALLOC; i++)
125 result += check_block(which, i);
126 return result;
127 }
128
129 static int
check_malloc(size_t in_use)130 check_malloc(size_t in_use)
131 {
132 int result = 0;
133
134 struct mallinfo info = mallinfo();
135 #ifdef _NANO_MALLOC
136 if (info.arena < info.fordblks + in_use) {
137 printf("non-free bytes in arena %zu free %zu\n", info.arena, info.fordblks);
138 result++;
139 }
140 if (in_use == 0 && info.ordblks != 1) {
141 printf("%zd blocks free\n", info.ordblks);
142 result++;
143 }
144 if (info.uordblks < in_use) {
145 printf("expected at least %zu in use (%zu)\n", in_use, info.uordblks);
146 result++;
147 }
148 if (in_use == 0 && info.uordblks != 0) {
149 printf("expected all free but %zu still reported in use\n", info.uordblks);
150 result++;
151 }
152 #endif
153 return result;
154 }
155
156 #ifdef _NANO_MALLOC
157 extern size_t __malloc_minsize, __malloc_align, __malloc_head;
158 #endif
159
160 int
main(void)161 main(void)
162 {
163 int loops;
164 int result = 0;
165
166 for (loops = 0; loops < 10; loops++)
167 {
168 long i;
169 size_t in_use;
170
171 reset_blocks();
172 in_use = 0;
173
174 /* Test slowly increasing size of a block using realloc */
175
176 for (i = 0; i < NUM_MALLOC; i++) {
177 result += check_block("grow", 0);
178 size_t new_size = block_size[0] + randint(MAX_ALLOC);
179 uint8_t *new_block = realloc(blocks[0], new_size);
180 if (new_block || new_size == 0) {
181 blocks[0] = new_block;
182 block_size[0] = new_size;
183 in_use = block_size[0];
184 fill_block(0);
185 }
186 }
187
188 result += check_block("grow done", 0);
189 result += check_malloc(in_use);
190
191 free(blocks[0]);
192 in_use = 0;
193
194 /* Make sure everything is free */
195 result += check_malloc(in_use);
196
197 reset_blocks();
198
199 #pragma GCC diagnostic push
200 #ifndef __clang__
201 #pragma GCC diagnostic ignored "-Walloc-size-larger-than=PTRDIFF_MAX"
202 #endif
203 /* Test huge malloc sizes */
204 for (i = sizeof(size_t) * 8 - 2; i < (long) (sizeof(size_t) * 8); i++) {
205 blocks[0] = malloc((size_t) 1 << i);
206 if (blocks[0])
207 free(blocks[0]);
208 }
209
210 in_use = 0;
211
212 /* Make sure everything is free */
213 result += check_malloc(in_use);
214
215 reset_blocks();
216
217 /* Test allocating negative amounts */
218 for (i = -1; i >= -128; i--) {
219 blocks[0] = malloc((size_t) i);
220 #pragma GCC diagnostic pop
221 if (blocks[0]) {
222 printf("malloc size %ld succeeded\n", i);
223 result++;
224 free(blocks[0]);
225 }
226 }
227
228 in_use = 0;
229
230 /* Make sure everything is free */
231 result += check_malloc(in_use);
232
233 reset_blocks();
234
235 /* Test allocating random chunks */
236 for (i = 0; i < NUM_MALLOC; i++) {
237 size_t size = randint(MAX_ALLOC);
238 uint8_t *new_block = malloc(size);
239 if (new_block || size == 0) {
240 block_size[i] = size;
241 blocks[i] = new_block;
242 in_use += size;
243 fill_block(i);
244 }
245 }
246
247 result += check_blocks("malloc");
248 result += check_malloc(in_use);
249
250 /* Realloc them to new random sizes in a random order
251 */
252 shuffle_order();
253 for (i = 0; i < NUM_MALLOC; i++) {
254 size_t size = randint(MAX_ALLOC);
255 int j = order[i];
256 uint8_t *new_block = realloc(blocks[j], size);
257 if (new_block || size == 0) {
258 blocks[j] = new_block;
259 in_use -= block_size[j];
260 block_size[j] = size;
261 in_use += size;
262 fill_block(j);
263 }
264 }
265
266 result += check_blocks("realloc");
267 result += check_malloc(in_use);
268
269 shuffle_order();
270 for (i = 0; i < NUM_MALLOC; i++) {
271 int j = order[i];
272 check_block("realloc block", j);
273 free(blocks[j]);
274 in_use -= block_size[j];
275 }
276
277 result += check_malloc(in_use);
278 if (in_use != 0) {
279 printf("malloc stress accounting error\n");
280 result++;
281 }
282
283 reset_blocks();
284
285 for (i = 0; i < NUM_MALLOC; i++) {
286 size_t size = randint(MAX_ALLOC);
287 size_t align = 1 << (3 + randint(7));
288
289 uint8_t *new_block = memalign(align, size);
290 if (new_block || size == 0) {
291 block_size[i] = size;
292 blocks[i] = new_block;
293
294 if ((uintptr_t) blocks[i] & (align - 1)) {
295 printf("unaligned block returned %p align %zu size %zu\n",
296 blocks[i], align, size);
297 result++;
298 }
299 fill_block(i);
300 in_use += size;
301 }
302 }
303 result += check_blocks("memalign");
304 result += check_malloc(in_use);
305
306 shuffle_order();
307 for (i = 0; i < NUM_MALLOC; i++) {
308 int j = order[i];
309 check_block("free", j);
310 free(blocks[j]);
311 in_use -= block_size[j];
312 }
313
314 result += check_malloc(in_use);
315
316 if (in_use != 0) {
317 printf("malloc stress accounting error\n");
318 result++;
319 }
320 }
321
322 return result;
323 }
324