]> git.ipfire.org Git - people/ms/strongswan.git/blame - src/libstrongswan/utils/leak_detective.c
Merge branch 'unit-tests'
[people/ms/strongswan.git] / src / libstrongswan / utils / leak_detective.c
CommitLineData
5113680f 1/*
17211b6b
MW
2 * Copyright (C) 2013 Tobias Brunner
3 * Copyright (C) 2006-2013 Martin Willi
5113680f
MW
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 */
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>
5113680f
MW
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
1d025fbc 25#include <unistd.h>
8bc96e08 26#include <syslog.h>
b1904247 27#include <netdb.h>
552cc11b 28#include <locale.h>
17211b6b 29#include <dlfcn.h>
7e3f6299 30#include <time.h>
d8f6f0c0
MW
31#include <errno.h>
32
33#ifdef __APPLE__
34#include <sys/mman.h>
35#include <malloc/malloc.h>
36/* overload some of our types clashing with mach */
37#define host_t strongswan_host_t
38#define processor_t strongswan_processor_t
39#define thread_t strongswan_thread_t
40#endif /* __APPLE__ */
5113680f
MW
41
42#include "leak_detective.h"
43
60356f33 44#include <library.h>
f05b4272 45#include <utils/debug.h>
f7237cf3 46#include <utils/backtrace.h>
12642a68 47#include <collections/hashtable.h>
17211b6b
MW
48#include <threading/thread_value.h>
49#include <threading/spinlock.h>
5113680f 50
552cc11b
MW
51typedef struct private_leak_detective_t private_leak_detective_t;
52
53/**
54 * private data of leak_detective
55 */
56struct private_leak_detective_t {
57
58 /**
59 * public functions
60 */
61 leak_detective_t public;
62};
5113680f
MW
63
64/**
269f7f44 65 * Magic value which helps to detect memory corruption. Yummy!
5113680f 66 */
269f7f44
MW
67#define MEMORY_HEADER_MAGIC 0x7ac0be11
68
b7ef3f62
MW
69/**
70 * Magic written to tail of allocation
71 */
72#define MEMORY_TAIL_MAGIC 0xcafebabe
73
269f7f44
MW
74/**
75 * Pattern which is filled in memory before freeing it
76 */
77#define MEMORY_FREE_PATTERN 0xFF
78
79/**
80 * Pattern which is filled in newly allocated memory
81 */
82#define MEMORY_ALLOC_PATTERN 0xEE
83
5113680f 84typedef struct memory_header_t memory_header_t;
b7ef3f62 85typedef struct memory_tail_t memory_tail_t;
5113680f
MW
86
87/**
88 * Header which is prepended to each allocated memory block
89 */
90struct memory_header_t {
7daf5226 91
5113680f
MW
92 /**
93 * Pointer to previous entry in linked list
94 */
95 memory_header_t *previous;
7daf5226 96
5113680f
MW
97 /**
98 * Pointer to next entry in linked list
99 */
100 memory_header_t *next;
7daf5226 101
f7237cf3
MW
102 /**
103 * backtrace taken during (re-)allocation
104 */
105 backtrace_t *backtrace;
7daf5226 106
3117824f
MW
107 /**
108 * Padding to make sizeof(memory_header_t) == 32
109 */
110 u_int32_t padding[sizeof(void*) == sizeof(u_int32_t) ? 3 : 0];
111
35c93479
MW
112 /**
113 * Number of bytes following after the header
114 */
115 u_int32_t bytes;
116
b7ef3f62
MW
117 /**
118 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
119 */
120 u_int32_t magic;
7daf5226 121
b7ef3f62
MW
122}__attribute__((__packed__));
123
124/**
125 * tail appended to each allocated memory block
126 */
127struct memory_tail_t {
128
129 /**
130 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
131 */
132 u_int32_t magic;
7daf5226 133
b7ef3f62 134}__attribute__((__packed__));
5113680f
MW
135
136/**
7daf5226 137 * first mem header is just a dummy to chain
5113680f
MW
138 * the others on it...
139 */
6b3292da 140static memory_header_t first_header = {
17211b6b 141 .magic = MEMORY_HEADER_MAGIC,
5113680f
MW
142};
143
1d025fbc 144/**
17211b6b 145 * Spinlock to access header linked list
1d025fbc 146 */
17211b6b
MW
147static spinlock_t *lock;
148
149/**
150 * Is leak detection currently enabled?
151 */
152static bool enabled = FALSE;
153
154/**
155 * Is leak detection disabled for the current thread?
156 */
157static thread_value_t *thread_disabled;
5113680f 158
eb4f4551
MW
159/**
160 * Installs the malloc hooks, enables leak detection
161 */
17211b6b 162static void enable_leak_detective()
eb4f4551 163{
17211b6b
MW
164 enabled = TRUE;
165}
166
167/**
168 * Uninstalls the malloc hooks, disables leak detection
169 */
170static void disable_leak_detective()
171{
172 enabled = FALSE;
173}
174
175/**
176 * Enable/Disable leak detective for the current thread
177 *
178 * @return Previous value
179 */
180static bool enable_thread(bool enable)
181{
182 bool before;
183
184 before = thread_disabled->get(thread_disabled) == NULL;
185 thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE);
186 return before;
187}
188
d8f6f0c0
MW
189#ifdef __APPLE__
190
191/**
192 * Copy of original default zone, with functions we call in hooks
193 */
194static malloc_zone_t original;
195
196/**
197 * Call original malloc()
198 */
199static void* real_malloc(size_t size)
200{
201 return original.malloc(malloc_default_zone(), size);
202}
203
204/**
205 * Call original free()
206 */
207static void real_free(void *ptr)
208{
209 original.free(malloc_default_zone(), ptr);
210}
211
212/**
213 * Call original realloc()
214 */
215static void* real_realloc(void *ptr, size_t size)
216{
217 return original.realloc(malloc_default_zone(), ptr, size);
218}
219
220/**
221 * Hook definition: static function with _hook suffix, takes additional zone
222 */
223#define HOOK(ret, name, ...) \
224 static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__)
225
226/**
227 * forward declaration of hooks
228 */
229HOOK(void*, malloc, size_t bytes);
230HOOK(void*, calloc, size_t nmemb, size_t size);
231HOOK(void*, valloc, size_t size);
232HOOK(void, free, void *ptr);
233HOOK(void*, realloc, void *old, size_t bytes);
234
235/**
236 * malloc zone size(), must consider the memory header prepended
237 */
238HOOK(size_t, size, const void *ptr)
239{
240 bool before;
241 size_t size;
242
243 if (enabled)
244 {
245 before = enable_thread(FALSE);
246 if (before)
247 {
248 ptr -= sizeof(memory_header_t);
249 }
250 }
251 size = original.size(malloc_default_zone(), ptr);
252 if (enabled)
253 {
254 enable_thread(before);
255 }
256 return size;
257}
258
259/**
260 * Version of malloc zones we currently support
261 */
262#define MALLOC_ZONE_VERSION 8 /* Snow Leopard */
263
264/**
265 * Hook-in our malloc functions into the default zone
266 */
267static bool register_hooks()
268{
269 malloc_zone_t *zone;
270 void *page;
271
272 zone = malloc_default_zone();
273 if (zone->version != MALLOC_ZONE_VERSION)
274 {
275 DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)",
276 zone->version, MALLOC_ZONE_VERSION);
277 return FALSE;
278 }
279
280 original = *zone;
281
282 page = (void*)((uintptr_t)zone / getpagesize() * getpagesize());
283 if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0)
284 {
285 DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno));
286 return FALSE;
287 }
288
289 zone->size = size_hook;
290 zone->malloc = malloc_hook;
291 zone->calloc = calloc_hook;
292 zone->valloc = valloc_hook;
293 zone->free = free_hook;
294 zone->realloc = realloc_hook;
295
296 /* those other functions can be NULLed out to not use them */
297 zone->batch_malloc = NULL;
298 zone->batch_free = NULL;
299 zone->memalign = NULL;
300 zone->free_definite_size = NULL;
301
302 return TRUE;
303}
304
305#else /* !__APPLE__ */
306
17211b6b
MW
307/**
308 * dlsym() might do a malloc(), but we can't do one before we get the malloc()
309 * function pointer. Use this minimalistic malloc implementation instead.
310 */
311static void* malloc_for_dlsym(size_t size)
312{
313 static char buf[1024] = {};
314 static size_t used = 0;
315 char *ptr;
316
317 /* roundup to a multiple of 32 */
318 size = (size - 1) / 32 * 32 + 32;
319
320 if (used + size > sizeof(buf))
eb4f4551 321 {
17211b6b 322 return NULL;
eb4f4551 323 }
17211b6b
MW
324 ptr = buf + used;
325 used += size;
326 return ptr;
eb4f4551
MW
327}
328
329/**
17211b6b
MW
330 * Lookup a malloc function, while disabling wrappers
331 */
332static void* get_malloc_fn(char *name)
333{
334 bool before = FALSE;
335 void *fn;
336
337 if (enabled)
338 {
339 before = enable_thread(FALSE);
340 }
341 fn = dlsym(RTLD_NEXT, name);
342 if (enabled)
343 {
344 enable_thread(before);
345 }
346 return fn;
347}
348
349/**
350 * Call original malloc()
351 */
352static void* real_malloc(size_t size)
353{
354 static void* (*fn)(size_t size);
355 static int recursive = 0;
356
357 if (!fn)
358 {
359 /* checking recursiveness should actually be thread-specific. But as
360 * it is very likely that the first allocation is done before we go
361 * multi-threaded, we keep it simple. */
362 if (recursive)
363 {
364 return malloc_for_dlsym(size);
365 }
366 recursive++;
367 fn = get_malloc_fn("malloc");
368 recursive--;
369 }
370 return fn(size);
371}
372
373/**
374 * Call original free()
eb4f4551 375 */
17211b6b 376static void real_free(void *ptr)
eb4f4551 377{
17211b6b
MW
378 static void (*fn)(void *ptr);
379
380 if (!fn)
eb4f4551 381 {
17211b6b 382 fn = get_malloc_fn("free");
eb4f4551 383 }
17211b6b
MW
384 return fn(ptr);
385}
386
387/**
388 * Call original realloc()
389 */
390static void* real_realloc(void *ptr, size_t size)
391{
392 static void* (*fn)(void *ptr, size_t size);
393
394 if (!fn)
395 {
396 fn = get_malloc_fn("realloc");
397 }
398 return fn(ptr, size);
eb4f4551
MW
399}
400
d8f6f0c0
MW
401/**
402 * Hook definition: plain function overloading existing malloc calls
403 */
404#define HOOK(ret, name, ...) ret name(__VA_ARGS__)
405
406/**
407 * Hook initialization when not using hooks
408 */
409static bool register_hooks()
410{
411 return TRUE;
412}
413
414#endif /* !__APPLE__ */
415
986d23bd 416/**
552cc11b 417 * Leak report white list
151168f6 418 *
552cc11b 419 * List of functions using static allocation buffers or should be suppressed
7daf5226 420 * otherwise on leak report.
986d23bd 421 */
552cc11b 422char *whitelist[] = {
f7237cf3
MW
423 /* backtraces, including own */
424 "backtrace_create",
7c8b9fcb 425 "safe_strerror",
7939864d 426 /* pthread stuff */
552cc11b
MW
427 "pthread_create",
428 "pthread_setspecific",
9eb85cff 429 "__pthread_setspecific",
7939864d 430 /* glibc functions */
552cc11b
MW
431 "inet_ntoa",
432 "strerror",
0ffedbfb 433 "getprotobyname",
552cc11b
MW
434 "getprotobynumber",
435 "getservbyport",
436 "getservbyname",
b97ca14d 437 "gethostbyname",
3f9ec06f 438 "gethostbyname2",
5a22a021
MW
439 "gethostbyname_r",
440 "gethostbyname2_r",
3f9ec06f 441 "getnetbyname",
25b12c69
MW
442 "getpwnam_r",
443 "getgrnam_r",
552cc11b 444 "register_printf_function",
86813bef 445 "register_printf_specifier",
552cc11b
MW
446 "syslog",
447 "vsyslog",
044e0dd1
MW
448 "__syslog_chk",
449 "__vsyslog_chk",
552cc11b
MW
450 "getaddrinfo",
451 "setlocale",
70789d28 452 "getpass",
b97ca14d
AS
453 "getpwent_r",
454 "setpwent",
455 "endpwent",
0bac49b0 456 "getspnam_r",
c9418d4f
AS
457 "getpwuid_r",
458 "initgroups",
7939864d
MW
459 /* ignore dlopen, as we do not dlclose to get proper leak reports */
460 "dlopen",
a3d92a37 461 "dlerror",
1caa265c 462 "dlclose",
07bda3fe 463 "dlsym",
7939864d 464 /* mysql functions */
552cc11b
MW
465 "mysql_init_character_set",
466 "init_client_errs",
467 "my_thread_init",
7939864d 468 /* fastcgi library */
cf4caefa 469 "FCGX_Init",
7939864d
MW
470 /* libxml */
471 "xmlInitCharEncodingHandlers",
472 "xmlInitParser",
473 "xmlInitParserCtxt",
727b0f11
AS
474 /* libcurl */
475 "Curl_client_write",
7939864d
MW
476 /* ClearSilver */
477 "nerr_init",
513a1a28
MW
478 /* libgcrypt */
479 "gcry_control",
480 "gcry_check_version",
a41d0932
MW
481 "gcry_randomize",
482 "gcry_create_nonce",
50a9e845
MW
483 /* NSPR */
484 "PR_CallOnce",
1888dd6b
AS
485 /* libapr */
486 "apr_pool_create_ex",
ec8426a3
MW
487 /* glib */
488 "g_type_init_with_debug_flags",
489 "g_type_register_static",
490 "g_type_class_ref",
491 "g_type_create_instance",
492 "g_type_add_interface_static",
493 "g_type_interface_add_prerequisite",
494 "g_socket_connection_factory_lookup_type",
495 /* libgpg */
496 "gpg_err_init",
7cfa84f5
MW
497 /* gnutls */
498 "gnutls_global_init",
986d23bd
MW
499};
500
7e3f6299
MW
501/**
502 * Some functions are hard to whitelist, as they don't use a symbol directly.
503 * Use some static initialization to suppress them on leak reports
504 */
505static void init_static_allocations()
506{
507 tzset();
508}
5113680f 509
fce3b5c3
MW
510/**
511 * Hashtable hash function
512 */
513static u_int hash(backtrace_t *key)
514{
515 enumerator_t *enumerator;
516 void *addr;
517 u_int hash = 0;
518
519 enumerator = key->create_frame_enumerator(key);
520 while (enumerator->enumerate(enumerator, &addr))
521 {
522 hash = chunk_hash_inc(chunk_from_thing(addr), hash);
523 }
524 enumerator->destroy(enumerator);
525
526 return hash;
527}
528
529/**
530 * Hashtable equals function
531 */
532static bool equals(backtrace_t *a, backtrace_t *b)
533{
534 return a->equals(a, b);
535}
536
eb4f4551
MW
537/**
538 * Summarize and print backtraces
539 */
540static int print_traces(private_leak_detective_t *this,
541 FILE *out, int thresh, bool detailed, int *whitelisted)
fce3b5c3 542{
eb4f4551 543 int leaks = 0;
fce3b5c3
MW
544 memory_header_t *hdr;
545 enumerator_t *enumerator;
546 hashtable_t *entries;
547 struct {
548 /** associated backtrace */
549 backtrace_t *backtrace;
550 /** total size of all allocations */
551 size_t bytes;
552 /** number of allocations */
553 u_int count;
554 } *entry;
17211b6b 555 bool before;
fce3b5c3 556
17211b6b 557 before = enable_thread(FALSE);
fce3b5c3
MW
558
559 entries = hashtable_create((hashtable_hash_t)hash,
560 (hashtable_equals_t)equals, 1024);
17211b6b 561 lock->lock(lock);
fce3b5c3
MW
562 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
563 {
eb4f4551
MW
564 if (whitelisted &&
565 hdr->backtrace->contains_function(hdr->backtrace,
566 whitelist, countof(whitelist)))
567 {
568 (*whitelisted)++;
569 continue;
570 }
fce3b5c3
MW
571 entry = entries->get(entries, hdr->backtrace);
572 if (entry)
573 {
574 entry->bytes += hdr->bytes;
575 entry->count++;
576 }
577 else
578 {
579 INIT(entry,
580 .backtrace = hdr->backtrace,
581 .bytes = hdr->bytes,
582 .count = 1,
583 );
584 entries->put(entries, hdr->backtrace, entry);
585 }
eb4f4551 586 leaks++;
fce3b5c3 587 }
17211b6b 588 lock->unlock(lock);
fce3b5c3
MW
589 enumerator = entries->create_enumerator(entries);
590 while (enumerator->enumerate(enumerator, NULL, &entry))
591 {
01e15ab5 592 if (out && (!thresh || entry->bytes >= thresh))
fce3b5c3 593 {
eb4f4551 594 fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
fce3b5c3 595 entry->bytes, entry->count, entry->bytes / entry->count);
eb4f4551 596 entry->backtrace->log(entry->backtrace, out, detailed);
fce3b5c3
MW
597 }
598 free(entry);
599 }
600 enumerator->destroy(enumerator);
601 entries->destroy(entries);
602
17211b6b 603 enable_thread(before);
eb4f4551
MW
604 return leaks;
605}
606
607METHOD(leak_detective_t, report, void,
608 private_leak_detective_t *this, bool detailed)
609{
610 if (lib->leak_detective)
611 {
01e15ab5 612 int leaks, whitelisted = 0;
eb4f4551
MW
613
614 leaks = print_traces(this, stderr, 0, detailed, &whitelisted);
615 switch (leaks)
616 {
617 case 0:
618 fprintf(stderr, "No leaks detected");
619 break;
620 case 1:
621 fprintf(stderr, "One leak detected");
622 break;
623 default:
624 fprintf(stderr, "%d leaks detected", leaks);
625 break;
626 }
627 fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
628 }
629 else
630 {
631 fprintf(stderr, "Leak detective disabled\n");
632 }
633}
634
01e15ab5
TB
635METHOD(leak_detective_t, leaks, int,
636 private_leak_detective_t *this)
637{
638 if (lib->leak_detective)
639 {
640 int leaks, whitelisted = 0;
641
642 leaks = print_traces(this, NULL, 0, FALSE, &whitelisted);
643 return leaks;
644 }
645 return 0;
646}
647
b46776ae
MW
648METHOD(leak_detective_t, set_state, bool,
649 private_leak_detective_t *this, bool enable)
650{
17211b6b 651 if (enable == enabled)
b46776ae 652 {
17211b6b 653 return enabled;
b46776ae 654 }
b46776ae
MW
655 if (enable)
656 {
17211b6b 657 enable_leak_detective();
b46776ae
MW
658 }
659 else
660 {
17211b6b 661 disable_leak_detective();
b46776ae 662 }
17211b6b 663 return !enabled;
b46776ae
MW
664}
665
eb4f4551
MW
666METHOD(leak_detective_t, usage, void,
667 private_leak_detective_t *this, FILE *out)
668{
eb4f4551 669 bool detailed;
17211b6b 670 int thresh;
eb4f4551
MW
671
672 thresh = lib->settings->get_int(lib->settings,
673 "libstrongswan.leak_detective.usage_threshold", 10240);
674 detailed = lib->settings->get_bool(lib->settings,
675 "libstrongswan.leak_detective.detailed", TRUE);
676
eb4f4551 677 print_traces(this, out, thresh, detailed, NULL);
fce3b5c3
MW
678}
679
5113680f 680/**
17211b6b 681 * Wrapped malloc() function
5113680f 682 */
d8f6f0c0 683HOOK(void*, malloc, size_t bytes)
5113680f
MW
684{
685 memory_header_t *hdr;
b7ef3f62 686 memory_tail_t *tail;
17211b6b 687 bool before;
7daf5226 688
17211b6b
MW
689 if (!enabled || thread_disabled->get(thread_disabled))
690 {
691 return real_malloc(bytes);
692 }
7daf5226 693
17211b6b 694 hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
b7ef3f62 695 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
986d23bd 696 /* set to something which causes crashes */
b7ef3f62
MW
697 memset(hdr, MEMORY_ALLOC_PATTERN,
698 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
7daf5226 699
17211b6b
MW
700 before = enable_thread(FALSE);
701 hdr->backtrace = backtrace_create(2);
702 enable_thread(before);
703
5113680f
MW
704 hdr->magic = MEMORY_HEADER_MAGIC;
705 hdr->bytes = bytes;
b7ef3f62 706 tail->magic = MEMORY_TAIL_MAGIC;
7daf5226 707
5113680f 708 /* insert at the beginning of the list */
17211b6b 709 lock->lock(lock);
5113680f
MW
710 hdr->next = first_header.next;
711 if (hdr->next)
712 {
713 hdr->next->previous = hdr;
714 }
715 hdr->previous = &first_header;
716 first_header.next = hdr;
17211b6b 717 lock->unlock(lock);
7daf5226 718
5113680f
MW
719 return hdr + 1;
720}
721
722/**
17211b6b 723 * Wrapped calloc() function
5113680f 724 */
d8f6f0c0 725HOOK(void*, calloc, size_t nmemb, size_t size)
17211b6b
MW
726{
727 void *ptr;
728
729 size *= nmemb;
730 ptr = malloc(size);
731 memset(ptr, 0, size);
732
733 return ptr;
734}
735
d8f6f0c0
MW
736/**
737 * Wrapped valloc(), TODO: currently not supported
738 */
739HOOK(void*, valloc, size_t size)
740{
741 DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
742 return NULL;
743}
744
17211b6b
MW
745/**
746 * Wrapped free() function
747 */
d8f6f0c0 748HOOK(void, free, void *ptr)
5113680f 749{
587ebae7 750 memory_header_t *hdr, *current;
b7ef3f62 751 memory_tail_t *tail;
323f9f99 752 backtrace_t *backtrace;
17211b6b 753 bool found = FALSE, before;
7daf5226 754
17211b6b
MW
755 if (!enabled || thread_disabled->get(thread_disabled))
756 {
757 real_free(ptr);
758 return;
759 }
5113680f
MW
760 /* allow freeing of NULL */
761 if (ptr == NULL)
762 {
763 return;
764 }
b7ef3f62
MW
765 hdr = ptr - sizeof(memory_header_t);
766 tail = ptr + hdr->bytes;
7daf5226 767
17211b6b 768 before = enable_thread(FALSE);
6c45e622
MW
769 if (hdr->magic != MEMORY_HEADER_MAGIC ||
770 tail->magic != MEMORY_TAIL_MAGIC)
5113680f 771 {
17211b6b 772 lock->lock(lock);
587ebae7
MW
773 for (current = &first_header; current != NULL; current = current->next)
774 {
775 if (current == hdr)
776 {
777 found = TRUE;
778 break;
779 }
780 }
17211b6b 781 lock->unlock(lock);
587ebae7
MW
782 if (found)
783 {
784 /* memory was allocated by our hooks but is corrupted */
785 fprintf(stderr, "freeing corrupted memory (%p): "
786 "header magic 0x%x, tail magic 0x%x:\n",
787 ptr, hdr->magic, tail->magic);
788 }
789 else
790 {
791 /* memory was not allocated by our hooks */
17211b6b 792 fprintf(stderr, "freeing invalid memory (%p)\n", ptr);
587ebae7 793 }
549eba30 794 backtrace = backtrace_create(2);
091d1780 795 backtrace->log(backtrace, stderr, TRUE);
f7237cf3 796 backtrace->destroy(backtrace);
5113680f 797 }
6c45e622 798 else
b7ef3f62 799 {
6c45e622 800 /* remove item from list */
17211b6b 801 lock->lock(lock);
6c45e622
MW
802 if (hdr->next)
803 {
804 hdr->next->previous = hdr->previous;
805 }
806 hdr->previous->next = hdr->next;
17211b6b
MW
807 lock->unlock(lock);
808
f7237cf3 809 hdr->backtrace->destroy(hdr->backtrace);
7daf5226 810
6c45e622 811 /* clear MAGIC, set mem to something remarkable */
587ebae7
MW
812 memset(hdr, MEMORY_FREE_PATTERN,
813 sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
7daf5226 814
17211b6b 815 real_free(hdr);
6c45e622 816 }
17211b6b 817 enable_thread(before);
5113680f
MW
818}
819
820/**
17211b6b 821 * Wrapped realloc() function
5113680f 822 */
d8f6f0c0 823HOOK(void*, realloc, void *old, size_t bytes)
5113680f 824{
a401efd0 825 memory_header_t *hdr;
b7ef3f62 826 memory_tail_t *tail;
f7237cf3 827 backtrace_t *backtrace;
17211b6b 828 bool before;
7daf5226 829
17211b6b
MW
830 if (!enabled || thread_disabled->get(thread_disabled))
831 {
832 return real_realloc(old, bytes);
833 }
5113680f
MW
834 /* allow reallocation of NULL */
835 if (old == NULL)
836 {
17211b6b 837 return malloc(bytes);
5113680f 838 }
7daf5226 839
a401efd0 840 hdr = old - sizeof(memory_header_t);
b7ef3f62 841 tail = old + hdr->bytes;
7daf5226 842
6c45e622
MW
843 if (hdr->magic != MEMORY_HEADER_MAGIC ||
844 tail->magic != MEMORY_TAIL_MAGIC)
b7ef3f62 845 {
6499354e
MW
846 fprintf(stderr, "reallocating invalid memory (%p):\n"
847 "header magic 0x%x:\n", old, hdr->magic);
549eba30 848 backtrace = backtrace_create(2);
091d1780 849 backtrace->log(backtrace, stderr, TRUE);
f7237cf3 850 backtrace->destroy(backtrace);
b7ef3f62 851 }
6499354e
MW
852 else
853 {
854 /* clear tail magic, allocate, set tail magic */
855 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
856 }
17211b6b
MW
857 hdr = real_realloc(hdr,
858 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
b7ef3f62
MW
859 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
860 tail->magic = MEMORY_TAIL_MAGIC;
6c45e622 861
a401efd0
MW
862 /* update statistics */
863 hdr->bytes = bytes;
17211b6b
MW
864
865 before = enable_thread(FALSE);
f7237cf3 866 hdr->backtrace->destroy(hdr->backtrace);
549eba30 867 hdr->backtrace = backtrace_create(2);
17211b6b 868 enable_thread(before);
6c45e622 869
a401efd0 870 /* update header of linked list neighbours */
17211b6b 871 lock->lock(lock);
a401efd0
MW
872 if (hdr->next)
873 {
874 hdr->next->previous = hdr;
875 }
876 hdr->previous->next = hdr;
17211b6b
MW
877 lock->unlock(lock);
878
a401efd0 879 return hdr + 1;
5113680f
MW
880}
881
42e0f26e
MW
882METHOD(leak_detective_t, destroy, void,
883 private_leak_detective_t *this)
5113680f 884{
17211b6b
MW
885 disable_leak_detective();
886 lock->destroy(lock);
887 thread_disabled->destroy(thread_disabled);
552cc11b 888 free(this);
6b3292da
MW
889}
890
552cc11b
MW
891/*
892 * see header file
73760ca5 893 */
552cc11b 894leak_detective_t *leak_detective_create()
73760ca5 895{
42e0f26e
MW
896 private_leak_detective_t *this;
897
898 INIT(this,
899 .public = {
900 .report = _report,
01e15ab5 901 .leaks = _leaks,
fce3b5c3 902 .usage = _usage,
b46776ae 903 .set_state = _set_state,
42e0f26e
MW
904 .destroy = _destroy,
905 },
906 );
7daf5226 907
17211b6b
MW
908 lock = spinlock_create();
909 thread_disabled = thread_value_create(NULL);
910
7e3f6299
MW
911 init_static_allocations();
912
552cc11b 913 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
73760ca5 914 {
d8f6f0c0
MW
915 if (register_hooks())
916 {
917 enable_leak_detective();
918 }
73760ca5 919 }
552cc11b 920 return &this->public;
73760ca5 921}