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 The following environment variables can be used:
43 When set to value 1, the program doesn't actually free any memory.
44 This provides more detailed information especially in case of double
45 free. If the location of the double free cannot be located, by
46 setting this variable the program will show where the memory was
47 originally allocated and freed.
51 When set to value 1, in case of fatal error, dumps the memory location,
52 if possible. This can help see what the memory contains.
54 To work correctly this of course expects that code uses SILC memory
55 allocation and access routines.
59 #include "silcruntime.h"
61 #ifdef SILC_STACKTRACE
64 static void *st_blocks = NULL;
65 static unsigned long st_blocks_count = 0;
66 static unsigned long st_num_malloc = 0;
67 static SilcBool dump = FALSE;
68 static SilcBool malloc_check = FALSE;
69 static SilcBool no_free = FALSE;
70 static SilcBool dump_mem = FALSE;
71 static SilcMutex lock = NULL;
74 #define SILC_ST_DEPTH 15
76 #define SILC_ST_DEPTH 8
77 #endif /* SILC_DEBUG */
79 /* Memory block with stack trace */
80 typedef struct SilcStBlockStruct {
81 struct SilcStBlockStruct *next;
82 struct SilcStBlockStruct *prev;
83 void *stack[SILC_ST_DEPTH]; /* Stack trace */
84 const char *file; /* Allocation file in program */
85 const char *free_file; /* Free file in program */
86 SilcUInt32 size; /* Allocated memory size */
87 SilcUInt16 line; /* Allocation line in program */
88 SilcUInt16 free_line; /* Free line in program */
89 SilcUInt16 depth; /* Depth of stack trace */
90 SilcUInt16 dumpped; /* Block is dumpped */
91 SilcUInt32 bound; /* Top bound */
94 #define SILC_ST_TOP_BOUND 0xfeed1977
95 #define SILC_ST_BOTTOM_BOUND 0x9152beef
96 #define SILC_ST_GET_SIZE(size) ((size + sizeof(struct SilcStBlockStruct) + 4))
97 #define SILC_ST_GET_STACK(p) ((SilcStBlock)(((unsigned char *)p) - \
98 sizeof(struct SilcStBlockStruct)))
99 #define SILC_ST_GET_PTR(p) (((unsigned char *)p) + \
100 sizeof(struct SilcStBlockStruct))
101 #define SILC_ST_GET_BOUND(p, size) (SilcUInt32 *)(((unsigned char *)p) + \
102 SILC_ST_GET_SIZE(size) - 4)
104 void silc_st_abort(SilcStBlock stack, const char *file, int line,
107 void *bt[SILC_ST_DEPTH];
113 vfprintf(stderr, fmt, va);
116 fprintf(stderr, "----- BACKTRACE -----\n%s:%d:\n", file, line);
117 btc = backtrace(bt, SILC_ST_DEPTH);
118 backtrace_symbols_fd(bt, btc, 2);
121 fprintf(stderr, "----- MEMORY TRACE -----\n");
122 if (stack->free_file)
123 fprintf(stderr, "Freed at: %s:%d\n", stack->free_file,
125 fprintf(stderr, "Originally allocated at:\n");
126 fprintf(stderr, "%s:%d:\n", stack->file, stack->line);
127 backtrace_symbols_fd(stack->stack, stack->depth, 2);
131 fprintf(stderr, "----- MEMORY HEADER -----\n");
132 fprintf(stderr, "Header length: %lu, total length %lu\n",
133 sizeof(struct SilcStBlockStruct), SILC_ST_GET_SIZE(stack->size));
134 silc_hexdump((void *)stack, sizeof(struct SilcStBlockStruct), stderr);
136 fprintf(stderr, "Header bound is: %p\n",
137 SILC_32_TO_PTR(stack->bound));
138 if (stack->bound != SILC_ST_TOP_BOUND) {
139 fprintf(stderr, "Header bound should be: %p\n",
140 SILC_32_TO_PTR(SILC_ST_TOP_BOUND));
141 fprintf(stderr, "MEMORY IS CORRUPTED (UNDERFLOW)!\n");
144 fprintf(stderr, "----- USER MEMORY -----\n");
145 fprintf(stderr, "Length: %d\n", stack->size);
146 silc_hexdump(((unsigned char *)stack) +
147 sizeof(struct SilcStBlockStruct), stack->size, stderr);
150 fprintf(stderr, "----- MEMORY FOOTER -----\n");
151 bound = SILC_ST_GET_BOUND(stack, stack->size);
152 silc_hexdump((unsigned char *)bound, 4, stderr);
153 fprintf(stderr, "Footer bound is: %p\n", SILC_32_TO_PTR(*bound));
154 if (*bound != SILC_ST_BOTTOM_BOUND) {
155 fprintf(stderr, "Footer bound should be: %p\n",
156 SILC_32_TO_PTR(SILC_ST_BOTTOM_BOUND));
157 fprintf(stderr, "MEMORY IS CORRUPTED (OVERFLOW)!\n");
167 void silc_st_stacktrace(SilcStBlock stack)
170 atexit(silc_st_dump);
177 var = silc_getenv("SILC_MALLOC_NO_FREE");
178 if (var && *var == '1')
181 var = silc_getenv("SILC_MALLOC_DUMP");
182 if (var && *var == '1')
185 /* Linux libc malloc check */
186 silc_setenv("MALLOC_CHECK_", "3");
188 /* NetBSD malloc check */
189 silc_setenv("MALLOC_OPTIONS", "AJ");
193 silc_mutex_alloc(&lock);
197 stack->depth = backtrace(stack->stack, SILC_ST_DEPTH);
200 void *silc_st_malloc(size_t size, const char *file, int line)
202 SilcStBlock stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size));
209 stack->free_file = NULL;
212 stack->bound = SILC_ST_TOP_BOUND;
213 silc_st_stacktrace(stack);
215 silc_mutex_lock(lock);
217 stack->next = st_blocks;
220 ((SilcStBlock)st_blocks)->prev = stack;
225 silc_mutex_unlock(lock);
227 *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
229 return SILC_ST_GET_PTR(stack);
232 void *silc_st_calloc(size_t items, size_t size, const char *file, int line)
234 void *addr = (void *)silc_st_malloc(items * size, file, line);
235 memset(addr, 0, items * size);
239 void *silc_st_realloc(void *ptr, size_t size, const char *file, int line)
244 return silc_st_malloc(size, file, line);
246 stack = SILC_ST_GET_STACK(ptr);
247 if (stack->size >= size) {
248 /* Must update footer when the size changes */
249 if (stack->size != size)
250 *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
255 void *addr = (void *)silc_st_malloc(size, file, line);
256 memcpy(addr, ptr, stack->size);
257 silc_st_free(ptr, file, line);
262 void silc_st_free(void *ptr, const char *file, int line)
264 SilcStBlock stack, s;
269 /* Check for double free */
270 if (!memcmp((unsigned char *)ptr - sizeof(struct SilcStBlockStruct),
271 "\x47\x47\x47\x47", 4))
272 silc_st_abort(no_free ? ptr - sizeof(struct SilcStBlockStruct) : NULL,
273 file, line, "SILC_MALLOC: double free: %p already freed\n",
274 ptr - sizeof(struct SilcStBlockStruct));
276 stack = SILC_ST_GET_STACK(ptr);
278 silc_mutex_lock(lock);
280 /* Check if we have ever made this allocation */
281 for (s = st_blocks; s; s = s->next)
285 silc_st_abort(NULL, file, line,
286 "SILC_MALLOC: %p was never allocated\n", stack);
288 /* Check for underflow */
289 if (stack->bound != SILC_ST_TOP_BOUND)
290 silc_st_abort(stack, file, line,
291 "SILC_MALLOC: %p was written out of bounds (underflow)\n",
294 /* Check for overflow */
295 if (*SILC_ST_GET_BOUND(stack, stack->size) != SILC_ST_BOTTOM_BOUND)
296 silc_st_abort(stack, file, line,
297 "SILC_MALLOC: %p was written out of bounds (overflow)\n",
301 stack->next->prev = stack->prev;
303 stack->prev->next = stack->next;
305 st_blocks = stack->next;
309 silc_mutex_unlock(lock);
311 stack->free_file = file;
312 stack->free_line = line;
315 memset(stack, 0x47, 8);
319 memset(stack, 0x47, SILC_ST_GET_SIZE(stack->size));
324 void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line)
326 unsigned char *addr = (unsigned char *)silc_st_malloc(size + 1, file, line);
327 memcpy((void *)addr, ptr, size);
332 void *silc_st_strdup(const char *string, const char *file, int line)
334 return silc_st_memdup(string, strlen(string), file, line);
337 /* Dumps the stack into file if there are leaks. */
339 void silc_st_dump(void)
341 SilcStBlock stack, s;
342 unsigned long leaks = 0, blocks, bytes;
352 for (stack = st_blocks; stack; stack = stack->next) {
361 fp = fopen("stacktrace.log", "wb");
366 /* Get symbol names */
367 syms = backtrace_symbols(stack->stack, stack->depth);
369 /* Find number of leaks and bytes leaked for this leak */
370 for (s = stack; s; s = s->next) {
371 if (s->file == stack->file && s->line == stack->line &&
372 s->depth == stack->depth &&
373 !memcmp(s->stack, stack->stack,
374 (s->depth * sizeof(stack->stack[0])))) {
382 fprintf(fp, "<leak>%s:%d: #blocks=%lu, bytes=%lu\n",
383 stack->file, stack->line, blocks, bytes);
384 for (i = 0; i < stack->depth; i++) {
388 cp = strchr(cp, '(') + 1;
389 else if (strchr(cp, ' '))
390 cp = strchr(cp, ' ') + 1;
392 *strchr(cp, ')') = ' ';
393 fprintf(fp, "\t%s\n", cp);
395 fprintf(fp, "\tpc=%p\n", stack->stack[i]);
404 fprintf(stderr, "\nNo memory leaks\n");
407 "-----------------------------------------\n"
408 "-----------------------------------------\n"
409 " Memory leaks dumped to 'stacktrace.log'\n"
410 " Leaks: %lu leaks, %lu blocks\n"
411 "-----------------------------------------\n"
412 "-----------------------------------------\n",
413 leaks, st_blocks_count);
414 fprintf(stderr, "Number of allocations: %lu\n", st_num_malloc);
417 if (fp && fp != stderr)
421 #endif /* SILC_STACKTRACE */