Whitelist gnutls init function
[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
16 #define _GNU_SOURCE
17 #include <sched.h>
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 <locale.h>
31
32 #include "leak_detective.h"
33
34 #include <library.h>
35 #include <debug.h>
36 #include <utils/backtrace.h>
37
38 typedef struct private_leak_detective_t private_leak_detective_t;
39
40 /**
41 * private data of leak_detective
42 */
43 struct private_leak_detective_t {
44
45 /**
46 * public functions
47 */
48 leak_detective_t public;
49 };
50
51 /**
52 * Magic value which helps to detect memory corruption. Yummy!
53 */
54 #define MEMORY_HEADER_MAGIC 0x7ac0be11
55
56 /**
57 * Magic written to tail of allocation
58 */
59 #define MEMORY_TAIL_MAGIC 0xcafebabe
60
61 /**
62 * Pattern which is filled in memory before freeing it
63 */
64 #define MEMORY_FREE_PATTERN 0xFF
65
66 /**
67 * Pattern which is filled in newly allocated memory
68 */
69 #define MEMORY_ALLOC_PATTERN 0xEE
70
71
72 static void install_hooks(void);
73 static void uninstall_hooks(void);
74 static void *malloc_hook(size_t, const void *);
75 static void *realloc_hook(void *, size_t, const void *);
76 static void free_hook(void*, const void *);
77
78 void *(*old_malloc_hook)(size_t, const void *);
79 void *(*old_realloc_hook)(void *, size_t, const void *);
80 void (*old_free_hook)(void*, const void *);
81
82 static u_int count_malloc = 0;
83 static u_int count_free = 0;
84 static u_int count_realloc = 0;
85
86 typedef struct memory_header_t memory_header_t;
87 typedef struct memory_tail_t memory_tail_t;
88
89 /**
90 * Header which is prepended to each allocated memory block
91 */
92 struct memory_header_t {
93
94 /**
95 * Number of bytes following after the header
96 */
97 u_int bytes;
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 * backtrace taken during (re-)allocation
111 */
112 backtrace_t *backtrace;
113
114 /**
115 * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
116 */
117 u_int32_t magic;
118
119 }__attribute__((__packed__));
120
121 /**
122 * tail appended to each allocated memory block
123 */
124 struct memory_tail_t {
125
126 /**
127 * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC
128 */
129 u_int32_t magic;
130
131 }__attribute__((__packed__));
132
133 /**
134 * first mem header is just a dummy to chain
135 * the others on it...
136 */
137 static memory_header_t first_header = {
138 magic: MEMORY_HEADER_MAGIC,
139 bytes: 0,
140 backtrace: NULL,
141 previous: NULL,
142 next: NULL
143 };
144
145 /**
146 * are the hooks currently installed?
147 */
148 static bool installed = FALSE;
149
150 /**
151 * Leak report white list
152 *
153 * List of functions using static allocation buffers or should be suppressed
154 * otherwise on leak report.
155 */
156 char *whitelist[] = {
157 /* backtraces, including own */
158 "backtrace_create",
159 /* pthread stuff */
160 "pthread_create",
161 "pthread_setspecific",
162 "__pthread_setspecific",
163 /* glibc functions */
164 "mktime",
165 "__gmtime_r",
166 "localtime_r",
167 "tzset",
168 "inet_ntoa",
169 "strerror",
170 "getprotobyname",
171 "getprotobynumber",
172 "getservbyport",
173 "getservbyname",
174 "gethostbyname2",
175 "gethostbyname_r",
176 "gethostbyname2_r",
177 "getnetbyname",
178 "getpwnam_r",
179 "getgrnam_r",
180 "register_printf_function",
181 "register_printf_specifier",
182 "syslog",
183 "vsyslog",
184 "__syslog_chk",
185 "__vsyslog_chk",
186 "getaddrinfo",
187 "setlocale",
188 "getpass",
189 /* ignore dlopen, as we do not dlclose to get proper leak reports */
190 "dlopen",
191 "dlerror",
192 "dlclose",
193 /* mysql functions */
194 "mysql_init_character_set",
195 "init_client_errs",
196 "my_thread_init",
197 /* fastcgi library */
198 "FCGX_Init",
199 /* libxml */
200 "xmlInitCharEncodingHandlers",
201 "xmlInitParser",
202 "xmlInitParserCtxt",
203 /* libcurl */
204 "Curl_client_write",
205 /* ClearSilver */
206 "nerr_init",
207 /* OpenSSL */
208 "RSA_new_method",
209 "DH_new_method",
210 "ENGINE_load_builtin_engines",
211 "OPENSSL_config",
212 "ecdsa_check",
213 "ERR_put_error",
214 /* libgcrypt */
215 "gcry_control",
216 "gcry_check_version",
217 "gcry_randomize",
218 "gcry_create_nonce",
219 /* NSPR */
220 "PR_CallOnce",
221 /* libapr */
222 "apr_pool_create_ex",
223 /* glib */
224 "g_type_init_with_debug_flags",
225 "g_type_register_static",
226 "g_type_class_ref",
227 "g_type_create_instance",
228 "g_type_add_interface_static",
229 "g_type_interface_add_prerequisite",
230 "g_socket_connection_factory_lookup_type",
231 /* libgpg */
232 "gpg_err_init",
233 /* gnutls */
234 "gnutls_global_init",
235 };
236
237 /**
238 * Report leaks at library destruction
239 */
240 static void report(private_leak_detective_t *this, bool detailed)
241 {
242 if (lib->leak_detective)
243 {
244 memory_header_t *hdr;
245 int leaks = 0, whitelisted = 0;
246
247 for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
248 {
249 if (hdr->backtrace->contains_function(hdr->backtrace,
250 whitelist, countof(whitelist)))
251 {
252 whitelisted++;
253 }
254 else
255 {
256 fprintf(stderr, "Leak (%d bytes at %p):\n", hdr->bytes, hdr + 1);
257 /* skip the first frame, contains leak detective logic */
258 hdr->backtrace->log(hdr->backtrace, stderr, detailed);
259 leaks++;
260 }
261 }
262 switch (leaks)
263 {
264 case 0:
265 fprintf(stderr, "No leaks detected");
266 break;
267 case 1:
268 fprintf(stderr, "One leak detected");
269 break;
270 default:
271 fprintf(stderr, "%d leaks detected", leaks);
272 break;
273 }
274 fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
275 }
276 else
277 {
278 fprintf(stderr, "Leak detective disabled\n");
279 }
280 }
281
282 /**
283 * Installs the malloc hooks, enables leak detection
284 */
285 static void install_hooks()
286 {
287 if (!installed)
288 {
289 old_malloc_hook = __malloc_hook;
290 old_realloc_hook = __realloc_hook;
291 old_free_hook = __free_hook;
292 __malloc_hook = malloc_hook;
293 __realloc_hook = realloc_hook;
294 __free_hook = free_hook;
295 installed = TRUE;
296 }
297 }
298
299 /**
300 * Uninstalls the malloc hooks, disables leak detection
301 */
302 static void uninstall_hooks()
303 {
304 if (installed)
305 {
306 __malloc_hook = old_malloc_hook;
307 __free_hook = old_free_hook;
308 __realloc_hook = old_realloc_hook;
309 installed = FALSE;
310 }
311 }
312
313 /**
314 * Hook function for malloc()
315 */
316 void *malloc_hook(size_t bytes, const void *caller)
317 {
318 memory_header_t *hdr;
319 memory_tail_t *tail;
320 pthread_t thread_id = pthread_self();
321 int oldpolicy;
322 struct sched_param oldparams, params;
323
324 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
325
326 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
327 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
328
329 count_malloc++;
330 uninstall_hooks();
331 hdr = malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
332 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
333 /* set to something which causes crashes */
334 memset(hdr, MEMORY_ALLOC_PATTERN,
335 sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
336
337 hdr->magic = MEMORY_HEADER_MAGIC;
338 hdr->bytes = bytes;
339 hdr->backtrace = backtrace_create(3);
340 tail->magic = MEMORY_TAIL_MAGIC;
341 install_hooks();
342
343 /* insert at the beginning of the list */
344 hdr->next = first_header.next;
345 if (hdr->next)
346 {
347 hdr->next->previous = hdr;
348 }
349 hdr->previous = &first_header;
350 first_header.next = hdr;
351
352 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
353
354 return hdr + 1;
355 }
356
357 /**
358 * Hook function for free()
359 */
360 void free_hook(void *ptr, const void *caller)
361 {
362 memory_header_t *hdr, *current;
363 memory_tail_t *tail;
364 backtrace_t *backtrace;
365 pthread_t thread_id = pthread_self();
366 int oldpolicy;
367 struct sched_param oldparams, params;
368 bool found = FALSE;
369
370 /* allow freeing of NULL */
371 if (ptr == NULL)
372 {
373 return;
374 }
375 hdr = ptr - sizeof(memory_header_t);
376 tail = ptr + hdr->bytes;
377
378 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
379
380 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
381 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
382
383 count_free++;
384 uninstall_hooks();
385 if (hdr->magic != MEMORY_HEADER_MAGIC ||
386 tail->magic != MEMORY_TAIL_MAGIC)
387 {
388 for (current = &first_header; current != NULL; current = current->next)
389 {
390 if (current == hdr)
391 {
392 found = TRUE;
393 break;
394 }
395 }
396 if (found)
397 {
398 /* memory was allocated by our hooks but is corrupted */
399 fprintf(stderr, "freeing corrupted memory (%p): "
400 "header magic 0x%x, tail magic 0x%x:\n",
401 ptr, hdr->magic, tail->magic);
402 }
403 else
404 {
405 /* memory was not allocated by our hooks */
406 fprintf(stderr, "freeing invalid memory (%p)", ptr);
407 }
408 backtrace = backtrace_create(3);
409 backtrace->log(backtrace, stderr, TRUE);
410 backtrace->destroy(backtrace);
411 }
412 else
413 {
414 /* remove item from list */
415 if (hdr->next)
416 {
417 hdr->next->previous = hdr->previous;
418 }
419 hdr->previous->next = hdr->next;
420 hdr->backtrace->destroy(hdr->backtrace);
421
422 /* clear MAGIC, set mem to something remarkable */
423 memset(hdr, MEMORY_FREE_PATTERN,
424 sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
425
426 free(hdr);
427 }
428
429 install_hooks();
430 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
431 }
432
433 /**
434 * Hook function for realloc()
435 */
436 void *realloc_hook(void *old, size_t bytes, const void *caller)
437 {
438 memory_header_t *hdr;
439 memory_tail_t *tail;
440 backtrace_t *backtrace;
441 pthread_t thread_id = pthread_self();
442 int oldpolicy;
443 struct sched_param oldparams, params;
444
445 /* allow reallocation of NULL */
446 if (old == NULL)
447 {
448 return malloc_hook(bytes, caller);
449 }
450
451 hdr = old - sizeof(memory_header_t);
452 tail = old + hdr->bytes;
453
454 pthread_getschedparam(thread_id, &oldpolicy, &oldparams);
455
456 params.__sched_priority = sched_get_priority_max(SCHED_FIFO);
457 pthread_setschedparam(thread_id, SCHED_FIFO, &params);
458
459 count_realloc++;
460 uninstall_hooks();
461 if (hdr->magic != MEMORY_HEADER_MAGIC ||
462 tail->magic != MEMORY_TAIL_MAGIC)
463 {
464 fprintf(stderr, "reallocating invalid memory (%p): "
465 "header magic 0x%x, tail magic 0x%x:\n",
466 old, hdr->magic, tail->magic);
467 backtrace = backtrace_create(3);
468 backtrace->log(backtrace, stderr, TRUE);
469 backtrace->destroy(backtrace);
470 }
471 /* clear tail magic, allocate, set tail magic */
472 memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
473 hdr = realloc(hdr, sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
474 tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
475 tail->magic = MEMORY_TAIL_MAGIC;
476
477 /* update statistics */
478 hdr->bytes = bytes;
479 hdr->backtrace->destroy(hdr->backtrace);
480 hdr->backtrace = backtrace_create(3);
481
482 /* update header of linked list neighbours */
483 if (hdr->next)
484 {
485 hdr->next->previous = hdr;
486 }
487 hdr->previous->next = hdr;
488 install_hooks();
489 pthread_setschedparam(thread_id, oldpolicy, &oldparams);
490 return hdr + 1;
491 }
492
493 /**
494 * Implementation of leak_detective_t.destroy
495 */
496 static void destroy(private_leak_detective_t *this)
497 {
498 if (installed)
499 {
500 uninstall_hooks();
501 }
502 free(this);
503 }
504
505 /*
506 * see header file
507 */
508 leak_detective_t *leak_detective_create()
509 {
510 private_leak_detective_t *this = malloc_thing(private_leak_detective_t);
511
512 this->public.report = (void(*)(leak_detective_t*,bool))report;
513 this->public.destroy = (void(*)(leak_detective_t*))destroy;
514
515 if (getenv("LEAK_DETECTIVE_DISABLE") == NULL)
516 {
517 cpu_set_t mask;
518
519 CPU_ZERO(&mask);
520 CPU_SET(0, &mask);
521
522 if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) != 0)
523 {
524 fprintf(stderr, "setting CPU affinity failed: %m");
525 }
526
527 lib->leak_detective = TRUE;
528 install_hooks();
529 }
530 return &this->public;
531 }
532