]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/utils/leak_detective.c
leak-detective: Whitelist functions added in OpenSSL 1.1.1
[thirdparty/strongswan.git] / src / libstrongswan / utils / leak_detective.c
CommitLineData
5113680f 1/*
472efd38 2 * Copyright (C) 2013-2018 Tobias Brunner
17211b6b 3 * Copyright (C) 2006-2013 Martin Willi
1b671669 4 * HSR Hochschule fuer Technik Rapperswil
5113680f
MW
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 */
7daf5226 16
7a485e90 17#define _GNU_SOURCE
5113680f 18#include <stddef.h>
5113680f
MW
19#include <string.h>
20#include <stdio.h>
7daf5226 21#include <signal.h>
1d025fbc 22#include <unistd.h>
552cc11b 23#include <locale.h>
922ee2c5 24#ifdef HAVE_DLADDR
17211b6b 25#include <dlfcn.h>
922ee2c5 26#endif
7e3f6299 27#include <time.h>
d8f6f0c0
MW
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__ */
5113680f
MW
38
39#include "leak_detective.h"
40
60356f33 41#include <library.h>
922ee2c5 42#include <utils/utils.h>
f05b4272 43#include <utils/debug.h>
f7237cf3 44#include <utils/backtrace.h>
12642a68 45#include <collections/hashtable.h>
17211b6b
MW
46#include <threading/thread_value.h>
47#include <threading/spinlock.h>
5113680f 48
552cc11b
MW
49typedef struct private_leak_detective_t private_leak_detective_t;
50
51/**
52 * private data of leak_detective
53 */
54struct private_leak_detective_t {
55
56 /**
57 * public functions
58 */
59 leak_detective_t public;
a426851f
MW
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;
552cc11b 75};
5113680f
MW
76
77/**
269f7f44 78 * Magic value which helps to detect memory corruption. Yummy!
5113680f 79 */
269f7f44
MW
80#define MEMORY_HEADER_MAGIC 0x7ac0be11
81
b7ef3f62
MW
82/**
83 * Magic written to tail of allocation
84 */
85#define MEMORY_TAIL_MAGIC 0xcafebabe
86
269f7f44
MW
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
5113680f 97typedef struct memory_header_t memory_header_t;
b7ef3f62 98typedef struct memory_tail_t memory_tail_t;
5113680f
MW
99
100/**
101 * Header which is prepended to each allocated memory block
102 */
103struct memory_header_t {
7daf5226 104
5113680f
MW
105 /**
106 * Pointer to previous entry in linked list
107 */
108 memory_header_t *previous;
7daf5226 109
5113680f
MW
110 /**
111 * Pointer to next entry in linked list
112 */
113 memory_header_t *next;
7daf5226 114
f7237cf3
MW
115 /**
116 * backtrace taken during (re-)allocation
117 */
118 backtrace_t *backtrace;
7daf5226 119
3117824f
MW
120 /**
121 * Padding to make sizeof(memory_header_t) == 32
122 */
b12c53ce 123 uint32_t padding[sizeof(void*) == sizeof(uint32_t) ? 3 : 0];
3117824f 124
35c93479
MW
125 /**
126 * Number of bytes following after the header
127 */
b12c53ce 128 uint32_t bytes;
35c93479 129
b7ef3f62
MW
130 /**
131 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
132 */
b12c53ce 133 uint32_t magic;
7daf5226 134
b7ef3f62
MW
135}__attribute__((__packed__));
136
137/**
138 * tail appended to each allocated memory block
139 */
140struct memory_tail_t {
141
142 /**
143 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
144 */
b12c53ce 145 uint32_t magic;
7daf5226 146
b7ef3f62 147}__attribute__((__packed__));
5113680f
MW
148
149/**
7daf5226 150 * first mem header is just a dummy to chain
5113680f
MW
151 * the others on it...
152 */
6b3292da 153static memory_header_t first_header = {
17211b6b 154 .magic = MEMORY_HEADER_MAGIC,
5113680f
MW
155};
156
1d025fbc 157/**
17211b6b 158 * Spinlock to access header linked list
1d025fbc 159 */
17211b6b
MW
160static spinlock_t *lock;
161
162/**
163 * Is leak detection currently enabled?
164 */
472efd38
TB
165static bool enabled;
166
167/**
168 * Whether to report calls to free() with memory not allocated by us
169 */
170static bool ignore_unknown;
17211b6b
MW
171
172/**
173 * Is leak detection disabled for the current thread?
174 */
175static thread_value_t *thread_disabled;
5113680f 176
eb4f4551
MW
177/**
178 * Installs the malloc hooks, enables leak detection
179 */
17211b6b 180static void enable_leak_detective()
eb4f4551 181{
17211b6b
MW
182 enabled = TRUE;
183}
184
185/**
186 * Uninstalls the malloc hooks, disables leak detection
187 */
188static 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 */
198static 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
3b26f04c
MW
207/**
208 * Add a header to the beginning of the list
209 */
210static 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 */
226static 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 */
240static 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
d8f6f0c0
MW
259#ifdef __APPLE__
260
261/**
262 * Copy of original default zone, with functions we call in hooks
263 */
264static malloc_zone_t original;
265
266/**
267 * Call original malloc()
268 */
269static void* real_malloc(size_t size)
270{
271 return original.malloc(malloc_default_zone(), size);
272}
273
274/**
275 * Call original free()
276 */
277static void real_free(void *ptr)
278{
279 original.free(malloc_default_zone(), ptr);
280}
281
282/**
283 * Call original realloc()
284 */
285static 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 */
299HOOK(void*, malloc, size_t bytes);
300HOOK(void*, calloc, size_t nmemb, size_t size);
301HOOK(void*, valloc, size_t size);
302HOOK(void, free, void *ptr);
303HOOK(void*, realloc, void *old, size_t bytes);
304
305/**
306 * malloc zone size(), must consider the memory header prepended
307 */
308HOOK(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 */
337static bool register_hooks()
338{
ef6d78d6 339 static bool once = FALSE;
d8f6f0c0
MW
340 malloc_zone_t *zone;
341 void *page;
342
ef6d78d6
MW
343 if (once)
344 {
345 return TRUE;
346 }
347 once = TRUE;
348
d8f6f0c0
MW
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
17211b6b
MW
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 */
388static 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))
eb4f4551 398 {
17211b6b 399 return NULL;
eb4f4551 400 }
17211b6b
MW
401 ptr = buf + used;
402 used += size;
403 return ptr;
eb4f4551
MW
404}
405
406/**
17211b6b
MW
407 * Lookup a malloc function, while disabling wrappers
408 */
409static 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 */
429static 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()
eb4f4551 452 */
17211b6b 453static void real_free(void *ptr)
eb4f4551 454{
17211b6b
MW
455 static void (*fn)(void *ptr);
456
457 if (!fn)
eb4f4551 458 {
17211b6b 459 fn = get_malloc_fn("free");
eb4f4551 460 }
17211b6b
MW
461 return fn(ptr);
462}
463
464/**
465 * Call original realloc()
466 */
467static 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);
eb4f4551
MW
476}
477
d8f6f0c0
MW
478/**
479 * Hook definition: plain function overloading existing malloc calls
480 */
481#define HOOK(ret, name, ...) ret name(__VA_ARGS__)
482
483/**
f5f7053b 484 * Hook initialization when not using hooks, resolve functions.
d8f6f0c0
MW
485 */
486static bool register_hooks()
487{
f5f7053b 488 void *buf = real_malloc(8);
b351acfe 489 buf = real_realloc(buf, 16);
f5f7053b 490 real_free(buf);
d8f6f0c0
MW
491 return TRUE;
492}
493
494#endif /* !__APPLE__ */
495
986d23bd 496/**
552cc11b 497 * Leak report white list
151168f6 498 *
552cc11b 499 * List of functions using static allocation buffers or should be suppressed
7daf5226 500 * otherwise on leak report.
986d23bd 501 */
95f9fa82 502static char *whitelist[] = {
f7237cf3
MW
503 /* backtraces, including own */
504 "backtrace_create",
ba10cd3c 505 "strerror_safe",
7939864d 506 /* pthread stuff */
552cc11b
MW
507 "pthread_create",
508 "pthread_setspecific",
9eb85cff 509 "__pthread_setspecific",
7939864d 510 /* glibc functions */
552cc11b
MW
511 "inet_ntoa",
512 "strerror",
0ffedbfb 513 "getprotobyname",
552cc11b
MW
514 "getprotobynumber",
515 "getservbyport",
516 "getservbyname",
b97ca14d 517 "gethostbyname",
3f9ec06f 518 "gethostbyname2",
5a22a021
MW
519 "gethostbyname_r",
520 "gethostbyname2_r",
3f9ec06f 521 "getnetbyname",
25b12c69
MW
522 "getpwnam_r",
523 "getgrnam_r",
552cc11b 524 "register_printf_function",
86813bef 525 "register_printf_specifier",
552cc11b
MW
526 "syslog",
527 "vsyslog",
044e0dd1
MW
528 "__syslog_chk",
529 "__vsyslog_chk",
8f180660 530 "__fprintf_chk",
552cc11b
MW
531 "getaddrinfo",
532 "setlocale",
70789d28 533 "getpass",
b97ca14d
AS
534 "getpwent_r",
535 "setpwent",
536 "endpwent",
0bac49b0 537 "getspnam_r",
c9418d4f
AS
538 "getpwuid_r",
539 "initgroups",
7bda0f0c 540 "tzset",
c87f4288 541 "_IO_file_doallocate",
7939864d
MW
542 /* ignore dlopen, as we do not dlclose to get proper leak reports */
543 "dlopen",
a3d92a37 544 "dlerror",
1caa265c 545 "dlclose",
07bda3fe 546 "dlsym",
7939864d 547 /* mysql functions */
552cc11b
MW
548 "mysql_init_character_set",
549 "init_client_errs",
550 "my_thread_init",
7939864d 551 /* fastcgi library */
cf4caefa 552 "FCGX_Init",
7939864d
MW
553 /* libxml */
554 "xmlInitCharEncodingHandlers",
555 "xmlInitParser",
556 "xmlInitParserCtxt",
727b0f11
AS
557 /* libcurl */
558 "Curl_client_write",
0b5d490e
TB
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",
f44e0efb
TB
566 /* libldap */
567 "ldap_int_initialize",
7939864d
MW
568 /* ClearSilver */
569 "nerr_init",
513a1a28 570 /* libgcrypt */
d7281749 571 "gcrypt_plugin_create",
513a1a28
MW
572 "gcry_control",
573 "gcry_check_version",
a41d0932
MW
574 "gcry_randomize",
575 "gcry_create_nonce",
092550b0
MW
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",
c1410cb0
TB
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",
0f7055b2
TB
585 "ERR_set_mark",
586 "ENGINE_load_builtin_engines",
587 "OPENSSL_load_builtin_modules",
588 "CONF_modules_load_file",
589 "CONF_module_add",
6fc90cea
TB
590 "RAND_DRBG_bytes",
591 "RAND_DRBG_generate",
592 "RAND_DRBG_get0_master",
593 "RAND_DRBG_get0_private",
594 "RAND_DRBG_get0_public",
6eaec1e3
MW
595 /* OpenSSL libssl */
596 "SSL_COMP_get_compression_methods",
50a9e845
MW
597 /* NSPR */
598 "PR_CallOnce",
1888dd6b
AS
599 /* libapr */
600 "apr_pool_create_ex",
ec8426a3 601 /* glib */
0b5d490e
TB
602 "g_output_stream_write",
603 "g_resolver_lookup_by_name",
604 "g_signal_connect_data",
605 "g_socket_connection_factory_lookup_type",
ec8426a3
MW
606 "g_type_init_with_debug_flags",
607 "g_type_register_static",
608 "g_type_class_ref",
609 "g_type_create_instance",
610 "g_type_add_interface_static",
611 "g_type_interface_add_prerequisite",
0b5d490e
TB
612 "g_private_set",
613 "g_queue_pop_tail",
ec8426a3
MW
614 /* libgpg */
615 "gpg_err_init",
7cfa84f5
MW
616 /* gnutls */
617 "gnutls_global_init",
fd2ade99
TB
618 /* Ada runtime */
619 "system__tasking__initialize",
620 "system__tasking__initialization__abort_defer",
621 "system__tasking__stages__create_task",
95f9fa82
TB
622 /* in case external threads call into our code */
623 "thread_current_id",
29e1c586
AS
624 /* FHH IMCs and IMVs */
625 "TNC_IMC_NotifyConnectionChange",
626 "TNC_IMV_NotifyConnectionChange",
c064a528
TB
627 /* Botan */
628 "botan_public_key_load",
629 "botan_privkey_create_ecdsa",
630 "botan_privkey_create_ecdh",
631 "botan_privkey_load_ecdh",
4bcc4bac 632 "botan_privkey_load",
986d23bd
MW
633};
634
7e3f6299
MW
635/**
636 * Some functions are hard to whitelist, as they don't use a symbol directly.
637 * Use some static initialization to suppress them on leak reports
638 */
639static void init_static_allocations()
640{
56866ecf
MW
641 struct tm tm;
642 time_t t = 0;
643
7e3f6299 644 tzset();
56866ecf
MW
645 gmtime_r(&t, &tm);
646 localtime_r(&t, &tm);
7e3f6299 647}
5113680f 648
fce3b5c3
MW
649/**
650 * Hashtable hash function
651 */
652static u_int hash(backtrace_t *key)
653{
654 enumerator_t *enumerator;
655 void *addr;
656 u_int hash = 0;
657
658 enumerator = key->create_frame_enumerator(key);
659 while (enumerator->enumerate(enumerator, &addr))
660 {
661 hash = chunk_hash_inc(chunk_from_thing(addr), hash);
662 }
663 enumerator->destroy(enumerator);
664
665 return hash;
666}
667
668/**
669 * Hashtable equals function
670 */
671static bool equals(backtrace_t *a, backtrace_t *b)
672{
673 return a->equals(a, b);
674}
675
eb4f4551
MW
676/**
677 * Summarize and print backtraces
678 */
679static int print_traces(private_leak_detective_t *this,
a426851f
MW
680 leak_detective_report_cb_t cb, void *user,
681 int thresh, int thresh_count,
c93cf853 682 bool detailed, int *whitelisted, size_t *sum)
fce3b5c3 683{
eb4f4551 684 int leaks = 0;
fce3b5c3
MW
685 memory_header_t *hdr;
686 enumerator_t *enumerator;
8eea2806
TB
687 hashtable_t *entries, *ignored = NULL;
688 backtrace_t *bt;
fce3b5c3
MW
689 struct {
690 /** associated backtrace */
691 backtrace_t *backtrace;
692 /** total size of all allocations */
693 size_t bytes;
694 /** number of allocations */
695 u_int count;
696 } *entry;
17211b6b 697 bool before;
fce3b5c3 698
17211b6b 699 before = enable_thread(FALSE);
fce3b5c3
MW
700
701 entries = hashtable_create((hashtable_hash_t)hash,
702 (hashtable_equals_t)equals, 1024);
8eea2806
TB
703 if (whitelisted)
704 {
705 ignored = hashtable_create((hashtable_hash_t)hash,
706 (hashtable_equals_t)equals, 1024);
707 }
708
17211b6b 709 lock->lock(lock);
fce3b5c3
MW
710 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
711 {
8eea2806 712 if (whitelisted)
eb4f4551 713 {
8eea2806
TB
714 bt = ignored->get(ignored, hdr->backtrace);
715 if (!bt)
716 {
717 if (hdr->backtrace->contains_function(hdr->backtrace, whitelist,
718 countof(whitelist)))
719 {
720 bt = hdr->backtrace;
721 ignored->put(ignored, bt, bt);
722 }
723 }
724 if (bt)
725 {
726 (*whitelisted)++;
727 continue;
728 }
eb4f4551 729 }
fce3b5c3
MW
730 entry = entries->get(entries, hdr->backtrace);
731 if (entry)
732 {
733 entry->bytes += hdr->bytes;
734 entry->count++;
735 }
736 else
737 {
738 INIT(entry,
f960b390 739 .backtrace = hdr->backtrace->clone(hdr->backtrace),
fce3b5c3
MW
740 .bytes = hdr->bytes,
741 .count = 1,
742 );
f960b390 743 entries->put(entries, entry->backtrace, entry);
fce3b5c3 744 }
c93cf853
MW
745 if (sum)
746 {
747 *sum += hdr->bytes;
748 }
eb4f4551 749 leaks++;
fce3b5c3 750 }
17211b6b 751 lock->unlock(lock);
8eea2806 752 DESTROY_IF(ignored);
a426851f 753
fce3b5c3
MW
754 enumerator = entries->create_enumerator(entries);
755 while (enumerator->enumerate(enumerator, NULL, &entry))
756 {
a426851f 757 if (cb)
fce3b5c3 758 {
a426851f
MW
759 if (!thresh || entry->bytes >= thresh)
760 {
761 if (!thresh_count || entry->count >= thresh_count)
762 {
e4d2c9f1
TB
763 cb(user, entry->count, entry->bytes, entry->backtrace,
764 detailed);
a426851f
MW
765 }
766 }
fce3b5c3 767 }
f960b390 768 entry->backtrace->destroy(entry->backtrace);
fce3b5c3
MW
769 free(entry);
770 }
771 enumerator->destroy(enumerator);
772 entries->destroy(entries);
773
17211b6b 774 enable_thread(before);
eb4f4551
MW
775 return leaks;
776}
777
778METHOD(leak_detective_t, report, void,
779 private_leak_detective_t *this, bool detailed)
780{
781 if (lib->leak_detective)
782 {
01e15ab5 783 int leaks, whitelisted = 0;
c93cf853 784 size_t sum = 0;
eb4f4551 785
a426851f
MW
786 leaks = print_traces(this, this->report_cb, this->report_data,
787 0, 0, detailed, &whitelisted, &sum);
788 if (this->report_scb)
eb4f4551 789 {
a426851f 790 this->report_scb(this->report_data, leaks, sum, whitelisted);
eb4f4551 791 }
eb4f4551
MW
792 }
793}
794
a426851f
MW
795METHOD(leak_detective_t, set_report_cb, void,
796 private_leak_detective_t *this, leak_detective_report_cb_t cb,
797 leak_detective_summary_cb_t scb, void *user)
798{
799 this->report_cb = cb;
800 this->report_scb = scb;
801 this->report_data = user;
802}
803
01e15ab5
TB
804METHOD(leak_detective_t, leaks, int,
805 private_leak_detective_t *this)
806{
a426851f 807 int whitelisted = 0;
01e15ab5 808
a426851f 809 return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL);
01e15ab5
TB
810}
811
b46776ae
MW
812METHOD(leak_detective_t, set_state, bool,
813 private_leak_detective_t *this, bool enable)
814{
82d0317b 815 return enable_thread(enable);
b46776ae
MW
816}
817
eb4f4551 818METHOD(leak_detective_t, usage, void,
a426851f
MW
819 private_leak_detective_t *this, leak_detective_report_cb_t cb,
820 leak_detective_summary_cb_t scb, void *user)
eb4f4551 821{
eb4f4551 822 bool detailed;
a426851f 823 int thresh, thresh_count, leaks, whitelisted = 0;
c93cf853 824 size_t sum = 0;
eb4f4551
MW
825
826 thresh = lib->settings->get_int(lib->settings,
8dc6e716 827 "%s.leak_detective.usage_threshold", 10240, lib->ns);
81959e64 828 thresh_count = lib->settings->get_int(lib->settings,
8dc6e716 829 "%s.leak_detective.usage_threshold_count", 0, lib->ns);
eb4f4551 830 detailed = lib->settings->get_bool(lib->settings,
8dc6e716 831 "%s.leak_detective.detailed", TRUE, lib->ns);
eb4f4551 832
a426851f
MW
833 leaks = print_traces(this, cb, user, thresh, thresh_count,
834 detailed, &whitelisted, &sum);
835 if (scb)
836 {
837 scb(user, leaks, sum, whitelisted);
838 }
fce3b5c3
MW
839}
840
5113680f 841/**
17211b6b 842 * Wrapped malloc() function
5113680f 843 */
d8f6f0c0 844HOOK(void*, malloc, size_t bytes)
5113680f
MW
845{
846 memory_header_t *hdr;
b7ef3f62 847 memory_tail_t *tail;
17211b6b 848 bool before;
7daf5226 849
17211b6b
MW
850 if (!enabled || thread_disabled->get(thread_disabled))
851 {
852 return real_malloc(bytes);
853 }
7daf5226 854
17211b6b 855 hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
b7ef3f62 856 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
986d23bd 857 /* set to something which causes crashes */
b7ef3f62
MW
858 memset(hdr, MEMORY_ALLOC_PATTERN,
859 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
7daf5226 860
17211b6b
MW
861 before = enable_thread(FALSE);
862 hdr->backtrace = backtrace_create(2);
863 enable_thread(before);
864
5113680f
MW
865 hdr->magic = MEMORY_HEADER_MAGIC;
866 hdr->bytes = bytes;
b7ef3f62 867 tail->magic = MEMORY_TAIL_MAGIC;
7daf5226 868
3b26f04c 869 add_hdr(hdr);
7daf5226 870
5113680f
MW
871 return hdr + 1;
872}
873
874/**
17211b6b 875 * Wrapped calloc() function
5113680f 876 */
d8f6f0c0 877HOOK(void*, calloc, size_t nmemb, size_t size)
17211b6b
MW
878{
879 void *ptr;
e0c59faa 880 volatile size_t total;
17211b6b 881
e0c59faa
TB
882 total = nmemb * size;
883 ptr = malloc(total);
884 memset(ptr, 0, total);
17211b6b
MW
885
886 return ptr;
887}
888
d8f6f0c0
MW
889/**
890 * Wrapped valloc(), TODO: currently not supported
891 */
892HOOK(void*, valloc, size_t size)
893{
894 DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
895 return NULL;
896}
897
17211b6b
MW
898/**
899 * Wrapped free() function
900 */
d8f6f0c0 901HOOK(void, free, void *ptr)
5113680f 902{
3b26f04c 903 memory_header_t *hdr;
b7ef3f62 904 memory_tail_t *tail;
323f9f99 905 backtrace_t *backtrace;
3b26f04c 906 bool before;
7daf5226 907
17211b6b
MW
908 if (!enabled || thread_disabled->get(thread_disabled))
909 {
505c3187
TB
910 /* after deinitialization we might have to free stuff we allocated
911 * while we were enabled */
912 if (!first_header.magic && ptr)
913 {
914 hdr = ptr - sizeof(memory_header_t);
915 tail = ptr + hdr->bytes;
916 if (hdr->magic == MEMORY_HEADER_MAGIC &&
917 tail->magic == MEMORY_TAIL_MAGIC)
918 {
919 ptr = hdr;
920 }
921 }
17211b6b
MW
922 real_free(ptr);
923 return;
924 }
5113680f 925 /* allow freeing of NULL */
472efd38 926 if (!ptr)
5113680f
MW
927 {
928 return;
929 }
b7ef3f62
MW
930 hdr = ptr - sizeof(memory_header_t);
931 tail = ptr + hdr->bytes;
7daf5226 932
17211b6b 933 before = enable_thread(FALSE);
6c45e622
MW
934 if (hdr->magic != MEMORY_HEADER_MAGIC ||
935 tail->magic != MEMORY_TAIL_MAGIC)
5113680f 936 {
472efd38
TB
937 bool bt = TRUE;
938
939 /* check if memory appears to be allocated by our hooks */
3b26f04c 940 if (has_hdr(hdr))
587ebae7 941 {
587ebae7 942 fprintf(stderr, "freeing corrupted memory (%p): "
472efd38
TB
943 "%u bytes, header magic 0x%x, tail magic 0x%x:\n",
944 ptr, hdr->bytes, hdr->magic, tail->magic);
945 remove_hdr(hdr);
946
947 if (hdr->magic == MEMORY_HEADER_MAGIC)
948 { /* only access the old backtrace if header magic is valid */
949 hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
950 hdr->backtrace->destroy(hdr->backtrace);
951 }
952 else
953 {
954 fprintf(stderr, " header magic invalid, ignore backtrace of "
955 "allocation\n");
956 }
587ebae7
MW
957 }
958 else
959 {
472efd38
TB
960 /* just free this block of unknown memory */
961 hdr = ptr;
962
963 if (ignore_unknown)
964 {
965 bt = FALSE;
966 }
967 else
968 {
969 fprintf(stderr, "freeing unknown memory (%p):\n", ptr);
970 }
971 }
972 if (bt)
973 {
974 backtrace = backtrace_create(2);
975 backtrace->log(backtrace, stderr, TRUE);
976 backtrace->destroy(backtrace);
587ebae7 977 }
5113680f 978 }
6c45e622 979 else
b7ef3f62 980 {
3b26f04c 981 remove_hdr(hdr);
17211b6b 982
f7237cf3 983 hdr->backtrace->destroy(hdr->backtrace);
7daf5226 984
472efd38 985 /* set mem to something remarkable */
587ebae7
MW
986 memset(hdr, MEMORY_FREE_PATTERN,
987 sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
6c45e622 988 }
472efd38 989 real_free(hdr);
17211b6b 990 enable_thread(before);
5113680f
MW
991}
992
993/**
17211b6b 994 * Wrapped realloc() function
5113680f 995 */
d8f6f0c0 996HOOK(void*, realloc, void *old, size_t bytes)
5113680f 997{
a401efd0 998 memory_header_t *hdr;
b7ef3f62 999 memory_tail_t *tail;
f7237cf3 1000 backtrace_t *backtrace;
472efd38 1001 bool before, have_backtrace = TRUE;
7daf5226 1002
17211b6b
MW
1003 if (!enabled || thread_disabled->get(thread_disabled))
1004 {
1005 return real_realloc(old, bytes);
1006 }
5113680f 1007 /* allow reallocation of NULL */
472efd38 1008 if (!old)
5113680f 1009 {
17211b6b 1010 return malloc(bytes);
5113680f 1011 }
1e54e40f 1012 /* handle zero size as a free() */
472efd38 1013 if (!bytes)
1e54e40f
MW
1014 {
1015 free(old);
1016 return NULL;
1017 }
7daf5226 1018
a401efd0 1019 hdr = old - sizeof(memory_header_t);
b7ef3f62 1020 tail = old + hdr->bytes;
7daf5226 1021
472efd38 1022 before = enable_thread(FALSE);
6c45e622
MW
1023 if (hdr->magic != MEMORY_HEADER_MAGIC ||
1024 tail->magic != MEMORY_TAIL_MAGIC)
b7ef3f62 1025 {
472efd38
TB
1026 bool bt = TRUE;
1027
1028 /* check if memory appears to be allocated by our hooks */
1029 if (has_hdr(hdr))
1030 {
1031 fprintf(stderr, "reallocating corrupted memory (%p, %u bytes): "
1032 "%zu bytes, header magic 0x%x, tail magic 0x%x:\n",
1033 old, hdr->bytes, bytes, hdr->magic, tail->magic);
1034 remove_hdr(hdr);
1035
1036 if (hdr->magic == MEMORY_HEADER_MAGIC)
1037 { /* only access header fields (backtrace, bytes) if header magic
1038 * is still valid */
1039 hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
1040 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
1041 }
1042 else
1043 {
1044 fprintf(stderr, " header magic invalid, ignore backtrace of "
1045 "allocation\n");
1046 have_backtrace = FALSE;
1047 hdr->magic = MEMORY_HEADER_MAGIC;
1048 }
1049 }
1050 else
1051 {
1052 /* adopt this block of unknown memory */
1053 hdr = old;
1054 have_backtrace = FALSE;
1055
1056 if (ignore_unknown)
1057 {
1058 bt = FALSE;
1059 }
1060 else
1061 {
1062 fprintf(stderr, "reallocating unknown memory (%p): %zu bytes:\n",
1063 old, bytes);
1064 }
1065 }
1066 if (bt)
1067 {
1068 backtrace = backtrace_create(2);
1069 backtrace->log(backtrace, stderr, TRUE);
1070 backtrace->destroy(backtrace);
1071 }
b7ef3f62 1072 }
6499354e
MW
1073 else
1074 {
472efd38 1075 remove_hdr(hdr);
6499354e
MW
1076 /* clear tail magic, allocate, set tail magic */
1077 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
1078 }
472efd38 1079
17211b6b
MW
1080 hdr = real_realloc(hdr,
1081 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
b7ef3f62
MW
1082 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
1083 tail->magic = MEMORY_TAIL_MAGIC;
6c45e622 1084
a401efd0
MW
1085 /* update statistics */
1086 hdr->bytes = bytes;
17211b6b 1087
472efd38
TB
1088 if (have_backtrace)
1089 {
1090 hdr->backtrace->destroy(hdr->backtrace);
1091 }
549eba30 1092 hdr->backtrace = backtrace_create(2);
17211b6b 1093 enable_thread(before);
6c45e622 1094
3b26f04c 1095 add_hdr(hdr);
17211b6b 1096
a401efd0 1097 return hdr + 1;
5113680f
MW
1098}
1099
42e0f26e
MW
1100METHOD(leak_detective_t, destroy, void,
1101 private_leak_detective_t *this)
5113680f 1102{
17211b6b
MW
1103 disable_leak_detective();
1104 lock->destroy(lock);
1105 thread_disabled->destroy(thread_disabled);
552cc11b 1106 free(this);
505c3187 1107 first_header.magic = 0;
f192526c 1108 first_header.next = NULL;
6b3292da
MW
1109}
1110
552cc11b
MW
1111/*
1112 * see header file
73760ca5 1113 */
552cc11b 1114leak_detective_t *leak_detective_create()
73760ca5 1115{
42e0f26e
MW
1116 private_leak_detective_t *this;
1117
1118 INIT(this,
1119 .public = {
1120 .report = _report,
a426851f 1121 .set_report_cb = _set_report_cb,
fce3b5c3 1122 .usage = _usage,
a426851f 1123 .leaks = _leaks,
b46776ae 1124 .set_state = _set_state,
42e0f26e
MW
1125 .destroy = _destroy,
1126 },
1127 );
7daf5226 1128
adc11574
TB
1129 if (getenv("LEAK_DETECTIVE_DISABLE") != NULL)
1130 {
1131 free(this);
1132 return NULL;
1133 }
472efd38 1134 ignore_unknown = getenv("LEAK_DETECTIVE_IGNORE_UNKNOWN") != NULL;
adc11574 1135
17211b6b
MW
1136 lock = spinlock_create();
1137 thread_disabled = thread_value_create(NULL);
1138
7e3f6299
MW
1139 init_static_allocations();
1140
adc11574 1141 if (register_hooks())
73760ca5 1142 {
adc11574 1143 enable_leak_detective();
73760ca5 1144 }
552cc11b 1145 return &this->public;
73760ca5 1146}