缓冲区溢出 - 局部变量在堆栈上的顺序

Buffer overflow - order of local variables on stack

我对局部变量在堆栈上的排序方式感到很困惑。我知道,(在 Intel x86 上)局部变量在代码中从高地址存储到低地址。很明显,这段代码:

int i = 0;
char buffer[4];
strcpy(buffer, "aaaaaaaaaaaaaaa");
printf("%d", i);

产生这样的东西:

1633771873

i 变量被溢出的缓冲区覆盖。

但是,如果我交换前两行:

char buffer[4];
int i = 0;
strcpy(buffer, "aaaaaaaaaaaaaaa");
printf("%d", i);

输出完全相同。

怎么可能? i 的地址低于 buffer 的地址,因此缓冲区溢出应该会覆盖其他数据,但不会覆盖 i。还是我遗漏了什么?

局部变量的顺序没有规定,所以编译器通常可以随意分配它们。但另一方面,编译器会使用许多策略来减少您自愿尝试做的事情发生的可能性。

其中一个安全增强措施是分配一个始终远离其他标量变量的缓冲区,因为数组可以越界寻址并且更倾向于膨胀相邻变量。另一个技巧是在数组之后添加一些空陷阱 space 来为边界问题创建一种隔离。

无论如何你可以使用调试器查看程序集以确认变量定位。

如果您想查看编译器如何分配局部变量,请尝试使用 gcc -S 进行编译,这将输出汇编代码。在汇编代码中,您可以看到编译器如何选择对变量进行排序。

在编译器如何选择对局部变量进行排序时要记住的一件事是每个 char 只需要按 1 对齐(这意味着它可以从内存的任何字节开始),另一方面, int必须按 4 对齐(这意味着它只能从可被 4 整除的字节开始),因此根据对齐方式,编译器有自己的逻辑来避免数据为空字节,这意味着它经常将变量组合在一起具有一定顺序的相似类型。所以即使你这样定义它们:

int a;
char c;
int b;
char d;

很可能编译器将内存中的 int 和 char 组合在一起,因此从顶部的低内存到底部的高内存的内存可能类似于:

 low memory
  |    |      | char d | char c|
  |           int b            |
  |           int a            |
  high memory

每块||代表一个字节,一整行代表4个字节。

找个时间尝试一下汇编代码,这很有趣。