2 * @file leak_detective.c
4 * @brief Allocation hooks to find memory leaks.
8 * Copyright (C) 2006 Martin Willi
9 * Hochschule fuer Technik Rapperswil
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>.
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
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
36 #include "leak_detective.h"
39 #include <utils/logger_manager.h>
44 * Magic value which helps to detect memory corruption
46 #define MEMORY_HEADER_MAGIC 0xF1367ADF
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();
55 typedef struct memory_header_t memory_header_t
;
58 * Header which is prepended to each allocated memory block
60 struct memory_header_t
{
62 * Magci byte which must(!) hold MEMORY_HEADER_MAGIC
67 * Number of bytes following after the header
72 * Stack frames at the time of allocation
74 void *stack_frames
[STACK_FRAMES_COUNT
];
77 * Number of stacks frames obtained in stack_frames
79 int stack_frame_count
;
82 * Pointer to previous entry in linked list
84 memory_header_t
*previous
;
87 * Pointer to next entry in linked list
89 memory_header_t
*next
;
93 * first mem header is just a dummy to chain
96 static memory_header_t first_header
= {
97 magic
: MEMORY_HEADER_MAGIC
,
105 * logger for the leak detective
107 static logger_t
*logger
;
110 * standard hooks, used to temparily remove hooking
112 static void *old_malloc_hook
, *old_realloc_hook
, *old_free_hook
;
115 * are the hooks currently installed?
117 static bool installed
= FALSE
;
120 * Mutex to exclusivly uninstall hooks, access heap list
122 static pthread_mutex_t mutex
= PTHREAD_MUTEX_INITIALIZER
;
126 * log stack frames queried by backtrace()
127 * TODO: Dump symbols of static functions!!!
129 static void log_stack_frames(void **stack_frames
, int stack_frame_count
)
134 strings
= backtrace_symbols (stack_frames
, stack_frame_count
);
136 logger
->log(logger
, ERROR
, " dumping %d stack frame addresses.", stack_frame_count
);
138 for (i
= 0; i
< stack_frame_count
; i
++)
140 logger
->log(logger
, ERROR
, " %s", strings
[i
]);
146 * Report leaks at library destruction
150 memory_header_t
*hdr
;
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
)
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
);
166 logger
->log(logger
, CONTROL
, "No leaks detected");
169 logger
->log(logger
, ERROR
, "One leak detected");
172 logger
->log(logger
, ERROR
, "%d leaks detected", leaks
);
178 * Installs the malloc hooks, enables leak detection
180 static void install_hooks()
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
;
195 * Uninstalls the malloc hooks, disables leak detection
197 static void uninstall_hooks()
201 __malloc_hook
= old_malloc_hook
;
202 __free_hook
= old_free_hook
;
203 __realloc_hook
= old_realloc_hook
;
209 * Hook function for malloc()
211 void *malloc_hook(size_t bytes
, const void *caller
)
213 memory_header_t
*hdr
;
215 pthread_mutex_lock(&mutex
);
217 hdr
= malloc(bytes
+ sizeof(memory_header_t
));
219 hdr
->magic
= MEMORY_HEADER_MAGIC
;
221 hdr
->stack_frame_count
= backtrace(hdr
->stack_frames
, STACK_FRAMES_COUNT
);
223 /* insert at the beginning of the list */
224 hdr
->next
= first_header
.next
;
227 hdr
->next
->previous
= hdr
;
229 hdr
->previous
= &first_header
;
230 first_header
.next
= hdr
;
232 pthread_mutex_unlock(&mutex
);
237 * Hook function for free()
239 void free_hook(void *ptr
, const void *caller
)
241 void *stack_frames
[STACK_FRAMES_COUNT
];
242 int stack_frame_count
;
243 memory_header_t
*hdr
= ptr
- sizeof(memory_header_t
);
245 /* allow freeing of NULL */
251 pthread_mutex_lock(&mutex
);
252 if (hdr
->magic
!= MEMORY_HEADER_MAGIC
)
254 pthread_mutex_unlock(&mutex
);
255 /* TODO: since pthread_join cannot be excluded cleanly, we are not whining about bad frees */
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
);
262 /* remove magic from hdr */
265 /* remove item from list */
268 hdr
->next
->previous
= hdr
->previous
;
270 hdr
->previous
->next
= hdr
->next
;
275 pthread_mutex_unlock(&mutex
);
279 * Hook function for realloc()
281 void *realloc_hook(void *old
, size_t bytes
, const void *caller
)
284 memory_header_t
*hdr
= old
- sizeof(memory_header_t
);
285 void *stack_frames
[STACK_FRAMES_COUNT
];
286 int stack_frame_count
;
288 /* allow reallocation of NULL */
291 return malloc_hook(bytes
, caller
);
293 if (hdr
->magic
!= MEMORY_HEADER_MAGIC
)
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
);
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
);
312 * Setup leak detective
314 void leak_detective_init()
316 logger
= logger_manager
->get_logger(logger_manager
, LEAK_DETECT
);
317 load_excluded_functions();
322 * Clean up leak detective
324 void leak_detective_cleanup()
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.
337 struct excluded_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
},
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
365 * Load libraries and function pointers for excluded functions
367 static void load_excluded_functions()
371 for (i
= 0; i
< sizeof(excluded_functions
)/sizeof(struct excluded_function
); i
++)
373 void *handle
, *function
;
374 handle
= dlopen(excluded_functions
[i
].lib_name
, RTLD_LAZY
);
377 kill(getpid(), SIGSEGV
);
380 function
= dlsym(handle
, excluded_functions
[i
].function_name
);
382 if (function
== NULL
)
385 kill(getpid(), SIGSEGV
);
387 excluded_functions
[i
].handle
= handle
;
388 excluded_functions
[i
].lib_function
= function
;
392 char *inet_ntoa(struct in_addr in
)
394 char *(*_inet_ntoa
)(struct in_addr
) = excluded_functions
[INET_NTOA
].lib_function
;
397 pthread_mutex_lock(&mutex
);
400 result
= _inet_ntoa(in
);
403 pthread_mutex_unlock(&mutex
);
407 int pthread_create(pthread_t
*__restrict __threadp
, __const pthread_attr_t
*__restrict __attr
,
408 void *(*__start_routine
) (void *), void *__restrict __arg
)
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
;
416 pthread_mutex_lock(&mutex
);
419 result
= _pthread_create(__threadp
, __attr
, __start_routine
, __arg
);
422 pthread_mutex_unlock(&mutex
);
427 int pthread_cancel(pthread_t __th
)
429 int (*_pthread_cancel
) (pthread_t
) = excluded_functions
[PTHREAD_CANCEL
].lib_function
;
432 pthread_mutex_lock(&mutex
);
435 result
= _pthread_cancel(__th
);
438 pthread_mutex_unlock(&mutex
);
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)
447 // int (*_pthread_join) (pthread_t, void **) = excluded_functions[PTHREAD_JOIN].lib_function;
450 // pthread_mutex_lock(&mutex);
451 // uninstall_hooks();
453 // result = _pthread_join(__th, __thread_return);
456 // pthread_mutex_unlock(&mutex);
460 // void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer,
461 // void (*__routine) (void *),
464 // int (*__pthread_cleanup_push) (struct _pthread_cleanup_buffer *__buffer,
465 // void (*__routine) (void *),
467 // excluded_functions[PTHREAD_CLEANUP_PUSH].lib_function;
469 // pthread_mutex_lock(&mutex);
470 // uninstall_hooks();
472 // __pthread_cleanup_push(__buffer, __routine, __arg);
475 // pthread_mutex_unlock(&mutex);
479 // void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer, int __execute)
481 // int (*__pthread_cleanup_pop) (struct _pthread_cleanup_buffer *__buffer, int __execute) =
482 // excluded_functions[PTHREAD_CLEANUP_POP].lib_function;
484 // pthread_mutex_lock(&mutex);
485 // uninstall_hooks();
487 // __pthread_cleanup_pop(__buffer, __execute);
490 // pthread_mutex_unlock(&mutex);
494 time_t mktime(struct tm
*tm
)
496 time_t (*_mktime
)(struct tm
*tm
) = excluded_functions
[MKTIME
].lib_function
;
499 pthread_mutex_lock(&mutex
);
502 result
= _mktime(tm
);
505 pthread_mutex_unlock(&mutex
);
509 void vsyslog (int __pri
, __const
char *__fmt
, __gnuc_va_list __ap
)
511 void (*_vsyslog
) (int __pri
, __const
char *__fmt
, __gnuc_va_list __ap
) = excluded_functions
[VSYSLOG
].lib_function
;
513 pthread_mutex_lock(&mutex
);
516 _vsyslog(__pri
, __fmt
, __ap
);
519 pthread_mutex_unlock(&mutex
);
525 char *strerror(int errnum
)
527 char* (*_strerror
) (int) = excluded_functions
[STRERROR
].lib_function
;
530 pthread_mutex_lock(&mutex
);
533 result
= _strerror(errnum
);
536 pthread_mutex_unlock(&mutex
);
540 #endif /* LEAK_DETECTION */