xref: /openbsd-src/regress/lib/libc/malloc/malloc_errs/malloc_errs.c (revision ff0e7be1ebbcc809ea8ad2b6dafe215824da9e46)
1 /*	$OpenBSD: malloc_errs.c,v 1.3 2023/06/04 06:58:33 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 struct test {
230 	void (*test)(void);
231 	const char *flags;
232 };
233 
234 struct test tests[] = {
235 	{ t0, "" },
236 	{ t1, "" },
237 	{ t2, "" },
238 	{ t3, "F" },
239 	{ t4, "" },
240 	{ t5, "" },
241 	{ t6, "J" },
242 	{ t7, "JJ" },
243 	{ t8, "FJ" },
244 	{ t9, "C" },
245 	{ t9, "JC" }, /* t10 re-uses code from t9 */
246 	{ t11, "" },
247 	{ t12, "" },
248 	{ t13, "" },
249 	{ t14, "C" },
250 	{ t15, "" },
251 	{ t16, "" },
252 	{ t17, "C" },
253 	{ t18, "" },
254 	{ t19, "" },
255 	{ t20, "C" },
256 };
257 
258 int main(int argc, char *argv[])
259 {
260 
261 	const struct rlimit lim = {0, 0};
262 	int i, status;
263 	pid_t pid;
264 	char num[10];
265 	char options[10];
266 	extern char* malloc_options;
267 
268 	if (argc == 3) {
269 		malloc_options = argv[2];
270 		/* prevent coredumps */
271 		setrlimit(RLIMIT_CORE, &lim);
272 		i = atoi(argv[1]);
273 		fprintf(stderr, "Test %d\n", i);
274 		(*tests[i].test)();
275 		return 0;
276 	}
277 
278 	for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
279 		pid = fork();
280 		switch (pid) {
281 		case 0:
282 			snprintf(options, sizeof(options), "us%s", tests[i].flags);
283 			snprintf(num, sizeof(num), "%d", i);
284 			execl(argv[0], argv[0], num, options, NULL);
285 			err(1, "exec");
286 		break;
287 		case -1:
288 			err(1, "fork");
289 		break;
290 		default:
291 			if (waitpid(pid, &status, 0) == -1)
292 				err(1, "wait");
293 			if (!WIFSIGNALED(status) ||
294 			    WTERMSIG(status) != SIGABRT)
295 			errx(1, "Test %d did not abort", i);
296 		break;
297 		}
298 	}
299 	return 0;
300 }
301 
302