New options for Memcheck, --malloc-fill=<hexnumber> and
--fill-free=<hexnumber>, which cause malloc'd(etc) and free'd(etc)
blocks to be filled with the specified value.  This can apparently be
useful for shaking out hard-to-track-down memory corruption.  The
definedness/addressability of said areas is not affected -- only the
contents.  Documentation to follow.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@7259 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/include/pub_tool_options.h b/include/pub_tool_options.h
index b2701e3..ed5c37e 100644
--- a/include/pub_tool_options.h
+++ b/include/pub_tool_options.h
@@ -72,6 +72,18 @@
       if ((qq_var) > (qq_hi)) (qq_var) = (qq_hi); \
    }
 
+/* Bounded hexadecimal arg */
+#define VG_BHEX_CLO(qq_arg, qq_option, qq_var, qq_lo, qq_hi) \
+   if (VG_CLO_STREQN(VG_(strlen)(qq_option)+1, qq_arg, qq_option"=")) { \
+      Char* s; \
+      Long n = VG_(strtoll16)( &qq_arg[ VG_(strlen)(qq_option)+1 ], &s );\
+      (qq_var) = n; \
+      /* Check for non-numeralness, or overflow */ \
+      if ('\0' != s[0] || (qq_var) != n) VG_(err_bad_option)(qq_arg); \
+      if ((qq_var) < (qq_lo)) (qq_var) = (qq_lo); \
+      if ((qq_var) > (qq_hi)) (qq_var) = (qq_hi); \
+   }
+
 /* Double arg */
 #define VG_DBL_CLO(qq_arg, qq_option, qq_var) \
    if (VG_CLO_STREQN(VG_(strlen)(qq_option)+1, qq_arg, qq_option"=")) { \
diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h
index c731271..c48e481 100644
--- a/memcheck/mc_include.h
+++ b/memcheck/mc_include.h
@@ -282,6 +282,15 @@
  * default: YES */
 extern Bool MC_(clo_undef_value_errors);
 
+/* Fill malloc-d/free-d client blocks with a specific value?  -1 if
+   not, else 0x00 .. 0xFF indicating the fill value to use.  Can be
+   useful for causing programs with bad heap corruption to fail in
+   more repeatable ways.  Note that malloc-filled and free-filled
+   areas are still undefined and noaccess respectively.  This merely
+   causes them to contain the specified values. */
+extern Int MC_(clo_malloc_fill);
+extern Int MC_(clo_free_fill);
+
 
 /*------------------------------------------------------------*/
 /*--- Instrumentation                                      ---*/
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index b24039c..b7e7006 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -4373,6 +4373,8 @@
 Bool          MC_(clo_show_reachable)         = False;
 Bool          MC_(clo_workaround_gcc296_bugs) = False;
 Bool          MC_(clo_undef_value_errors)     = True;
+Int           MC_(clo_malloc_fill)            = -1;
+Int           MC_(clo_free_fill)              = -1;
 
 static Bool mc_process_cmd_line_options(Char* arg)
 {
@@ -4429,6 +4431,9 @@
       }
    }
 
+   else VG_BHEX_CLO(arg, "--malloc-fill", MC_(clo_malloc_fill), 0x00, 0xFF)
+   else VG_BHEX_CLO(arg, "--free-fill",   MC_(clo_free_fill), 0x00, 0xFF)
+
    else
       return VG_(replacement_malloc_process_cmd_line_option)(arg);
 
@@ -4446,6 +4451,8 @@
 "    --freelist-vol=<number>          volume of freed blocks queue [10000000]\n"
 "    --workaround-gcc296-bugs=no|yes  self explanatory [no]\n"
 "    --ignore-ranges=0xPP-0xQQ[,0xRR-0xSS]   assume given addresses are OK\n"
+"    --malloc-fill=<hexnumber>        fill malloc'd areas with given value\n"
+"    --free-fill=<hexnumber>          fill free'd areas with given value\n"
    );
    VG_(replacement_malloc_print_usage)();
 }
diff --git a/memcheck/mc_malloc_wrappers.c b/memcheck/mc_malloc_wrappers.c
index 4fd9aec..adedded 100644
--- a/memcheck/mc_malloc_wrappers.c
+++ b/memcheck/mc_malloc_wrappers.c
@@ -182,8 +182,8 @@
 
 /* Allocate memory and note change in memory available */
 void* MC_(new_block) ( ThreadId tid,
-                        Addr p, SizeT szB, SizeT alignB, UInt rzB,
-                        Bool is_zeroed, MC_AllocKind kind, VgHashTable table)
+                       Addr p, SizeT szB, SizeT alignB, UInt rzB,
+                       Bool is_zeroed, MC_AllocKind kind, VgHashTable table)
 {
    cmalloc_n_mallocs ++;
 
@@ -196,7 +196,13 @@
       if (!p) {
          return NULL;
       }
-      if (is_zeroed) VG_(memset)((void*)p, 0, szB);
+      if (is_zeroed) {
+         VG_(memset)((void*)p, 0, szB);
+      } else 
+      if (MC_(clo_malloc_fill) != -1) {
+         tl_assert(MC_(clo_malloc_fill) >= 0x00 && MC_(clo_malloc_fill) <= 0xFF);
+         VG_(memset)((void*)p, MC_(clo_malloc_fill), szB);
+      }
    }
 
    // Only update this stat if allocation succeeded.
@@ -270,6 +276,11 @@
 static
 void die_and_free_mem ( ThreadId tid, MC_Chunk* mc, SizeT rzB )
 {
+   if (MC_(clo_free_fill) != -1) {
+      tl_assert(MC_(clo_free_fill) >= 0x00 && MC_(clo_free_fill) <= 0xFF);
+      VG_(memset)((void*)mc->data, MC_(clo_free_fill), mc->szB);
+   }
+
    /* Note: make redzones noaccess again -- just in case user made them
       accessible with a client request... */
    MC_(make_mem_noaccess)( mc->data-rzB, mc->szB + 2*rzB );
@@ -363,11 +374,19 @@
       mc->szB = new_szB;
       mc->where = VG_(record_ExeContext)(tid, 0/*first_ip_delta*/);
       p_new = p_old;
+      /* Possibly fill freed area with specified junk. */
+      if (MC_(clo_free_fill) != -1) {
+         tl_assert(MC_(clo_free_fill) >= 0x00 && MC_(clo_free_fill) <= 0xFF);
+         VG_(memset)((void*)(mc->data+new_szB), MC_(clo_free_fill), 
+                                                old_szB-new_szB);
+      }
 
    } else {
       /* new size is bigger */
+      Addr a_new; 
+      tl_assert(old_szB < new_szB);
       /* Get new memory */
-      Addr a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_szB);
+      a_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_szB);
 
       if (a_new) {
          /* First half kept and copied, second half new, red zones as normal */
@@ -376,9 +395,23 @@
          MC_(make_mem_undefined)( a_new+mc->szB, new_szB-mc->szB );
          MC_(make_mem_noaccess) ( a_new+new_szB, MC_MALLOC_REDZONE_SZB );
 
+         /* Possibly fill new area with specified junk */
+         if (MC_(clo_malloc_fill) != -1) {
+            tl_assert(MC_(clo_malloc_fill) >= 0x00
+                      && MC_(clo_malloc_fill) <= 0xFF);
+            VG_(memset)((void*)(a_new+old_szB), MC_(clo_malloc_fill), 
+                                                new_szB-old_szB);
+         }
+
          /* Copy from old to new */
          VG_(memcpy)((void*)a_new, p_old, mc->szB);
 
+         /* Possibly fill freed area with specified junk. */
+         if (MC_(clo_free_fill) != -1) {
+            tl_assert(MC_(clo_free_fill) >= 0x00 && MC_(clo_free_fill) <= 0xFF);
+            VG_(memset)((void*)p_old, MC_(clo_free_fill), old_szB);
+         }
+
          /* Free old memory */
          /* Nb: we have to allocate a new MC_Chunk for the new memory rather
             than recycling the old one, so that any erroneous accesses to the
diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am
index 98cb1cb..c336b9c 100644
--- a/memcheck/tests/Makefile.am
+++ b/memcheck/tests/Makefile.am
@@ -66,6 +66,8 @@
 	leakotron.vgtest leakotron.stdout.exp leakotron.stderr.exp \
 	long_namespace_xml.vgtest long_namespace_xml.stdout.exp \
 	long_namespace_xml.stderr.exp \
+	malloc_free_fill.vgtest malloc_free_fill.stdout.exp \
+	malloc_free_fill.stderr.exp \
 	malloc_usable.stderr.exp malloc_usable.vgtest \
 	malloc1.stderr.exp malloc1.vgtest \
 	malloc2.stderr.exp malloc2.vgtest \
@@ -157,6 +159,7 @@
 	fprw fwrite hello inits inline \
 	leak-0 leak-cycle leak-pool leak-tree leak-regroot leakotron \
 	long_namespace_xml \
+	malloc_free_fill \
 	malloc_usable malloc1 malloc2 malloc3 manuel1 manuel2 manuel3 \
 	match-overrun \
 	memalign_test memalign2 memcmptest mempool mmaptest \
diff --git a/memcheck/tests/malloc_free_fill.c b/memcheck/tests/malloc_free_fill.c
new file mode 100644
index 0000000..1e9cef6
--- /dev/null
+++ b/memcheck/tests/malloc_free_fill.c
@@ -0,0 +1,64 @@
+
+/* Test for correct functioning of the --malloc-fill and --free-fill
+   flags.  Needs --malloc-fill=0x55 and --free-fill=0x77. */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+
+int main ( void )
+{
+  int *r, *oldr, *a;
+
+
+  fprintf(stderr, "test simple malloc/free:\n");
+
+  a = malloc(10 * sizeof(int)); assert(a);
+  fprintf(stderr, "(should be malloc-filled)     a[4] = %x\n", a[4]);
+
+  free(a);
+  fprintf(stderr, "(should be free-filled)       a[5] = %x\n", a[5]);
+
+
+
+  fprintf(stderr, "test realloc-larger:\n");
+
+  r = malloc(30 * sizeof(int)); assert(r);
+  fprintf(stderr, "(should be malloc-filled)    r[25] = %x\n", r[25]);
+
+  /* Make larger */
+  oldr = r;
+  r = realloc(r, 40 * sizeof(int)); assert(r);
+
+  fprintf(stderr, "(should be free-filled)   oldr[26] = %x\n", oldr[26]);
+  fprintf(stderr, "(should be malloc-filled)    r[35] = %x\n", r[35]);
+
+  free(r);
+
+
+
+  fprintf(stderr, "test realloc-smaller:\n");
+
+  r = malloc(30 * sizeof(int)); assert(r);
+  fprintf(stderr, "(should be malloc-filled)    r[25] = %x\n", r[25]);
+
+  /* Make smaller */
+  oldr = r;
+  r = realloc(r, 20 * sizeof(int)); assert(r);
+
+  fprintf(stderr, "(should be free-filled)   oldr[26] = %x\n", oldr[26]);
+
+  free(r);
+
+
+
+  fprintf(stderr, "test calloc:\n");
+  a = calloc(100, sizeof(int)); assert(r);
+
+  fprintf(stderr, "(should be zero)             a[42] = %x\n", a[42]);
+
+  free(a);
+
+
+  return 0;
+}
diff --git a/memcheck/tests/malloc_free_fill.stderr.exp b/memcheck/tests/malloc_free_fill.stderr.exp
new file mode 100644
index 0000000..380a486
--- /dev/null
+++ b/memcheck/tests/malloc_free_fill.stderr.exp
@@ -0,0 +1,57 @@
+
+test simple malloc/free:
+Use of uninitialised value of size 8
+   at 0x........: _itoa_word (in /...libc...)
+   by 0x........: ...
+   by 0x........: ...
+   by 0x........: ...
+   by 0x........: ...
+   by 0x........: main (malloc_free_fill.c:17)
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: _itoa_word (in /...libc...)
+   by 0x........: ...
+   by 0x........: ...
+   by 0x........: ...
+   by 0x........: ...
+   by 0x........: main (malloc_free_fill.c:17)
+
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: vfprintf (in /...libc...)
+   by 0x........: ...
+   by 0x........: ...
+   by 0x........: ...
+   by 0x........: main (malloc_free_fill.c:17)
+(should be malloc-filled)     a[4] = 55555555
+
+Invalid read of size 4
+   at 0x........: main (malloc_free_fill.c:20)
+ Address 0x........ is 20 bytes inside a block of size 40 free'd
+   at 0x........: free (vg_replace_malloc.c:...)
+   by 0x........: main (malloc_free_fill.c:19)
+(should be free-filled)       a[5] = 77777777
+test realloc-larger:
+(should be malloc-filled)    r[25] = 55555555
+
+Invalid read of size 4
+   at 0x........: main (malloc_free_fill.c:33)
+ Address 0x........ is 104 bytes inside a block of size 120 free'd
+   at 0x........: realloc (vg_replace_malloc.c:...)
+   by 0x........: main (malloc_free_fill.c:31)
+(should be free-filled)   oldr[26] = 77777777
+(should be malloc-filled)    r[35] = 55555555
+test realloc-smaller:
+(should be malloc-filled)    r[25] = 55555555
+
+Invalid read of size 4
+   at 0x........: main (malloc_free_fill.c:49)
+ Address 0x........ is not stack'd, malloc'd or (recently) free'd
+(should be free-filled)   oldr[26] = 77777777
+test calloc:
+(should be zero)             a[42] = 0
+
+ERROR SUMMARY: 67 errors from 6 contexts (suppressed: 0 from 0)
+malloc/free: in use at exit: 0 bytes in 0 blocks.
+malloc/free: 6 allocs, 6 frees, 920 bytes allocated.
+For a detailed leak analysis,  rerun with: --leak-check=yes
+For counts of detected errors, rerun with: -v
diff --git a/memcheck/tests/malloc_free_fill.stdout.exp b/memcheck/tests/malloc_free_fill.stdout.exp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/memcheck/tests/malloc_free_fill.stdout.exp
diff --git a/memcheck/tests/malloc_free_fill.vgtest b/memcheck/tests/malloc_free_fill.vgtest
new file mode 100644
index 0000000..ba14381
--- /dev/null
+++ b/memcheck/tests/malloc_free_fill.vgtest
@@ -0,0 +1,2 @@
+prog: malloc_free_fill
+vgopts: --malloc-fill=0x55 --free-fill=0x77