OLD | NEW |
(Empty) | |
| 1 /* |
| 2 american fuzzy lop - error-checking, memory-zeroing alloc routines |
| 3 ------------------------------------------------------------------ |
| 4 |
| 5 Written and maintained by Michal Zalewski <lcamtuf@google.com> |
| 6 |
| 7 Copyright 2013, 2014, 2015 Google Inc. All rights reserved. |
| 8 |
| 9 Licensed under the Apache License, Version 2.0 (the "License"); |
| 10 you may not use this file except in compliance with the License. |
| 11 You may obtain a copy of the License at: |
| 12 |
| 13 http://www.apache.org/licenses/LICENSE-2.0 |
| 14 |
| 15 This allocator is not designed to resist malicious attackers (the canaries |
| 16 are small and predictable), but provides a robust and portable way to detect |
| 17 use-after-free, off-by-one writes, stale pointers, and so on. |
| 18 |
| 19 */ |
| 20 |
| 21 #ifndef _HAVE_ALLOC_INL_H |
| 22 #define _HAVE_ALLOC_INL_H |
| 23 |
| 24 #include <stdio.h> |
| 25 #include <stdlib.h> |
| 26 #include <string.h> |
| 27 |
| 28 #include "config.h" |
| 29 #include "types.h" |
| 30 #include "debug.h" |
| 31 |
| 32 /* User-facing macro to sprintf() to a dynamically allocated buffer. */ |
| 33 |
| 34 #define alloc_printf(_str...) ({ \ |
| 35 u8* _tmp; \ |
| 36 s32 _len = snprintf(NULL, 0, _str); \ |
| 37 if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \ |
| 38 _tmp = ck_alloc(_len + 1); \ |
| 39 snprintf((char*)_tmp, _len + 1, _str); \ |
| 40 _tmp; \ |
| 41 }) |
| 42 |
| 43 /* Macro to enforce allocation limits as a last-resort defense against |
| 44 integer overflows. */ |
| 45 |
| 46 #define ALLOC_CHECK_SIZE(_s) do { \ |
| 47 if ((_s) > MAX_ALLOC) \ |
| 48 ABORT("Bad alloc request: %u bytes", (_s)); \ |
| 49 } while (0) |
| 50 |
| 51 /* Macro to check malloc() failures and the like. */ |
| 52 |
| 53 #define ALLOC_CHECK_RESULT(_r, _s) do { \ |
| 54 if (!(_r)) \ |
| 55 ABORT("Out of memory: can't allocate %u bytes", (_s)); \ |
| 56 } while (0) |
| 57 |
| 58 /* Magic tokens used to mark used / freed chunks. */ |
| 59 |
| 60 #define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */ |
| 61 #define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */ |
| 62 #define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */ |
| 63 |
| 64 /* Positions of guard tokens in relation to the user-visible pointer. */ |
| 65 |
| 66 #define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2]) |
| 67 #define ALLOC_S(_ptr) (((u32*)(_ptr))[-1]) |
| 68 #define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)]) |
| 69 |
| 70 #define ALLOC_OFF_HEAD 8 |
| 71 #define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1) |
| 72 |
| 73 /* Allocator increments for ck_realloc_block(). */ |
| 74 |
| 75 #define ALLOC_BLK_INC 256 |
| 76 |
| 77 /* Sanity-checking macros for pointers. */ |
| 78 |
| 79 #define CHECK_PTR(_p) do { \ |
| 80 if (_p) { \ |
| 81 if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\ |
| 82 if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \ |
| 83 ABORT("Use after free."); \ |
| 84 else ABORT("Corrupted head alloc canary."); \ |
| 85 } \ |
| 86 if (ALLOC_C2(_p) ^ ALLOC_MAGIC_C2) \ |
| 87 ABORT("Corrupted tail alloc canary."); \ |
| 88 } \ |
| 89 } while (0) |
| 90 |
| 91 #define CHECK_PTR_EXPR(_p) ({ \ |
| 92 typeof (_p) _tmp = (_p); \ |
| 93 CHECK_PTR(_tmp); \ |
| 94 _tmp; \ |
| 95 }) |
| 96 |
| 97 |
| 98 /* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized |
| 99 requests. */ |
| 100 |
| 101 static inline void* DFL_ck_alloc_nozero(u32 size) { |
| 102 |
| 103 void* ret; |
| 104 |
| 105 if (!size) return NULL; |
| 106 |
| 107 ALLOC_CHECK_SIZE(size); |
| 108 ret = malloc(size + ALLOC_OFF_TOTAL); |
| 109 ALLOC_CHECK_RESULT(ret, size); |
| 110 |
| 111 ret += ALLOC_OFF_HEAD; |
| 112 |
| 113 ALLOC_C1(ret) = ALLOC_MAGIC_C1; |
| 114 ALLOC_S(ret) = size; |
| 115 ALLOC_C2(ret) = ALLOC_MAGIC_C2; |
| 116 |
| 117 return ret; |
| 118 |
| 119 } |
| 120 |
| 121 |
| 122 /* Allocate a buffer, returning zeroed memory. */ |
| 123 |
| 124 static inline void* DFL_ck_alloc(u32 size) { |
| 125 |
| 126 void* mem; |
| 127 |
| 128 if (!size) return NULL; |
| 129 mem = DFL_ck_alloc_nozero(size); |
| 130 |
| 131 return memset(mem, 0, size); |
| 132 |
| 133 } |
| 134 |
| 135 |
| 136 /* Free memory, checking for double free and corrupted heap. When DEBUG_BUILD |
| 137 is set, the old memory will be also clobbered with 0xFF. */ |
| 138 |
| 139 static inline void DFL_ck_free(void* mem) { |
| 140 |
| 141 if (!mem) return; |
| 142 |
| 143 CHECK_PTR(mem); |
| 144 |
| 145 #ifdef DEBUG_BUILD |
| 146 |
| 147 /* Catch pointer issues sooner. */ |
| 148 memset(mem, 0xFF, ALLOC_S(mem)); |
| 149 |
| 150 #endif /* DEBUG_BUILD */ |
| 151 |
| 152 ALLOC_C1(mem) = ALLOC_MAGIC_F; |
| 153 |
| 154 free(mem - ALLOC_OFF_HEAD); |
| 155 |
| 156 } |
| 157 |
| 158 |
| 159 /* Re-allocate a buffer, checking for issues and zeroing any newly-added tail. |
| 160 With DEBUG_BUILD, the buffer is always reallocated to a new addresses and the |
| 161 old memory is clobbered with 0xFF. */ |
| 162 |
| 163 static inline void* DFL_ck_realloc(void* orig, u32 size) { |
| 164 |
| 165 void* ret; |
| 166 u32 old_size = 0; |
| 167 |
| 168 if (!size) { |
| 169 |
| 170 DFL_ck_free(orig); |
| 171 return NULL; |
| 172 |
| 173 } |
| 174 |
| 175 if (orig) { |
| 176 |
| 177 CHECK_PTR(orig); |
| 178 |
| 179 #ifndef DEBUG_BUILD |
| 180 ALLOC_C1(orig) = ALLOC_MAGIC_F; |
| 181 #endif /* !DEBUG_BUILD */ |
| 182 |
| 183 old_size = ALLOC_S(orig); |
| 184 orig -= ALLOC_OFF_HEAD; |
| 185 |
| 186 ALLOC_CHECK_SIZE(old_size); |
| 187 |
| 188 } |
| 189 |
| 190 ALLOC_CHECK_SIZE(size); |
| 191 |
| 192 #ifndef DEBUG_BUILD |
| 193 |
| 194 ret = realloc(orig, size + ALLOC_OFF_TOTAL); |
| 195 ALLOC_CHECK_RESULT(ret, size); |
| 196 |
| 197 #else |
| 198 |
| 199 /* Catch pointer issues sooner: force relocation and make sure that the |
| 200 original buffer is wiped. */ |
| 201 |
| 202 ret = malloc(size + ALLOC_OFF_TOTAL); |
| 203 ALLOC_CHECK_RESULT(ret, size); |
| 204 |
| 205 if (orig) { |
| 206 |
| 207 memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size)); |
| 208 memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size); |
| 209 |
| 210 ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; |
| 211 |
| 212 free(orig); |
| 213 |
| 214 } |
| 215 |
| 216 #endif /* ^!DEBUG_BUILD */ |
| 217 |
| 218 ret += ALLOC_OFF_HEAD; |
| 219 |
| 220 ALLOC_C1(ret) = ALLOC_MAGIC_C1; |
| 221 ALLOC_S(ret) = size; |
| 222 ALLOC_C2(ret) = ALLOC_MAGIC_C2; |
| 223 |
| 224 if (size > old_size) |
| 225 memset(ret + old_size, 0, size - old_size); |
| 226 |
| 227 return ret; |
| 228 |
| 229 } |
| 230 |
| 231 |
| 232 /* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up |
| 233 repeated small reallocs without complicating the user code). */ |
| 234 |
| 235 static inline void* DFL_ck_realloc_block(void* orig, u32 size) { |
| 236 |
| 237 #ifndef DEBUG_BUILD |
| 238 |
| 239 if (orig) { |
| 240 |
| 241 CHECK_PTR(orig); |
| 242 |
| 243 if (ALLOC_S(orig) >= size) return orig; |
| 244 |
| 245 size += ALLOC_BLK_INC; |
| 246 |
| 247 } |
| 248 |
| 249 #endif /* !DEBUG_BUILD */ |
| 250 |
| 251 return DFL_ck_realloc(orig, size); |
| 252 |
| 253 } |
| 254 |
| 255 |
| 256 /* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */ |
| 257 |
| 258 static inline u8* DFL_ck_strdup(u8* str) { |
| 259 |
| 260 void* ret; |
| 261 u32 size; |
| 262 |
| 263 if (!str) return NULL; |
| 264 |
| 265 size = strlen((char*)str) + 1; |
| 266 |
| 267 ALLOC_CHECK_SIZE(size); |
| 268 ret = malloc(size + ALLOC_OFF_TOTAL); |
| 269 ALLOC_CHECK_RESULT(ret, size); |
| 270 |
| 271 ret += ALLOC_OFF_HEAD; |
| 272 |
| 273 ALLOC_C1(ret) = ALLOC_MAGIC_C1; |
| 274 ALLOC_S(ret) = size; |
| 275 ALLOC_C2(ret) = ALLOC_MAGIC_C2; |
| 276 |
| 277 return memcpy(ret, str, size); |
| 278 |
| 279 } |
| 280 |
| 281 |
| 282 /* Create a buffer with a copy of a memory block. Returns NULL for zero-sized |
| 283 or NULL inputs. */ |
| 284 |
| 285 static inline void* DFL_ck_memdup(void* mem, u32 size) { |
| 286 |
| 287 void* ret; |
| 288 |
| 289 if (!mem || !size) return NULL; |
| 290 |
| 291 ALLOC_CHECK_SIZE(size); |
| 292 ret = malloc(size + ALLOC_OFF_TOTAL); |
| 293 ALLOC_CHECK_RESULT(ret, size); |
| 294 |
| 295 ret += ALLOC_OFF_HEAD; |
| 296 |
| 297 ALLOC_C1(ret) = ALLOC_MAGIC_C1; |
| 298 ALLOC_S(ret) = size; |
| 299 ALLOC_C2(ret) = ALLOC_MAGIC_C2; |
| 300 |
| 301 return memcpy(ret, mem, size); |
| 302 |
| 303 } |
| 304 |
| 305 |
| 306 /* Create a buffer with a block of text, appending a NUL terminator at the end. |
| 307 Returns NULL for zero-sized or NULL inputs. */ |
| 308 |
| 309 static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) { |
| 310 |
| 311 u8* ret; |
| 312 |
| 313 if (!mem || !size) return NULL; |
| 314 |
| 315 ALLOC_CHECK_SIZE(size); |
| 316 ret = malloc(size + ALLOC_OFF_TOTAL + 1); |
| 317 ALLOC_CHECK_RESULT(ret, size); |
| 318 |
| 319 ret += ALLOC_OFF_HEAD; |
| 320 |
| 321 ALLOC_C1(ret) = ALLOC_MAGIC_C1; |
| 322 ALLOC_S(ret) = size; |
| 323 ALLOC_C2(ret) = ALLOC_MAGIC_C2; |
| 324 |
| 325 memcpy(ret, mem, size); |
| 326 ret[size] = 0; |
| 327 |
| 328 return ret; |
| 329 |
| 330 } |
| 331 |
| 332 |
| 333 #ifndef DEBUG_BUILD |
| 334 |
| 335 /* In non-debug mode, we just do straightforward aliasing of the above functions |
| 336 to user-visible names such as ck_alloc(). */ |
| 337 |
| 338 #define ck_alloc DFL_ck_alloc |
| 339 #define ck_alloc_nozero DFL_ck_alloc_nozero |
| 340 #define ck_realloc DFL_ck_realloc |
| 341 #define ck_realloc_block DFL_ck_realloc_block |
| 342 #define ck_strdup DFL_ck_strdup |
| 343 #define ck_memdup DFL_ck_memdup |
| 344 #define ck_memdup_str DFL_ck_memdup_str |
| 345 #define ck_free DFL_ck_free |
| 346 |
| 347 #define alloc_report() |
| 348 |
| 349 #else |
| 350 |
| 351 /* In debugging mode, we also track allocations to detect memory leaks, and the |
| 352 flow goes through one more layer of indirection. */ |
| 353 |
| 354 /* Alloc tracking data structures: */ |
| 355 |
| 356 #define ALLOC_BUCKETS 4096 |
| 357 |
| 358 struct TRK_obj { |
| 359 void *ptr; |
| 360 char *file, *func; |
| 361 u32 line; |
| 362 }; |
| 363 |
| 364 #ifdef AFL_MAIN |
| 365 |
| 366 struct TRK_obj* TRK[ALLOC_BUCKETS]; |
| 367 u32 TRK_cnt[ALLOC_BUCKETS]; |
| 368 |
| 369 # define alloc_report() TRK_report() |
| 370 |
| 371 #else |
| 372 |
| 373 extern struct TRK_obj* TRK[ALLOC_BUCKETS]; |
| 374 extern u32 TRK_cnt[ALLOC_BUCKETS]; |
| 375 |
| 376 # define alloc_report() |
| 377 |
| 378 #endif /* ^AFL_MAIN */ |
| 379 |
| 380 /* Bucket-assigning function for a given pointer: */ |
| 381 |
| 382 #define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS) |
| 383 |
| 384 |
| 385 /* Add a new entry to the list of allocated objects. */ |
| 386 |
| 387 static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func, |
| 388 u32 line) { |
| 389 |
| 390 u32 i, bucket; |
| 391 |
| 392 if (!ptr) return; |
| 393 |
| 394 bucket = TRKH(ptr); |
| 395 |
| 396 /* Find a free slot in the list of entries for that bucket. */ |
| 397 |
| 398 for (i = 0; i < TRK_cnt[bucket]; i++) |
| 399 |
| 400 if (!TRK[bucket][i].ptr) { |
| 401 |
| 402 TRK[bucket][i].ptr = ptr; |
| 403 TRK[bucket][i].file = (char*)file; |
| 404 TRK[bucket][i].func = (char*)func; |
| 405 TRK[bucket][i].line = line; |
| 406 return; |
| 407 |
| 408 } |
| 409 |
| 410 /* No space available - allocate more. */ |
| 411 |
| 412 TRK[bucket] = DFL_ck_realloc_block(TRK[bucket], |
| 413 (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); |
| 414 |
| 415 TRK[bucket][i].ptr = ptr; |
| 416 TRK[bucket][i].file = (char*)file; |
| 417 TRK[bucket][i].func = (char*)func; |
| 418 TRK[bucket][i].line = line; |
| 419 |
| 420 TRK_cnt[bucket]++; |
| 421 |
| 422 } |
| 423 |
| 424 |
| 425 /* Remove entry from the list of allocated objects. */ |
| 426 |
| 427 static inline void TRK_free_buf(void* ptr, const char* file, const char* func, |
| 428 u32 line) { |
| 429 |
| 430 u32 i, bucket; |
| 431 |
| 432 if (!ptr) return; |
| 433 |
| 434 bucket = TRKH(ptr); |
| 435 |
| 436 /* Find the element on the list... */ |
| 437 |
| 438 for (i = 0; i < TRK_cnt[bucket]; i++) |
| 439 |
| 440 if (TRK[bucket][i].ptr == ptr) { |
| 441 |
| 442 TRK[bucket][i].ptr = 0; |
| 443 return; |
| 444 |
| 445 } |
| 446 |
| 447 WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)", |
| 448 func, file, line); |
| 449 |
| 450 } |
| 451 |
| 452 |
| 453 /* Do a final report on all non-deallocated objects. */ |
| 454 |
| 455 static inline void TRK_report(void) { |
| 456 |
| 457 u32 i, bucket; |
| 458 |
| 459 fflush(0); |
| 460 |
| 461 for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++) |
| 462 for (i = 0; i < TRK_cnt[bucket]; i++) |
| 463 if (TRK[bucket][i].ptr) |
| 464 WARNF("ALLOC: Memory never freed, created in %s (%s:%u)", |
| 465 TRK[bucket][i].func, TRK[bucket][i].file, TRK[bucket][i].line); |
| 466 |
| 467 } |
| 468 |
| 469 |
| 470 /* Simple wrappers for non-debugging functions: */ |
| 471 |
| 472 static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func, |
| 473 u32 line) { |
| 474 |
| 475 void* ret = DFL_ck_alloc(size); |
| 476 TRK_alloc_buf(ret, file, func, line); |
| 477 return ret; |
| 478 |
| 479 } |
| 480 |
| 481 |
| 482 static inline void* TRK_ck_realloc(void* orig, u32 size, const char* file, |
| 483 const char* func, u32 line) { |
| 484 |
| 485 void* ret = DFL_ck_realloc(orig, size); |
| 486 TRK_free_buf(orig, file, func, line); |
| 487 TRK_alloc_buf(ret, file, func, line); |
| 488 return ret; |
| 489 |
| 490 } |
| 491 |
| 492 |
| 493 static inline void* TRK_ck_realloc_block(void* orig, u32 size, const char* file, |
| 494 const char* func, u32 line) { |
| 495 |
| 496 void* ret = DFL_ck_realloc_block(orig, size); |
| 497 TRK_free_buf(orig, file, func, line); |
| 498 TRK_alloc_buf(ret, file, func, line); |
| 499 return ret; |
| 500 |
| 501 } |
| 502 |
| 503 |
| 504 static inline void* TRK_ck_strdup(u8* str, const char* file, const char* func, |
| 505 u32 line) { |
| 506 |
| 507 void* ret = DFL_ck_strdup(str); |
| 508 TRK_alloc_buf(ret, file, func, line); |
| 509 return ret; |
| 510 |
| 511 } |
| 512 |
| 513 |
| 514 static inline void* TRK_ck_memdup(void* mem, u32 size, const char* file, |
| 515 const char* func, u32 line) { |
| 516 |
| 517 void* ret = DFL_ck_memdup(mem, size); |
| 518 TRK_alloc_buf(ret, file, func, line); |
| 519 return ret; |
| 520 |
| 521 } |
| 522 |
| 523 |
| 524 static inline void* TRK_ck_memdup_str(void* mem, u32 size, const char* file, |
| 525 const char* func, u32 line) { |
| 526 |
| 527 void* ret = DFL_ck_memdup_str(mem, size); |
| 528 TRK_alloc_buf(ret, file, func, line); |
| 529 return ret; |
| 530 |
| 531 } |
| 532 |
| 533 |
| 534 static inline void TRK_ck_free(void* ptr, const char* file, |
| 535 const char* func, u32 line) { |
| 536 |
| 537 TRK_free_buf(ptr, file, func, line); |
| 538 DFL_ck_free(ptr); |
| 539 |
| 540 } |
| 541 |
| 542 /* Aliasing user-facing names to tracking functions: */ |
| 543 |
| 544 #define ck_alloc(_p1) \ |
| 545 TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__) |
| 546 |
| 547 #define ck_alloc_nozero(_p1) \ |
| 548 TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__) |
| 549 |
| 550 #define ck_realloc(_p1, _p2) \ |
| 551 TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) |
| 552 |
| 553 #define ck_realloc_block(_p1, _p2) \ |
| 554 TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) |
| 555 |
| 556 #define ck_strdup(_p1) \ |
| 557 TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__) |
| 558 |
| 559 #define ck_memdup(_p1, _p2) \ |
| 560 TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) |
| 561 |
| 562 #define ck_memdup_str(_p1, _p2) \ |
| 563 TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) |
| 564 |
| 565 #define ck_free(_p1) \ |
| 566 TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__) |
| 567 |
| 568 #endif /* ^!DEBUG_BUILD */ |
| 569 |
| 570 #endif /* ! _HAVE_ALLOC_INL_H */ |
OLD | NEW |