Added protected memory allocation to stacktrace
authorPekka Riikonen <priikone@silcnet.org>
Sat, 27 Sep 2008 17:46:15 +0000 (20:46 +0300)
committerPekka Riikonen <priikone@silcnet.org>
Sat, 27 Sep 2008 17:46:15 +0000 (20:46 +0300)
If SILC_MALLOC_PROTECT environment variable is set the program will
abort whenever memory is accessed beyond its boundaries.

lib/silcutil/stacktrace.c
lib/silcutil/tests/test_silcmime.c
lib/silcutil/tests/test_silctree.c

index b5cd543a960f59cb16da6849da215c4dc3964577..60d8f056cea0180df85a20ce47d6c6e3d47881d2 100644 (file)
      By setting SILC_MALLOC_DUMP the memory is dummped to help and see
      what it contains.
 
      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
    The following environment variables can be used:
 
    SILC_MALLOC_NO_FREE
      When set to value 1, in case of fatal error, dumps the memory location,
      if possible.  This can help see what the memory contains.
 
      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.
 
    To work correctly this of course expects that code uses SILC memory
    allocation and access routines.
 
 
 #ifdef SILC_STACKTRACE
 #include <execinfo.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 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 SilcBool no_free = FALSE;
 static SilcBool dump_mem = FALSE;
+static SilcUInt32 pg = 0;
 static SilcMutex lock = NULL;
 
 #ifdef SILC_DEBUG
 static SilcMutex lock = NULL;
 
 #ifdef SILC_DEBUG
@@ -101,6 +115,16 @@ typedef struct SilcStBlockStruct {
 #define SILC_ST_GET_BOUND(p, size) (SilcUInt32 *)(((unsigned char *)p) + \
                                    SILC_ST_GET_SIZE(size) - 4)
 
 #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))
+
 void silc_st_abort(SilcStBlock stack, const char *file, int line,
                   char *fmt, ...)
 {
 void silc_st_abort(SilcStBlock stack, const char *file, int line,
                   char *fmt, ...)
 {
@@ -164,45 +188,98 @@ void silc_st_abort(SilcStBlock stack, const char *file, int line,
   abort();
 }
 
   abort();
 }
 
-void silc_st_stacktrace(SilcStBlock stack)
+void silc_st_sigsegv(int sig, siginfo_t *si, void *context)
 {
 {
-  if (!dump) {
-    atexit(silc_st_dump);
-    dump = TRUE;
-  }
+  SilcStBlock orig, stack = (SilcStBlock)si->si_addr;
+
+  /* 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");
+}
 
 
-  if (!malloc_check) {
-    const char *var;
+void silc_st_stacktrace_init(void)
+{
+  const char *var;
 
 
-    var = silc_getenv("SILC_MALLOC_NO_FREE");
-    if (var && *var == '1')
-      no_free = TRUE;
+  atexit(silc_st_dump);
+  dump = TRUE;
 
 
-    var = silc_getenv("SILC_MALLOC_DUMP");
-    if (var && *var == '1')
-      dump_mem = TRUE;
+  var = silc_getenv("SILC_MALLOC_NO_FREE");
+  if (var && *var == '1')
+    no_free = TRUE;
 
 
-    /* Linux libc malloc check */
-    silc_setenv("MALLOC_CHECK_", "3");
+  var = silc_getenv("SILC_MALLOC_DUMP");
+  if (var && *var == '1')
+    dump_mem = TRUE;
 
 
-    /* NetBSD malloc check */
-    silc_setenv("MALLOC_OPTIONS", "AJ");
+  var = silc_getenv("SILC_MALLOC_PROTECT");
+  if (var && *var == '1') {
+    struct sigaction sa;
 
 
-    malloc_check = TRUE;
+    sa.sa_flags = SA_SIGINFO;
+    sa.sa_sigaction = silc_st_sigsegv;
+    sigemptyset(&sa.sa_mask);
+    sigaction(SIGSEGV, &sa, NULL);
 
 
-    silc_mutex_alloc(&lock);
+#if defined(_SC_PAGESIZE)
+    pg = sysconf(_SC_PAGESIZE);
+#elif defined(_SC_PAGE_SIZE)
+    pg = sysconf(_SC_PAGE_SIZE);
+#else
+    pg = getpagesize();
+#endif /* _SC_PAGESIZE */
   }
 
   }
 
-  /* Get backtrace */
-  stack->depth = backtrace(stack->stack, SILC_ST_DEPTH);
+  /* Linux libc malloc check */
+  silc_setenv("MALLOC_CHECK_", "3");
+
+  /* NetBSD malloc check */
+  silc_setenv("MALLOC_OPTIONS", "AJ");
+
+  silc_mutex_alloc(&lock);
 }
 
 void *silc_st_malloc(size_t size, const char *file, int line)
 {
 }
 
 void *silc_st_malloc(size_t size, const char *file, int line)
 {
-  SilcStBlock stack = (SilcStBlock)malloc(SILC_ST_GET_SIZE(size));
+  SilcStBlock stack;
+
+  if (silc_unlikely(!dump))
+    silc_st_stacktrace_init();
 
 
-  if (!stack)
-    return NULL;
+  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->dumpped = 0;
   stack->file = file;
@@ -210,7 +287,7 @@ void *silc_st_malloc(size_t size, const char *file, int line)
   stack->line = line;
   stack->size = size;
   stack->bound = SILC_ST_TOP_BOUND;
   stack->line = line;
   stack->size = size;
   stack->bound = SILC_ST_TOP_BOUND;
-  silc_st_stacktrace(stack);
+  stack->depth = backtrace(stack->stack, SILC_ST_DEPTH);
 
   silc_mutex_lock(lock);
 
 
   silc_mutex_lock(lock);
 
@@ -224,7 +301,8 @@ void *silc_st_malloc(size_t size, const char *file, int line)
 
   silc_mutex_unlock(lock);
 
 
   silc_mutex_unlock(lock);
 
-  *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
+  if (!pg)
+    *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
 
   return SILC_ST_GET_PTR(stack);
 }
 
   return SILC_ST_GET_PTR(stack);
 }
@@ -232,7 +310,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);
 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;
 }
 
   return addr;
 }
 
@@ -244,7 +323,7 @@ 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);
     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;
     /* Must update footer when the size changes */
     if (stack->size != size)
       *SILC_ST_GET_BOUND(stack, size) = SILC_ST_BOTTOM_BOUND;
@@ -253,8 +332,10 @@ void *silc_st_realloc(void *ptr, size_t size, const char *file, int line)
     return ptr;
   } else {
     void *addr = (void *)silc_st_malloc(size, file, line);
     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;
   }
 }
     return addr;
   }
 }
@@ -285,17 +366,19 @@ void silc_st_free(void *ptr, const char *file, int line)
     silc_st_abort(NULL, file, line,
                  "SILC_MALLOC: %p was never allocated\n", stack);
 
     silc_st_abort(NULL, file, line,
                  "SILC_MALLOC: %p was never allocated\n", stack);
 
-  /* 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 (!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->next)
     stack->next->prev = stack->prev;
@@ -316,16 +399,25 @@ void silc_st_free(void *ptr, const char *file, int line)
     return;
   }
 
     return;
   }
 
-  memset(stack, 0x47, SILC_ST_GET_SIZE(stack->size));
-
-  free(stack);
+  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);
 }
 
 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;
 }
 
   return (void *)addr;
 }
 
index 5cf38bd82a8f4791b15f6223f8d7b613cb68484d..ea879e66c1bf12c7c952248813a6e7c4fc75d6ad 100644 (file)
@@ -47,7 +47,7 @@ int main(int argc, char **argv)
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
-  SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
+  SILC_LOG_HEXDUMP(("Encoded MIME message"), enc, enc_len);
   silc_mime_free(mime);
   SILC_LOG_DEBUG(("Decoding MIME message"));
   mime = silc_mime_decode(NULL, enc, enc_len);
   silc_mime_free(mime);
   SILC_LOG_DEBUG(("Decoding MIME message"));
   mime = silc_mime_decode(NULL, enc, enc_len);
@@ -58,7 +58,7 @@ int main(int argc, char **argv)
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
-  SILC_LOG_DEBUG(("Re-encoded MIME message: \n%s", enc));
+  SILC_LOG_HEXDUMP(("Re-encoded MIME message"), enc, enc_len);
   silc_free(enc);
   silc_mime_free(mime);
 
   silc_free(enc);
   silc_mime_free(mime);
 
@@ -76,7 +76,7 @@ int main(int argc, char **argv)
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
-  SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
+  SILC_LOG_HEXDUMP(("Encoded MIME message"), enc, enc_len);
   silc_mime_free(mime);
   SILC_LOG_DEBUG(("Decoding MIME message"));
   mime = silc_mime_decode(NULL, enc, enc_len);
   silc_mime_free(mime);
   SILC_LOG_DEBUG(("Decoding MIME message"));
   mime = silc_mime_decode(NULL, enc, enc_len);
@@ -87,7 +87,7 @@ int main(int argc, char **argv)
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
-  SILC_LOG_HEXDUMP(("Re-encoded MIME message:"), enc, enc_len);
+  SILC_LOG_HEXDUMP(("Re-encoded MIME message"), enc, enc_len);
   silc_free(enc);
   silc_mime_free(mime);
 
   silc_free(enc);
   silc_mime_free(mime);
 
@@ -183,7 +183,7 @@ int main(int argc, char **argv)
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
-  SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
+  SILC_LOG_HEXDUMP(("Encoded MIME message"), enc, enc_len);
   silc_mime_free(mime);
   SILC_LOG_DEBUG(("Decoding MIME message"));
   mime = silc_mime_decode(NULL, enc, enc_len);
   silc_mime_free(mime);
   SILC_LOG_DEBUG(("Decoding MIME message"));
   mime = silc_mime_decode(NULL, enc, enc_len);
@@ -194,7 +194,7 @@ int main(int argc, char **argv)
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
-  SILC_LOG_DEBUG(("Re-encoded MIME message: \n%s", enc));
+  SILC_LOG_HEXDUMP(("Re-encoded MIME message"), enc, enc_len);
   silc_free(enc);
   SILC_LOG_DEBUG(("Get multiparts"));
   frag = silc_mime_get_multiparts(mime, &mtype);
   silc_free(enc);
   SILC_LOG_DEBUG(("Get multiparts"));
   frag = silc_mime_get_multiparts(mime, &mtype);
@@ -209,7 +209,7 @@ int main(int argc, char **argv)
         goto err;
     if (silc_mime_is_multipart(part))
         SILC_LOG_DEBUG(("Is multipart"));
         goto err;
     if (silc_mime_is_multipart(part))
         SILC_LOG_DEBUG(("Is multipart"));
-    SILC_LOG_DEBUG(("Encoded MIME part: \n%s", enc));
+    SILC_LOG_HEXDUMP(("Encoded MIME message"), enc, enc_len);
     silc_free(enc);
   }
   silc_mime_free(mime);
     silc_free(enc);
   }
   silc_mime_free(mime);
@@ -243,7 +243,7 @@ int main(int argc, char **argv)
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
   enc = silc_mime_encode(mime, &enc_len);
   if (!enc)
     goto err;
-  SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
+  SILC_LOG_HEXDUMP(("Encoded MIME message"), enc, enc_len);
   silc_free(enc);
   SILC_LOG_DEBUG(("Fragment MIME message in 100 byte chunks"));
   frag = silc_mime_encode_partial(mime, 100);
   silc_free(enc);
   SILC_LOG_DEBUG(("Fragment MIME message in 100 byte chunks"));
   frag = silc_mime_encode_partial(mime, 100);
@@ -251,7 +251,8 @@ int main(int argc, char **argv)
     goto err;
   silc_dlist_start(frag);
   while ((buf = silc_dlist_get(frag)) != SILC_LIST_END)
     goto err;
   silc_dlist_start(frag);
   while ((buf = silc_dlist_get(frag)) != SILC_LIST_END)
-    SILC_LOG_DEBUG(("Fragment \n%s", buf->data, silc_buffer_len(buf)));
+    SILC_LOG_HEXDUMP(("Fragment %d", silc_buffer_len(buf)), buf->data,
+                    silc_buffer_len(buf));
   SILC_LOG_DEBUG(("Defragment"));
   silc_dlist_start(frag);
   while ((buf = silc_dlist_get(frag)) != SILC_LIST_END) {
   SILC_LOG_DEBUG(("Defragment"));
   silc_dlist_start(frag);
   while ((buf = silc_dlist_get(frag)) != SILC_LIST_END) {
@@ -265,7 +266,7 @@ int main(int argc, char **argv)
       enc = silc_mime_encode(mime, &enc_len);
       if (!enc)
         SILC_LOG_DEBUG(("Error encoding"));
       enc = silc_mime_encode(mime, &enc_len);
       if (!enc)
         SILC_LOG_DEBUG(("Error encoding"));
-      SILC_LOG_DEBUG(("Encoded MIME message: \n%s", enc));
+      SILC_LOG_HEXDUMP(("Encoded MIME message"), enc, enc_len);
       silc_free(enc);
       silc_mime_free(part);
     }
       silc_free(enc);
       silc_mime_free(part);
     }
index 9f614c185796dca7db63bc95a607308a45c32ae7..ea2be1ab12453f936ae97c18f361b6082d2ef87d 100644 (file)
@@ -18,7 +18,11 @@ static int compare(void *e1, void *e2, void *context)
 {
   Foo *f1 = e1, *f2 = e2;
   SILC_LOG_DEBUG(("%p %d > %p %d", f1, f1->id, f2, f2->id));
 {
   Foo *f1 = e1, *f2 = e2;
   SILC_LOG_DEBUG(("%p %d > %p %d", f1, f1->id, f2, f2->id));
-  return f1->id - f2->id;
+  if (f1->id > f2->id)
+    return SILC_COMPARE_GREATER_THAN;
+  if (f1->id < f2->id)
+    return SILC_COMPARE_LESS_THAN;
+  return SILC_COMPARE_EQUAL_TO;
 }
 
 int main(int argc, char **argv)
 }
 
 int main(int argc, char **argv)