xref: /openbsd-src/regress/lib/libc/malloc/malloc_errs/malloc_errs.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: malloc_errs.c,v 1.2 2023/05/09 19:07:37 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;
142 
143 	p = malloc(100);
144 	p[100] = 0;
145 	free(p);
146 }
147 
148 /* t10 is the same as t9 with different flags */
149 
150 /* modified chunk pointer */
151 void
152 t11(void)
153 {
154 	char *p = malloc(100);
155 	free(p + 1);
156 }
157 
158 /* free chunk pointer */
159 void
160 t12(void)
161 {
162 	char *p = malloc(16);
163 	free(p + 16);
164 }
165 
166 /* freezero with wrong size */
167 void
168 t13(void)
169 {
170 	char *p = malloc(16);
171 	freezero(p, 17);
172 }
173 
174 /* freezero with wrong size 2 */
175 void
176 t14(void)
177 {
178 	char *p = malloc(15);
179 	freezero(p, 16);
180 }
181 
182 /* freezero with wrong size, pages */
183 void
184 t15(void)
185 {
186 	char *p = malloc(getpagesize());
187 	freezero(p, getpagesize() + 1);
188 }
189 
190 /* recallocarray with wrong size */
191 void
192 t16(void)
193 {
194 	abort(); /* not yet */
195 	char *p = recallocarray(NULL, 0, 16, 1);
196 	char *q = recallocarray(p, 2, 3, 16);
197 }
198 
199 /* recallocarray with wrong size 2 */
200 void
201 t17(void)
202 {
203 	char *p = recallocarray(NULL, 0, 15, 1);
204 	char *q = recallocarray(p, 2, 3, 15);
205 }
206 
207 /* recallocarray with wrong size, pages */
208 void
209 t18(void)
210 {
211 	abort(); /* not yet */
212 	char *p = recallocarray(NULL, 0, 1, getpagesize());
213 	char *q = recallocarray(p, 2, 3, getpagesize());
214 }
215 
216 struct test {
217 	void (*test)(void);
218 	const char *flags;
219 };
220 
221 struct test tests[] = {
222 	{ t0, "" },
223 	{ t1, "" },
224 	{ t2, "" },
225 	{ t3, "F" },
226 	{ t4, "" },
227 	{ t5, "" },
228 	{ t6, "J" },
229 	{ t7, "JJ" },
230 	{ t8, "FJ" },
231 	{ t9, "C" },
232 	{ t9, "JC" }, /* t10 re-uses code from t9 */
233 	{ t11, "" },
234 	{ t12, "" },
235 	{ t13, "" },
236 	{ t14, "C" },
237 	{ t15, "" },
238 	{ t16, "" },
239 	{ t17, "C" },
240 	{ t18, "" },
241 };
242 
243 int main(int argc, char *argv[])
244 {
245 
246 	const struct rlimit lim = {0, 0};
247 	int i, status;
248 	pid_t pid;
249 	char num[10];
250 	char options[10];
251 	extern char* malloc_options;
252 
253 	if (argc == 3) {
254 		malloc_options = argv[2];
255 		/* prevent coredumps */
256 		setrlimit(RLIMIT_CORE, &lim);
257 		i = atoi(argv[1]);
258 		fprintf(stderr, "Test %d\n", i);
259 		(*tests[i].test)();
260 		return 0;
261 	}
262 
263 	for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
264 		pid = fork();
265 		switch (pid) {
266 		case 0:
267 			snprintf(options, sizeof(options), "us%s", tests[i].flags);
268 			snprintf(num, sizeof(num), "%d", i);
269 			execl(argv[0], argv[0], num, options, NULL);
270 			err(1, "exec");
271 		break;
272 		case -1:
273 			err(1, "fork");
274 		break;
275 		default:
276 			if (waitpid(pid, &status, 0) == -1)
277 				err(1, "wait");
278 			if (!WIFSIGNALED(status) ||
279 			    WTERMSIG(status) != SIGABRT)
280 			errx(1, "Test %d did not abort", i);
281 		break;
282 		}
283 	}
284 	return 0;
285 }
286 
287