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         (void) in_use;
135 #ifdef _NANO_MALLOC
136 	struct mallinfo info = mallinfo();
137 	if (info.arena < info.fordblks + in_use) {
138 		printf("non-free bytes in arena %zu free %zu\n", info.arena, info.fordblks);
139 		result++;
140 	}
141 	if (in_use == 0 && info.ordblks != 1) {
142 		printf("%zd blocks free\n", info.ordblks);
143 		result++;
144 	}
145 	if (info.uordblks < in_use) {
146 		printf("expected at least %zu in use (%zu)\n", in_use, info.uordblks);
147 		result++;
148 	}
149         if (in_use == 0 && info.uordblks != 0) {
150                 printf("expected all free but %zu still reported in use\n", info.uordblks);
151                 result++;
152         }
153 #endif
154 	return result;
155 }
156 
157 #ifdef _NANO_MALLOC
158 extern size_t __malloc_minsize, __malloc_align, __malloc_head;
159 #endif
160 
161 int
main(void)162 main(void)
163 {
164 	int loops;
165 	int result = 0;
166 
167 	for (loops = 0; loops < 10; loops++)
168 	{
169 		long i;
170 		size_t in_use;
171 
172 		reset_blocks();
173 		in_use = 0;
174 
175 		/* Test slowly increasing size of a block using realloc */
176 
177 		for (i = 0; i < NUM_MALLOC; i++) {
178 			result += check_block("grow", 0);
179                         size_t new_size = block_size[0] + randint(MAX_ALLOC);
180                         uint8_t *new_block = realloc(blocks[0], new_size);
181                         if (new_block || new_size == 0) {
182                             blocks[0] = new_block;
183                             block_size[0] = new_size;
184                             in_use = block_size[0];
185                             fill_block(0);
186                         }
187 		}
188 
189 		result += check_block("grow done", 0);
190 		result += check_malloc(in_use);
191 
192 		free(blocks[0]);
193 		in_use = 0;
194 
195 		/* Make sure everything is free */
196 		result += check_malloc(in_use);
197 
198 		reset_blocks();
199 
200 #pragma GCC diagnostic push
201 #ifndef __clang__
202 #pragma GCC diagnostic ignored "-Walloc-size-larger-than=PTRDIFF_MAX"
203 #endif
204 		/* Test huge malloc sizes */
205 		for (i = sizeof(size_t) * 8 - 2; i < (long) (sizeof(size_t) * 8); i++) {
206 			blocks[0] = malloc((size_t) 1 << i);
207 			if (blocks[0])
208 				free(blocks[0]);
209 		}
210 
211 		in_use = 0;
212 
213 		/* Make sure everything is free */
214 		result += check_malloc(in_use);
215 
216 		reset_blocks();
217 
218 		/* Test allocating negative amounts */
219 		for (i = -1; i >= -128; i--) {
220 			blocks[0] = malloc((size_t) i);
221 #pragma GCC diagnostic pop
222 			if (blocks[0]) {
223 				printf("malloc size %ld succeeded\n", i);
224 				result++;
225 				free(blocks[0]);
226 			}
227 		}
228 
229 		in_use = 0;
230 
231 		/* Make sure everything is free */
232 		result += check_malloc(in_use);
233 
234 		reset_blocks();
235 
236 		/* Test allocating random chunks */
237 		for (i = 0; i < NUM_MALLOC; i++) {
238 			size_t size = randint(MAX_ALLOC);
239                         uint8_t *new_block = malloc(size);
240                         if (new_block || size == 0) {
241                             block_size[i] = size;
242                             blocks[i] = new_block;
243                             in_use += size;
244                             fill_block(i);
245                         }
246 		}
247 
248 		result += check_blocks("malloc");
249 		result += check_malloc(in_use);
250 
251 		/* Realloc them to new random sizes in a random order
252 		 */
253 		shuffle_order();
254 		for (i = 0; i < NUM_MALLOC; i++) {
255 			size_t size = randint(MAX_ALLOC);
256 			int j = order[i];
257                         uint8_t *new_block = realloc(blocks[j], size);
258                         if (new_block || size == 0) {
259                             blocks[j] = new_block;
260                             in_use -= block_size[j];
261                             block_size[j] = size;
262                             in_use += size;
263                             fill_block(j);
264                         }
265 		}
266 
267 		result += check_blocks("realloc");
268 		result += check_malloc(in_use);
269 
270 		shuffle_order();
271 		for (i = 0; i < NUM_MALLOC; i++) {
272 			int j = order[i];
273 			check_block("realloc block", j);
274 			free(blocks[j]);
275 			in_use -= block_size[j];
276 		}
277 
278 		result += check_malloc(in_use);
279 		if (in_use != 0) {
280 			printf("malloc stress accounting error\n");
281 			result++;
282 		}
283 
284 		reset_blocks();
285 
286 		for (i = 0; i < NUM_MALLOC; i++) {
287 			size_t size = randint(MAX_ALLOC);
288 			size_t align = 1 << (3 + randint(7));
289 
290                         uint8_t *new_block = memalign(align, size);
291                         if (new_block || size == 0) {
292                             block_size[i] = size;
293                             blocks[i] = new_block;
294 
295                             if ((uintptr_t) blocks[i] & (align - 1)) {
296                                 printf("unaligned block returned %p align %zu size %zu\n",
297                                        blocks[i], align, size);
298                                 result++;
299                             }
300                             fill_block(i);
301                             in_use += size;
302                         }
303 		}
304 		result += check_blocks("memalign");
305 		result += check_malloc(in_use);
306 
307 		shuffle_order();
308 		for (i = 0; i < NUM_MALLOC; i++) {
309 			int j = order[i];
310 			check_block("free", j);
311 			free(blocks[j]);
312 			in_use -= block_size[j];
313 		}
314 
315 		result += check_malloc(in_use);
316 
317 		if (in_use != 0) {
318 			printf("malloc stress accounting error\n");
319 			result++;
320 		}
321 	}
322 
323 	return result;
324 }
325