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