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