1 /*	$OpenBSD: malloc_errs.c,v 1.5 2024/04/14 17:47:41 otto Exp $	*/
2 /*
3  * Copyright (c) 2023 Otto Moerbeek <otto@drijf.net>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/resource.h>
19 #include <sys/wait.h>
20 #include <err.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdint.h>
24 #include <signal.h>
25 #include <unistd.h>
26 
27 /* Test erroneous use of API and heap that malloc should catch */
28 
29 void
30 clearq(void *p)
31 {
32 	int i;
33 	void *q;
34 
35 	/* Clear delayed free queue */
36 	for (i = 0; i < 400; i++) {
37 		q = malloc(100);
38 		free(q);
39 		if (p == q) {
40 			fprintf(stderr, "Re-use\n");
41 			abort();
42 		}
43 	}
44 }
45 
46 /* test the test setup */
47 void
48 t0(void)
49 {
50 	abort();
51 }
52 
53 /* double free >= page size */
54 void
55 t1(void)
56 {
57 	void *p = malloc(10000);
58 	free(p);
59 	free(p);
60 }
61 
62 /* double free chunks are different, have a delayed free list */
63 void
64 t2(void)
65 {
66 	void *p, *q;
67 	int i;
68 
69 	p = malloc(100);
70 	free(p);
71 	clearq(p);
72 	free(p);
73 }
74 
75 /* double free without clearing delayed free list, needs F */
76 void
77 t3(void)
78 {
79 	void *p = malloc(100);
80 	free(p);
81 	free(p);
82 }
83 
84 /* free without prior allocation */
85 void
86 t4(void)
87 {
88 	free((void*)1);
89 }
90 
91 /* realloc of bogus pointer */
92 void
93 t5(void)
94 {
95 	realloc((void*)1, 10);
96 }
97 
98 /* write after free for chunk */
99 void
100 t6(void)
101 {
102 	char *p = malloc(32);
103 	free(p);
104 	p[0] = ~p[0];
105 	clearq(NULL);
106 }
107 
108 /* write after free large alloction */
109 void
110 t7(void)
111 {
112 	char *p, *q;
113 	int i;
114 
115 	p = malloc(10000);
116 	free(p);
117 	p[0] = ~p[0];
118 	/* force re-use from the cache */
119 	for (i = 0; i < 100; i++) {
120 		q = malloc(10000);
121 		free(q);
122 	}
123 }
124 
125 /* write after free for chunk, no clearing of delayed free queue */
126 void
127 t8(void)
128 {
129 	char *p, *q;
130 
131 	p = malloc(32);
132 	q = malloc(32);
133 	free(p);
134 	p[0] = ~p[0];
135 	free(q);
136 }
137 
138 /* canary check */
139 void
140 t9(void)
141 {
142 	char *p = malloc(100);
143 	p[100] = 0;
144 	free(p);
145 }
146 
147 /* t10 is the same as t9 with different flags */
148 
149 /* modified chunk pointer */
150 void
151 t11(void)
152 {
153 	char *p = malloc(100);
154 	free(p + 1);
155 }
156 
157 /* free chunk pointer */
158 void
159 t12(void)
160 {
161 	char *p = malloc(16);
162 	free(p + 16);
163 }
164 
165 /* freezero with wrong size */
166 void
167 t13(void)
168 {
169 	char *p = malloc(16);
170 	freezero(p, 17);
171 }
172 
173 /* freezero with wrong size 2 */
174 void
175 t14(void)
176 {
177 	char *p = malloc(15);
178 	freezero(p, 16);
179 }
180 
181 /* freezero with wrong size, pages */
182 void
183 t15(void)
184 {
185 	char *p = malloc(getpagesize());
186 	freezero(p, getpagesize() + 1);
187 }
188 
189 /* recallocarray with wrong size */
190 void
191 t16(void)
192 {
193 	char *p = recallocarray(NULL, 0, 16, 1);
194 	char *q = recallocarray(p, 2, 3, 16);
195 }
196 
197 /* recallocarray with wrong size 2 */
198 void
199 t17(void)
200 {
201 	char *p = recallocarray(NULL, 0, 15, 1);
202 	char *q = recallocarray(p, 2, 3, 15);
203 }
204 
205 /* recallocarray with wrong size, pages */
206 void
207 t18(void)
208 {
209 	char *p = recallocarray(NULL, 0, 1, getpagesize());
210 	char *q = recallocarray(p, 2, 3, getpagesize());
211 }
212 
213 /* recallocarray with wrong size, pages */
214 void
215 t19(void)
216 {
217 	char *p = recallocarray(NULL, 0, 1, 10 * getpagesize());
218 	char *q = recallocarray(p, 1, 2, 4 * getpagesize());
219 }
220 
221 /* canary check pages */
222 void
223 t20(void)
224 {
225 	char *p = malloc(2*getpagesize() - 100);
226 	p[2*getpagesize() - 100] = 0;
227 	free(p);
228 }
229 
230 /* out-of-bound write preceding chunk */
231 void
232 t22(void)
233 {
234 	int i, j;
235 	unsigned char *p;
236 	while (1) {
237 		uintptr_t address;
238 		p = malloc(32);
239 		address = (uintptr_t)(void *)p;
240 		/* we don't want to have a chunk on the last slot of a page */
241 		if (address / getpagesize() == (address + 32) / getpagesize())
242 			break;
243 		free(p);
244 	}
245 	p[32] = 0;
246 	for (i = 0; i < 10000; i++)
247 		p = malloc(32);
248 }
249 
250 struct test {
251 	void (*test)(void);
252 	const char *flags;
253 };
254 
255 struct test tests[] = {
256 	{ t0, "" },
257 	{ t1, "" },
258 	{ t2, "" },
259 	{ t3, "F" },
260 	{ t4, "" },
261 	{ t5, "" },
262 	{ t6, "J" },
263 	{ t7, "JJ" },
264 	{ t8, "FJ" },
265 	{ t9, "C" },
266 	{ t9, "JC" }, /* t10 re-uses code from t9 */
267 	{ t11, "" },
268 	{ t12, "" },
269 	{ t13, "" },
270 	{ t14, "C" },
271 	{ t15, "" },
272 	{ t16, "" },
273 	{ t17, "C" },
274 	{ t18, "" },
275 	{ t19, "" },
276 	{ t20, "C" },
277 	{ t8, "FJD" }, /* t21 re-uses code from t8 */
278 	{ t22, "J" },
279 	{ t22, "JD" }, /* t23 re-uses code from t22 */
280 };
281 
282 int main(int argc, char *argv[])
283 {
284 
285 	const struct rlimit lim = {0, 0};
286 	int i, status;
287 	pid_t pid;
288 	char num[10];
289 	char options[10];
290 	extern char* malloc_options;
291 
292 	if (argc == 3) {
293 		malloc_options = argv[2];
294 		/* prevent coredumps */
295 		setrlimit(RLIMIT_CORE, &lim);
296 		i = atoi(argv[1]);
297 		fprintf(stderr, "Test %d\n", i);
298 		(*tests[i].test)();
299 		return 0;
300 	}
301 
302 	for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
303 		pid = fork();
304 		switch (pid) {
305 		case 0:
306 			snprintf(options, sizeof(options), "us%s", tests[i].flags);
307 			snprintf(num, sizeof(num), "%d", i);
308 			execl(argv[0], argv[0], num, options, NULL);
309 			err(1, "exec");
310 		break;
311 		case -1:
312 			err(1, "fork");
313 		break;
314 		default:
315 			if (waitpid(pid, &status, 0) == -1)
316 				err(1, "wait");
317 			if (!WIFSIGNALED(status) ||
318 			    WTERMSIG(status) != SIGABRT)
319 			errx(1, "Test %d did not abort", i);
320 		break;
321 		}
322 	}
323 	return 0;
324 }
325 
326