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