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