OSDN Git Service

Add some debug support code to try to catch future mistakes in the area of
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Apr 2008 22:54:23 +0000 (22:54 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 11 Apr 2008 22:54:23 +0000 (22:54 +0000)
input functions that include garbage bytes in their results.  Provide a
compile-time option RANDOMIZE_ALLOCATED_MEMORY to make palloc fill returned
blocks with variable contents.  This option also makes the parser perform
conversions of literal constants twice and compare the results, emitting a
WARNING if they don't match.  (This is the code I used to catch the input
function bugs fixed in the previous commit.)  For the moment, I've set it
to be activated automatically by --enable-cassert.

src/backend/parser/parse_type.c
src/backend/utils/mmgr/aset.c
src/include/pg_config_manual.h

index e7fc0e3..e07879b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.94 2008/01/01 19:45:51 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.95 2008/04/11 22:54:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@
 #include "parser/parse_type.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
+#include "utils/datum.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
@@ -485,13 +486,38 @@ typeTypeRelid(Type typ)
 Datum
 stringTypeDatum(Type tp, char *string, int32 atttypmod)
 {
-       Oid                     typinput;
-       Oid                     typioparam;
+       Form_pg_type typform = (Form_pg_type) GETSTRUCT(tp);
+       Oid                     typinput = typform->typinput;
+       Oid                     typioparam = getTypeIOParam(tp);
+       Datum           result;
 
-       typinput = ((Form_pg_type) GETSTRUCT(tp))->typinput;
-       typioparam = getTypeIOParam(tp);
-       return OidInputFunctionCall(typinput, string,
-                                                               typioparam, atttypmod);
+       result = OidInputFunctionCall(typinput, string,
+                                                                 typioparam, atttypmod);
+
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+       /*
+        * For pass-by-reference data types, repeat the conversion to see if the
+        * input function leaves any uninitialized bytes in the result.  We can
+        * only detect that reliably if RANDOMIZE_ALLOCATED_MEMORY is enabled,
+        * so we don't bother testing otherwise.  The reason we don't want any
+        * instability in the input function is that comparison of Const nodes
+        * relies on bytewise comparison of the datums, so if the input function
+        * leaves garbage then subexpressions that should be identical may not get
+        * recognized as such.  See pgsql-hackers discussion of 2008-04-04.
+        */
+       if (string && !typform->typbyval)
+       {
+               Datum           result2;
+
+               result2 = OidInputFunctionCall(typinput, string,
+                                                                          typioparam, atttypmod);
+               if (!datumIsEqual(result, result2, typform->typbyval, typform->typlen))
+                       elog(WARNING, "type %s has unstable input conversion for \"%s\"",
+                                NameStr(typform->typname), string);
+       }
+#endif
+
+       return result;
 }
 
 /* given a typeid, return the type's typrelid (associated relation, if any) */
index 81f73f9..a6ac6ab 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.76 2008/01/01 19:45:55 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.77 2008/04/11 22:54:23 tgl Exp $
  *
  * NOTE:
  *     This is a new (Feb. 05, 1999) implementation of the allocation set
@@ -282,6 +282,32 @@ AllocSetFreeIndex(Size size)
        return idx;
 }
 
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+
+/*
+ * Fill a just-allocated piece of memory with "random" data.  It's not really
+ * very random, just a repeating sequence with a length that's prime.  What
+ * we mainly want out of it is to have a good probability that two palloc's
+ * of the same number of bytes start out containing different data.
+ */
+static void
+randomize_mem(char *ptr, size_t size)
+{
+       static int      save_ctr = 1;
+       int                     ctr;
+
+       ctr = save_ctr;
+       while (size-- > 0)
+       {
+               *ptr++ = ctr;
+               if (++ctr > 251)
+                       ctr = 1;
+       }
+       save_ctr = ctr;
+}
+
+#endif /* RANDOMIZE_ALLOCATED_MEMORY */
+
 
 /*
  * Public routines
@@ -552,6 +578,10 @@ AllocSetAlloc(MemoryContext context, Size size)
                if (size < chunk_size)
                        ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
 #endif
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+               /* fill the allocated space with junk */
+               randomize_mem((char *) AllocChunkGetPointer(chunk), size);
+#endif
 
                /*
                 * Stick the new block underneath the active allocation block, so that
@@ -596,6 +626,10 @@ AllocSetAlloc(MemoryContext context, Size size)
                if (size < chunk->size)
                        ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
 #endif
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+               /* fill the allocated space with junk */
+               randomize_mem((char *) AllocChunkGetPointer(chunk), size);
+#endif
 
                /* isReset must be false already */
                Assert(!set->isReset);
@@ -752,6 +786,10 @@ AllocSetAlloc(MemoryContext context, Size size)
        if (size < chunk->size)
                ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E;
 #endif
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+       /* fill the allocated space with junk */
+       randomize_mem((char *) AllocChunkGetPointer(chunk), size);
+#endif
 
        set->isReset = false;
 
@@ -864,6 +902,13 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
        if (oldsize >= size)
        {
 #ifdef MEMORY_CONTEXT_CHECKING
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+               /* We can only fill the extra space if we know the prior request */
+               if (size > chunk->requested_size)
+                       randomize_mem((char *) AllocChunkGetPointer(chunk) + chunk->requested_size,
+                                                 size - chunk->requested_size);
+#endif
+
                chunk->requested_size = size;
                /* set mark to catch clobber of "unused" space */
                if (size < oldsize)
@@ -921,6 +966,12 @@ AllocSetRealloc(MemoryContext context, void *pointer, Size size)
                chunk->size = chksize;
 
 #ifdef MEMORY_CONTEXT_CHECKING
+#ifdef RANDOMIZE_ALLOCATED_MEMORY
+               /* We can only fill the extra space if we know the prior request */
+               randomize_mem((char *) AllocChunkGetPointer(chunk) + chunk->requested_size,
+                                         size - chunk->requested_size);
+#endif
+
                chunk->requested_size = size;
                /* set mark to catch clobber of "unused" space */
                if (size < chunk->size)
index 5eead55..6d7ae4e 100644 (file)
@@ -6,7 +6,7 @@
  * for developers.     If you edit any of these, be sure to do a *full*
  * rebuild (and an initdb if noted).
  *
- * $PostgreSQL: pgsql/src/include/pg_config_manual.h,v 1.30 2008/03/27 03:57:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/pg_config_manual.h,v 1.31 2008/04/11 22:54:23 tgl Exp $
  *------------------------------------------------------------------------
  */
 
  */
 
 /*
+ * Define this to cause palloc()'d memory to be filled with random data, to
+ * facilitate catching code that depends on the contents of uninitialized
+ * memory.  Right now, this gets defined automatically if --enable-cassert.
+ */
+#ifdef USE_ASSERT_CHECKING
+#define RANDOMIZE_ALLOCATED_MEMORY
+#endif
+
+/*
  * Define this to cause pfree()'d memory to be cleared immediately, to
- * facilitate catching bugs that refer to already-freed values.  XXX
- * Right now, this gets defined automatically if --enable-cassert.     In
- * the long term it probably doesn't need to be on by default.
+ * facilitate catching bugs that refer to already-freed values.
+ * Right now, this gets defined automatically if --enable-cassert.
  */
 #ifdef USE_ASSERT_CHECKING
 #define CLOBBER_FREED_MEMORY
 /*
  * Define this to check memory allocation errors (scribbling on more
  * bytes than were allocated). Right now, this gets defined
- * automatically if --enable-cassert.  In the long term it probably
- * doesn't need to be on by default.
+ * automatically if --enable-cassert.
  */
 #ifdef USE_ASSERT_CHECKING
 #define MEMORY_CONTEXT_CHECKING