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