]>
Commit | Line | Data |
---|---|---|
5113680f | 1 | /* |
472efd38 | 2 | * Copyright (C) 2013-2018 Tobias Brunner |
17211b6b | 3 | * Copyright (C) 2006-2013 Martin Willi |
1b671669 | 4 | * HSR Hochschule fuer Technik Rapperswil |
5113680f MW |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
15 | */ | |
7daf5226 | 16 | |
7a485e90 | 17 | #define _GNU_SOURCE |
5113680f | 18 | #include <stddef.h> |
5113680f MW |
19 | #include <string.h> |
20 | #include <stdio.h> | |
7daf5226 | 21 | #include <signal.h> |
1d025fbc | 22 | #include <unistd.h> |
552cc11b | 23 | #include <locale.h> |
922ee2c5 | 24 | #ifdef HAVE_DLADDR |
17211b6b | 25 | #include <dlfcn.h> |
922ee2c5 | 26 | #endif |
7e3f6299 | 27 | #include <time.h> |
d8f6f0c0 MW |
28 | #include <errno.h> |
29 | ||
30 | #ifdef __APPLE__ | |
31 | #include <sys/mman.h> | |
32 | #include <malloc/malloc.h> | |
33 | /* overload some of our types clashing with mach */ | |
34 | #define host_t strongswan_host_t | |
35 | #define processor_t strongswan_processor_t | |
36 | #define thread_t strongswan_thread_t | |
37 | #endif /* __APPLE__ */ | |
5113680f MW |
38 | |
39 | #include "leak_detective.h" | |
40 | ||
60356f33 | 41 | #include <library.h> |
922ee2c5 | 42 | #include <utils/utils.h> |
f05b4272 | 43 | #include <utils/debug.h> |
f7237cf3 | 44 | #include <utils/backtrace.h> |
12642a68 | 45 | #include <collections/hashtable.h> |
17211b6b MW |
46 | #include <threading/thread_value.h> |
47 | #include <threading/spinlock.h> | |
5113680f | 48 | |
552cc11b MW |
49 | typedef struct private_leak_detective_t private_leak_detective_t; |
50 | ||
51 | /** | |
52 | * private data of leak_detective | |
53 | */ | |
54 | struct private_leak_detective_t { | |
55 | ||
56 | /** | |
57 | * public functions | |
58 | */ | |
59 | leak_detective_t public; | |
a426851f MW |
60 | |
61 | /** | |
62 | * Registered report() function | |
63 | */ | |
64 | leak_detective_report_cb_t report_cb; | |
65 | ||
66 | /** | |
67 | * Registered report() summary function | |
68 | */ | |
69 | leak_detective_summary_cb_t report_scb; | |
70 | ||
71 | /** | |
72 | * Registered user data for callbacks | |
73 | */ | |
74 | void *report_data; | |
552cc11b | 75 | }; |
5113680f MW |
76 | |
77 | /** | |
269f7f44 | 78 | * Magic value which helps to detect memory corruption. Yummy! |
5113680f | 79 | */ |
269f7f44 MW |
80 | #define MEMORY_HEADER_MAGIC 0x7ac0be11 |
81 | ||
b7ef3f62 MW |
82 | /** |
83 | * Magic written to tail of allocation | |
84 | */ | |
85 | #define MEMORY_TAIL_MAGIC 0xcafebabe | |
86 | ||
269f7f44 MW |
87 | /** |
88 | * Pattern which is filled in memory before freeing it | |
89 | */ | |
90 | #define MEMORY_FREE_PATTERN 0xFF | |
91 | ||
92 | /** | |
93 | * Pattern which is filled in newly allocated memory | |
94 | */ | |
95 | #define MEMORY_ALLOC_PATTERN 0xEE | |
96 | ||
5113680f | 97 | typedef struct memory_header_t memory_header_t; |
b7ef3f62 | 98 | typedef struct memory_tail_t memory_tail_t; |
5113680f MW |
99 | |
100 | /** | |
101 | * Header which is prepended to each allocated memory block | |
102 | */ | |
103 | struct memory_header_t { | |
7daf5226 | 104 | |
5113680f MW |
105 | /** |
106 | * Pointer to previous entry in linked list | |
107 | */ | |
108 | memory_header_t *previous; | |
7daf5226 | 109 | |
5113680f MW |
110 | /** |
111 | * Pointer to next entry in linked list | |
112 | */ | |
113 | memory_header_t *next; | |
7daf5226 | 114 | |
f7237cf3 MW |
115 | /** |
116 | * backtrace taken during (re-)allocation | |
117 | */ | |
118 | backtrace_t *backtrace; | |
7daf5226 | 119 | |
3117824f MW |
120 | /** |
121 | * Padding to make sizeof(memory_header_t) == 32 | |
122 | */ | |
b12c53ce | 123 | uint32_t padding[sizeof(void*) == sizeof(uint32_t) ? 3 : 0]; |
3117824f | 124 | |
35c93479 MW |
125 | /** |
126 | * Number of bytes following after the header | |
127 | */ | |
b12c53ce | 128 | uint32_t bytes; |
35c93479 | 129 | |
b7ef3f62 MW |
130 | /** |
131 | * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC | |
132 | */ | |
b12c53ce | 133 | uint32_t magic; |
7daf5226 | 134 | |
b7ef3f62 MW |
135 | }__attribute__((__packed__)); |
136 | ||
137 | /** | |
138 | * tail appended to each allocated memory block | |
139 | */ | |
140 | struct memory_tail_t { | |
141 | ||
142 | /** | |
143 | * Magic bytes to detect heap overflow, MEMORY_TAIL_MAGIC | |
144 | */ | |
b12c53ce | 145 | uint32_t magic; |
7daf5226 | 146 | |
b7ef3f62 | 147 | }__attribute__((__packed__)); |
5113680f MW |
148 | |
149 | /** | |
7daf5226 | 150 | * first mem header is just a dummy to chain |
5113680f MW |
151 | * the others on it... |
152 | */ | |
6b3292da | 153 | static memory_header_t first_header = { |
17211b6b | 154 | .magic = MEMORY_HEADER_MAGIC, |
5113680f MW |
155 | }; |
156 | ||
1d025fbc | 157 | /** |
17211b6b | 158 | * Spinlock to access header linked list |
1d025fbc | 159 | */ |
17211b6b MW |
160 | static spinlock_t *lock; |
161 | ||
162 | /** | |
163 | * Is leak detection currently enabled? | |
164 | */ | |
472efd38 TB |
165 | static bool enabled; |
166 | ||
167 | /** | |
168 | * Whether to report calls to free() with memory not allocated by us | |
169 | */ | |
170 | static bool ignore_unknown; | |
17211b6b MW |
171 | |
172 | /** | |
173 | * Is leak detection disabled for the current thread? | |
174 | */ | |
175 | static thread_value_t *thread_disabled; | |
5113680f | 176 | |
eb4f4551 MW |
177 | /** |
178 | * Installs the malloc hooks, enables leak detection | |
179 | */ | |
17211b6b | 180 | static void enable_leak_detective() |
eb4f4551 | 181 | { |
17211b6b MW |
182 | enabled = TRUE; |
183 | } | |
184 | ||
185 | /** | |
186 | * Uninstalls the malloc hooks, disables leak detection | |
187 | */ | |
188 | static void disable_leak_detective() | |
189 | { | |
190 | enabled = FALSE; | |
191 | } | |
192 | ||
193 | /** | |
194 | * Enable/Disable leak detective for the current thread | |
195 | * | |
196 | * @return Previous value | |
197 | */ | |
198 | static bool enable_thread(bool enable) | |
199 | { | |
200 | bool before; | |
201 | ||
202 | before = thread_disabled->get(thread_disabled) == NULL; | |
203 | thread_disabled->set(thread_disabled, enable ? NULL : (void*)TRUE); | |
204 | return before; | |
205 | } | |
206 | ||
3b26f04c MW |
207 | /** |
208 | * Add a header to the beginning of the list | |
209 | */ | |
210 | static void add_hdr(memory_header_t *hdr) | |
211 | { | |
212 | lock->lock(lock); | |
213 | hdr->next = first_header.next; | |
214 | if (hdr->next) | |
215 | { | |
216 | hdr->next->previous = hdr; | |
217 | } | |
218 | hdr->previous = &first_header; | |
219 | first_header.next = hdr; | |
220 | lock->unlock(lock); | |
221 | } | |
222 | ||
223 | /** | |
224 | * Remove a header from the list | |
225 | */ | |
226 | static void remove_hdr(memory_header_t *hdr) | |
227 | { | |
228 | lock->lock(lock); | |
229 | if (hdr->next) | |
230 | { | |
231 | hdr->next->previous = hdr->previous; | |
232 | } | |
233 | hdr->previous->next = hdr->next; | |
234 | lock->unlock(lock); | |
235 | } | |
236 | ||
237 | /** | |
238 | * Check if a header is in the list | |
239 | */ | |
240 | static bool has_hdr(memory_header_t *hdr) | |
241 | { | |
242 | memory_header_t *current; | |
243 | bool found = FALSE; | |
244 | ||
245 | lock->lock(lock); | |
246 | for (current = &first_header; current != NULL; current = current->next) | |
247 | { | |
248 | if (current == hdr) | |
249 | { | |
250 | found = TRUE; | |
251 | break; | |
252 | } | |
253 | } | |
254 | lock->unlock(lock); | |
255 | ||
256 | return found; | |
257 | } | |
258 | ||
d8f6f0c0 MW |
259 | #ifdef __APPLE__ |
260 | ||
261 | /** | |
262 | * Copy of original default zone, with functions we call in hooks | |
263 | */ | |
264 | static malloc_zone_t original; | |
265 | ||
266 | /** | |
267 | * Call original malloc() | |
268 | */ | |
269 | static void* real_malloc(size_t size) | |
270 | { | |
271 | return original.malloc(malloc_default_zone(), size); | |
272 | } | |
273 | ||
274 | /** | |
275 | * Call original free() | |
276 | */ | |
277 | static void real_free(void *ptr) | |
278 | { | |
279 | original.free(malloc_default_zone(), ptr); | |
280 | } | |
281 | ||
282 | /** | |
283 | * Call original realloc() | |
284 | */ | |
285 | static void* real_realloc(void *ptr, size_t size) | |
286 | { | |
287 | return original.realloc(malloc_default_zone(), ptr, size); | |
288 | } | |
289 | ||
290 | /** | |
291 | * Hook definition: static function with _hook suffix, takes additional zone | |
292 | */ | |
293 | #define HOOK(ret, name, ...) \ | |
294 | static ret name ## _hook(malloc_zone_t *_z, __VA_ARGS__) | |
295 | ||
296 | /** | |
297 | * forward declaration of hooks | |
298 | */ | |
299 | HOOK(void*, malloc, size_t bytes); | |
300 | HOOK(void*, calloc, size_t nmemb, size_t size); | |
301 | HOOK(void*, valloc, size_t size); | |
302 | HOOK(void, free, void *ptr); | |
303 | HOOK(void*, realloc, void *old, size_t bytes); | |
304 | ||
305 | /** | |
306 | * malloc zone size(), must consider the memory header prepended | |
307 | */ | |
308 | HOOK(size_t, size, const void *ptr) | |
309 | { | |
310 | bool before; | |
311 | size_t size; | |
312 | ||
313 | if (enabled) | |
314 | { | |
315 | before = enable_thread(FALSE); | |
316 | if (before) | |
317 | { | |
318 | ptr -= sizeof(memory_header_t); | |
319 | } | |
320 | } | |
321 | size = original.size(malloc_default_zone(), ptr); | |
322 | if (enabled) | |
323 | { | |
324 | enable_thread(before); | |
325 | } | |
326 | return size; | |
327 | } | |
328 | ||
329 | /** | |
330 | * Version of malloc zones we currently support | |
331 | */ | |
332 | #define MALLOC_ZONE_VERSION 8 /* Snow Leopard */ | |
333 | ||
334 | /** | |
335 | * Hook-in our malloc functions into the default zone | |
336 | */ | |
337 | static bool register_hooks() | |
338 | { | |
ef6d78d6 | 339 | static bool once = FALSE; |
d8f6f0c0 MW |
340 | malloc_zone_t *zone; |
341 | void *page; | |
342 | ||
ef6d78d6 MW |
343 | if (once) |
344 | { | |
345 | return TRUE; | |
346 | } | |
347 | once = TRUE; | |
348 | ||
d8f6f0c0 MW |
349 | zone = malloc_default_zone(); |
350 | if (zone->version != MALLOC_ZONE_VERSION) | |
351 | { | |
352 | DBG1(DBG_CFG, "malloc zone version %d unsupported (requiring %d)", | |
353 | zone->version, MALLOC_ZONE_VERSION); | |
354 | return FALSE; | |
355 | } | |
356 | ||
357 | original = *zone; | |
358 | ||
359 | page = (void*)((uintptr_t)zone / getpagesize() * getpagesize()); | |
360 | if (mprotect(page, getpagesize(), PROT_WRITE | PROT_READ) != 0) | |
361 | { | |
362 | DBG1(DBG_CFG, "malloc zone unprotection failed: %s", strerror(errno)); | |
363 | return FALSE; | |
364 | } | |
365 | ||
366 | zone->size = size_hook; | |
367 | zone->malloc = malloc_hook; | |
368 | zone->calloc = calloc_hook; | |
369 | zone->valloc = valloc_hook; | |
370 | zone->free = free_hook; | |
371 | zone->realloc = realloc_hook; | |
372 | ||
373 | /* those other functions can be NULLed out to not use them */ | |
374 | zone->batch_malloc = NULL; | |
375 | zone->batch_free = NULL; | |
376 | zone->memalign = NULL; | |
377 | zone->free_definite_size = NULL; | |
378 | ||
379 | return TRUE; | |
380 | } | |
381 | ||
382 | #else /* !__APPLE__ */ | |
383 | ||
17211b6b MW |
384 | /** |
385 | * dlsym() might do a malloc(), but we can't do one before we get the malloc() | |
386 | * function pointer. Use this minimalistic malloc implementation instead. | |
387 | */ | |
388 | static void* malloc_for_dlsym(size_t size) | |
389 | { | |
390 | static char buf[1024] = {}; | |
391 | static size_t used = 0; | |
392 | char *ptr; | |
393 | ||
394 | /* roundup to a multiple of 32 */ | |
395 | size = (size - 1) / 32 * 32 + 32; | |
396 | ||
397 | if (used + size > sizeof(buf)) | |
eb4f4551 | 398 | { |
17211b6b | 399 | return NULL; |
eb4f4551 | 400 | } |
17211b6b MW |
401 | ptr = buf + used; |
402 | used += size; | |
403 | return ptr; | |
eb4f4551 MW |
404 | } |
405 | ||
406 | /** | |
17211b6b MW |
407 | * Lookup a malloc function, while disabling wrappers |
408 | */ | |
409 | static void* get_malloc_fn(char *name) | |
410 | { | |
411 | bool before = FALSE; | |
412 | void *fn; | |
413 | ||
414 | if (enabled) | |
415 | { | |
416 | before = enable_thread(FALSE); | |
417 | } | |
418 | fn = dlsym(RTLD_NEXT, name); | |
419 | if (enabled) | |
420 | { | |
421 | enable_thread(before); | |
422 | } | |
423 | return fn; | |
424 | } | |
425 | ||
426 | /** | |
427 | * Call original malloc() | |
428 | */ | |
429 | static void* real_malloc(size_t size) | |
430 | { | |
431 | static void* (*fn)(size_t size); | |
432 | static int recursive = 0; | |
433 | ||
434 | if (!fn) | |
435 | { | |
436 | /* checking recursiveness should actually be thread-specific. But as | |
437 | * it is very likely that the first allocation is done before we go | |
438 | * multi-threaded, we keep it simple. */ | |
439 | if (recursive) | |
440 | { | |
441 | return malloc_for_dlsym(size); | |
442 | } | |
443 | recursive++; | |
444 | fn = get_malloc_fn("malloc"); | |
445 | recursive--; | |
446 | } | |
447 | return fn(size); | |
448 | } | |
449 | ||
450 | /** | |
451 | * Call original free() | |
eb4f4551 | 452 | */ |
17211b6b | 453 | static void real_free(void *ptr) |
eb4f4551 | 454 | { |
17211b6b MW |
455 | static void (*fn)(void *ptr); |
456 | ||
457 | if (!fn) | |
eb4f4551 | 458 | { |
17211b6b | 459 | fn = get_malloc_fn("free"); |
eb4f4551 | 460 | } |
17211b6b MW |
461 | return fn(ptr); |
462 | } | |
463 | ||
464 | /** | |
465 | * Call original realloc() | |
466 | */ | |
467 | static void* real_realloc(void *ptr, size_t size) | |
468 | { | |
469 | static void* (*fn)(void *ptr, size_t size); | |
470 | ||
471 | if (!fn) | |
472 | { | |
473 | fn = get_malloc_fn("realloc"); | |
474 | } | |
475 | return fn(ptr, size); | |
eb4f4551 MW |
476 | } |
477 | ||
d8f6f0c0 MW |
478 | /** |
479 | * Hook definition: plain function overloading existing malloc calls | |
480 | */ | |
481 | #define HOOK(ret, name, ...) ret name(__VA_ARGS__) | |
482 | ||
483 | /** | |
f5f7053b | 484 | * Hook initialization when not using hooks, resolve functions. |
d8f6f0c0 MW |
485 | */ |
486 | static bool register_hooks() | |
487 | { | |
f5f7053b | 488 | void *buf = real_malloc(8); |
b351acfe | 489 | buf = real_realloc(buf, 16); |
f5f7053b | 490 | real_free(buf); |
d8f6f0c0 MW |
491 | return TRUE; |
492 | } | |
493 | ||
494 | #endif /* !__APPLE__ */ | |
495 | ||
986d23bd | 496 | /** |
552cc11b | 497 | * Leak report white list |
151168f6 | 498 | * |
552cc11b | 499 | * List of functions using static allocation buffers or should be suppressed |
7daf5226 | 500 | * otherwise on leak report. |
986d23bd | 501 | */ |
95f9fa82 | 502 | static char *whitelist[] = { |
f7237cf3 MW |
503 | /* backtraces, including own */ |
504 | "backtrace_create", | |
ba10cd3c | 505 | "strerror_safe", |
7939864d | 506 | /* pthread stuff */ |
552cc11b MW |
507 | "pthread_create", |
508 | "pthread_setspecific", | |
9eb85cff | 509 | "__pthread_setspecific", |
7939864d | 510 | /* glibc functions */ |
552cc11b MW |
511 | "inet_ntoa", |
512 | "strerror", | |
0ffedbfb | 513 | "getprotobyname", |
552cc11b MW |
514 | "getprotobynumber", |
515 | "getservbyport", | |
516 | "getservbyname", | |
b97ca14d | 517 | "gethostbyname", |
3f9ec06f | 518 | "gethostbyname2", |
5a22a021 MW |
519 | "gethostbyname_r", |
520 | "gethostbyname2_r", | |
3f9ec06f | 521 | "getnetbyname", |
25b12c69 MW |
522 | "getpwnam_r", |
523 | "getgrnam_r", | |
552cc11b | 524 | "register_printf_function", |
86813bef | 525 | "register_printf_specifier", |
552cc11b MW |
526 | "syslog", |
527 | "vsyslog", | |
044e0dd1 MW |
528 | "__syslog_chk", |
529 | "__vsyslog_chk", | |
8f180660 | 530 | "__fprintf_chk", |
552cc11b MW |
531 | "getaddrinfo", |
532 | "setlocale", | |
70789d28 | 533 | "getpass", |
b97ca14d AS |
534 | "getpwent_r", |
535 | "setpwent", | |
536 | "endpwent", | |
0bac49b0 | 537 | "getspnam_r", |
c9418d4f AS |
538 | "getpwuid_r", |
539 | "initgroups", | |
7bda0f0c | 540 | "tzset", |
c87f4288 | 541 | "_IO_file_doallocate", |
7939864d MW |
542 | /* ignore dlopen, as we do not dlclose to get proper leak reports */ |
543 | "dlopen", | |
a3d92a37 | 544 | "dlerror", |
1caa265c | 545 | "dlclose", |
07bda3fe | 546 | "dlsym", |
7939864d | 547 | /* mysql functions */ |
552cc11b MW |
548 | "mysql_init_character_set", |
549 | "init_client_errs", | |
550 | "my_thread_init", | |
7939864d | 551 | /* fastcgi library */ |
cf4caefa | 552 | "FCGX_Init", |
7939864d MW |
553 | /* libxml */ |
554 | "xmlInitCharEncodingHandlers", | |
555 | "xmlInitParser", | |
556 | "xmlInitParserCtxt", | |
727b0f11 AS |
557 | /* libcurl */ |
558 | "Curl_client_write", | |
0b5d490e TB |
559 | /* libsoup */ |
560 | "soup_message_headers_append", | |
561 | "soup_message_headers_clear", | |
562 | "soup_message_headers_get_list", | |
563 | "soup_message_headers_get_one", | |
564 | "soup_session_abort", | |
565 | "soup_session_get_type", | |
f44e0efb TB |
566 | /* libldap */ |
567 | "ldap_int_initialize", | |
7939864d MW |
568 | /* ClearSilver */ |
569 | "nerr_init", | |
513a1a28 | 570 | /* libgcrypt */ |
d7281749 | 571 | "gcrypt_plugin_create", |
513a1a28 MW |
572 | "gcry_control", |
573 | "gcry_check_version", | |
a41d0932 MW |
574 | "gcry_randomize", |
575 | "gcry_create_nonce", | |
092550b0 MW |
576 | /* OpenSSL: These are needed for unit-tests only, the openssl plugin |
577 | * does properly clean up any memory during destroy(). */ | |
578 | "ECDSA_do_sign_ex", | |
579 | "ECDSA_verify", | |
580 | "RSA_new_method", | |
c1410cb0 TB |
581 | /* OpenSSL 1.1.0 does not cleanup anymore until the library is unloaded */ |
582 | "OPENSSL_init_crypto", | |
583 | "CRYPTO_THREAD_lock_new", | |
584 | "ERR_add_error_data", | |
0f7055b2 TB |
585 | "ERR_set_mark", |
586 | "ENGINE_load_builtin_engines", | |
587 | "OPENSSL_load_builtin_modules", | |
588 | "CONF_modules_load_file", | |
589 | "CONF_module_add", | |
6fc90cea TB |
590 | "RAND_DRBG_bytes", |
591 | "RAND_DRBG_generate", | |
592 | "RAND_DRBG_get0_master", | |
593 | "RAND_DRBG_get0_private", | |
594 | "RAND_DRBG_get0_public", | |
6eaec1e3 MW |
595 | /* OpenSSL libssl */ |
596 | "SSL_COMP_get_compression_methods", | |
50a9e845 MW |
597 | /* NSPR */ |
598 | "PR_CallOnce", | |
1888dd6b AS |
599 | /* libapr */ |
600 | "apr_pool_create_ex", | |
ec8426a3 | 601 | /* glib */ |
0b5d490e TB |
602 | "g_output_stream_write", |
603 | "g_resolver_lookup_by_name", | |
604 | "g_signal_connect_data", | |
605 | "g_socket_connection_factory_lookup_type", | |
ec8426a3 MW |
606 | "g_type_init_with_debug_flags", |
607 | "g_type_register_static", | |
608 | "g_type_class_ref", | |
609 | "g_type_create_instance", | |
610 | "g_type_add_interface_static", | |
611 | "g_type_interface_add_prerequisite", | |
0b5d490e TB |
612 | "g_private_set", |
613 | "g_queue_pop_tail", | |
ec8426a3 MW |
614 | /* libgpg */ |
615 | "gpg_err_init", | |
7cfa84f5 MW |
616 | /* gnutls */ |
617 | "gnutls_global_init", | |
fd2ade99 TB |
618 | /* Ada runtime */ |
619 | "system__tasking__initialize", | |
620 | "system__tasking__initialization__abort_defer", | |
621 | "system__tasking__stages__create_task", | |
95f9fa82 TB |
622 | /* in case external threads call into our code */ |
623 | "thread_current_id", | |
29e1c586 AS |
624 | /* FHH IMCs and IMVs */ |
625 | "TNC_IMC_NotifyConnectionChange", | |
626 | "TNC_IMV_NotifyConnectionChange", | |
c064a528 TB |
627 | /* Botan */ |
628 | "botan_public_key_load", | |
629 | "botan_privkey_create_ecdsa", | |
630 | "botan_privkey_create_ecdh", | |
631 | "botan_privkey_load_ecdh", | |
4bcc4bac | 632 | "botan_privkey_load", |
986d23bd MW |
633 | }; |
634 | ||
7e3f6299 MW |
635 | /** |
636 | * Some functions are hard to whitelist, as they don't use a symbol directly. | |
637 | * Use some static initialization to suppress them on leak reports | |
638 | */ | |
639 | static void init_static_allocations() | |
640 | { | |
56866ecf MW |
641 | struct tm tm; |
642 | time_t t = 0; | |
643 | ||
7e3f6299 | 644 | tzset(); |
56866ecf MW |
645 | gmtime_r(&t, &tm); |
646 | localtime_r(&t, &tm); | |
7e3f6299 | 647 | } |
5113680f | 648 | |
fce3b5c3 MW |
649 | /** |
650 | * Hashtable hash function | |
651 | */ | |
652 | static u_int hash(backtrace_t *key) | |
653 | { | |
654 | enumerator_t *enumerator; | |
655 | void *addr; | |
656 | u_int hash = 0; | |
657 | ||
658 | enumerator = key->create_frame_enumerator(key); | |
659 | while (enumerator->enumerate(enumerator, &addr)) | |
660 | { | |
661 | hash = chunk_hash_inc(chunk_from_thing(addr), hash); | |
662 | } | |
663 | enumerator->destroy(enumerator); | |
664 | ||
665 | return hash; | |
666 | } | |
667 | ||
668 | /** | |
669 | * Hashtable equals function | |
670 | */ | |
671 | static bool equals(backtrace_t *a, backtrace_t *b) | |
672 | { | |
673 | return a->equals(a, b); | |
674 | } | |
675 | ||
eb4f4551 MW |
676 | /** |
677 | * Summarize and print backtraces | |
678 | */ | |
679 | static int print_traces(private_leak_detective_t *this, | |
a426851f MW |
680 | leak_detective_report_cb_t cb, void *user, |
681 | int thresh, int thresh_count, | |
c93cf853 | 682 | bool detailed, int *whitelisted, size_t *sum) |
fce3b5c3 | 683 | { |
eb4f4551 | 684 | int leaks = 0; |
fce3b5c3 MW |
685 | memory_header_t *hdr; |
686 | enumerator_t *enumerator; | |
8eea2806 TB |
687 | hashtable_t *entries, *ignored = NULL; |
688 | backtrace_t *bt; | |
fce3b5c3 MW |
689 | struct { |
690 | /** associated backtrace */ | |
691 | backtrace_t *backtrace; | |
692 | /** total size of all allocations */ | |
693 | size_t bytes; | |
694 | /** number of allocations */ | |
695 | u_int count; | |
696 | } *entry; | |
17211b6b | 697 | bool before; |
fce3b5c3 | 698 | |
17211b6b | 699 | before = enable_thread(FALSE); |
fce3b5c3 MW |
700 | |
701 | entries = hashtable_create((hashtable_hash_t)hash, | |
702 | (hashtable_equals_t)equals, 1024); | |
8eea2806 TB |
703 | if (whitelisted) |
704 | { | |
705 | ignored = hashtable_create((hashtable_hash_t)hash, | |
706 | (hashtable_equals_t)equals, 1024); | |
707 | } | |
708 | ||
17211b6b | 709 | lock->lock(lock); |
fce3b5c3 MW |
710 | for (hdr = first_header.next; hdr != NULL; hdr = hdr->next) |
711 | { | |
8eea2806 | 712 | if (whitelisted) |
eb4f4551 | 713 | { |
8eea2806 TB |
714 | bt = ignored->get(ignored, hdr->backtrace); |
715 | if (!bt) | |
716 | { | |
717 | if (hdr->backtrace->contains_function(hdr->backtrace, whitelist, | |
718 | countof(whitelist))) | |
719 | { | |
720 | bt = hdr->backtrace; | |
721 | ignored->put(ignored, bt, bt); | |
722 | } | |
723 | } | |
724 | if (bt) | |
725 | { | |
726 | (*whitelisted)++; | |
727 | continue; | |
728 | } | |
eb4f4551 | 729 | } |
fce3b5c3 MW |
730 | entry = entries->get(entries, hdr->backtrace); |
731 | if (entry) | |
732 | { | |
733 | entry->bytes += hdr->bytes; | |
734 | entry->count++; | |
735 | } | |
736 | else | |
737 | { | |
738 | INIT(entry, | |
f960b390 | 739 | .backtrace = hdr->backtrace->clone(hdr->backtrace), |
fce3b5c3 MW |
740 | .bytes = hdr->bytes, |
741 | .count = 1, | |
742 | ); | |
f960b390 | 743 | entries->put(entries, entry->backtrace, entry); |
fce3b5c3 | 744 | } |
c93cf853 MW |
745 | if (sum) |
746 | { | |
747 | *sum += hdr->bytes; | |
748 | } | |
eb4f4551 | 749 | leaks++; |
fce3b5c3 | 750 | } |
17211b6b | 751 | lock->unlock(lock); |
8eea2806 | 752 | DESTROY_IF(ignored); |
a426851f | 753 | |
fce3b5c3 MW |
754 | enumerator = entries->create_enumerator(entries); |
755 | while (enumerator->enumerate(enumerator, NULL, &entry)) | |
756 | { | |
a426851f | 757 | if (cb) |
fce3b5c3 | 758 | { |
a426851f MW |
759 | if (!thresh || entry->bytes >= thresh) |
760 | { | |
761 | if (!thresh_count || entry->count >= thresh_count) | |
762 | { | |
e4d2c9f1 TB |
763 | cb(user, entry->count, entry->bytes, entry->backtrace, |
764 | detailed); | |
a426851f MW |
765 | } |
766 | } | |
fce3b5c3 | 767 | } |
f960b390 | 768 | entry->backtrace->destroy(entry->backtrace); |
fce3b5c3 MW |
769 | free(entry); |
770 | } | |
771 | enumerator->destroy(enumerator); | |
772 | entries->destroy(entries); | |
773 | ||
17211b6b | 774 | enable_thread(before); |
eb4f4551 MW |
775 | return leaks; |
776 | } | |
777 | ||
778 | METHOD(leak_detective_t, report, void, | |
779 | private_leak_detective_t *this, bool detailed) | |
780 | { | |
781 | if (lib->leak_detective) | |
782 | { | |
01e15ab5 | 783 | int leaks, whitelisted = 0; |
c93cf853 | 784 | size_t sum = 0; |
eb4f4551 | 785 | |
a426851f MW |
786 | leaks = print_traces(this, this->report_cb, this->report_data, |
787 | 0, 0, detailed, &whitelisted, &sum); | |
788 | if (this->report_scb) | |
eb4f4551 | 789 | { |
a426851f | 790 | this->report_scb(this->report_data, leaks, sum, whitelisted); |
eb4f4551 | 791 | } |
eb4f4551 MW |
792 | } |
793 | } | |
794 | ||
a426851f MW |
795 | METHOD(leak_detective_t, set_report_cb, void, |
796 | private_leak_detective_t *this, leak_detective_report_cb_t cb, | |
797 | leak_detective_summary_cb_t scb, void *user) | |
798 | { | |
799 | this->report_cb = cb; | |
800 | this->report_scb = scb; | |
801 | this->report_data = user; | |
802 | } | |
803 | ||
01e15ab5 TB |
804 | METHOD(leak_detective_t, leaks, int, |
805 | private_leak_detective_t *this) | |
806 | { | |
a426851f | 807 | int whitelisted = 0; |
01e15ab5 | 808 | |
a426851f | 809 | return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL); |
01e15ab5 TB |
810 | } |
811 | ||
b46776ae MW |
812 | METHOD(leak_detective_t, set_state, bool, |
813 | private_leak_detective_t *this, bool enable) | |
814 | { | |
82d0317b | 815 | return enable_thread(enable); |
b46776ae MW |
816 | } |
817 | ||
eb4f4551 | 818 | METHOD(leak_detective_t, usage, void, |
a426851f MW |
819 | private_leak_detective_t *this, leak_detective_report_cb_t cb, |
820 | leak_detective_summary_cb_t scb, void *user) | |
eb4f4551 | 821 | { |
eb4f4551 | 822 | bool detailed; |
a426851f | 823 | int thresh, thresh_count, leaks, whitelisted = 0; |
c93cf853 | 824 | size_t sum = 0; |
eb4f4551 MW |
825 | |
826 | thresh = lib->settings->get_int(lib->settings, | |
8dc6e716 | 827 | "%s.leak_detective.usage_threshold", 10240, lib->ns); |
81959e64 | 828 | thresh_count = lib->settings->get_int(lib->settings, |
8dc6e716 | 829 | "%s.leak_detective.usage_threshold_count", 0, lib->ns); |
eb4f4551 | 830 | detailed = lib->settings->get_bool(lib->settings, |
8dc6e716 | 831 | "%s.leak_detective.detailed", TRUE, lib->ns); |
eb4f4551 | 832 | |
a426851f MW |
833 | leaks = print_traces(this, cb, user, thresh, thresh_count, |
834 | detailed, &whitelisted, &sum); | |
835 | if (scb) | |
836 | { | |
837 | scb(user, leaks, sum, whitelisted); | |
838 | } | |
fce3b5c3 MW |
839 | } |
840 | ||
5113680f | 841 | /** |
17211b6b | 842 | * Wrapped malloc() function |
5113680f | 843 | */ |
d8f6f0c0 | 844 | HOOK(void*, malloc, size_t bytes) |
5113680f MW |
845 | { |
846 | memory_header_t *hdr; | |
b7ef3f62 | 847 | memory_tail_t *tail; |
17211b6b | 848 | bool before; |
7daf5226 | 849 | |
17211b6b MW |
850 | if (!enabled || thread_disabled->get(thread_disabled)) |
851 | { | |
852 | return real_malloc(bytes); | |
853 | } | |
7daf5226 | 854 | |
17211b6b | 855 | hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); |
b7ef3f62 | 856 | tail = ((void*)hdr) + bytes + sizeof(memory_header_t); |
986d23bd | 857 | /* set to something which causes crashes */ |
b7ef3f62 MW |
858 | memset(hdr, MEMORY_ALLOC_PATTERN, |
859 | sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); | |
7daf5226 | 860 | |
17211b6b MW |
861 | before = enable_thread(FALSE); |
862 | hdr->backtrace = backtrace_create(2); | |
863 | enable_thread(before); | |
864 | ||
5113680f MW |
865 | hdr->magic = MEMORY_HEADER_MAGIC; |
866 | hdr->bytes = bytes; | |
b7ef3f62 | 867 | tail->magic = MEMORY_TAIL_MAGIC; |
7daf5226 | 868 | |
3b26f04c | 869 | add_hdr(hdr); |
7daf5226 | 870 | |
5113680f MW |
871 | return hdr + 1; |
872 | } | |
873 | ||
874 | /** | |
17211b6b | 875 | * Wrapped calloc() function |
5113680f | 876 | */ |
d8f6f0c0 | 877 | HOOK(void*, calloc, size_t nmemb, size_t size) |
17211b6b MW |
878 | { |
879 | void *ptr; | |
e0c59faa | 880 | volatile size_t total; |
17211b6b | 881 | |
e0c59faa TB |
882 | total = nmemb * size; |
883 | ptr = malloc(total); | |
884 | memset(ptr, 0, total); | |
17211b6b MW |
885 | |
886 | return ptr; | |
887 | } | |
888 | ||
d8f6f0c0 MW |
889 | /** |
890 | * Wrapped valloc(), TODO: currently not supported | |
891 | */ | |
892 | HOOK(void*, valloc, size_t size) | |
893 | { | |
894 | DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing"); | |
895 | return NULL; | |
896 | } | |
897 | ||
17211b6b MW |
898 | /** |
899 | * Wrapped free() function | |
900 | */ | |
d8f6f0c0 | 901 | HOOK(void, free, void *ptr) |
5113680f | 902 | { |
3b26f04c | 903 | memory_header_t *hdr; |
b7ef3f62 | 904 | memory_tail_t *tail; |
323f9f99 | 905 | backtrace_t *backtrace; |
3b26f04c | 906 | bool before; |
7daf5226 | 907 | |
17211b6b MW |
908 | if (!enabled || thread_disabled->get(thread_disabled)) |
909 | { | |
505c3187 TB |
910 | /* after deinitialization we might have to free stuff we allocated |
911 | * while we were enabled */ | |
912 | if (!first_header.magic && ptr) | |
913 | { | |
914 | hdr = ptr - sizeof(memory_header_t); | |
915 | tail = ptr + hdr->bytes; | |
916 | if (hdr->magic == MEMORY_HEADER_MAGIC && | |
917 | tail->magic == MEMORY_TAIL_MAGIC) | |
918 | { | |
919 | ptr = hdr; | |
920 | } | |
921 | } | |
17211b6b MW |
922 | real_free(ptr); |
923 | return; | |
924 | } | |
5113680f | 925 | /* allow freeing of NULL */ |
472efd38 | 926 | if (!ptr) |
5113680f MW |
927 | { |
928 | return; | |
929 | } | |
b7ef3f62 MW |
930 | hdr = ptr - sizeof(memory_header_t); |
931 | tail = ptr + hdr->bytes; | |
7daf5226 | 932 | |
17211b6b | 933 | before = enable_thread(FALSE); |
6c45e622 MW |
934 | if (hdr->magic != MEMORY_HEADER_MAGIC || |
935 | tail->magic != MEMORY_TAIL_MAGIC) | |
5113680f | 936 | { |
472efd38 TB |
937 | bool bt = TRUE; |
938 | ||
939 | /* check if memory appears to be allocated by our hooks */ | |
3b26f04c | 940 | if (has_hdr(hdr)) |
587ebae7 | 941 | { |
587ebae7 | 942 | fprintf(stderr, "freeing corrupted memory (%p): " |
472efd38 TB |
943 | "%u bytes, header magic 0x%x, tail magic 0x%x:\n", |
944 | ptr, hdr->bytes, hdr->magic, tail->magic); | |
945 | remove_hdr(hdr); | |
946 | ||
947 | if (hdr->magic == MEMORY_HEADER_MAGIC) | |
948 | { /* only access the old backtrace if header magic is valid */ | |
949 | hdr->backtrace->log(hdr->backtrace, stderr, TRUE); | |
950 | hdr->backtrace->destroy(hdr->backtrace); | |
951 | } | |
952 | else | |
953 | { | |
954 | fprintf(stderr, " header magic invalid, ignore backtrace of " | |
955 | "allocation\n"); | |
956 | } | |
587ebae7 MW |
957 | } |
958 | else | |
959 | { | |
472efd38 TB |
960 | /* just free this block of unknown memory */ |
961 | hdr = ptr; | |
962 | ||
963 | if (ignore_unknown) | |
964 | { | |
965 | bt = FALSE; | |
966 | } | |
967 | else | |
968 | { | |
969 | fprintf(stderr, "freeing unknown memory (%p):\n", ptr); | |
970 | } | |
971 | } | |
972 | if (bt) | |
973 | { | |
974 | backtrace = backtrace_create(2); | |
975 | backtrace->log(backtrace, stderr, TRUE); | |
976 | backtrace->destroy(backtrace); | |
587ebae7 | 977 | } |
5113680f | 978 | } |
6c45e622 | 979 | else |
b7ef3f62 | 980 | { |
3b26f04c | 981 | remove_hdr(hdr); |
17211b6b | 982 | |
f7237cf3 | 983 | hdr->backtrace->destroy(hdr->backtrace); |
7daf5226 | 984 | |
472efd38 | 985 | /* set mem to something remarkable */ |
587ebae7 MW |
986 | memset(hdr, MEMORY_FREE_PATTERN, |
987 | sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t)); | |
6c45e622 | 988 | } |
472efd38 | 989 | real_free(hdr); |
17211b6b | 990 | enable_thread(before); |
5113680f MW |
991 | } |
992 | ||
993 | /** | |
17211b6b | 994 | * Wrapped realloc() function |
5113680f | 995 | */ |
d8f6f0c0 | 996 | HOOK(void*, realloc, void *old, size_t bytes) |
5113680f | 997 | { |
a401efd0 | 998 | memory_header_t *hdr; |
b7ef3f62 | 999 | memory_tail_t *tail; |
f7237cf3 | 1000 | backtrace_t *backtrace; |
472efd38 | 1001 | bool before, have_backtrace = TRUE; |
7daf5226 | 1002 | |
17211b6b MW |
1003 | if (!enabled || thread_disabled->get(thread_disabled)) |
1004 | { | |
1005 | return real_realloc(old, bytes); | |
1006 | } | |
5113680f | 1007 | /* allow reallocation of NULL */ |
472efd38 | 1008 | if (!old) |
5113680f | 1009 | { |
17211b6b | 1010 | return malloc(bytes); |
5113680f | 1011 | } |
1e54e40f | 1012 | /* handle zero size as a free() */ |
472efd38 | 1013 | if (!bytes) |
1e54e40f MW |
1014 | { |
1015 | free(old); | |
1016 | return NULL; | |
1017 | } | |
7daf5226 | 1018 | |
a401efd0 | 1019 | hdr = old - sizeof(memory_header_t); |
b7ef3f62 | 1020 | tail = old + hdr->bytes; |
7daf5226 | 1021 | |
472efd38 | 1022 | before = enable_thread(FALSE); |
6c45e622 MW |
1023 | if (hdr->magic != MEMORY_HEADER_MAGIC || |
1024 | tail->magic != MEMORY_TAIL_MAGIC) | |
b7ef3f62 | 1025 | { |
472efd38 TB |
1026 | bool bt = TRUE; |
1027 | ||
1028 | /* check if memory appears to be allocated by our hooks */ | |
1029 | if (has_hdr(hdr)) | |
1030 | { | |
1031 | fprintf(stderr, "reallocating corrupted memory (%p, %u bytes): " | |
1032 | "%zu bytes, header magic 0x%x, tail magic 0x%x:\n", | |
1033 | old, hdr->bytes, bytes, hdr->magic, tail->magic); | |
1034 | remove_hdr(hdr); | |
1035 | ||
1036 | if (hdr->magic == MEMORY_HEADER_MAGIC) | |
1037 | { /* only access header fields (backtrace, bytes) if header magic | |
1038 | * is still valid */ | |
1039 | hdr->backtrace->log(hdr->backtrace, stderr, TRUE); | |
1040 | memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic)); | |
1041 | } | |
1042 | else | |
1043 | { | |
1044 | fprintf(stderr, " header magic invalid, ignore backtrace of " | |
1045 | "allocation\n"); | |
1046 | have_backtrace = FALSE; | |
1047 | hdr->magic = MEMORY_HEADER_MAGIC; | |
1048 | } | |
1049 | } | |
1050 | else | |
1051 | { | |
1052 | /* adopt this block of unknown memory */ | |
1053 | hdr = old; | |
1054 | have_backtrace = FALSE; | |
1055 | ||
1056 | if (ignore_unknown) | |
1057 | { | |
1058 | bt = FALSE; | |
1059 | } | |
1060 | else | |
1061 | { | |
1062 | fprintf(stderr, "reallocating unknown memory (%p): %zu bytes:\n", | |
1063 | old, bytes); | |
1064 | } | |
1065 | } | |
1066 | if (bt) | |
1067 | { | |
1068 | backtrace = backtrace_create(2); | |
1069 | backtrace->log(backtrace, stderr, TRUE); | |
1070 | backtrace->destroy(backtrace); | |
1071 | } | |
b7ef3f62 | 1072 | } |
6499354e MW |
1073 | else |
1074 | { | |
472efd38 | 1075 | remove_hdr(hdr); |
6499354e MW |
1076 | /* clear tail magic, allocate, set tail magic */ |
1077 | memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic)); | |
1078 | } | |
472efd38 | 1079 | |
17211b6b MW |
1080 | hdr = real_realloc(hdr, |
1081 | sizeof(memory_header_t) + bytes + sizeof(memory_tail_t)); | |
b7ef3f62 MW |
1082 | tail = ((void*)hdr) + bytes + sizeof(memory_header_t); |
1083 | tail->magic = MEMORY_TAIL_MAGIC; | |
6c45e622 | 1084 | |
a401efd0 MW |
1085 | /* update statistics */ |
1086 | hdr->bytes = bytes; | |
17211b6b | 1087 | |
472efd38 TB |
1088 | if (have_backtrace) |
1089 | { | |
1090 | hdr->backtrace->destroy(hdr->backtrace); | |
1091 | } | |
549eba30 | 1092 | hdr->backtrace = backtrace_create(2); |
17211b6b | 1093 | enable_thread(before); |
6c45e622 | 1094 | |
3b26f04c | 1095 | add_hdr(hdr); |
17211b6b | 1096 | |
a401efd0 | 1097 | return hdr + 1; |
5113680f MW |
1098 | } |
1099 | ||
42e0f26e MW |
1100 | METHOD(leak_detective_t, destroy, void, |
1101 | private_leak_detective_t *this) | |
5113680f | 1102 | { |
17211b6b MW |
1103 | disable_leak_detective(); |
1104 | lock->destroy(lock); | |
1105 | thread_disabled->destroy(thread_disabled); | |
552cc11b | 1106 | free(this); |
505c3187 | 1107 | first_header.magic = 0; |
f192526c | 1108 | first_header.next = NULL; |
6b3292da MW |
1109 | } |
1110 | ||
552cc11b MW |
1111 | /* |
1112 | * see header file | |
73760ca5 | 1113 | */ |
552cc11b | 1114 | leak_detective_t *leak_detective_create() |
73760ca5 | 1115 | { |
42e0f26e MW |
1116 | private_leak_detective_t *this; |
1117 | ||
1118 | INIT(this, | |
1119 | .public = { | |
1120 | .report = _report, | |
a426851f | 1121 | .set_report_cb = _set_report_cb, |
fce3b5c3 | 1122 | .usage = _usage, |
a426851f | 1123 | .leaks = _leaks, |
b46776ae | 1124 | .set_state = _set_state, |
42e0f26e MW |
1125 | .destroy = _destroy, |
1126 | }, | |
1127 | ); | |
7daf5226 | 1128 | |
adc11574 TB |
1129 | if (getenv("LEAK_DETECTIVE_DISABLE") != NULL) |
1130 | { | |
1131 | free(this); | |
1132 | return NULL; | |
1133 | } | |
472efd38 | 1134 | ignore_unknown = getenv("LEAK_DETECTIVE_IGNORE_UNKNOWN") != NULL; |
adc11574 | 1135 | |
17211b6b MW |
1136 | lock = spinlock_create(); |
1137 | thread_disabled = thread_value_create(NULL); | |
1138 | ||
7e3f6299 MW |
1139 | init_static_allocations(); |
1140 | ||
adc11574 | 1141 | if (register_hooks()) |
73760ca5 | 1142 | { |
adc11574 | 1143 | enable_leak_detective(); |
73760ca5 | 1144 | } |
552cc11b | 1145 | return &this->public; |
73760ca5 | 1146 | } |