Annotation of embedaddon/strongswan/src/libstrongswan/utils/leak_detective.c, revision 1.1.1.2

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",
1.1.1.2 ! misho     586:        "OPENSSL_init_ssl",
1.1       misho     587:        "CRYPTO_THREAD_lock_new",
                    588:        "ERR_add_error_data",
                    589:        "ERR_set_mark",
                    590:        "ENGINE_load_builtin_engines",
                    591:        "OPENSSL_load_builtin_modules",
                    592:        "CONF_modules_load_file",
                    593:        "CONF_module_add",
                    594:        "RAND_DRBG_bytes",
                    595:        "RAND_DRBG_generate",
                    596:        "RAND_DRBG_get0_master",
                    597:        "RAND_DRBG_get0_private",
                    598:        "RAND_DRBG_get0_public",
1.1.1.2 ! misho     599:        /* We get this via libcurl and OpenSSL 1.1.1 */
        !           600:        "CRYPTO_get_ex_new_index",
1.1       misho     601:        /* OpenSSL libssl */
                    602:        "SSL_COMP_get_compression_methods",
                    603:        /* NSPR */
                    604:        "PR_CallOnce",
                    605:        /* libapr */
                    606:        "apr_pool_create_ex",
                    607:        /* glib */
                    608:        "g_output_stream_write",
                    609:        "g_resolver_lookup_by_name",
                    610:        "g_signal_connect_data",
                    611:        "g_socket_connection_factory_lookup_type",
                    612:        "g_type_init_with_debug_flags",
                    613:        "g_type_register_static",
                    614:        "g_type_class_ref",
                    615:        "g_type_create_instance",
                    616:        "g_type_add_interface_static",
                    617:        "g_type_interface_add_prerequisite",
                    618:        "g_private_set",
                    619:        "g_queue_pop_tail",
                    620:        /* libgpg */
                    621:        "gpg_err_init",
                    622:        /* gnutls */
                    623:        "gnutls_global_init",
                    624:        /* Ada runtime */
                    625:        "system__tasking__initialize",
                    626:        "system__tasking__initialization__abort_defer",
                    627:        "system__tasking__stages__create_task",
1.1.1.2 ! misho     628:        "system__task_primitives__operations__register_foreign_thread__2",
1.1       misho     629:        /* in case external threads call into our code */
                    630:        "thread_current_id",
                    631:        /* FHH IMCs and IMVs */
                    632:        "TNC_IMC_NotifyConnectionChange",
                    633:        "TNC_IMV_NotifyConnectionChange",
                    634:        /* Botan */
                    635:        "botan_public_key_load",
                    636:        "botan_privkey_create",
                    637:        "botan_privkey_load_ecdh",
                    638:        "botan_privkey_load",
                    639: };
                    640: 
                    641: /**
                    642:  * Some functions are hard to whitelist, as they don't use a symbol directly.
                    643:  * Use some static initialization to suppress them on leak reports
                    644:  */
                    645: static void init_static_allocations()
                    646: {
                    647:        struct tm tm;
                    648:        time_t t = 0;
                    649: 
                    650:        tzset();
                    651:        gmtime_r(&t, &tm);
                    652:        localtime_r(&t, &tm);
                    653: }
                    654: 
                    655: /**
                    656:  * Hashtable hash function
                    657:  */
                    658: static u_int hash(backtrace_t *key)
                    659: {
                    660:        enumerator_t *enumerator;
                    661:        void *addr;
                    662:        u_int hash = 0;
                    663: 
                    664:        enumerator = key->create_frame_enumerator(key);
                    665:        while (enumerator->enumerate(enumerator, &addr))
                    666:        {
                    667:                hash = chunk_hash_inc(chunk_from_thing(addr), hash);
                    668:        }
                    669:        enumerator->destroy(enumerator);
                    670: 
                    671:        return hash;
                    672: }
                    673: 
                    674: /**
                    675:  * Hashtable equals function
                    676:  */
                    677: static bool equals(backtrace_t *a, backtrace_t *b)
                    678: {
                    679:        return a->equals(a, b);
                    680: }
                    681: 
                    682: /**
                    683:  * Summarize and print backtraces
                    684:  */
                    685: static int print_traces(private_leak_detective_t *this,
                    686:                                                leak_detective_report_cb_t cb, void *user,
                    687:                                                int thresh, int thresh_count,
                    688:                                                bool detailed, int *whitelisted, size_t *sum)
                    689: {
                    690:        int leaks = 0;
                    691:        memory_header_t *hdr;
                    692:        enumerator_t *enumerator;
                    693:        hashtable_t *entries, *ignored = NULL;
                    694:        backtrace_t *bt;
                    695:        struct {
                    696:                /** associated backtrace */
                    697:                backtrace_t *backtrace;
                    698:                /** total size of all allocations */
                    699:                size_t bytes;
                    700:                /** number of allocations */
                    701:                u_int count;
                    702:        } *entry;
                    703:        bool before;
                    704: 
                    705:        before = enable_thread(FALSE);
                    706: 
                    707:        entries = hashtable_create((hashtable_hash_t)hash,
                    708:                                                           (hashtable_equals_t)equals, 1024);
                    709:        if (whitelisted)
                    710:        {
                    711:                ignored = hashtable_create((hashtable_hash_t)hash,
                    712:                                                                   (hashtable_equals_t)equals, 1024);
                    713:        }
                    714: 
                    715:        lock->lock(lock);
                    716:        for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
                    717:        {
                    718:                if (whitelisted)
                    719:                {
                    720:                        bt = ignored->get(ignored, hdr->backtrace);
                    721:                        if (!bt)
                    722:                        {
                    723:                                if (hdr->backtrace->contains_function(hdr->backtrace, whitelist,
                    724:                                                                                                          countof(whitelist)))
                    725:                                {
                    726:                                        bt = hdr->backtrace;
                    727:                                        ignored->put(ignored, bt, bt);
                    728:                                }
                    729:                        }
                    730:                        if (bt)
                    731:                        {
                    732:                                (*whitelisted)++;
                    733:                                continue;
                    734:                        }
                    735:                }
                    736:                entry = entries->get(entries, hdr->backtrace);
                    737:                if (entry)
                    738:                {
                    739:                        entry->bytes += hdr->bytes;
                    740:                        entry->count++;
                    741:                }
                    742:                else
                    743:                {
                    744:                        INIT(entry,
                    745:                                .backtrace = hdr->backtrace->clone(hdr->backtrace),
                    746:                                .bytes = hdr->bytes,
                    747:                                .count = 1,
                    748:                        );
                    749:                        entries->put(entries, entry->backtrace, entry);
                    750:                }
                    751:                if (sum)
                    752:                {
                    753:                        *sum += hdr->bytes;
                    754:                }
                    755:                leaks++;
                    756:        }
                    757:        lock->unlock(lock);
                    758:        DESTROY_IF(ignored);
                    759: 
                    760:        enumerator = entries->create_enumerator(entries);
                    761:        while (enumerator->enumerate(enumerator, NULL, &entry))
                    762:        {
                    763:                if (cb)
                    764:                {
                    765:                        if (!thresh || entry->bytes >= thresh)
                    766:                        {
                    767:                                if (!thresh_count || entry->count >= thresh_count)
                    768:                                {
                    769:                                        cb(user, entry->count, entry->bytes, entry->backtrace,
                    770:                                           detailed);
                    771:                                }
                    772:                        }
                    773:                }
                    774:                entry->backtrace->destroy(entry->backtrace);
                    775:                free(entry);
                    776:        }
                    777:        enumerator->destroy(enumerator);
                    778:        entries->destroy(entries);
                    779: 
                    780:        enable_thread(before);
                    781:        return leaks;
                    782: }
                    783: 
                    784: METHOD(leak_detective_t, report, void,
                    785:        private_leak_detective_t *this, bool detailed)
                    786: {
                    787:        if (lib->leak_detective)
                    788:        {
                    789:                int leaks, whitelisted = 0;
                    790:                size_t sum = 0;
                    791: 
                    792:                leaks = print_traces(this, this->report_cb, this->report_data,
                    793:                                                         0, 0, detailed, &whitelisted, &sum);
                    794:                if (this->report_scb)
                    795:                {
                    796:                        this->report_scb(this->report_data, leaks, sum, whitelisted);
                    797:                }
                    798:        }
                    799: }
                    800: 
                    801: METHOD(leak_detective_t, set_report_cb, void,
                    802:        private_leak_detective_t *this, leak_detective_report_cb_t cb,
                    803:        leak_detective_summary_cb_t scb, void *user)
                    804: {
                    805:        this->report_cb = cb;
                    806:        this->report_scb = scb;
                    807:        this->report_data = user;
                    808: }
                    809: 
                    810: METHOD(leak_detective_t, leaks, int,
                    811:        private_leak_detective_t *this)
                    812: {
                    813:        int whitelisted = 0;
                    814: 
                    815:        return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL);
                    816: }
                    817: 
                    818: METHOD(leak_detective_t, set_state, bool,
                    819:        private_leak_detective_t *this, bool enable)
                    820: {
                    821:        return enable_thread(enable);
                    822: }
                    823: 
                    824: METHOD(leak_detective_t, usage, void,
                    825:        private_leak_detective_t *this, leak_detective_report_cb_t cb,
                    826:        leak_detective_summary_cb_t scb, void *user)
                    827: {
                    828:        bool detailed;
                    829:        int thresh, thresh_count, leaks, whitelisted = 0;
                    830:        size_t sum = 0;
                    831: 
                    832:        thresh = lib->settings->get_int(lib->settings,
                    833:                                                "%s.leak_detective.usage_threshold", 10240, lib->ns);
                    834:        thresh_count = lib->settings->get_int(lib->settings,
                    835:                                                "%s.leak_detective.usage_threshold_count", 0, lib->ns);
                    836:        detailed = lib->settings->get_bool(lib->settings,
                    837:                                                "%s.leak_detective.detailed", TRUE, lib->ns);
                    838: 
                    839:        leaks = print_traces(this, cb, user, thresh, thresh_count,
                    840:                                                 detailed, &whitelisted, &sum);
                    841:        if (scb)
                    842:        {
                    843:                scb(user, leaks, sum, whitelisted);
                    844:        }
                    845: }
                    846: 
                    847: /**
                    848:  * Wrapped malloc() function
                    849:  */
                    850: HOOK(void*, malloc, size_t bytes)
                    851: {
                    852:        memory_header_t *hdr;
                    853:        memory_tail_t *tail;
                    854:        bool before;
                    855: 
                    856:        if (!enabled || thread_disabled->get(thread_disabled))
                    857:        {
                    858:                return real_malloc(bytes);
                    859:        }
                    860: 
                    861:        hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
                    862:        tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
                    863:        /* set to something which causes crashes */
                    864:        memset(hdr, MEMORY_ALLOC_PATTERN,
                    865:                   sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
                    866: 
                    867:        before = enable_thread(FALSE);
                    868:        hdr->backtrace = backtrace_create(2);
                    869:        enable_thread(before);
                    870: 
                    871:        hdr->magic = MEMORY_HEADER_MAGIC;
                    872:        hdr->bytes = bytes;
                    873:        tail->magic = MEMORY_TAIL_MAGIC;
                    874: 
                    875:        add_hdr(hdr);
                    876: 
                    877:        return hdr + 1;
                    878: }
                    879: 
                    880: /**
                    881:  * Wrapped calloc() function
                    882:  */
                    883: HOOK(void*, calloc, size_t nmemb, size_t size)
                    884: {
                    885:        void *ptr;
                    886:        volatile size_t total;
                    887: 
                    888:        total = nmemb * size;
                    889:        ptr = malloc(total);
                    890:        memset(ptr, 0, total);
                    891: 
                    892:        return ptr;
                    893: }
                    894: 
                    895: /**
                    896:  * Wrapped valloc(), TODO: currently not supported
                    897:  */
                    898: HOOK(void*, valloc, size_t size)
                    899: {
                    900:        DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
                    901:        return NULL;
                    902: }
                    903: 
                    904: /**
                    905:  * Wrapped free() function
                    906:  */
                    907: HOOK(void, free, void *ptr)
                    908: {
                    909:        memory_header_t *hdr;
                    910:        memory_tail_t *tail;
                    911:        backtrace_t *backtrace;
                    912:        bool before;
                    913: 
                    914:        if (!enabled || thread_disabled->get(thread_disabled))
                    915:        {
                    916:                /* after deinitialization we might have to free stuff we allocated
                    917:                 * while we were enabled */
                    918:                if (!first_header.magic && ptr)
                    919:                {
                    920:                        hdr = ptr - sizeof(memory_header_t);
                    921:                        tail = ptr + hdr->bytes;
                    922:                        if (hdr->magic == MEMORY_HEADER_MAGIC &&
                    923:                                tail->magic == MEMORY_TAIL_MAGIC)
                    924:                        {
                    925:                                ptr = hdr;
                    926:                        }
                    927:                }
                    928:                real_free(ptr);
                    929:                return;
                    930:        }
                    931:        /* allow freeing of NULL */
                    932:        if (!ptr)
                    933:        {
                    934:                return;
                    935:        }
                    936:        hdr = ptr - sizeof(memory_header_t);
                    937:        tail = ptr + hdr->bytes;
                    938: 
                    939:        before = enable_thread(FALSE);
                    940:        if (hdr->magic != MEMORY_HEADER_MAGIC ||
                    941:                tail->magic != MEMORY_TAIL_MAGIC)
                    942:        {
                    943:                bool bt = TRUE;
                    944: 
                    945:                /* check if memory appears to be allocated by our hooks */
                    946:                if (has_hdr(hdr))
                    947:                {
                    948:                        fprintf(stderr, "freeing corrupted memory (%p): "
                    949:                                        "%u bytes, header magic 0x%x, tail magic 0x%x:\n",
                    950:                                        ptr, hdr->bytes, hdr->magic, tail->magic);
                    951:                        remove_hdr(hdr);
                    952: 
                    953:                        if (hdr->magic == MEMORY_HEADER_MAGIC)
                    954:                        {       /* only access the old backtrace if header magic is valid */
                    955:                                hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
                    956:                                hdr->backtrace->destroy(hdr->backtrace);
                    957:                        }
                    958:                        else
                    959:                        {
                    960:                                fprintf(stderr, " header magic invalid, ignore backtrace of "
                    961:                                                "allocation\n");
                    962:                        }
                    963:                }
                    964:                else
                    965:                {
                    966:                        /* just free this block of unknown memory */
                    967:                        hdr = ptr;
                    968: 
                    969:                        if (ignore_unknown)
                    970:                        {
                    971:                                bt = FALSE;
                    972:                        }
                    973:                        else
                    974:                        {
                    975:                                fprintf(stderr, "freeing unknown memory (%p):\n", ptr);
                    976:                        }
                    977:                }
                    978:                if (bt)
                    979:                {
                    980:                        backtrace = backtrace_create(2);
                    981:                        backtrace->log(backtrace, stderr, TRUE);
                    982:                        backtrace->destroy(backtrace);
                    983:                }
                    984:        }
                    985:        else
                    986:        {
                    987:                remove_hdr(hdr);
                    988: 
                    989:                hdr->backtrace->destroy(hdr->backtrace);
                    990: 
                    991:                /* set mem to something remarkable */
                    992:                memset(hdr, MEMORY_FREE_PATTERN,
                    993:                           sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
                    994:        }
                    995:        real_free(hdr);
                    996:        enable_thread(before);
                    997: }
                    998: 
                    999: /**
                   1000:  * Wrapped realloc() function
                   1001:  */
                   1002: HOOK(void*, realloc, void *old, size_t bytes)
                   1003: {
                   1004:        memory_header_t *hdr;
                   1005:        memory_tail_t *tail;
                   1006:        backtrace_t *backtrace;
                   1007:        bool before, have_backtrace = TRUE;
                   1008: 
                   1009:        if (!enabled || thread_disabled->get(thread_disabled))
                   1010:        {
                   1011:                return real_realloc(old, bytes);
                   1012:        }
                   1013:        /* allow reallocation of NULL */
                   1014:        if (!old)
                   1015:        {
                   1016:                return malloc(bytes);
                   1017:        }
                   1018:        /* handle zero size as a free() */
                   1019:        if (!bytes)
                   1020:        {
                   1021:                free(old);
                   1022:                return NULL;
                   1023:        }
                   1024: 
                   1025:        hdr = old - sizeof(memory_header_t);
                   1026:        tail = old + hdr->bytes;
                   1027: 
                   1028:        before = enable_thread(FALSE);
                   1029:        if (hdr->magic != MEMORY_HEADER_MAGIC ||
                   1030:                tail->magic != MEMORY_TAIL_MAGIC)
                   1031:        {
                   1032:                bool bt = TRUE;
                   1033: 
                   1034:                /* check if memory appears to be allocated by our hooks */
                   1035:                if (has_hdr(hdr))
                   1036:                {
                   1037:                        fprintf(stderr, "reallocating corrupted memory (%p, %u bytes): "
                   1038:                                        "%zu bytes, header magic 0x%x, tail magic 0x%x:\n",
                   1039:                                        old, hdr->bytes, bytes, hdr->magic, tail->magic);
                   1040:                        remove_hdr(hdr);
                   1041: 
                   1042:                        if (hdr->magic == MEMORY_HEADER_MAGIC)
                   1043:                        {       /* only access header fields (backtrace, bytes) if header magic
                   1044:                                 * is still valid */
                   1045:                                hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
                   1046:                                memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
                   1047:                        }
                   1048:                        else
                   1049:                        {
                   1050:                                fprintf(stderr, " header magic invalid, ignore backtrace of "
                   1051:                                                "allocation\n");
                   1052:                                have_backtrace = FALSE;
                   1053:                                hdr->magic = MEMORY_HEADER_MAGIC;
                   1054:                        }
                   1055:                }
                   1056:                else
                   1057:                {
                   1058:                        /* adopt this block of unknown memory */
                   1059:                        hdr = old;
                   1060:                        have_backtrace = FALSE;
                   1061: 
                   1062:                        if (ignore_unknown)
                   1063:                        {
                   1064:                                bt = FALSE;
                   1065:                        }
                   1066:                        else
                   1067:                        {
                   1068:                                fprintf(stderr, "reallocating unknown memory (%p): %zu bytes:\n",
                   1069:                                                old, bytes);
                   1070:                        }
                   1071:                }
                   1072:                if (bt)
                   1073:                {
                   1074:                        backtrace = backtrace_create(2);
                   1075:                        backtrace->log(backtrace, stderr, TRUE);
                   1076:                        backtrace->destroy(backtrace);
                   1077:                }
                   1078:        }
                   1079:        else
                   1080:        {
                   1081:                remove_hdr(hdr);
                   1082:                /* clear tail magic, allocate, set tail magic */
                   1083:                memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
                   1084:        }
                   1085: 
                   1086:        hdr = real_realloc(hdr,
                   1087:                                           sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
                   1088:        tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
                   1089:        tail->magic = MEMORY_TAIL_MAGIC;
                   1090: 
                   1091:        /* update statistics */
                   1092:        hdr->bytes = bytes;
                   1093: 
                   1094:        if (have_backtrace)
                   1095:        {
                   1096:                hdr->backtrace->destroy(hdr->backtrace);
                   1097:        }
                   1098:        hdr->backtrace = backtrace_create(2);
                   1099:        enable_thread(before);
                   1100: 
                   1101:        add_hdr(hdr);
                   1102: 
                   1103:        return hdr + 1;
                   1104: }
                   1105: 
                   1106: METHOD(leak_detective_t, destroy, void,
                   1107:        private_leak_detective_t *this)
                   1108: {
                   1109:        disable_leak_detective();
                   1110:        lock->destroy(lock);
                   1111:        thread_disabled->destroy(thread_disabled);
                   1112:        free(this);
                   1113:        first_header.magic = 0;
                   1114:        first_header.next = NULL;
                   1115: }
                   1116: 
                   1117: /*
                   1118:  * see header file
                   1119:  */
                   1120: leak_detective_t *leak_detective_create()
                   1121: {
                   1122:        private_leak_detective_t *this;
                   1123: 
                   1124:        INIT(this,
                   1125:                .public = {
                   1126:                        .report = _report,
                   1127:                        .set_report_cb = _set_report_cb,
                   1128:                        .usage = _usage,
                   1129:                        .leaks = _leaks,
                   1130:                        .set_state = _set_state,
                   1131:                        .destroy = _destroy,
                   1132:                },
                   1133:        );
                   1134: 
                   1135:        if (getenv("LEAK_DETECTIVE_DISABLE") != NULL)
                   1136:        {
                   1137:                free(this);
                   1138:                return NULL;
                   1139:        }
                   1140:        ignore_unknown = getenv("LEAK_DETECTIVE_IGNORE_UNKNOWN") != NULL;
                   1141: 
                   1142:        lock = spinlock_create();
                   1143:        thread_disabled = thread_value_create(NULL);
                   1144: 
                   1145:        init_static_allocations();
                   1146: 
                   1147:        if (register_hooks())
                   1148:        {
                   1149:                enable_leak_detective();
                   1150:        }
                   1151:        return &this->public;
                   1152: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>