silcd: fixed memory leaks
authorPekka Riikonen <priikone@silcnet.org>
Wed, 22 Oct 2008 19:22:00 +0000 (22:22 +0300)
committerPekka Riikonen <priikone@silcnet.org>
Wed, 22 Oct 2008 19:22:00 +0000 (22:22 +0300)
apps/silcd/idlist.c
apps/silcd/server.c
lib/configure.ad
lib/silcske/silcske.c
lib/silcutil/stacktrace.c
lib/silcutil/stacktrace.h

index 1c75d7181bf0b8a73af4a9ed3c5562771a8ec2eb..13093c09e9ede52f1a7c1d93aedec81881b44b51 100644 (file)
@@ -55,7 +55,10 @@ void silc_idlist_del_data(void *entry)
 
   if (idata->hash)
     silc_hash_free(idata->hash);
+  if (idata->rekey)
+    silc_ske_free_rekey_material(idata->rekey);
 
+  idata->rekey = NULL;
   idata->hash = NULL;
   idata->public_key = NULL;
 }
@@ -395,6 +398,7 @@ void silc_idlist_client_destructor(SilcIDCache cache,
                              client);
 
     assert(!silc_hash_table_count(client->channels));
+    silc_free(entry->name);
     silc_free(client->nickname);
     silc_free(client->servername);
     silc_free(client->username);
index c2df082c61118703639b85fca1f9ad34de233bfd..0e1cffb084ddac0918c65e38d35bcd17a0e4c0ef 100644 (file)
@@ -704,6 +704,7 @@ void silc_server_free(SilcServer server)
   silc_free(server->local_list);
   silc_free(server->global_list);
   silc_free(server->server_name);
+  silc_free(server->id);
   silc_free(server);
 
   silc_hmac_unregister_all();
@@ -1608,7 +1609,6 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
   SilcConnAuth connauth;
   SilcCipher send_key, receive_key;
   SilcHmac hmac_send, hmac_receive;
-  SilcHash hash;
 
   server = entry->server;
   sconn = entry->data.sconn;
@@ -1644,7 +1644,7 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status,
 
   /* Set the keys into use.  The data will be encrypted after this. */
   if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
-                        &hmac_send, &hmac_receive, &hash)) {
+                        &hmac_send, &hmac_receive, NULL)) {
     silc_ske_free(ske);
 
     /* Try reconnecting if configuration wants it */
@@ -2686,9 +2686,11 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status,
   idata->rekey = rekey;
   idata->public_key = silc_pkcs_public_key_copy(prop->public_key);
   pk = silc_pkcs_public_key_encode(idata->public_key, &pk_len);
-  silc_hash_make(server->sha1hash, pk, pk_len, idata->fingerprint);
-
-  silc_hash_alloc(silc_hash_get_name(prop->hash), &idata->hash);
+  if (pk) {
+    silc_hash_make(server->sha1hash, pk, pk_len, idata->fingerprint);
+    silc_free(pk);
+  }
+  idata->hash = hash;
 
   SILC_LOG_DEBUG(("Starting connection authentication"));
   server->stat.auth_attempts++;
@@ -2911,7 +2913,7 @@ SILC_TASK_CALLBACK(silc_server_do_rekey)
   SILC_LOG_DEBUG(("Perform rekey, sock %p", sock));
 
   /* Do not execute rekey with disabled connections */
-  if (idata->status & SILC_IDLIST_STATUS_DISABLED)
+  if (idata->status & SILC_IDLIST_STATUS_DISABLED || !idata->rekey)
     return;
 
   /* If another protocol is active do not start rekey */
@@ -2975,6 +2977,11 @@ static void silc_server_rekey(SilcServer server, SilcPacketStream sock,
   SilcIDListData idata = silc_packet_get_context(sock);
   SilcSKE ske;
 
+  if (!idata->rekey) {
+    silc_packet_free(packet);
+    return;
+  }
+
   SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s], sock %p",
                  idata->sconn->remote_host, idata->sconn->remote_port,
                  SILC_CONNTYPE_STRING(idata->conn_type), sock));
index 9ff9f49a75dfa90831a0085afc3ed1bfb8bdaf94..6f381c7b2729719b478a88f2848b0f474669f281 100644 (file)
@@ -163,6 +163,8 @@ AC_ARG_ENABLE(stack-trace,
     yes)
       AC_MSG_RESULT(yes)
       AC_DEFINE([SILC_STACKTRACE], [], [SILC_STACKTRACE])
+      CFLAGS="$CFLAGS -rdynamic"
+      PC_CFLAGS="$PC_CFLAGS -rdynamic"
       ;;
     *)
       AC_MSG_RESULT(no)
index caf2579628bf9a1a426057a5179f2949dfa5d07e..bac8a5c4fac9b8feb642da88c272092fa4a1ccb3 100644 (file)
@@ -182,15 +182,11 @@ static void silc_ske_skr_callback(SilcSKR repository,
 
 static SilcSKEStatus silc_ske_check_version(SilcSKE ske)
 {
-  SilcUInt32 r_software_version = 0;
-  char *r_software_string = NULL;
-
   if (!ske->remote_version || !ske->version)
     return SILC_SKE_STATUS_BAD_VERSION;
 
   if (!silc_parse_version_string(ske->remote_version, NULL, NULL,
-                                &r_software_version,
-                                &r_software_string, NULL))
+                                NULL, NULL, NULL))
     return SILC_SKE_STATUS_BAD_VERSION;
 
   return SILC_SKE_STATUS_OK;
@@ -2419,7 +2415,7 @@ SilcAsyncOperation silc_ske_responder(SilcSKE ske,
   ske->timeout = params->timeout_secs ? params->timeout_secs : 30;
   if (ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)
     ske->session_port = params->session_port;
-  ske->version = strdup(params->version);
+  ske->version = params->version;
   if (!ske->version)
     return NULL;
   ske->running = TRUE;
index 9f335e44258dffe481751c37de03a3fb05a2a61d..85e5417fdeb9397d095fd354fea6ae8abb1647b0 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002, 2007 Pekka Riikonen
+  Copyright (C) 2002 - 2008 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 
 */
 
+/* This file implements memory leak checker and basic memory corruption
+   and double free checker.  It is multi-thread safe.  It does the
+   following:
+
+   o Tracks all memory allocations and report any unfreed memory at the
+     end of the program with backtrace where the memory was allocated.
+
+   o Checks if a memory location has been freed already and abort the
+     program with the backtrace of the location of the double free.
+
+   o Checks if a given pointer has been allocated at all and abort the
+     program with the backtrace where the invalid free was given.
+
+   o Checks at the time of free if the memory was written out of bounds
+     (overflow) and abort with the backtrace of the free.  The backtrace
+     might not help to find the overflow but at least it is detected.
+     By setting SILC_MALLOC_DUMP the memory is dummped to help and see
+     what it contains.
+
+   o Can detect if the memory is read or written out of bounds (overflow)
+     and abort immediately the with the backtrace when the illegal access
+     occurs.  This can be enabled by using SILC_MALLOC_PROTECT.
+
+   The following environment variables can be used:
+
+   SILC_MALLOC_NO_FREE
+
+     When set to value 1, the program doesn't actually free any memory.
+     This provides more detailed information especially in case of double
+     free.  If the location of the double free cannot be located, by
+     setting this variable the program will show where the memory was
+     originally allocated and freed.
+
+   SILC_MALLOC_DUMP
+
+     When set to value 1, in case of fatal error, dumps the memory location,
+     if possible.  This can help see what the memory contains.
+
+   SILC_MALLOC_PROTECT
+
+     When set to value 1 each allocation will have an inaccesible memory
+     page following the allocated memory area.  This will detect if the
+     the memory is accessed (read or write) beyond its boundaries.  This
+     will help to identify the place where illegal memory access occurs.
+
+   To work correctly this of course expects that code uses SILC memory
+   allocation and access routines.
+
+*/
+
 #include "silc.h"
 
 #ifdef SILC_STACKTRACE
+#include <execinfo.h>
+#include <signal.h>
+#include <malloc.h>
+#include <sys/mman.h>
 
 static void *st_blocks = NULL;
 static unsigned long st_blocks_count = 0;
+static unsigned long st_num_malloc = 0;
 static SilcBool dump = FALSE;
-static SilcBool malloc_check = FALSE;
+static SilcBool no_free = FALSE;
+static SilcBool dump_mem = FALSE;
+static SilcUInt32 pg = 0;
+static SilcMutex lock = NULL;
 
 #ifdef SILC_DEBUG
 #define SILC_ST_DEPTH 15
@@ -34,71 +92,207 @@ static SilcBool malloc_check = FALSE;
 
 /* Memory block with stack trace */
 typedef struct SilcStBlockStruct {
-  unsigned int dumpped  : 1;   /* Block is dumpped */
-  unsigned int depth    : 8;   /* Depth of stack trace */
-  unsigned int line     : 23;  /* Allocation line in program */
-  void *stack[SILC_ST_DEPTH];  /* Stack trace */
-  const char *file;            /* Allocation file in program */
-  unsigned long size;          /* Allocated memory size */
   struct SilcStBlockStruct *next;
   struct SilcStBlockStruct *prev;
+  void *stack[SILC_ST_DEPTH];  /* Stack trace */
+  const char *file;            /* Allocation file in program */
+  const char *free_file;       /* Free file in program */
+  SilcUInt32 size;             /* Allocated memory size */
+  SilcUInt16 line;             /* Allocation line in program */
+  SilcUInt16 free_line;                /* Free line in program */
+  SilcUInt16 depth;            /* Depth of stack trace */
+  SilcUInt16 dumpped;          /* Block is dumpped */
+  SilcUInt32 bound;            /* Top bound */
 } *SilcStBlock;
 
-/* Get current frame pointer */
-#define SILC_ST_GET_FP(ret_fp)                 \
-do {                                           \
-  register void *cfp;                          \
-  asm volatile ("movl %%ebp, %0" : "=r" (cfp));        \
-  (ret_fp) = cfp;                              \
-} while(0);
-
-#define SILC_ST_GET_SIZE(size) ((size + sizeof(struct SilcStBlockStruct)))
+#define SILC_ST_TOP_BOUND 0xfeed1977
+#define SILC_ST_BOTTOM_BOUND 0x9152beef
+#define SILC_ST_GET_SIZE(size) ((size + sizeof(struct SilcStBlockStruct) + 4))
 #define SILC_ST_GET_STACK(p) ((SilcStBlock)(((unsigned char *)p) -     \
                            sizeof(struct SilcStBlockStruct)))
 #define SILC_ST_GET_PTR(p) (((unsigned char *)p) +             \
                            sizeof(struct SilcStBlockStruct))
-
-void silc_st_stacktrace(SilcStBlock stack)
+#define SILC_ST_GET_BOUND(p, size) (SilcUInt32 *)(((unsigned char *)p) + \
+                                   SILC_ST_GET_SIZE(size) - 4)
+
+#define SILC_ST_ALIGN(bytes, align) (((bytes) + (align - 1)) & ~(align - 1))
+#define SILC_ST_GET_SIZE_ALIGN(size, align) \
+  SILC_ST_ALIGN(SILC_ST_GET_SIZE(size) - 4, align)
+#define SILC_ST_GET_PTR_ALIGN(stack, align)                              \
+  (((unsigned char *)stack) - (SILC_ST_GET_SIZE_ALIGN(stack->size, pg) -  \
+                              SILC_ST_GET_SIZE(stack->size)) - 4)
+#define SILC_ST_GET_STACK_ALIGN(p, size, align)                                    \
+  ((SilcStBlock)(((unsigned char *)p) + (SILC_ST_GET_SIZE_ALIGN(size, pg) - \
+                                        SILC_ST_GET_SIZE(size)) + 4))
+
+#define silc_hexdump(ptr, size, file) \
+  silc_log_output_hexdump("", "", 0, ptr, size, "")
+
+void silc_st_abort(SilcStBlock stack, const char *file, int line,
+                  char *fmt, ...)
 {
-  void *fp;
+  void *bt[SILC_ST_DEPTH];
+  SilcUInt32 *bound;
+  va_list va;
+  int btc;
+
+  va_start(va, fmt);
+  vfprintf(stderr, fmt, va);
+  va_end(va);
+
+  fprintf(stderr, "----- BACKTRACE -----\n%s:%d:\n", file, line);
+  btc = backtrace(bt, SILC_ST_DEPTH);
+  backtrace_symbols_fd(bt, btc, 2);
+
+  if (stack) {
+    fprintf(stderr, "----- MEMORY TRACE -----\n");
+    if (stack->free_file)
+      fprintf(stderr, "Freed at: %s:%d\n", stack->free_file,
+             stack->free_line);
+    fprintf(stderr, "Originally allocated at:\n");
+    fprintf(stderr, "%s:%d:\n", stack->file, stack->line);
+    backtrace_symbols_fd(stack->stack, stack->depth, 2);
+    fflush(stderr);
+
+    if (dump_mem) {
+      fprintf(stderr, "----- MEMORY HEADER -----\n");
+      fprintf(stderr, "Header length: %lu, total length %lu\n",
+             sizeof(struct SilcStBlockStruct), SILC_ST_GET_SIZE(stack->size));
+      silc_hexdump((void *)stack, sizeof(struct SilcStBlockStruct), stderr);
+      fflush(stderr);
+      fprintf(stderr, "Header bound is: %p\n",
+             SILC_32_TO_PTR(stack->bound));
+      if (stack->bound != SILC_ST_TOP_BOUND) {
+       fprintf(stderr, "Header bound should be: %p\n",
+               SILC_32_TO_PTR(SILC_ST_TOP_BOUND));
+        fprintf(stderr, "MEMORY IS CORRUPTED (UNDERFLOW)!\n");
+      }
 
-  if (!dump) {
-    atexit(silc_st_dump);
-    dump = TRUE;
+      fprintf(stderr, "----- USER MEMORY -----\n");
+      fprintf(stderr, "Length: %d\n", stack->size);
+      silc_hexdump(((unsigned char *)stack) +
+                   sizeof(struct SilcStBlockStruct), stack->size, stderr);
+      fflush(stderr);
+
+      fprintf(stderr, "----- MEMORY FOOTER -----\n");
+      bound = SILC_ST_GET_BOUND(stack, stack->size);
+      silc_hexdump((unsigned char *)bound, 4, stderr);
+      fprintf(stderr, "Footer bound is: %p\n", SILC_32_TO_PTR(*bound));
+      if (*bound != SILC_ST_BOTTOM_BOUND) {
+       fprintf(stderr, "Footer bound should be: %p\n",
+               SILC_32_TO_PTR(SILC_ST_BOTTOM_BOUND));
+        fprintf(stderr, "MEMORY IS CORRUPTED (OVERFLOW)!\n");
+      }
+    }
   }
 
-  if (!malloc_check) {
-    /* Linux libc malloc check */
-    setenv("MALLOC_CHECK_", "2", 1);
+  fflush(stderr);
 
-    /* NetBSD malloc check */
-    setenv("MALLOC_OPTIONS", "AJ", 1);
+  abort();
+}
 
-    malloc_check = TRUE;
-  }
+void silc_st_sigsegv(int sig, siginfo_t *si, void *context)
+{
+  SilcStBlock orig, stack = (SilcStBlock)si->si_addr;
 
-  /* Save the stack */
-  SILC_ST_GET_FP(fp);
-  for (stack->depth = 0; fp; stack->depth++) {
-    if (stack->depth == SILC_ST_DEPTH)
-      break;
+  /* Make the page accessible again */
+  mprotect(si->si_addr, pg, PROT_READ | PROT_WRITE);
+
+  /* Get the original page from the violated page */
+  orig = (SilcStBlock)(((unsigned char *)si->si_addr) -
+                       SILC_ST_GET_SIZE_ALIGN(stack->size, pg));
+  stack = SILC_ST_GET_STACK_ALIGN(orig, stack->size, pg);
+
+  silc_st_abort(stack, __FILE__, __LINE__,
+               "SILC_MALLOC: access violation (overflow)\n");
+}
+
+void silc_st_stacktrace_init(void)
+{
+  const char *var;
+
+  atexit(silc_st_dump);
+  dump = TRUE;
+
+  var = getenv("SILC_MALLOC_NO_FREE");
+  if (var && *var == '1')
+    no_free = TRUE;
+
+  var = getenv("SILC_MALLOC_DUMP");
+  if (var && *var == '1')
+    dump_mem = TRUE;
+
+  var = getenv("SILC_MALLOC_PROTECT");
+  if (var && *var == '1') {
+    struct sigaction sa;
+
+    sa.sa_flags = SA_SIGINFO;
+    sa.sa_sigaction = silc_st_sigsegv;
+    sigemptyset(&sa.sa_mask);
+    sigaction(SIGSEGV, &sa, NULL);
 
-    /* Get program pointer and frame pointer from this frame */
-    stack->stack[stack->depth] = *((void **)(((unsigned char *)fp) + 4));
-    fp = *((void **)fp);
+#if defined(_SC_PAGESIZE)
+    pg = sysconf(_SC_PAGESIZE);
+#elif defined(_SC_PAGE_SIZE)
+    pg = sysconf(_SC_PAGE_SIZE);
+#else
+    pg = getpagesize();
+#endif /* _SC_PAGESIZE */
   }
+
+  /* Linux libc malloc check */
+  setenv("MALLOC_CHECK_", "3", 1);
+
+  /* NetBSD malloc check */
+  setenv("MALLOC_OPTIONS", "AJ", 1);
+
+  silc_mutex_alloc(&lock);
 }
 
 void *silc_st_malloc(size_t size, const char *file, int line)
 {
-  SilcStBlock stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size));
-  assert(stack != NULL);
+  SilcStBlock stack;
+
+  if (silc_unlikely(!dump))
+    silc_st_stacktrace_init();
+
+  if (pg) {
+    unsigned char *ptr;
+
+    if (posix_memalign((void *)&ptr, pg,
+                      SILC_ST_GET_SIZE_ALIGN(size, pg) + pg))
+      return NULL;
+
+    /* The inaccessible page too will include the allocation information
+       so that we can get it when access violation occurs in that page. */
+    stack = (SilcStBlock)(ptr + SILC_ST_GET_SIZE_ALIGN(size, pg));
+    stack->size = size;
+
+    /* Protect the page */
+    if (mprotect(stack, pg, PROT_NONE))
+      silc_st_abort(NULL, file, line, "SILC_MALLOC: mprotect() error: %s\n",
+                   errno == ENOMEM ? "Cannot allocate memory. \nYour program "
+                   "leaks memory, allocates too much or system \n"
+                   "is out of memory.  The SILC_MALLOC_PROTECT cannot "
+                   "be used." : strerror(errno));
+
+    /* Get the accessible page */
+    stack = SILC_ST_GET_STACK_ALIGN(ptr, size, pg);
+  } else {
+    stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size));
+    if (!stack)
+      return NULL;
+  }
 
   stack->dumpped = 0;
   stack->file = file;
+  stack->free_file = NULL;
   stack->line = line;
   stack->size = size;
-  silc_st_stacktrace(stack);
+  stack->bound = SILC_ST_TOP_BOUND;
+  stack->depth = backtrace(stack->stack, SILC_ST_DEPTH);
+
+  silc_mutex_lock(lock);
 
   stack->next = st_blocks;
   stack->prev = NULL;
@@ -106,6 +300,12 @@ void *silc_st_malloc(size_t size, const char *file, int line)
     ((SilcStBlock)st_blocks)->prev = stack;
   st_blocks = stack;
   st_blocks_count++;
+  st_num_malloc++;
+
+  silc_mutex_unlock(lock);
+
+  if (!pg)
+    *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
 
   return SILC_ST_GET_PTR(stack);
 }
@@ -113,7 +313,8 @@ void *silc_st_malloc(size_t size, const char *file, int line)
 void *silc_st_calloc(size_t items, size_t size, const char *file, int line)
 {
   void *addr = (void *)silc_st_malloc(items * size, file, line);
-  memset(addr, 0, items * size);
+  if (addr)
+    memset(addr, 0, items * size);
   return addr;
 }
 
@@ -125,25 +326,63 @@ void *silc_st_realloc(void *ptr, size_t size, const char *file, int line)
     return silc_st_malloc(size, file, line);
 
   stack = SILC_ST_GET_STACK(ptr);
-  if (stack->size >= size) {
+  if (!pg && stack->size >= size) {
+    /* Must update footer when the size changes */
+    if (stack->size != size)
+      *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
+
     stack->size = size;
     return ptr;
   } else {
     void *addr = (void *)silc_st_malloc(size, file, line);
-    memcpy(addr, ptr, stack->size);
-    silc_st_free(ptr, file, line);
+    if (addr) {
+      memcpy(addr, ptr, size > stack->size ? stack->size : size);
+      silc_st_free(ptr, file, line);
+    }
     return addr;
   }
 }
 
 void silc_st_free(void *ptr, const char *file, int line)
 {
-  SilcStBlock stack;
+  SilcStBlock stack, s;
 
   if (!ptr)
     return;
 
+  /* Check for double free */
+  if (!memcmp((unsigned char *)ptr - sizeof(struct SilcStBlockStruct),
+             "\x47\x47\x47\x47", 4))
+    silc_st_abort(no_free ? ptr - sizeof(struct SilcStBlockStruct) : NULL,
+                 file, line, "SILC_MALLOC: double free: %p already freed\n",
+                 ptr - sizeof(struct SilcStBlockStruct));
+
   stack = SILC_ST_GET_STACK(ptr);
+
+  silc_mutex_lock(lock);
+
+  /* Check if we have ever made this allocation */
+  for (s = st_blocks; s; s = s->next)
+    if (s == stack)
+      break;
+  if (s == NULL)
+    silc_st_abort(NULL, file, line,
+                 "SILC_MALLOC: %p was never allocated\n", stack);
+
+  if (!pg) {
+    /* Check for underflow */
+    if (stack->bound != SILC_ST_TOP_BOUND)
+      silc_st_abort(stack, file, line,
+                   "SILC_MALLOC: %p was written out of bounds (underflow)\n",
+                   stack);
+
+    /* Check for overflow */
+    if (*SILC_ST_GET_BOUND(stack, stack->size) != SILC_ST_BOTTOM_BOUND)
+      silc_st_abort(stack, file, line,
+                   "SILC_MALLOC: %p was written out of bounds (overflow)\n",
+                   stack);
+  }
+
   if (stack->next)
     stack->next->prev = stack->prev;
   if (stack->prev)
@@ -153,15 +392,35 @@ void silc_st_free(void *ptr, const char *file, int line)
 
   st_blocks_count--;
 
-  memset(stack, 'F', SILC_ST_GET_SIZE(stack->size));
-  free(stack);
+  silc_mutex_unlock(lock);
+
+  stack->free_file = file;
+  stack->free_line = line;
+
+  if (no_free) {
+    memset(stack, 0x47, 8);
+    return;
+  }
+
+  if (pg) {
+    ptr = SILC_ST_GET_PTR_ALIGN(stack, pg);
+    mprotect(ptr + SILC_ST_GET_SIZE_ALIGN(stack->size, pg), pg,
+            PROT_READ | PROT_WRITE);
+    memset(ptr, 0x47, SILC_ST_GET_SIZE_ALIGN(stack->size, pg));
+    free(ptr);
+  } else {
+    memset(stack, 0x47, SILC_ST_GET_SIZE(stack->size));
+    free(stack);
+  }
 }
 
 void *silc_st_memdup(const void *ptr, size_t size, const char *file, int line)
 {
   unsigned char *addr = (unsigned char *)silc_st_malloc(size + 1, file, line);
-  memcpy((void *)addr, ptr, size);
-  addr[size] = '\0';
+  if (addr) {
+    memcpy((void *)addr, ptr, size);
+    addr[size] = '\0';
+  }
   return (void *)addr;
 }
 
@@ -170,15 +429,20 @@ void *silc_st_strdup(const char *string, const char *file, int line)
   return silc_st_memdup(string, strlen(string), file, line);
 }
 
-/* Dumps the stack into file if there are leaks.  The file can be read
-   with a special stacktrace tool. */
+/* Dumps the stack into file if there are leaks. */
 
 void silc_st_dump(void)
 {
   SilcStBlock stack, s;
   unsigned long leaks = 0, blocks, bytes;
   FILE *fp = NULL;
+  char **syms, *cp;
   int i;
+  SilcMutex l;
+
+  l = lock;
+  lock = NULL;
+  silc_mutex_free(l);
 
   for (stack = st_blocks; stack; stack = stack->next) {
     bytes = blocks = 0;
@@ -194,6 +458,10 @@ void silc_st_dump(void)
        fp = stderr;
     }
 
+    /* Get symbol names */
+    syms = backtrace_symbols(stack->stack, stack->depth);
+
+    /* Find number of leaks and bytes leaked for this leak */
     for (s = stack; s; s = s->next) {
       if (s->file == stack->file && s->line == stack->line &&
          s->depth == stack->depth &&
@@ -206,10 +474,24 @@ void silc_st_dump(void)
     }
 
     if (blocks) {
-      fprintf(fp, "<stacktrace>%s:%d: #blocks=%lu, bytes=%lu\n",
+      fprintf(fp, "<leak>%s:%d: #blocks=%lu, bytes=%lu\n",
              stack->file, stack->line, blocks, bytes);
-      for (i = 0; i < stack->depth; i++)
-       fprintf(fp, "\tpc=%p\n", stack->stack[i]);
+      for (i = 0; i < stack->depth; i++) {
+       if (syms) {
+         cp = syms[i];
+         if (strchr(cp, '('))
+           cp = strchr(cp, '(') + 1;
+         else if (strchr(cp, ' '))
+           cp = strchr(cp, ' ') + 1;
+         if (strchr(cp, ')'))
+           *strchr(cp, ')') = ' ';
+         fprintf(fp, "\t%s\n", cp);
+       } else {
+         fprintf(fp, "\tpc=%p\n", stack->stack[i]);
+       }
+      }
+      fprintf(fp, "\n");
+      free(syms);
     }
   }
 
@@ -224,6 +506,7 @@ void silc_st_dump(void)
            "-----------------------------------------\n"
            "-----------------------------------------\n",
            leaks, st_blocks_count);
+    fprintf(stderr, "Number of allocations: %lu\n", st_num_malloc);
   }
 
   if (fp && fp != stderr)
index bfe8ae035e34badbcb7ed1de5b74f363aba705ce..ae14890491a1a578b990c67680a66c8f8408de39 100644 (file)
@@ -1,6 +1,6 @@
 /*
 
-  stacktrace.h 
+  stacktrace.h
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
 
 */
 
-#ifndef STACKTRACE_H
-#define STACKTRACE_H
+#ifndef MEMTRACE_H
+#define MEMTRACE_H
 
 #ifndef SILCMEMORY_H
 #error "Do not include internal header file directly"
 #endif
 
-#if defined(__GNUC__) && defined(__i386__)
+#if defined(__GNUC__)
 
 #undef strdup
 #define silc_malloc(s)      silc_st_malloc((s), __FILE__, __LINE__)
@@ -32,6 +32,7 @@
 #define silc_realloc(p, s)  silc_st_realloc((p), (s), __FILE__, __LINE__)
 #define silc_free(p)        silc_st_free((p), __FILE__, __LINE__)
 #define silc_memdup(p, s)   silc_st_memdup((p), (s), __FILE__, __LINE__)
+#define silc_strdup(s)      silc_st_strdup((s), __FILE__, __LINE__)
 #define strdup(s)           silc_st_strdup((s), __FILE__, __LINE__)
 
 void *silc_st_malloc(size_t size, const char *file, int line);
@@ -44,6 +45,6 @@ void silc_st_dump(void);
 
 #else
 #error "memory allocation stack trace not supported on this platform"
-#endif /* __GNUC__ && __i386__ */
+#endif /* __GNUC__ */
 
-#endif /* STACKTRACE_H */
+#endif /* MEMTRACE_H */