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

            Bug ID: 82926
           Summary: x86_64 inline assembly with push/pop produces buggy
                    code
           Product: gcc
           Version: unknown
               URL: https://bugs.freedesktop.org/show_bug.cgi?id=99066
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: inline-asm
          Assignee: unassigned at gcc dot gnu.org
          Reporter: EoD at xmw dot de
  Target Milestone: ---

Created attachment 42569
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=42569&action=edit
C++ program showing the issue

Currently, pulseaudio fails compiling with "-O0" due to a crash in its
get_cpuid() function (see URL above for further details).

I attached a simple C++ program to show the issue.


When compiling the program with "-O0" a segfault is caused
  g++ -mx32 -O0 get_cpuid_crash.cpp


When compiling the program with either "-O1" or "-m64", the program does not
segfault:
  g++ -mx32 -O1 get_cpuid_crash.cpp
  g++ -m64 -O0 get_cpuid_crash.cpp

Also using clang does not cause a segfault
  clang++ -mx32 -O0 get_cpuid_crash.cpp


I would also like to quote tanuk from
https://bugs.freedesktop.org/show_bug.cgi?id=99066#c14
> I was asked for more information about the cpuid crash, so here we go:
> 
> This is the code that GCC generates for get_cpuid() on x32:
> 
> 0xf7b6a270 <get_cpuid>          push   %rbp
> 0xf7b6a271 <get_cpuid+1>        mov    %esp,%ebp
> 0xf7b6a273 <get_cpuid+3>        mov    %edi,-0x4(%ebp)
> 0xf7b6a277 <get_cpuid+7>        mov    %rcx,%rax
> 0xf7b6a27a <get_cpuid+10>       mov    %r8,%rcx
> 0xf7b6a27d <get_cpuid+13>       mov    %esi,-0x8(%ebp)
> 0xf7b6a281 <get_cpuid+17>       mov    %edx,-0xc(%ebp)
> 0xf7b6a285 <get_cpuid+21>       mov    %eax,-0x10(%ebp)
> 0xf7b6a289 <get_cpuid+25>       mov    %ecx,-0x14(%ebp)
> 0xf7b6a28d <get_cpuid+29>       mov    -0x4(%ebp),%eax      [breakpoint]
> 0xf7b6a291 <get_cpuid+33>       push   %rbx
> 0xf7b6a292 <get_cpuid+34>       cpuid
> 0xf7b6a294 <get_cpuid+36>       mov    %ebx,%esi
> 0xf7b6a296 <get_cpuid+38>       pop    %rbx
> 0xf7b6a297 <get_cpuid+39>       mov    -0x8(%ebp),%edi
> 0xf7b6a29b <get_cpuid+43>       mov    %eax,(%edi)          [segfault]
> 0xf7b6a29e <get_cpuid+46>       mov    -0xc(%ebp),%eax
> 0xf7b6a2a2 <get_cpuid+50>       mov    %esi,(%eax)
> 0xf7b6a2a5 <get_cpuid+53>       mov    -0x10(%ebp),%eax
> 0xf7b6a2a9 <get_cpuid+57>       mov    %ecx,(%eax)
> 0xf7b6a2ac <get_cpuid+60>       mov    -0x14(%ebp),%eax
> 
> "[breakpoint]" marks the place where the execution stops if you set a
> breakpoint with "break get_cpuid". "[segfault]" marks the place where the
> crash happens.
> 
> Before the breakpoint there's the code that copies the function parameters
> to the stack as follows:
> 
> %edi is the "op" parameter. It's saved to -0x4(%ebp).
> %rcx is the "c" parameter. It's moved to %rax and from %rax to -0x10(%ebp).
> %r8 is the "d" parameter. It's moved to %rcx and from %rcx to -0x14(%ebp).
> %esi is the "a" parameter. It's saved to -0x8(%ebp).
> %edx is the "b" parameter. It's saved to -0xc(%ebp).
> 
> The stack pointer is not updated when the parameters are saved to the stack.
> Since the stack pointer points to the beginning of the frame, the push
> instruction overwrites 8 bytes from the beginning of the frame, overwriting
> the "op" and "a" parameters.
> 
> I think the push is done, because the %rbx register is special in that it
> must always have the same value when returning from a function as it had
> when the function started. The cpuid instruction modifies the %rbx register,
> so that's why we need to save and restore the %rbx register.
> 
> After the pop, this happens:
> 
> 0xf7b6a297 <get_cpuid+39>       mov    -0x8(%ebp),%edi
> 
> This reads the stack from the position where the "a" parameter was saved.
> The compiler seems to assume that it has the same value that was written
> there in the beginning of the function, but the push instruction has written
> some random garbage there.
> 
> 0xf7b6a29b <get_cpuid+43>       mov    %eax,(%edi)          [segfault]
> 
> This is supposed to save the return value (well, one part of the return
> value) of the cpuid instruction to the address stored in %edi, but we just
> wrote garbage to %edi, so we end up dereferencing using garbage as the
> pointer (in my tests the value in %edi was 1).

Reply via email to