https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77673

            Bug ID: 77673
           Summary: 4-byte load generated instead of 1-byte load, possibly
                    reading past the end of object
           Product: gcc
           Version: 5.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: laurynas.biveinis at gmail dot com
  Target Milestone: ---

For the following function

void mach_parse_compressed(unsigned char* ptr, unsigned long int* val)
{
    if (ptr[0] < 0xC0U) {
        *val = ptr[0] + ptr[1];
        return;
    }

  *val = ((unsigned long int)(ptr[0]) << 24)
      | ((unsigned long int)(ptr[1]) << 16)
      | ((unsigned long int)(ptr[2]) << 8)
      | ptr[3];
}

starting with GCC 5.1, with -O2 -fPIC, the following is generated on x86_64:

mach_parse_compressed(unsigned char*, unsigned long*):
        movl    (%rdi), %eax <--- this load is not safe before branching
        bswap   %eax
        movl    %eax, %edx
        movzbl  (%rdi), %eax
        cmpb    $-65, %al
        jbe     .L5
        movl    %edx, %eax
        movq    %rax, (%rsi)
        ret
.L5:
        movzbl  1(%rdi), %edx
        addl    %edx, %eax
        cltq
        movq    %rax, (%rsi)
        ret

"movl    (%rdi), %eax" loads all of ptr[0]..ptr[3] even though before we
compare ptr[0] with 0xC0U, we don't know whether we can assume that
ptr[1]..ptr[3] does not point past the end of an object.

GCC 5.1/5.2/5.3/5.4/6.1/6.2 are all affected. Versions 4.7, 4.8, 4.9 are not. I
have checked this using Ubuntu GCC build and http://gcc.godbolt.org/. If
necessary, I can build GCC from pristine .tar.gz and re-test.

Reply via email to