Annotation of embedaddon/strongswan/src/libstrongswan/utils/leak_detective.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2013-2018 Tobias Brunner
! 3: * Copyright (C) 2006-2013 Martin Willi
! 4: * HSR Hochschule fuer Technik Rapperswil
! 5: *
! 6: * This program is free software; you can redistribute it and/or modify it
! 7: * under the terms of the GNU General Public License as published by the
! 8: * Free Software Foundation; either version 2 of the License, or (at your
! 9: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 10: *
! 11: * This program is distributed in the hope that it will be useful, but
! 12: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 13: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 14: * for more details.
! 15: */
! 16:
! 17: #define _GNU_SOURCE
! 18: #include <stddef.h>
! 19: #include <string.h>
! 20: #include <stdio.h>
! 21: #include <signal.h>
! 22: #include <unistd.h>
! 23: #include <locale.h>
! 24: #ifdef HAVE_DLADDR
! 25: #include <dlfcn.h>
! 26: #endif
! 27: #include <time.h>
! 28: #include <errno.h>
! 29:
! 30: #ifdef __APPLE__
! 31: #include <sys/mman.h>
! 32: #include <malloc/malloc.h>
! 33: /* overload some of our types clashing with mach */
! 34: #define host_t strongswan_host_t
! 35: #define processor_t strongswan_processor_t
! 36: #define thread_t strongswan_thread_t
! 37: #endif /* __APPLE__ */
! 38:
! 39: #include "leak_detective.h"
! 40:
! 41: #include <library.h>
! 42: #include <utils/utils.h>
! 43: #include <utils/debug.h>
! 44: #include <utils/backtrace.h>
! 45: #include <collections/hashtable.h>
! 46: #include <threading/thread_value.h>
! 47: #include <threading/spinlock.h>
! 48:
! 49: typedef struct private_leak_detective_t private_leak_detective_t;
! 50:
! 51: /**
! 52: * private data of leak_detective
! 53: */
! 54: struct private_leak_detective_t {
! 55:
! 56: /**
! 57: * public functions
! 58: */
! 59: leak_detective_t public;
! 60:
! 61: /**
! 62: * Registered report() function
! 63: */
! 64: leak_detective_report_cb_t report_cb;
! 65:
! 66: /**
! 67: * Registered report() summary function
! 68: */
! 69: leak_detective_summary_cb_t report_scb;
! 70:
! 71: /**
! 72: * Registered user data for callbacks
! 73: */
! 74: void *report_data;
! 75: };
! 76:
! 77: /**
! 78: * Magic value which helps to detect memory corruption. Yummy!
! 79: */
! 80: #define MEMORY_HEADER_MAGIC 0x7ac0be11
! 81:
! 82: /**
! 83: * Magic written to tail of allocation
! 84: */
! 85: #define MEMORY_TAIL_MAGIC 0xcafebabe
! 86:
! 87: /**
! 88: * Pattern which is filled in memory before freeing it
! 89: */
! 90: #define MEMORY_FREE_PATTERN 0xFF
! 91:
! 92: /**
! 93: * Pattern which is filled in newly allocated memory
! 94: */
! 95: #define MEMORY_ALLOC_PATTERN 0xEE
! 96:
! 97: typedef struct memory_header_t memory_header_t;
! 98: typedef struct memory_tail_t memory_tail_t;
! 99:
! 100: /**
! 101: * Header which is prepended to each allocated memory block
! 102: */
! 103: struct memory_header_t {
! 104:
! 105: /**
! 106: * Pointer to previous entry in linked list
! 107: */
! 108: memory_header_t *previous;
! 109:
! 110: /**
! 111: * Pointer to next entry in linked list
! 112: */
! 113: memory_header_t *next;
! 114:
! 115: /**
! 116: * backtrace taken during (re-)allocation
! 117: */
! 118: backtrace_t *backtrace;
! 119:
! 120: /**
! 121: * Padding to make sizeof(memory_header_t) == 32
! 122: */
! 123: uint32_t padding[sizeof(void*) == sizeof(uint32_t) ? 3 : 0];
! 124:
! 125: /**
! 126: * Number of bytes following after the header
! 127: */
! 128: uint32_t bytes;
! 129:
! 130: /**
! 131: * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
! 132: */
! 133: uint32_t magic;
! 134:
! 135: }__attribute__((__packed__));
! 136:
! 137: /**
! 138: * tail appended to each allocated memory block
! 139: */
! 140: struct memory_tail_t {
! 141:
! 142: /**
! 143: * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
! 144: */
! 145: uint32_t magic;
! 146:
! 147: }__attribute__((__packed__));
! 148:
! 149: /**
! 150: * first mem header is just a dummy to chain
! 151: * the others on it...
! 152: */
! 153: static memory_header_t first_header = {
! 154: .magic = MEMORY_HEADER_MAGIC,
! 155: };
! 156:
! 157: /**
! 158: * Spinlock to access header linked list
! 159: */
! 160: static spinlock_t *lock;
! 161:
! 162: /**
! 163: * Is leak detection currently enabled?
! 164: */
! 165: static bool enabled;
! 166:
! 167: /**
! 168: * Whether to report calls to free() with memory not allocated by us
! 169: */
! 170: static bool ignore_unknown;
! 171:
! 172: /**
! 173: * Is leak detection disabled for the current thread?
! 174: */
! 175: static thread_value_t *thread_disabled;
! 176:
! 177: /**
! 178: * Installs the malloc hooks, enables leak detection
! 179: */
! 180: static void enable_leak_detective()
! 181: {
! 182: enabled = TRUE;
! 183: }
! 184:
! 185: /**
! 186: * Uninstalls the malloc hooks, disables leak detection
! 187: */
! 188: static void disable_leak_detective()
! 189: {
! 190: enabled = FALSE;
! 191: }
! 192:
! 193: /**
! 194: * Enable/Disable leak detective for the current thread
! 195: *
! 196: * @return Previous value
! 197: */
! 198: static bool enable_thread(bool enable)
! 199: {
! 200: bool before;
! 201:
! 202: before = thread_disabled->get(thread_disabled) == NULL;
! 203: thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE);
! 204: return before;
! 205: }
! 206:
! 207: /**
! 208: * Add a header to the beginning of the list
! 209: */
! 210: static void add_hdr(memory_header_t *hdr)
! 211: {
! 212: lock->lock(lock);
! 213: hdr->next = first_header.next;
! 214: if (hdr->next)
! 215: {
! 216: hdr->next->previous = hdr;
! 217: }
! 218: hdr->previous = &first_header;
! 219: first_header.next = hdr;
! 220: lock->unlock(lock);
! 221: }
! 222:
! 223: /**
! 224: * Remove a header from the list
! 225: */
! 226: static void remove_hdr(memory_header_t *hdr)
! 227: {
! 228: lock->lock(lock);
! 229: if (hdr->next)
! 230: {
! 231: hdr->next->previous = hdr->previous;
! 232: }
! 233: hdr->previous->next = hdr->next;
! 234: lock->unlock(lock);
! 235: }
! 236:
! 237: /**
! 238: * Check if a header is in the list
! 239: */
! 240: static bool has_hdr(memory_header_t *hdr)
! 241: {
! 242: memory_header_t *current;
! 243: bool found = FALSE;
! 244:
! 245: lock->lock(lock);
! 246: for (current = &first_header; current != NULL; current = current->next)
! 247: {
! 248: if (current == hdr)
! 249: {
! 250: found = TRUE;
! 251: break;
! 252: }
! 253: }
! 254: lock->unlock(lock);
! 255:
! 256: return found;
! 257: }
! 258:
! 259: #ifdef __APPLE__
! 260:
! 261: /**
! 262: * Copy of original default zone, with functions we call in hooks
! 263: */
! 264: static malloc_zone_t original;
! 265:
! 266: /**
! 267: * Call original malloc()
! 268: */
! 269: static void* real_malloc(size_t size)
! 270: {
! 271: return original.malloc(malloc_default_zone(), size);
! 272: }
! 273:
! 274: /**
! 275: * Call original free()
! 276: */
! 277: static void real_free(void *ptr)
! 278: {
! 279: original.free(malloc_default_zone(), ptr);
! 280: }
! 281:
! 282: /**
! 283: * Call original realloc()
! 284: */
! 285: static void* real_realloc(void *ptr, size_t size)
! 286: {
! 287: return original.realloc(malloc_default_zone(), ptr, size);
! 288: }
! 289:
! 290: /**
! 291: * Hook definition: static function with _hook suffix, takes additional zone
! 292: */
! 293: #define HOOK(ret, name, ...) \
! 294: static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__)
! 295:
! 296: /**
! 297: * forward declaration of hooks
! 298: */
! 299: HOOK(void*, malloc, size_t bytes);
! 300: HOOK(void*, calloc, size_t nmemb, size_t size);
! 301: HOOK(void*, valloc, size_t size);
! 302: HOOK(void, free, void *ptr);
! 303: HOOK(void*, realloc, void *old, size_t bytes);
! 304:
! 305: /**
! 306: * malloc zone size(), must consider the memory header prepended
! 307: */
! 308: HOOK(size_t, size, const void *ptr)
! 309: {
! 310: bool before;
! 311: size_t size;
! 312:
! 313: if (enabled)
! 314: {
! 315: before = enable_thread(FALSE);
! 316: if (before)
! 317: {
! 318: ptr -= sizeof(memory_header_t);
! 319: }
! 320: }
! 321: size = original.size(malloc_default_zone(), ptr);
! 322: if (enabled)
! 323: {
! 324: enable_thread(before);
! 325: }
! 326: return size;
! 327: }
! 328:
! 329: /**
! 330: * Version of malloc zones we currently support
! 331: */
! 332: #define MALLOC_ZONE_VERSION 8 /* Snow Leopard */
! 333:
! 334: /**
! 335: * Hook-in our malloc functions into the default zone
! 336: */
! 337: static bool register_hooks()
! 338: {
! 339: static bool once = FALSE;
! 340: malloc_zone_t *zone;
! 341: void *page;
! 342:
! 343: if (once)
! 344: {
! 345: return TRUE;
! 346: }
! 347: once = TRUE;
! 348:
! 349: zone = malloc_default_zone();
! 350: if (zone->version != MALLOC_ZONE_VERSION)
! 351: {
! 352: DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)",
! 353: zone->version, MALLOC_ZONE_VERSION);
! 354: return FALSE;
! 355: }
! 356:
! 357: original = *zone;
! 358:
! 359: page = (void*)((uintptr_t)zone / getpagesize() * getpagesize());
! 360: if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0)
! 361: {
! 362: DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno));
! 363: return FALSE;
! 364: }
! 365:
! 366: zone->size = size_hook;
! 367: zone->malloc = malloc_hook;
! 368: zone->calloc = calloc_hook;
! 369: zone->valloc = valloc_hook;
! 370: zone->free = free_hook;
! 371: zone->realloc = realloc_hook;
! 372:
! 373: /* those other functions can be NULLed out to not use them */
! 374: zone->batch_malloc = NULL;
! 375: zone->batch_free = NULL;
! 376: zone->memalign = NULL;
! 377: zone->free_definite_size = NULL;
! 378:
! 379: return TRUE;
! 380: }
! 381:
! 382: #else /* !__APPLE__ */
! 383:
! 384: /**
! 385: * dlsym() might do a malloc(), but we can't do one before we get the malloc()
! 386: * function pointer. Use this minimalistic malloc implementation instead.
! 387: */
! 388: static void* malloc_for_dlsym(size_t size)
! 389: {
! 390: static char buf[1024] = {};
! 391: static size_t used = 0;
! 392: char *ptr;
! 393:
! 394: /* roundup to a multiple of 32 */
! 395: size = (size - 1) / 32 * 32 + 32;
! 396:
! 397: if (used + size > sizeof(buf))
! 398: {
! 399: return NULL;
! 400: }
! 401: ptr = buf + used;
! 402: used += size;
! 403: return ptr;
! 404: }
! 405:
! 406: /**
! 407: * Lookup a malloc function, while disabling wrappers
! 408: */
! 409: static void* get_malloc_fn(char *name)
! 410: {
! 411: bool before = FALSE;
! 412: void *fn;
! 413:
! 414: if (enabled)
! 415: {
! 416: before = enable_thread(FALSE);
! 417: }
! 418: fn = dlsym(RTLD_NEXT, name);
! 419: if (enabled)
! 420: {
! 421: enable_thread(before);
! 422: }
! 423: return fn;
! 424: }
! 425:
! 426: /**
! 427: * Call original malloc()
! 428: */
! 429: static void* real_malloc(size_t size)
! 430: {
! 431: static void* (*fn)(size_t size);
! 432: static int recursive = 0;
! 433:
! 434: if (!fn)
! 435: {
! 436: /* checking recursiveness should actually be thread-specific. But as
! 437: * it is very likely that the first allocation is done before we go
! 438: * multi-threaded, we keep it simple. */
! 439: if (recursive)
! 440: {
! 441: return malloc_for_dlsym(size);
! 442: }
! 443: recursive++;
! 444: fn = get_malloc_fn("malloc");
! 445: recursive--;
! 446: }
! 447: return fn(size);
! 448: }
! 449:
! 450: /**
! 451: * Call original free()
! 452: */
! 453: static void real_free(void *ptr)
! 454: {
! 455: static void (*fn)(void *ptr);
! 456:
! 457: if (!fn)
! 458: {
! 459: fn = get_malloc_fn("free");
! 460: }
! 461: return fn(ptr);
! 462: }
! 463:
! 464: /**
! 465: * Call original realloc()
! 466: */
! 467: static void* real_realloc(void *ptr, size_t size)
! 468: {
! 469: static void* (*fn)(void *ptr, size_t size);
! 470:
! 471: if (!fn)
! 472: {
! 473: fn = get_malloc_fn("realloc");
! 474: }
! 475: return fn(ptr, size);
! 476: }
! 477:
! 478: /**
! 479: * Hook definition: plain function overloading existing malloc calls
! 480: */
! 481: #define HOOK(ret, name, ...) ret name(__VA_ARGS__)
! 482:
! 483: /**
! 484: * Hook initialization when not using hooks, resolve functions.
! 485: */
! 486: static bool register_hooks()
! 487: {
! 488: void *buf = real_malloc(8);
! 489: buf = real_realloc(buf, 16);
! 490: real_free(buf);
! 491: return TRUE;
! 492: }
! 493:
! 494: #endif /* !__APPLE__ */
! 495:
! 496: /**
! 497: * Leak report white list
! 498: *
! 499: * List of functions using static allocation buffers or should be suppressed
! 500: * otherwise on leak report.
! 501: */
! 502: static char *whitelist[] = {
! 503: /* backtraces, including own */
! 504: "backtrace_create",
! 505: "strerror_safe",
! 506: /* pthread stuff */
! 507: "pthread_create",
! 508: "pthread_setspecific",
! 509: "__pthread_setspecific",
! 510: /* glibc functions */
! 511: "inet_ntoa",
! 512: "strerror",
! 513: "getprotobyname",
! 514: "getprotobynumber",
! 515: "getservbyport",
! 516: "getservbyname",
! 517: "gethostbyname",
! 518: "gethostbyname2",
! 519: "gethostbyname_r",
! 520: "gethostbyname2_r",
! 521: "getnetbyname",
! 522: "getpwnam_r",
! 523: "getgrnam_r",
! 524: "register_printf_function",
! 525: "register_printf_specifier",
! 526: "syslog",
! 527: "vsyslog",
! 528: "__syslog_chk",
! 529: "__vsyslog_chk",
! 530: "__fprintf_chk",
! 531: "getaddrinfo",
! 532: "setlocale",
! 533: "getpass",
! 534: "getpwent_r",
! 535: "setpwent",
! 536: "endpwent",
! 537: "getspnam_r",
! 538: "getpwuid_r",
! 539: "initgroups",
! 540: "tzset",
! 541: "_IO_file_doallocate",
! 542: /* ignore dlopen, as we do not dlclose to get proper leak reports */
! 543: "dlopen",
! 544: "dlerror",
! 545: "dlclose",
! 546: "dlsym",
! 547: /* mysql functions */
! 548: "mysql_init_character_set",
! 549: "init_client_errs",
! 550: "my_thread_init",
! 551: /* fastcgi library */
! 552: "FCGX_Init",
! 553: /* libxml */
! 554: "xmlInitCharEncodingHandlers",
! 555: "xmlInitParser",
! 556: "xmlInitParserCtxt",
! 557: /* libcurl */
! 558: "Curl_client_write",
! 559: /* libsoup */
! 560: "soup_add_timeout",
! 561: "soup_headers_parse_response",
! 562: "soup_message_headers_append",
! 563: "soup_message_headers_clear",
! 564: "soup_message_headers_get_list",
! 565: "soup_message_headers_get_one",
! 566: "soup_session_abort",
! 567: "soup_session_get_type",
! 568: "soup_session_remove_feature",
! 569: /* libldap */
! 570: "ldap_int_initialize",
! 571: /* ClearSilver */
! 572: "nerr_init",
! 573: /* libgcrypt */
! 574: "gcrypt_plugin_create",
! 575: "gcry_control",
! 576: "gcry_check_version",
! 577: "gcry_randomize",
! 578: "gcry_create_nonce",
! 579: /* OpenSSL: These are needed for unit-tests only, the openssl plugin
! 580: * does properly clean up any memory during destroy(). */
! 581: "ECDSA_do_sign_ex",
! 582: "ECDSA_verify",
! 583: "RSA_new_method",
! 584: /* OpenSSL 1.1.0 does not cleanup anymore until the library is unloaded */
! 585: "OPENSSL_init_crypto",
! 586: "CRYPTO_THREAD_lock_new",
! 587: "ERR_add_error_data",
! 588: "ERR_set_mark",
! 589: "ENGINE_load_builtin_engines",
! 590: "OPENSSL_load_builtin_modules",
! 591: "CONF_modules_load_file",
! 592: "CONF_module_add",
! 593: "RAND_DRBG_bytes",
! 594: "RAND_DRBG_generate",
! 595: "RAND_DRBG_get0_master",
! 596: "RAND_DRBG_get0_private",
! 597: "RAND_DRBG_get0_public",
! 598: /* OpenSSL libssl */
! 599: "SSL_COMP_get_compression_methods",
! 600: /* NSPR */
! 601: "PR_CallOnce",
! 602: /* libapr */
! 603: "apr_pool_create_ex",
! 604: /* glib */
! 605: "g_output_stream_write",
! 606: "g_resolver_lookup_by_name",
! 607: "g_signal_connect_data",
! 608: "g_socket_connection_factory_lookup_type",
! 609: "g_type_init_with_debug_flags",
! 610: "g_type_register_static",
! 611: "g_type_class_ref",
! 612: "g_type_create_instance",
! 613: "g_type_add_interface_static",
! 614: "g_type_interface_add_prerequisite",
! 615: "g_private_set",
! 616: "g_queue_pop_tail",
! 617: /* libgpg */
! 618: "gpg_err_init",
! 619: /* gnutls */
! 620: "gnutls_global_init",
! 621: /* Ada runtime */
! 622: "system__tasking__initialize",
! 623: "system__tasking__initialization__abort_defer",
! 624: "system__tasking__stages__create_task",
! 625: /* in case external threads call into our code */
! 626: "thread_current_id",
! 627: /* FHH IMCs and IMVs */
! 628: "TNC_IMC_NotifyConnectionChange",
! 629: "TNC_IMV_NotifyConnectionChange",
! 630: /* Botan */
! 631: "botan_public_key_load",
! 632: "botan_privkey_create",
! 633: "botan_privkey_load_ecdh",
! 634: "botan_privkey_load",
! 635: };
! 636:
! 637: /**
! 638: * Some functions are hard to whitelist, as they don't use a symbol directly.
! 639: * Use some static initialization to suppress them on leak reports
! 640: */
! 641: static void init_static_allocations()
! 642: {
! 643: struct tm tm;
! 644: time_t t = 0;
! 645:
! 646: tzset();
! 647: gmtime_r(&t, &tm);
! 648: localtime_r(&t, &tm);
! 649: }
! 650:
! 651: /**
! 652: * Hashtable hash function
! 653: */
! 654: static u_int hash(backtrace_t *key)
! 655: {
! 656: enumerator_t *enumerator;
! 657: void *addr;
! 658: u_int hash = 0;
! 659:
! 660: enumerator = key->create_frame_enumerator(key);
! 661: while (enumerator->enumerate(enumerator, &addr))
! 662: {
! 663: hash = chunk_hash_inc(chunk_from_thing(addr), hash);
! 664: }
! 665: enumerator->destroy(enumerator);
! 666:
! 667: return hash;
! 668: }
! 669:
! 670: /**
! 671: * Hashtable equals function
! 672: */
! 673: static bool equals(backtrace_t *a, backtrace_t *b)
! 674: {
! 675: return a->equals(a, b);
! 676: }
! 677:
! 678: /**
! 679: * Summarize and print backtraces
! 680: */
! 681: static int print_traces(private_leak_detective_t *this,
! 682: leak_detective_report_cb_t cb, void *user,
! 683: int thresh, int thresh_count,
! 684: bool detailed, int *whitelisted, size_t *sum)
! 685: {
! 686: int leaks = 0;
! 687: memory_header_t *hdr;
! 688: enumerator_t *enumerator;
! 689: hashtable_t *entries, *ignored = NULL;
! 690: backtrace_t *bt;
! 691: struct {
! 692: /** associated backtrace */
! 693: backtrace_t *backtrace;
! 694: /** total size of all allocations */
! 695: size_t bytes;
! 696: /** number of allocations */
! 697: u_int count;
! 698: } *entry;
! 699: bool before;
! 700:
! 701: before = enable_thread(FALSE);
! 702:
! 703: entries = hashtable_create((hashtable_hash_t)hash,
! 704: (hashtable_equals_t)equals, 1024);
! 705: if (whitelisted)
! 706: {
! 707: ignored = hashtable_create((hashtable_hash_t)hash,
! 708: (hashtable_equals_t)equals, 1024);
! 709: }
! 710:
! 711: lock->lock(lock);
! 712: for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
! 713: {
! 714: if (whitelisted)
! 715: {
! 716: bt = ignored->get(ignored, hdr->backtrace);
! 717: if (!bt)
! 718: {
! 719: if (hdr->backtrace->contains_function(hdr->backtrace, whitelist,
! 720: countof(whitelist)))
! 721: {
! 722: bt = hdr->backtrace;
! 723: ignored->put(ignored, bt, bt);
! 724: }
! 725: }
! 726: if (bt)
! 727: {
! 728: (*whitelisted)++;
! 729: continue;
! 730: }
! 731: }
! 732: entry = entries->get(entries, hdr->backtrace);
! 733: if (entry)
! 734: {
! 735: entry->bytes += hdr->bytes;
! 736: entry->count++;
! 737: }
! 738: else
! 739: {
! 740: INIT(entry,
! 741: .backtrace = hdr->backtrace->clone(hdr->backtrace),
! 742: .bytes = hdr->bytes,
! 743: .count = 1,
! 744: );
! 745: entries->put(entries, entry->backtrace, entry);
! 746: }
! 747: if (sum)
! 748: {
! 749: *sum += hdr->bytes;
! 750: }
! 751: leaks++;
! 752: }
! 753: lock->unlock(lock);
! 754: DESTROY_IF(ignored);
! 755:
! 756: enumerator = entries->create_enumerator(entries);
! 757: while (enumerator->enumerate(enumerator, NULL, &entry))
! 758: {
! 759: if (cb)
! 760: {
! 761: if (!thresh || entry->bytes >= thresh)
! 762: {
! 763: if (!thresh_count || entry->count >= thresh_count)
! 764: {
! 765: cb(user, entry->count, entry->bytes, entry->backtrace,
! 766: detailed);
! 767: }
! 768: }
! 769: }
! 770: entry->backtrace->destroy(entry->backtrace);
! 771: free(entry);
! 772: }
! 773: enumerator->destroy(enumerator);
! 774: entries->destroy(entries);
! 775:
! 776: enable_thread(before);
! 777: return leaks;
! 778: }
! 779:
! 780: METHOD(leak_detective_t, report, void,
! 781: private_leak_detective_t *this, bool detailed)
! 782: {
! 783: if (lib->leak_detective)
! 784: {
! 785: int leaks, whitelisted = 0;
! 786: size_t sum = 0;
! 787:
! 788: leaks = print_traces(this, this->report_cb, this->report_data,
! 789: 0, 0, detailed, &whitelisted, &sum);
! 790: if (this->report_scb)
! 791: {
! 792: this->report_scb(this->report_data, leaks, sum, whitelisted);
! 793: }
! 794: }
! 795: }
! 796:
! 797: METHOD(leak_detective_t, set_report_cb, void,
! 798: private_leak_detective_t *this, leak_detective_report_cb_t cb,
! 799: leak_detective_summary_cb_t scb, void *user)
! 800: {
! 801: this->report_cb = cb;
! 802: this->report_scb = scb;
! 803: this->report_data = user;
! 804: }
! 805:
! 806: METHOD(leak_detective_t, leaks, int,
! 807: private_leak_detective_t *this)
! 808: {
! 809: int whitelisted = 0;
! 810:
! 811: return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL);
! 812: }
! 813:
! 814: METHOD(leak_detective_t, set_state, bool,
! 815: private_leak_detective_t *this, bool enable)
! 816: {
! 817: return enable_thread(enable);
! 818: }
! 819:
! 820: METHOD(leak_detective_t, usage, void,
! 821: private_leak_detective_t *this, leak_detective_report_cb_t cb,
! 822: leak_detective_summary_cb_t scb, void *user)
! 823: {
! 824: bool detailed;
! 825: int thresh, thresh_count, leaks, whitelisted = 0;
! 826: size_t sum = 0;
! 827:
! 828: thresh = lib->settings->get_int(lib->settings,
! 829: "%s.leak_detective.usage_threshold", 10240, lib->ns);
! 830: thresh_count = lib->settings->get_int(lib->settings,
! 831: "%s.leak_detective.usage_threshold_count", 0, lib->ns);
! 832: detailed = lib->settings->get_bool(lib->settings,
! 833: "%s.leak_detective.detailed", TRUE, lib->ns);
! 834:
! 835: leaks = print_traces(this, cb, user, thresh, thresh_count,
! 836: detailed, &whitelisted, &sum);
! 837: if (scb)
! 838: {
! 839: scb(user, leaks, sum, whitelisted);
! 840: }
! 841: }
! 842:
! 843: /**
! 844: * Wrapped malloc() function
! 845: */
! 846: HOOK(void*, malloc, size_t bytes)
! 847: {
! 848: memory_header_t *hdr;
! 849: memory_tail_t *tail;
! 850: bool before;
! 851:
! 852: if (!enabled || thread_disabled->get(thread_disabled))
! 853: {
! 854: return real_malloc(bytes);
! 855: }
! 856:
! 857: hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
! 858: tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
! 859: /* set to something which causes crashes */
! 860: memset(hdr, MEMORY_ALLOC_PATTERN,
! 861: sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
! 862:
! 863: before = enable_thread(FALSE);
! 864: hdr->backtrace = backtrace_create(2);
! 865: enable_thread(before);
! 866:
! 867: hdr->magic = MEMORY_HEADER_MAGIC;
! 868: hdr->bytes = bytes;
! 869: tail->magic = MEMORY_TAIL_MAGIC;
! 870:
! 871: add_hdr(hdr);
! 872:
! 873: return hdr + 1;
! 874: }
! 875:
! 876: /**
! 877: * Wrapped calloc() function
! 878: */
! 879: HOOK(void*, calloc, size_t nmemb, size_t size)
! 880: {
! 881: void *ptr;
! 882: volatile size_t total;
! 883:
! 884: total = nmemb * size;
! 885: ptr = malloc(total);
! 886: memset(ptr, 0, total);
! 887:
! 888: return ptr;
! 889: }
! 890:
! 891: /**
! 892: * Wrapped valloc(), TODO: currently not supported
! 893: */
! 894: HOOK(void*, valloc, size_t size)
! 895: {
! 896: DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
! 897: return NULL;
! 898: }
! 899:
! 900: /**
! 901: * Wrapped free() function
! 902: */
! 903: HOOK(void, free, void *ptr)
! 904: {
! 905: memory_header_t *hdr;
! 906: memory_tail_t *tail;
! 907: backtrace_t *backtrace;
! 908: bool before;
! 909:
! 910: if (!enabled || thread_disabled->get(thread_disabled))
! 911: {
! 912: /* after deinitialization we might have to free stuff we allocated
! 913: * while we were enabled */
! 914: if (!first_header.magic && ptr)
! 915: {
! 916: hdr = ptr - sizeof(memory_header_t);
! 917: tail = ptr + hdr->bytes;
! 918: if (hdr->magic == MEMORY_HEADER_MAGIC &&
! 919: tail->magic == MEMORY_TAIL_MAGIC)
! 920: {
! 921: ptr = hdr;
! 922: }
! 923: }
! 924: real_free(ptr);
! 925: return;
! 926: }
! 927: /* allow freeing of NULL */
! 928: if (!ptr)
! 929: {
! 930: return;
! 931: }
! 932: hdr = ptr - sizeof(memory_header_t);
! 933: tail = ptr + hdr->bytes;
! 934:
! 935: before = enable_thread(FALSE);
! 936: if (hdr->magic != MEMORY_HEADER_MAGIC ||
! 937: tail->magic != MEMORY_TAIL_MAGIC)
! 938: {
! 939: bool bt = TRUE;
! 940:
! 941: /* check if memory appears to be allocated by our hooks */
! 942: if (has_hdr(hdr))
! 943: {
! 944: fprintf(stderr, "freeing corrupted memory (%p): "
! 945: "%u bytes, header magic 0x%x, tail magic 0x%x:\n",
! 946: ptr, hdr->bytes, hdr->magic, tail->magic);
! 947: remove_hdr(hdr);
! 948:
! 949: if (hdr->magic == MEMORY_HEADER_MAGIC)
! 950: { /* only access the old backtrace if header magic is valid */
! 951: hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
! 952: hdr->backtrace->destroy(hdr->backtrace);
! 953: }
! 954: else
! 955: {
! 956: fprintf(stderr, " header magic invalid, ignore backtrace of "
! 957: "allocation\n");
! 958: }
! 959: }
! 960: else
! 961: {
! 962: /* just free this block of unknown memory */
! 963: hdr = ptr;
! 964:
! 965: if (ignore_unknown)
! 966: {
! 967: bt = FALSE;
! 968: }
! 969: else
! 970: {
! 971: fprintf(stderr, "freeing unknown memory (%p):\n", ptr);
! 972: }
! 973: }
! 974: if (bt)
! 975: {
! 976: backtrace = backtrace_create(2);
! 977: backtrace->log(backtrace, stderr, TRUE);
! 978: backtrace->destroy(backtrace);
! 979: }
! 980: }
! 981: else
! 982: {
! 983: remove_hdr(hdr);
! 984:
! 985: hdr->backtrace->destroy(hdr->backtrace);
! 986:
! 987: /* set mem to something remarkable */
! 988: memset(hdr, MEMORY_FREE_PATTERN,
! 989: sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
! 990: }
! 991: real_free(hdr);
! 992: enable_thread(before);
! 993: }
! 994:
! 995: /**
! 996: * Wrapped realloc() function
! 997: */
! 998: HOOK(void*, realloc, void *old, size_t bytes)
! 999: {
! 1000: memory_header_t *hdr;
! 1001: memory_tail_t *tail;
! 1002: backtrace_t *backtrace;
! 1003: bool before, have_backtrace = TRUE;
! 1004:
! 1005: if (!enabled || thread_disabled->get(thread_disabled))
! 1006: {
! 1007: return real_realloc(old, bytes);
! 1008: }
! 1009: /* allow reallocation of NULL */
! 1010: if (!old)
! 1011: {
! 1012: return malloc(bytes);
! 1013: }
! 1014: /* handle zero size as a free() */
! 1015: if (!bytes)
! 1016: {
! 1017: free(old);
! 1018: return NULL;
! 1019: }
! 1020:
! 1021: hdr = old - sizeof(memory_header_t);
! 1022: tail = old + hdr->bytes;
! 1023:
! 1024: before = enable_thread(FALSE);
! 1025: if (hdr->magic != MEMORY_HEADER_MAGIC ||
! 1026: tail->magic != MEMORY_TAIL_MAGIC)
! 1027: {
! 1028: bool bt = TRUE;
! 1029:
! 1030: /* check if memory appears to be allocated by our hooks */
! 1031: if (has_hdr(hdr))
! 1032: {
! 1033: fprintf(stderr, "reallocating corrupted memory (%p, %u bytes): "
! 1034: "%zu bytes, header magic 0x%x, tail magic 0x%x:\n",
! 1035: old, hdr->bytes, bytes, hdr->magic, tail->magic);
! 1036: remove_hdr(hdr);
! 1037:
! 1038: if (hdr->magic == MEMORY_HEADER_MAGIC)
! 1039: { /* only access header fields (backtrace, bytes) if header magic
! 1040: * is still valid */
! 1041: hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
! 1042: memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
! 1043: }
! 1044: else
! 1045: {
! 1046: fprintf(stderr, " header magic invalid, ignore backtrace of "
! 1047: "allocation\n");
! 1048: have_backtrace = FALSE;
! 1049: hdr->magic = MEMORY_HEADER_MAGIC;
! 1050: }
! 1051: }
! 1052: else
! 1053: {
! 1054: /* adopt this block of unknown memory */
! 1055: hdr = old;
! 1056: have_backtrace = FALSE;
! 1057:
! 1058: if (ignore_unknown)
! 1059: {
! 1060: bt = FALSE;
! 1061: }
! 1062: else
! 1063: {
! 1064: fprintf(stderr, "reallocating unknown memory (%p): %zu bytes:\n",
! 1065: old, bytes);
! 1066: }
! 1067: }
! 1068: if (bt)
! 1069: {
! 1070: backtrace = backtrace_create(2);
! 1071: backtrace->log(backtrace, stderr, TRUE);
! 1072: backtrace->destroy(backtrace);
! 1073: }
! 1074: }
! 1075: else
! 1076: {
! 1077: remove_hdr(hdr);
! 1078: /* clear tail magic, allocate, set tail magic */
! 1079: memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
! 1080: }
! 1081:
! 1082: hdr = real_realloc(hdr,
! 1083: sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
! 1084: tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
! 1085: tail->magic = MEMORY_TAIL_MAGIC;
! 1086:
! 1087: /* update statistics */
! 1088: hdr->bytes = bytes;
! 1089:
! 1090: if (have_backtrace)
! 1091: {
! 1092: hdr->backtrace->destroy(hdr->backtrace);
! 1093: }
! 1094: hdr->backtrace = backtrace_create(2);
! 1095: enable_thread(before);
! 1096:
! 1097: add_hdr(hdr);
! 1098:
! 1099: return hdr + 1;
! 1100: }
! 1101:
! 1102: METHOD(leak_detective_t, destroy, void,
! 1103: private_leak_detective_t *this)
! 1104: {
! 1105: disable_leak_detective();
! 1106: lock->destroy(lock);
! 1107: thread_disabled->destroy(thread_disabled);
! 1108: free(this);
! 1109: first_header.magic = 0;
! 1110: first_header.next = NULL;
! 1111: }
! 1112:
! 1113: /*
! 1114: * see header file
! 1115: */
! 1116: leak_detective_t *leak_detective_create()
! 1117: {
! 1118: private_leak_detective_t *this;
! 1119:
! 1120: INIT(this,
! 1121: .public = {
! 1122: .report = _report,
! 1123: .set_report_cb = _set_report_cb,
! 1124: .usage = _usage,
! 1125: .leaks = _leaks,
! 1126: .set_state = _set_state,
! 1127: .destroy = _destroy,
! 1128: },
! 1129: );
! 1130:
! 1131: if (getenv("LEAK_DETECTIVE_DISABLE") != NULL)
! 1132: {
! 1133: free(this);
! 1134: return NULL;
! 1135: }
! 1136: ignore_unknown = getenv("LEAK_DETECTIVE_IGNORE_UNKNOWN") != NULL;
! 1137:
! 1138: lock = spinlock_create();
! 1139: thread_disabled = thread_value_create(NULL);
! 1140:
! 1141: init_static_allocations();
! 1142:
! 1143: if (register_hooks())
! 1144: {
! 1145: enable_leak_detective();
! 1146: }
! 1147: return &this->public;
! 1148: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>