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

时间:2015-12-01 00:45:25

标签: c buffer

我在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 two\n\n", 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中执行缓冲区?

另外作为附注,在两个系统上,整个字符串仍然会在缓冲区2中打印,而只在缓冲区1中打印溢出的项目。为什么会这样?为什么剩下的角色不会只是去下一个呢?如果这个问题没有得到很好的表达,那就是一个例子:

如果我在我的Mac上输入1234567890,则1234567890将在缓冲区2中打印,而90将在缓冲区1中打印。即使它已经溢出,90怎么仍然适合缓冲区2。 (这与linux上的概念相同,但溢出需要10个以上的字节)

2 个答案:

答案 0 :(得分:6)

第一个问题的答案是内存中变量的对齐是实现定义的。 (参见C11 draft中的第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()开始读取您提供的地址的字节,直到它读取空终止符({{1} }})。

因此,当你0x00“1234567890”到strcpy()时,你实际上写了11个字节,包括字符串末尾的空终止符(buffer_two)。在Mac上,0x00buffer_two相隔8个字节,因此当buffer_oneprintf()读取时,它会在看到空终结符之前读取10个字符,这恰好是在buffer_two指向的地址之后。当buffer_oneprintf()读取时,它会在看到空终止符之前读取2个字符。

答案 1 :(得分:1)

它可以“内部”的方式是因为在“内部”结束时并没有真正停止。你要求char[]长度为8并且你得到它,可能是第二个就在它旁边(尽管编译器可以根据需要自由重新排列这些东西)。然后你将10个字符写入第一个缓冲区。当你打印那个缓冲区时,它不知道它应该只有8个字符,它知道它从哪里开始直到它到达NUL字符。

所以它将打印整个字符串。旁边的另一个缓冲区也知道它的内存开始的位置,这恰好是字符串中9溢出到其空间的位置。打印一个人告诉它转到其字符串的第一个内存位置并打印,直到它到达NUL,在这种情况下为90

所以你是满溢的,只是对于这个例子不是特别具有破坏性或危险性的方式。如果你在字符串中写了更多的数据,你可以开始覆盖不仅是相邻的字符串缓冲区,还有其他可能重要的东西。