From: Julian Seward Date: Fri, 30 Nov 2007 21:41:40 +0000 (+0000) Subject: New options for Memcheck, --malloc-fill= and X-Git-Tag: svn/VALGRIND_3_3_0~33 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9ad4d494fad5635ae10a9b1f0aa0a82f37b22dbb;p=thirdparty%2Fvalgrind.git New options for Memcheck, --malloc-fill= and --fill-free=, 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 --- diff --git a/include/pub_tool_options.h b/include/pub_tool_options.h index b2701e3fa8..ed5c37ea4a 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 c731271f18..c48e48142f 100644 --- a/memcheck/mc_include.h +++ b/memcheck/mc_include.h @@ -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 ---*/ diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c index b24039c9b0..b7e7006147 100644 --- a/memcheck/mc_main.c +++ b/memcheck/mc_main.c @@ -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= 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= fill malloc'd areas with given value\n" +" --free-fill= 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 4fd9aec93a..adeddedcf5 100644 --- a/memcheck/mc_malloc_wrappers.c +++ b/memcheck/mc_malloc_wrappers.c @@ -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 diff --git a/memcheck/tests/Makefile.am b/memcheck/tests/Makefile.am index 98cb1cb1a3..c336b9c53a 100644 --- a/memcheck/tests/Makefile.am +++ b/memcheck/tests/Makefile.am @@ -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 \ diff --git a/memcheck/tests/malloc_free_fill.c b/memcheck/tests/malloc_free_fill.c new file mode 100644 index 0000000000..1e9cef660c --- /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 +#include +#include + +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 0000000000..380a4869b7 --- /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 0000000000..e69de29bb2 diff --git a/memcheck/tests/malloc_free_fill.vgtest b/memcheck/tests/malloc_free_fill.vgtest new file mode 100644 index 0000000000..ba14381853 --- /dev/null +++ b/memcheck/tests/malloc_free_fill.vgtest @@ -0,0 +1,2 @@ +prog: malloc_free_fill +vgopts: --malloc-fill=0x55 --free-fill=0x77