Hi, I added Julian to the CC who will probably correct any mistakes I make. Or even might know a simple way to make valgrind detect this idiom.
On Wed, Sep 07, 2016 at 04:21:29PM -0700, Paul Eggert wrote: > On 09/07/2016 04:52 AM, Mark Wielaard wrote: > > If valgrind believes the > > memory isn't in valid memory then it will complain about an invalid access. > > But if the memory is accessible but uninitialised then it will just track > > the undefinedness complain later if such a value is used. > > I think the former is what happened in Gnulib fts.c before Gnulib was fixed. BTW. Do gnulib and glibc syncronize? I had no idea gnulib also contained fts. I recently fixed some issue (adding LFS support) in glibc. > > valgrind also has --partial-loads-ok (which in newer versions defaults > > to =yes): > > > > Controls how Memcheck handles 32-, 64-, 128- and 256-bit naturally > > aligned loads from addresses for which some bytes are addressable > > and others are not. > > Although this helps in some cases, it does not suffice in general since the > problem can occur with 16-bit aligned loads. I think that is what happened > with fts.c. > > > Does anybody have an example program of the above issue compiled with > > gcc that produces false positives with valgrind? > > > > Sure, attached. On Fedora 24 x86-64 (GCC 6.1.1 20160621, valgrind 3.11.0), > when I compile with "gcc -O2 flexouch.c" and run with "valgrind ./a.out", > valgrind complains "Invalid read of size 2". This is because GCC compiles > "p->d[0] == 2 && p->d[1] == 3" into "cmpw $770, 8(%rax); sete %al", which > loads the uninitialized byte p->d[1] simultaneously with the initialized > byte p->d[0]. > > As mentioned previously, although flexouch.c does not conform to C11, this > is arguably a defect in C11. > > #include <stddef.h> > #include <stdlib.h> > > struct s { struct s *next; char d[]; }; > > int > main (void) > { > struct s *p = malloc (offsetof (struct s, d) + 1); > p->d[0] = 1; > return p->d[0] == 2 && p->d[1] == 3; > } OK, I can replicate that with the same GCC when using -O2. ==25520== Invalid read of size 2 ==25520== at 0x400442: main (in /home/mark/src/tests/flexouch) ==25520== Address 0x51fa048 is 8 bytes inside a block of size 9 alloc'd ==25520== at 0x4C2BBAD: malloc (vg_replace_malloc.c:299) ==25520== by 0x40043D: main (in /home/mark/src/tests/flexouch) Without optimization the p->d[1] == 3 is never executed. Dump of assembler code for function main: => 0x0000000000400430 <+0>: sub $0x8,%rsp 0x0000000000400434 <+4>: mov $0x9,%edi 0x0000000000400439 <+9>: callq 0x400410 <malloc@plt> 0x000000000040043e <+14>: movb $0x1,0x8(%rax) 0x0000000000400442 <+18>: cmpw $0x302,0x8(%rax) 0x0000000000400448 <+24>: sete %al 0x000000000040044b <+27>: add $0x8,%rsp 0x000000000040044f <+31>: movzbl %al,%eax 0x0000000000400452 <+34>: retq End of assembler dump. Note that valgrind will also get this "wrong" if you do allocate enough space, but don't initialize d[1]: ==25906== Syscall param exit_group(status) contains uninitialised byte(s) ==25906== at 0x4F00CC8: _Exit (in /usr/lib64/libc-2.23.so) ==25906== by 0x4E7119A: __run_exit_handlers (in /usr/lib64/libc-2.23.so) ==25906== by 0x4E71234: exit (in /usr/lib64/libc-2.23.so) ==25906== by 0x4E58737: (below main) (in /usr/lib64/libc-2.23.so) ==25906== Uninitialised value was created by a heap allocation ==25906== at 0x4C2BBAD: malloc (vg_replace_malloc.c:299) ==25906== by 0x40043D: main (in /home/mark/src/tests/flexouch) I don't think there is anything valgrind can do to detect that compw really only depends on d[0] if the result is false. Cheers, Mark