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