leak-detective: Whitelist thread ID getter
[strongswan.git] / src / libstrongswan / utils / leak_detective.c
1 /*
2 * Copyright (C) 2013-2014 Tobias Brunner
3 * Copyright (C) 2006-2013 Martin Willi
4 * 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 = FALSE;
166
167 /**
168 * Is leak detection disabled for the current thread?
169 */
170 static thread_value_t *thread_disabled;
171
172 /**
173 * Installs the malloc hooks, enables leak detection
174 */
175 static void enable_leak_detective()
176 {
177 enabled = TRUE;
178 }
179
180 /**
181 * Uninstalls the malloc hooks, disables leak detection
182 */
183 static void disable_leak_detective()
184 {
185 enabled = FALSE;
186 }
187
188 /**
189 * Enable/Disable leak detective for the current thread
190 *
191 * @return Previous value
192 */
193 static bool enable_thread(bool enable)
194 {
195 bool before;
196
197 before = thread_disabled->get(thread_disabled) == NULL;
198 thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE);
199 return before;
200 }
201
202 /**
203 * Add a header to the beginning of the list
204 */
205 static void add_hdr(memory_header_t *hdr)
206 {
207 lock->lock(lock);
208 hdr->next = first_header.next;
209 if (hdr->next)
210 {
211 hdr->next->previous = hdr;
212 }
213 hdr->previous = &first_header;
214 first_header.next = hdr;
215 lock->unlock(lock);
216 }
217
218 /**
219 * Remove a header from the list
220 */
221 static void remove_hdr(memory_header_t *hdr)
222 {
223 lock->lock(lock);
224 if (hdr->next)
225 {
226 hdr->next->previous = hdr->previous;
227 }
228 hdr->previous->next = hdr->next;
229 lock->unlock(lock);
230 }
231
232 /**
233 * Check if a header is in the list
234 */
235 static bool has_hdr(memory_header_t *hdr)
236 {
237 memory_header_t *current;
238 bool found = FALSE;
239
240 lock->lock(lock);
241 for (current = &first_header; current != NULL; current = current->next)
242 {
243 if (current == hdr)
244 {
245 found = TRUE;
246 break;
247 }
248 }
249 lock->unlock(lock);
250
251 return found;
252 }
253
254 #ifdef __APPLE__
255
256 /**
257 * Copy of original default zone, with functions we call in hooks
258 */
259 static malloc_zone_t original;
260
261 /**
262 * Call original malloc()
263 */
264 static void* real_malloc(size_t size)
265 {
266 return original.malloc(malloc_default_zone(), size);
267 }
268
269 /**
270 * Call original free()
271 */
272 static void real_free(void *ptr)
273 {
274 original.free(malloc_default_zone(), ptr);
275 }
276
277 /**
278 * Call original realloc()
279 */
280 static void* real_realloc(void *ptr, size_t size)
281 {
282 return original.realloc(malloc_default_zone(), ptr, size);
283 }
284
285 /**
286 * Hook definition: static function with _hook suffix, takes additional zone
287 */
288 #define HOOK(ret, name, ...) \
289 static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__)
290
291 /**
292 * forward declaration of hooks
293 */
294 HOOK(void*, malloc, size_t bytes);
295 HOOK(void*, calloc, size_t nmemb, size_t size);
296 HOOK(void*, valloc, size_t size);
297 HOOK(void, free, void *ptr);
298 HOOK(void*, realloc, void *old, size_t bytes);
299
300 /**
301 * malloc zone size(), must consider the memory header prepended
302 */
303 HOOK(size_t, size, const void *ptr)
304 {
305 bool before;
306 size_t size;
307
308 if (enabled)
309 {
310 before = enable_thread(FALSE);
311 if (before)
312 {
313 ptr -= sizeof(memory_header_t);
314 }
315 }
316 size = original.size(malloc_default_zone(), ptr);
317 if (enabled)
318 {
319 enable_thread(before);
320 }
321 return size;
322 }
323
324 /**
325 * Version of malloc zones we currently support
326 */
327 #define MALLOC_ZONE_VERSION 8 /* Snow Leopard */
328
329 /**
330 * Hook-in our malloc functions into the default zone
331 */
332 static bool register_hooks()
333 {
334 static bool once = FALSE;
335 malloc_zone_t *zone;
336 void *page;
337
338 if (once)
339 {
340 return TRUE;
341 }
342 once = TRUE;
343
344 zone = malloc_default_zone();
345 if (zone->version != MALLOC_ZONE_VERSION)
346 {
347 DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)",
348 zone->version, MALLOC_ZONE_VERSION);
349 return FALSE;
350 }
351
352 original = *zone;
353
354 page = (void*)((uintptr_t)zone / getpagesize() * getpagesize());
355 if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0)
356 {
357 DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno));
358 return FALSE;
359 }
360
361 zone->size = size_hook;
362 zone->malloc = malloc_hook;
363 zone->calloc = calloc_hook;
364 zone->valloc = valloc_hook;
365 zone->free = free_hook;
366 zone->realloc = realloc_hook;
367
368 /* those other functions can be NULLed out to not use them */
369 zone->batch_malloc = NULL;
370 zone->batch_free = NULL;
371 zone->memalign = NULL;
372 zone->free_definite_size = NULL;
373
374 return TRUE;
375 }
376
377 #else /* !__APPLE__ */
378
379 /**
380 * dlsym() might do a malloc(), but we can't do one before we get the malloc()
381 * function pointer. Use this minimalistic malloc implementation instead.
382 */
383 static void* malloc_for_dlsym(size_t size)
384 {
385 static char buf[1024] = {};
386 static size_t used = 0;
387 char *ptr;
388
389 /* roundup to a multiple of 32 */
390 size = (size - 1) / 32 * 32 + 32;
391
392 if (used + size > sizeof(buf))
393 {
394 return NULL;
395 }
396 ptr = buf + used;
397 used += size;
398 return ptr;
399 }
400
401 /**
402 * Lookup a malloc function, while disabling wrappers
403 */
404 static void* get_malloc_fn(char *name)
405 {
406 bool before = FALSE;
407 void *fn;
408
409 if (enabled)
410 {
411 before = enable_thread(FALSE);
412 }
413 fn = dlsym(RTLD_NEXT, name);
414 if (enabled)
415 {
416 enable_thread(before);
417 }
418 return fn;
419 }
420
421 /**
422 * Call original malloc()
423 */
424 static void* real_malloc(size_t size)
425 {
426 static void* (*fn)(size_t size);
427 static int recursive = 0;
428
429 if (!fn)
430 {
431 /* checking recursiveness should actually be thread-specific. But as
432 * it is very likely that the first allocation is done before we go
433 * multi-threaded, we keep it simple. */
434 if (recursive)
435 {
436 return malloc_for_dlsym(size);
437 }
438 recursive++;
439 fn = get_malloc_fn("malloc");
440 recursive--;
441 }
442 return fn(size);
443 }
444
445 /**
446 * Call original free()
447 */
448 static void real_free(void *ptr)
449 {
450 static void (*fn)(void *ptr);
451
452 if (!fn)
453 {
454 fn = get_malloc_fn("free");
455 }
456 return fn(ptr);
457 }
458
459 /**
460 * Call original realloc()
461 */
462 static void* real_realloc(void *ptr, size_t size)
463 {
464 static void* (*fn)(void *ptr, size_t size);
465
466 if (!fn)
467 {
468 fn = get_malloc_fn("realloc");
469 }
470 return fn(ptr, size);
471 }
472
473 /**
474 * Hook definition: plain function overloading existing malloc calls
475 */
476 #define HOOK(ret, name, ...) ret name(__VA_ARGS__)
477
478 /**
479 * Hook initialization when not using hooks, resolve functions.
480 */
481 static bool register_hooks()
482 {
483 void *buf = real_malloc(8);
484 buf = real_realloc(buf, 16);
485 real_free(buf);
486 return TRUE;
487 }
488
489 #endif /* !__APPLE__ */
490
491 /**
492 * Leak report white list
493 *
494 * List of functions using static allocation buffers or should be suppressed
495 * otherwise on leak report.
496 */
497 static char *whitelist[] = {
498 /* backtraces, including own */
499 "backtrace_create",
500 "strerror_safe",
501 /* pthread stuff */
502 "pthread_create",
503 "pthread_setspecific",
504 "__pthread_setspecific",
505 /* glibc functions */
506 "inet_ntoa",
507 "strerror",
508 "getprotobyname",
509 "getprotobynumber",
510 "getservbyport",
511 "getservbyname",
512 "gethostbyname",
513 "gethostbyname2",
514 "gethostbyname_r",
515 "gethostbyname2_r",
516 "getnetbyname",
517 "getpwnam_r",
518 "getgrnam_r",
519 "register_printf_function",
520 "register_printf_specifier",
521 "syslog",
522 "vsyslog",
523 "__syslog_chk",
524 "__vsyslog_chk",
525 "__fprintf_chk",
526 "getaddrinfo",
527 "setlocale",
528 "getpass",
529 "getpwent_r",
530 "setpwent",
531 "endpwent",
532 "getspnam_r",
533 "getpwuid_r",
534 "initgroups",
535 "tzset",
536 "_IO_file_doallocate",
537 /* ignore dlopen, as we do not dlclose to get proper leak reports */
538 "dlopen",
539 "dlerror",
540 "dlclose",
541 "dlsym",
542 /* mysql functions */
543 "mysql_init_character_set",
544 "init_client_errs",
545 "my_thread_init",
546 /* fastcgi library */
547 "FCGX_Init",
548 /* libxml */
549 "xmlInitCharEncodingHandlers",
550 "xmlInitParser",
551 "xmlInitParserCtxt",
552 /* libcurl */
553 "Curl_client_write",
554 /* libsoup */
555 "soup_message_headers_append",
556 "soup_message_headers_clear",
557 "soup_message_headers_get_list",
558 "soup_message_headers_get_one",
559 "soup_session_abort",
560 "soup_session_get_type",
561 /* libldap */
562 "ldap_int_initialize",
563 /* ClearSilver */
564 "nerr_init",
565 /* libgcrypt */
566 "gcrypt_plugin_create",
567 "gcry_control",
568 "gcry_check_version",
569 "gcry_randomize",
570 "gcry_create_nonce",
571 /* OpenSSL: These are needed for unit-tests only, the openssl plugin
572 * does properly clean up any memory during destroy(). */
573 "ECDSA_do_sign_ex",
574 "ECDSA_verify",
575 "RSA_new_method",
576 /* OpenSSL 1.1.0 does not cleanup anymore until the library is unloaded */
577 "OPENSSL_init_crypto",
578 "CRYPTO_THREAD_lock_new",
579 "ERR_add_error_data",
580 /* OpenSSL libssl */
581 "SSL_COMP_get_compression_methods",
582 /* NSPR */
583 "PR_CallOnce",
584 /* libapr */
585 "apr_pool_create_ex",
586 /* glib */
587 "g_output_stream_write",
588 "g_resolver_lookup_by_name",
589 "g_signal_connect_data",
590 "g_socket_connection_factory_lookup_type",
591 "g_type_init_with_debug_flags",
592 "g_type_register_static",
593 "g_type_class_ref",
594 "g_type_create_instance",
595 "g_type_add_interface_static",
596 "g_type_interface_add_prerequisite",
597 "g_private_set",
598 "g_queue_pop_tail",
599 /* libgpg */
600 "gpg_err_init",
601 /* gnutls */
602 "gnutls_global_init",
603 /* Ada runtime */
604 "system__tasking__initialize",
605 "system__tasking__initialization__abort_defer",
606 "system__tasking__stages__create_task",
607 /* in case external threads call into our code */
608 "thread_current_id",
609 };
610
611 /**
612 * Some functions are hard to whitelist, as they don't use a symbol directly.
613 * Use some static initialization to suppress them on leak reports
614 */
615 static void init_static_allocations()
616 {
617 struct tm tm;
618 time_t t = 0;
619
620 tzset();
621 gmtime_r(&t, &tm);
622 localtime_r(&t, &tm);
623 }
624
625 /**
626 * Hashtable hash function
627 */
628 static u_int hash(backtrace_t *key)
629 {
630 enumerator_t *enumerator;
631 void *addr;
632 u_int hash = 0;
633
634 enumerator = key->create_frame_enumerator(key);
635 while (enumerator->enumerate(enumerator, &addr))
636 {
637 hash = chunk_hash_inc(chunk_from_thing(addr), hash);
638 }
639 enumerator->destroy(enumerator);
640
641 return hash;
642 }
643
644 /**
645 * Hashtable equals function
646 */
647 static bool equals(backtrace_t *a, backtrace_t *b)
648 {
649 return a->equals(a, b);
650 }
651
652 /**
653 * Summarize and print backtraces
654 */
655 static int print_traces(private_leak_detective_t *this,
656 leak_detective_report_cb_t cb, void *user,
657 int thresh, int thresh_count,
658 bool detailed, int *whitelisted, size_t *sum)
659 {
660 int leaks = 0;
661 memory_header_t *hdr;
662 enumerator_t *enumerator;
663 hashtable_t *entries;
664 struct {
665 /** associated backtrace */
666 backtrace_t *backtrace;
667 /** total size of all allocations */
668 size_t bytes;
669 /** number of allocations */
670 u_int count;
671 } *entry;
672 bool before;
673
674 before = enable_thread(FALSE);
675
676 entries = hashtable_create((hashtable_hash_t)hash,
677 (hashtable_equals_t)equals, 1024);
678 lock->lock(lock);
679 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
680 {
681 if (whitelisted &&
682 hdr->backtrace->contains_function(hdr->backtrace,
683 whitelist, countof(whitelist)))
684 {
685 (*whitelisted)++;
686 continue;
687 }
688 entry = entries->get(entries, hdr->backtrace);
689 if (entry)
690 {
691 entry->bytes += hdr->bytes;
692 entry->count++;
693 }
694 else
695 {
696 INIT(entry,
697 .backtrace = hdr->backtrace->clone(hdr->backtrace),
698 .bytes = hdr->bytes,
699 .count = 1,
700 );
701 entries->put(entries, entry->backtrace, entry);
702 }
703 if (sum)
704 {
705 *sum += hdr->bytes;
706 }
707 leaks++;
708 }
709 lock->unlock(lock);
710
711 enumerator = entries->create_enumerator(entries);
712 while (enumerator->enumerate(enumerator, NULL, &entry))
713 {
714 if (cb)
715 {
716 if (!thresh || entry->bytes >= thresh)
717 {
718 if (!thresh_count || entry->count >= thresh_count)
719 {
720 cb(user, entry->count, entry->bytes, entry->backtrace,
721 detailed);
722 }
723 }
724 }
725 entry->backtrace->destroy(entry->backtrace);
726 free(entry);
727 }
728 enumerator->destroy(enumerator);
729 entries->destroy(entries);
730
731 enable_thread(before);
732 return leaks;
733 }
734
735 METHOD(leak_detective_t, report, void,
736 private_leak_detective_t *this, bool detailed)
737 {
738 if (lib->leak_detective)
739 {
740 int leaks, whitelisted = 0;
741 size_t sum = 0;
742
743 leaks = print_traces(this, this->report_cb, this->report_data,
744 0, 0, detailed, &whitelisted, &sum);
745 if (this->report_scb)
746 {
747 this->report_scb(this->report_data, leaks, sum, whitelisted);
748 }
749 }
750 }
751
752 METHOD(leak_detective_t, set_report_cb, void,
753 private_leak_detective_t *this, leak_detective_report_cb_t cb,
754 leak_detective_summary_cb_t scb, void *user)
755 {
756 this->report_cb = cb;
757 this->report_scb = scb;
758 this->report_data = user;
759 }
760
761 METHOD(leak_detective_t, leaks, int,
762 private_leak_detective_t *this)
763 {
764 int whitelisted = 0;
765
766 return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL);
767 }
768
769 METHOD(leak_detective_t, set_state, bool,
770 private_leak_detective_t *this, bool enable)
771 {
772 return enable_thread(enable);
773 }
774
775 METHOD(leak_detective_t, usage, void,
776 private_leak_detective_t *this, leak_detective_report_cb_t cb,
777 leak_detective_summary_cb_t scb, void *user)
778 {
779 bool detailed;
780 int thresh, thresh_count, leaks, whitelisted = 0;
781 size_t sum = 0;
782
783 thresh = lib->settings->get_int(lib->settings,
784 "%s.leak_detective.usage_threshold", 10240, lib->ns);
785 thresh_count = lib->settings->get_int(lib->settings,
786 "%s.leak_detective.usage_threshold_count", 0, lib->ns);
787 detailed = lib->settings->get_bool(lib->settings,
788 "%s.leak_detective.detailed", TRUE, lib->ns);
789
790 leaks = print_traces(this, cb, user, thresh, thresh_count,
791 detailed, &whitelisted, &sum);
792 if (scb)
793 {
794 scb(user, leaks, sum, whitelisted);
795 }
796 }
797
798 /**
799 * Wrapped malloc() function
800 */
801 HOOK(void*, malloc, size_t bytes)
802 {
803 memory_header_t *hdr;
804 memory_tail_t *tail;
805 bool before;
806
807 if (!enabled || thread_disabled->get(thread_disabled))
808 {
809 return real_malloc(bytes);
810 }
811
812 hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
813 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
814 /* set to something which causes crashes */
815 memset(hdr, MEMORY_ALLOC_PATTERN,
816 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
817
818 before = enable_thread(FALSE);
819 hdr->backtrace = backtrace_create(2);
820 enable_thread(before);
821
822 hdr->magic = MEMORY_HEADER_MAGIC;
823 hdr->bytes = bytes;
824 tail->magic = MEMORY_TAIL_MAGIC;
825
826 add_hdr(hdr);
827
828 return hdr + 1;
829 }
830
831 /**
832 * Wrapped calloc() function
833 */
834 HOOK(void*, calloc, size_t nmemb, size_t size)
835 {
836 void *ptr;
837 volatile size_t total;
838
839 total = nmemb * size;
840 ptr = malloc(total);
841 memset(ptr, 0, total);
842
843 return ptr;
844 }
845
846 /**
847 * Wrapped valloc(), TODO: currently not supported
848 */
849 HOOK(void*, valloc, size_t size)
850 {
851 DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
852 return NULL;
853 }
854
855 /**
856 * Wrapped free() function
857 */
858 HOOK(void, free, void *ptr)
859 {
860 memory_header_t *hdr;
861 memory_tail_t *tail;
862 backtrace_t *backtrace;
863 bool before;
864
865 if (!enabled || thread_disabled->get(thread_disabled))
866 {
867 /* after deinitialization we might have to free stuff we allocated
868 * while we were enabled */
869 if (!first_header.magic && ptr)
870 {
871 hdr = ptr - sizeof(memory_header_t);
872 tail = ptr + hdr->bytes;
873 if (hdr->magic == MEMORY_HEADER_MAGIC &&
874 tail->magic == MEMORY_TAIL_MAGIC)
875 {
876 ptr = hdr;
877 }
878 }
879 real_free(ptr);
880 return;
881 }
882 /* allow freeing of NULL */
883 if (ptr == NULL)
884 {
885 return;
886 }
887 hdr = ptr - sizeof(memory_header_t);
888 tail = ptr + hdr->bytes;
889
890 before = enable_thread(FALSE);
891 if (hdr->magic != MEMORY_HEADER_MAGIC ||
892 tail->magic != MEMORY_TAIL_MAGIC)
893 {
894 if (has_hdr(hdr))
895 {
896 /* memory was allocated by our hooks but is corrupted */
897 fprintf(stderr, "freeing corrupted memory (%p): "
898 "header magic 0x%x, tail magic 0x%x:\n",
899 ptr, hdr->magic, tail->magic);
900 }
901 else
902 {
903 /* memory was not allocated by our hooks */
904 fprintf(stderr, "freeing invalid memory (%p)\n", ptr);
905 }
906 backtrace = backtrace_create(2);
907 backtrace->log(backtrace, stderr, TRUE);
908 backtrace->destroy(backtrace);
909 }
910 else
911 {
912 remove_hdr(hdr);
913
914 hdr->backtrace->destroy(hdr->backtrace);
915
916 /* clear MAGIC, set mem to something remarkable */
917 memset(hdr, MEMORY_FREE_PATTERN,
918 sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
919
920 real_free(hdr);
921 }
922 enable_thread(before);
923 }
924
925 /**
926 * Wrapped realloc() function
927 */
928 HOOK(void*, realloc, void *old, size_t bytes)
929 {
930 memory_header_t *hdr;
931 memory_tail_t *tail;
932 backtrace_t *backtrace;
933 bool before;
934
935 if (!enabled || thread_disabled->get(thread_disabled))
936 {
937 return real_realloc(old, bytes);
938 }
939 /* allow reallocation of NULL */
940 if (old == NULL)
941 {
942 return malloc(bytes);
943 }
944 /* handle zero size as a free() */
945 if (bytes == 0)
946 {
947 free(old);
948 return NULL;
949 }
950
951 hdr = old - sizeof(memory_header_t);
952 tail = old + hdr->bytes;
953
954 remove_hdr(hdr);
955
956 if (hdr->magic != MEMORY_HEADER_MAGIC ||
957 tail->magic != MEMORY_TAIL_MAGIC)
958 {
959 fprintf(stderr, "reallocating invalid memory (%p):\n"
960 "header magic 0x%x:\n", old, hdr->magic);
961 backtrace = backtrace_create(2);
962 backtrace->log(backtrace, stderr, TRUE);
963 backtrace->destroy(backtrace);
964 }
965 else
966 {
967 /* clear tail magic, allocate, set tail magic */
968 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
969 }
970 hdr = real_realloc(hdr,
971 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
972 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
973 tail->magic = MEMORY_TAIL_MAGIC;
974
975 /* update statistics */
976 hdr->bytes = bytes;
977
978 before = enable_thread(FALSE);
979 hdr->backtrace->destroy(hdr->backtrace);
980 hdr->backtrace = backtrace_create(2);
981 enable_thread(before);
982
983 add_hdr(hdr);
984
985 return hdr + 1;
986 }
987
988 METHOD(leak_detective_t, destroy, void,
989 private_leak_detective_t *this)
990 {
991 disable_leak_detective();
992 lock->destroy(lock);
993 thread_disabled->destroy(thread_disabled);
994 free(this);
995 first_header.magic = 0;
996 first_header.next = NULL;
997 }
998
999 /*
1000 * see header file
1001 */
1002 leak_detective_t *leak_detective_create()
1003 {
1004 private_leak_detective_t *this;
1005
1006 INIT(this,
1007 .public = {
1008 .report = _report,
1009 .set_report_cb = _set_report_cb,
1010 .usage = _usage,
1011 .leaks = _leaks,
1012 .set_state = _set_state,
1013 .destroy = _destroy,
1014 },
1015 );
1016
1017 if (getenv("LEAK_DETECTIVE_DISABLE") != NULL)
1018 {
1019 free(this);
1020 return NULL;
1021 }
1022
1023 lock = spinlock_create();
1024 thread_disabled = thread_value_create(NULL);
1025
1026 init_static_allocations();
1027
1028 if (register_hooks())
1029 {
1030 enable_leak_detective();
1031 }
1032 return &this->public;
1033 }