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