fixed compilation warnings and errors when not using curl
[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 <signal.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <dlfcn.h>
31 #include <unistd.h>
32 #include <syslog.h>
33 #include <pthread.h>
34 #include <netdb.h>
35 #include <printf.h>
36 #ifdef HAVE_BACKTRACE
37 # include <execinfo.h>
38 #endif /* HAVE_BACKTRACE */
39
40 #include "leak_detective.h"
41
42 #include <library.h>
43 #include <debug.h>
44
45 #ifdef LEAK_DETECTIVE
46
47 /**
48 * Magic value which helps to detect memory corruption. Yummy!
49 */
50 #define MEMORY_HEADER_MAGIC 0x7ac0be11
51
52 /**
53 * Pattern which is filled in memory before freeing it
54 */
55 #define MEMORY_FREE_PATTERN 0xFF
56
57 /**
58 * Pattern which is filled in newly allocated memory
59 */
60 #define MEMORY_ALLOC_PATTERN 0xEE
61
62
63 static void install_hooks(void);
64 static void uninstall_hooks(void);
65 static void *malloc_hook(size_t, const void *);
66 static void *realloc_hook(void *, size_t, const void *);
67 static void free_hook(void*, const void *);
68
69 static u_int count_malloc = 0;
70 static u_int count_free = 0;
71 static u_int count_realloc = 0;
72
73 typedef struct memory_header_t memory_header_t;
74
75 /**
76 * Header which is prepended to each allocated memory block
77 */
78 struct memory_header_t {
79 /**
80 * Magci byte which must(!) hold MEMORY_HEADER_MAGIC
81 */
82 u_int32_t magic;
83
84 /**
85 * Number of bytes following after the header
86 */
87 size_t bytes;
88
89 /**
90 * Stack frames at the time of allocation
91 */
92 void *stack_frames[STACK_FRAMES_COUNT];
93
94 /**
95 * Number of stacks frames obtained in stack_frames
96 */
97 int stack_frame_count;
98
99 /**
100 * Pointer to previous entry in linked list
101 */
102 memory_header_t *previous;
103
104 /**
105 * Pointer to next entry in linked list
106 */
107 memory_header_t *next;
108 };
109
110 /**
111 * first mem header is just a dummy to chain
112 * the others on it...
113 */
114 static memory_header_t first_header = {
115 magic: MEMORY_HEADER_MAGIC,
116 bytes: 0,
117 stack_frame_count: 0,
118 previous: NULL,
119 next: NULL
120 };
121
122 /**
123 * standard hooks, used to temparily remove hooking
124 */
125 static void *old_malloc_hook, *old_realloc_hook, *old_free_hook;
126
127 /**
128 * are the hooks currently installed?
129 */
130 static bool installed = FALSE;
131
132 /**
133 * Mutex to exclusivly uninstall hooks, access heap list
134 */
135 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
136
137
138 /**
139 * log stack frames queried by backtrace()
140 * TODO: Dump symbols of static functions. This could be done with
141 * the addr2line utility or the GNU BFD Library...
142 */
143 static void log_stack_frames(void **stack_frames, int stack_frame_count)
144 {
145 #ifdef HAVE_BACKTRACE
146 char **strings;
147 size_t i;
148
149 strings = backtrace_symbols (stack_frames, stack_frame_count);
150
151 DBG1(" dumping %d stack frame addresses", stack_frame_count);
152
153 for (i = 0; i < stack_frame_count; i++)
154 {
155 DBG1(" %s", strings[i]);
156 }
157 free (strings);
158 #endif /* HAVE_BACKTRACE */
159 }
160
161 /**
162 * Whitelist, which contains address ranges in stack frames ignored when leaking.
163 *
164 * This is necessary, as some function use allocation hacks (static buffers)
165 * and so on, which we want to suppress on leak reports.
166 *
167 * The range_size is calculated using the readelf utility, e.g.:
168 * readelf -s /lib/glibc.so.6
169 * The values are for glibc-2.4 and may or may not be correct on other systems.
170 */
171 typedef struct whitelist_t whitelist_t;
172
173 struct whitelist_t {
174 void* range_start;
175 size_t range_size;
176 };
177
178 #ifdef LIBCURL
179 /* dummy declaration for whitelisting */
180 void *Curl_getaddrinfo(void);
181 #endif /* LIBCURL */
182
183 whitelist_t whitelist[] = {
184 {pthread_create, 2542},
185 {pthread_setspecific, 217},
186 {mktime, 60},
187 {tzset, 123},
188 {inet_ntoa, 249},
189 {strerror, 180},
190 {getprotobynumber, 291},
191 {getservbyport, 311},
192 {register_printf_function, 159},
193 {syslog, 45},
194 {dlopen, 109},
195 # ifdef LIBCURL
196 /* from /usr/lib/libcurl.so.3 */
197 {Curl_getaddrinfo, 480},
198 # endif /* LIBCURL */
199 };
200
201 /**
202 * Check if this stack frame is whitelisted.
203 */
204 static bool is_whitelisted(void **stack_frames, int stack_frame_count)
205 {
206 int i, j;
207
208 for (i=0; i< stack_frame_count; i++)
209 {
210 for (j=0; j<sizeof(whitelist)/sizeof(whitelist_t); j++)
211 {
212 if (stack_frames[i] >= whitelist[j].range_start &&
213 stack_frames[i] <= (whitelist[j].range_start + whitelist[j].range_size))
214 {
215 return TRUE;
216 }
217 }
218 }
219 return FALSE;
220 }
221
222 /**
223 * Report leaks at library destruction
224 */
225 void report_leaks()
226 {
227 memory_header_t *hdr;
228 int leaks = 0;
229
230 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
231 {
232 if (!is_whitelisted(hdr->stack_frames, hdr->stack_frame_count))
233 {
234 DBG1("Leak (%d bytes at %p):", hdr->bytes, hdr + 1);
235 log_stack_frames(hdr->stack_frames, hdr->stack_frame_count);
236 leaks++;
237 }
238 }
239
240 switch (leaks)
241 {
242 case 0:
243 DBG1("No leaks detected");
244 break;
245 case 1:
246 DBG1("One leak detected");
247 break;
248 default:
249 DBG1("%d leaks detected", leaks);
250 break;
251 }
252 }
253
254 /**
255 * Installs the malloc hooks, enables leak detection
256 */
257 static void install_hooks()
258 {
259 if (!installed)
260 {
261 old_malloc_hook = __malloc_hook;
262 old_realloc_hook = __realloc_hook;
263 old_free_hook = __free_hook;
264 __malloc_hook = malloc_hook;
265 __realloc_hook = realloc_hook;
266 __free_hook = free_hook;
267 installed = TRUE;
268 }
269 }
270
271 /**
272 * Uninstalls the malloc hooks, disables leak detection
273 */
274 static void uninstall_hooks()
275 {
276 if (installed)
277 {
278 __malloc_hook = old_malloc_hook;
279 __free_hook = old_free_hook;
280 __realloc_hook = old_realloc_hook;
281 installed = FALSE;
282 }
283 }
284
285 /**
286 * Hook function for malloc()
287 */
288 void *malloc_hook(size_t bytes, const void *caller)
289 {
290 memory_header_t *hdr;
291
292 pthread_mutex_lock(&mutex);
293 count_malloc++;
294 uninstall_hooks();
295 hdr = malloc(bytes + sizeof(memory_header_t));
296 /* set to something which causes crashes */
297 memset(hdr, MEMORY_ALLOC_PATTERN, bytes + sizeof(memory_header_t));
298
299 hdr->magic = MEMORY_HEADER_MAGIC;
300 hdr->bytes = bytes;
301 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
302 install_hooks();
303
304 /* insert at the beginning of the list */
305 hdr->next = first_header.next;
306 if (hdr->next)
307 {
308 hdr->next->previous = hdr;
309 }
310 hdr->previous = &first_header;
311 first_header.next = hdr;
312 pthread_mutex_unlock(&mutex);
313 return hdr + 1;
314 }
315
316 /**
317 * Hook function for free()
318 */
319 void free_hook(void *ptr, const void *caller)
320 {
321 void *stack_frames[STACK_FRAMES_COUNT];
322 int stack_frame_count;
323 memory_header_t *hdr = ptr - sizeof(memory_header_t);
324
325 /* allow freeing of NULL */
326 if (ptr == NULL)
327 {
328 return;
329 }
330
331 pthread_mutex_lock(&mutex);
332 count_free++;
333 uninstall_hooks();
334 if (hdr->magic != MEMORY_HEADER_MAGIC)
335 {
336 DBG1("freeing of invalid memory (%p, MAGIC 0x%x != 0x%x):",
337 ptr, hdr->magic, MEMORY_HEADER_MAGIC);
338 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
339 log_stack_frames(stack_frames, stack_frame_count);
340 install_hooks();
341 pthread_mutex_unlock(&mutex);
342 return;
343 }
344
345 /* remove item from list */
346 if (hdr->next)
347 {
348 hdr->next->previous = hdr->previous;
349 }
350 hdr->previous->next = hdr->next;
351
352 /* clear MAGIC, set mem to something remarkable */
353 memset(hdr, MEMORY_FREE_PATTERN, hdr->bytes + sizeof(memory_header_t));
354
355 free(hdr);
356 install_hooks();
357 pthread_mutex_unlock(&mutex);
358 }
359
360 /**
361 * Hook function for realloc()
362 */
363 void *realloc_hook(void *old, size_t bytes, const void *caller)
364 {
365 memory_header_t *hdr;
366 void *stack_frames[STACK_FRAMES_COUNT];
367 int stack_frame_count;
368
369 /* allow reallocation of NULL */
370 if (old == NULL)
371 {
372 return malloc_hook(bytes, caller);
373 }
374
375 hdr = old - sizeof(memory_header_t);
376
377 pthread_mutex_lock(&mutex);
378 count_realloc++;
379 uninstall_hooks();
380 if (hdr->magic != MEMORY_HEADER_MAGIC)
381 {
382 DBG1("reallocation of invalid memory (%p):", old);
383 stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT);
384 log_stack_frames(stack_frames, stack_frame_count);
385 install_hooks();
386 pthread_mutex_unlock(&mutex);
387 raise(SIGKILL);
388 return NULL;
389 }
390
391 hdr = realloc(hdr, bytes + sizeof(memory_header_t));
392
393 /* update statistics */
394 hdr->bytes = bytes;
395 hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT);
396
397 /* update header of linked list neighbours */
398 if (hdr->next)
399 {
400 hdr->next->previous = hdr;
401 }
402 hdr->previous->next = hdr;
403 install_hooks();
404 pthread_mutex_unlock(&mutex);
405 return hdr + 1;
406 }
407
408 /**
409 * Setup leak detective
410 */
411 void __attribute__ ((constructor)) leak_detective_init()
412 {
413 install_hooks();
414 }
415
416 /**
417 * Clean up leak detective
418 */
419 void __attribute__ ((destructor)) leak_detective_cleanup()
420 {
421 uninstall_hooks();
422 report_leaks();
423 }
424
425 /**
426 * Log memory allocation statistics
427 */
428 void leak_detective_status(FILE *stream)
429 {
430 u_int blocks = 0;
431 size_t bytes = 0;
432 memory_header_t *hdr = &first_header;
433
434 pthread_mutex_lock(&mutex);
435 while ((hdr = hdr->next))
436 {
437 blocks++;
438 bytes += hdr->bytes;
439 }
440 pthread_mutex_unlock(&mutex);
441
442 fprintf(stream, "allocation statistics:\n");
443 fprintf(stream, " call stats: malloc: %d, free: %d, realloc: %d\n",
444 count_malloc, count_free, count_realloc);
445 fprintf(stream, " allocated %d blocks, total size %d bytes (avg. %d bytes)\n",
446 blocks, bytes, bytes/blocks);
447 }
448
449 #else /* !LEAK_DETECTION */
450
451 /**
452 * Dummy when !using LEAK_DETECTIVE
453 */
454 void leak_detective_status(FILE *stream)
455 {
456
457 }
458
459 #endif /* LEAK_DETECTION */