- fixed doxygen build
[strongswan.git] / Source / lib / utils / leak_detective.c
1 /**
2 * @file leak_detective.c
3 *
4 * @brief Allocation hooks to find memory leaks.
5 */
6
7 /*
8 * Copyright (C) 2006 Martin Willi
9 * Hochschule fuer Technik Rapperswil
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 */
21
22 #include <stddef.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <malloc.h>
26 #include <execinfo.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
36 #include "leak_detective.h"
37
38 #include <types.h>
39 #include <utils/logger_manager.h>
40
41 #ifdef LEAK_DETECTIVE
42
43 /**
44 * Magic value which helps to detect memory corruption
45 */
46 #define MEMORY_HEADER_MAGIC 0xF1367ADF
47
48 static void install_hooks(void);
49 static void uninstall_hooks(void);
50 static void *malloc_hook(size_t, const void *);
51 static void *realloc_hook(void *, size_t, const void *);
52 static void free_hook(void*, const void *);
53 static void load_excluded_functions();
54
55 typedef struct memory_header_t memory_header_t;
56
57 /**
58 * Header which is prepended to each allocated memory block
59 */
60 struct memory_header_t {
61 /**
62 * Magci byte which must(!) hold MEMORY_HEADER_MAGIC
63 */
64 u_int32_t magic;
65
66 /**
67 * Number of bytes following after the header
68 */
69 size_t bytes;
70
71 /**
72 * Stack frames at the time of allocation
73 */
74 void *stack_frames[STACK_FRAMES_COUNT];
75
76 /**
77 * Number of stacks frames obtained in stack_frames
78 */
79 int stack_frame_count;
80
81 /**
82 * Pointer to previous entry in linked list
83 */
84 memory_header_t *previous;
85
86 /**
87 * Pointer to next entry in linked list
88 */
89 memory_header_t *next;
90 };
91
92 /**
93 * first mem header is just a dummy to chain
94 * the others on it...
95 */
96 static memory_header_t first_header = {
97 magic: MEMORY_HEADER_MAGIC,
98 bytes: 0,
99 stack_frame_count: 0,
100 previous: NULL,
101 next: NULL
102 };
103
104 /**
105 * logger for the leak detective
106 */
107 static logger_t *logger;
108
109 /**
110 * standard hooks, used to temparily remove hooking
111 */
112 static void *old_malloc_hook, *old_realloc_hook, *old_free_hook;
113
114 /**
115 * are the hooks currently installed?
116 */
117 static bool installed = FALSE;
118
119 /**
120 * Mutex to exclusivly uninstall hooks, access heap list
121 */
122 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
123
124
125 /**
126 * log stack frames queried by backtrace()
127 * TODO: Dump symbols of static functions!!!
128 */
129 static void log_stack_frames(void **stack_frames, int stack_frame_count)
130 {
131 char **strings;
132 size_t i;
133
134 strings = backtrace_symbols (stack_frames, stack_frame_count);
135
136 logger->log(logger, ERROR, " dumping %d stack frame addresses.", stack_frame_count);
137
138 for (i = 0; i < stack_frame_count; i++)
139 {
140 logger->log(logger, ERROR, " %s", strings[i]);
141 }
142 free (strings);
143 }
144
145 /**
146 * Report leaks at library destruction
147 */
148 void report_leaks()
149 {
150 memory_header_t *hdr;
151 int leaks = 0;
152
153 /* reaquire a logger is necessary, this will force ((destructor))
154 * order to work correctly */
155 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
156 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
157 {
158 logger->log(logger, ERROR, "Leak (%d bytes at %p)", hdr->bytes, hdr + 1);
159 log_stack_frames(hdr->stack_frames, hdr->stack_frame_count);
160 leaks++;
161 }
162
163 switch (leaks)
164 {
165 case 0:
166 logger->log(logger, CONTROL, "No leaks detected");
167 break;
168 case 1:
169 logger->log(logger, ERROR, "One leak detected");
170 break;
171 default:
172 logger->log(logger, ERROR, "%d leaks detected", leaks);
173 break;
174 }
175 }
176
177 /**
178 * Installs the malloc hooks, enables leak detection
179 */
180 static void install_hooks()
181 {
182 if (!installed)
183 {
184 old_malloc_hook = __malloc_hook;
185 old_realloc_hook = __realloc_hook;
186 old_free_hook = __free_hook;
187 __malloc_hook = malloc_hook;
188 __realloc_hook = realloc_hook;
189 __free_hook = free_hook;
190 installed = TRUE;
191 }
192 }
193
194 /**
195 * Uninstalls the malloc hooks, disables leak detection
196 */
197 static void uninstall_hooks()
198 {
199 if (installed)
200 {
201 __malloc_hook = old_malloc_hook;
202 __free_hook = old_free_hook;
203 __realloc_hook = old_realloc_hook;
204 installed = FALSE;
205 }
206 }
207
208 /**
209 * Hook function for malloc()
210 */
211 void *malloc_hook(size_t bytes, const void *caller)
212 {
213 memory_header_t *hdr;
214
215 pthread_mutex_lock(&mutex);
216 uninstall_hooks();
217 hdr = malloc(bytes + sizeof(memory_header_t));
218
219 hdr->magic = MEMORY_HEADER_MAGIC;
220 hdr->bytes = bytes;
221 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
222
223 /* insert at the beginning of the list */
224 hdr->next = first_header.next;
225 if (hdr->next)
226 {
227 hdr->next->previous = hdr;
228 }
229 hdr->previous = &first_header;
230 first_header.next = hdr;
231 install_hooks();
232 pthread_mutex_unlock(&mutex);
233 return hdr + 1;
234 }
235
236 /**
237 * Hook function for free()
238 */
239 void free_hook(void *ptr, const void *caller)
240 {
241 void *stack_frames[STACK_FRAMES_COUNT];
242 int stack_frame_count;
243 memory_header_t *hdr = ptr - sizeof(memory_header_t);
244
245 /* allow freeing of NULL */
246 if (ptr == NULL)
247 {
248 return;
249 }
250
251 pthread_mutex_lock(&mutex);
252 if (hdr->magic != MEMORY_HEADER_MAGIC)
253 {
254 pthread_mutex_unlock(&mutex);
255 /* TODO: since pthread_join cannot be excluded cleanly, we are not whining about bad frees */
256 return;
257 logger->log(logger, ERROR, "freeing of invalid memory (%p)", ptr);
258 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
259 log_stack_frames(stack_frames, stack_frame_count);
260 return;
261 }
262 /* remove magic from hdr */
263 hdr->magic = 0;
264
265 /* remove item from list */
266 if (hdr->next)
267 {
268 hdr->next->previous = hdr->previous;
269 }
270 hdr->previous->next = hdr->next;
271
272 uninstall_hooks();
273 free(hdr);
274 install_hooks();
275 pthread_mutex_unlock(&mutex);
276 }
277
278 /**
279 * Hook function for realloc()
280 */
281 void *realloc_hook(void *old, size_t bytes, const void *caller)
282 {
283 void *new;
284 memory_header_t *hdr = old - sizeof(memory_header_t);
285 void *stack_frames[STACK_FRAMES_COUNT];
286 int stack_frame_count;
287
288 /* allow reallocation of NULL */
289 if (old == NULL)
290 {
291 return malloc_hook(bytes, caller);
292 }
293 if (hdr->magic != MEMORY_HEADER_MAGIC)
294 {
295 logger->log(logger, ERROR, "reallocation of invalid memory (%p)", old);
296 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
297 log_stack_frames(stack_frames, stack_frame_count);
298 kill(getpid(), SIGKILL);
299 return NULL;
300 }
301
302 /* malloc and free is done with hooks */
303 new = malloc_hook(bytes, caller);
304 memcpy(new, old, min(bytes, hdr->bytes));
305 free_hook(old, caller);
306
307 return new;
308 }
309
310
311 /**
312 * Setup leak detective
313 */
314 void leak_detective_init()
315 {
316 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
317 load_excluded_functions();
318 install_hooks();
319 }
320
321 /**
322 * Clean up leak detective
323 */
324 void leak_detective_cleanup()
325 {
326 report_leaks();
327 uninstall_hooks();
328 }
329
330
331 /**
332 * The following glibc functions are excluded from leak detection, since
333 * they use static allocated buffers or other ugly allocation hacks.
334 * For this to work, the linker must link libstrongswan preferred to
335 * the other (overriden) libs.
336 */
337 struct excluded_function {
338 char *lib_name;
339 char *function_name;
340 void *handle;
341 void *lib_function;
342 } excluded_functions[] = {
343 {"libc.so.6", "inet_ntoa", NULL, NULL},
344 {"libpthread.so.0", "pthread_create", NULL, NULL},
345 {"libpthread.so.0", "pthread_cancel", NULL, NULL},
346 {"libpthread.so.0", "pthread_join", NULL, NULL},
347 {"libpthread.so.0", "_pthread_cleanup_push",NULL, NULL},
348 {"libpthread.so.0", "_pthread_cleanup_pop", NULL, NULL},
349 {"libc.so.6", "mktime", NULL, NULL},
350 {"libc.so.6", "vsyslog", NULL, NULL},
351 };
352 #define INET_NTOA 0
353 #define PTHREAD_CREATE 1
354 #define PTHREAD_CANCEL 2
355 #define PTHREAD_JOIN 3
356 #define PTHREAD_CLEANUP_PUSH 4
357 #define PTHREAD_CLEANUP_POP 5
358 #define MKTIME 6
359 #define VSYSLOG 7
360
361
362 /**
363 * Load libraries and function pointers for excluded functions
364 */
365 static void load_excluded_functions()
366 {
367 int i;
368
369 for (i = 0; i < sizeof(excluded_functions)/sizeof(struct excluded_function); i++)
370 {
371 void *handle, *function;
372 handle = dlopen(excluded_functions[i].lib_name, RTLD_LAZY);
373 if (handle == NULL)
374 {
375 kill(getpid(), SIGSEGV);
376 }
377
378 function = dlsym(handle, excluded_functions[i].function_name);
379
380 if (function == NULL)
381 {
382 dlclose(handle);
383 kill(getpid(), SIGSEGV);
384 }
385 excluded_functions[i].handle = handle;
386 excluded_functions[i].lib_function = function;
387 }
388 }
389
390 char *inet_ntoa(struct in_addr in)
391 {
392 char *(*_inet_ntoa)(struct in_addr) = excluded_functions[INET_NTOA].lib_function;
393 char *result;
394
395 pthread_mutex_lock(&mutex);
396 uninstall_hooks();
397
398 result = _inet_ntoa(in);
399
400 install_hooks();
401 pthread_mutex_unlock(&mutex);
402 return result;
403 }
404
405 int pthread_create(pthread_t *__restrict __threadp, __const pthread_attr_t *__restrict __attr,
406 void *(*__start_routine) (void *), void *__restrict __arg)
407 {
408 int (*_pthread_create) (pthread_t *__restrict __threadp,
409 __const pthread_attr_t *__restrict __attr,
410 void *(*__start_routine) (void *),
411 void *__restrict __arg) = excluded_functions[PTHREAD_CREATE].lib_function;
412 int result;
413
414 pthread_mutex_lock(&mutex);
415 uninstall_hooks();
416
417 result = _pthread_create(__threadp, __attr, __start_routine, __arg);
418
419 install_hooks();
420 pthread_mutex_unlock(&mutex);
421 return result;
422 }
423
424
425 int pthread_cancel(pthread_t __th)
426 {
427 int (*_pthread_cancel) (pthread_t) = excluded_functions[PTHREAD_CANCEL].lib_function;
428 int result;
429
430 pthread_mutex_lock(&mutex);
431 uninstall_hooks();
432
433 result = _pthread_cancel(__th);
434
435 install_hooks();
436 pthread_mutex_unlock(&mutex);
437 return result;
438 }
439
440 /* TODO: join has probs, since it dellocates memory
441 * allocated (somewhere) with leak_detective :-(.
442 * We should exclude all pthread_ functions to fix it !?
443 int pthread_join(pthread_t __th, void **__thread_return)
444 {
445 int (*_pthread_join) (pthread_t, void **) = excluded_functions[PTHREAD_JOIN].lib_function;
446 int result;
447
448 pthread_mutex_lock(&mutex);
449 uninstall_hooks();
450
451 result = _pthread_join(__th, __thread_return);
452
453 install_hooks();
454 pthread_mutex_unlock(&mutex);
455 return result;
456 }
457
458 void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer,
459 void (*__routine) (void *),
460 void *__arg)
461 {
462 int (*__pthread_cleanup_push) (struct _pthread_cleanup_buffer *__buffer,
463 void (*__routine) (void *),
464 void *__arg) =
465 excluded_functions[PTHREAD_CLEANUP_PUSH].lib_function;
466
467 pthread_mutex_lock(&mutex);
468 uninstall_hooks();
469
470 __pthread_cleanup_push(__buffer, __routine, __arg);
471
472 install_hooks();
473 pthread_mutex_unlock(&mutex);
474 return;
475 }
476
477 void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer, int __execute)
478 {
479 int (*__pthread_cleanup_pop) (struct _pthread_cleanup_buffer *__buffer, int __execute) =
480 excluded_functions[PTHREAD_CLEANUP_POP].lib_function;
481
482 pthread_mutex_lock(&mutex);
483 uninstall_hooks();
484
485 __pthread_cleanup_pop(__buffer, __execute);
486
487 install_hooks();
488 pthread_mutex_unlock(&mutex);
489 return;
490 }*/
491
492 time_t mktime(struct tm *tm)
493 {
494 time_t (*_mktime)(struct tm *tm) = excluded_functions[MKTIME].lib_function;
495 time_t result;
496
497 pthread_mutex_lock(&mutex);
498 uninstall_hooks();
499
500 result = _mktime(tm);
501
502 install_hooks();
503 pthread_mutex_unlock(&mutex);
504 return result;
505 }
506
507 void vsyslog (int __pri, __const char *__fmt, __gnuc_va_list __ap)
508 {
509 void (*_vsyslog) (int __pri, __const char *__fmt, __gnuc_va_list __ap) = excluded_functions[VSYSLOG].lib_function;
510
511 pthread_mutex_lock(&mutex);
512 uninstall_hooks();
513
514 _vsyslog(__pri, __fmt, __ap);
515
516 install_hooks();
517 pthread_mutex_unlock(&mutex);
518 return;
519 }
520
521 #endif /* LEAK_DETECTION */