/* $OpenBSD: malloc_errs.c,v 1.5 2024/04/14 17:47:41 otto Exp $ */ /* * Copyright (c) 2023 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include /* Test erroneous use of API and heap that malloc should catch */ void clearq(void *p) { int i; void *q; /* Clear delayed free queue */ for (i = 0; i < 400; i++) { q = malloc(100); free(q); if (p == q) { fprintf(stderr, "Re-use\n"); abort(); } } } /* test the test setup */ void t0(void) { abort(); } /* double free >= page size */ void t1(void) { void *p = malloc(10000); free(p); free(p); } /* double free chunks are different, have a delayed free list */ void t2(void) { void *p, *q; int i; p = malloc(100); free(p); clearq(p); free(p); } /* double free without clearing delayed free list, needs F */ void t3(void) { void *p = malloc(100); free(p); free(p); } /* free without prior allocation */ void t4(void) { free((void*)1); } /* realloc of bogus pointer */ void t5(void) { realloc((void*)1, 10); } /* write after free for chunk */ void t6(void) { char *p = malloc(32); free(p); p[0] = ~p[0]; clearq(NULL); } /* write after free large alloction */ void t7(void) { char *p, *q; int i; p = malloc(10000); free(p); p[0] = ~p[0]; /* force re-use from the cache */ for (i = 0; i < 100; i++) { q = malloc(10000); free(q); } } /* write after free for chunk, no clearing of delayed free queue */ void t8(void) { char *p, *q; p = malloc(32); q = malloc(32); free(p); p[0] = ~p[0]; free(q); } /* canary check */ void t9(void) { char *p = malloc(100); p[100] = 0; free(p); } /* t10 is the same as t9 with different flags */ /* modified chunk pointer */ void t11(void) { char *p = malloc(100); free(p + 1); } /* free chunk pointer */ void t12(void) { char *p = malloc(16); free(p + 16); } /* freezero with wrong size */ void t13(void) { char *p = malloc(16); freezero(p, 17); } /* freezero with wrong size 2 */ void t14(void) { char *p = malloc(15); freezero(p, 16); } /* freezero with wrong size, pages */ void t15(void) { char *p = malloc(getpagesize()); freezero(p, getpagesize() + 1); } /* recallocarray with wrong size */ void t16(void) { char *p = recallocarray(NULL, 0, 16, 1); char *q = recallocarray(p, 2, 3, 16); } /* recallocarray with wrong size 2 */ void t17(void) { char *p = recallocarray(NULL, 0, 15, 1); char *q = recallocarray(p, 2, 3, 15); } /* recallocarray with wrong size, pages */ void t18(void) { char *p = recallocarray(NULL, 0, 1, getpagesize()); char *q = recallocarray(p, 2, 3, getpagesize()); } /* recallocarray with wrong size, pages */ void t19(void) { char *p = recallocarray(NULL, 0, 1, 10 * getpagesize()); char *q = recallocarray(p, 1, 2, 4 * getpagesize()); } /* canary check pages */ void t20(void) { char *p = malloc(2*getpagesize() - 100); p[2*getpagesize() - 100] = 0; free(p); } /* out-of-bound write preceding chunk */ void t22(void) { int i, j; unsigned char *p; while (1) { uintptr_t address; p = malloc(32); address = (uintptr_t)(void *)p; /* we don't want to have a chunk on the last slot of a page */ if (address / getpagesize() == (address + 32) / getpagesize()) break; free(p); } p[32] = 0; for (i = 0; i < 10000; i++) p = malloc(32); } struct test { void (*test)(void); const char *flags; }; struct test tests[] = { { t0, "" }, { t1, "" }, { t2, "" }, { t3, "F" }, { t4, "" }, { t5, "" }, { t6, "J" }, { t7, "JJ" }, { t8, "FJ" }, { t9, "C" }, { t9, "JC" }, /* t10 re-uses code from t9 */ { t11, "" }, { t12, "" }, { t13, "" }, { t14, "C" }, { t15, "" }, { t16, "" }, { t17, "C" }, { t18, "" }, { t19, "" }, { t20, "C" }, { t8, "FJD" }, /* t21 re-uses code from t8 */ { t22, "J" }, { t22, "JD" }, /* t23 re-uses code from t22 */ }; int main(int argc, char *argv[]) { const struct rlimit lim = {0, 0}; int i, status; pid_t pid; char num[10]; char options[10]; extern char* malloc_options; if (argc == 3) { malloc_options = argv[2]; /* prevent coredumps */ setrlimit(RLIMIT_CORE, &lim); i = atoi(argv[1]); fprintf(stderr, "Test %d\n", i); (*tests[i].test)(); return 0; } for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { pid = fork(); switch (pid) { case 0: snprintf(options, sizeof(options), "us%s", tests[i].flags); snprintf(num, sizeof(num), "%d", i); execl(argv[0], argv[0], num, options, NULL); err(1, "exec"); break; case -1: err(1, "fork"); break; default: if (waitpid(pid, &status, 0) == -1) err(1, "wait"); if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT) errx(1, "Test %d did not abort", i); break; } } return 0; }