foxygit / doom Log in
commit 31c0b0c7e1e9340145e11866c74e20ceddae77f9
Author:     Simon Howard <fraggle@soulsphere.org>
AuthorDate: Sun Apr 26 21:19:40 2015 -0400
Commit:     Simon Howard <fraggle@soulsphere.org>
CommitDate: Sun Apr 26 21:19:40 2015 -0400

    zone: Add -zonescan parameter.

    As further tooling to help debug bugs like #530, add a command line
    parameter that will scan the zone heap every time a block of memory
    is freed with Z_Free(), to detect dangling pointers to the freed
    block.
---
 src/z_zone.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 1 deletion(-)

diff --git a/src/z_zone.c b/src/z_zone.c
index 91f31fb2..311eff50 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -64,6 +64,7 @@ typedef struct

 static memzone_t *mainzone;
 static boolean zero_on_free;
+static boolean scan_on_free;


 //
@@ -120,12 +121,54 @@ void Z_Init (void)
     block->size = mainzone->size - sizeof(memzone_t);

     //!
-    // Zone memory debugging flag. If set, zero memory after it is freed
+    // Zone memory debugging flag. If set, memory is zeroed after it is freed
     // to deliberately break any code that attempts to use it after free.
     //
     zero_on_free = M_ParmExists("-zonezero");
+
+    //!
+    // Zone memory debugging flag. If set, each time memory is freed, the zone
+    // heap is scanned to look for remaining pointers to the freed block.
+    //
+    scan_on_free = M_ParmExists("-zonescan");
 }

+// Scan the zone heap for pointers within the specified range, and warn about
+// any remaining pointers.
+static void ScanForBlock(void *start, void *end)
+{
+    memblock_t *block;
+    void **mem;
+    int i, len, tag;
+
+    block = mainzone->blocklist.next;
+
+    while (block->next != &mainzone->blocklist)
+    {
+        tag = block->tag;
+
+        if (tag == PU_STATIC || tag == PU_LEVEL || tag == PU_LEVSPEC)
+        {
+            // Scan for pointers on the assumption that pointers are aligned
+            // on word boundaries (word size depending on pointer size):
+            mem = (void **) ((byte *) block + sizeof(memblock_t));
+            len = (block->size - sizeof(memblock_t)) / sizeof(void *);
+
+            for (i = 0; i < len; ++i)
+            {
+                if (start <= mem[i] && mem[i] <= end)
+                {
+                    fprintf(stderr,
+                            "%p has dangling pointer into freed block "
+                            "%p (%p -> %p)\n",
+                            mem, start, &mem[i], mem[i]);
+                }
+            }
+        }
+
+        block = block->next;
+    }
+}

 //
 // Z_Free
@@ -157,6 +200,11 @@ void Z_Free (void* ptr)
     {
         memset(ptr, 0, block->size - sizeof(memblock_t));
     }
+    if (scan_on_free)
+    {
+        ScanForBlock(ptr,
+                     (byte *) ptr + block->size - sizeof(memblock_t));
+    }

     other = block->prev;