[英]How come this string doesn't overflow the 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个字节)
第一个问题的答案是内存中变量的对齐方式是实现定义的。 (请参阅C11草案中的6.2.8节“对象的对齐”。)基本上,不同的编译器可能要求在内存中两个对象之间使用不同的最小字节数。 在Mac上使用的编译器将两个8字节缓冲区紧挨着放在堆栈中,这可能是因为char[]
的对齐方式是8个字节或更少。 您在Linux上使用的编译器在两个地址之间留出了更多字节,这可能是因为char[]
的对齐方式为16个字节。
对于第二个问题, buffer_one
和buffer_two
只是程序可以访问的连续内存块中的两个地址。 在这种情况下,由于堆栈的实现, buffer_two
出现在内存中比buffer_one
低的地址buffer_one
,因此写入buffer_two
数据溢出到buffer_one
。 从buffer_two
打印“ 1234567890”并从buffer_two
打印“ 90”的buffer_one
是, printf()
开始在您提供的地址读取字节,直到它读取空终止符( 0x00
)。
因此,当您将strcpy()
“ 1234567890”写入buffer_two
,您实际上写入了11个字节,包括字符串末尾的空终止符( 0x00
)。 在Mac上, buffer_two
和buffer_one
相隔8个字节,因此,当printf()
从buffer_two
读取时,它将读取10个字符,然后再看到空终止符,该终止符恰好在buffer_one
指向的地址之后。 当printf()
从buffer_one
读取时,它会读取2个字符,然后才能看到空终止符。
之所以可以在“内部”进行,是因为在“内部”末尾并没有真正的止损。 您要求一个长度为8的char[]
并得到它,大概第二个就在它的旁边(尽管编译器可以根据需要随意重新排列这些内容)。 然后,您将10个字符写入第一个缓冲区。 当您打印该缓冲区时,它根本不知道应该只有8个字符,它知道它从哪里开始直到到达NUL
字符。
这样它将打印整个字符串。 在它旁边的另一个缓冲区也知道其内存从哪里开始,恰好是字符串中的9
溢出到其空间的位置。 打印该命令会告诉它转到其字符串的第一个存储位置并打印直到到达NUL
,在这种情况下为90
。
因此,您正在溢出,只是对于本示例而言,不是特别破坏性或危险的方式。 如果您在字符串中写入了更多数据,则不仅可以覆盖相邻的字符串缓冲区,还可以覆盖堆栈中其他潜在的重要内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.