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

            Bug ID: 79235
           Summary: x86 - Can't read stack transferred parameters when
                    using one of the parameters in a nested function
           Product: gcc
           Version: 6.3.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c
          Assignee: unassigned at gcc dot gnu.org
          Reporter: oren.twaig at gmail dot com
  Target Milestone: ---

Created attachment 40582
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=40582&action=edit
example code + save-temp output + asm output

x86-64 standard calling convention supports passing more than six parameters
using the stack. The caller places the extra params on the stack which the
callee is expected to read from. As so, if we deference the first on-stack
param, we can get access to all the others.

Example (Full example + full asm are attached)::

function a(int a, int b, int c, int d, int e, int f, int on_stack)
{
    int* pvargs = &on_stack;
    int second=pvargs[1];

    printf("second on_stack param=%d\n", second);
}

So far, all good.

However, There is a weird bug. If we use one of the stack-transfered parameters
in a nested function (of the function with expects more than six parameters)
the assembler of the callee (which contains the nested function) is no longer
correct and doesn't read the extra parameters from the correct place in the
stack.

Example (Full example + full asm are attached):


function a(int a, int b, int c, int d, int e, int f, int on_stack)
{
    int* pvargs = &on_stack;
    int second=pvargs[1];

    no-inline _nested(void) {
        printf("on_stack=%d\n", on_stack);  ->> referncing on_stack will cause
the bug.

        printf("second on_stack param=%d\n", second); --> second will no longer
be printed correctly.
    }   

}


In my more-adanced attached code, I did just that. Below is what happens when I
pass three '0xdeadbee[012]' on-stack. When DO_BUG is defined, I add a reference
to the on-stack variable as described earlier.



 comp="/tmp/6.3.0-gcc-bin/bin/gcc"; $comp --version |head -n 1; echo ----
TESTING ----  &&echo && echo without: && $comp -o main -O3 a.c  b.c && ./main 
&& echo &&  echo "with:   -DDO_BUG" && $comp -o main -O3 a.c  b.c -DDO_BUG  &&
./main
gcc (GCC) 6.3.0


---- TESTING ----

without:
  0=0xdeadbee0 1=0xdeadbee1 2=0xdeadbee2

with:   -DDO_BUG
  0=0xdeadbee0 1=0x8857fd60 2=0x5560fba0   --->> BUG, what happend to '1' and
'2' ???



We can clearly see the diff in the ASM. The callee changed! Instead of the
'72','80' and '88' correct offsets, we get some weird 'random' like offsets of
'48', '80', and '64'. Which obviously result in the wrong print.

diff b_good.s b_bad.s  -u

 #APP
 # 14 "b.c" 1
        nop;nop;nop
 # 0 "" 2
 #NO_APP
-       movq    72(%rsp), %rax
-       movl    %eax, 28(%rsp)
-       movq    80(%rsp), %rax
-       movl    %eax, 24(%rsp)
-       movq    88(%rsp), %rax
-       movl    %eax, 20(%rsp)
+       movq    48(%rsp), %rax
+       movl    %eax, 44(%rsp)
+       leaq    80(%rsp), %rax
+       movq    %rax, 8(%rsp)
+       movl    8(%rsp), %eax
+       movl    %eax, 40(%rsp)
+       movq    64(%rsp), %rax
+       movl    %eax, 36(%rsp)
 #APP
 # 18 "b.c" 1
        nop;nop;nop
 # 0 "" 2
 #NO_APP

Reply via email to