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

            Bug ID: 85894
           Summary: PPC64LE alloca stack slot allocation allows memset to
                    destroy the stack
           Product: gcc
           Version: 6.3.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: rcardoso at linux dot vnet.ibm.com
  Target Milestone: ---

Created attachment 44172
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=44172&action=edit
testcase

I was working on a bug in setjmp/longjmp against libc on Power (PPC64LE)
https://sourceware.org/bugzilla/show_bug.cgi?id=21895 and I may find a problem
with alloca. For some reason alloca is not calculating the right offset when a
stack have a parameter saving area which allows a memset to destroy the caller
stack frame.

void                                                                            
foo ()                                                                          
{                                                                               
  int i = setjmp(jb);                                                           
  char *c = alloca(256);                                                        
  memset(c, 0, 256);                                                            
  lbar(i);                                                                      
  for(;;);                                                                      
}

So, here the caller stack frame created on <foo> function before calling
setjmp:

0x00003fffb7fb0900 <foo+12>:    std     r0,16(r1)
0x00003fffb7fb0904 <foo+16>:    std     r31,-8(r1)
0x00003fffb7fb0908 <foo+20>:    stdu    r1,-128(r1)
0x00003fffb7fb090c <foo+24>:    mr      r31,r1
0x00003fffb7fb0910 <foo+28>:    nop
0x00003fffb7fb0914 <foo+32>:    ld      r3,-32712(r2)
0x00003fffb7fb0918 <foo+36>:    bl      0x3fffb7fb06a0
<00000017.plt_call._setjmp@@GLIBC_2.17>

That's how the stack frame will looks like on Power:

|    Parameter area     | <= $r1+32
+-----------------------+
|  TOC (0x3fffb7fd7f00) | <= $r1+24
+-----------------------+
|         LR            | <= $r1+16
+-----------------------+
|       CR/rsv          |
+-----------------------+
|  foo(0x3ffffffff160)  | <= $r1(sp) 0x3ffffffff0e0 <foo> 
+-----------------------+
|    0x3ffffffff160     | <= $r1-8
+-----------------------+

The stack pointer ($r1) is at 0x3ffffffff0e0.

Now alloca is called to create a temporary space on the caller stackframe:

--------------------------------------------------------
   0x00003fffb7fb0924 <+48>:    stw     r9,96(r31)
   0x00003fffb7fb0928 <+52>:    ld      r9,0(r1)
B+>0x00003fffb7fb092c <+56>:    stdu    r9,-272(r1)
   0x00003fffb7fb0930 <+60>:    addi    r9,r1,96
--------------------------------------------------------
   0x00003fffb7fb0934 <+64>:    addi    r9,r9,15
   0x00003fffb7fb0938 <+68>:    rldicl  r9,r9,60,4
   0x00003fffb7fb093c <+72>:    rldicr  r9,r9,4,59
   0x00003fffb7fb0940 <+76>:    std     r9,104(r31)
   0x00003fffb7fb0944 <+80>:    li      r5,256
   0x00003fffb7fb0948 <+84>:    li      r4,0
   0x00003fffb7fb094c <+88>:    ld      r3,104(r31)
   0x00003fffb7fb0950 <+92>:    bl      0x3fffb7fb06d0
<00000017.plt_call.memset@@GLIBC_2.17>

Notice, that stdu r9,-272(r1) move the stack pointer to alloca(te) the space on
the stack. Now $r1 (stack pointer) is 0x3fffffffefd0 on my tests.

After addi r9,r1,96 the $r9 will be 0x3ffffffff030 which marks the begin of the
allocated area on the stack. The code calls memset after alloca to set those
256 bytes to zero, memset will set 16 bytes and sum 16 to the address
0x3ffffffff030 resulting in 0x3ffffffff040 and then sum this with 224 to get
the end of allocated region on the stack:

0x3fffb7e6db4c <__memset_ppc+204>       clrldi. r5,r5,59
0x3fffb7e6db50 <__memset_ppc+208>       add     r6,r6,r7

This will results on the value 0x3ffffffff120 and then it will start to set the
alloca(ted) area with zeros 16 by 16 bytes:

0x3fffb7e6db60 <__memset_ppc+224>       std     r4,-8(r6)     
0x3fffb7e6db64 <__memset_ppc+228>       std     r4,-16(r6)   
0x3fffb7e6db68 <__memset_ppc+232>       std     r4,-24(r6)  
0x3fffb7e6db6c <__memset_ppc+236>       stdu    r4,-32(r6)

After the 3th iteration he will execute the code above on address
0x3ffffffff0e0 which is the caller stack frame (previous sp) and will set the
32 bits after with zeros. The caller stack frame is gone and when longjmp
executes we get a Segmentation Fault at:

0x3fffb7fb0954 <foo+96>         ld      r2,24(r1)   

I don't think this is the right behavior. 

Notice this problem only happens when the frame needs a parameter saving area.
Change lbar() to lbar(void) the code runs fine. 

I guess the problem with alloca is at this line:

0x00003fffb7fb092c <+56>:       stdu    r9,-272(r1)

That offset is always -272 for a stack with or without parameters and I don't
think that's correct. I've made a small test here and sum this value with the
parameter offset (96):

0x00003fffb7fb092c <+56>:       stdu    r9,-368(r1)

And it works. The code attached is the same reported on BZ#21895

Reply via email to