plugin load configuration in strongswan.conf
[strongswan.git] / src / libstrongswan / utils / leak_detective.c
1 /*
2 * Copyright (C) 2006-2008 Martin Willi
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 *
15 * $Id$
16 */
17
18 #ifdef HAVE_DLADDR
19 # define _GNU_SOURCE
20 # include <dlfcn.h>
21 #endif /* HAVE_DLADDR */
22
23 #include <stddef.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <malloc.h>
27 #include <signal.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <dlfcn.h>
32 #include <unistd.h>
33 #include <syslog.h>
34 #include <pthread.h>
35 #include <netdb.h>
36 #include <printf.h>
37 #include <locale.h>
38 #ifdef HAVE_BACKTRACE
39 # include <execinfo.h>
40 #endif /* HAVE_BACKTRACE */
41
42 #include "leak_detective.h"
43
44 #include <library.h>
45 #include <debug.h>
46
47 typedef struct private_leak_detective_t private_leak_detective_t;
48
49 /**
50 * private data of leak_detective
51 */
52 struct private_leak_detective_t {
53
54 /**
55 * public functions
56 */
57 leak_detective_t public;
58 };
59
60 /**
61 * Magic value which helps to detect memory corruption. Yummy!
62 */
63 #define MEMORY_HEADER_MAGIC 0x7ac0be11
64
65 /**
66 * Magic written to tail of allocation
67 */
68 #define MEMORY_TAIL_MAGIC 0xcafebabe
69
70 /**
71 * Pattern which is filled in memory before freeing it
72 */
73 #define MEMORY_FREE_PATTERN 0xFF
74
75 /**
76 * Pattern which is filled in newly allocated memory
77 */
78 #define MEMORY_ALLOC_PATTERN 0xEE
79
80
81 static void install_hooks(void);
82 static void uninstall_hooks(void);
83 static void *malloc_hook(size_t, const void *);
84 static void *realloc_hook(void *, size_t, const void *);
85 static void free_hook(void*, const void *);
86
87 void *(*old_malloc_hook)(size_t, const void *);
88 void *(*old_realloc_hook)(void *, size_t, const void *);
89 void (*old_free_hook)(void*, const void *);
90
91 static u_int count_malloc = 0;
92 static u_int count_free = 0;
93 static u_int count_realloc = 0;
94
95 typedef struct memory_header_t memory_header_t;
96 typedef struct memory_tail_t memory_tail_t;
97
98 /**
99 * Header which is prepended to each allocated memory block
100 */
101 struct memory_header_t {
102
103 /**
104 * Number of bytes following after the header
105 */
106 u_int bytes;
107
108 /**
109 * Stack frames at the time of allocation
110 */
111 void *stack_frames[STACK_FRAMES_COUNT];
112
113 /**
114 * Number of stacks frames obtained in stack_frames
115 */
116 int stack_frame_count;
117
118 /**
119 * Pointer to previous entry in linked list
120 */
121 memory_header_t *previous;
122
123 /**
124 * Pointer to next entry in linked list
125 */
126 memory_header_t *next;
127
128 /**
129 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
130 */
131 u_int32_t magic;
132
133 }__attribute__((__packed__));
134
135 /**
136 * tail appended to each allocated memory block
137 */
138 struct memory_tail_t {
139
140 /**
141 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
142 */
143 u_int32_t magic;
144
145 }__attribute__((__packed__));
146
147 /**
148 * first mem header is just a dummy to chain
149 * the others on it...
150 */
151 static memory_header_t first_header = {
152 magic: MEMORY_HEADER_MAGIC,
153 bytes: 0,
154 stack_frame_count: 0,
155 previous: NULL,
156 next: NULL
157 };
158
159 /**
160 * are the hooks currently installed?
161 */
162 static bool installed = FALSE;
163
164 /**
165 * log stack frames queried by backtrace()
166 * TODO: Dump symbols of static functions. This could be done with
167 * the addr2line utility or the GNU BFD Library...
168 */
169 static void log_stack_frames(void **stack_frames, int stack_frame_count)
170 {
171 #ifdef HAVE_BACKTRACE
172 size_t i;
173 char **strings;
174
175 strings = backtrace_symbols(stack_frames, stack_frame_count);
176
177 fprintf(stderr, " dumping %d stack frame addresses:\n", stack_frame_count);
178 for (i = 0; i < stack_frame_count; i++)
179 {
180 #ifdef HAVE_DLADDR
181 Dl_info info;
182
183 if (dladdr(stack_frames[i], &info))
184 {
185 char cmd[1024];
186 FILE *output;
187 char c;
188 void *ptr = stack_frames[i];
189
190 if (strstr(info.dli_fname, ".so"))
191 {
192 ptr = (void*)(stack_frames[i] - info.dli_fbase);
193 }
194 snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", info.dli_fname, ptr);
195 if (info.dli_sname)
196 {
197 fprintf(stderr, " \e[33m%s\e[0m @ %p (\e[31m%s\e[0m+0x%x) [%p]\n",
198 info.dli_fname, info.dli_fbase, info.dli_sname,
199 stack_frames[i] - info.dli_saddr, stack_frames[i]);
200 }
201 else
202 {
203 fprintf(stderr, " \e[33m%s\e[0m @ %p [%p]\n", info.dli_fname,
204 info.dli_fbase, stack_frames[i]);
205 }
206 fprintf(stderr, " -> \e[32m");
207 output = popen(cmd, "r");
208 if (output)
209 {
210 while (TRUE)
211 {
212 c = getc(output);
213 if (c == '\n' || c == EOF)
214 {
215 break;
216 }
217 fputc(c, stderr);
218 }
219 }
220 else
221 {
222 #endif /* HAVE_DLADDR */
223 fprintf(stderr, " %s\n", strings[i]);
224 #ifdef HAVE_DLADDR
225 }
226 fprintf(stderr, "\n\e[0m");
227 }
228 #endif /* HAVE_DLADDR */
229 }
230 free (strings);
231 #endif /* HAVE_BACKTRACE */
232 }
233
234 /**
235 * Leak report white list
236 *
237 * List of functions using static allocation buffers or should be suppressed
238 * otherwise on leak report.
239 */
240 char *whitelist[] = {
241 /* pthread stuff */
242 "pthread_create",
243 "pthread_setspecific",
244 /* glibc functions */
245 "mktime",
246 "__gmtime_r",
247 "tzset",
248 "inet_ntoa",
249 "strerror",
250 "getprotobynumber",
251 "getservbyport",
252 "getservbyname",
253 "getpwnam_r",
254 "getgrnam_r",
255 "register_printf_function",
256 "syslog",
257 "vsyslog",
258 "getaddrinfo",
259 "setlocale",
260 /* ignore dlopen, as we do not dlclose to get proper leak reports */
261 "dlopen",
262 "dlerror",
263 /* mysql functions */
264 "mysql_init_character_set",
265 "init_client_errs",
266 "my_thread_init",
267 /* fastcgi library */
268 "FCGX_Init",
269 /* libxml */
270 "xmlInitCharEncodingHandlers",
271 "xmlInitParser",
272 "xmlInitParserCtxt",
273 /* ClearSilver */
274 "nerr_init",
275 };
276
277 /**
278 * check if a stack frame contains functions listed above
279 */
280 static bool is_whitelisted(void **stack_frames, int stack_frame_count)
281 {
282 int i, j;
283
284 #ifdef HAVE_DLADDR
285 for (i=0; i< stack_frame_count; i++)
286 {
287 Dl_info info;
288
289 if (dladdr(stack_frames[i], &info) && info.dli_sname)
290 {
291 for (j = 0; j < sizeof(whitelist)/sizeof(char*); j++)
292 {
293 if (streq(info.dli_sname, whitelist[j]))
294 {
295 return TRUE;
296 }
297 }
298 }
299 }
300 #endif /* HAVE_DLADDR */
301 return FALSE;
302 }
303
304 /**
305 * Report leaks at library destruction
306 */
307 void report_leaks()
308 {
309 memory_header_t *hdr;
310 int leaks = 0, whitelisted = 0;
311
312 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
313 {
314 if (is_whitelisted(hdr->stack_frames, hdr->stack_frame_count))
315 {
316 whitelisted++;
317 }
318 else
319 {
320 fprintf(stderr, "Leak (%d bytes at %p):\n", hdr->bytes, hdr + 1);
321 /* skip the first frame, contains leak detective logic */
322 log_stack_frames(hdr->stack_frames + 1, hdr->stack_frame_count - 1);
323 leaks++;
324 }
325 }
326
327 switch (leaks)
328 {
329 case 0:
330 fprintf(stderr, "No leaks detected");
331 break;
332 case 1:
333 fprintf(stderr, "One leak detected");
334 break;
335 default:
336 fprintf(stderr, "%d leaks detected", leaks);
337 break;
338 }
339 fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
340 }
341
342 /**
343 * Installs the malloc hooks, enables leak detection
344 */
345 static void install_hooks()
346 {
347 if (!installed)
348 {
349 old_malloc_hook = __malloc_hook;
350 old_realloc_hook = __realloc_hook;
351 old_free_hook = __free_hook;
352 __malloc_hook = malloc_hook;
353 __realloc_hook = realloc_hook;
354 __free_hook = free_hook;
355 installed = TRUE;
356 }
357 }
358
359 /**
360 * Uninstalls the malloc hooks, disables leak detection
361 */
362 static void uninstall_hooks()
363 {
364 if (installed)
365 {
366 __malloc_hook = old_malloc_hook;
367 __free_hook = old_free_hook;
368 __realloc_hook = old_realloc_hook;
369 installed = FALSE;
370 }
371 }
372
373 /**
374 * Hook function for malloc()
375 */
376 void *malloc_hook(size_t bytes, const void *caller)
377 {
378 memory_header_t *hdr;
379 memory_tail_t *tail;
380 pthread_t thread_id = pthread_self();
381 int oldpolicy;
382 struct sched_param oldparams, params;
383
384 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
385
386 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
387 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
388
389 count_malloc++;
390 uninstall_hooks();
391 hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
392 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
393 /* set to something which causes crashes */
394 memset(hdr, MEMORY_ALLOC_PATTERN,
395 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
396
397 hdr->magic = MEMORY_HEADER_MAGIC;
398 hdr->bytes = bytes;
399 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
400 tail->magic = MEMORY_TAIL_MAGIC;
401 install_hooks();
402
403 /* insert at the beginning of the list */
404 hdr->next = first_header.next;
405 if (hdr->next)
406 {
407 hdr->next->previous = hdr;
408 }
409 hdr->previous = &first_header;
410 first_header.next = hdr;
411
412 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
413
414 return hdr + 1;
415 }
416
417 /**
418 * Hook function for free()
419 */
420 void free_hook(void *ptr, const void *caller)
421 {
422 void *stack_frames[STACK_FRAMES_COUNT];
423 int stack_frame_count;
424 memory_header_t *hdr;
425 memory_tail_t *tail;
426 pthread_t thread_id = pthread_self();
427 int oldpolicy;
428 struct sched_param oldparams, params;
429
430 /* allow freeing of NULL */
431 if (ptr == NULL)
432 {
433 return;
434 }
435 hdr = ptr - sizeof(memory_header_t);
436 tail = ptr + hdr->bytes;
437
438 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
439
440 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
441 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
442
443 count_free++;
444 uninstall_hooks();
445 if (hdr->magic != MEMORY_HEADER_MAGIC ||
446 tail->magic != MEMORY_TAIL_MAGIC)
447 {
448 fprintf(stderr, "freeing invalid memory (%p): "
449 "header magic 0x%x, tail magic 0x%x:\n",
450 ptr, hdr->magic, tail->magic);
451 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
452 log_stack_frames(stack_frames, stack_frame_count);
453 }
454 else
455 {
456 /* remove item from list */
457 if (hdr->next)
458 {
459 hdr->next->previous = hdr->previous;
460 }
461 hdr->previous->next = hdr->next;
462
463 /* clear MAGIC, set mem to something remarkable */
464 memset(hdr, MEMORY_FREE_PATTERN, hdr->bytes + sizeof(memory_header_t));
465
466 free(hdr);
467 }
468
469 install_hooks();
470 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
471 }
472
473 /**
474 * Hook function for realloc()
475 */
476 void *realloc_hook(void *old, size_t bytes, const void *caller)
477 {
478 memory_header_t *hdr;
479 void *stack_frames[STACK_FRAMES_COUNT];
480 int stack_frame_count;
481 memory_tail_t *tail;
482 pthread_t thread_id = pthread_self();
483 int oldpolicy;
484 struct sched_param oldparams, params;
485
486 /* allow reallocation of NULL */
487 if (old == NULL)
488 {
489 return malloc_hook(bytes, caller);
490 }
491
492 hdr = old - sizeof(memory_header_t);
493 tail = old + hdr->bytes;
494
495 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
496
497 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
498 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
499
500 count_realloc++;
501 uninstall_hooks();
502 if (hdr->magic != MEMORY_HEADER_MAGIC ||
503 tail->magic != MEMORY_TAIL_MAGIC)
504 {
505 fprintf(stderr, "reallocating invalid memory (%p): "
506 "header magic 0x%x, tail magic 0x%x:\n",
507 old, hdr->magic, tail->magic);
508 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
509 log_stack_frames(stack_frames, stack_frame_count);
510 }
511 /* clear tail magic, allocate, set tail magic */
512 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
513 hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
514 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
515 tail->magic = MEMORY_TAIL_MAGIC;
516
517 /* update statistics */
518 hdr->bytes = bytes;
519 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
520
521 /* update header of linked list neighbours */
522 if (hdr->next)
523 {
524 hdr->next->previous = hdr;
525 }
526 hdr->previous->next = hdr;
527 install_hooks();
528 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
529 return hdr + 1;
530 }
531
532 /**
533 * Implementation of leak_detective_t.destroy
534 */
535 static void destroy(private_leak_detective_t *this)
536 {
537 if (installed)
538 {
539 uninstall_hooks();
540 report_leaks();
541 }
542 free(this);
543 }
544
545 /*
546 * see header file
547 */
548 leak_detective_t *leak_detective_create()
549 {
550 private_leak_detective_t *this = malloc_thing(private_leak_detective_t);
551
552 this->public.destroy = (void(*)(leak_detective_t*))destroy;
553
554 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
555 {
556 install_hooks();
557 }
558 return &this->public;
559 }
560