a6a5c9a9187ab88493c5bf8e2b1177b4f6737903
[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 uninstall_hooks();
327 report_leaks();
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 {"libc.so.6", "strerror", NULL, NULL},
352 };
353 #define INET_NTOA 0
354 #define PTHREAD_CREATE 1
355 #define PTHREAD_CANCEL 2
356 #define PTHREAD_JOIN 3
357 #define PTHREAD_CLEANUP_PUSH 4
358 #define PTHREAD_CLEANUP_POP 5
359 #define MKTIME 6
360 #define VSYSLOG 7
361 #define STRERROR 8
362
363
364 /**
365 * Load libraries and function pointers for excluded functions
366 */
367 static void load_excluded_functions()
368 {
369 int i;
370
371 for (i = 0; i < sizeof(excluded_functions)/sizeof(struct excluded_function); i++)
372 {
373 void *handle, *function;
374 handle = dlopen(excluded_functions[i].lib_name, RTLD_LAZY);
375 if (handle == NULL)
376 {
377 kill(getpid(), SIGSEGV);
378 }
379
380 function = dlsym(handle, excluded_functions[i].function_name);
381
382 if (function == NULL)
383 {
384 dlclose(handle);
385 kill(getpid(), SIGSEGV);
386 }
387 excluded_functions[i].handle = handle;
388 excluded_functions[i].lib_function = function;
389 }
390 }
391
392 char *inet_ntoa(struct in_addr in)
393 {
394 char *(*_inet_ntoa)(struct in_addr) = excluded_functions[INET_NTOA].lib_function;
395 char *result;
396
397 pthread_mutex_lock(&mutex);
398 uninstall_hooks();
399
400 result = _inet_ntoa(in);
401
402 install_hooks();
403 pthread_mutex_unlock(&mutex);
404 return result;
405 }
406
407 // int pthread_create(pthread_t *__restrict __threadp, __const pthread_attr_t *__restrict __attr,
408 // void *(*__start_routine) (void *), void *__restrict __arg)
409 // {
410 // int (*_pthread_create) (pthread_t *__restrict __threadp,
411 // __const pthread_attr_t *__restrict __attr,
412 // void *(*__start_routine) (void *),
413 // void *__restrict __arg) = excluded_functions[PTHREAD_CREATE].lib_function;
414 // int result;
415 //
416 // pthread_mutex_lock(&mutex);
417 // uninstall_hooks();
418 //
419 // result = _pthread_create(__threadp, __attr, __start_routine, __arg);
420 //
421 // install_hooks();
422 // pthread_mutex_unlock(&mutex);
423 // return result;
424 // }
425 //
426 //
427 // int pthread_cancel(pthread_t __th)
428 // {
429 // int (*_pthread_cancel) (pthread_t) = excluded_functions[PTHREAD_CANCEL].lib_function;
430 // int result;
431 //
432 // pthread_mutex_lock(&mutex);
433 // uninstall_hooks();
434 //
435 // result = _pthread_cancel(__th);
436 //
437 // install_hooks();
438 // pthread_mutex_unlock(&mutex);
439 // return result;
440 // }
441 //
442 // /* TODO: join has probs, since it dellocates memory
443 // * allocated (somewhere) with leak_detective :-(.
444 // * We should exclude all pthread_ functions to fix it !? */
445 // int pthread_join(pthread_t __th, void **__thread_return)
446 // {
447 // int (*_pthread_join) (pthread_t, void **) = excluded_functions[PTHREAD_JOIN].lib_function;
448 // int result;
449 //
450 // pthread_mutex_lock(&mutex);
451 // uninstall_hooks();
452 //
453 // result = _pthread_join(__th, __thread_return);
454 //
455 // install_hooks();
456 // pthread_mutex_unlock(&mutex);
457 // return result;
458 // }
459 //
460 // void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer,
461 // void (*__routine) (void *),
462 // void *__arg)
463 // {
464 // int (*__pthread_cleanup_push) (struct _pthread_cleanup_buffer *__buffer,
465 // void (*__routine) (void *),
466 // void *__arg) =
467 // excluded_functions[PTHREAD_CLEANUP_PUSH].lib_function;
468 //
469 // pthread_mutex_lock(&mutex);
470 // uninstall_hooks();
471 //
472 // __pthread_cleanup_push(__buffer, __routine, __arg);
473 //
474 // install_hooks();
475 // pthread_mutex_unlock(&mutex);
476 // return;
477 // }
478 //
479 // void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer, int __execute)
480 // {
481 // int (*__pthread_cleanup_pop) (struct _pthread_cleanup_buffer *__buffer, int __execute) =
482 // excluded_functions[PTHREAD_CLEANUP_POP].lib_function;
483 //
484 // pthread_mutex_lock(&mutex);
485 // uninstall_hooks();
486 //
487 // __pthread_cleanup_pop(__buffer, __execute);
488 //
489 // install_hooks();
490 // pthread_mutex_unlock(&mutex);
491 // return;
492 // }
493
494 time_t mktime(struct tm *tm)
495 {
496 time_t (*_mktime)(struct tm *tm) = excluded_functions[MKTIME].lib_function;
497 time_t result;
498
499 pthread_mutex_lock(&mutex);
500 uninstall_hooks();
501
502 result = _mktime(tm);
503
504 install_hooks();
505 pthread_mutex_unlock(&mutex);
506 return result;
507 }
508
509 void vsyslog (int __pri, __const char *__fmt, __gnuc_va_list __ap)
510 {
511 void (*_vsyslog) (int __pri, __const char *__fmt, __gnuc_va_list __ap) = excluded_functions[VSYSLOG].lib_function;
512
513 pthread_mutex_lock(&mutex);
514 uninstall_hooks();
515
516 _vsyslog(__pri, __fmt, __ap);
517
518 install_hooks();
519 pthread_mutex_unlock(&mutex);
520 return;
521 }
522
523
524
525 char *strerror(int errnum)
526 {
527 char* (*_strerror) (int) = excluded_functions[STRERROR].lib_function;
528 char *result;
529
530 pthread_mutex_lock(&mutex);
531 uninstall_hooks();
532
533 result = _strerror(errnum);
534
535 install_hooks();
536 pthread_mutex_unlock(&mutex);
537 return result;
538 }
539
540 #endif /* LEAK_DETECTION */