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 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 48 t0(void) 49 { 50 abort(); 51 } 52 53 /* double free >= page size */ 54 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 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 77 t3(void) 78 { 79 void *p = malloc(100); 80 free(p); 81 free(p); 82 } 83 84 /* free without prior allocation */ 85 void 86 t4(void) 87 { 88 free((void*)1); 89 } 90 91 /* realloc of bogus pointer */ 92 void 93 t5(void) 94 { 95 realloc((void*)1, 10); 96 } 97 98 /* write after free for chunk */ 99 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 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 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 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 151 t11(void) 152 { 153 char *p = malloc(100); 154 free(p + 1); 155 } 156 157 /* free chunk pointer */ 158 void 159 t12(void) 160 { 161 char *p = malloc(16); 162 free(p + 16); 163 } 164 165 /* freezero with wrong size */ 166 void 167 t13(void) 168 { 169 char *p = malloc(16); 170 freezero(p, 17); 171 } 172 173 /* freezero with wrong size 2 */ 174 void 175 t14(void) 176 { 177 char *p = malloc(15); 178 freezero(p, 16); 179 } 180 181 /* freezero with wrong size, pages */ 182 void 183 t15(void) 184 { 185 char *p = malloc(getpagesize()); 186 freezero(p, getpagesize() + 1); 187 } 188 189 /* recallocarray with wrong size */ 190 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 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 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 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 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 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 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