GCC 为局部变量保留了比需要更多的 space

GCC reserving more space than needed for local variables

我想知道缓冲区溢出是如何工作的,所以我正在研究各种简单的例子,涉及 C 和函数 gets() 和 puts()。其中一个程序的源代码如下:

#include<stdio.h>
GetInput()
{
    char buffer[8];

    gets(buffer);
    puts(buffer);
}

main();
{
    GetInput();

    exit 0;
}

我正在使用以下行进行编译:

gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack demo.c -mpreferred-stack-boundary=2 -g -o demo

GCC版本为4.4.3,32位系统,内核2.6.32

调用GetInput()时,main()的return地址压入de栈,然后存储之前的EBP记录,然后分配8个字节给local var 缓冲区,所以要覆盖RET地址,我应该输入12个字节和预期的RET地址。

但事实并非如此,当我将其加载到 GDB 并分解 GetInput() 时,它表示如下:

0x080483f4 <+0>:    push   %ebp
0x080483f5 <+1>:    mov    %esp,%ebp
0x080483f7 <+3>:    sub    [=13=]xc,%esp    <------- 
0x080483fa <+6>:    lea    -0x8(%ebp),%eax
0x080483fd <+9>:    mov    %eax,(%esp)
0x08048400 <+12>:   call   0x804830c <gets@plt>
0x08048405 <+17>:   lea    -0x8(%ebp),%eax
0x08048408 <+20>:   mov    %eax,(%esp)
0x0804840b <+23>:   call   0x804832c <puts@plt>
0x08048410 <+28>:   leave  
0x08048411 <+29>:   ret

我已经标记了保留 12 个字节而不是 8 个字节的行。

谁能帮我搞定这个?

我尝试在 https://gcc.godbolt.org/ 中使用不同版本的 GCC 编译您的代码。

gcc 4.4.7
and
gcc 4.8.2
GetInput():
        pushl   %ebp
        movl    %esp, %ebp
        subl    , %esp
        leal    -8(%ebp), %eax
        movl    %eax, (%esp) <---------
        call    gets
        leal    -8(%ebp), %eax
        movl    %eax, (%esp)
        call    puts
        leave
        ret

gcc 4.9.0
GetInput():
        pushl   %ebp
        movl    %esp, %ebp
        subl    , %esp
        leal    -8(%ebp), %eax
        pushl   %eax <---------------
        call    gets
        addl    , %esp
        leal    -8(%ebp), %eax
        pushl   %eax
        call    puts
        addl    , %esp
        leave
        ret

注意缓冲区的地址是如何传递给gets()的,在GCC 4.4.7和4.8.2中,编译器减去12个字节,地址直接写入栈顶。而对于 GCC 4.9.0,只减去了 8 个字节,还需要一个额外的 PUSH。所以,是的,看起来额外的 4 个字节用于缓冲区的地址。