]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/utils/leak_detective.c
Moved debug.[ch] to utils folder
[thirdparty/strongswan.git] / src / libstrongswan / utils / leak_detective.c
CommitLineData
5113680f 1/*
552cc11b 2 * Copyright (C) 2006-2008 Martin Willi
5113680f
MW
3 * Hochschule fuer Technik Rapperswil
4 *
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>.
9 *
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
13 * for more details.
14 */
7daf5226 15
7a485e90
MW
16#define _GNU_SOURCE
17#include <sched.h>
5113680f 18#include <stddef.h>
5113680f
MW
19#include <string.h>
20#include <stdio.h>
21#include <malloc.h>
7daf5226 22#include <signal.h>
5113680f
MW
23#include <sys/socket.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
1d025fbc 26#include <unistd.h>
8bc96e08 27#include <syslog.h>
8bc96e08 28#include <pthread.h>
b1904247 29#include <netdb.h>
552cc11b 30#include <locale.h>
5113680f
MW
31
32#include "leak_detective.h"
33
60356f33 34#include <library.h>
f05b4272 35#include <utils/debug.h>
f7237cf3 36#include <utils/backtrace.h>
12642a68 37#include <collections/hashtable.h>
5113680f 38
552cc11b
MW
39typedef struct private_leak_detective_t private_leak_detective_t;
40
41/**
42 * private data of leak_detective
43 */
44struct private_leak_detective_t {
45
46 /**
47 * public functions
48 */
49 leak_detective_t public;
50};
5113680f
MW
51
52/**
269f7f44 53 * Magic value which helps to detect memory corruption. Yummy!
5113680f 54 */
269f7f44
MW
55#define MEMORY_HEADER_MAGIC 0x7ac0be11
56
b7ef3f62
MW
57/**
58 * Magic written to tail of allocation
59 */
60#define MEMORY_TAIL_MAGIC 0xcafebabe
61
269f7f44
MW
62/**
63 * Pattern which is filled in memory before freeing it
64 */
65#define MEMORY_FREE_PATTERN 0xFF
66
67/**
68 * Pattern which is filled in newly allocated memory
69 */
70#define MEMORY_ALLOC_PATTERN 0xEE
71
5113680f 72
5113680f
MW
73static void install_hooks(void);
74static void uninstall_hooks(void);
75static void *malloc_hook(size_t, const void *);
76static void *realloc_hook(void *, size_t, const void *);
77static void free_hook(void*, const void *);
73760ca5 78
cce97b64
MW
79void *(*old_malloc_hook)(size_t, const void *);
80void *(*old_realloc_hook)(void *, size_t, const void *);
81void (*old_free_hook)(void*, const void *);
82
73760ca5
MW
83static u_int count_malloc = 0;
84static u_int count_free = 0;
85static u_int count_realloc = 0;
5113680f
MW
86
87typedef struct memory_header_t memory_header_t;
b7ef3f62 88typedef struct memory_tail_t memory_tail_t;
5113680f
MW
89
90/**
91 * Header which is prepended to each allocated memory block
92 */
93struct memory_header_t {
7daf5226 94
5113680f
MW
95 /**
96 * Pointer to previous entry in linked list
97 */
98 memory_header_t *previous;
7daf5226 99
5113680f
MW
100 /**
101 * Pointer to next entry in linked list
102 */
103 memory_header_t *next;
7daf5226 104
f7237cf3
MW
105 /**
106 * backtrace taken during (re-)allocation
107 */
108 backtrace_t *backtrace;
7daf5226 109
35c93479
MW
110 /**
111 * Number of bytes following after the header
112 */
113 u_int32_t bytes;
114
b7ef3f62
MW
115 /**
116 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
117 */
118 u_int32_t magic;
7daf5226 119
b7ef3f62
MW
120}__attribute__((__packed__));
121
122/**
123 * tail appended to each allocated memory block
124 */
125struct memory_tail_t {
126
127 /**
128 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
129 */
130 u_int32_t magic;
7daf5226 131
b7ef3f62 132}__attribute__((__packed__));
5113680f
MW
133
134/**
7daf5226 135 * first mem header is just a dummy to chain
5113680f
MW
136 * the others on it...
137 */
6b3292da 138static memory_header_t first_header = {
5113680f
MW
139 magic: MEMORY_HEADER_MAGIC,
140 bytes: 0,
f7237cf3 141 backtrace: NULL,
5113680f
MW
142 previous: NULL,
143 next: NULL
144};
145
1d025fbc 146/**
7daf5226 147 * are the hooks currently installed?
1d025fbc 148 */
6b3292da 149static bool installed = FALSE;
5113680f 150
eb4f4551
MW
151/**
152 * Installs the malloc hooks, enables leak detection
153 */
154static void install_hooks()
155{
156 if (!installed)
157 {
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;
164 installed = TRUE;
165 }
166}
167
168/**
169 * Uninstalls the malloc hooks, disables leak detection
170 */
171static void uninstall_hooks()
172{
173 if (installed)
174 {
175 __malloc_hook = old_malloc_hook;
176 __free_hook = old_free_hook;
177 __realloc_hook = old_realloc_hook;
178 installed = FALSE;
179 }
180}
181
986d23bd 182/**
552cc11b 183 * Leak report white list
151168f6 184 *
552cc11b 185 * List of functions using static allocation buffers or should be suppressed
7daf5226 186 * otherwise on leak report.
986d23bd 187 */
552cc11b 188char *whitelist[] = {
f7237cf3
MW
189 /* backtraces, including own */
190 "backtrace_create",
7c8b9fcb 191 "safe_strerror",
7939864d 192 /* pthread stuff */
552cc11b
MW
193 "pthread_create",
194 "pthread_setspecific",
9eb85cff 195 "__pthread_setspecific",
7939864d 196 /* glibc functions */
552cc11b 197 "mktime",
4002b41b 198 "ctime",
e69f33f6 199 "__gmtime_r",
c198fc55 200 "localtime_r",
552cc11b 201 "tzset",
07bda3fe 202 "time_printf_hook",
552cc11b
MW
203 "inet_ntoa",
204 "strerror",
0ffedbfb 205 "getprotobyname",
552cc11b
MW
206 "getprotobynumber",
207 "getservbyport",
208 "getservbyname",
b97ca14d 209 "gethostbyname",
3f9ec06f 210 "gethostbyname2",
5a22a021
MW
211 "gethostbyname_r",
212 "gethostbyname2_r",
3f9ec06f 213 "getnetbyname",
25b12c69
MW
214 "getpwnam_r",
215 "getgrnam_r",
552cc11b 216 "register_printf_function",
86813bef 217 "register_printf_specifier",
552cc11b
MW
218 "syslog",
219 "vsyslog",
044e0dd1
MW
220 "__syslog_chk",
221 "__vsyslog_chk",
552cc11b
MW
222 "getaddrinfo",
223 "setlocale",
70789d28 224 "getpass",
b97ca14d
AS
225 "getpwent_r",
226 "setpwent",
227 "endpwent",
0bac49b0 228 "getspnam_r",
7939864d
MW
229 /* ignore dlopen, as we do not dlclose to get proper leak reports */
230 "dlopen",
a3d92a37 231 "dlerror",
1caa265c 232 "dlclose",
07bda3fe 233 "dlsym",
7939864d 234 /* mysql functions */
552cc11b
MW
235 "mysql_init_character_set",
236 "init_client_errs",
237 "my_thread_init",
7939864d 238 /* fastcgi library */
cf4caefa 239 "FCGX_Init",
7939864d
MW
240 /* libxml */
241 "xmlInitCharEncodingHandlers",
242 "xmlInitParser",
243 "xmlInitParserCtxt",
727b0f11
AS
244 /* libcurl */
245 "Curl_client_write",
7939864d
MW
246 /* ClearSilver */
247 "nerr_init",
915e04b2
MW
248 /* OpenSSL */
249 "RSA_new_method",
250 "DH_new_method",
fe5d7c43 251 "ENGINE_load_builtin_engines",
04425625 252 "OPENSSL_config",
85fd609e 253 "ecdsa_check",
40b2be16 254 "ERR_put_error",
513a1a28
MW
255 /* libgcrypt */
256 "gcry_control",
257 "gcry_check_version",
a41d0932
MW
258 "gcry_randomize",
259 "gcry_create_nonce",
50a9e845
MW
260 /* NSPR */
261 "PR_CallOnce",
1888dd6b
AS
262 /* libapr */
263 "apr_pool_create_ex",
ec8426a3
MW
264 /* glib */
265 "g_type_init_with_debug_flags",
266 "g_type_register_static",
267 "g_type_class_ref",
268 "g_type_create_instance",
269 "g_type_add_interface_static",
270 "g_type_interface_add_prerequisite",
271 "g_socket_connection_factory_lookup_type",
272 /* libgpg */
273 "gpg_err_init",
7cfa84f5
MW
274 /* gnutls */
275 "gnutls_global_init",
986d23bd
MW
276};
277
5113680f 278
fce3b5c3
MW
279/**
280 * Hashtable hash function
281 */
282static u_int hash(backtrace_t *key)
283{
284 enumerator_t *enumerator;
285 void *addr;
286 u_int hash = 0;
287
288 enumerator = key->create_frame_enumerator(key);
289 while (enumerator->enumerate(enumerator, &addr))
290 {
291 hash = chunk_hash_inc(chunk_from_thing(addr), hash);
292 }
293 enumerator->destroy(enumerator);
294
295 return hash;
296}
297
298/**
299 * Hashtable equals function
300 */
301static bool equals(backtrace_t *a, backtrace_t *b)
302{
303 return a->equals(a, b);
304}
305
eb4f4551
MW
306/**
307 * Summarize and print backtraces
308 */
309static int print_traces(private_leak_detective_t *this,
310 FILE *out, int thresh, bool detailed, int *whitelisted)
fce3b5c3 311{
eb4f4551 312 int leaks = 0;
fce3b5c3
MW
313 memory_header_t *hdr;
314 enumerator_t *enumerator;
315 hashtable_t *entries;
316 struct {
317 /** associated backtrace */
318 backtrace_t *backtrace;
319 /** total size of all allocations */
320 size_t bytes;
321 /** number of allocations */
322 u_int count;
323 } *entry;
324
fce3b5c3
MW
325 uninstall_hooks();
326
327 entries = hashtable_create((hashtable_hash_t)hash,
328 (hashtable_equals_t)equals, 1024);
fce3b5c3
MW
329 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
330 {
eb4f4551
MW
331 if (whitelisted &&
332 hdr->backtrace->contains_function(hdr->backtrace,
333 whitelist, countof(whitelist)))
334 {
335 (*whitelisted)++;
336 continue;
337 }
fce3b5c3
MW
338 entry = entries->get(entries, hdr->backtrace);
339 if (entry)
340 {
341 entry->bytes += hdr->bytes;
342 entry->count++;
343 }
344 else
345 {
346 INIT(entry,
347 .backtrace = hdr->backtrace,
348 .bytes = hdr->bytes,
349 .count = 1,
350 );
351 entries->put(entries, hdr->backtrace, entry);
352 }
eb4f4551 353 leaks++;
fce3b5c3
MW
354 }
355 enumerator = entries->create_enumerator(entries);
356 while (enumerator->enumerate(enumerator, NULL, &entry))
357 {
eb4f4551 358 if (!thresh || entry->bytes >= thresh)
fce3b5c3 359 {
eb4f4551 360 fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
fce3b5c3 361 entry->bytes, entry->count, entry->bytes / entry->count);
eb4f4551 362 entry->backtrace->log(entry->backtrace, out, detailed);
fce3b5c3
MW
363 }
364 free(entry);
365 }
366 enumerator->destroy(enumerator);
367 entries->destroy(entries);
368
369 install_hooks();
eb4f4551
MW
370 return leaks;
371}
372
373METHOD(leak_detective_t, report, void,
374 private_leak_detective_t *this, bool detailed)
375{
376 if (lib->leak_detective)
377 {
378 int leaks = 0, whitelisted = 0;
379
380 leaks = print_traces(this, stderr, 0, detailed, &whitelisted);
381 switch (leaks)
382 {
383 case 0:
384 fprintf(stderr, "No leaks detected");
385 break;
386 case 1:
387 fprintf(stderr, "One leak detected");
388 break;
389 default:
390 fprintf(stderr, "%d leaks detected", leaks);
391 break;
392 }
393 fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
394 }
395 else
396 {
397 fprintf(stderr, "Leak detective disabled\n");
398 }
399}
400
b46776ae
MW
401METHOD(leak_detective_t, set_state, bool,
402 private_leak_detective_t *this, bool enable)
403{
404 static struct sched_param oldparams;
405 static int oldpolicy;
406 struct sched_param params;
407 pthread_t thread_id;
408
409 if (enable == installed)
410 {
411 return installed;
412 }
413 thread_id = pthread_self();
414 if (enable)
415 {
416 install_hooks();
417 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
418 }
419 else
420 {
421 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
422 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
423 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
424 uninstall_hooks();
425 }
426 installed = enable;
427 return !installed;
428}
429
eb4f4551
MW
430METHOD(leak_detective_t, usage, void,
431 private_leak_detective_t *this, FILE *out)
432{
433 int oldpolicy, thresh;
434 bool detailed;
435 pthread_t thread_id = pthread_self();
436 struct sched_param oldparams, params;
437
438 thresh = lib->settings->get_int(lib->settings,
439 "libstrongswan.leak_detective.usage_threshold", 10240);
440 detailed = lib->settings->get_bool(lib->settings,
441 "libstrongswan.leak_detective.detailed", TRUE);
442
443 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
444 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
445 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
446
447 print_traces(this, out, thresh, detailed, NULL);
448
fce3b5c3
MW
449 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
450}
451
5113680f
MW
452/**
453 * Hook function for malloc()
454 */
6b3292da 455void *malloc_hook(size_t bytes, const void *caller)
5113680f
MW
456{
457 memory_header_t *hdr;
b7ef3f62 458 memory_tail_t *tail;
84b18d5f 459 pthread_t thread_id = pthread_self();
323f9f99
MW
460 int oldpolicy;
461 struct sched_param oldparams, params;
7daf5226 462
323f9f99 463 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
7daf5226 464
323f9f99 465 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
84b18d5f 466 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
7daf5226 467
73760ca5 468 count_malloc++;
5113680f 469 uninstall_hooks();
b7ef3f62
MW
470 hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
471 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
986d23bd 472 /* set to something which causes crashes */
b7ef3f62
MW
473 memset(hdr, MEMORY_ALLOC_PATTERN,
474 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
7daf5226 475
5113680f
MW
476 hdr->magic = MEMORY_HEADER_MAGIC;
477 hdr->bytes = bytes;
549eba30 478 hdr->backtrace = backtrace_create(2);
b7ef3f62 479 tail->magic = MEMORY_TAIL_MAGIC;
269f7f44 480 install_hooks();
7daf5226 481
5113680f
MW
482 /* insert at the beginning of the list */
483 hdr->next = first_header.next;
484 if (hdr->next)
485 {
486 hdr->next->previous = hdr;
487 }
488 hdr->previous = &first_header;
489 first_header.next = hdr;
7daf5226 490
84b18d5f 491 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
7daf5226 492
5113680f
MW
493 return hdr + 1;
494}
495
496/**
497 * Hook function for free()
498 */
6b3292da 499void free_hook(void *ptr, const void *caller)
5113680f 500{
587ebae7 501 memory_header_t *hdr, *current;
b7ef3f62 502 memory_tail_t *tail;
323f9f99 503 backtrace_t *backtrace;
84b18d5f 504 pthread_t thread_id = pthread_self();
323f9f99
MW
505 int oldpolicy;
506 struct sched_param oldparams, params;
587ebae7 507 bool found = FALSE;
7daf5226 508
5113680f
MW
509 /* allow freeing of NULL */
510 if (ptr == NULL)
511 {
512 return;
513 }
b7ef3f62
MW
514 hdr = ptr - sizeof(memory_header_t);
515 tail = ptr + hdr->bytes;
7daf5226 516
84b18d5f 517 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
7daf5226 518
323f9f99 519 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
84b18d5f 520 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
7daf5226 521
73760ca5 522 count_free++;
269f7f44 523 uninstall_hooks();
6c45e622
MW
524 if (hdr->magic != MEMORY_HEADER_MAGIC ||
525 tail->magic != MEMORY_TAIL_MAGIC)
5113680f 526 {
587ebae7
MW
527 for (current = &first_header; current != NULL; current = current->next)
528 {
529 if (current == hdr)
530 {
531 found = TRUE;
532 break;
533 }
534 }
535 if (found)
536 {
537 /* memory was allocated by our hooks but is corrupted */
538 fprintf(stderr, "freeing corrupted memory (%p): "
539 "header magic 0x%x, tail magic 0x%x:\n",
540 ptr, hdr->magic, tail->magic);
541 }
542 else
543 {
544 /* memory was not allocated by our hooks */
545 fprintf(stderr, "freeing invalid memory (%p)", ptr);
546 }
549eba30 547 backtrace = backtrace_create(2);
091d1780 548 backtrace->log(backtrace, stderr, TRUE);
f7237cf3 549 backtrace->destroy(backtrace);
5113680f 550 }
6c45e622 551 else
b7ef3f62 552 {
6c45e622
MW
553 /* remove item from list */
554 if (hdr->next)
555 {
556 hdr->next->previous = hdr->previous;
557 }
558 hdr->previous->next = hdr->next;
f7237cf3 559 hdr->backtrace->destroy(hdr->backtrace);
7daf5226 560
6c45e622 561 /* clear MAGIC, set mem to something remarkable */
587ebae7
MW
562 memset(hdr, MEMORY_FREE_PATTERN,
563 sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
7daf5226 564
6c45e622
MW
565 free(hdr);
566 }
7daf5226 567
5113680f 568 install_hooks();
6c90949f 569 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
5113680f
MW
570}
571
572/**
573 * Hook function for realloc()
574 */
6b3292da 575void *realloc_hook(void *old, size_t bytes, const void *caller)
5113680f 576{
a401efd0 577 memory_header_t *hdr;
b7ef3f62 578 memory_tail_t *tail;
f7237cf3 579 backtrace_t *backtrace;
84b18d5f 580 pthread_t thread_id = pthread_self();
323f9f99
MW
581 int oldpolicy;
582 struct sched_param oldparams, params;
7daf5226 583
5113680f
MW
584 /* allow reallocation of NULL */
585 if (old == NULL)
586 {
587 return malloc_hook(bytes, caller);
588 }
7daf5226 589
a401efd0 590 hdr = old - sizeof(memory_header_t);
b7ef3f62 591 tail = old + hdr->bytes;
7daf5226 592
84b18d5f 593 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
7daf5226 594
84b18d5f
TB
595 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
596 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
7daf5226 597
73760ca5 598 count_realloc++;
a401efd0 599 uninstall_hooks();
6c45e622
MW
600 if (hdr->magic != MEMORY_HEADER_MAGIC ||
601 tail->magic != MEMORY_TAIL_MAGIC)
b7ef3f62 602 {
6499354e
MW
603 fprintf(stderr, "reallocating invalid memory (%p):\n"
604 "header magic 0x%x:\n", old, hdr->magic);
549eba30 605 backtrace = backtrace_create(2);
091d1780 606 backtrace->log(backtrace, stderr, TRUE);
f7237cf3 607 backtrace->destroy(backtrace);
b7ef3f62 608 }
6499354e
MW
609 else
610 {
611 /* clear tail magic, allocate, set tail magic */
612 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
613 }
b7ef3f62
MW
614 hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
615 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
616 tail->magic = MEMORY_TAIL_MAGIC;
6c45e622 617
a401efd0
MW
618 /* update statistics */
619 hdr->bytes = bytes;
f7237cf3 620 hdr->backtrace->destroy(hdr->backtrace);
549eba30 621 hdr->backtrace = backtrace_create(2);
6c45e622 622
a401efd0
MW
623 /* update header of linked list neighbours */
624 if (hdr->next)
625 {
626 hdr->next->previous = hdr;
627 }
628 hdr->previous->next = hdr;
629 install_hooks();
84b18d5f 630 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
a401efd0 631 return hdr + 1;
5113680f
MW
632}
633
42e0f26e
MW
634METHOD(leak_detective_t, destroy, void,
635 private_leak_detective_t *this)
5113680f 636{
552cc11b 637 if (installed)
9cc7a297
MW
638 {
639 uninstall_hooks();
9cc7a297 640 }
552cc11b 641 free(this);
6b3292da
MW
642}
643
552cc11b
MW
644/*
645 * see header file
73760ca5 646 */
552cc11b 647leak_detective_t *leak_detective_create()
73760ca5 648{
42e0f26e
MW
649 private_leak_detective_t *this;
650
651 INIT(this,
652 .public = {
653 .report = _report,
fce3b5c3 654 .usage = _usage,
b46776ae 655 .set_state = _set_state,
42e0f26e
MW
656 .destroy = _destroy,
657 },
658 );
7daf5226 659
552cc11b 660 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
73760ca5 661 {
7a485e90 662 cpu_set_t mask;
7daf5226 663
7a485e90
MW
664 CPU_ZERO(&mask);
665 CPU_SET(0, &mask);
7daf5226 666
7a485e90
MW
667 if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0)
668 {
669 fprintf(stderr, "setting CPU affinity failed: %m");
670 }
7daf5226 671
552cc11b 672 install_hooks();
73760ca5 673 }
552cc11b 674 return &this->public;
73760ca5
MW
675}
676