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
29void
30clearq(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 */
47void
48t0(void)
49{
50	abort();
51}
52
53/* double free >= page size */
54void
55t1(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 */
63void
64t2(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 */
76void
77t3(void)
78{
79	void *p = malloc(100);
80	free(p);
81	free(p);
82}
83
84/* free without prior allocation */
85void
86t4(void)
87{
88	free((void*)1);
89}
90
91/* realloc of bogus pointer */
92void
93t5(void)
94{
95	realloc((void*)1, 10);
96}
97
98/* write after free for chunk */
99void
100t6(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 */
109void
110t7(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 */
126void
127t8(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 */
139void
140t9(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 */
150void
151t11(void)
152{
153	char *p = malloc(100);
154	free(p + 1);
155}
156
157/* free chunk pointer */
158void
159t12(void)
160{
161	char *p = malloc(16);
162	free(p + 16);
163}
164
165/* freezero with wrong size */
166void
167t13(void)
168{
169	char *p = malloc(16);
170	freezero(p, 17);
171}
172
173/* freezero with wrong size 2 */
174void
175t14(void)
176{
177	char *p = malloc(15);
178	freezero(p, 16);
179}
180
181/* freezero with wrong size, pages */
182void
183t15(void)
184{
185	char *p = malloc(getpagesize());
186	freezero(p, getpagesize() + 1);
187}
188
189/* recallocarray with wrong size */
190void
191t16(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 */
198void
199t17(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 */
206void
207t18(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 */
214void
215t19(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 */
222void
223t20(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 */
231void
232t22(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
250struct test {
251	void (*test)(void);
252	const char *flags;
253};
254
255struct 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
282int 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