C函数溢出strcpy()

时间:2016-10-14 09:50:28

标签: c

我在Linux环境下用C语言编程 我很困惑,为什么在此代码中不会发生分段错误:

int main(){
 char buffer[4];
 char tmp="qqqqqqqqqqqqqqqqqqqqqqqq";
 char *r;
 r=strcpy(buffer,tmp);
 return 0;}

我使用变量tmp比缓冲区更长,尽管我可以正确地标记缓冲区变量而没有任何错误。

此外,我不会在这种情况下取​​消原因:

int main(){
 static char buffer[4];
 int i=0;
 while(i<5){
    (*(buffer+i)='a');
      i++;}
 return 0;}

只有在我没有声明缓冲区静态时才会发生分段错误。

提前谢谢。

3 个答案:

答案 0 :(得分:4)

在第一种情况下,buffer足以容纳4个字符,通常这意味着它可以容纳3个字符+ 1个字符。 strcpy不允许您防止溢出,而strncpy 确实。这是一个简单的写作问题:

const char *tmp = "your string"; // const char *, not char
char buffer[4];
strncpy(buffer, tmp, (sizeof buffer) - 1); // sizeof char array == number of characters buffer can store
buffer[3] = '\0';//add terminating nul char

在第二种情况下,最大的问题是您的while循环正在访问超出范围的索引(i<5表示最后一次迭代将具有i == 4)。数组是零索引的,因此buffer的最后一个有效索引是3.将循环更改为:

while(i<3) {
    buffer[i++] = 'a';
}
buffer[i] = '\0';

您可以通过正确初始化buffer来取消使用nul char:

char buffer[4] = "";

所以我可能会写这样的东西:

int main ( void )
{
    const char *tmp = "some long string";
    char buffer[4] = "";
    strncpy(buffer, tmp, (sizeof buffer) - 1);
    return 0;
}

答案 1 :(得分:2)

C让你有能力用脚射击自己。

确定接收缓冲区足够大,以确保传递给strcpy的源字符串的内容 (别忘了为nul-terminator留出空间)。这就是为什么喜欢当前职业的人会使用strncpy,这允许你对要复制的字符数设置上限。

目前,您的程序的行为是 undefined 。分段错误是一种可能的表现形式。

答案 2 :(得分:1)

由于您的第一个示例甚至不应该编译,我将假设它是问题中的一个简单拼写错误,并使用以下代码而不是您的第一个示例:

int main(){
 char buffer[4];
 char *tmp="qqqqqqqqqqqqqqqqqqqqqqqq"; // this should be a pointer
 char *r;
 r=strcpy(buffer,tmp);
 return 0;}

每个人对未定义行为的评论都是正确的,因为它不必导致seg。错误,但我认为这些评论缺少你的问题。因此,当您使用static关键字时,我将跳过显而易见的原因,您不应该编写这样的代码并关注为什么它似乎工作(缺少更好的词)。

static关键字在语义上会更改buffer的生命周期,但会降低它,也会更改内存中存储buffer的位置。

你可能听说过the heap and the stack,如果你没有,你应该阅读它们,因为它们是用C语言编程的关键概念,但是there are more memory regions than just those two in a C program。堆栈和堆用于动态内存,但静态内存存储在程序的数据 bss 段中。

在不同的内存区域中溢出缓冲区会对程序的行为产生不同的影响,这完全取决于缓冲区内存位置周围存储的内容。

在没有static关键字的情况下,在第一个示例中,buffer被放置在堆栈上,并被函数局部变量以及其他信息包围,例如代码中要执行的点函数返回后。 understand the stack frame a.k.a. Call stack非常重要。由于我怀疑您的缓冲区溢出调查受到缓冲区溢出攻击的启发,我建议您阅读this explanation有关它们如何工作的信息。

当你在堆栈上超出缓冲区时,一个可能的结果是你的程序试图从错误的点继续,这可能甚至不是可执行代码。如果你的程序试图执行非代码的操作,那么操作系统会介入并像欺骗丈夫/妻子一样转储你。但请注意,这只是一种可能性。

然而,通过创建缓冲区static,您将它从堆栈中取出并放在远离可执行代码的其他位置,并且很可能被其他数据包围。当你溢出这个缓冲区然后你破坏数据而不是代码所以现在你的程序的行为完全取决于什么数据已经被破坏,以及你的程序是否会因为它而做一些疯狂的事情。它可能只是表现得很奇怪,但不会崩溃,也可能会立即崩溃,具体取决于更改的内容。

从标准的角度来看,未定义的行为是未定义的。当您在C中使用未定义的行为时,您获得的行为取决于您的编译器和您的机器,它们可以做任何他们喜欢的事情。计算机本质上是确定性的,它们所做的一切都是由它们运行的​​代码定义的,因此如果你挖得足够深,就可以定义未定义的行为。但是避免它会更加安全,并且它会使你的代码在任何地方工作,而不仅仅是在那个模糊/过时的编译器版本的机器上......