c79d75f797f1e01ee71da1edc6067c5a17316587
[strongswan.git] / src / libstrongswan / utils / leak_detective.c
1 /*
2 * Copyright (C) 2006-2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 *
15 * $Id$
16 */
17
18 #include <stddef.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <malloc.h>
22 #include <signal.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <unistd.h>
27 #include <syslog.h>
28 #include <pthread.h>
29 #include <netdb.h>
30 #include <printf.h>
31 #include <locale.h>
32
33 #include "leak_detective.h"
34
35 #include <library.h>
36 #include <debug.h>
37 #include <utils/backtrace.h>
38
39 typedef struct private_leak_detective_t private_leak_detective_t;
40
41 /**
42 * private data of leak_detective
43 */
44 struct private_leak_detective_t {
45
46 /**
47 * public functions
48 */
49 leak_detective_t public;
50 };
51
52 /**
53 * Magic value which helps to detect memory corruption. Yummy!
54 */
55 #define MEMORY_HEADER_MAGIC 0x7ac0be11
56
57 /**
58 * Magic written to tail of allocation
59 */
60 #define MEMORY_TAIL_MAGIC 0xcafebabe
61
62 /**
63 * Pattern which is filled in memory before freeing it
64 */
65 #define MEMORY_FREE_PATTERN 0xFF
66
67 /**
68 * Pattern which is filled in newly allocated memory
69 */
70 #define MEMORY_ALLOC_PATTERN 0xEE
71
72
73 static void install_hooks(void);
74 static void uninstall_hooks(void);
75 static void *malloc_hook(size_t, const void *);
76 static void *realloc_hook(void *, size_t, const void *);
77 static void free_hook(void*, const void *);
78
79 void *(*old_malloc_hook)(size_t, const void *);
80 void *(*old_realloc_hook)(void *, size_t, const void *);
81 void (*old_free_hook)(void*, const void *);
82
83 static u_int count_malloc = 0;
84 static u_int count_free = 0;
85 static u_int count_realloc = 0;
86
87 typedef struct memory_header_t memory_header_t;
88 typedef struct memory_tail_t memory_tail_t;
89
90 /**
91 * Header which is prepended to each allocated memory block
92 */
93 struct memory_header_t {
94
95 /**
96 * Number of bytes following after the header
97 */
98 u_int bytes;
99
100 /**
101 * Pointer to previous entry in linked list
102 */
103 memory_header_t *previous;
104
105 /**
106 * Pointer to next entry in linked list
107 */
108 memory_header_t *next;
109
110 /**
111 * backtrace taken during (re-)allocation
112 */
113 backtrace_t *backtrace;
114
115 /**
116 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
117 */
118 u_int32_t magic;
119
120 }__attribute__((__packed__));
121
122 /**
123 * tail appended to each allocated memory block
124 */
125 struct memory_tail_t {
126
127 /**
128 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
129 */
130 u_int32_t magic;
131
132 }__attribute__((__packed__));
133
134 /**
135 * first mem header is just a dummy to chain
136 * the others on it...
137 */
138 static memory_header_t first_header = {
139 magic: MEMORY_HEADER_MAGIC,
140 bytes: 0,
141 backtrace: NULL,
142 previous: NULL,
143 next: NULL
144 };
145
146 /**
147 * are the hooks currently installed?
148 */
149 static bool installed = FALSE;
150
151 /**
152 * Leak report white list
153 *
154 * List of functions using static allocation buffers or should be suppressed
155 * otherwise on leak report.
156 */
157 char *whitelist[] = {
158 /* backtraces, including own */
159 "backtrace_create",
160 /* pthread stuff */
161 "pthread_create",
162 "pthread_setspecific",
163 /* glibc functions */
164 "mktime",
165 "__gmtime_r",
166 "localtime_r",
167 "tzset",
168 "inet_ntoa",
169 "strerror",
170 "getprotobynumber",
171 "getservbyport",
172 "getservbyname",
173 "gethostbyname_r",
174 "gethostbyname2_r",
175 "getpwnam_r",
176 "getgrnam_r",
177 "register_printf_function",
178 "syslog",
179 "vsyslog",
180 "getaddrinfo",
181 "setlocale",
182 /* ignore dlopen, as we do not dlclose to get proper leak reports */
183 "dlopen",
184 "dlerror",
185 "dlclose",
186 /* mysql functions */
187 "mysql_init_character_set",
188 "init_client_errs",
189 "my_thread_init",
190 /* fastcgi library */
191 "FCGX_Init",
192 /* libxml */
193 "xmlInitCharEncodingHandlers",
194 "xmlInitParser",
195 "xmlInitParserCtxt",
196 /* ClearSilver */
197 "nerr_init",
198 /* OpenSSL */
199 "RSA_new_method",
200 "DH_new_method",
201 "ENGINE_load_builtin_engines",
202 "OPENSSL_config",
203 };
204
205 /**
206 * check if a stack frame contains functions listed above
207 */
208 static bool is_whitelisted(backtrace_t *backtrace)
209 {
210 int i;
211 for (i = 0; i < sizeof(whitelist)/sizeof(char*); i++)
212 {
213 if (backtrace->contains_function(backtrace, whitelist[i]))
214 {
215 return TRUE;
216 }
217 }
218 return FALSE;
219 }
220
221 /**
222 * Report leaks at library destruction
223 */
224 void report_leaks()
225 {
226 memory_header_t *hdr;
227 int leaks = 0, whitelisted = 0;
228
229 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
230 {
231 if (is_whitelisted(hdr->backtrace))
232 {
233 whitelisted++;
234 }
235 else
236 {
237 fprintf(stderr, "Leak (%d bytes at %p):\n", hdr->bytes, hdr + 1);
238 /* skip the first frame, contains leak detective logic */
239 hdr->backtrace->log(hdr->backtrace, stderr);
240 leaks++;
241 }
242 }
243
244 switch (leaks)
245 {
246 case 0:
247 fprintf(stderr, "No leaks detected");
248 break;
249 case 1:
250 fprintf(stderr, "One leak detected");
251 break;
252 default:
253 fprintf(stderr, "%d leaks detected", leaks);
254 break;
255 }
256 fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
257 }
258
259 /**
260 * Installs the malloc hooks, enables leak detection
261 */
262 static void install_hooks()
263 {
264 if (!installed)
265 {
266 old_malloc_hook = __malloc_hook;
267 old_realloc_hook = __realloc_hook;
268 old_free_hook = __free_hook;
269 __malloc_hook = malloc_hook;
270 __realloc_hook = realloc_hook;
271 __free_hook = free_hook;
272 installed = TRUE;
273 }
274 }
275
276 /**
277 * Uninstalls the malloc hooks, disables leak detection
278 */
279 static void uninstall_hooks()
280 {
281 if (installed)
282 {
283 __malloc_hook = old_malloc_hook;
284 __free_hook = old_free_hook;
285 __realloc_hook = old_realloc_hook;
286 installed = FALSE;
287 }
288 }
289
290 /**
291 * Hook function for malloc()
292 */
293 void *malloc_hook(size_t bytes, const void *caller)
294 {
295 memory_header_t *hdr;
296 memory_tail_t *tail;
297 pthread_t thread_id = pthread_self();
298 int oldpolicy;
299 struct sched_param oldparams, params;
300
301 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
302
303 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
304 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
305
306 count_malloc++;
307 uninstall_hooks();
308 hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
309 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
310 /* set to something which causes crashes */
311 memset(hdr, MEMORY_ALLOC_PATTERN,
312 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
313
314 hdr->magic = MEMORY_HEADER_MAGIC;
315 hdr->bytes = bytes;
316 hdr->backtrace = backtrace_create(3);
317 tail->magic = MEMORY_TAIL_MAGIC;
318 install_hooks();
319
320 /* insert at the beginning of the list */
321 hdr->next = first_header.next;
322 if (hdr->next)
323 {
324 hdr->next->previous = hdr;
325 }
326 hdr->previous = &first_header;
327 first_header.next = hdr;
328
329 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
330
331 return hdr + 1;
332 }
333
334 /**
335 * Hook function for free()
336 */
337 void free_hook(void *ptr, const void *caller)
338 {
339 memory_header_t *hdr;
340 memory_tail_t *tail;
341 backtrace_t *backtrace;
342 pthread_t thread_id = pthread_self();
343 int oldpolicy;
344 struct sched_param oldparams, params;
345
346 /* allow freeing of NULL */
347 if (ptr == NULL)
348 {
349 return;
350 }
351 hdr = ptr - sizeof(memory_header_t);
352 tail = ptr + hdr->bytes;
353
354 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
355
356 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
357 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
358
359 count_free++;
360 uninstall_hooks();
361 if (hdr->magic != MEMORY_HEADER_MAGIC ||
362 tail->magic != MEMORY_TAIL_MAGIC)
363 {
364 fprintf(stderr, "freeing invalid memory (%p): "
365 "header magic 0x%x, tail magic 0x%x:\n",
366 ptr, hdr->magic, tail->magic);
367 backtrace = backtrace_create(3);
368 backtrace->log(backtrace, stderr);
369 backtrace->destroy(backtrace);
370 }
371 else
372 {
373 /* remove item from list */
374 if (hdr->next)
375 {
376 hdr->next->previous = hdr->previous;
377 }
378 hdr->previous->next = hdr->next;
379 hdr->backtrace->destroy(hdr->backtrace);
380
381 /* clear MAGIC, set mem to something remarkable */
382 memset(hdr, MEMORY_FREE_PATTERN, hdr->bytes + sizeof(memory_header_t));
383
384 free(hdr);
385 }
386
387 install_hooks();
388 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
389 }
390
391 /**
392 * Hook function for realloc()
393 */
394 void *realloc_hook(void *old, size_t bytes, const void *caller)
395 {
396 memory_header_t *hdr;
397 memory_tail_t *tail;
398 backtrace_t *backtrace;
399 pthread_t thread_id = pthread_self();
400 int oldpolicy;
401 struct sched_param oldparams, params;
402
403 /* allow reallocation of NULL */
404 if (old == NULL)
405 {
406 return malloc_hook(bytes, caller);
407 }
408
409 hdr = old - sizeof(memory_header_t);
410 tail = old + hdr->bytes;
411
412 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
413
414 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
415 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
416
417 count_realloc++;
418 uninstall_hooks();
419 if (hdr->magic != MEMORY_HEADER_MAGIC ||
420 tail->magic != MEMORY_TAIL_MAGIC)
421 {
422 fprintf(stderr, "reallocating invalid memory (%p): "
423 "header magic 0x%x, tail magic 0x%x:\n",
424 old, hdr->magic, tail->magic);
425 backtrace = backtrace_create(3);
426 backtrace->log(backtrace, stderr);
427 backtrace->destroy(backtrace);
428 }
429 /* clear tail magic, allocate, set tail magic */
430 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
431 hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
432 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
433 tail->magic = MEMORY_TAIL_MAGIC;
434
435 /* update statistics */
436 hdr->bytes = bytes;
437 hdr->backtrace->destroy(hdr->backtrace);
438 hdr->backtrace = backtrace_create(3);
439
440 /* update header of linked list neighbours */
441 if (hdr->next)
442 {
443 hdr->next->previous = hdr;
444 }
445 hdr->previous->next = hdr;
446 install_hooks();
447 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
448 return hdr + 1;
449 }
450
451 /**
452 * Implementation of leak_detective_t.destroy
453 */
454 static void destroy(private_leak_detective_t *this)
455 {
456 if (installed)
457 {
458 uninstall_hooks();
459 report_leaks();
460 }
461 free(this);
462 }
463
464 /*
465 * see header file
466 */
467 leak_detective_t *leak_detective_create()
468 {
469 private_leak_detective_t *this = malloc_thing(private_leak_detective_t);
470
471 this->public.destroy = (void(*)(leak_detective_t*))destroy;
472
473 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
474 {
475 lib->leak_detective = TRUE;
476 install_hooks();
477 }
478 return &this->public;
479 }
480