为什么“堆栈溢出检测”不会在溢出后立即出现?

5
我了解“检测到堆栈破坏”的含义。在这里已经有很多关于它的问题了。但是我没有找到以下问题的答案。看一下C代码:
int main(int argc, char **args) {
   char puffer[5];
   strcpy(puffer, *++args);
   printf("%s\n",puffer);
   return EXIT_SUCCESS;
}

当我在Ubuntu 13.10中使用gcc 4.8.1进行编译时,./buffer 123456789会出现预期的错误stack smashing detected。但是为什么./buffer 12345678没有出现错误?我本以为每个长度超过5个字符的字符串都会引发错误。

你可能实际上需要使用-fmudflap或者Valgrind。 - Dietrich Epp
2个回答

5
理论上你是正确的,程序应该是这样运行的。当你的程序使用超过5个字节时,这可能会导致未定义的行为。但是出于各种性能原因,栈指针通常会对齐到特定边界。对齐因架构而异。因此,并不是每个大于5的输入都会出现这个问题。
下面显示了你程序的反汇编代码。请查看sub $0x20,%rsp指令,它为该函数在堆栈上分配了16个字节的内存。
(gdb) disassemble main
Dump of assembler code for function main(int, char**):
   0x00000000004008b0 <+0>: push   %rbp
   0x00000000004008b1 <+1>: mov    %rsp,%rbp
=> 0x00000000004008b4 <+4>: sub    $0x20,%rsp
   0x00000000004008b8 <+8>: mov    %edi,-0x14(%rbp)
   0x00000000004008bb <+11>:    mov    %rsi,-0x20(%rbp) 
  0x00000000004008bf <+15>: mov    %fs:0x28,%rax
   0x00000000004008c8 <+24>:    mov    %rax,-0x8(%rbp)
   0x00000000004008cc <+28>:    xor    %eax,%eax
   0x00000000004008ce <+30>:    addq   $0x8,-0x20(%rbp)
   0x00000000004008d3 <+35>:    mov    -0x20(%rbp),%rax
   0x00000000004008d7 <+39>:    mov    (%rax),%rdx
   0x00000000004008da <+42>:    lea    -0x10(%rbp),%rax
   0x00000000004008de <+46>:    mov    %rdx,%rsi
   0x00000000004008e1 <+49>:    mov    %rax,%rdi
   0x00000000004008e4 <+52>:    callq  0x400770 <strcpy@plt>
   0x00000000004008e9 <+57>:    lea    -0x10(%rbp),%rax
   0x00000000004008ed <+61>:    mov    %rax,%rdi
   0x00000000004008f0 <+64>:    callq  0x400710 <puts@plt>
   0x00000000004008f5 <+69>:    mov    $0x0,%eax
   0x00000000004008fa <+74>:    mov    -0x8(%rbp),%rcx
   0x00000000004008fe <+78>:    xor    %fs:0x28,%rcx
   0x0000000000400907 <+87>:    je     0x400918 <main(int, char**)+104>
   0x0000000000400909 <+89>:    jmp    0x400913 <main(int, char**)+99>
   0x000000000040090b <+91>:    mov    %rax,%rdi
   0x000000000040090e <+94>:    callq  0x400790 <_Unwind_Resume@plt>
   0x0000000000400913 <+99>:    callq  0x400760 <__stack_chk_fail@plt>
   0x0000000000400918 <+104>:   leaveq 
   0x0000000000400919 <+105>:   retq   

是的,SSE2指令需要16字节的堆栈对齐。 - Zan Lynx

2

编译器实现了不同的堆栈破坏保护措施,但通常为了防止堆栈溢出攻击,在保存的帧指针和返回地址之前放置了一个canary。canary的作用是保护保存的帧指针和返回地址不被覆盖。基本上,编译器在缓冲区结束和帧指针/返回地址之间插入了一些填充。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接