Annotation of embedaddon/strongswan/src/libstrongswan/utils/leak_detective.c, revision 1.1.1.2
1.1 misho 1: /*
2: * Copyright (C) 2013-2018 Tobias Brunner
3: * Copyright (C) 2006-2013 Martin Willi
4: * HSR Hochschule fuer Technik Rapperswil
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: */
16:
17: #define _GNU_SOURCE
18: #include <stddef.h>
19: #include <string.h>
20: #include <stdio.h>
21: #include <signal.h>
22: #include <unistd.h>
23: #include <locale.h>
24: #ifdef HAVE_DLADDR
25: #include <dlfcn.h>
26: #endif
27: #include <time.h>
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__ */
38:
39: #include "leak_detective.h"
40:
41: #include <library.h>
42: #include <utils/utils.h>
43: #include <utils/debug.h>
44: #include <utils/backtrace.h>
45: #include <collections/hashtable.h>
46: #include <threading/thread_value.h>
47: #include <threading/spinlock.h>
48:
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;
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;
75: };
76:
77: /**
78: * Magic value which helps to detect memory corruption. Yummy!
79: */
80: #define MEMORY_HEADER_MAGIC 0x7ac0be11
81:
82: /**
83: * Magic written to tail of allocation
84: */
85: #define MEMORY_TAIL_MAGIC 0xcafebabe
86:
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:
97: typedef struct memory_header_t memory_header_t;
98: typedef struct memory_tail_t memory_tail_t;
99:
100: /**
101: * Header which is prepended to each allocated memory block
102: */
103: struct memory_header_t {
104:
105: /**
106: * Pointer to previous entry in linked list
107: */
108: memory_header_t *previous;
109:
110: /**
111: * Pointer to next entry in linked list
112: */
113: memory_header_t *next;
114:
115: /**
116: * backtrace taken during (re-)allocation
117: */
118: backtrace_t *backtrace;
119:
120: /**
121: * Padding to make sizeof(memory_header_t) == 32
122: */
123: uint32_t padding[sizeof(void*) == sizeof(uint32_t) ? 3 : 0];
124:
125: /**
126: * Number of bytes following after the header
127: */
128: uint32_t bytes;
129:
130: /**
131: * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC
132: */
133: uint32_t magic;
134:
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: */
145: uint32_t magic;
146:
147: }__attribute__((__packed__));
148:
149: /**
150: * first mem header is just a dummy to chain
151: * the others on it...
152: */
153: static memory_header_t first_header = {
154: .magic = MEMORY_HEADER_MAGIC,
155: };
156:
157: /**
158: * Spinlock to access header linked list
159: */
160: static spinlock_t *lock;
161:
162: /**
163: * Is leak detection currently enabled?
164: */
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;
171:
172: /**
173: * Is leak detection disabled for the current thread?
174: */
175: static thread_value_t *thread_disabled;
176:
177: /**
178: * Installs the malloc hooks, enables leak detection
179: */
180: static void enable_leak_detective()
181: {
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:
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:
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: {
339: static bool once = FALSE;
340: malloc_zone_t *zone;
341: void *page;
342:
343: if (once)
344: {
345: return TRUE;
346: }
347: once = TRUE;
348:
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:
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))
398: {
399: return NULL;
400: }
401: ptr = buf + used;
402: used += size;
403: return ptr;
404: }
405:
406: /**
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()
452: */
453: static void real_free(void *ptr)
454: {
455: static void (*fn)(void *ptr);
456:
457: if (!fn)
458: {
459: fn = get_malloc_fn("free");
460: }
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);
476: }
477:
478: /**
479: * Hook definition: plain function overloading existing malloc calls
480: */
481: #define HOOK(ret, name, ...) ret name(__VA_ARGS__)
482:
483: /**
484: * Hook initialization when not using hooks, resolve functions.
485: */
486: static bool register_hooks()
487: {
488: void *buf = real_malloc(8);
489: buf = real_realloc(buf, 16);
490: real_free(buf);
491: return TRUE;
492: }
493:
494: #endif /* !__APPLE__ */
495:
496: /**
497: * Leak report white list
498: *
499: * List of functions using static allocation buffers or should be suppressed
500: * otherwise on leak report.
501: */
502: static char *whitelist[] = {
503: /* backtraces, including own */
504: "backtrace_create",
505: "strerror_safe",
506: /* pthread stuff */
507: "pthread_create",
508: "pthread_setspecific",
509: "__pthread_setspecific",
510: /* glibc functions */
511: "inet_ntoa",
512: "strerror",
513: "getprotobyname",
514: "getprotobynumber",
515: "getservbyport",
516: "getservbyname",
517: "gethostbyname",
518: "gethostbyname2",
519: "gethostbyname_r",
520: "gethostbyname2_r",
521: "getnetbyname",
522: "getpwnam_r",
523: "getgrnam_r",
524: "register_printf_function",
525: "register_printf_specifier",
526: "syslog",
527: "vsyslog",
528: "__syslog_chk",
529: "__vsyslog_chk",
530: "__fprintf_chk",
531: "getaddrinfo",
532: "setlocale",
533: "getpass",
534: "getpwent_r",
535: "setpwent",
536: "endpwent",
537: "getspnam_r",
538: "getpwuid_r",
539: "initgroups",
540: "tzset",
541: "_IO_file_doallocate",
542: /* ignore dlopen, as we do not dlclose to get proper leak reports */
543: "dlopen",
544: "dlerror",
545: "dlclose",
546: "dlsym",
547: /* mysql functions */
548: "mysql_init_character_set",
549: "init_client_errs",
550: "my_thread_init",
551: /* fastcgi library */
552: "FCGX_Init",
553: /* libxml */
554: "xmlInitCharEncodingHandlers",
555: "xmlInitParser",
556: "xmlInitParserCtxt",
557: /* libcurl */
558: "Curl_client_write",
559: /* libsoup */
560: "soup_add_timeout",
561: "soup_headers_parse_response",
562: "soup_message_headers_append",
563: "soup_message_headers_clear",
564: "soup_message_headers_get_list",
565: "soup_message_headers_get_one",
566: "soup_session_abort",
567: "soup_session_get_type",
568: "soup_session_remove_feature",
569: /* libldap */
570: "ldap_int_initialize",
571: /* ClearSilver */
572: "nerr_init",
573: /* libgcrypt */
574: "gcrypt_plugin_create",
575: "gcry_control",
576: "gcry_check_version",
577: "gcry_randomize",
578: "gcry_create_nonce",
579: /* OpenSSL: These are needed for unit-tests only, the openssl plugin
580: * does properly clean up any memory during destroy(). */
581: "ECDSA_do_sign_ex",
582: "ECDSA_verify",
583: "RSA_new_method",
584: /* OpenSSL 1.1.0 does not cleanup anymore until the library is unloaded */
585: "OPENSSL_init_crypto",
1.1.1.2 ! misho 586: "OPENSSL_init_ssl",
1.1 misho 587: "CRYPTO_THREAD_lock_new",
588: "ERR_add_error_data",
589: "ERR_set_mark",
590: "ENGINE_load_builtin_engines",
591: "OPENSSL_load_builtin_modules",
592: "CONF_modules_load_file",
593: "CONF_module_add",
594: "RAND_DRBG_bytes",
595: "RAND_DRBG_generate",
596: "RAND_DRBG_get0_master",
597: "RAND_DRBG_get0_private",
598: "RAND_DRBG_get0_public",
1.1.1.2 ! misho 599: /* We get this via libcurl and OpenSSL 1.1.1 */
! 600: "CRYPTO_get_ex_new_index",
1.1 misho 601: /* OpenSSL libssl */
602: "SSL_COMP_get_compression_methods",
603: /* NSPR */
604: "PR_CallOnce",
605: /* libapr */
606: "apr_pool_create_ex",
607: /* glib */
608: "g_output_stream_write",
609: "g_resolver_lookup_by_name",
610: "g_signal_connect_data",
611: "g_socket_connection_factory_lookup_type",
612: "g_type_init_with_debug_flags",
613: "g_type_register_static",
614: "g_type_class_ref",
615: "g_type_create_instance",
616: "g_type_add_interface_static",
617: "g_type_interface_add_prerequisite",
618: "g_private_set",
619: "g_queue_pop_tail",
620: /* libgpg */
621: "gpg_err_init",
622: /* gnutls */
623: "gnutls_global_init",
624: /* Ada runtime */
625: "system__tasking__initialize",
626: "system__tasking__initialization__abort_defer",
627: "system__tasking__stages__create_task",
1.1.1.2 ! misho 628: "system__task_primitives__operations__register_foreign_thread__2",
1.1 misho 629: /* in case external threads call into our code */
630: "thread_current_id",
631: /* FHH IMCs and IMVs */
632: "TNC_IMC_NotifyConnectionChange",
633: "TNC_IMV_NotifyConnectionChange",
634: /* Botan */
635: "botan_public_key_load",
636: "botan_privkey_create",
637: "botan_privkey_load_ecdh",
638: "botan_privkey_load",
639: };
640:
641: /**
642: * Some functions are hard to whitelist, as they don't use a symbol directly.
643: * Use some static initialization to suppress them on leak reports
644: */
645: static void init_static_allocations()
646: {
647: struct tm tm;
648: time_t t = 0;
649:
650: tzset();
651: gmtime_r(&t, &tm);
652: localtime_r(&t, &tm);
653: }
654:
655: /**
656: * Hashtable hash function
657: */
658: static u_int hash(backtrace_t *key)
659: {
660: enumerator_t *enumerator;
661: void *addr;
662: u_int hash = 0;
663:
664: enumerator = key->create_frame_enumerator(key);
665: while (enumerator->enumerate(enumerator, &addr))
666: {
667: hash = chunk_hash_inc(chunk_from_thing(addr), hash);
668: }
669: enumerator->destroy(enumerator);
670:
671: return hash;
672: }
673:
674: /**
675: * Hashtable equals function
676: */
677: static bool equals(backtrace_t *a, backtrace_t *b)
678: {
679: return a->equals(a, b);
680: }
681:
682: /**
683: * Summarize and print backtraces
684: */
685: static int print_traces(private_leak_detective_t *this,
686: leak_detective_report_cb_t cb, void *user,
687: int thresh, int thresh_count,
688: bool detailed, int *whitelisted, size_t *sum)
689: {
690: int leaks = 0;
691: memory_header_t *hdr;
692: enumerator_t *enumerator;
693: hashtable_t *entries, *ignored = NULL;
694: backtrace_t *bt;
695: struct {
696: /** associated backtrace */
697: backtrace_t *backtrace;
698: /** total size of all allocations */
699: size_t bytes;
700: /** number of allocations */
701: u_int count;
702: } *entry;
703: bool before;
704:
705: before = enable_thread(FALSE);
706:
707: entries = hashtable_create((hashtable_hash_t)hash,
708: (hashtable_equals_t)equals, 1024);
709: if (whitelisted)
710: {
711: ignored = hashtable_create((hashtable_hash_t)hash,
712: (hashtable_equals_t)equals, 1024);
713: }
714:
715: lock->lock(lock);
716: for (hdr = first_header.next; hdr != NULL; hdr = hdr->next)
717: {
718: if (whitelisted)
719: {
720: bt = ignored->get(ignored, hdr->backtrace);
721: if (!bt)
722: {
723: if (hdr->backtrace->contains_function(hdr->backtrace, whitelist,
724: countof(whitelist)))
725: {
726: bt = hdr->backtrace;
727: ignored->put(ignored, bt, bt);
728: }
729: }
730: if (bt)
731: {
732: (*whitelisted)++;
733: continue;
734: }
735: }
736: entry = entries->get(entries, hdr->backtrace);
737: if (entry)
738: {
739: entry->bytes += hdr->bytes;
740: entry->count++;
741: }
742: else
743: {
744: INIT(entry,
745: .backtrace = hdr->backtrace->clone(hdr->backtrace),
746: .bytes = hdr->bytes,
747: .count = 1,
748: );
749: entries->put(entries, entry->backtrace, entry);
750: }
751: if (sum)
752: {
753: *sum += hdr->bytes;
754: }
755: leaks++;
756: }
757: lock->unlock(lock);
758: DESTROY_IF(ignored);
759:
760: enumerator = entries->create_enumerator(entries);
761: while (enumerator->enumerate(enumerator, NULL, &entry))
762: {
763: if (cb)
764: {
765: if (!thresh || entry->bytes >= thresh)
766: {
767: if (!thresh_count || entry->count >= thresh_count)
768: {
769: cb(user, entry->count, entry->bytes, entry->backtrace,
770: detailed);
771: }
772: }
773: }
774: entry->backtrace->destroy(entry->backtrace);
775: free(entry);
776: }
777: enumerator->destroy(enumerator);
778: entries->destroy(entries);
779:
780: enable_thread(before);
781: return leaks;
782: }
783:
784: METHOD(leak_detective_t, report, void,
785: private_leak_detective_t *this, bool detailed)
786: {
787: if (lib->leak_detective)
788: {
789: int leaks, whitelisted = 0;
790: size_t sum = 0;
791:
792: leaks = print_traces(this, this->report_cb, this->report_data,
793: 0, 0, detailed, &whitelisted, &sum);
794: if (this->report_scb)
795: {
796: this->report_scb(this->report_data, leaks, sum, whitelisted);
797: }
798: }
799: }
800:
801: METHOD(leak_detective_t, set_report_cb, void,
802: private_leak_detective_t *this, leak_detective_report_cb_t cb,
803: leak_detective_summary_cb_t scb, void *user)
804: {
805: this->report_cb = cb;
806: this->report_scb = scb;
807: this->report_data = user;
808: }
809:
810: METHOD(leak_detective_t, leaks, int,
811: private_leak_detective_t *this)
812: {
813: int whitelisted = 0;
814:
815: return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL);
816: }
817:
818: METHOD(leak_detective_t, set_state, bool,
819: private_leak_detective_t *this, bool enable)
820: {
821: return enable_thread(enable);
822: }
823:
824: METHOD(leak_detective_t, usage, void,
825: private_leak_detective_t *this, leak_detective_report_cb_t cb,
826: leak_detective_summary_cb_t scb, void *user)
827: {
828: bool detailed;
829: int thresh, thresh_count, leaks, whitelisted = 0;
830: size_t sum = 0;
831:
832: thresh = lib->settings->get_int(lib->settings,
833: "%s.leak_detective.usage_threshold", 10240, lib->ns);
834: thresh_count = lib->settings->get_int(lib->settings,
835: "%s.leak_detective.usage_threshold_count", 0, lib->ns);
836: detailed = lib->settings->get_bool(lib->settings,
837: "%s.leak_detective.detailed", TRUE, lib->ns);
838:
839: leaks = print_traces(this, cb, user, thresh, thresh_count,
840: detailed, &whitelisted, &sum);
841: if (scb)
842: {
843: scb(user, leaks, sum, whitelisted);
844: }
845: }
846:
847: /**
848: * Wrapped malloc() function
849: */
850: HOOK(void*, malloc, size_t bytes)
851: {
852: memory_header_t *hdr;
853: memory_tail_t *tail;
854: bool before;
855:
856: if (!enabled || thread_disabled->get(thread_disabled))
857: {
858: return real_malloc(bytes);
859: }
860:
861: hdr = real_malloc(sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
862: tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
863: /* set to something which causes crashes */
864: memset(hdr, MEMORY_ALLOC_PATTERN,
865: sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
866:
867: before = enable_thread(FALSE);
868: hdr->backtrace = backtrace_create(2);
869: enable_thread(before);
870:
871: hdr->magic = MEMORY_HEADER_MAGIC;
872: hdr->bytes = bytes;
873: tail->magic = MEMORY_TAIL_MAGIC;
874:
875: add_hdr(hdr);
876:
877: return hdr + 1;
878: }
879:
880: /**
881: * Wrapped calloc() function
882: */
883: HOOK(void*, calloc, size_t nmemb, size_t size)
884: {
885: void *ptr;
886: volatile size_t total;
887:
888: total = nmemb * size;
889: ptr = malloc(total);
890: memset(ptr, 0, total);
891:
892: return ptr;
893: }
894:
895: /**
896: * Wrapped valloc(), TODO: currently not supported
897: */
898: HOOK(void*, valloc, size_t size)
899: {
900: DBG1(DBG_LIB, "valloc() used, but leak-detective hook missing");
901: return NULL;
902: }
903:
904: /**
905: * Wrapped free() function
906: */
907: HOOK(void, free, void *ptr)
908: {
909: memory_header_t *hdr;
910: memory_tail_t *tail;
911: backtrace_t *backtrace;
912: bool before;
913:
914: if (!enabled || thread_disabled->get(thread_disabled))
915: {
916: /* after deinitialization we might have to free stuff we allocated
917: * while we were enabled */
918: if (!first_header.magic && ptr)
919: {
920: hdr = ptr - sizeof(memory_header_t);
921: tail = ptr + hdr->bytes;
922: if (hdr->magic == MEMORY_HEADER_MAGIC &&
923: tail->magic == MEMORY_TAIL_MAGIC)
924: {
925: ptr = hdr;
926: }
927: }
928: real_free(ptr);
929: return;
930: }
931: /* allow freeing of NULL */
932: if (!ptr)
933: {
934: return;
935: }
936: hdr = ptr - sizeof(memory_header_t);
937: tail = ptr + hdr->bytes;
938:
939: before = enable_thread(FALSE);
940: if (hdr->magic != MEMORY_HEADER_MAGIC ||
941: tail->magic != MEMORY_TAIL_MAGIC)
942: {
943: bool bt = TRUE;
944:
945: /* check if memory appears to be allocated by our hooks */
946: if (has_hdr(hdr))
947: {
948: fprintf(stderr, "freeing corrupted memory (%p): "
949: "%u bytes, header magic 0x%x, tail magic 0x%x:\n",
950: ptr, hdr->bytes, hdr->magic, tail->magic);
951: remove_hdr(hdr);
952:
953: if (hdr->magic == MEMORY_HEADER_MAGIC)
954: { /* only access the old backtrace if header magic is valid */
955: hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
956: hdr->backtrace->destroy(hdr->backtrace);
957: }
958: else
959: {
960: fprintf(stderr, " header magic invalid, ignore backtrace of "
961: "allocation\n");
962: }
963: }
964: else
965: {
966: /* just free this block of unknown memory */
967: hdr = ptr;
968:
969: if (ignore_unknown)
970: {
971: bt = FALSE;
972: }
973: else
974: {
975: fprintf(stderr, "freeing unknown memory (%p):\n", ptr);
976: }
977: }
978: if (bt)
979: {
980: backtrace = backtrace_create(2);
981: backtrace->log(backtrace, stderr, TRUE);
982: backtrace->destroy(backtrace);
983: }
984: }
985: else
986: {
987: remove_hdr(hdr);
988:
989: hdr->backtrace->destroy(hdr->backtrace);
990:
991: /* set mem to something remarkable */
992: memset(hdr, MEMORY_FREE_PATTERN,
993: sizeof(memory_header_t) + hdr->bytes + sizeof(memory_tail_t));
994: }
995: real_free(hdr);
996: enable_thread(before);
997: }
998:
999: /**
1000: * Wrapped realloc() function
1001: */
1002: HOOK(void*, realloc, void *old, size_t bytes)
1003: {
1004: memory_header_t *hdr;
1005: memory_tail_t *tail;
1006: backtrace_t *backtrace;
1007: bool before, have_backtrace = TRUE;
1008:
1009: if (!enabled || thread_disabled->get(thread_disabled))
1010: {
1011: return real_realloc(old, bytes);
1012: }
1013: /* allow reallocation of NULL */
1014: if (!old)
1015: {
1016: return malloc(bytes);
1017: }
1018: /* handle zero size as a free() */
1019: if (!bytes)
1020: {
1021: free(old);
1022: return NULL;
1023: }
1024:
1025: hdr = old - sizeof(memory_header_t);
1026: tail = old + hdr->bytes;
1027:
1028: before = enable_thread(FALSE);
1029: if (hdr->magic != MEMORY_HEADER_MAGIC ||
1030: tail->magic != MEMORY_TAIL_MAGIC)
1031: {
1032: bool bt = TRUE;
1033:
1034: /* check if memory appears to be allocated by our hooks */
1035: if (has_hdr(hdr))
1036: {
1037: fprintf(stderr, "reallocating corrupted memory (%p, %u bytes): "
1038: "%zu bytes, header magic 0x%x, tail magic 0x%x:\n",
1039: old, hdr->bytes, bytes, hdr->magic, tail->magic);
1040: remove_hdr(hdr);
1041:
1042: if (hdr->magic == MEMORY_HEADER_MAGIC)
1043: { /* only access header fields (backtrace, bytes) if header magic
1044: * is still valid */
1045: hdr->backtrace->log(hdr->backtrace, stderr, TRUE);
1046: memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
1047: }
1048: else
1049: {
1050: fprintf(stderr, " header magic invalid, ignore backtrace of "
1051: "allocation\n");
1052: have_backtrace = FALSE;
1053: hdr->magic = MEMORY_HEADER_MAGIC;
1054: }
1055: }
1056: else
1057: {
1058: /* adopt this block of unknown memory */
1059: hdr = old;
1060: have_backtrace = FALSE;
1061:
1062: if (ignore_unknown)
1063: {
1064: bt = FALSE;
1065: }
1066: else
1067: {
1068: fprintf(stderr, "reallocating unknown memory (%p): %zu bytes:\n",
1069: old, bytes);
1070: }
1071: }
1072: if (bt)
1073: {
1074: backtrace = backtrace_create(2);
1075: backtrace->log(backtrace, stderr, TRUE);
1076: backtrace->destroy(backtrace);
1077: }
1078: }
1079: else
1080: {
1081: remove_hdr(hdr);
1082: /* clear tail magic, allocate, set tail magic */
1083: memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic));
1084: }
1085:
1086: hdr = real_realloc(hdr,
1087: sizeof(memory_header_t) + bytes + sizeof(memory_tail_t));
1088: tail = ((void*)hdr) + bytes + sizeof(memory_header_t);
1089: tail->magic = MEMORY_TAIL_MAGIC;
1090:
1091: /* update statistics */
1092: hdr->bytes = bytes;
1093:
1094: if (have_backtrace)
1095: {
1096: hdr->backtrace->destroy(hdr->backtrace);
1097: }
1098: hdr->backtrace = backtrace_create(2);
1099: enable_thread(before);
1100:
1101: add_hdr(hdr);
1102:
1103: return hdr + 1;
1104: }
1105:
1106: METHOD(leak_detective_t, destroy, void,
1107: private_leak_detective_t *this)
1108: {
1109: disable_leak_detective();
1110: lock->destroy(lock);
1111: thread_disabled->destroy(thread_disabled);
1112: free(this);
1113: first_header.magic = 0;
1114: first_header.next = NULL;
1115: }
1116:
1117: /*
1118: * see header file
1119: */
1120: leak_detective_t *leak_detective_create()
1121: {
1122: private_leak_detective_t *this;
1123:
1124: INIT(this,
1125: .public = {
1126: .report = _report,
1127: .set_report_cb = _set_report_cb,
1128: .usage = _usage,
1129: .leaks = _leaks,
1130: .set_state = _set_state,
1131: .destroy = _destroy,
1132: },
1133: );
1134:
1135: if (getenv("LEAK_DETECTIVE_DISABLE") != NULL)
1136: {
1137: free(this);
1138: return NULL;
1139: }
1140: ignore_unknown = getenv("LEAK_DETECTIVE_IGNORE_UNKNOWN") != NULL;
1141:
1142: lock = spinlock_create();
1143: thread_disabled = thread_value_create(NULL);
1144:
1145: init_static_allocations();
1146:
1147: if (register_hooks())
1148: {
1149: enable_leak_detective();
1150: }
1151: return &this->public;
1152: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>