Implement ticket #314: Added PJ_SAFE_POOL configuration in PJLIB to track down memory corruptions

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@1333 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
index c999bb8..cbdc0aa 100644
--- a/pjlib/include/pj/config.h
+++ b/pjlib/include/pj/config.h
@@ -388,6 +388,22 @@
 
 
 /**
+ * Set this flag to non-zero to enable various checking for pool
+ * operations. When this flag is set, assertion must be enabled
+ * in the application.
+ *
+ * This will slow down pool creation and destruction and will add
+ * few bytes of overhead, so application would normally want to 
+ * disable this feature on release build.
+ *
+ * Default: 0
+ */
+#ifndef PJ_SAFE_POOL
+#   define PJ_SAFE_POOL		    0
+#endif
+
+
+/**
  * If pool debugging is used, then each memory allocation from the pool
  * will call malloc(), and pool will release all memory chunks when it
  * is destroyed. This works better when memory verification programs
diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c
index b00bf10..01c303d 100644
--- a/pjlib/src/pj/pool_caching.c
+++ b/pjlib/src/pj/pool_caching.c
@@ -99,6 +99,8 @@
     while (pool != (pj_pool_t*) &cp->used_list) {
 	pj_pool_t *next = pool->next;
 	pj_list_erase(pool);
+	PJ_LOG(4,(pool->obj_name, 
+		  "Pool is not released by application, releasing now"));
 	pj_pool_destroy_int(pool);
 	pool = next;
     }
@@ -197,8 +199,18 @@
 
     PJ_CHECK_STACK();
 
+    PJ_ASSERT_ON_FAIL(pf && pool, return);
+
     pj_lock_acquire(cp->lock);
 
+#if PJ_SAFE_POOL
+    /* Make sure pool is still in our used list */
+    if (pj_list_find_node(&cp->used_list, pool) != pool) {
+	pj_assert(!"Attempt to destroy pool that has been destroyed before");
+	return;
+    }
+#endif
+
     /* Erase from the used list. */
     pj_list_erase(pool);
 
diff --git a/pjlib/src/pj/pool_policy_malloc.c b/pjlib/src/pj/pool_policy_malloc.c
index d3f282d..d7e8ad1 100644
--- a/pjlib/src/pj/pool_policy_malloc.c
+++ b/pjlib/src/pj/pool_policy_malloc.c
@@ -26,6 +26,7 @@
 /*
  * This file contains pool default policy definition and implementation.
  */
+#include "pool_signature.h"
 
 
 static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
@@ -41,11 +42,16 @@
 	    return NULL;
     }
 
-    p = malloc(size);
+    p = malloc(size+(SIG_SIZE << 1));
 
     if (p == NULL) {
 	if (factory->on_block_free) 
 	    factory->on_block_free(factory, size);
+    } else {
+	/* Apply signature when PJ_SAFE_POOL is set. It will move
+	 * "p" pointer forward.
+	 */
+	APPLY_SIG(p, size);
     }
 
     return p;
@@ -59,6 +65,15 @@
     if (factory->on_block_free) 
         factory->on_block_free(factory, size);
 
+    /* Check and remove signature when PJ_SAFE_POOL is set. It will
+     * move "mem" pointer backward.
+     */
+    REMOVE_SIG(mem, size);
+
+    /* Note that when PJ_SAFE_POOL is set, the actual size of the block
+     * is size + SIG_SIZE*2.
+     */
+
     free(mem);
 }
 
diff --git a/pjlib/src/pj/pool_policy_new.cpp b/pjlib/src/pj/pool_policy_new.cpp
index 367be0f..82374ad 100644
--- a/pjlib/src/pj/pool_policy_new.cpp
+++ b/pjlib/src/pj/pool_policy_new.cpp
@@ -25,15 +25,28 @@
 /*
  * This file contains pool default policy definition and implementation.
  */
+#include "pool_signature.h"
  
 
 static void *operator_new(pj_pool_factory *factory, pj_size_t size)
 {
+    void *mem;
+
     PJ_CHECK_STACK();
     PJ_UNUSED_ARG(factory);
     PJ_UNUSED_ARG(size);
 
-    return new char[size];
+    mem = (void*) new char[size+(SIG_SIZE << 1)];
+    
+    /* Exception for new operator may be disabled, so.. */
+    if (mem) {
+	/* Apply signature when PJ_SAFE_POOL is set. It will move
+	 * "mem" pointer forward.
+	 */
+	APPLY_SIG(mem, size);
+    }
+
+    return mem;
 }
 
 static void operator_delete(pj_pool_factory *factory, void *mem, pj_size_t size)
@@ -42,6 +55,15 @@
     PJ_UNUSED_ARG(factory);
     PJ_UNUSED_ARG(size);
 
+    /* Check and remove signature when PJ_SAFE_POOL is set. It will
+     * move "mem" pointer backward.
+     */
+    REMOVE_SIG(mem, size);
+
+    /* Note that when PJ_SAFE_POOL is set, the actual size of the block
+     * is size + SIG_SIZE*2.
+     */
+
     char *p = (char*)mem;
     delete [] p;
 }
diff --git a/pjlib/src/pj/pool_signature.h b/pjlib/src/pj/pool_signature.h
new file mode 100644
index 0000000..baf4139
--- /dev/null
+++ b/pjlib/src/pj/pool_signature.h
@@ -0,0 +1,67 @@
+/* $Id$ */
+/* 
+ * Copyright (C)2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/assert.h>
+#include <pj/string.h>
+
+#if PJ_SAFE_POOL
+#   define SIG_SIZE		sizeof(pj_uint32_t)
+
+static void apply_signature(void *p, pj_size_t size);
+static void check_pool_signature(void *p, pj_size_t size);
+
+#   define APPLY_SIG(p,sz)	apply_signature(p,sz), \
+				p=(void*)(((char*)p)+SIG_SIZE)
+#   define REMOVE_SIG(p,sz)	check_pool_signature(p,sz), \
+				p=(void*)(((char*)p)-SIG_SIZE)
+
+#   define SIG_BEGIN	    0x600DC0DE
+#   define SIG_END	    0x0BADC0DE
+
+static void apply_signature(void *p, pj_size_t size)
+{
+    pj_uint32_t sig;
+
+    sig = SIG_BEGIN;
+    pj_memcpy(p, &sig, SIG_SIZE);
+
+    sig = SIG_END;
+    pj_memcpy(((char*)p)+SIG_SIZE+size, &sig, SIG_SIZE);
+}
+
+static void check_pool_signature(void *p, pj_size_t size)
+{
+    pj_uint32_t sig;
+    pj_uint8_t *mem = (pj_uint8_t*)p;
+
+    /* Check that signature at the start of the block is still intact */
+    sig = SIG_BEGIN;
+    pj_assert(!pj_memcmp(mem-SIG_SIZE, &sig, SIG_SIZE));
+
+    /* Check that signature at the end of the block is still intact.
+     * Note that "mem" has been incremented by SIG_SIZE 
+     */
+    sig = SIG_END;
+    pj_assert(!pj_memcmp(mem+size, &sig, SIG_SIZE));
+}
+
+#else
+#   define SIG_SIZE	    0
+#   define APPLY_SIG(p,sz)
+#   define REMOVE_SIG(p,sz)
+#endif