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 29void 30clearq(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 */ 47void 48t0(void) 49{ 50 abort(); 51} 52 53/* double free >= page size */ 54void 55t1(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 */ 63void 64t2(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 */ 76void 77t3(void) 78{ 79 void *p = malloc(100); 80 free(p); 81 free(p); 82} 83 84/* free without prior allocation */ 85void 86t4(void) 87{ 88 free((void*)1); 89} 90 91/* realloc of bogus pointer */ 92void 93t5(void) 94{ 95 realloc((void*)1, 10); 96} 97 98/* write after free for chunk */ 99void 100t6(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 */ 109void 110t7(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 */ 126void 127t8(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 */ 139void 140t9(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 */ 150void 151t11(void) 152{ 153 char *p = malloc(100); 154 free(p + 1); 155} 156 157/* free chunk pointer */ 158void 159t12(void) 160{ 161 char *p = malloc(16); 162 free(p + 16); 163} 164 165/* freezero with wrong size */ 166void 167t13(void) 168{ 169 char *p = malloc(16); 170 freezero(p, 17); 171} 172 173/* freezero with wrong size 2 */ 174void 175t14(void) 176{ 177 char *p = malloc(15); 178 freezero(p, 16); 179} 180 181/* freezero with wrong size, pages */ 182void 183t15(void) 184{ 185 char *p = malloc(getpagesize()); 186 freezero(p, getpagesize() + 1); 187} 188 189/* recallocarray with wrong size */ 190void 191t16(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 */ 198void 199t17(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 */ 206void 207t18(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 */ 214void 215t19(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 */ 222void 223t20(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 */ 231void 232t22(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 250struct test { 251 void (*test)(void); 252 const char *flags; 253}; 254 255struct 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 282int 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