proper leak detective hook for realloc
[strongswan.git] / src / libstrongswan / 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 memory_header_t *hdr;
284 void *stack_frames[STACK_FRAMES_COUNT];
285 int stack_frame_count;
286
287 /* allow reallocation of NULL */
288 if (old == NULL)
289 {
290 return malloc_hook(bytes, caller);
291 }
292
293 hdr = old - sizeof(memory_header_t);
294 pthread_mutex_lock(&mutex);
295 uninstall_hooks();
296 if (hdr->magic != MEMORY_HEADER_MAGIC)
297 {
298 logger->log(logger, ERROR, "reallocation of invalid memory (%p)", old);
299 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
300 log_stack_frames(stack_frames, stack_frame_count);
301 kill(getpid(), SIGKILL);
302 return NULL;
303 }
304
305 hdr = realloc(hdr, bytes + sizeof(memory_header_t));
306
307 /* update statistics */
308 hdr->bytes = bytes;
309 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
310
311 /* update header of linked list neighbours */
312 if (hdr->next)
313 {
314 hdr->next->previous = hdr;
315 }
316 hdr->previous->next = hdr;
317 install_hooks();
318 pthread_mutex_unlock(&mutex);
319 return hdr + 1;
320 }
321
322 /**
323 * Setup leak detective
324 */
325 void leak_detective_init()
326 {
327 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
328 load_excluded_functions();
329 install_hooks();
330 }
331
332 /**
333 * Clean up leak detective
334 */
335 void leak_detective_cleanup()
336 {
337 uninstall_hooks();
338 report_leaks();
339 }
340
341
342 /**
343 * The following glibc functions are excluded from leak detection, since
344 * they use static allocated buffers or other ugly allocation hacks.
345 * For this to work, the linker must link libstrongswan preferred to
346 * the other (overriden) libs.
347 */
348 struct excluded_function {
349 char *lib_name;
350 char *function_name;
351 void *handle;
352 void *lib_function;
353 } excluded_functions[] = {
354 {"libc.so.6", "inet_ntoa", NULL, NULL},
355 {"libpthread.so.0", "pthread_create", NULL, NULL},
356 {"libpthread.so.0", "pthread_cancel", NULL, NULL},
357 {"libpthread.so.0", "pthread_join", NULL, NULL},
358 {"libpthread.so.0", "_pthread_cleanup_push",NULL, NULL},
359 {"libpthread.so.0", "_pthread_cleanup_pop", NULL, NULL},
360 {"libc.so.6", "mktime", NULL, NULL},
361 {"libc.so.6", "vsyslog", NULL, NULL},
362 {"libc.so.6", "strerror", NULL, NULL},
363 {"libpthread.so.0", "pthread_setspecific", NULL, NULL},
364 };
365 #define INET_NTOA 0
366 #define PTHREAD_CREATE 1
367 #define PTHREAD_CANCEL 2
368 #define PTHREAD_JOIN 3
369 #define PTHREAD_CLEANUP_PUSH 4
370 #define PTHREAD_CLEANUP_POP 5
371 #define MKTIME 6
372 #define VSYSLOG 7
373 #define STRERROR 8
374 #define PTHREAD_SETSPECIFIC 9
375
376 /**
377 * Load libraries and function pointers for excluded functions
378 */
379 static void load_excluded_functions()
380 {
381 int i;
382
383 for (i = 0; i < sizeof(excluded_functions)/sizeof(struct excluded_function); i++)
384 {
385 void *handle, *function;
386 handle = dlopen(excluded_functions[i].lib_name, RTLD_LAZY);
387 if (handle == NULL)
388 {
389 kill(getpid(), SIGSEGV);
390 }
391
392 function = dlsym(handle, excluded_functions[i].function_name);
393
394 if (function == NULL)
395 {
396 dlclose(handle);
397 kill(getpid(), SIGSEGV);
398 }
399 excluded_functions[i].handle = handle;
400 excluded_functions[i].lib_function = function;
401 }
402 }
403
404 char *inet_ntoa(struct in_addr in)
405 {
406 char *(*_inet_ntoa)(struct in_addr) = excluded_functions[INET_NTOA].lib_function;
407 char *result;
408
409 pthread_mutex_lock(&mutex);
410 uninstall_hooks();
411
412 result = _inet_ntoa(in);
413
414 install_hooks();
415 pthread_mutex_unlock(&mutex);
416 return result;
417 }
418
419 int pthread_create(pthread_t *__restrict __threadp, __const pthread_attr_t *__restrict __attr,
420 void *(*__start_routine) (void *), void *__restrict __arg)
421 {
422 int (*_pthread_create) (pthread_t *__restrict __threadp,
423 __const pthread_attr_t *__restrict __attr,
424 void *(*__start_routine) (void *),
425 void *__restrict __arg) = excluded_functions[PTHREAD_CREATE].lib_function;
426 int result;
427
428 pthread_mutex_lock(&mutex);
429 uninstall_hooks();
430
431 result = _pthread_create(__threadp, __attr, __start_routine, __arg);
432
433 install_hooks();
434 pthread_mutex_unlock(&mutex);
435 return result;
436 }
437
438
439 int pthread_cancel(pthread_t __th)
440 {
441 int (*_pthread_cancel) (pthread_t) = excluded_functions[PTHREAD_CANCEL].lib_function;
442 int result;
443
444 pthread_mutex_lock(&mutex);
445 uninstall_hooks();
446
447 result = _pthread_cancel(__th);
448
449 install_hooks();
450 pthread_mutex_unlock(&mutex);
451 return result;
452 }
453
454 int pthread_setspecific(pthread_key_t __key, __const void *__pointer)
455 {
456 int (*_pthread_setspecific) (pthread_key_t,__const void*) = excluded_functions[PTHREAD_SETSPECIFIC].lib_function;
457 int result;
458
459 pthread_mutex_lock(&mutex);
460 uninstall_hooks();
461
462 result = _pthread_setspecific(__key, __pointer);
463
464 install_hooks();
465 pthread_mutex_unlock(&mutex);
466 return result;
467 }
468
469 // /* TODO: join has probs, since it dellocates memory
470 // * allocated (somewhere) with leak_detective :-(.
471 // * We should exclude all pthread_ functions to fix it !? */
472 // int pthread_join(pthread_t __th, void **__thread_return)
473 // {
474 // int (*_pthread_join) (pthread_t, void **) = excluded_functions[PTHREAD_JOIN].lib_function;
475 // int result;
476 //
477 // pthread_mutex_lock(&mutex);
478 // uninstall_hooks();
479 //
480 // result = _pthread_join(__th, __thread_return);
481 //
482 // install_hooks();
483 // pthread_mutex_unlock(&mutex);
484 // return result;
485 // }
486 //
487 // void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer,
488 // void (*__routine) (void *),
489 // void *__arg)
490 // {
491 // int (*__pthread_cleanup_push) (struct _pthread_cleanup_buffer *__buffer,
492 // void (*__routine) (void *),
493 // void *__arg) =
494 // excluded_functions[PTHREAD_CLEANUP_PUSH].lib_function;
495 //
496 // pthread_mutex_lock(&mutex);
497 // uninstall_hooks();
498 //
499 // __pthread_cleanup_push(__buffer, __routine, __arg);
500 //
501 // install_hooks();
502 // pthread_mutex_unlock(&mutex);
503 // return;
504 // }
505 //
506 // void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer, int __execute)
507 // {
508 // int (*__pthread_cleanup_pop) (struct _pthread_cleanup_buffer *__buffer, int __execute) =
509 // excluded_functions[PTHREAD_CLEANUP_POP].lib_function;
510 //
511 // pthread_mutex_lock(&mutex);
512 // uninstall_hooks();
513 //
514 // __pthread_cleanup_pop(__buffer, __execute);
515 //
516 // install_hooks();
517 // pthread_mutex_unlock(&mutex);
518 // return;
519 // }
520
521 time_t mktime(struct tm *tm)
522 {
523 time_t (*_mktime)(struct tm *tm) = excluded_functions[MKTIME].lib_function;
524 time_t result;
525
526 pthread_mutex_lock(&mutex);
527 uninstall_hooks();
528
529 result = _mktime(tm);
530
531 install_hooks();
532 pthread_mutex_unlock(&mutex);
533 return result;
534 }
535
536 void vsyslog (int __pri, __const char *__fmt, __gnuc_va_list __ap)
537 {
538 void (*_vsyslog) (int __pri, __const char *__fmt, __gnuc_va_list __ap) = excluded_functions[VSYSLOG].lib_function;
539
540 pthread_mutex_lock(&mutex);
541 uninstall_hooks();
542
543 _vsyslog(__pri, __fmt, __ap);
544
545 install_hooks();
546 pthread_mutex_unlock(&mutex);
547 return;
548 }
549
550
551
552 char *strerror(int errnum)
553 {
554 char* (*_strerror) (int) = excluded_functions[STRERROR].lib_function;
555 char *result;
556
557 pthread_mutex_lock(&mutex);
558 uninstall_hooks();
559
560 result = _strerror(errnum);
561
562 install_hooks();
563 pthread_mutex_unlock(&mutex);
564 return result;
565 }
566
567 #endif /* LEAK_DETECTION */