diff options
author | sewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-03-11 21:06:59 +0000 |
---|---|---|
committer | sewardj <sewardj@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-03-11 21:06:59 +0000 |
commit | 622fe49b55cb60d6132bb100236f591de1515146 (patch) | |
tree | 2a47ad57c0dc35c4390201407121750c9e6d93af /helgrind | |
parent | 4a12aad7742a773025737cbff88cb5e361712889 (diff) |
Add free-is-write functionality (experimental, not enabled by default).
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11627 a5019735-40e9-0310-863c-91ae7b9d1cf9
Diffstat (limited to 'helgrind')
-rw-r--r-- | helgrind/docs/hg-manual.xml | 22 | ||||
-rw-r--r-- | helgrind/hg_basics.c | 2 | ||||
-rw-r--r-- | helgrind/hg_basics.h | 6 | ||||
-rw-r--r-- | helgrind/hg_main.c | 16 | ||||
-rw-r--r-- | helgrind/tests/Makefile.am | 3 | ||||
-rw-r--r-- | helgrind/tests/free_is_write.c | 42 | ||||
-rw-r--r-- | helgrind/tests/free_is_write.stderr.exp | 20 | ||||
-rw-r--r-- | helgrind/tests/free_is_write.stdout.exp | 0 | ||||
-rw-r--r-- | helgrind/tests/free_is_write.vgtest | 2 |
9 files changed, 112 insertions, 1 deletions
diff --git a/helgrind/docs/hg-manual.xml b/helgrind/docs/hg-manual.xml index bbb3f562..25c2a69d 100644 --- a/helgrind/docs/hg-manual.xml +++ b/helgrind/docs/hg-manual.xml @@ -955,6 +955,28 @@ unlock(mx) unlock(mx) <!-- start of xi:include in the manpage --> <variablelist id="hg.opts.list"> + <varlistentry id="opt.free-is-write" + xreflabel="--free-is-write"> + <term> + <option><![CDATA[--free-is-write=no|yes + [default: no] ]]></option> + </term> + <listitem> + <para>When enabled (not the default), Helgrind treats freeing of + heap memory as if the memory was written immediately before + the free. This exposes races where memory is referenced by + one thread, and freed by another, but there is no observable + synchronisation event to ensure that the reference happens + before the free. + </para> + <para>This functionality is new in Valgrind 3.7.0, and is + regarded as experimental. It is not enabled by default + because its interaction with custom memory allocators is not + well understood at present. User feedback is welcomed. + </para> + </listitem> + </varlistentry> + <varlistentry id="opt.track-lockorders" xreflabel="--track-lockorders"> <term> diff --git a/helgrind/hg_basics.c b/helgrind/hg_basics.c index 7cef39db..172b1dae 100644 --- a/helgrind/hg_basics.c +++ b/helgrind/hg_basics.c @@ -80,6 +80,8 @@ UWord HG_(clo_conflict_cache_size) = 1000000; Word HG_(clo_sanity_flags) = 0; +Bool HG_(clo_free_is_write) = False; + /*--------------------------------------------------------------------*/ /*--- end hg_basics.c ---*/ diff --git a/helgrind/hg_basics.h b/helgrind/hg_basics.h index fc34a39e..edf05e83 100644 --- a/helgrind/hg_basics.h +++ b/helgrind/hg_basics.h @@ -103,6 +103,12 @@ extern UWord HG_(clo_conflict_cache_size); SCE_{THREADS,LOCKS,BIGRANGE,ACCESS,LAOG}. */ extern Word HG_(clo_sanity_flags); +/* Treat heap frees as if the memory was written immediately prior to + the free. This shakes out races in which memory is referenced by + one thread, and freed by another, and there's no observable + synchronisation event to guarantee that the reference happens + before the free. */ +extern Bool HG_(clo_free_is_write); #endif /* ! __HG_BASICS_H */ diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c index a09e18c1..8baffcad 100644 --- a/helgrind/hg_main.c +++ b/helgrind/hg_main.c @@ -1705,9 +1705,20 @@ void evh__new_mem_heap ( Addr a, SizeT len, Bool is_inited ) { static void evh__die_mem_heap ( Addr a, SizeT len ) { + Thread* thr; if (SHOW_EVENTS >= 1) VG_(printf)("evh__die_mem_heap(%p, %lu)\n", (void*)a, len ); - shadow_mem_make_NoAccess( get_current_Thread(), a, len ); + thr = get_current_Thread(); + tl_assert(thr); + if (HG_(clo_free_is_write)) { + /* Treat frees as if the memory was written immediately prior to + the free. This shakes out more races, specifically, cases + where memory is referenced by one thread, and freed by + another, and there's no observable synchronisation event to + guarantee that the reference happens before the free. */ + shadow_mem_cwrite_range(thr, a, len); + } + shadow_mem_make_NoAccess( thr, a, len ); if (len >= SCE_BIGRANGE_T && (HG_(clo_sanity_flags) & SCE_BIGRANGE)) all__sanity_check("evh__pre_mem_read-post"); } @@ -4580,6 +4591,8 @@ static Bool hg_process_cmd_line_option ( Char* arg ) if (0) VG_(printf)("XXX sanity flags: 0x%lx\n", HG_(clo_sanity_flags)); } + else if VG_BOOL_CLO(arg, "--free-is-write", + HG_(clo_free_is_write)) {} else return VG_(replacement_malloc_process_cmd_line_option)(arg); @@ -4589,6 +4602,7 @@ static Bool hg_process_cmd_line_option ( Char* arg ) static void hg_print_usage ( void ) { VG_(printf)( +" --free-is-write=no|yes treat heap frees as writes [no]\n" " --track-lockorders=no|yes show lock ordering errors? [yes]\n" " --history-level=none|approx|full [full]\n" " full: show both stack traces for a data race (can be very slow)\n" diff --git a/helgrind/tests/Makefile.am b/helgrind/tests/Makefile.am index 9fd18c9d..f48293a7 100644 --- a/helgrind/tests/Makefile.am +++ b/helgrind/tests/Makefile.am @@ -12,6 +12,8 @@ EXTRA_DIST = \ annotate_smart_pointer.stderr.exp \ bar_bad.vgtest bar_bad.stdout.exp bar_bad.stderr.exp \ bar_trivial.vgtest bar_trivial.stdout.exp bar_trivial.stderr.exp \ + free_is_write.vgtest free_is_write.stdout.exp \ + free_is_write.stderr.exp \ hg01_all_ok.vgtest hg01_all_ok.stdout.exp hg01_all_ok.stderr.exp \ hg02_deadlock.vgtest hg02_deadlock.stdout.exp hg02_deadlock.stderr.exp \ hg03_inherit.vgtest hg03_inherit.stdout.exp hg03_inherit.stderr.exp \ @@ -79,6 +81,7 @@ EXTRA_DIST = \ # should be conditionally compiled like tc20_verifywrap is. check_PROGRAMS = \ annotate_hbefore \ + free_is_write \ hg01_all_ok \ hg02_deadlock \ hg03_inherit \ diff --git a/helgrind/tests/free_is_write.c b/helgrind/tests/free_is_write.c new file mode 100644 index 00000000..0dc53f8d --- /dev/null +++ b/helgrind/tests/free_is_write.c @@ -0,0 +1,42 @@ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <unistd.h> + +static char* s_mem; + +/* wait a second, so as to guarantee that the parent access + the malloc'd area, then free it. */ +static void* thread_func(void* arg) +{ + sleep(1); + free(s_mem); + return NULL; +} + +int main(int argc, char** argv) +{ + pthread_t tid; + int quiet; + + fprintf(stderr, "Start.\n"); + + quiet = argc > 1; + + s_mem = malloc(10); + if (0 && !quiet) + fprintf(stderr, "Pointer to allocated memory: %p\n", s_mem); + assert(s_mem); + pthread_create(&tid, NULL, thread_func, NULL); + + /* Write, which isn't coordinated with the free ==> a race + should be reported. */ + char c = s_mem[5]; + __asm__ __volatile__("" : : "r"((long)c) ); + + pthread_join(tid, NULL); + fprintf(stderr, "Done.\n"); + return 0; +} diff --git a/helgrind/tests/free_is_write.stderr.exp b/helgrind/tests/free_is_write.stderr.exp new file mode 100644 index 00000000..672915d7 --- /dev/null +++ b/helgrind/tests/free_is_write.stderr.exp @@ -0,0 +1,20 @@ + +Start. +Thread #x was created + ... + by 0x........: pthread_create@* (hg_intercepts.c:...) + by 0x........: main (free_is_write.c:32) + +Thread #x is the program's root thread + +Possible data race during write of size 1 at 0x........ by thread #x + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: thread_func (free_is_write.c:15) + by 0x........: mythread_wrapper (hg_intercepts.c:...) + ... + This conflicts with a previous read of size 1 by thread #x + at 0x........: main (free_is_write.c:36) + +Done. + +ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) diff --git a/helgrind/tests/free_is_write.stdout.exp b/helgrind/tests/free_is_write.stdout.exp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/helgrind/tests/free_is_write.stdout.exp diff --git a/helgrind/tests/free_is_write.vgtest b/helgrind/tests/free_is_write.vgtest new file mode 100644 index 00000000..5ba9d342 --- /dev/null +++ b/helgrind/tests/free_is_write.vgtest @@ -0,0 +1,2 @@ +prog: free_is_write +vgopts: --free-is-write=yes |