Add free-is-write functionality (experimental, not enabled by default).

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11627
This commit is contained in:
Julian Seward 2011-03-11 21:06:59 +00:00
parent 4db10284cd
commit d77ef03457
9 changed files with 112 additions and 1 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

View File

@ -0,0 +1,2 @@
prog: free_is_write
vgopts: --free-is-write=yes