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
clearq(void * p)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
t0(void)48 t0(void)
49 {
50 abort();
51 }
52
53 /* double free >= page size */
54 void
t1(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
t2(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
t3(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
t4(void)86 t4(void)
87 {
88 free((void*)1);
89 }
90
91 /* realloc of bogus pointer */
92 void
t5(void)93 t5(void)
94 {
95 realloc((void*)1, 10);
96 }
97
98 /* write after free for chunk */
99 void
t6(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
t7(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
t8(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
t9(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
t11(void)151 t11(void)
152 {
153 char *p = malloc(100);
154 free(p + 1);
155 }
156
157 /* free chunk pointer */
158 void
t12(void)159 t12(void)
160 {
161 char *p = malloc(16);
162 free(p + 16);
163 }
164
165 /* freezero with wrong size */
166 void
t13(void)167 t13(void)
168 {
169 char *p = malloc(16);
170 freezero(p, 17);
171 }
172
173 /* freezero with wrong size 2 */
174 void
t14(void)175 t14(void)
176 {
177 char *p = malloc(15);
178 freezero(p, 16);
179 }
180
181 /* freezero with wrong size, pages */
182 void
t15(void)183 t15(void)
184 {
185 char *p = malloc(getpagesize());
186 freezero(p, getpagesize() + 1);
187 }
188
189 /* recallocarray with wrong size */
190 void
t16(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
t17(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
t18(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
t19(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
t20(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
t22(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
main(int argc,char * argv[])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