- fixed log-to-syslog behavior
[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
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 /**
50 * logger for the leak detective
51 */
52 logger_t *logger;
53
54 static void install_hooks(void);
55 static void uninstall_hooks(void);
56 static void *malloc_hook(size_t, const void *);
57 static void *realloc_hook(void *, size_t, const void *);
58 static void free_hook(void*, const void *);
59
60 typedef struct memory_header_t memory_header_t;
61
62 /**
63 * Header which is prepended to each allocated memory block
64 */
65 struct memory_header_t {
66 /**
67 * Magci byte which must(!) hold MEMORY_HEADER_MAGIC
68 */
69 u_int32_t magic;
70
71 /**
72 * Number of bytes following after the header
73 */
74 size_t bytes;
75
76 /**
77 * Stack frames at the time of allocation
78 */
79 void *stack_frames[STACK_FRAMES_COUNT];
80
81 /**
82 * Number of stacks frames obtained in stack_frames
83 */
84 int stack_frame_count;
85
86 /**
87 * Pointer to previous entry in linked list
88 */
89 memory_header_t *previous;
90
91 /**
92 * Pointer to next entry in linked list
93 */
94 memory_header_t *next;
95 };
96
97 /**
98 * first mem header is just a dummy to chain
99 * the others on it...
100 */
101 memory_header_t first_header = {
102 magic: MEMORY_HEADER_MAGIC,
103 bytes: 0,
104 stack_frame_count: 0,
105 previous: NULL,
106 next: NULL
107 };
108
109 /**
110 * standard hooks, used to temparily remove hooking
111 */
112 void *old_malloc_hook, *old_realloc_hook, *old_free_hook;
113 static bool installed = FALSE;
114
115 /**
116 * Mutex to exclusivly uninstall hooks, access heap list
117 */
118 pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
119
120 /**
121 * Setup leak detective at malloc initialization
122 */
123 void setup_leak_detective()
124 {
125 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
126 install_hooks();
127 }
128 void (*__malloc_initialize_hook) (void) = setup_leak_detective;
129
130 /**
131 * log stack frames queried by backtrace()
132 * TODO: Dump symbols of static functions!!!
133 */
134 void log_stack_frames(void **stack_frames, int stack_frame_count)
135 {
136 char **strings;
137 size_t i;
138
139 strings = backtrace_symbols (stack_frames, stack_frame_count);
140
141 logger->log(logger, ERROR, " dumping %d stack frame addresses.", stack_frame_count);
142
143 for (i = 0; i < stack_frame_count; i++)
144 {
145 logger->log(logger, ERROR, " %s", strings[i]);
146 }
147 free (strings);
148 }
149
150 /**
151 * Installs the malloc hooks, enables leak detection
152 */
153 void install_hooks()
154 {
155 if (!installed)
156 {
157 old_malloc_hook = __malloc_hook;
158 old_realloc_hook = __realloc_hook;
159 old_free_hook = __free_hook;
160 __malloc_hook = malloc_hook;
161 __realloc_hook = realloc_hook;
162 __free_hook = free_hook;
163 installed = TRUE;
164 }
165 }
166
167 /**
168 * Uninstalls the malloc hooks, disables leak detection
169 */
170 void uninstall_hooks()
171 {
172 if (installed)
173 {
174 __malloc_hook = old_malloc_hook;
175 __free_hook = old_free_hook;
176 __realloc_hook = old_realloc_hook;
177 installed = FALSE;
178 }
179 }
180
181 /**
182 * Hook function for malloc()
183 */
184 static void *malloc_hook(size_t bytes, const void *caller)
185 {
186 memory_header_t *hdr;
187
188 pthread_mutex_lock(&mutex);
189 uninstall_hooks();
190 hdr = malloc(bytes + sizeof(memory_header_t));
191
192 hdr->magic = MEMORY_HEADER_MAGIC;
193 hdr->bytes = bytes;
194 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
195
196 /* insert at the beginning of the list */
197 hdr->next = first_header.next;
198 if (hdr->next)
199 {
200 hdr->next->previous = hdr;
201 }
202 hdr->previous = &first_header;
203 first_header.next = hdr;
204 install_hooks();
205 pthread_mutex_unlock(&mutex);
206 return hdr + 1;
207 }
208
209 /**
210 * Hook function for free()
211 */
212 static void free_hook(void *ptr, const void *caller)
213 {
214 void *stack_frames[STACK_FRAMES_COUNT];
215 int stack_frame_count;
216 memory_header_t *hdr = ptr - sizeof(memory_header_t);
217
218 /* allow freeing of NULL */
219 if (ptr == NULL)
220 {
221 return;
222 }
223
224 pthread_mutex_lock(&mutex);
225 if (hdr->magic != MEMORY_HEADER_MAGIC)
226 {
227 pthread_mutex_unlock(&mutex);
228 /* TODO: Since we get a lot of theses from the pthread lib, its deactivated for now... */
229 return;
230 logger->log(logger, ERROR, "freeing of invalid memory (%p)", ptr);
231 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
232 log_stack_frames(stack_frames, stack_frame_count);
233 kill(getpid(), SIGKILL);
234 return;
235 }
236 /* remove magic from hdr */
237 hdr->magic = 0;
238
239 /* remove item from list */
240 if (hdr->next)
241 {
242 hdr->next->previous = hdr->previous;
243 }
244 hdr->previous->next = hdr->next;
245
246 uninstall_hooks();
247 free(hdr);
248 install_hooks();
249 pthread_mutex_unlock(&mutex);
250 }
251
252 /**
253 * Hook function for realloc()
254 */
255 static void *realloc_hook(void *old, size_t bytes, const void *caller)
256 {
257 void *new;
258 memory_header_t *hdr = old - sizeof(memory_header_t);
259 void *stack_frames[STACK_FRAMES_COUNT];
260 int stack_frame_count;
261
262 /* allow reallocation of NULL */
263 if (old == NULL)
264 {
265 return malloc_hook(bytes, caller);
266 }
267 if (hdr->magic != MEMORY_HEADER_MAGIC)
268 {
269 logger->log(logger, ERROR, "reallocation of invalid memory (%p)", old);
270 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
271 log_stack_frames(stack_frames, stack_frame_count);
272 kill(getpid(), SIGKILL);
273 return NULL;
274 }
275
276 /* malloc and free is done with hooks */
277 new = malloc_hook(bytes, caller);
278 memcpy(new, old, min(bytes, hdr->bytes));
279 free_hook(old, caller);
280
281 return new;
282 }
283
284 /**
285 * Report leaks at library destruction
286 */
287 void __attribute__ ((destructor)) report_leaks()
288 {
289 memory_header_t *hdr;
290 int leaks = 0;
291
292 /* reaquire a logger is necessary, this will force ((destructor))
293 * order to work correctly */
294 logger = logger_manager->get_logger(logger_manager, LEAK_DETECT);
295
296 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
297 {
298 logger->log(logger, ERROR, "Leak (%d bytes at %p)", hdr->bytes, hdr + 1);
299 log_stack_frames(hdr->stack_frames, hdr->stack_frame_count);
300 leaks++;
301 }
302
303 switch (leaks)
304 {
305 case 0:
306 logger->log(logger, CONTROL, "No leaks detected");
307 break;
308 case 1:
309 logger->log(logger, ERROR, "One leak detected");
310 break;
311 default:
312 logger->log(logger, ERROR, "%d leaks detected", leaks);
313 break;
314 }
315 }
316
317 /*
318 * The following glibc functions are excluded from leak detection, since
319 * they use static allocated buffers or other ugly allocation hacks.
320 * The Makefile links theses function preferred to their counterparts
321 * in the target lib...
322 * TODO: Generic handling would be nice, with a list of blacklisted
323 * functions.
324 */
325
326
327 char *inet_ntoa(struct in_addr in)
328 {
329 char *(*_inet_ntoa)(struct in_addr);
330 void *handle;
331 char *result;
332
333 pthread_mutex_lock(&mutex);
334 uninstall_hooks();
335
336 handle = dlopen("libc.so.6", RTLD_LAZY);
337 if (handle == NULL)
338 {
339 install_hooks();
340 pthread_mutex_unlock(&mutex);
341 kill(getpid(), SIGSEGV);
342 }
343 _inet_ntoa = dlsym(handle, "inet_ntoa");
344
345 if (_inet_ntoa == NULL)
346 {
347 dlclose(handle);
348 install_hooks();
349 pthread_mutex_unlock(&mutex);
350 kill(getpid(), SIGSEGV);
351 }
352 result = _inet_ntoa(in);
353 dlclose(handle);
354 install_hooks();
355 pthread_mutex_unlock(&mutex);
356 return result;
357 }
358
359
360 int pthread_create(pthread_t *__restrict __threadp, __const pthread_attr_t *__restrict __attr,
361 void *(*__start_routine) (void *), void *__restrict __arg)
362 {
363 int (*_pthread_create) (pthread_t *__restrict __threadp,
364 __const pthread_attr_t *__restrict __attr,
365 void *(*__start_routine) (void *),
366 void *__restrict __arg);
367 void *handle;
368 int result;
369
370 pthread_mutex_lock(&mutex);
371 uninstall_hooks();
372
373 handle = dlopen("libpthread.so.0", RTLD_LAZY);
374 if (handle == NULL)
375 {
376 install_hooks();
377 pthread_mutex_unlock(&mutex);
378 kill(getpid(), SIGSEGV);
379 }
380 _pthread_create = dlsym(handle, "pthread_create");
381
382 if (_pthread_create == NULL)
383 {
384 dlclose(handle);
385 install_hooks();
386 pthread_mutex_unlock(&mutex);
387 kill(getpid(), SIGSEGV);
388 }
389 result = _pthread_create(__threadp, __attr, __start_routine, __arg);
390 dlclose(handle);
391 install_hooks();
392 pthread_mutex_unlock(&mutex);
393 return result;
394 }
395
396
397 time_t mktime(struct tm *tm)
398 {
399 time_t (*_mktime)(struct tm *tm);
400 time_t result;
401 void *handle;
402
403 pthread_mutex_lock(&mutex);
404 uninstall_hooks();
405
406 handle = dlopen("libc.so.6", RTLD_LAZY);
407 if (handle == NULL)
408 {
409 install_hooks();
410 pthread_mutex_unlock(&mutex);
411 kill(getpid(), SIGSEGV);
412 }
413 _mktime = dlsym(handle, "mktime");
414
415 if (_mktime == NULL)
416 {
417 dlclose(handle);
418 install_hooks();
419 pthread_mutex_unlock(&mutex);
420 kill(getpid(), SIGSEGV);
421 }
422 result = _mktime(tm);
423 dlclose(handle);
424 install_hooks();
425 pthread_mutex_unlock(&mutex);
426 return result;
427 }
428
429 void vsyslog (int __pri, __const char *__fmt, __gnuc_va_list __ap)
430 {
431 void (*_vsyslog) (int __pri, __const char *__fmt, __gnuc_va_list __ap);
432 void *handle;
433
434 pthread_mutex_lock(&mutex);
435 uninstall_hooks();
436
437 handle = dlopen("libc.so.6", RTLD_LAZY);
438 if (handle == NULL)
439 {
440 install_hooks();
441 pthread_mutex_unlock(&mutex);
442 kill(getpid(), SIGSEGV);
443 }
444 _vsyslog = dlsym(handle, "vsyslog");
445
446 if (_vsyslog == NULL)
447 {
448 dlclose(handle);
449 install_hooks();
450 pthread_mutex_unlock(&mutex);
451 kill(getpid(), SIGSEGV);
452 }
453 _vsyslog(__pri, __fmt, __ap);
454 dlclose(handle);
455 install_hooks();
456 pthread_mutex_unlock(&mutex);
457 return;
458 }
459
460 #endif /* LEAK_DETECTION */