为什么它不会违反分段?

时间:2010-03-30 03:12:29

标签: c memory stack segmentation-fault

以下代码据说会违反分段:

#include <stdio.h> 
#include <string.h> 

void function(char *str) {
   char buffer[16];

   strcpy(buffer,str);
}

int main() {
  char large_string[256];
  int i;

  for( i = 0; i < 255; i++)
    large_string[i] = 'A';

  function(large_string);
  return 1;
}

它的编译和运行如下:

gcc -Wall -Wextra hw.cpp && a.exe

但没有任何输出。

注意

如果您真的了解了下面的内容,上面的代码确实会覆盖转发地址等等。

ret 地址将0x41414141具体。

重要 这需要深刻的堆栈知识

10 个答案:

答案 0 :(得分:6)

你很幸运。代码没有理由产生分段错误(或任何其他类型的错误)。不过,这仍然是一个坏主意。你可以通过增加large_string的大小来使它失败。

答案 1 :(得分:3)

可能在您的实施中,buffer紧靠堆栈上的large_string。因此,当对strcpy的调用溢出buffer时,它只会写入large_string的大部分内容,而不会造成任何特定的损害。它将写入至少255个字节,但它是否写入更多取决于large_string以上(以及large_string的最后一个字节的未初始化值)。它似乎在做任何伤害或分裂之前已经停止。

通过侥幸,function的呼叫的返回地址没有被删除。要么它在堆栈下面buffer,要么在寄存器中,或者函数内联,我不记得没有优化。如果你不能打扰检查拆卸,我也不能;-)。所以你回来并没有问题退出。

谁说代码会产生段错误可能并不可靠。它导致未定义的行为。在这种情况下,行为是输出任何内容并退出。

[编辑:我检查了我的编译器(在cygwin上的GCC),对于这段代码,它使用标准的x86调用约定和进入/退出代码。它确实是段错误。]

答案 2 :(得分:2)

你正在通过调用gcc(而不是g ++)来编译.cpp(c ++)程序...不确定这是否是原因,但是在Linux系统上(由于默认情况,它似乎是你在Windows上运行的)。 exe输出)它在尝试编译时抛出以下错误,如你所说:

/tmp/ccSZCCBR.o :(。eh_frame + 0x12):未定义对`__gxx_personality_v0'的引用 collect2:ld返回1退出状态

答案 3 :(得分:1)

其UB(未定义的行为)。 Strcpy可能已将更多字节复制到缓冲区指向的内存中,并且此时可能不会导致问题。

答案 4 :(得分:1)

这是未定义的行为,这意味着任何事情都可能发生。该程序甚至可以正常工作。

看起来你恰好碰巧没有覆盖其余(短)程序仍然需要的内存的任何部分(或者超出程序地址空间/写保护/ ...),所以没什么特别的发生。至少没有什么可以导致任何输出。

答案 5 :(得分:1)

堆栈上有一个零字节可以阻止strcpy()并且堆栈上有足够的空间来阻止受保护的页面。尝试在该功能中打印strlen(buffer)。在任何情况下,结果都是未定义的行为

养成使用strlcpy(3)系列函数的习惯。

答案 6 :(得分:1)

您可以通过其他方式对此进行测试:

#include <stdlib.h>
int main() {
    int *a=(int *)malloc(10*sizeof(int));
    int i;
    for (i=0;i<1000000; i++) a[i] = i;
    return 0;
}

在我的机器中,这导致SIGSEGV仅在i = 37000左右! (通过用gdb检查核心进行测试)。

为了防范这些问题,请使用malloc调试器测试您的程序......并使用大量的malloc,因为我知道没有可以查看静态内存的内存调试库。示例:Electric Fence

gcc -g -Wall docore.c -o c -lefence

现在,i=10会尽快触发SIGSEGV,正如预期的那样。

答案 7 :(得分:1)

正如大家所说,你的程序有不确定的行为。事实上,你的程序有比你想象的更多的错误,但是在它已经未定义之后它没有进一步的未定义。

这是我猜测为什么没有输出。您没有完全禁用优化。编译器发现function()中的代码对程序的其余部分没有任何已定义的影响。编译器优化了对function()的调用。

答案 8 :(得分:1)

可能的是,长字符串实际上是由i中的零字节终止的。假设main中的变量按照它们被声明的顺序排列 - 这是我所知道的语言规范中的任何内容都不需要但在实践中似乎很可能 - 那么large_string将首先在内存中,然后是i 。循环将i设置为0并计数到255.无论是存储大端还是小端,无论哪种方式都有一个零字节。所以在遍历large_string时,无论是字节256还是257,你都会遇到一个空字节。

除此之外,我还必须研究生成的代码,以弄清楚为什么不会这样做。正如你似乎指出的那样,我希望复制到缓冲区会覆盖strcpy的返回地址,所以当它试图返回时你会进入深空,某些地方会很快爆炸。

但正如其他人所说,“未定义”意味着“不可预测”。

答案 9 :(得分:0)

你的'char buffer [16]'中可能有任何内容,包括\ 0。 strcpy复制,直到找到第一个\ 0 - 因此不会超过你的16个字符的边界。