- library initialization done at a central point (library.c)
[strongswan.git] / Source / lib / utils / leak_detective.c
1 /**
2 * @file leak_detective.c
3 *
4 * @brief Implementation of leak_detective_t.
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 #define __USE_GNU /* needed for recursiv mutex initializer */
35 #include <pthread.h>
36
37 #include "leak_detective.h"
38
39 #include <types.h>
40 #include <utils/logger_manager.h>
41
42 #ifdef LEAK_DETECTIVE
43
44 /**
45 * Magic value which helps to detect memory corruption
46 */
47 #define MEMORY_HEADER_MAGIC 0xF1367ADF
48
49 static void install_hooks(void);
50 static void uninstall_hooks(void);
51 static void *malloc_hook(size_t, const void *);
52 static void *realloc_hook(void *, size_t, const void *);
53 static void free_hook(void*, const void *);
54 static void load_excluded_functions();
55
56 typedef struct memory_header_t memory_header_t;
57
58 /**
59 * Header which is prepended to each allocated memory block
60 */
61 struct memory_header_t {
62 /**
63 * Magci byte which must(!) hold MEMORY_HEADER_MAGIC
64 */
65 u_int32_t magic;
66
67 /**
68 * Number of bytes following after the header
69 */
70 size_t bytes;
71
72 /**
73 * Stack frames at the time of allocation
74 */
75 void *stack_frames[STACK_FRAMES_COUNT];
76
77 /**
78 * Number of stacks frames obtained in stack_frames
79 */
80 int stack_frame_count;
81
82 /**
83 * Pointer to previous entry in linked list
84 */
85 memory_header_t *previous;
86
87 /**
88 * Pointer to next entry in linked list
89 */
90 memory_header_t *next;
91 };
92
93 /**
94 * first mem header is just a dummy to chain
95 * the others on it...
96 */
97 static memory_header_t first_header = {
98 magic: MEMORY_HEADER_MAGIC,
99 bytes: 0,
100 stack_frame_count: 0,
101 previous: NULL,
102 next: NULL
103 };
104
105 /**
106 * logger for the leak detective
107 */
108 static logger_t *logger;
109
110 /**
111 * standard hooks, used to temparily remove hooking
112 */
113 static void *old_malloc_hook, *old_realloc_hook, *old_free_hook;
114
115 /**
116 * are the hooks currently installed?
117 */
118 static bool installed = FALSE;
119
120 /**
121 * Mutex to exclusivly uninstall hooks, access heap list
122 */
123 static pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
124
125
126
127 /**
128 * log stack frames queried by backtrace()
129 * TODO: Dump symbols of static functions!!!
130 */
131 static void log_stack_frames(void **stack_frames, int stack_frame_count)
132 {
133 char **strings;
134 size_t i;
135
136 strings = backtrace_symbols (stack_frames, stack_frame_count);
137
138 logger->log(logger, ERROR, " dumping %d stack frame addresses.", stack_frame_count);
139
140 for (i = 0; i < stack_frame_count; i++)
141 {
142 logger->log(logger, ERROR, " %s", strings[i]);
143 }
144 free (strings);
145 }
146
147 /**
148 * Report leaks at library destruction
149 */
150 void report_leaks()
151 {
152 memory_header_t *hdr;
153 int leaks = 0;
154
155 /* reaquire a logger is necessary, this will force ((destructor))
156 * order to work correctly */
157 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
158 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
159 {
160 logger->log(logger, ERROR, "Leak (%d bytes at %p)", hdr->bytes, hdr + 1);
161 log_stack_frames(hdr->stack_frames, hdr->stack_frame_count);
162 leaks++;
163 }
164
165 switch (leaks)
166 {
167 case 0:
168 logger->log(logger, CONTROL, "No leaks detected");
169 break;
170 case 1:
171 logger->log(logger, ERROR, "One leak detected");
172 break;
173 default:
174 logger->log(logger, ERROR, "%d leaks detected", leaks);
175 break;
176 }
177 }
178
179 /**
180 * Installs the malloc hooks, enables leak detection
181 */
182 static void install_hooks()
183 {
184 if (!installed)
185 {
186 old_malloc_hook = __malloc_hook;
187 old_realloc_hook = __realloc_hook;
188 old_free_hook = __free_hook;
189 __malloc_hook = malloc_hook;
190 __realloc_hook = realloc_hook;
191 __free_hook = free_hook;
192 installed = TRUE;
193 }
194 }
195
196 /**
197 * Uninstalls the malloc hooks, disables leak detection
198 */
199 static void uninstall_hooks()
200 {
201 if (installed)
202 {
203 __malloc_hook = old_malloc_hook;
204 __free_hook = old_free_hook;
205 __realloc_hook = old_realloc_hook;
206 installed = FALSE;
207 }
208 }
209
210 /**
211 * Hook function for malloc()
212 */
213 void *malloc_hook(size_t bytes, const void *caller)
214 {
215 memory_header_t *hdr;
216
217 pthread_mutex_lock(&mutex);
218 uninstall_hooks();
219 hdr = malloc(bytes + sizeof(memory_header_t));
220
221 hdr->magic = MEMORY_HEADER_MAGIC;
222 hdr->bytes = bytes;
223 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
224
225 /* insert at the beginning of the list */
226 hdr->next = first_header.next;
227 if (hdr->next)
228 {
229 hdr->next->previous = hdr;
230 }
231 hdr->previous = &first_header;
232 first_header.next = hdr;
233 install_hooks();
234 pthread_mutex_unlock(&mutex);
235 return hdr + 1;
236 }
237
238 /**
239 * Hook function for free()
240 */
241 void free_hook(void *ptr, const void *caller)
242 {
243 void *stack_frames[STACK_FRAMES_COUNT];
244 int stack_frame_count;
245 memory_header_t *hdr = ptr - sizeof(memory_header_t);
246
247 /* allow freeing of NULL */
248 if (ptr == NULL)
249 {
250 return;
251 }
252
253 pthread_mutex_lock(&mutex);
254 if (hdr->magic != MEMORY_HEADER_MAGIC)
255 {
256 pthread_mutex_unlock(&mutex);
257 /* TODO: since pthread_join cannot be excluded cleanly, we are not whining about bad frees */
258 return;
259 logger->log(logger, ERROR, "freeing of invalid memory (%p)", ptr);
260 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
261 log_stack_frames(stack_frames, stack_frame_count);
262 return;
263 }
264 /* remove magic from hdr */
265 hdr->magic = 0;
266
267 /* remove item from list */
268 if (hdr->next)
269 {
270 hdr->next->previous = hdr->previous;
271 }
272 hdr->previous->next = hdr->next;
273
274 uninstall_hooks();
275 free(hdr);
276 install_hooks();
277 pthread_mutex_unlock(&mutex);
278 }
279
280 /**
281 * Hook function for realloc()
282 */
283 void *realloc_hook(void *old, size_t bytes, const void *caller)
284 {
285 void *new;
286 memory_header_t *hdr = old - sizeof(memory_header_t);
287 void *stack_frames[STACK_FRAMES_COUNT];
288 int stack_frame_count;
289
290 /* allow reallocation of NULL */
291 if (old == NULL)
292 {
293 return malloc_hook(bytes, caller);
294 }
295 if (hdr->magic != MEMORY_HEADER_MAGIC)
296 {
297 logger->log(logger, ERROR, "reallocation of invalid memory (%p)", old);
298 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
299 log_stack_frames(stack_frames, stack_frame_count);
300 kill(getpid(), SIGKILL);
301 return NULL;
302 }
303
304 /* malloc and free is done with hooks */
305 new = malloc_hook(bytes, caller);
306 memcpy(new, old, min(bytes, hdr->bytes));
307 free_hook(old, caller);
308
309 return new;
310 }
311
312
313 /**
314 * Setup leak detective
315 */
316 void leak_detective_init()
317 {
318 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
319 load_excluded_functions();
320 install_hooks();
321 }
322
323 /**
324 * Clean up leak detective
325 */
326 void leak_detective_cleanup()
327 {
328 report_leaks();
329 uninstall_hooks();
330 }
331
332
333 /**
334 * The following glibc functions are excluded from leak detection, since
335 * they use static allocated buffers or other ugly allocation hacks.
336 * For this to work, the linker must link libstrongswan preferred to
337 * the other (overriden) libs.
338 */
339 struct excluded_function {
340 char *lib_name;
341 char *function_name;
342 void *handle;
343 void *lib_function;
344 } excluded_functions[] = {
345 {"libc.so.6", "inet_ntoa", NULL, NULL},
346 {"libpthread.so.0", "pthread_create", NULL, NULL},
347 {"libpthread.so.0", "pthread_cancel", NULL, NULL},
348 {"libpthread.so.0", "pthread_join", NULL, NULL},
349 {"libpthread.so.0", "_pthread_cleanup_push",NULL, NULL},
350 {"libpthread.so.0", "_pthread_cleanup_pop", NULL, NULL},
351 {"libc.so.6", "mktime", NULL, NULL},
352 {"libc.so.6", "vsyslog", NULL, NULL},
353 };
354 #define INET_NTOA 0
355 #define PTHREAD_CREATE 1
356 #define PTHREAD_CANCEL 2
357 #define PTHREAD_JOIN 3
358 #define PTHREAD_CLEANUP_PUSH 4
359 #define PTHREAD_CLEANUP_POP 5
360 #define MKTIME 6
361 #define VSYSLOG 7
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 #endif /* LEAK_DETECTION */