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