5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2002 - 2008 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 /* This file implements memory leak checker and basic memory corruption
21 and double free checker. It is multi-thread safe. It does the
24 o Tracks all memory allocations and report any unfreed memory at the
25 end of the program with backtrace where the memory was allocated.
27 o Checks if a memory location has been freed already and abort the
28 program with the backtrace of the location of the double free.
30 o Checks if a given pointer has been allocated at all and abort the
31 program with the backtrace where the invalid free was given.
33 o Checks at the time of free if the memory was written out of bounds
34 (overflow) and abort with the backtrace of the free. The backtrace
35 might not help to find the overflow but at least it is detected.
36 By setting SILC_MALLOC_DUMP the memory is dummped to help and see
39 o Can detect if the memory is read or written out of bounds (overflow)
40 and abort immediately the with the backtrace when the illegal access
41 occurs. This can be enabled by using SILC_MALLOC_PROTECT.
43 The following environment variables can be used:
47 When set to value 1, the program doesn't actually free any memory.
48 This provides more detailed information especially in case of double
49 free. If the location of the double free cannot be located, by
50 setting this variable the program will show where the memory was
51 originally allocated and freed.
55 When set to value 1, in case of fatal error, dumps the memory location,
56 if possible. This can help see what the memory contains.
60 When set to value 1 each allocation will have an inaccesible memory
61 page following the allocated memory area. This will detect if the
62 the memory is accessed (read or write) beyond its boundaries. This
63 will help to identify the place where illegal memory access occurs.
65 To work correctly this of course expects that code uses SILC memory
66 allocation and access routines.
72 #ifdef SILC_STACKTRACE
73 #if defined(HAVE_BACKTRACE)
75 #endif /* HAVE_BACKTRACE */
77 #if !defined(__APPLE__)
80 #include <sys/malloc.h>
84 static void *st_blocks = NULL;
85 static unsigned long st_blocks_count = 0;
86 static unsigned long st_num_malloc = 0;
87 static SilcBool dump = FALSE;
88 static SilcBool no_free = FALSE;
89 static SilcBool dump_mem = FALSE;
90 static SilcUInt32 pg = 0;
91 static SilcMutex lock = NULL;
94 #define SILC_ST_DEPTH 15
96 #define SILC_ST_DEPTH 8
97 #endif /* SILC_DEBUG */
99 /* Memory block with stack trace */
100 typedef struct SilcStBlockStruct {
101 struct SilcStBlockStruct *next;
102 struct SilcStBlockStruct *prev;
103 void *stack[SILC_ST_DEPTH]; /* Stack trace */
104 const char *file; /* Allocation file in program */
105 const char *free_file; /* Free file in program */
106 SilcUInt32 size; /* Allocated memory size */
107 SilcUInt16 line; /* Allocation line in program */
108 SilcUInt16 free_line; /* Free line in program */
109 SilcUInt16 depth; /* Depth of stack trace */
110 SilcUInt16 dumpped; /* Block is dumpped */
111 SilcUInt32 bound; /* Top bound */
114 #define SILC_ST_TOP_BOUND 0xfeed1977
115 #define SILC_ST_BOTTOM_BOUND 0x9152beef
116 #define SILC_ST_GET_SIZE(size) ((size + sizeof(struct SilcStBlockStruct) + 4))
117 #define SILC_ST_GET_STACK(p) ((SilcStBlock)(((unsigned char *)p) - \
118 sizeof(struct SilcStBlockStruct)))
119 #define SILC_ST_GET_PTR(p) (((unsigned char *)p) + \
120 sizeof(struct SilcStBlockStruct))
121 #define SILC_ST_GET_BOUND(p, size) (SilcUInt32 *)(((unsigned char *)p) + \
122 SILC_ST_GET_SIZE(size) - 4)
124 #define SILC_ST_ALIGN(bytes, align) (((bytes) + (align - 1)) & ~(align - 1))
125 #define SILC_ST_GET_SIZE_ALIGN(size, align) \
126 SILC_ST_ALIGN(SILC_ST_GET_SIZE(size) - 4, align)
127 #define SILC_ST_GET_PTR_ALIGN(stack, align) \
128 (((unsigned char *)stack) - (SILC_ST_GET_SIZE_ALIGN(stack->size, pg) - \
129 SILC_ST_GET_SIZE(stack->size)) - 4)
130 #define SILC_ST_GET_STACK_ALIGN(p, size, align) \
131 ((SilcStBlock)(((unsigned char *)p) + (SILC_ST_GET_SIZE_ALIGN(size, pg) - \
132 SILC_ST_GET_SIZE(size)) + 4))
134 #define silc_hexdump(ptr, size, file) \
135 silc_log_output_hexdump("", "", 0, ptr, size, "")
137 int silc_st_backtrace(void **stack)
139 #if defined(HAVE_BACKTRACE)
140 return backtrace(stack, SILC_ST_DEPTH);
144 asm volatile ("movl %%ebp, %0" : "=r" (fp));
145 for (depth = 0; fp; depth++) {
146 if (depth == SILC_ST_DEPTH)
149 /* Get program pointer and frame pointer from this frame */
150 stack[depth] = *((void **)(((unsigned char *)fp) + 4));
154 #endif /* HAVE_BACKTRACE */
157 char **silc_st_backtrace_symbols(void **stack, int depth)
159 #if defined(HAVE_BACKTRACE)
160 return backtrace_symbols(stack, depth);
163 #endif /* HAVE_BACKTRACE */
166 void silc_st_backtrace_symbols_stderr(void **stack, int depth)
168 #if defined(HAVE_BACKTRACE)
169 backtrace_symbols_fd(stack, depth, 2);
172 for (i = 0; i < depth; i++)
173 fprintf(stderr, "? [%p]\n", stack[i]);
174 #endif /* HAVE_BACKTRACE */
177 void silc_st_abort(SilcStBlock stack, const char *file, int line,
180 void *bt[SILC_ST_DEPTH];
186 vfprintf(stderr, fmt, va);
189 fprintf(stderr, "----- BACKTRACE -----\n%s:%d:\n", file, line);
190 btc = silc_st_backtrace(bt);
191 silc_st_backtrace_symbols_stderr(bt, btc);
194 fprintf(stderr, "----- MEMORY TRACE -----\n");
195 if (stack->free_file)
196 fprintf(stderr, "Freed at: %s:%d\n", stack->free_file,
198 fprintf(stderr, "Originally allocated at:\n");
199 fprintf(stderr, "%s:%d:\n", stack->file, stack->line);
200 silc_st_backtrace_symbols(stack->stack, stack->depth);
204 fprintf(stderr, "----- MEMORY HEADER -----\n");
205 fprintf(stderr, "Header length: %lu, total length %lu\n",
206 sizeof(struct SilcStBlockStruct), SILC_ST_GET_SIZE(stack->size));
207 silc_hexdump((void *)stack, sizeof(struct SilcStBlockStruct), stderr);
209 fprintf(stderr, "Header bound is: %p\n",
210 SILC_32_TO_PTR(stack->bound));
211 if (stack->bound != SILC_ST_TOP_BOUND) {
212 fprintf(stderr, "Header bound should be: %p\n",
213 SILC_32_TO_PTR(SILC_ST_TOP_BOUND));
214 fprintf(stderr, "MEMORY IS CORRUPTED (UNDERFLOW)!\n");
217 fprintf(stderr, "----- USER MEMORY -----\n");
218 fprintf(stderr, "Length: %d\n", stack->size);
219 silc_hexdump(((unsigned char *)stack) +
220 sizeof(struct SilcStBlockStruct), stack->size, stderr);
223 fprintf(stderr, "----- MEMORY FOOTER -----\n");
224 bound = SILC_ST_GET_BOUND(stack, stack->size);
225 silc_hexdump((unsigned char *)bound, 4, stderr);
226 fprintf(stderr, "Footer bound is: %p\n", SILC_32_TO_PTR(*bound));
227 if (*bound != SILC_ST_BOTTOM_BOUND) {
228 fprintf(stderr, "Footer bound should be: %p\n",
229 SILC_32_TO_PTR(SILC_ST_BOTTOM_BOUND));
230 fprintf(stderr, "MEMORY IS CORRUPTED (OVERFLOW)!\n");
240 void silc_st_sigsegv(int sig, siginfo_t *si, void *context)
242 SilcStBlock orig, stack = (SilcStBlock)si->si_addr;
244 /* Make the page accessible again */
245 mprotect(si->si_addr, pg, PROT_READ | PROT_WRITE);
247 /* Get the original page from the violated page */
248 orig = (SilcStBlock)(((unsigned char *)si->si_addr) -
249 SILC_ST_GET_SIZE_ALIGN(stack->size, pg));
250 stack = SILC_ST_GET_STACK_ALIGN(orig, stack->size, pg);
252 silc_st_abort(stack, __FILE__, __LINE__,
253 "SILC_MALLOC: access violation (overflow)\n");
256 void silc_st_stacktrace_init(void)
260 atexit(silc_st_dump);
263 var = getenv("SILC_MALLOC_NO_FREE");
264 if (var && *var == '1')
267 var = getenv("SILC_MALLOC_DUMP");
268 if (var && *var == '1')
271 var = getenv("SILC_MALLOC_PROTECT");
272 if (var && *var == '1') {
275 sa.sa_flags = SA_SIGINFO;
276 sa.sa_sigaction = silc_st_sigsegv;
277 sigemptyset(&sa.sa_mask);
278 sigaction(SIGSEGV, &sa, NULL);
280 #if defined(_SC_PAGESIZE)
281 pg = sysconf(_SC_PAGESIZE);
282 #elif defined(_SC_PAGE_SIZE)
283 pg = sysconf(_SC_PAGE_SIZE);
286 #endif /* _SC_PAGESIZE */
289 /* Linux libc malloc check */
290 setenv("MALLOC_CHECK_", "3", 1);
292 /* NetBSD malloc check */
293 setenv("MALLOC_OPTIONS", "AJ", 1);
295 silc_mutex_alloc(&lock);
298 void *silc_st_malloc(size_t size, const char *file, int line)
302 if (silc_unlikely(!dump))
303 silc_st_stacktrace_init();
308 #if defined(HAVE_POSIX_MEMALIGN)
309 if (posix_memalign((void *)&ptr, pg,
310 SILC_ST_GET_SIZE_ALIGN(size, pg) + pg))
311 #endif /* HAVE_POSIX_MEMALIGN */
314 /* The inaccessible page too will include the allocation information
315 so that we can get it when access violation occurs in that page. */
316 stack = (SilcStBlock)(ptr + SILC_ST_GET_SIZE_ALIGN(size, pg));
319 /* Protect the page */
320 if (mprotect(stack, pg, PROT_NONE))
321 silc_st_abort(NULL, file, line, "SILC_MALLOC: mprotect() error: %s\n",
322 errno == ENOMEM ? "Cannot allocate memory. \nYour program "
323 "leaks memory, allocates too much or system \n"
324 "is out of memory. The SILC_MALLOC_PROTECT cannot "
325 "be used." : strerror(errno));
327 /* Get the accessible page */
328 stack = SILC_ST_GET_STACK_ALIGN(ptr, size, pg);
330 stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size));
337 stack->free_file = NULL;
340 stack->bound = SILC_ST_TOP_BOUND;
341 stack->depth = silc_st_backtrace(stack->stack);
343 silc_mutex_lock(lock);
345 stack->next = st_blocks;
348 ((SilcStBlock)st_blocks)->prev = stack;
353 silc_mutex_unlock(lock);
356 *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
358 return SILC_ST_GET_PTR(stack);
361 void *silc_st_calloc(size_t items, size_t size, const char *file, int line)
363 void *addr = (void *)silc_st_malloc(items * size, file, line);
365 memset(addr, 0, items * size);
369 void *silc_st_realloc(void *ptr, size_t size, const char *file, int line)
374 return silc_st_malloc(size, file, line);
376 stack = SILC_ST_GET_STACK(ptr);
377 if (!pg && stack->size >= size) {
378 /* Must update footer when the size changes */
379 if (stack->size != size)
380 *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
385 void *addr = (void *)silc_st_malloc(size, file, line);
387 memcpy(addr, ptr, size > stack->size ? stack->size : size);
388 silc_st_free(ptr, file, line);
394 void silc_st_free(void *ptr, const char *file, int line)
396 SilcStBlock stack, s;
401 /* Check for double free */
402 if (!memcmp((unsigned char *)ptr - sizeof(struct SilcStBlockStruct),
403 "\x47\x47\x47\x47", 4))
404 silc_st_abort(no_free ? ptr - sizeof(struct SilcStBlockStruct) : NULL,
405 file, line, "SILC_MALLOC: double free: %p already freed\n",
406 ptr - sizeof(struct SilcStBlockStruct));
408 stack = SILC_ST_GET_STACK(ptr);
410 silc_mutex_lock(lock);
412 /* Check if we have ever made this allocation */
413 for (s = st_blocks; s; s = s->next)
417 silc_st_abort(NULL, file, line,
418 "SILC_MALLOC: %p was never allocated\n", stack);
421 /* Check for underflow */
422 if (stack->bound != SILC_ST_TOP_BOUND)
423 silc_st_abort(stack, file, line,
424 "SILC_MALLOC: %p was written out of bounds (underflow)\n",
427 /* Check for overflow */
428 if (*SILC_ST_GET_BOUND(stack, stack->size) != SILC_ST_BOTTOM_BOUND)
429 silc_st_abort(stack, file, line,
430 "SILC_MALLOC: %p was written out of bounds (overflow)\n",
435 stack->next->prev = stack->prev;
437 stack->prev->next = stack->next;
439 st_blocks = stack->next;
443 silc_mutex_unlock(lock);
445 stack->free_file = file;
446 stack->free_line = line;
449 memset(stack, 0x47, 8);
454 ptr = SILC_ST_GET_PTR_ALIGN(stack, pg);
455 mprotect(ptr + SILC_ST_GET_SIZE_ALIGN(stack->size, pg), pg,
456 PROT_READ | PROT_WRITE);
457 memset(ptr, 0x47, SILC_ST_GET_SIZE_ALIGN(stack->size, pg));
460 memset(stack, 0x47, SILC_ST_GET_SIZE(stack->size));
465 void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line)
467 unsigned char *addr = (unsigned char *)silc_st_malloc(size + 1, file, line);
469 memcpy((void *)addr, ptr, size);
475 void *silc_st_strdup(const char *string, const char *file, int line)
477 return silc_st_memdup(string, strlen(string), file, line);
480 /* Dumps the stack into file if there are leaks. */
482 void silc_st_dump(void)
484 SilcStBlock stack, s;
485 unsigned long leaks = 0, blocks, bytes;
495 for (stack = st_blocks; stack; stack = stack->next) {
504 fp = fopen("stacktrace.log", "wb");
509 /* Get symbol names */
510 syms = silc_st_backtrace_symbols(stack->stack, stack->depth);
512 /* Find number of leaks and bytes leaked for this leak */
513 for (s = stack; s; s = s->next) {
514 if (s->file == stack->file && s->line == stack->line &&
515 s->depth == stack->depth &&
516 !memcmp(s->stack, stack->stack,
517 (s->depth * sizeof(stack->stack[0])))) {
525 fprintf(fp, "<leak>%s:%d: #blocks=%lu, bytes=%lu\n",
526 stack->file, stack->line, blocks, bytes);
527 for (i = 0; i < stack->depth; i++) {
531 cp = strchr(cp, '(') + 1;
532 else if (strchr(cp, ' '))
533 cp = strchr(cp, ' ') + 1;
535 *strchr(cp, ')') = ' ';
536 fprintf(fp, "\t%s\n", cp);
538 fprintf(fp, "\tpc=%p\n", stack->stack[i]);
547 fprintf(stderr, "\nNo memory leaks\n");
550 "-----------------------------------------\n"
551 "-----------------------------------------\n"
552 " Memory leaks dumped to 'stacktrace.log'\n"
553 " Leaks: %lu leaks, %lu blocks\n"
554 "-----------------------------------------\n"
555 "-----------------------------------------\n",
556 leaks, st_blocks_count);
557 fprintf(stderr, "Number of allocations: %lu\n", st_num_malloc);
560 if (fp && fp != stderr)
564 #endif /* SILC_STACKTRACE */