]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libstrongswan/utils/leak_detective.c
Don't use a strongswan.conf in checksum_builder, disables checksumming
[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>
db7ef624 35#include <debug.h>
f7237cf3 36#include <utils/backtrace.h>
fce3b5c3 37#include <utils/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 * Number of bytes following after the header
97 */
b7ef3f62 98 u_int bytes;
7daf5226 99
5113680f
MW
100 /**
101 * Pointer to previous entry in linked list
102 */
103 memory_header_t *previous;
7daf5226 104
5113680f
MW
105 /**
106 * Pointer to next entry in linked list
107 */
108 memory_header_t *next;
7daf5226 109
f7237cf3
MW
110 /**
111 * backtrace taken during (re-)allocation
112 */
113 backtrace_t *backtrace;
7daf5226 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
986d23bd 151/**
552cc11b 152 * Leak report white list
151168f6 153 *
552cc11b 154 * List of functions using static allocation buffers or should be suppressed
7daf5226 155 * otherwise on leak report.
986d23bd 156 */
552cc11b 157char *whitelist[] = {
f7237cf3
MW
158 /* backtraces, including own */
159 "backtrace_create",
7939864d 160 /* pthread stuff */
552cc11b
MW
161 "pthread_create",
162 "pthread_setspecific",
9eb85cff 163 "__pthread_setspecific",
7939864d 164 /* glibc functions */
552cc11b 165 "mktime",
e69f33f6 166 "__gmtime_r",
c198fc55 167 "localtime_r",
552cc11b 168 "tzset",
07bda3fe 169 "time_printf_hook",
552cc11b
MW
170 "inet_ntoa",
171 "strerror",
0ffedbfb 172 "getprotobyname",
552cc11b
MW
173 "getprotobynumber",
174 "getservbyport",
175 "getservbyname",
3f9ec06f 176 "gethostbyname2",
5a22a021
MW
177 "gethostbyname_r",
178 "gethostbyname2_r",
3f9ec06f 179 "getnetbyname",
25b12c69
MW
180 "getpwnam_r",
181 "getgrnam_r",
552cc11b 182 "register_printf_function",
86813bef 183 "register_printf_specifier",
552cc11b
MW
184 "syslog",
185 "vsyslog",
044e0dd1
MW
186 "__syslog_chk",
187 "__vsyslog_chk",
552cc11b
MW
188 "getaddrinfo",
189 "setlocale",
70789d28 190 "getpass",
7939864d
MW
191 /* ignore dlopen, as we do not dlclose to get proper leak reports */
192 "dlopen",
a3d92a37 193 "dlerror",
1caa265c 194 "dlclose",
07bda3fe 195 "dlsym",
7939864d 196 /* mysql functions */
552cc11b
MW
197 "mysql_init_character_set",
198 "init_client_errs",
199 "my_thread_init",
7939864d 200 /* fastcgi library */
cf4caefa 201 "FCGX_Init",
7939864d
MW
202 /* libxml */
203 "xmlInitCharEncodingHandlers",
204 "xmlInitParser",
205 "xmlInitParserCtxt",
727b0f11
AS
206 /* libcurl */
207 "Curl_client_write",
7939864d
MW
208 /* ClearSilver */
209 "nerr_init",
915e04b2
MW
210 /* OpenSSL */
211 "RSA_new_method",
212 "DH_new_method",
fe5d7c43 213 "ENGINE_load_builtin_engines",
04425625 214 "OPENSSL_config",
85fd609e 215 "ecdsa_check",
40b2be16 216 "ERR_put_error",
513a1a28
MW
217 /* libgcrypt */
218 "gcry_control",
219 "gcry_check_version",
a41d0932
MW
220 "gcry_randomize",
221 "gcry_create_nonce",
50a9e845
MW
222 /* NSPR */
223 "PR_CallOnce",
1888dd6b
AS
224 /* libapr */
225 "apr_pool_create_ex",
ec8426a3
MW
226 /* glib */
227 "g_type_init_with_debug_flags",
228 "g_type_register_static",
229 "g_type_class_ref",
230 "g_type_create_instance",
231 "g_type_add_interface_static",
232 "g_type_interface_add_prerequisite",
233 "g_socket_connection_factory_lookup_type",
234 /* libgpg */
235 "gpg_err_init",
7cfa84f5
MW
236 /* gnutls */
237 "gnutls_global_init",
986d23bd
MW
238};
239
42e0f26e
MW
240METHOD(leak_detective_t, report, void,
241 private_leak_detective_t *this, bool detailed)
6b3292da 242{
091d1780 243 if (lib->leak_detective)
6b3292da 244 {
091d1780
MW
245 memory_header_t *hdr;
246 int leaks = 0, whitelisted = 0;
247
248 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
cf4caefa 249 {
b94feb4b
MW
250 if (hdr->backtrace->contains_function(hdr->backtrace,
251 whitelist, countof(whitelist)))
091d1780
MW
252 {
253 whitelisted++;
254 }
255 else
256 {
257 fprintf(stderr, "Leak (%d bytes at %p):\n", hdr->bytes, hdr + 1);
258 /* skip the first frame, contains leak detective logic */
259 hdr->backtrace->log(hdr->backtrace, stderr, detailed);
260 leaks++;
261 }
cf4caefa 262 }
091d1780 263 switch (leaks)
986d23bd 264 {
091d1780
MW
265 case 0:
266 fprintf(stderr, "No leaks detected");
267 break;
268 case 1:
269 fprintf(stderr, "One leak detected");
270 break;
271 default:
272 fprintf(stderr, "%d leaks detected", leaks);
273 break;
986d23bd 274 }
091d1780 275 fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
6b3292da 276 }
091d1780 277 else
6b3292da 278 {
091d1780 279 fprintf(stderr, "Leak detective disabled\n");
6b3292da
MW
280 }
281}
282
5113680f
MW
283/**
284 * Installs the malloc hooks, enables leak detection
285 */
6b3292da 286static void install_hooks()
5113680f 287{
8bc96e08
MW
288 if (!installed)
289 {
290 old_malloc_hook = __malloc_hook;
291 old_realloc_hook = __realloc_hook;
292 old_free_hook = __free_hook;
293 __malloc_hook = malloc_hook;
294 __realloc_hook = realloc_hook;
295 __free_hook = free_hook;
296 installed = TRUE;
297 }
5113680f
MW
298}
299
300/**
301 * Uninstalls the malloc hooks, disables leak detection
302 */
6b3292da 303static void uninstall_hooks()
5113680f 304{
8bc96e08
MW
305 if (installed)
306 {
307 __malloc_hook = old_malloc_hook;
308 __free_hook = old_free_hook;
309 __realloc_hook = old_realloc_hook;
310 installed = FALSE;
311 }
5113680f
MW
312}
313
fce3b5c3
MW
314/**
315 * Hashtable hash function
316 */
317static u_int hash(backtrace_t *key)
318{
319 enumerator_t *enumerator;
320 void *addr;
321 u_int hash = 0;
322
323 enumerator = key->create_frame_enumerator(key);
324 while (enumerator->enumerate(enumerator, &addr))
325 {
326 hash = chunk_hash_inc(chunk_from_thing(addr), hash);
327 }
328 enumerator->destroy(enumerator);
329
330 return hash;
331}
332
333/**
334 * Hashtable equals function
335 */
336static bool equals(backtrace_t *a, backtrace_t *b)
337{
338 return a->equals(a, b);
339}
340
341METHOD(leak_detective_t, usage, void,
342 private_leak_detective_t *this, FILE *out)
343{
344 int oldpolicy, thresh;
345 pthread_t thread_id = pthread_self();
346 struct sched_param oldparams, params;
347 memory_header_t *hdr;
348 enumerator_t *enumerator;
349 hashtable_t *entries;
350 struct {
351 /** associated backtrace */
352 backtrace_t *backtrace;
353 /** total size of all allocations */
354 size_t bytes;
355 /** number of allocations */
356 u_int count;
357 } *entry;
358
359 thresh = lib->settings->get_int(lib->settings,
360 "libstrongswan.leak_detective.usage_threshold", 10240);
361
362 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
363 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
364 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
365 uninstall_hooks();
366
367 entries = hashtable_create((hashtable_hash_t)hash,
368 (hashtable_equals_t)equals, 1024);
369
370 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
371 {
372 entry = entries->get(entries, hdr->backtrace);
373 if (entry)
374 {
375 entry->bytes += hdr->bytes;
376 entry->count++;
377 }
378 else
379 {
380 INIT(entry,
381 .backtrace = hdr->backtrace,
382 .bytes = hdr->bytes,
383 .count = 1,
384 );
385 entries->put(entries, hdr->backtrace, entry);
386 }
387 }
388 enumerator = entries->create_enumerator(entries);
389 while (enumerator->enumerate(enumerator, NULL, &entry))
390 {
391 if (entry->bytes >= thresh)
392 {
393 fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
394 entry->bytes, entry->count, entry->bytes / entry->count);
395 entry->backtrace->log(entry->backtrace, out, TRUE);
396 }
397 free(entry);
398 }
399 enumerator->destroy(enumerator);
400 entries->destroy(entries);
401
402 install_hooks();
403 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
404}
405
5113680f
MW
406/**
407 * Hook function for malloc()
408 */
6b3292da 409void *malloc_hook(size_t bytes, const void *caller)
5113680f
MW
410{
411 memory_header_t *hdr;
b7ef3f62 412 memory_tail_t *tail;
84b18d5f 413 pthread_t thread_id = pthread_self();
323f9f99
MW
414 int oldpolicy;
415 struct sched_param oldparams, params;
7daf5226 416
323f9f99 417 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
7daf5226 418
323f9f99 419 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
84b18d5f 420 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
7daf5226 421
73760ca5 422 count_malloc++;
5113680f 423 uninstall_hooks();
b7ef3f62
MW
424 hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
425 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
986d23bd 426 /* set to something which causes crashes */
b7ef3f62
MW
427 memset(hdr, MEMORY_ALLOC_PATTERN,
428 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
7daf5226 429
5113680f
MW
430 hdr->magic = MEMORY_HEADER_MAGIC;
431 hdr->bytes = bytes;
f7237cf3 432 hdr->backtrace = backtrace_create(3);
b7ef3f62 433 tail->magic = MEMORY_TAIL_MAGIC;
269f7f44 434 install_hooks();
7daf5226 435
5113680f
MW
436 /* insert at the beginning of the list */
437 hdr->next = first_header.next;
438 if (hdr->next)
439 {
440 hdr->next->previous = hdr;
441 }
442 hdr->previous = &first_header;
443 first_header.next = hdr;
7daf5226 444
84b18d5f 445 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
7daf5226 446
5113680f
MW
447 return hdr + 1;
448}
449
450/**
451 * Hook function for free()
452 */
6b3292da 453void free_hook(void *ptr, const void *caller)
5113680f 454{
587ebae7 455 memory_header_t *hdr, *current;
b7ef3f62 456 memory_tail_t *tail;
323f9f99 457 backtrace_t *backtrace;
84b18d5f 458 pthread_t thread_id = pthread_self();
323f9f99
MW
459 int oldpolicy;
460 struct sched_param oldparams, params;
587ebae7 461 bool found = FALSE;
7daf5226 462
5113680f
MW
463 /* allow freeing of NULL */
464 if (ptr == NULL)
465 {
466 return;
467 }
b7ef3f62
MW
468 hdr = ptr - sizeof(memory_header_t);
469 tail = ptr + hdr->bytes;
7daf5226 470
84b18d5f 471 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
7daf5226 472
323f9f99 473 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
84b18d5f 474 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
7daf5226 475
73760ca5 476 count_free++;
269f7f44 477 uninstall_hooks();
6c45e622
MW
478 if (hdr->magic != MEMORY_HEADER_MAGIC ||
479 tail->magic != MEMORY_TAIL_MAGIC)
5113680f 480 {
587ebae7
MW
481 for (current = &first_header; current != NULL; current = current->next)
482 {
483 if (current == hdr)
484 {
485 found = TRUE;
486 break;
487 }
488 }
489 if (found)
490 {
491 /* memory was allocated by our hooks but is corrupted */
492 fprintf(stderr, "freeing corrupted memory (%p): "
493 "header magic 0x%x, tail magic 0x%x:\n",
494 ptr, hdr->magic, tail->magic);
495 }
496 else
497 {
498 /* memory was not allocated by our hooks */
499 fprintf(stderr, "freeing invalid memory (%p)", ptr);
500 }
f7237cf3 501 backtrace = backtrace_create(3);
091d1780 502 backtrace->log(backtrace, stderr, TRUE);
f7237cf3 503 backtrace->destroy(backtrace);
5113680f 504 }
6c45e622 505 else
b7ef3f62 506 {
6c45e622
MW
507 /* remove item from list */
508 if (hdr->next)
509 {
510 hdr->next->previous = hdr->previous;
511 }
512 hdr->previous->next = hdr->next;
f7237cf3 513 hdr->backtrace->destroy(hdr->backtrace);
7daf5226 514
6c45e622 515 /* clear MAGIC, set mem to something remarkable */
587ebae7
MW
516 memset(hdr, MEMORY_FREE_PATTERN,
517 sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
7daf5226 518
6c45e622
MW
519 free(hdr);
520 }
7daf5226 521
5113680f 522 install_hooks();
6c90949f 523 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
5113680f
MW
524}
525
526/**
527 * Hook function for realloc()
528 */
6b3292da 529void *realloc_hook(void *old, size_t bytes, const void *caller)
5113680f 530{
a401efd0 531 memory_header_t *hdr;
b7ef3f62 532 memory_tail_t *tail;
f7237cf3 533 backtrace_t *backtrace;
84b18d5f 534 pthread_t thread_id = pthread_self();
323f9f99
MW
535 int oldpolicy;
536 struct sched_param oldparams, params;
7daf5226 537
5113680f
MW
538 /* allow reallocation of NULL */
539 if (old == NULL)
540 {
541 return malloc_hook(bytes, caller);
542 }
7daf5226 543
a401efd0 544 hdr = old - sizeof(memory_header_t);
b7ef3f62 545 tail = old + hdr->bytes;
7daf5226 546
84b18d5f 547 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
7daf5226 548
84b18d5f
TB
549 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
550 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
7daf5226 551
73760ca5 552 count_realloc++;
a401efd0 553 uninstall_hooks();
6c45e622
MW
554 if (hdr->magic != MEMORY_HEADER_MAGIC ||
555 tail->magic != MEMORY_TAIL_MAGIC)
b7ef3f62 556 {
6c45e622
MW
557 fprintf(stderr, "reallocating invalid memory (%p): "
558 "header magic 0x%x, tail magic 0x%x:\n",
559 old, hdr->magic, tail->magic);
f7237cf3 560 backtrace = backtrace_create(3);
091d1780 561 backtrace->log(backtrace, stderr, TRUE);
f7237cf3 562 backtrace->destroy(backtrace);
b7ef3f62
MW
563 }
564 /* clear tail magic, allocate, set tail magic */
565 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
566 hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
567 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
568 tail->magic = MEMORY_TAIL_MAGIC;
6c45e622 569
a401efd0
MW
570 /* update statistics */
571 hdr->bytes = bytes;
f7237cf3
MW
572 hdr->backtrace->destroy(hdr->backtrace);
573 hdr->backtrace = backtrace_create(3);
6c45e622 574
a401efd0
MW
575 /* update header of linked list neighbours */
576 if (hdr->next)
577 {
578 hdr->next->previous = hdr;
579 }
580 hdr->previous->next = hdr;
581 install_hooks();
84b18d5f 582 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
a401efd0 583 return hdr + 1;
5113680f
MW
584}
585
42e0f26e
MW
586METHOD(leak_detective_t, destroy, void,
587 private_leak_detective_t *this)
5113680f 588{
552cc11b 589 if (installed)
9cc7a297
MW
590 {
591 uninstall_hooks();
9cc7a297 592 }
552cc11b 593 free(this);
6b3292da
MW
594}
595
552cc11b
MW
596/*
597 * see header file
73760ca5 598 */
552cc11b 599leak_detective_t *leak_detective_create()
73760ca5 600{
42e0f26e
MW
601 private_leak_detective_t *this;
602
603 INIT(this,
604 .public = {
605 .report = _report,
fce3b5c3 606 .usage = _usage,
42e0f26e
MW
607 .destroy = _destroy,
608 },
609 );
7daf5226 610
552cc11b 611 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
73760ca5 612 {
7a485e90 613 cpu_set_t mask;
7daf5226 614
7a485e90
MW
615 CPU_ZERO(&mask);
616 CPU_SET(0, &mask);
7daf5226 617
7a485e90
MW
618 if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0)
619 {
620 fprintf(stderr, "setting CPU affinity failed: %m");
621 }
7daf5226 622
552cc11b 623 install_hooks();
73760ca5 624 }
552cc11b 625 return &this->public;
73760ca5
MW
626}
627