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 push %rbp
> 0xf7b6a271 mov%esp,%ebp
> 0xf7b6a273 mov%edi,-0x4(%ebp)
> 0xf7b6a277 mov%rcx,%rax
> 0xf7b6a27amov%r8,%rcx
> 0xf7b6a27dmov%esi,-0x8(%ebp)
> 0xf7b6a281mov%edx,-0xc(%ebp)
> 0xf7b6a285mov%eax,-0x10(%ebp)
> 0xf7b6a289mov%ecx,-0x14(%ebp)
> 0xf7b6a28dmov-0x4(%ebp),%eax [breakpoint]
> 0xf7b6a291push %rbx
> 0xf7b6a292cpuid
> 0xf7b6a294mov%ebx,%esi
> 0xf7b6a296pop%rbx
> 0xf7b6a297mov-0x8(%ebp),%edi
> 0xf7b6a29bmov%eax,(%edi) [segfault]
> 0xf7b6a29emov-0xc(%ebp),%eax
> 0xf7b6a2a2mov%esi,(%eax)
> 0xf7b6a2a5mov-0x10(%ebp),%eax
> 0xf7b6a2a9mov%ecx,(%eax)
> 0xf7b6a2acmov-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:
>
> 0xf7b6a297mov-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.
>
> 0xf7b6a29bmov%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).