我在mac和linux上运行了这段代码:
#include#include 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_two
溢出到buffer_one
.打印"1234567890" buffer_two
和"90" 的原因buffer_one
是,它printf()
开始读取您提供的地址的字节,直到它读取空终止符(0x00
).
所以,当你strcpy()
"1234567890"时buffer_two
,你实际上写了11个字节,包括0x00
字符串末尾的null-terminator().在Mac上,buffer_two
并且buffer_one
相隔8个字节,因此在printf()
读取时buffer_two
,它会在看到空终止符之前读取10个字符,这恰好在指向的地址之后buffer_one
.当printf()
从读取buffer_one
,它看到空终止之前读取2个字符.