Memcheck does not realize that the entire tail of the page containing _end[], not just the .bss portion from Elf32_Phdr.p_filesz to .p_memsz, is initialized to 0 by the Linux kernel. See the calls to padzero() in linux/fs/binfmt_elf.c. Testcase (valgrind 3.0.0 from .bz2 source, Fedora Core 4, glibc-2.3.5, x86): -----end.c extern int _end[]; main() { write(9, _end, 0xfff & -(int)&_end); return 0; } -----gcc -g -static -o end end.c -----valgrind3 ./end 9>foo ==25024== Memcheck, a memory error detector. ==25024== Copyright (C) 2002-2005, and GNU GPL'd, by Julian Seward et al. ==25024== Using LibVEX rev 1313, a library for dynamic binary translation. ==25024== Copyright (C) 2004-2005, and GNU GPL'd, by OpenWorks LLP. ==25024== Using valgrind-3.0.0, a dynamic binary instrumentation framework. ==25024== Copyright (C) 2000-2005, and GNU GPL'd, by Julian Seward et al. ==25024== For more details, rerun with: -v ==25024== ==25024== Conditional jump or move depends on uninitialised value(s) ==25024== at 0x804F7F6: __register_atfork (in /home/jreiser/end) ==25024== by 0x804D22C: ptmalloc_init (in /home/jreiser/end) ==25024== by 0x804D809: malloc_hook_ini (in /home/jreiser/end) ==25024== by 0x804C09B: malloc (in /home/jreiser/end) ==25024== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==25024== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==25024== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==25024== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==25024== by 0x8048140: (within /home/jreiser/end) ==25024== ==25024== Conditional jump or move depends on uninitialised value(s) ==25024== at 0x804F86E: __register_atfork (in /home/jreiser/end) ==25024== by 0x804D22C: ptmalloc_init (in /home/jreiser/end) ==25024== by 0x804D809: malloc_hook_ini (in /home/jreiser/end) ==25024== by 0x804C09B: malloc (in /home/jreiser/end) ==25024== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==25024== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==25024== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==25024== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==25024== by 0x8048140: (within /home/jreiser/end) ==25024== ==25024== Conditional jump or move depends on uninitialised value(s) ==25024== at 0x804C0C7: malloc (in /home/jreiser/end) ==25024== by 0x804C09B: malloc (in /home/jreiser/end) ==25024== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==25024== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==25024== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==25024== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==25024== by 0x8048140: (within /home/jreiser/end) ==25024== ==25024== Conditional jump or move depends on uninitialised value(s) ==25024== at 0x804C0EC: malloc (in /home/jreiser/end) ==25024== by 0x804C09B: malloc (in /home/jreiser/end) ==25024== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==25024== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==25024== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==25024== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==25024== by 0x8048140: (within /home/jreiser/end) ==25024== ==25024== Conditional jump or move depends on uninitialised value(s) ==25024== at 0x804C0C7: malloc (in /home/jreiser/end) ==25024== by 0x8068692: _dl_init_paths (in /home/jreiser/end) ==25024== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==25024== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==25024== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==25024== by 0x8048140: (within /home/jreiser/end) ==25024== ==25024== Conditional jump or move depends on uninitialised value(s) ==25024== at 0x804C0EC: malloc (in /home/jreiser/end) ==25024== by 0x8068692: _dl_init_paths (in /home/jreiser/end) ==25024== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==25024== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==25024== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==25024== by 0x8048140: (within /home/jreiser/end) ==25024== ==25024== Conditional jump or move depends on uninitialised value(s) ==25024== at 0x8048C3C: __cxa_atexit (in /home/jreiser/end) ==25024== by 0x804839B: __libc_start_main (in /home/jreiser/end) ==25024== by 0x8048140: (within /home/jreiser/end) ==25024== ==25024== Conditional jump or move depends on uninitialised value(s) ==25024== at 0x8048C98: __cxa_atexit (in /home/jreiser/end) ==25024== by 0x804839B: __libc_start_main (in /home/jreiser/end) ==25024== by 0x8048140: (within /home/jreiser/end) ==25024== ==25024== Conditional jump or move depends on uninitialised value(s) ==25024== at 0x804EC08: write (in /home/jreiser/end) ==25024== by 0x804823F: main (end.c:5) ==25024== ==25024== Syscall param write(buf) points to uninitialised byte(s) ==25024== at 0x804EC1E: __write_nocancel (in /home/jreiser/end) ==25024== by 0x804823F: main (end.c:5) ==25024== Address 0x80B37B4 is not stack'd, malloc'd or (recently) free'd ==25024== ==25024== ERROR SUMMARY: 10 errors from 10 contexts (suppressed: 0 from 0) ==25024== malloc/free: in use at exit: 0 bytes in 0 blocks. ==25024== malloc/free: 0 allocs, 0 frees, 0 bytes allocated. ==25024== For counts of detected errors, rerun with: -v ==25024== No malloc'd blocks -- no leaks are possible. ----- The last error [Syscall param write(buf) points to uninitialised byte(s)] is the source of the problem. In reality the whole region specified to write() (the tail of the page containing _end[]) was cleared to 0 by the Linux kernel.
This bug impedes systemmatic use of memcheck by the developers of glibc. ld-linux.so.2 takes advantage of the memset(,0,) performed by the kernel. Because memcheck does not, then memcheck spews large numbers of false positive uninit complaints during the glibc automated testcases, which run ld-linux.so.2 explicitly.
> ------- This bug impedes systemmatic use of memcheck by the developers of > glibc. Noted. I guess that raises its importance somewhat.
Actually it turns out that valgrind does zero the memory in question by virtue of the fact that it uses mmap to aquire anonymous pages for the bss (and zeros any partial page at the start of the bss). See mapelf() in m_ume.c for the details. That mapped is segment is then declared as readable to memcheck in the normal way and memcheck considers it defined for a while. Then the client program starts executing and it fairly quickly calls brk(0) and the system call handler for brk marks any memory beyond the newly returned break limit as invalid.
So after brk(0) the memory at higher addresses in .data+.bss is logically invalid, and memcheck is acting accordingly. However, the linux kernel deals only in whole pages here, and will not "scribble" on the region between brk(0) and the high end of that page. So the kernel implementation is somewhat more generous than the logical model, preserving memory contents upto a page boundary. Could memcheck do the accounting that way, too? Bug 109744 has a similar issue with the last page of .data+.bss upon direct invocation of ld-linux.so.2. I'm almost certain that ld-linux does not call brk(0) in reference to itself [but may call brk(0) in reference to the a.elf of which ld-linux is the PT_INTERP], so there may be a related fix for bug 109744.
Created attachment 12857 [details] Treat the whole of the page containing the break point as valid I can't actually reproduce this problem in the current SVN code but this patch will keep the whole of the page containing the break point valid.
John: since the brk handling just got redone, could you first confirm that the problem still exists in the svn trunk?
I can reproduce the problem with VEX r1412, valgrind r4868 using the end.c and compile+run recipe given in the original report ( gcc -g -static -o end end.c; valgrind ./end 9>foo ) It seems strange to me that Tom cannot (Comment #5.)
I can reproduce it in 3.0.1 but not in the SVN code. The reason is that in SVN the heap no longer follows immediately after the bss segment so the break adjustments no long wind up marking data after _end as undefined.
Is there a fundamental reproducibility problem with the build+run environment? Using VEX 1412, valgrind 4868 on Fedora Core 4 kernel 2.6.13-1.1526_FC4 with gcc (GCC) 4.0.1 20050727 (Red Hat 4.0.1-5), glibc-2.3.5-10, binutils-2.15.94.0.2.2-2.1 [/usr/bin/as, /usr/bin/ld, etc.] I can reproduce the original problem. Configure was "./configure --prefix=/usr/local/valgrindtrunk" with a symlink so that the name /usr/local/bin/valgrindtrunk resolves to /usr/local/valgrindtrunk/bin/valgrind. I have several other valgrind versions installed in other /usr/local/valgrind* directories, and it seems to me that I get the correct versions at runtime based on the symlink /usr/local/bin/valgrind* --> /usr/local/valgrind*/bin/valgrind. The -v verbose output below says that "/valgrindtrunk/" was inserted at the appropriate times [such as, "Reading syms from /usr/local/valgrindtrunk/lib/valgrind/memcheck (0xB0000000)"], so it seems to me that versions are not being confused at runtime. ===== $ valgrindtrunk -v ./end 9>foo ==9708== Memcheck, a memory error detector. ==9708== Copyright (C) 2002-2005, and GNU GPL'd, by Julian Seward et al. ==9708== Using LibVEX rev 1412, a library for dynamic binary translation. ==9708== Copyright (C) 2004-2005, and GNU GPL'd, by OpenWorks LLP. ==9708== Using valgrind-3.1.SVN, a dynamic binary instrumentation framework. ==9708== Copyright (C) 2000-2005, and GNU GPL'd, by Julian Seward et al. ==9708== --9708-- Valgrind library directory: /usr/local/valgrindtrunk/lib/valgrind --9708-- Command line --9708-- ./end --9708-- Startup, with flags: --9708-- -v --9708-- Contents of /proc/version: --9708-- Linux version 2.6.13-1.1526_FC4 (bhcompile@hs20-bc1-6.build.redhat.com) (gcc version 4.0.1 20050727 (Red Hat 4.0.1-5)) #1 Wed Sep 28 19:15:10 EDT 2005 --9708-- Reading syms from /home/jreiser/end (0x8048000) --9708-- object doesn't have a dynamic symbol table --9708-- Reading syms from /usr/local/valgrindtrunk/lib/valgrind/memcheck (0xB0000000) --9708-- object doesn't have a dynamic symbol table --9708-- Reading suppressions file: /usr/local/valgrindtrunk/lib/valgrind/default.supp ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804F7F6: __register_atfork (in /home/jreiser/end) ==9708== by 0x804D22C: ptmalloc_init (in /home/jreiser/end) ==9708== by 0x804D809: malloc_hook_ini (in /home/jreiser/end) ==9708== by 0x804C09B: malloc (in /home/jreiser/end) ==9708== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804F86E: __register_atfork (in /home/jreiser/end) ==9708== by 0x804D22C: ptmalloc_init (in /home/jreiser/end) ==9708== by 0x804D809: malloc_hook_ini (in /home/jreiser/end) ==9708== by 0x804C09B: malloc (in /home/jreiser/end) ==9708== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804C0C7: malloc (in /home/jreiser/end) ==9708== by 0x804C09B: malloc (in /home/jreiser/end) ==9708== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804C0EC: malloc (in /home/jreiser/end) ==9708== by 0x804C09B: malloc (in /home/jreiser/end) ==9708== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804C0C7: malloc (in /home/jreiser/end) ==9708== by 0x8068692: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804C0EC: malloc (in /home/jreiser/end) ==9708== by 0x8068692: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x8048C3C: __cxa_atexit (in /home/jreiser/end) ==9708== by 0x804839B: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x8048C98: __cxa_atexit (in /home/jreiser/end) ==9708== by 0x804839B: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804EC08: write (in /home/jreiser/end) ==9708== by 0x804823F: main (in /home/jreiser/end) ==9708== ==9708== ERROR SUMMARY: 9 errors from 9 contexts (suppressed: 0 from 0) ==9708== ==9708== 1 errors in context 1 of 9: ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804EC08: write (in /home/jreiser/end) ==9708== by 0x804823F: main (in /home/jreiser/end) ==9708== ==9708== 1 errors in context 2 of 9: ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x8048C98: __cxa_atexit (in /home/jreiser/end) ==9708== by 0x804839B: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== 1 errors in context 3 of 9: ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x8048C3C: __cxa_atexit (in /home/jreiser/end) ==9708== by 0x804839B: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== 1 errors in context 4 of 9: ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804C0EC: malloc (in /home/jreiser/end) ==9708== by 0x8068692: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== 1 errors in context 5 of 9: ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804C0C7: malloc (in /home/jreiser/end) ==9708== by 0x8068692: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== 1 errors in context 6 of 9: ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804C0EC: malloc (in /home/jreiser/end) ==9708== by 0x804C09B: malloc (in /home/jreiser/end) ==9708== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== 1 errors in context 7 of 9: ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804C0C7: malloc (in /home/jreiser/end) ==9708== by 0x804C09B: malloc (in /home/jreiser/end) ==9708== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== 1 errors in context 8 of 9: ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804F86E: __register_atfork (in /home/jreiser/end) ==9708== by 0x804D22C: ptmalloc_init (in /home/jreiser/end) ==9708== by 0x804D809: malloc_hook_ini (in /home/jreiser/end) ==9708== by 0x804C09B: malloc (in /home/jreiser/end) ==9708== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== ==9708== 1 errors in context 9 of 9: ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804F7F6: __register_atfork (in /home/jreiser/end) ==9708== by 0x804D22C: ptmalloc_init (in /home/jreiser/end) ==9708== by 0x804D809: malloc_hook_ini (in /home/jreiser/end) ==9708== by 0x804C09B: malloc (in /home/jreiser/end) ==9708== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ==9708== by 0x805188A: _dl_non_dynamic_init (in /home/jreiser/end) ==9708== by 0x8051DBC: __libc_init_first (in /home/jreiser/end) ==9708== by 0x8048384: __libc_start_main (in /home/jreiser/end) ==9708== by 0x8048140: (within /home/jreiser/end) ==9708== IN SUMMARY: 9 errors from 9 contexts (suppressed: 0 from 0) ==9708== ==9708== malloc/free: in use at exit: 0 bytes in 0 blocks. ==9708== malloc/free: 0 allocs, 0 frees, 0 bytes allocated. ==9708== ==9708== No malloc'd blocks -- no leaks are possible. --9708-- memcheck: sanity checks: 0 cheap, 1 expensive --9708-- memcheck: auxmaps: 0 auxmap entries (0k, 0M) in use --9708-- memcheck: auxmaps: 0 searches, 0 comparisons --9708-- memcheck: secondaries: 5 issued (320k, 0M) --9708-- memcheck: secondaries: 7 accessible and distinguished (448k, 0M) --9708-- tt/tc: 650 tt lookups requiring 649 probes --9708-- tt/tc: 650 fast-cache updates, 2 flushes --9708-- translate: new 325 (6345 -> 104103; ratio 164:10) [0 scs] --9708-- translate: dumped 0 (0 -> ??) --9708-- translate: discarded 0 (0 -> ??) --9708-- scheduler: 1103 jumps (bb entries). --9708-- scheduler: 0/333 major/minor sched events. --9708-- sanity: 1 cheap, 1 expensive checks. --9708-- exectx: 30011 lists, 9 contexts (avg 0 per list) --9708-- exectx: 9 searches, 0 full compares (0 per 1000) --9708-- exectx: 0 cmp2, 36 cmp4, 0 cmpAll $ =====
The "Syscall param write(buf) points to uninitialised byte(s)" message that was the the thing complained about in the origianl report appears to be missing from that trace.
> ------- Additional Comments From tom compton nu 2005-10-05 17:34 ------- > I can reproduce it in 3.0.1 but not in the SVN code. The reason is that in > SVN the heap no longer follows immediately after the bss segment so the ^ always This is exactly right, but misses one essential word. John's and your observations are not in conflict: it's just that on his kernel V can do the traditional after-bss layout whereas for yours it has to put it (the data segment) elsewhere. That's what setup_client_dataseg() does. What this makes me think is that a proper fix will not involve the brk syscall handler. Instead, just before starting to run the first thread, ask m_debuginfo the address of _end, and paint the page appropriately.
The point is that the data segment is always at the start of a new page now - load_client rounds up to a page boundary when computing VG_(brk_base) and the heap allocation all goes through the address space manager and hence has to be page aligned. The result is that any partial page at the end of the bss will never be used for the data segment now.
Tom's Comment #10 is correct, the "Syscall param write(buf) points to uninitialised byte(s)" is no longer present. I did not check carefully enough. Are the other complaints explained by the "-static" linking? The first one is ===== ==9708== Conditional jump or move depends on uninitialised value(s) ==9708== at 0x804F7F6: __register_atfork (in /home/jreiser/end) ==9708== by 0x804D22C: ptmalloc_init (in /home/jreiser/end) ==9708== by 0x804D809: malloc_hook_ini (in /home/jreiser/end) ==9708== by 0x804C09B: malloc (in /home/jreiser/end) ==9708== by 0x806865D: _dl_init_paths (in /home/jreiser/end) ===== which looks peculiar because it contains things at deeper call levels than 'malloc'. This suggests that malloc() was not intercepted.
Of course malloc wasn't intercepted - it's a static build so there is nothing with an soame of libc.so.6 to do the intercepts in. Basically valgrind doesn't work very well on statically linked programs, at least with any tool that relies on intercepting the memory allocation calls.
So does that and the fact that the data segment is always at the start of a new page now mean this bug is now dead?
Fixed in VEX r1412 valgrind r4868 . See also bug #109744. "valgrind /lib/ld-linux.so.2 /bin/date" no longer complains about ld-linux assuming that the tail of the page containing _end[] is initialized.