Annotation of embedaddon/strongswan/src/libstrongswan/utils/leak_detective.c, revision 1.1.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>