util: add more memory debugging features
authorBrian Paul <brianp@vmware.com>
Wed, 28 Nov 2012 20:35:01 +0000 (13:35 -0700)
committerBrian Paul <brianp@vmware.com>
Wed, 28 Nov 2012 22:03:29 +0000 (15:03 -0700)
Add a DEBUG_FREED_MEMORY option to help catch use-after-free errors.
Add debug_memory_check() function which can be periodically called to
check that all known blocks are good.

Reviewed-by: José Fonseca <jfonseca@vmware.com>
src/gallium/auxiliary/os/os_memory_debug.h
src/gallium/auxiliary/util/u_debug_memory.c

index c664be9aad58f5e1739a2b5d35182689d684dc4f..36b8fc63a6e1c95e1c875412c3b3ca131191841a 100644 (file)
@@ -60,6 +60,9 @@ void *
 debug_realloc(const char *file, unsigned line, const char *function,
               void *old_ptr, size_t old_size, size_t new_size );
 
+void 
+debug_memory_check(void);
+
 
 #ifdef __cplusplus
 }
index e24a8bc0b433208095f5938e7f2ad66e6874022c..4bf26a5241499d1de5242199883015c38bf24984 100644 (file)
 #define DEBUG_MEMORY_MAGIC 0x6e34090aU 
 #define DEBUG_MEMORY_STACK 0 /* XXX: disabled until we have symbol lookup */
 
+/**
+ * Set to 1 to enable checking of freed blocks of memory.
+ * Basically, don't really deallocate freed memory; keep it in the list
+ * but mark it as freed and do extra checking in debug_memory_check().
+ * This can detect some cases of use-after-free.  But note that since we
+ * never really free anything this will use a lot of memory.
+ */
+#define DEBUG_FREED_MEMORY 0
+#define DEBUG_FREED_BYTE 0x33
+
 
 struct debug_memory_header 
 {
@@ -61,7 +71,10 @@ struct debug_memory_header
    struct debug_stack_frame backtrace[DEBUG_MEMORY_STACK];
 #endif
    size_t size;
-   
+#if DEBUG_FREED_MEMORY
+   boolean freed;  /**< Is this a freed block? */
+#endif
+
    unsigned magic;
 };
 
@@ -127,6 +140,9 @@ debug_malloc(const char *file, unsigned line, const char *function,
    hdr->function = function;
    hdr->size = size;
    hdr->magic = DEBUG_MEMORY_MAGIC;
+#if DEBUG_FREED_MEMORY
+   hdr->freed = FALSE;
+#endif
 
 #if DEBUG_MEMORY_STACK
    debug_backtrace_capture(hdr->backtrace, 0, DEBUG_MEMORY_STACK);
@@ -169,6 +185,17 @@ debug_free(const char *file, unsigned line, const char *function,
       debug_assert(0);
    }
 
+#if DEBUG_FREED_MEMORY
+   /* Check for double-free */
+   assert(!hdr->freed);
+   /* Mark the block as freed but don't really free it */
+   hdr->freed = TRUE;
+   /* Save file/line where freed */
+   hdr->file = file;
+   hdr->line = line;
+   /* set freed memory to special value */
+   memset(ptr, DEBUG_FREED_BYTE, hdr->size);
+#else
    pipe_mutex_lock(list_mutex);
    LIST_DEL(&hdr->head);
    pipe_mutex_unlock(list_mutex);
@@ -176,6 +203,7 @@ debug_free(const char *file, unsigned line, const char *function,
    ftr->magic = 0;
    
    os_free(hdr);
+#endif
 }
 
 void *
@@ -235,6 +263,9 @@ debug_realloc(const char *file, unsigned line, const char *function,
    new_hdr->function = old_hdr->function;
    new_hdr->size = new_size;
    new_hdr->magic = DEBUG_MEMORY_MAGIC;
+#if DEBUG_FREED_MEMORY
+   new_hdr->freed = FALSE;
+#endif
    
    new_ftr = footer_from_header(new_hdr);
    new_ftr->magic = DEBUG_MEMORY_MAGIC;
@@ -314,3 +345,52 @@ debug_memory_end(unsigned long start_no)
       debug_printf("No memory leaks detected.\n");
    }
 }
+
+
+/**
+ * We can periodically call this from elsewhere to do a basic sanity
+ * check of the heap memory we've allocated.
+ */
+void 
+debug_memory_check(void)
+{
+   struct list_head *entry;
+
+   entry = list.prev;
+   for (; entry != &list; entry = entry->prev) {
+      struct debug_memory_header *hdr;
+      struct debug_memory_footer *ftr;
+      const char *ptr;
+
+      hdr = LIST_ENTRY(struct debug_memory_header, entry, head);
+      ftr = footer_from_header(hdr);
+      ptr = (const char *) data_from_header(hdr);
+
+      if (hdr->magic != DEBUG_MEMORY_MAGIC) {
+         debug_printf("%s:%u:%s: bad or corrupted memory %p\n",
+                      hdr->file, hdr->line, hdr->function, ptr);
+         debug_assert(0);
+      }
+
+      if (ftr->magic != DEBUG_MEMORY_MAGIC) {
+         debug_printf("%s:%u:%s: buffer overflow %p\n",
+                      hdr->file, hdr->line, hdr->function, ptr);
+         debug_assert(0);
+      }
+
+#if DEBUG_FREED_MEMORY
+      /* If this block is marked as freed, check that it hasn't been touched */
+      if (hdr->freed) {
+         int i;
+         for (i = 0; i < hdr->size; i++) {
+            if (ptr[i] != DEBUG_FREED_BYTE) {
+               debug_printf("Memory error: byte %d of block at %p of size %d is 0x%x\n",
+                            i, ptr, hdr->size, ptr[i]);
+               debug_printf("Block was freed at %s:%d\n", hdr->file, hdr->line);
+            }
+            assert(ptr[i] == DEBUG_FREED_BYTE);
+         }
+      }
+#endif
+   }
+}