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
This commit is contained in:
Julian Seward 2007-11-30 21:41:40 +00:00
parent e70c4211ff
commit 9ad4d494fa
9 changed files with 191 additions and 4 deletions

View File

@ -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"=")) { \

View File

@ -282,6 +282,15 @@ extern Bool MC_(clo_workaround_gcc296_bugs);
* 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 ---*/

View File

@ -4373,6 +4373,8 @@ VgRes MC_(clo_leak_resolution) = Vg_LowRes;
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 @@ static Bool mc_process_cmd_line_options(Char* arg)
}
}
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 @@ static void mc_print_usage(void)
" --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)();
}

View File

@ -182,8 +182,8 @@ static Bool complain_about_silly_args2(SizeT n, SizeT sizeB)
/* 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 @@ void* MC_(new_block) ( ThreadId tid,
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 @@ void* MC_(calloc) ( ThreadId tid, SizeT nmemb, SizeT size1 )
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 @@ void* MC_(realloc) ( ThreadId tid, void* p_old, SizeT new_szB )
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 @@ void* MC_(realloc) ( ThreadId tid, void* p_old, SizeT new_szB )
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

View File

@ -66,6 +66,8 @@ EXTRA_DIST = $(noinst_SCRIPTS) \
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 @@ check_PROGRAMS = \
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 \

View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,2 @@
prog: malloc_free_fill
vgopts: --malloc-fill=0x55 --free-fill=0x77