leak-detective: override malloc functions instead of using deprecated hooks
[strongswan.git] / src / libstrongswan / utils / leak_detective.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Copyright (C) 2006-2013 Martin Willi
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 */
16
17 #define _GNU_SOURCE
18 #include <stddef.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <signal.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <unistd.h>
26 #include <syslog.h>
27 #include <netdb.h>
28 #include <locale.h>
29 #include <dlfcn.h>
30
31 #include "leak_detective.h"
32
33 #include <library.h>
34 #include <utils/debug.h>
35 #include <utils/backtrace.h>
36 #include <collections/hashtable.h>
37 #include <threading/thread_value.h>
38 #include <threading/spinlock.h>
39
40 typedef struct private_leak_detective_t private_leak_detective_t;
41
42 /**
43 * private data of leak_detective
44 */
45 struct private_leak_detective_t {
46
47 /**
48 * public functions
49 */
50 leak_detective_t public;
51 };
52
53 /**
54 * Magic value which helps to detect memory corruption. Yummy!
55 */
56 #define MEMORY_HEADER_MAGIC 0x7ac0be11
57
58 /**
59 * Magic written to tail of allocation
60 */
61 #define MEMORY_TAIL_MAGIC 0xcafebabe
62
63 /**
64 * Pattern which is filled in memory before freeing it
65 */
66 #define MEMORY_FREE_PATTERN 0xFF
67
68 /**
69 * Pattern which is filled in newly allocated memory
70 */
71 #define MEMORY_ALLOC_PATTERN 0xEE
72
73 static u_int count_malloc = 0;
74 static u_int count_free = 0;
75 static u_int count_realloc = 0;
76
77 typedef struct memory_header_t memory_header_t;
78 typedef struct memory_tail_t memory_tail_t;
79
80 /**
81 * Header which is prepended to each allocated memory block
82 */
83 struct memory_header_t {
84
85 /**
86 * Pointer to previous entry in linked list
87 */
88 memory_header_t *previous;
89
90 /**
91 * Pointer to next entry in linked list
92 */
93 memory_header_t *next;
94
95 /**
96 * backtrace taken during (re-)allocation
97 */
98 backtrace_t *backtrace;
99
100 /**
101 * Number of bytes following after the header
102 */
103 u_int32_t bytes;
104
105 /**
106 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
107 */
108 u_int32_t magic;
109
110 }__attribute__((__packed__));
111
112 /**
113 * tail appended to each allocated memory block
114 */
115 struct memory_tail_t {
116
117 /**
118 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
119 */
120 u_int32_t magic;
121
122 }__attribute__((__packed__));
123
124 /**
125 * first mem header is just a dummy to chain
126 * the others on it...
127 */
128 static memory_header_t first_header = {
129 .magic = MEMORY_HEADER_MAGIC,
130 };
131
132 /**
133 * Spinlock to access header linked list
134 */
135 static spinlock_t *lock;
136
137 /**
138 * Is leak detection currently enabled?
139 */
140 static bool enabled = FALSE;
141
142 /**
143 * Is leak detection disabled for the current thread?
144 */
145 static thread_value_t *thread_disabled;
146
147 /**
148 * Installs the malloc hooks, enables leak detection
149 */
150 static void enable_leak_detective()
151 {
152 enabled = TRUE;
153 }
154
155 /**
156 * Uninstalls the malloc hooks, disables leak detection
157 */
158 static void disable_leak_detective()
159 {
160 enabled = FALSE;
161 }
162
163 /**
164 * Enable/Disable leak detective for the current thread
165 *
166 * @return Previous value
167 */
168 static bool enable_thread(bool enable)
169 {
170 bool before;
171
172 before = thread_disabled->get(thread_disabled) == NULL;
173 thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE);
174 return before;
175 }
176
177 /**
178 * dlsym() might do a malloc(), but we can't do one before we get the malloc()
179 * function pointer. Use this minimalistic malloc implementation instead.
180 */
181 static void* malloc_for_dlsym(size_t size)
182 {
183 static char buf[1024] = {};
184 static size_t used = 0;
185 char *ptr;
186
187 /* roundup to a multiple of 32 */
188 size = (size - 1) / 32 * 32 + 32;
189
190 if (used + size > sizeof(buf))
191 {
192 return NULL;
193 }
194 ptr = buf + used;
195 used += size;
196 return ptr;
197 }
198
199 /**
200 * Lookup a malloc function, while disabling wrappers
201 */
202 static void* get_malloc_fn(char *name)
203 {
204 bool before = FALSE;
205 void *fn;
206
207 if (enabled)
208 {
209 before = enable_thread(FALSE);
210 }
211 fn = dlsym(RTLD_NEXT, name);
212 if (enabled)
213 {
214 enable_thread(before);
215 }
216 return fn;
217 }
218
219 /**
220 * Call original malloc()
221 */
222 static void* real_malloc(size_t size)
223 {
224 static void* (*fn)(size_t size);
225 static int recursive = 0;
226
227 if (!fn)
228 {
229 /* checking recursiveness should actually be thread-specific. But as
230 * it is very likely that the first allocation is done before we go
231 * multi-threaded, we keep it simple. */
232 if (recursive)
233 {
234 return malloc_for_dlsym(size);
235 }
236 recursive++;
237 fn = get_malloc_fn("malloc");
238 recursive--;
239 }
240 return fn(size);
241 }
242
243 /**
244 * Call original free()
245 */
246 static void real_free(void *ptr)
247 {
248 static void (*fn)(void *ptr);
249
250 if (!fn)
251 {
252 fn = get_malloc_fn("free");
253 }
254 return fn(ptr);
255 }
256
257 /**
258 * Call original realloc()
259 */
260 static void* real_realloc(void *ptr, size_t size)
261 {
262 static void* (*fn)(void *ptr, size_t size);
263
264 if (!fn)
265 {
266 fn = get_malloc_fn("realloc");
267 }
268 return fn(ptr, size);
269 }
270
271 /**
272 * Leak report white list
273 *
274 * List of functions using static allocation buffers or should be suppressed
275 * otherwise on leak report.
276 */
277 char *whitelist[] = {
278 /* backtraces, including own */
279 "backtrace_create",
280 "safe_strerror",
281 /* pthread stuff */
282 "pthread_create",
283 "pthread_setspecific",
284 "__pthread_setspecific",
285 /* glibc functions */
286 "mktime",
287 "ctime",
288 "__gmtime_r",
289 "localtime_r",
290 "tzset",
291 "time_printf_hook",
292 "inet_ntoa",
293 "strerror",
294 "getprotobyname",
295 "getprotobynumber",
296 "getservbyport",
297 "getservbyname",
298 "gethostbyname",
299 "gethostbyname2",
300 "gethostbyname_r",
301 "gethostbyname2_r",
302 "getnetbyname",
303 "getpwnam_r",
304 "getgrnam_r",
305 "register_printf_function",
306 "register_printf_specifier",
307 "syslog",
308 "vsyslog",
309 "__syslog_chk",
310 "__vsyslog_chk",
311 "getaddrinfo",
312 "setlocale",
313 "getpass",
314 "getpwent_r",
315 "setpwent",
316 "endpwent",
317 "getspnam_r",
318 "getpwuid_r",
319 "initgroups",
320 /* ignore dlopen, as we do not dlclose to get proper leak reports */
321 "dlopen",
322 "dlerror",
323 "dlclose",
324 "dlsym",
325 /* mysql functions */
326 "mysql_init_character_set",
327 "init_client_errs",
328 "my_thread_init",
329 /* fastcgi library */
330 "FCGX_Init",
331 /* libxml */
332 "xmlInitCharEncodingHandlers",
333 "xmlInitParser",
334 "xmlInitParserCtxt",
335 /* libcurl */
336 "Curl_client_write",
337 /* ClearSilver */
338 "nerr_init",
339 /* OpenSSL */
340 "RSA_new_method",
341 "DH_new_method",
342 "ENGINE_load_builtin_engines",
343 "OPENSSL_config",
344 "ecdsa_check",
345 "ERR_put_error",
346 /* libgcrypt */
347 "gcry_control",
348 "gcry_check_version",
349 "gcry_randomize",
350 "gcry_create_nonce",
351 /* NSPR */
352 "PR_CallOnce",
353 /* libapr */
354 "apr_pool_create_ex",
355 /* glib */
356 "g_type_init_with_debug_flags",
357 "g_type_register_static",
358 "g_type_class_ref",
359 "g_type_create_instance",
360 "g_type_add_interface_static",
361 "g_type_interface_add_prerequisite",
362 "g_socket_connection_factory_lookup_type",
363 /* libgpg */
364 "gpg_err_init",
365 /* gnutls */
366 "gnutls_global_init",
367 };
368
369
370 /**
371 * Hashtable hash function
372 */
373 static u_int hash(backtrace_t *key)
374 {
375 enumerator_t *enumerator;
376 void *addr;
377 u_int hash = 0;
378
379 enumerator = key->create_frame_enumerator(key);
380 while (enumerator->enumerate(enumerator, &addr))
381 {
382 hash = chunk_hash_inc(chunk_from_thing(addr), hash);
383 }
384 enumerator->destroy(enumerator);
385
386 return hash;
387 }
388
389 /**
390 * Hashtable equals function
391 */
392 static bool equals(backtrace_t *a, backtrace_t *b)
393 {
394 return a->equals(a, b);
395 }
396
397 /**
398 * Summarize and print backtraces
399 */
400 static int print_traces(private_leak_detective_t *this,
401 FILE *out, int thresh, bool detailed, int *whitelisted)
402 {
403 int leaks = 0;
404 memory_header_t *hdr;
405 enumerator_t *enumerator;
406 hashtable_t *entries;
407 struct {
408 /** associated backtrace */
409 backtrace_t *backtrace;
410 /** total size of all allocations */
411 size_t bytes;
412 /** number of allocations */
413 u_int count;
414 } *entry;
415 bool before;
416
417 before = enable_thread(FALSE);
418
419 entries = hashtable_create((hashtable_hash_t)hash,
420 (hashtable_equals_t)equals, 1024);
421 lock->lock(lock);
422 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
423 {
424 if (whitelisted &&
425 hdr->backtrace->contains_function(hdr->backtrace,
426 whitelist, countof(whitelist)))
427 {
428 (*whitelisted)++;
429 continue;
430 }
431 entry = entries->get(entries, hdr->backtrace);
432 if (entry)
433 {
434 entry->bytes += hdr->bytes;
435 entry->count++;
436 }
437 else
438 {
439 INIT(entry,
440 .backtrace = hdr->backtrace,
441 .bytes = hdr->bytes,
442 .count = 1,
443 );
444 entries->put(entries, hdr->backtrace, entry);
445 }
446 leaks++;
447 }
448 lock->unlock(lock);
449 enumerator = entries->create_enumerator(entries);
450 while (enumerator->enumerate(enumerator, NULL, &entry))
451 {
452 if (!thresh || entry->bytes >= thresh)
453 {
454 fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
455 entry->bytes, entry->count, entry->bytes / entry->count);
456 entry->backtrace->log(entry->backtrace, out, detailed);
457 }
458 free(entry);
459 }
460 enumerator->destroy(enumerator);
461 entries->destroy(entries);
462
463 enable_thread(before);
464 return leaks;
465 }
466
467 METHOD(leak_detective_t, report, void,
468 private_leak_detective_t *this, bool detailed)
469 {
470 if (lib->leak_detective)
471 {
472 int leaks = 0, whitelisted = 0;
473
474 leaks = print_traces(this, stderr, 0, detailed, &whitelisted);
475 switch (leaks)
476 {
477 case 0:
478 fprintf(stderr, "No leaks detected");
479 break;
480 case 1:
481 fprintf(stderr, "One leak detected");
482 break;
483 default:
484 fprintf(stderr, "%d leaks detected", leaks);
485 break;
486 }
487 fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
488 }
489 else
490 {
491 fprintf(stderr, "Leak detective disabled\n");
492 }
493 }
494
495 METHOD(leak_detective_t, set_state, bool,
496 private_leak_detective_t *this, bool enable)
497 {
498 if (enable == enabled)
499 {
500 return enabled;
501 }
502 if (enable)
503 {
504 enable_leak_detective();
505 }
506 else
507 {
508 disable_leak_detective();
509 }
510 return !enabled;
511 }
512
513 METHOD(leak_detective_t, usage, void,
514 private_leak_detective_t *this, FILE *out)
515 {
516 bool detailed;
517 int thresh;
518
519 thresh = lib->settings->get_int(lib->settings,
520 "libstrongswan.leak_detective.usage_threshold", 10240);
521 detailed = lib->settings->get_bool(lib->settings,
522 "libstrongswan.leak_detective.detailed", TRUE);
523
524 print_traces(this, out, thresh, detailed, NULL);
525 }
526
527 /**
528 * Wrapped malloc() function
529 */
530 void* malloc(size_t bytes)
531 {
532 memory_header_t *hdr;
533 memory_tail_t *tail;
534 bool before;
535
536 if (!enabled || thread_disabled->get(thread_disabled))
537 {
538 return real_malloc(bytes);
539 }
540
541 count_malloc++;
542 hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
543 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
544 /* set to something which causes crashes */
545 memset(hdr, MEMORY_ALLOC_PATTERN,
546 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
547
548 before = enable_thread(FALSE);
549 hdr->backtrace = backtrace_create(2);
550 enable_thread(before);
551
552 hdr->magic = MEMORY_HEADER_MAGIC;
553 hdr->bytes = bytes;
554 tail->magic = MEMORY_TAIL_MAGIC;
555
556 /* insert at the beginning of the list */
557 lock->lock(lock);
558 hdr->next = first_header.next;
559 if (hdr->next)
560 {
561 hdr->next->previous = hdr;
562 }
563 hdr->previous = &first_header;
564 first_header.next = hdr;
565 lock->unlock(lock);
566
567 return hdr + 1;
568 }
569
570 /**
571 * Wrapped calloc() function
572 */
573 void* calloc(size_t nmemb, size_t size)
574 {
575 void *ptr;
576
577 size *= nmemb;
578 ptr = malloc(size);
579 memset(ptr, 0, size);
580
581 return ptr;
582 }
583
584 /**
585 * Wrapped free() function
586 */
587 void free(void *ptr)
588 {
589 memory_header_t *hdr, *current;
590 memory_tail_t *tail;
591 backtrace_t *backtrace;
592 bool found = FALSE, before;
593
594 if (!enabled || thread_disabled->get(thread_disabled))
595 {
596 real_free(ptr);
597 return;
598 }
599 /* allow freeing of NULL */
600 if (ptr == NULL)
601 {
602 return;
603 }
604 hdr = ptr - sizeof(memory_header_t);
605 tail = ptr + hdr->bytes;
606
607 count_free++;
608 before = enable_thread(FALSE);
609 if (hdr->magic != MEMORY_HEADER_MAGIC ||
610 tail->magic != MEMORY_TAIL_MAGIC)
611 {
612 lock->lock(lock);
613 for (current = &first_header; current != NULL; current = current->next)
614 {
615 if (current == hdr)
616 {
617 found = TRUE;
618 break;
619 }
620 }
621 lock->unlock(lock);
622 if (found)
623 {
624 /* memory was allocated by our hooks but is corrupted */
625 fprintf(stderr, "freeing corrupted memory (%p): "
626 "header magic 0x%x, tail magic 0x%x:\n",
627 ptr, hdr->magic, tail->magic);
628 }
629 else
630 {
631 /* memory was not allocated by our hooks */
632 fprintf(stderr, "freeing invalid memory (%p)\n", ptr);
633 }
634 backtrace = backtrace_create(2);
635 backtrace->log(backtrace, stderr, TRUE);
636 backtrace->destroy(backtrace);
637 }
638 else
639 {
640 /* remove item from list */
641 lock->lock(lock);
642 if (hdr->next)
643 {
644 hdr->next->previous = hdr->previous;
645 }
646 hdr->previous->next = hdr->next;
647 lock->unlock(lock);
648
649 hdr->backtrace->destroy(hdr->backtrace);
650
651 /* clear MAGIC, set mem to something remarkable */
652 memset(hdr, MEMORY_FREE_PATTERN,
653 sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
654
655 real_free(hdr);
656 }
657 enable_thread(before);
658 }
659
660 /**
661 * Wrapped realloc() function
662 */
663 void* realloc(void *old, size_t bytes)
664 {
665 memory_header_t *hdr;
666 memory_tail_t *tail;
667 backtrace_t *backtrace;
668 bool before;
669
670 if (!enabled || thread_disabled->get(thread_disabled))
671 {
672 return real_realloc(old, bytes);
673 }
674 /* allow reallocation of NULL */
675 if (old == NULL)
676 {
677 return malloc(bytes);
678 }
679
680 hdr = old - sizeof(memory_header_t);
681 tail = old + hdr->bytes;
682
683 count_realloc++;
684 if (hdr->magic != MEMORY_HEADER_MAGIC ||
685 tail->magic != MEMORY_TAIL_MAGIC)
686 {
687 fprintf(stderr, "reallocating invalid memory (%p):\n"
688 "header magic 0x%x:\n", old, hdr->magic);
689 backtrace = backtrace_create(2);
690 backtrace->log(backtrace, stderr, TRUE);
691 backtrace->destroy(backtrace);
692 }
693 else
694 {
695 /* clear tail magic, allocate, set tail magic */
696 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
697 }
698 hdr = real_realloc(hdr,
699 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
700 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
701 tail->magic = MEMORY_TAIL_MAGIC;
702
703 /* update statistics */
704 hdr->bytes = bytes;
705
706 before = enable_thread(FALSE);
707 hdr->backtrace->destroy(hdr->backtrace);
708 hdr->backtrace = backtrace_create(2);
709 enable_thread(before);
710
711 /* update header of linked list neighbours */
712 lock->lock(lock);
713 if (hdr->next)
714 {
715 hdr->next->previous = hdr;
716 }
717 hdr->previous->next = hdr;
718 lock->unlock(lock);
719
720 return hdr + 1;
721 }
722
723 METHOD(leak_detective_t, destroy, void,
724 private_leak_detective_t *this)
725 {
726 disable_leak_detective();
727 lock->destroy(lock);
728 thread_disabled->destroy(thread_disabled);
729 free(this);
730 }
731
732 /*
733 * see header file
734 */
735 leak_detective_t *leak_detective_create()
736 {
737 private_leak_detective_t *this;
738
739 INIT(this,
740 .public = {
741 .report = _report,
742 .usage = _usage,
743 .set_state = _set_state,
744 .destroy = _destroy,
745 },
746 );
747
748 lock = spinlock_create();
749 thread_disabled = thread_value_create(NULL);
750
751 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
752 {
753 enable_leak_detective();
754 }
755 return &this->public;
756 }