2 * Copyright (C) 2006-2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
32 #include "leak_detective.h"
36 #include <utils/backtrace.h>
37 #include <utils/hashtable.h>
39 typedef struct private_leak_detective_t private_leak_detective_t
;
42 * private data of leak_detective
44 struct private_leak_detective_t
{
49 leak_detective_t
public;
53 * Magic value which helps to detect memory corruption. Yummy!
55 #define MEMORY_HEADER_MAGIC 0x7ac0be11
58 * Magic written to tail of allocation
60 #define MEMORY_TAIL_MAGIC 0xcafebabe
63 * Pattern which is filled in memory before freeing it
65 #define MEMORY_FREE_PATTERN 0xFF
68 * Pattern which is filled in newly allocated memory
70 #define MEMORY_ALLOC_PATTERN 0xEE
73 static void install_hooks(void);
74 static void uninstall_hooks(void);
75 static void *malloc_hook(size_t, const void *);
76 static void *realloc_hook(void *, size_t, const void *);
77 static void free_hook(void*, const void *);
79 void *(*old_malloc_hook
)(size_t, const void *);
80 void *(*old_realloc_hook
)(void *, size_t, const void *);
81 void (*old_free_hook
)(void*, const void *);
83 static u_int count_malloc
= 0;
84 static u_int count_free
= 0;
85 static u_int count_realloc
= 0;
87 typedef struct memory_header_t memory_header_t
;
88 typedef struct memory_tail_t memory_tail_t
;
91 * Header which is prepended to each allocated memory block
93 struct memory_header_t
{
96 * Pointer to previous entry in linked list
98 memory_header_t
*previous
;
101 * Pointer to next entry in linked list
103 memory_header_t
*next
;
106 * backtrace taken during (re-)allocation
108 backtrace_t
*backtrace
;
111 * Number of bytes following after the header
116 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
120 }__attribute__((__packed__
));
123 * tail appended to each allocated memory block
125 struct memory_tail_t
{
128 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
132 }__attribute__((__packed__
));
135 * first mem header is just a dummy to chain
136 * the others on it...
138 static memory_header_t first_header
= {
139 magic
: MEMORY_HEADER_MAGIC
,
147 * are the hooks currently installed?
149 static bool installed
= FALSE
;
152 * Installs the malloc hooks, enables leak detection
154 static void install_hooks()
158 old_malloc_hook
= __malloc_hook
;
159 old_realloc_hook
= __realloc_hook
;
160 old_free_hook
= __free_hook
;
161 __malloc_hook
= malloc_hook
;
162 __realloc_hook
= realloc_hook
;
163 __free_hook
= free_hook
;
169 * Uninstalls the malloc hooks, disables leak detection
171 static void uninstall_hooks()
175 __malloc_hook
= old_malloc_hook
;
176 __free_hook
= old_free_hook
;
177 __realloc_hook
= old_realloc_hook
;
183 * Leak report white list
185 * List of functions using static allocation buffers or should be suppressed
186 * otherwise on leak report.
188 char *whitelist
[] = {
189 /* backtraces, including own */
193 "pthread_setspecific",
194 "__pthread_setspecific",
195 /* glibc functions */
213 "register_printf_function",
214 "register_printf_specifier",
222 /* ignore dlopen, as we do not dlclose to get proper leak reports */
227 /* mysql functions */
228 "mysql_init_character_set",
231 /* fastcgi library */
234 "xmlInitCharEncodingHandlers",
244 "ENGINE_load_builtin_engines",
250 "gcry_check_version",
256 "apr_pool_create_ex",
258 "g_type_init_with_debug_flags",
259 "g_type_register_static",
261 "g_type_create_instance",
262 "g_type_add_interface_static",
263 "g_type_interface_add_prerequisite",
264 "g_socket_connection_factory_lookup_type",
268 "gnutls_global_init",
270 "Tspi_Context_Create",
271 "Tspi_Context_Connect",
276 * Hashtable hash function
278 static u_int
hash(backtrace_t
*key
)
280 enumerator_t
*enumerator
;
284 enumerator
= key
->create_frame_enumerator(key
);
285 while (enumerator
->enumerate(enumerator
, &addr
))
287 hash
= chunk_hash_inc(chunk_from_thing(addr
), hash
);
289 enumerator
->destroy(enumerator
);
295 * Hashtable equals function
297 static bool equals(backtrace_t
*a
, backtrace_t
*b
)
299 return a
->equals(a
, b
);
303 * Summarize and print backtraces
305 static int print_traces(private_leak_detective_t
*this,
306 FILE *out
, int thresh
, bool detailed
, int *whitelisted
)
309 memory_header_t
*hdr
;
310 enumerator_t
*enumerator
;
311 hashtable_t
*entries
;
313 /** associated backtrace */
314 backtrace_t
*backtrace
;
315 /** total size of all allocations */
317 /** number of allocations */
323 entries
= hashtable_create((hashtable_hash_t
)hash
,
324 (hashtable_equals_t
)equals
, 1024);
325 for (hdr
= first_header
.next
; hdr
!= NULL
; hdr
= hdr
->next
)
328 hdr
->backtrace
->contains_function(hdr
->backtrace
,
329 whitelist
, countof(whitelist
)))
334 entry
= entries
->get(entries
, hdr
->backtrace
);
337 entry
->bytes
+= hdr
->bytes
;
343 .backtrace
= hdr
->backtrace
,
347 entries
->put(entries
, hdr
->backtrace
, entry
);
351 enumerator
= entries
->create_enumerator(entries
);
352 while (enumerator
->enumerate(enumerator
, NULL
, &entry
))
354 if (!thresh
|| entry
->bytes
>= thresh
)
356 fprintf(out
, "%d bytes total, %d allocations, %d bytes average:\n",
357 entry
->bytes
, entry
->count
, entry
->bytes
/ entry
->count
);
358 entry
->backtrace
->log(entry
->backtrace
, out
, detailed
);
362 enumerator
->destroy(enumerator
);
363 entries
->destroy(entries
);
369 METHOD(leak_detective_t
, report
, void,
370 private_leak_detective_t
*this, bool detailed
)
372 if (lib
->leak_detective
)
374 int leaks
= 0, whitelisted
= 0;
376 leaks
= print_traces(this, stderr
, 0, detailed
, &whitelisted
);
380 fprintf(stderr
, "No leaks detected");
383 fprintf(stderr
, "One leak detected");
386 fprintf(stderr
, "%d leaks detected", leaks
);
389 fprintf(stderr
, ", %d suppressed by whitelist\n", whitelisted
);
393 fprintf(stderr
, "Leak detective disabled\n");
397 METHOD(leak_detective_t
, usage
, void,
398 private_leak_detective_t
*this, FILE *out
)
400 int oldpolicy
, thresh
;
402 pthread_t thread_id
= pthread_self();
403 struct sched_param oldparams
, params
;
405 thresh
= lib
->settings
->get_int(lib
->settings
,
406 "libstrongswan.leak_detective.usage_threshold", 10240);
407 detailed
= lib
->settings
->get_bool(lib
->settings
,
408 "libstrongswan.leak_detective.detailed", TRUE
);
410 pthread_getschedparam(thread_id
, &oldpolicy
, &oldparams
);
411 params
.__sched_priority
= sched_get_priority_max(SCHED_FIFO
);
412 pthread_setschedparam(thread_id
, SCHED_FIFO
, ¶ms
);
414 print_traces(this, out
, thresh
, detailed
, NULL
);
416 pthread_setschedparam(thread_id
, oldpolicy
, &oldparams
);
420 * Hook function for malloc()
422 void *malloc_hook(size_t bytes
, const void *caller
)
424 memory_header_t
*hdr
;
426 pthread_t thread_id
= pthread_self();
428 struct sched_param oldparams
, params
;
430 pthread_getschedparam(thread_id
, &oldpolicy
, &oldparams
);
432 params
.__sched_priority
= sched_get_priority_max(SCHED_FIFO
);
433 pthread_setschedparam(thread_id
, SCHED_FIFO
, ¶ms
);
437 hdr
= malloc(sizeof(memory_header_t
) + bytes
+ sizeof(memory_tail_t
));
438 tail
= ((void*)hdr
) + bytes
+ sizeof(memory_header_t
);
439 /* set to something which causes crashes */
440 memset(hdr
, MEMORY_ALLOC_PATTERN
,
441 sizeof(memory_header_t
) + bytes
+ sizeof(memory_tail_t
));
443 hdr
->magic
= MEMORY_HEADER_MAGIC
;
445 hdr
->backtrace
= backtrace_create(3);
446 tail
->magic
= MEMORY_TAIL_MAGIC
;
449 /* insert at the beginning of the list */
450 hdr
->next
= first_header
.next
;
453 hdr
->next
->previous
= hdr
;
455 hdr
->previous
= &first_header
;
456 first_header
.next
= hdr
;
458 pthread_setschedparam(thread_id
, oldpolicy
, &oldparams
);
464 * Hook function for free()
466 void free_hook(void *ptr
, const void *caller
)
468 memory_header_t
*hdr
, *current
;
470 backtrace_t
*backtrace
;
471 pthread_t thread_id
= pthread_self();
473 struct sched_param oldparams
, params
;
476 /* allow freeing of NULL */
481 hdr
= ptr
- sizeof(memory_header_t
);
482 tail
= ptr
+ hdr
->bytes
;
484 pthread_getschedparam(thread_id
, &oldpolicy
, &oldparams
);
486 params
.__sched_priority
= sched_get_priority_max(SCHED_FIFO
);
487 pthread_setschedparam(thread_id
, SCHED_FIFO
, ¶ms
);
491 if (hdr
->magic
!= MEMORY_HEADER_MAGIC
||
492 tail
->magic
!= MEMORY_TAIL_MAGIC
)
494 for (current
= &first_header
; current
!= NULL
; current
= current
->next
)
504 /* memory was allocated by our hooks but is corrupted */
505 fprintf(stderr
, "freeing corrupted memory (%p): "
506 "header magic 0x%x, tail magic 0x%x:\n",
507 ptr
, hdr
->magic
, tail
->magic
);
511 /* memory was not allocated by our hooks */
512 fprintf(stderr
, "freeing invalid memory (%p)", ptr
);
514 backtrace
= backtrace_create(3);
515 backtrace
->log(backtrace
, stderr
, TRUE
);
516 backtrace
->destroy(backtrace
);
520 /* remove item from list */
523 hdr
->next
->previous
= hdr
->previous
;
525 hdr
->previous
->next
= hdr
->next
;
526 hdr
->backtrace
->destroy(hdr
->backtrace
);
528 /* clear MAGIC, set mem to something remarkable */
529 memset(hdr
, MEMORY_FREE_PATTERN
,
530 sizeof(memory_header_t
) + hdr
->bytes
+ sizeof(memory_tail_t
));
536 pthread_setschedparam(thread_id
, oldpolicy
, &oldparams
);
540 * Hook function for realloc()
542 void *realloc_hook(void *old
, size_t bytes
, const void *caller
)
544 memory_header_t
*hdr
;
546 backtrace_t
*backtrace
;
547 pthread_t thread_id
= pthread_self();
549 struct sched_param oldparams
, params
;
551 /* allow reallocation of NULL */
554 return malloc_hook(bytes
, caller
);
557 hdr
= old
- sizeof(memory_header_t
);
558 tail
= old
+ hdr
->bytes
;
560 pthread_getschedparam(thread_id
, &oldpolicy
, &oldparams
);
562 params
.__sched_priority
= sched_get_priority_max(SCHED_FIFO
);
563 pthread_setschedparam(thread_id
, SCHED_FIFO
, ¶ms
);
567 if (hdr
->magic
!= MEMORY_HEADER_MAGIC
||
568 tail
->magic
!= MEMORY_TAIL_MAGIC
)
570 fprintf(stderr
, "reallocating invalid memory (%p): "
571 "header magic 0x%x, tail magic 0x%x:\n",
572 old
, hdr
->magic
, tail
->magic
);
573 backtrace
= backtrace_create(3);
574 backtrace
->log(backtrace
, stderr
, TRUE
);
575 backtrace
->destroy(backtrace
);
577 /* clear tail magic, allocate, set tail magic */
578 memset(&tail
->magic
, MEMORY_ALLOC_PATTERN
, sizeof(tail
->magic
));
579 hdr
= realloc(hdr
, sizeof(memory_header_t
) + bytes
+ sizeof(memory_tail_t
));
580 tail
= ((void*)hdr
) + bytes
+ sizeof(memory_header_t
);
581 tail
->magic
= MEMORY_TAIL_MAGIC
;
583 /* update statistics */
585 hdr
->backtrace
->destroy(hdr
->backtrace
);
586 hdr
->backtrace
= backtrace_create(3);
588 /* update header of linked list neighbours */
591 hdr
->next
->previous
= hdr
;
593 hdr
->previous
->next
= hdr
;
595 pthread_setschedparam(thread_id
, oldpolicy
, &oldparams
);
599 METHOD(leak_detective_t
, destroy
, void,
600 private_leak_detective_t
*this)
612 leak_detective_t
*leak_detective_create()
614 private_leak_detective_t
*this;
624 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL
)
631 if (sched_setaffinity(0, sizeof(cpu_set_t
), &mask
) != 0)
633 fprintf(stderr
, "setting CPU affinity failed: %m");
638 return &this->public;