6

我正在阅读有关缓冲区溢出的信息。我发现关于堆栈上局部变量的内存分配的一件奇怪的事情

int f1 ()
{
    char string1[12];
    char string2[4];
}

这里分配发生在堆栈上。

现在,在GCC string2 中分配了 4 个字节,但如果我声明的不是 2 的幂(最多 16 个),那么编译器会为其分配 16 个字节。这意味着如果我在 3,5,6,7,....,15 个字节中分配 string2,那么编译器会为其分配 16 个字节,但如果我分配 2 的幂,例如 1,2,4,8...然后它被分配完全相同的大小。如果我分配超过 16 个字节(不是 2 的幂),那么它分配 32 个字节(我猜最多 32 个字节)。

而在 Visual Studio 中,如果我分配 1 个字节,则分配 9 个字节,如果从 2-4 个字节分配,则分配 12 个字节,如果从 5-8 个字节分配,则由编译器分配 16 个字节。

有谁知道为什么会有这样的任务???

至少在 Visual Studio 中,如果存在缓冲区溢出,我会收到调试错误,但在 gcc 中没有任何反应。GCC 仅在发生太大溢出时才提供分段错误。

4

3 回答 3

13

堆栈帧大小受内存对齐选择的影响,对于 32 位代码通常是 4 的倍数,对于 64 位代码通常是 8 的倍数。

两种编译器都可以使用金丝雀进行堆栈帧损坏检查,这是堆栈顶部的一个额外的 32 位值,在函数入口处初始化并在函数出口处检查。如果金丝雀的值发生了变化,那么程序就会中止,因为堆栈帧很可能被破坏了,可能是恶意代码,它可以改变函数返回地址并让它返回到任意位置。一种非常流行的恶意软件注入向量。

MSVC 具有 /RTC 选项,在调试配置中默认启用。这会在每个局部变量之间添加这些金丝雀。因此它可以检测每个单独变量的缓冲区溢出问题。

这些金丝雀当然会占用额外的空间,从而影响堆栈帧的大小。

于 2013-09-10T11:18:39.150 回答
5

那是由于内存对齐。CPU 更容易访问请求数据大小的倍数的内存地址。
所以编译器用清晰的字节填充你的结构来对齐结构。例如:

struct foo
{
    char a
    int b , c;
    short d;
    bool e;
    double f;
};

理论上,该结构的大小是(假设大小bool为 1 字节、char1 字节、int4 字节、short2 字节、double8 字节)20 字节。但实际上,编译器会在结构中添加“孔”以对齐内存:

         0 | 1 | 2 | 3
-----------+---+---+---
0x0000 | a |   |   |
-----------+---+---+---
0x0004 | b | b | b | b
-----------+---+---+---
0x0008 | c | c | c | c
-----------+---+---+---
0x000C | d | d |   |
-----------+---+---+---
0x0010 | e |   |   |
-----------+---+---+---
0x0014 | f | f | f | f
-----------+---+---+---
0x0018 | f | f | f | f
-------+---+---+---+---
于 2013-09-10T11:06:43.867 回答
0

我记得这样做的原因是优化,也许是由于解决了大小问题。另一个像这样的例子是布尔类型,它通常消耗 8 位并且可能只有一个。这可能因不同的编译器而有很大差异。您可以在为什么 c++ 中的 char 和 bool 大小相同?

于 2013-09-10T11:13:24.867 回答