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
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 #define silc_hexdump(ptr, size, file) \
129 silc_log_output_hexdump("", "", 0, ptr, size, "")
131 void silc_st_abort(SilcStBlock stack, const char *file, int line,
134 void *bt[SILC_ST_DEPTH];
140 vfprintf(stderr, fmt, va);
143 fprintf(stderr, "----- BACKTRACE -----\n%s:%d:\n", file, line);
144 btc = backtrace(bt, SILC_ST_DEPTH);
145 backtrace_symbols_fd(bt, btc, 2);
148 fprintf(stderr, "----- MEMORY TRACE -----\n");
149 if (stack->free_file)
150 fprintf(stderr, "Freed at: %s:%d\n", stack->free_file,
152 fprintf(stderr, "Originally allocated at:\n");
153 fprintf(stderr, "%s:%d:\n", stack->file, stack->line);
154 backtrace_symbols_fd(stack->stack, stack->depth, 2);
158 fprintf(stderr, "----- MEMORY HEADER -----\n");
159 fprintf(stderr, "Header length: %lu, total length %lu\n",
160 sizeof(struct SilcStBlockStruct), SILC_ST_GET_SIZE(stack->size));
161 silc_hexdump((void *)stack, sizeof(struct SilcStBlockStruct), stderr);
163 fprintf(stderr, "Header bound is: %p\n",
164 SILC_32_TO_PTR(stack->bound));
165 if (stack->bound != SILC_ST_TOP_BOUND) {
166 fprintf(stderr, "Header bound should be: %p\n",
167 SILC_32_TO_PTR(SILC_ST_TOP_BOUND));
168 fprintf(stderr, "MEMORY IS CORRUPTED (UNDERFLOW)!\n");
171 fprintf(stderr, "----- USER MEMORY -----\n");
172 fprintf(stderr, "Length: %d\n", stack->size);
173 silc_hexdump(((unsigned char *)stack) +
174 sizeof(struct SilcStBlockStruct), stack->size, stderr);
177 fprintf(stderr, "----- MEMORY FOOTER -----\n");
178 bound = SILC_ST_GET_BOUND(stack, stack->size);
179 silc_hexdump((unsigned char *)bound, 4, stderr);
180 fprintf(stderr, "Footer bound is: %p\n", SILC_32_TO_PTR(*bound));
181 if (*bound != SILC_ST_BOTTOM_BOUND) {
182 fprintf(stderr, "Footer bound should be: %p\n",
183 SILC_32_TO_PTR(SILC_ST_BOTTOM_BOUND));
184 fprintf(stderr, "MEMORY IS CORRUPTED (OVERFLOW)!\n");
194 void silc_st_sigsegv(int sig, siginfo_t *si, void *context)
196 SilcStBlock orig, stack = (SilcStBlock)si->si_addr;
198 /* Make the page accessible again */
199 mprotect(si->si_addr, pg, PROT_READ | PROT_WRITE);
201 /* Get the original page from the violated page */
202 orig = (SilcStBlock)(((unsigned char *)si->si_addr) -
203 SILC_ST_GET_SIZE_ALIGN(stack->size, pg));
204 stack = SILC_ST_GET_STACK_ALIGN(orig, stack->size, pg);
206 silc_st_abort(stack, __FILE__, __LINE__,
207 "SILC_MALLOC: access violation (overflow)\n");
210 void silc_st_stacktrace_init(void)
214 atexit(silc_st_dump);
217 var = getenv("SILC_MALLOC_NO_FREE");
218 if (var && *var == '1')
221 var = getenv("SILC_MALLOC_DUMP");
222 if (var && *var == '1')
225 var = getenv("SILC_MALLOC_PROTECT");
226 if (var && *var == '1') {
229 sa.sa_flags = SA_SIGINFO;
230 sa.sa_sigaction = silc_st_sigsegv;
231 sigemptyset(&sa.sa_mask);
232 sigaction(SIGSEGV, &sa, NULL);
234 #if defined(_SC_PAGESIZE)
235 pg = sysconf(_SC_PAGESIZE);
236 #elif defined(_SC_PAGE_SIZE)
237 pg = sysconf(_SC_PAGE_SIZE);
240 #endif /* _SC_PAGESIZE */
243 /* Linux libc malloc check */
244 setenv("MALLOC_CHECK_", "3", 1);
246 /* NetBSD malloc check */
247 setenv("MALLOC_OPTIONS", "AJ", 1);
249 silc_mutex_alloc(&lock);
252 void *silc_st_malloc(size_t size, const char *file, int line)
256 if (silc_unlikely(!dump))
257 silc_st_stacktrace_init();
262 if (posix_memalign((void *)&ptr, pg,
263 SILC_ST_GET_SIZE_ALIGN(size, pg) + pg))
266 /* The inaccessible page too will include the allocation information
267 so that we can get it when access violation occurs in that page. */
268 stack = (SilcStBlock)(ptr + SILC_ST_GET_SIZE_ALIGN(size, pg));
271 /* Protect the page */
272 if (mprotect(stack, pg, PROT_NONE))
273 silc_st_abort(NULL, file, line, "SILC_MALLOC: mprotect() error: %s\n",
274 errno == ENOMEM ? "Cannot allocate memory. \nYour program "
275 "leaks memory, allocates too much or system \n"
276 "is out of memory. The SILC_MALLOC_PROTECT cannot "
277 "be used." : strerror(errno));
279 /* Get the accessible page */
280 stack = SILC_ST_GET_STACK_ALIGN(ptr, size, pg);
282 stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size));
289 stack->free_file = NULL;
292 stack->bound = SILC_ST_TOP_BOUND;
293 stack->depth = backtrace(stack->stack, SILC_ST_DEPTH);
295 silc_mutex_lock(lock);
297 stack->next = st_blocks;
300 ((SilcStBlock)st_blocks)->prev = stack;
305 silc_mutex_unlock(lock);
308 *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
310 return SILC_ST_GET_PTR(stack);
313 void *silc_st_calloc(size_t items, size_t size, const char *file, int line)
315 void *addr = (void *)silc_st_malloc(items * size, file, line);
317 memset(addr, 0, items * size);
321 void *silc_st_realloc(void *ptr, size_t size, const char *file, int line)
326 return silc_st_malloc(size, file, line);
328 stack = SILC_ST_GET_STACK(ptr);
329 if (!pg && stack->size >= size) {
330 /* Must update footer when the size changes */
331 if (stack->size != size)
332 *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
337 void *addr = (void *)silc_st_malloc(size, file, line);
339 memcpy(addr, ptr, size > stack->size ? stack->size : size);
340 silc_st_free(ptr, file, line);
346 void silc_st_free(void *ptr, const char *file, int line)
348 SilcStBlock stack, s;
353 /* Check for double free */
354 if (!memcmp((unsigned char *)ptr - sizeof(struct SilcStBlockStruct),
355 "\x47\x47\x47\x47", 4))
356 silc_st_abort(no_free ? ptr - sizeof(struct SilcStBlockStruct) : NULL,
357 file, line, "SILC_MALLOC: double free: %p already freed\n",
358 ptr - sizeof(struct SilcStBlockStruct));
360 stack = SILC_ST_GET_STACK(ptr);
362 silc_mutex_lock(lock);
364 /* Check if we have ever made this allocation */
365 for (s = st_blocks; s; s = s->next)
369 silc_st_abort(NULL, file, line,
370 "SILC_MALLOC: %p was never allocated\n", stack);
373 /* Check for underflow */
374 if (stack->bound != SILC_ST_TOP_BOUND)
375 silc_st_abort(stack, file, line,
376 "SILC_MALLOC: %p was written out of bounds (underflow)\n",
379 /* Check for overflow */
380 if (*SILC_ST_GET_BOUND(stack, stack->size) != SILC_ST_BOTTOM_BOUND)
381 silc_st_abort(stack, file, line,
382 "SILC_MALLOC: %p was written out of bounds (overflow)\n",
387 stack->next->prev = stack->prev;
389 stack->prev->next = stack->next;
391 st_blocks = stack->next;
395 silc_mutex_unlock(lock);
397 stack->free_file = file;
398 stack->free_line = line;
401 memset(stack, 0x47, 8);
406 ptr = SILC_ST_GET_PTR_ALIGN(stack, pg);
407 mprotect(ptr + SILC_ST_GET_SIZE_ALIGN(stack->size, pg), pg,
408 PROT_READ | PROT_WRITE);
409 memset(ptr, 0x47, SILC_ST_GET_SIZE_ALIGN(stack->size, pg));
412 memset(stack, 0x47, SILC_ST_GET_SIZE(stack->size));
417 void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line)
419 unsigned char *addr = (unsigned char *)silc_st_malloc(size + 1, file, line);
421 memcpy((void *)addr, ptr, size);
427 void *silc_st_strdup(const char *string, const char *file, int line)
429 return silc_st_memdup(string, strlen(string), file, line);
432 /* Dumps the stack into file if there are leaks. */
434 void silc_st_dump(void)
436 SilcStBlock stack, s;
437 unsigned long leaks = 0, blocks, bytes;
447 for (stack = st_blocks; stack; stack = stack->next) {
456 fp = fopen("stacktrace.log", "wb");
461 /* Get symbol names */
462 syms = backtrace_symbols(stack->stack, stack->depth);
464 /* Find number of leaks and bytes leaked for this leak */
465 for (s = stack; s; s = s->next) {
466 if (s->file == stack->file && s->line == stack->line &&
467 s->depth == stack->depth &&
468 !memcmp(s->stack, stack->stack,
469 (s->depth * sizeof(stack->stack[0])))) {
477 fprintf(fp, "<leak>%s:%d: #blocks=%lu, bytes=%lu\n",
478 stack->file, stack->line, blocks, bytes);
479 for (i = 0; i < stack->depth; i++) {
483 cp = strchr(cp, '(') + 1;
484 else if (strchr(cp, ' '))
485 cp = strchr(cp, ' ') + 1;
487 *strchr(cp, ')') = ' ';
488 fprintf(fp, "\t%s\n", cp);
490 fprintf(fp, "\tpc=%p\n", stack->stack[i]);
499 fprintf(stderr, "\nNo memory leaks\n");
502 "-----------------------------------------\n"
503 "-----------------------------------------\n"
504 " Memory leaks dumped to 'stacktrace.log'\n"
505 " Leaks: %lu leaks, %lu blocks\n"
506 "-----------------------------------------\n"
507 "-----------------------------------------\n",
508 leaks, st_blocks_count);
509 fprintf(stderr, "Number of allocations: %lu\n", st_num_malloc);
512 if (fp && fp != stderr)
516 #endif /* SILC_STACKTRACE */