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