https://bugs.kde.org/show_bug.cgi?id=382041

            Bug ID: 382041
           Summary: False uninitialized on bit packing when the compiler
                    chooses XOR to implement masking and shifting (x86_64)
           Product: valgrind
           Version: 3.14 SVN
          Platform: Other
                OS: other
            Status: UNCONFIRMED
          Severity: normal
          Priority: NOR
         Component: memcheck
          Assignee: jsew...@acm.org
          Reporter: broscutama...@gmail.com
  Target Milestone: ---

Created attachment 106457
  --> https://bugs.kde.org/attachment.cgi?id=106457&action=edit
Full output

While troubleshooting an image processing program, I've received a lot of
warnings regarding uninitialized values after running a custom bit packing
routine. Managed to narrow down and write a minimal example:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

void __attribute__((noinline))
set(uint16_t * x, uint16_t val, uint16_t mask)
{
    *x = (*x & ~mask) | ((val << 4) & mask);
}

int main()
{
    /* allocate 2 bytes of uninitialized memory */
    uint16_t * buf = malloc(2);
    /* write 0x2340 in 2 steps, using 2 bit masks and a shift*/
    /* this way, some compilers are "tempted" to use xor */
    /* exact mask value doesn't matter - it's complemented */
    set(buf, 0x1234, 0x003F);
    set(buf, 0x1234, ~0x003F);
    /* print the result => 0x2340 */
    printf("%x\n", *buf);
    free(buf);
    return 0;
}

Compiled with e.g.:
gcc bit.c -O2

Results:

gcc version 5.2.1 20151010 (Ubuntu 5.2.1-22ubuntu2) and
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4):

...
==22509== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
...
==22509== Use of uninitialised value of size 8
==22509==    at 0x4E81711: _itoa_word (_itoa.c:180)
==22509==    by 0x4E85A78: vfprintf (vfprintf.c:1641)
==22509==    by 0x4F4E955: __printf_chk (printf_chk.c:35)
==22509==    by 0x400545: main (in /home/alex/vg/a.out)
...
==22509== ERROR SUMMARY: 20 errors from 10 contexts (suppressed: 0 from 0)

objdump a.out -d -M intel | grep "<set>:" -A 8
0000000000400670 <set>:
  400670:       0f b7 07                movzx  eax,WORD PTR [rdi]
  400673:       c1 e6 04                shl    esi,0x4
  400676:       31 c6                   xor    esi,eax
  400678:       21 d6                   and    esi,edx
  40067a:       31 f0                   xor    eax,esi
  40067c:       66 89 07                mov    WORD PTR [rdi],ax
  40067f:       c3                      ret    

gcc version 4.8.5 (Ubuntu 4.8.5-1ubuntu1) 
==1105== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

0000000000400660 <set>:
  400660:       89 d0                   mov    eax,edx
  400662:       c1 e6 04                shl    esi,0x4
  400665:       f7 d0                   not    eax
  400667:       66 23 07                and    ax,WORD PTR [rdi]
  40066a:       21 d6                   and    esi,edx
  40066c:       09 f0                   or     eax,esi
  40066e:       66 89 07                mov    WORD PTR [rdi],ax
  400671:       c3                      ret    

Ubuntu clang version 3.6.2-1 (tags/RELEASE_362/final) (based on LLVM 3.6.2)
==1302== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

0000000000400590 <set>:
  400590:       89 d0                   mov    eax,edx
  400592:       f7 d0                   not    eax
  400594:       66 23 07                and    ax,WORD PTR [rdi]
  400597:       c1 e6 04                shl    esi,0x4
  40059a:       21 d6                   and    esi,edx
  40059c:       09 c6                   or     esi,eax
  40059e:       66 89 37                mov    WORD PTR [rdi],si
  4005a1:       c3                      ret    

My conclusion: the warning only appears when the compiler implements these
bitfield-like operations with XOR.




Another example, this time without shift. However, it only triggers the warning
on gcc 5.2.1, not on 5.4.0.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main()
{
    /* allocate 2 bytes of uninitialized memory */
    uint16_t * buf = malloc(2);

    /* write 0x1234 in 2 steps, using 2 bit masks */
    /* exact mask value doesn't matter */
    const uint16_t mask1 = 0x3F;
    *buf = (*buf & ~mask1) | (0x1234 & mask1);
    /* complement the mask and write the other "half" of the number */
    uint16_t mask2 = ~mask1;
    *buf = (*buf & ~mask2) | (0x1234 & mask2);
    /* print the result => 0x1234 */
    printf("%x\n", *buf);
    free(buf);
    return 0;
}

gcc bit.c -O2

This is clean, as the compiler optimizes it out (because the masks are
constant).

0000000000400470 <main>:
  400470:       48 83 ec 08             sub    rsp,0x8
  400474:       ba 34 12 00 00          mov    edx,0x1234
  400479:       be 24 06 40 00          mov    esi,0x400624
  40047e:       bf 01 00 00 00          mov    edi,0x1
  400483:       31 c0                   xor    eax,eax
  400485:       e8 d6 ff ff ff          call   400460 <__printf_chk@plt>

Remove one of the const's (say the second):

==25688== ERROR SUMMARY: 9 errors from 5 contexts (suppressed: 0 from 0)
gcc version 5.2.1 20151010 (Ubuntu 5.2.1-22ubuntu2) 
0000000000400500 <main>:
  400500:       53                      push   rbx
  400501:       bf 02 00 00 00          mov    edi,0x2
  400506:       e8 d5 ff ff ff          call   4004e0 <malloc@plt>
  40050b:       48 89 c3                mov    rbx,rax
  40050e:       0f b7 00                movzx  eax,WORD PTR [rax]
  400511:       be c4 06 40 00          mov    esi,0x4006c4
  400516:       bf 01 00 00 00          mov    edi,0x1
  40051b:       83 e0 c0                and    eax,0xffffffc0
  40051e:       89 c2                   mov    edx,eax
  400520:       80 f4 12                xor    ah,0x12
  400523:       83 ca 34                or     edx,0x34
  400526:       31 d0                   xor    eax,edx
  400528:       0f b7 d0                movzx  edx,ax
  40052b:       31 c0                   xor    eax,eax
  40052d:       e8 be ff ff ff          call   4004f0 <__printf_chk@plt>

On gcc 5.4, removing "const" doesn't change the disassembly (therefore it
doesn't trigger the warnings); the compiler apparently figures out the second
mask is constant.

Valgrind versions tested:
valgrind-3.11.0 (prepackaged with Ubuntu)
valgrind-3.13.0 (compiled from source)
valgrind-3.14.0.SVN rev 16458 (compiled from source)

Attached full output for both examples, using valgrind from SVN and gcc 5.2.1.

Thanks for reading.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to