replaced --with-gid/uid by --with-group/user
[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 "tzset",
247 "inet_ntoa",
248 "strerror",
249 "getprotobynumber",
250 "getservbyport",
251 "getservbyname",
252 "getpwnam_r",
253 "getgrnam_r",
254 "register_printf_function",
255 "syslog",
256 "vsyslog",
257 "getaddrinfo",
258 "setlocale",
259 /* ignore dlopen, as we do not dlclose to get proper leak reports */
260 "dlopen",
261 /* mysql functions */
262 "mysql_init_character_set",
263 "init_client_errs",
264 "my_thread_init",
265 /* fastcgi library */
266 "FCGX_Init",
267 /* libxml */
268 "xmlInitCharEncodingHandlers",
269 "xmlInitParser",
270 "xmlInitParserCtxt",
271 /* ClearSilver */
272 "nerr_init",
273 };
274
275 /**
276 * check if a stack frame contains functions listed above
277 */
278 static bool is_whitelisted(void **stack_frames, int stack_frame_count)
279 {
280 int i, j;
281
282 #ifdef HAVE_DLADDR
283 for (i=0; i< stack_frame_count; i++)
284 {
285 Dl_info info;
286
287 if (dladdr(stack_frames[i], &info) && info.dli_sname)
288 {
289 for (j = 0; j < sizeof(whitelist)/sizeof(char*); j++)
290 {
291 if (streq(info.dli_sname, whitelist[j]))
292 {
293 return TRUE;
294 }
295 }
296 }
297 }
298 #endif /* HAVE_DLADDR */
299 return FALSE;
300 }
301
302 /**
303 * Report leaks at library destruction
304 */
305 void report_leaks()
306 {
307 memory_header_t *hdr;
308 int leaks = 0, whitelisted = 0;
309
310 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
311 {
312 if (is_whitelisted(hdr->stack_frames, hdr->stack_frame_count))
313 {
314 whitelisted++;
315 }
316 else
317 {
318 fprintf(stderr, "Leak (%d bytes at %p):\n", hdr->bytes, hdr + 1);
319 /* skip the first frame, contains leak detective logic */
320 log_stack_frames(hdr->stack_frames + 1, hdr->stack_frame_count - 1);
321 leaks++;
322 }
323 }
324
325 switch (leaks)
326 {
327 case 0:
328 fprintf(stderr, "No leaks detected");
329 break;
330 case 1:
331 fprintf(stderr, "One leak detected");
332 break;
333 default:
334 fprintf(stderr, "%d leaks detected", leaks);
335 break;
336 }
337 fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
338 }
339
340 /**
341 * Installs the malloc hooks, enables leak detection
342 */
343 static void install_hooks()
344 {
345 if (!installed)
346 {
347 old_malloc_hook = __malloc_hook;
348 old_realloc_hook = __realloc_hook;
349 old_free_hook = __free_hook;
350 __malloc_hook = malloc_hook;
351 __realloc_hook = realloc_hook;
352 __free_hook = free_hook;
353 installed = TRUE;
354 }
355 }
356
357 /**
358 * Uninstalls the malloc hooks, disables leak detection
359 */
360 static void uninstall_hooks()
361 {
362 if (installed)
363 {
364 __malloc_hook = old_malloc_hook;
365 __free_hook = old_free_hook;
366 __realloc_hook = old_realloc_hook;
367 installed = FALSE;
368 }
369 }
370
371 /**
372 * Hook function for malloc()
373 */
374 void *malloc_hook(size_t bytes, const void *caller)
375 {
376 memory_header_t *hdr;
377 memory_tail_t *tail;
378 pthread_t thread_id = pthread_self();
379 int oldpolicy;
380 struct sched_param oldparams, params;
381
382 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
383
384 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
385 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
386
387 count_malloc++;
388 uninstall_hooks();
389 hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
390 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
391 /* set to something which causes crashes */
392 memset(hdr, MEMORY_ALLOC_PATTERN,
393 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
394
395 hdr->magic = MEMORY_HEADER_MAGIC;
396 hdr->bytes = bytes;
397 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
398 tail->magic = MEMORY_TAIL_MAGIC;
399 install_hooks();
400
401 /* insert at the beginning of the list */
402 hdr->next = first_header.next;
403 if (hdr->next)
404 {
405 hdr->next->previous = hdr;
406 }
407 hdr->previous = &first_header;
408 first_header.next = hdr;
409
410 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
411
412 return hdr + 1;
413 }
414
415 /**
416 * Hook function for free()
417 */
418 void free_hook(void *ptr, const void *caller)
419 {
420 void *stack_frames[STACK_FRAMES_COUNT];
421 int stack_frame_count;
422 memory_header_t *hdr;
423 memory_tail_t *tail;
424 pthread_t thread_id = pthread_self();
425 int oldpolicy;
426 struct sched_param oldparams, params;
427
428 /* allow freeing of NULL */
429 if (ptr == NULL)
430 {
431 return;
432 }
433 hdr = ptr - sizeof(memory_header_t);
434 tail = ptr + hdr->bytes;
435
436 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
437
438 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
439 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
440
441 count_free++;
442 uninstall_hooks();
443 if (hdr->magic != MEMORY_HEADER_MAGIC ||
444 tail->magic != MEMORY_TAIL_MAGIC)
445 {
446 fprintf(stderr, "freeing invalid memory (%p): "
447 "header magic 0x%x, tail magic 0x%x:\n",
448 ptr, hdr->magic, tail->magic);
449 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
450 log_stack_frames(stack_frames, stack_frame_count);
451 }
452 else
453 {
454 /* remove item from list */
455 if (hdr->next)
456 {
457 hdr->next->previous = hdr->previous;
458 }
459 hdr->previous->next = hdr->next;
460
461 /* clear MAGIC, set mem to something remarkable */
462 memset(hdr, MEMORY_FREE_PATTERN, hdr->bytes + sizeof(memory_header_t));
463
464 free(hdr);
465 }
466
467 install_hooks();
468 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
469 }
470
471 /**
472 * Hook function for realloc()
473 */
474 void *realloc_hook(void *old, size_t bytes, const void *caller)
475 {
476 memory_header_t *hdr;
477 void *stack_frames[STACK_FRAMES_COUNT];
478 int stack_frame_count;
479 memory_tail_t *tail;
480 pthread_t thread_id = pthread_self();
481 int oldpolicy;
482 struct sched_param oldparams, params;
483
484 /* allow reallocation of NULL */
485 if (old == NULL)
486 {
487 return malloc_hook(bytes, caller);
488 }
489
490 hdr = old - sizeof(memory_header_t);
491 tail = old + hdr->bytes;
492
493 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
494
495 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
496 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
497
498 count_realloc++;
499 uninstall_hooks();
500 if (hdr->magic != MEMORY_HEADER_MAGIC ||
501 tail->magic != MEMORY_TAIL_MAGIC)
502 {
503 fprintf(stderr, "reallocating invalid memory (%p): "
504 "header magic 0x%x, tail magic 0x%x:\n",
505 old, hdr->magic, tail->magic);
506 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
507 log_stack_frames(stack_frames, stack_frame_count);
508 }
509 /* clear tail magic, allocate, set tail magic */
510 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
511 hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
512 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
513 tail->magic = MEMORY_TAIL_MAGIC;
514
515 /* update statistics */
516 hdr->bytes = bytes;
517 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
518
519 /* update header of linked list neighbours */
520 if (hdr->next)
521 {
522 hdr->next->previous = hdr;
523 }
524 hdr->previous->next = hdr;
525 install_hooks();
526 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
527 return hdr + 1;
528 }
529
530 /**
531 * Implementation of leak_detective_t.destroy
532 */
533 static void destroy(private_leak_detective_t *this)
534 {
535 if (installed)
536 {
537 uninstall_hooks();
538 report_leaks();
539 }
540 free(this);
541 }
542
543 /*
544 * see header file
545 */
546 leak_detective_t *leak_detective_create()
547 {
548 private_leak_detective_t *this = malloc_thing(private_leak_detective_t);
549
550 this->public.destroy = (void(*)(leak_detective_t*))destroy;
551
552 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
553 {
554 install_hooks();
555 }
556 return &this->public;
557 }
558