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