--- /dev/null
+/* $OpenBSD: malloc_errs.c,v 1.1 2023/05/08 11:12:44 otto Exp $ */
+/*
+ * Copyright (c) 2023 Otto Moerbeek <otto@drijf.net>
+ *
+ * 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 <sys/resource.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+
+/* 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;
+
+ 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)
+{
+ abort(); /* not yet */
+ 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)
+{
+ abort(); /* not yet */
+ char *p = recallocarray(NULL, 0, 1, getpagesize());
+ char *q = recallocarray(p, 2, 3, getpagesize());
+}
+
+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, "" },
+};
+
+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), "cfjgu%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;
+}
+