1 /* $OpenBSD: malloc_errs.c,v 1.3 2023/06/04 06:58:33 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 = malloc(100); 142 p[100] = 0; 143 free(p); 144 } 145 146 /* t10 is the same as t9 with different flags */ 147 148 /* modified chunk pointer */ 149 void 150 t11(void) 151 { 152 char *p = malloc(100); 153 free(p + 1); 154 } 155 156 /* free chunk pointer */ 157 void 158 t12(void) 159 { 160 char *p = malloc(16); 161 free(p + 16); 162 } 163 164 /* freezero with wrong size */ 165 void 166 t13(void) 167 { 168 char *p = malloc(16); 169 freezero(p, 17); 170 } 171 172 /* freezero with wrong size 2 */ 173 void 174 t14(void) 175 { 176 char *p = malloc(15); 177 freezero(p, 16); 178 } 179 180 /* freezero with wrong size, pages */ 181 void 182 t15(void) 183 { 184 char *p = malloc(getpagesize()); 185 freezero(p, getpagesize() + 1); 186 } 187 188 /* recallocarray with wrong size */ 189 void 190 t16(void) 191 { 192 char *p = recallocarray(NULL, 0, 16, 1); 193 char *q = recallocarray(p, 2, 3, 16); 194 } 195 196 /* recallocarray with wrong size 2 */ 197 void 198 t17(void) 199 { 200 char *p = recallocarray(NULL, 0, 15, 1); 201 char *q = recallocarray(p, 2, 3, 15); 202 } 203 204 /* recallocarray with wrong size, pages */ 205 void 206 t18(void) 207 { 208 char *p = recallocarray(NULL, 0, 1, getpagesize()); 209 char *q = recallocarray(p, 2, 3, getpagesize()); 210 } 211 212 /* recallocarray with wrong size, pages */ 213 void 214 t19(void) 215 { 216 char *p = recallocarray(NULL, 0, 1, 10 * getpagesize()); 217 char *q = recallocarray(p, 1, 2, 4 * getpagesize()); 218 } 219 220 /* canary check pages */ 221 void 222 t20(void) 223 { 224 char *p = malloc(2*getpagesize() - 100); 225 p[2*getpagesize() - 100] = 0; 226 free(p); 227 } 228 229 struct test { 230 void (*test)(void); 231 const char *flags; 232 }; 233 234 struct test tests[] = { 235 { t0, "" }, 236 { t1, "" }, 237 { t2, "" }, 238 { t3, "F" }, 239 { t4, "" }, 240 { t5, "" }, 241 { t6, "J" }, 242 { t7, "JJ" }, 243 { t8, "FJ" }, 244 { t9, "C" }, 245 { t9, "JC" }, /* t10 re-uses code from t9 */ 246 { t11, "" }, 247 { t12, "" }, 248 { t13, "" }, 249 { t14, "C" }, 250 { t15, "" }, 251 { t16, "" }, 252 { t17, "C" }, 253 { t18, "" }, 254 { t19, "" }, 255 { t20, "C" }, 256 }; 257 258 int main(int argc, char *argv[]) 259 { 260 261 const struct rlimit lim = {0, 0}; 262 int i, status; 263 pid_t pid; 264 char num[10]; 265 char options[10]; 266 extern char* malloc_options; 267 268 if (argc == 3) { 269 malloc_options = argv[2]; 270 /* prevent coredumps */ 271 setrlimit(RLIMIT_CORE, &lim); 272 i = atoi(argv[1]); 273 fprintf(stderr, "Test %d\n", i); 274 (*tests[i].test)(); 275 return 0; 276 } 277 278 for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { 279 pid = fork(); 280 switch (pid) { 281 case 0: 282 snprintf(options, sizeof(options), "us%s", tests[i].flags); 283 snprintf(num, sizeof(num), "%d", i); 284 execl(argv[0], argv[0], num, options, NULL); 285 err(1, "exec"); 286 break; 287 case -1: 288 err(1, "fork"); 289 break; 290 default: 291 if (waitpid(pid, &status, 0) == -1) 292 err(1, "wait"); 293 if (!WIFSIGNALED(status) || 294 WTERMSIG(status) != SIGABRT) 295 errx(1, "Test %d did not abort", i); 296 break; 297 } 298 } 299 return 0; 300 } 301 302