C函数strcpy()中的溢出

C函数strcpy()中的溢出,c,C,我在Linux环境下用C语言编程 我不明白为什么在这段代码中没有出现分段错误: int main(){ char buffer[4]; char tmp="qqqqqqqqqqqqqqqqqqqqqqqq"; char *r; r=strcpy(buffer,tmp); return 0;} 我使用的变量tmp比buffer长,尽管如此,我可以正确地标记buffer变量,而不会出现任何错误 此外,我不知道在这种情况下为什么: int main(){ static char buff

我在Linux环境下用C语言编程 我不明白为什么在这段代码中没有出现分段错误:

int main(){
 char buffer[4];
 char tmp="qqqqqqqqqqqqqqqqqqqqqqqq";
 char *r;
 r=strcpy(buffer,tmp);
 return 0;}
我使用的变量tmp比buffer长,尽管如此,我可以正确地标记buffer变量,而不会出现任何错误

此外,我不知道在这种情况下为什么:

int main(){
 static char buffer[4];
 int i=0;
 while(i<5){
    (*(buffer+i)='a');
      i++;}
 return 0;}
intmain(){
静态字符缓冲区[4];
int i=0;

而(iC)可以让你射中自己的脚

您有责任确保接收缓冲区足够大,可以容纳传递到strcpy的源字符串的内容(不要忘记为nul终止符留出空间)。这就是为什么喜欢当前工作的人会使用
strncpy
,它允许您为要复制的字符数设置上限


当前程序的行为尚未定义。分段错误是一种可能的表现形式。

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

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),因为您的第一个示例甚至不应该编译,所以我将假设这是问题中的一个简单输入错误,并使用以下代码而不是第一个示例:

int main(){
 char buffer[4];
 char *tmp="qqqqqqqqqqqqqqqqqqqqqqqq"; // this should be a pointer
 char *r;
 r=strcpy(buffer,tmp);
 return 0;}
每个人关于未定义行为的评论都是正确的,因为它不一定会导致seg.fault,但我认为这些评论没有抓住你问题的重点。所以我将跳过你不应该这样编写代码的明显原因,并重点讨论它为什么会起作用(因为没有更好的词)当您使用
static
关键字时

静态
关键字在语义上改变了
缓冲区的生存时间
,但降低它也会改变
缓冲区
在内存中的存储位置

你可能听说过,如果没有听说过,你可能应该读一下,因为它们是C语言编程的关键概念,但是堆栈和堆用于动态内存,而静态内存则存储在程序的数据段和bss段中

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

没有
static
关键字,在您的第一个示例中,
缓冲区
被放在堆栈上,被函数局部变量以及其他信息包围,比如函数返回后代码中要执行的点。这对a.k.a.很重要,因为我怀疑您的缓冲区溢出调查是错误的受缓冲区溢出攻击的启发,我建议阅读它们的工作原理

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

但是,通过使缓冲区
静态
,您将其从堆栈中取出,并将其放在远离可执行代码且最有可能被其他数据包围的其他地方。当您溢出此缓冲区时,您正在破坏数据,而不是代码,因此,现在程序的行为完全取决于所包含的数据如果你的程序被破坏了,你的程序会因为它而做一些疯狂的事情。它可能表现得很奇怪,但不会崩溃,或者它可能会立即崩溃,这取决于所做的更改


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

UB并不意味着你会得到一个SegFault,请参见
char tmp=“qqqqqqqqqq”;
是错误的:您将
常量char*
分配给
char
,不要忽略编译器警告
,同时(无法保证如果溢出缓冲区,您会自动生成segfault;这就是缓冲区溢出攻击起作用的原因:-(.答案在平台上有所不同,但基本上是变量的类型(static或no,function local,malloc()ed)改变了它在内存中的填充位置,根据硬件辅助(例如NC)、堆栈破坏保护和其他因素,内存可能更松或更紧。简言之:不要指望内存保护来避免所有形式的愚蠢行为:-/第一个示例是否编译?调用
strcpy(缓冲区,tmp)
应产生编译器错误,因为
tmp
被声明为
char
而不是
char*
char buffer[4] = "";
int main ( void )
{
    const char *tmp = "some long string";
    char buffer[4] = "";
    strncpy(buffer, tmp, (sizeof buffer) - 1);
    return 0;
}
int main(){
 char buffer[4];
 char *tmp="qqqqqqqqqqqqqqqqqqqqqqqq"; // this should be a pointer
 char *r;
 r=strcpy(buffer,tmp);
 return 0;}