xref: /openbsd-src/regress/lib/libc/malloc/malloc_errs/malloc_errs.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
1 /*	$OpenBSD: malloc_errs.c,v 1.4 2023/10/22 12:20:07 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 <signal.h>
24 #include <unistd.h>
25 
26 /* Test erroneous use of API and heap that malloc should catch */
27 
28 void
29 clearq(void *p)
30 {
31 	int i;
32 	void *q;
33 
34 	/* Clear delayed free queue */
35 	for (i = 0; i < 400; i++) {
36 		q = malloc(100);
37 		free(q);
38 		if (p == q) {
39 			fprintf(stderr, "Re-use\n");
40 			abort();
41 		}
42 	}
43 }
44 
45 /* test the test setup */
46 void
47 t0(void)
48 {
49 	abort();
50 }
51 
52 /* double free >= page size */
53 void
54 t1(void)
55 {
56 	void *p = malloc(10000);
57 	free(p);
58 	free(p);
59 }
60 
61 /* double free chunks are different, have a delayed free list */
62 void
63 t2(void)
64 {
65 	void *p, *q;
66 	int i;
67 
68 	p = malloc(100);
69 	free(p);
70 	clearq(p);
71 	free(p);
72 }
73 
74 /* double free without clearing delayed free list, needs F */
75 void
76 t3(void)
77 {
78 	void *p = malloc(100);
79 	free(p);
80 	free(p);
81 }
82 
83 /* free without prior allocation */
84 void
85 t4(void)
86 {
87 	free((void*)1);
88 }
89 
90 /* realloc of bogus pointer */
91 void
92 t5(void)
93 {
94 	realloc((void*)1, 10);
95 }
96 
97 /* write after free for chunk */
98 void
99 t6(void)
100 {
101 	char *p = malloc(32);
102 	free(p);
103 	p[0] = ~p[0];
104 	clearq(NULL);
105 }
106 
107 /* write after free large alloction */
108 void
109 t7(void)
110 {
111 	char *p, *q;
112 	int i;
113 
114 	p = malloc(10000);
115 	free(p);
116 	p[0] = ~p[0];
117 	/* force re-use from the cache */
118 	for (i = 0; i < 100; i++) {
119 		q = malloc(10000);
120 		free(q);
121 	}
122 }
123 
124 /* write after free for chunk, no clearing of delayed free queue */
125 void
126 t8(void)
127 {
128 	char *p, *q;
129 
130 	p = malloc(32);
131 	q = malloc(32);
132 	free(p);
133 	p[0] = ~p[0];
134 	free(q);
135 }
136 
137 /* canary check */
138 void
139 t9(void)
140 {
141 	char *p = malloc(100);
142 	p[100] = 0;
143 	free(p);
144 }
145 
146 /* t10 is the same as t9 with different flags */
147 
148 /* modified chunk pointer */
149 void
150 t11(void)
151 {
152 	char *p = malloc(100);
153 	free(p + 1);
154 }
155 
156 /* free chunk pointer */
157 void
158 t12(void)
159 {
160 	char *p = malloc(16);
161 	free(p + 16);
162 }
163 
164 /* freezero with wrong size */
165 void
166 t13(void)
167 {
168 	char *p = malloc(16);
169 	freezero(p, 17);
170 }
171 
172 /* freezero with wrong size 2 */
173 void
174 t14(void)
175 {
176 	char *p = malloc(15);
177 	freezero(p, 16);
178 }
179 
180 /* freezero with wrong size, pages */
181 void
182 t15(void)
183 {
184 	char *p = malloc(getpagesize());
185 	freezero(p, getpagesize() + 1);
186 }
187 
188 /* recallocarray with wrong size */
189 void
190 t16(void)
191 {
192 	char *p = recallocarray(NULL, 0, 16, 1);
193 	char *q = recallocarray(p, 2, 3, 16);
194 }
195 
196 /* recallocarray with wrong size 2 */
197 void
198 t17(void)
199 {
200 	char *p = recallocarray(NULL, 0, 15, 1);
201 	char *q = recallocarray(p, 2, 3, 15);
202 }
203 
204 /* recallocarray with wrong size, pages */
205 void
206 t18(void)
207 {
208 	char *p = recallocarray(NULL, 0, 1, getpagesize());
209 	char *q = recallocarray(p, 2, 3, getpagesize());
210 }
211 
212 /* recallocarray with wrong size, pages */
213 void
214 t19(void)
215 {
216 	char *p = recallocarray(NULL, 0, 1, 10 * getpagesize());
217 	char *q = recallocarray(p, 1, 2, 4 * getpagesize());
218 }
219 
220 /* canary check pages */
221 void
222 t20(void)
223 {
224 	char *p = malloc(2*getpagesize() - 100);
225 	p[2*getpagesize() - 100] = 0;
226 	free(p);
227 }
228 
229 /* out-of-bound write preceding chunk */
230 void
231 t22(void)
232 {
233 	int i, j;
234 	unsigned char *p = malloc(32);
235 	p[32] = 0;
236 	for (i = 0; i < 10000; i++)
237 		p = malloc(32);
238 }
239 
240 struct test {
241 	void (*test)(void);
242 	const char *flags;
243 };
244 
245 struct test tests[] = {
246 	{ t0, "" },
247 	{ t1, "" },
248 	{ t2, "" },
249 	{ t3, "F" },
250 	{ t4, "" },
251 	{ t5, "" },
252 	{ t6, "J" },
253 	{ t7, "JJ" },
254 	{ t8, "FJ" },
255 	{ t9, "C" },
256 	{ t9, "JC" }, /* t10 re-uses code from t9 */
257 	{ t11, "" },
258 	{ t12, "" },
259 	{ t13, "" },
260 	{ t14, "C" },
261 	{ t15, "" },
262 	{ t16, "" },
263 	{ t17, "C" },
264 	{ t18, "" },
265 	{ t19, "" },
266 	{ t20, "C" },
267 	{ t8, "FJD" }, /* t21 re-uses code from t8 */
268 	{ t22, "J" },
269 	{ t22, "JD" }, /* t23 re-uses code from t22 */
270 };
271 
272 int main(int argc, char *argv[])
273 {
274 
275 	const struct rlimit lim = {0, 0};
276 	int i, status;
277 	pid_t pid;
278 	char num[10];
279 	char options[10];
280 	extern char* malloc_options;
281 
282 	if (argc == 3) {
283 		malloc_options = argv[2];
284 		/* prevent coredumps */
285 		setrlimit(RLIMIT_CORE, &lim);
286 		i = atoi(argv[1]);
287 		fprintf(stderr, "Test %d\n", i);
288 		(*tests[i].test)();
289 		return 0;
290 	}
291 
292 	for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
293 		pid = fork();
294 		switch (pid) {
295 		case 0:
296 			snprintf(options, sizeof(options), "us%s", tests[i].flags);
297 			snprintf(num, sizeof(num), "%d", i);
298 			execl(argv[0], argv[0], num, options, NULL);
299 			err(1, "exec");
300 		break;
301 		case -1:
302 			err(1, "fork");
303 		break;
304 		default:
305 			if (waitpid(pid, &status, 0) == -1)
306 				err(1, "wait");
307 			if (!WIFSIGNALED(status) ||
308 			    WTERMSIG(status) != SIGABRT)
309 			errx(1, "Test %d did not abort", i);
310 		break;
311 		}
312 	}
313 	return 0;
314 }
315 
316