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.
70 #include "silcruntime.h"
72 #ifdef SILC_STACKTRACE
78 static void *st_blocks = NULL;
79 static unsigned long st_blocks_count = 0;
80 static unsigned long st_num_malloc = 0;
81 static SilcBool dump = FALSE;
82 static SilcBool no_free = FALSE;
83 static SilcBool dump_mem = FALSE;
84 static SilcUInt32 pg = 0;
85 static SilcMutex lock = NULL;
88 #define SILC_ST_DEPTH 15
90 #define SILC_ST_DEPTH 8
91 #endif /* SILC_DEBUG */
93 /* Memory block with stack trace */
94 typedef struct SilcStBlockStruct {
95 struct SilcStBlockStruct *next;
96 struct SilcStBlockStruct *prev;
97 void *stack[SILC_ST_DEPTH]; /* Stack trace */
98 const char *file; /* Allocation file in program */
99 const char *free_file; /* Free file in program */
100 SilcUInt32 size; /* Allocated memory size */
101 SilcUInt16 line; /* Allocation line in program */
102 SilcUInt16 free_line; /* Free line in program */
103 SilcUInt16 depth; /* Depth of stack trace */
104 SilcUInt16 dumpped; /* Block is dumpped */
105 SilcUInt32 bound; /* Top bound */
108 #define SILC_ST_TOP_BOUND 0xfeed1977
109 #define SILC_ST_BOTTOM_BOUND 0x9152beef
110 #define SILC_ST_GET_SIZE(size) ((size + sizeof(struct SilcStBlockStruct) + 4))
111 #define SILC_ST_GET_STACK(p) ((SilcStBlock)(((unsigned char *)p) - \
112 sizeof(struct SilcStBlockStruct)))
113 #define SILC_ST_GET_PTR(p) (((unsigned char *)p) + \
114 sizeof(struct SilcStBlockStruct))
115 #define SILC_ST_GET_BOUND(p, size) (SilcUInt32 *)(((unsigned char *)p) + \
116 SILC_ST_GET_SIZE(size) - 4)
118 #define SILC_ST_ALIGN(bytes, align) (((bytes) + (align - 1)) & ~(align - 1))
119 #define SILC_ST_GET_SIZE_ALIGN(size, align) \
120 SILC_ST_ALIGN(SILC_ST_GET_SIZE(size) - 4, align)
121 #define SILC_ST_GET_PTR_ALIGN(stack, align) \
122 (((unsigned char *)stack) - (SILC_ST_GET_SIZE_ALIGN(stack->size, pg) - \
123 SILC_ST_GET_SIZE(stack->size)) - 4)
124 #define SILC_ST_GET_STACK_ALIGN(p, size, align) \
125 ((SilcStBlock)(((unsigned char *)p) + (SILC_ST_GET_SIZE_ALIGN(size, pg) - \
126 SILC_ST_GET_SIZE(size)) + 4))
128 void silc_st_abort(SilcStBlock stack, const char *file, int line,
131 void *bt[SILC_ST_DEPTH];
137 vfprintf(stderr, fmt, va);
140 fprintf(stderr, "----- BACKTRACE -----\n%s:%d:\n", file, line);
141 btc = backtrace(bt, SILC_ST_DEPTH);
142 backtrace_symbols_fd(bt, btc, 2);
145 fprintf(stderr, "----- MEMORY TRACE -----\n");
146 if (stack->free_file)
147 fprintf(stderr, "Freed at: %s:%d\n", stack->free_file,
149 fprintf(stderr, "Originally allocated at:\n");
150 fprintf(stderr, "%s:%d:\n", stack->file, stack->line);
151 backtrace_symbols_fd(stack->stack, stack->depth, 2);
155 fprintf(stderr, "----- MEMORY HEADER -----\n");
156 fprintf(stderr, "Header length: %lu, total length %lu\n",
157 sizeof(struct SilcStBlockStruct), SILC_ST_GET_SIZE(stack->size));
158 silc_hexdump((void *)stack, sizeof(struct SilcStBlockStruct), stderr);
160 fprintf(stderr, "Header bound is: %p\n",
161 SILC_32_TO_PTR(stack->bound));
162 if (stack->bound != SILC_ST_TOP_BOUND) {
163 fprintf(stderr, "Header bound should be: %p\n",
164 SILC_32_TO_PTR(SILC_ST_TOP_BOUND));
165 fprintf(stderr, "MEMORY IS CORRUPTED (UNDERFLOW)!\n");
168 fprintf(stderr, "----- USER MEMORY -----\n");
169 fprintf(stderr, "Length: %d\n", stack->size);
170 silc_hexdump(((unsigned char *)stack) +
171 sizeof(struct SilcStBlockStruct), stack->size, stderr);
174 fprintf(stderr, "----- MEMORY FOOTER -----\n");
175 bound = SILC_ST_GET_BOUND(stack, stack->size);
176 silc_hexdump((unsigned char *)bound, 4, stderr);
177 fprintf(stderr, "Footer bound is: %p\n", SILC_32_TO_PTR(*bound));
178 if (*bound != SILC_ST_BOTTOM_BOUND) {
179 fprintf(stderr, "Footer bound should be: %p\n",
180 SILC_32_TO_PTR(SILC_ST_BOTTOM_BOUND));
181 fprintf(stderr, "MEMORY IS CORRUPTED (OVERFLOW)!\n");
191 void silc_st_sigsegv(int sig, siginfo_t *si, void *context)
193 SilcStBlock orig, stack = (SilcStBlock)si->si_addr;
195 /* Make the page accessible again */
196 mprotect(si->si_addr, pg, PROT_READ | PROT_WRITE);
198 /* Get the original page from the violated page */
199 orig = (SilcStBlock)(((unsigned char *)si->si_addr) -
200 SILC_ST_GET_SIZE_ALIGN(stack->size, pg));
201 stack = SILC_ST_GET_STACK_ALIGN(orig, stack->size, pg);
203 silc_st_abort(stack, __FILE__, __LINE__,
204 "SILC_MALLOC: access violation (overflow)\n");
207 void silc_st_stacktrace_init(void)
211 atexit(silc_st_dump);
214 var = silc_getenv("SILC_MALLOC_NO_FREE");
215 if (var && *var == '1')
218 var = silc_getenv("SILC_MALLOC_DUMP");
219 if (var && *var == '1')
222 var = silc_getenv("SILC_MALLOC_PROTECT");
223 if (var && *var == '1') {
226 sa.sa_flags = SA_SIGINFO;
227 sa.sa_sigaction = silc_st_sigsegv;
228 sigemptyset(&sa.sa_mask);
229 sigaction(SIGSEGV, &sa, NULL);
231 #if defined(_SC_PAGESIZE)
232 pg = sysconf(_SC_PAGESIZE);
233 #elif defined(_SC_PAGE_SIZE)
234 pg = sysconf(_SC_PAGE_SIZE);
237 #endif /* _SC_PAGESIZE */
240 /* Linux libc malloc check */
241 silc_setenv("MALLOC_CHECK_", "3");
243 /* NetBSD malloc check */
244 silc_setenv("MALLOC_OPTIONS", "AJ");
246 silc_mutex_alloc(&lock);
249 void *silc_st_malloc(size_t size, const char *file, int line)
253 if (silc_unlikely(!dump))
254 silc_st_stacktrace_init();
259 if (posix_memalign((void *)&ptr, pg,
260 SILC_ST_GET_SIZE_ALIGN(size, pg) + pg))
263 /* The inaccessible page too will include the allocation information
264 so that we can get it when access violation occurs in that page. */
265 stack = (SilcStBlock)(ptr + SILC_ST_GET_SIZE_ALIGN(size, pg));
268 /* Protect the page */
269 if (mprotect(stack, pg, PROT_NONE))
270 silc_st_abort(NULL, file, line, "SILC_MALLOC: mprotect() error: %s\n",
271 errno == ENOMEM ? "Cannot allocate memory. \nYour program "
272 "leaks memory, allocates too much or system \n"
273 "is out of memory. The SILC_MALLOC_PROTECT cannot "
274 "be used." : strerror(errno));
276 /* Get the accessible page */
277 stack = SILC_ST_GET_STACK_ALIGN(ptr, size, pg);
279 stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size));
286 stack->free_file = NULL;
289 stack->bound = SILC_ST_TOP_BOUND;
290 stack->depth = backtrace(stack->stack, SILC_ST_DEPTH);
292 silc_mutex_lock(lock);
294 stack->next = st_blocks;
297 ((SilcStBlock)st_blocks)->prev = stack;
302 silc_mutex_unlock(lock);
305 *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
307 return SILC_ST_GET_PTR(stack);
310 void *silc_st_calloc(size_t items, size_t size, const char *file, int line)
312 void *addr = (void *)silc_st_malloc(items * size, file, line);
314 memset(addr, 0, items * size);
318 void *silc_st_realloc(void *ptr, size_t size, const char *file, int line)
323 return silc_st_malloc(size, file, line);
325 stack = SILC_ST_GET_STACK(ptr);
326 if (!pg && stack->size >= size) {
327 /* Must update footer when the size changes */
328 if (stack->size != size)
329 *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
334 void *addr = (void *)silc_st_malloc(size, file, line);
336 memcpy(addr, ptr, size > stack->size ? stack->size : size);
337 silc_st_free(ptr, file, line);
343 void silc_st_free(void *ptr, const char *file, int line)
345 SilcStBlock stack, s;
350 /* Check for double free */
351 if (!memcmp((unsigned char *)ptr - sizeof(struct SilcStBlockStruct),
352 "\x47\x47\x47\x47", 4))
353 silc_st_abort(no_free ? ptr - sizeof(struct SilcStBlockStruct) : NULL,
354 file, line, "SILC_MALLOC: double free: %p already freed\n",
355 ptr - sizeof(struct SilcStBlockStruct));
357 stack = SILC_ST_GET_STACK(ptr);
359 silc_mutex_lock(lock);
361 /* Check if we have ever made this allocation */
362 for (s = st_blocks; s; s = s->next)
366 silc_st_abort(NULL, file, line,
367 "SILC_MALLOC: %p was never allocated\n", stack);
370 /* Check for underflow */
371 if (stack->bound != SILC_ST_TOP_BOUND)
372 silc_st_abort(stack, file, line,
373 "SILC_MALLOC: %p was written out of bounds (underflow)\n",
376 /* Check for overflow */
377 if (*SILC_ST_GET_BOUND(stack, stack->size) != SILC_ST_BOTTOM_BOUND)
378 silc_st_abort(stack, file, line,
379 "SILC_MALLOC: %p was written out of bounds (overflow)\n",
384 stack->next->prev = stack->prev;
386 stack->prev->next = stack->next;
388 st_blocks = stack->next;
392 silc_mutex_unlock(lock);
394 stack->free_file = file;
395 stack->free_line = line;
398 memset(stack, 0x47, 8);
403 ptr = SILC_ST_GET_PTR_ALIGN(stack, pg);
404 mprotect(ptr + SILC_ST_GET_SIZE_ALIGN(stack->size, pg), pg,
405 PROT_READ | PROT_WRITE);
406 memset(ptr, 0x47, SILC_ST_GET_SIZE_ALIGN(stack->size, pg));
409 memset(stack, 0x47, SILC_ST_GET_SIZE(stack->size));
414 void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line)
416 unsigned char *addr = (unsigned char *)silc_st_malloc(size + 1, file, line);
418 memcpy((void *)addr, ptr, size);
424 void *silc_st_strdup(const char *string, const char *file, int line)
426 return silc_st_memdup(string, strlen(string), file, line);
429 /* Dumps the stack into file if there are leaks. */
431 void silc_st_dump(void)
433 SilcStBlock stack, s;
434 unsigned long leaks = 0, blocks, bytes;
444 for (stack = st_blocks; stack; stack = stack->next) {
453 fp = fopen("stacktrace.log", "wb");
458 /* Get symbol names */
459 syms = backtrace_symbols(stack->stack, stack->depth);
461 /* Find number of leaks and bytes leaked for this leak */
462 for (s = stack; s; s = s->next) {
463 if (s->file == stack->file && s->line == stack->line &&
464 s->depth == stack->depth &&
465 !memcmp(s->stack, stack->stack,
466 (s->depth * sizeof(stack->stack[0])))) {
474 fprintf(fp, "<leak>%s:%d: #blocks=%lu, bytes=%lu\n",
475 stack->file, stack->line, blocks, bytes);
476 for (i = 0; i < stack->depth; i++) {
480 cp = strchr(cp, '(') + 1;
481 else if (strchr(cp, ' '))
482 cp = strchr(cp, ' ') + 1;
484 *strchr(cp, ')') = ' ';
485 fprintf(fp, "\t%s\n", cp);
487 fprintf(fp, "\tpc=%p\n", stack->stack[i]);
496 fprintf(stderr, "\nNo memory leaks\n");
499 "-----------------------------------------\n"
500 "-----------------------------------------\n"
501 " Memory leaks dumped to 'stacktrace.log'\n"
502 " Leaks: %lu leaks, %lu blocks\n"
503 "-----------------------------------------\n"
504 "-----------------------------------------\n",
505 leaks, st_blocks_count);
506 fprintf(stderr, "Number of allocations: %lu\n", st_num_malloc);
509 if (fp && fp != stderr)
513 #endif /* SILC_STACKTRACE */