c语言 - 为什么这个字符串不会溢出缓冲区?



我在mac和linux上运行了这段代码:

#include <stdio.h>
#include <string.h>
int main (int argc, char *argv[]){
    int value = 5;
    char buffer_one[8], buffer_two[8];
    strcpy(buffer_one, "one");
    strcpy(buffer_two, "two");
    printf("[BEFORE] buffer_two is at %p and contains '%s'n", buffer_two, buffer_two);
    printf("[BEFORE] buffer_one is at %p and contains '%s'n", buffer_one, buffer_one);
    printf("[BEFORE] value is at %p and is %i (0x%08x)n", &value, value, value);
    printf("n[STRCPY] copying %i bytes into buffer twonn", strlen(argv[1]));
    strcpy(buffer_two, argv[1]); 
    printf("[AFTER] buffer_two is at %p and contains '%s'n", buffer_two, buffer_two);
    printf("[AFTER] buffer_one is at %p and contains '%s'n", buffer_one, buffer_one);
    printf("[AFTER] value is at %p and is %i (0x%08x)n", &value, value, value);
}

在mac上,如果我输入"1234567890"作为命令行参数,90溢出到缓冲区1中,正如我所期望的,因为8字节的缓冲区超过了2。

但是,如果我在Linux系统上运行它,则需要更多的字符才能溢出缓冲区。为什么/为什么我可以在Linux中免除缓冲区?

另请注意,在两个系统上,整个字符串仍将打印在缓冲区二中,而只有溢出的项将打印在缓冲器一中。为什么会发生这种情况?为什么其他角色不直接进入下一个?如果这个问题用词不当,这里有一个例子:

如果我在mac上输入1234567890,则1234567890将打印在缓冲区二中,90将打印到缓冲区一中。即使90已经溢出,它怎么还能放在缓冲区二里面呢。(这在linux上是相同的概念,但溢出需要超过10个字节)

第一个问题的答案是,内存中变量的对齐是由实现定义的。(请参阅C11草案中的第6.2.8节"对象对齐"。)基本上,不同的编译器可能要求内存中两个对象之间的最小字节数不同。您在Mac上使用的编译器将两个8字节的缓冲区紧挨着放在堆栈上,这可能是因为char[]的对齐是8字节或更少。您在Linux上使用的编译器在两个地址之间留下了更多的字节,可能是因为char[]的对齐方式是16个字节。

对于第二个问题,buffer_onebuffer_two只是程序可以访问的连续内存块中的两个地址。在这种情况下,由于堆栈的实现,buffer_two在内存中的地址低于buffer_one,因此写入buffer_two的数据溢出到buffer_one。从buffer_two打印"1234567890",从buffer_one打印"90"的原因是,printf()开始在您指定的地址读取字节,直到读取空终止符(0x00)。

因此,当您将strcpy()"1234567890"写入buffer_two时,实际上写入了11个字节,包括字符串末尾的空终止符(0x00)。在Mac上,buffer_twobuffer_one相距8个字节,因此当printf()buffer_two读取时,它会在看到空终止符之前读取10个字符,而空终止符恰好在buffer_one指向的地址之后。当printf()buffer_one读取时,它在看到空终止符之前读取2个字符。

它可以是"内部"的方式是因为在"内部"末尾没有真正的停止。你要求一个长度为8的char[],你得到了它,大概第二个就在它旁边(尽管编译器可以自由地重新排列这些东西,如果它愿意的话)。然后,将10个字符写入第一个缓冲区。当您打印缓冲区时,它不知道它应该只有8个字符,它知道它从哪里开始,一直到NUL字符。

所以它会打印整个字符串。它旁边的另一个缓冲区也知道它的内存从哪里开始,恰好是字符串中的9溢出到它的空间的地方。打印该字符串告诉它转到字符串的第一个内存位置并打印,直到它到达NUL,在本例中为90

所以你正在泛滥,只是在这个例子中不是以一种特别具有破坏性或危险的方式。如果在字符串中写入更多的数据,那么不仅可以开始覆盖相邻的字符串缓冲区,还可以开始覆盖堆栈上其他可能重要的东西。

最新更新