GCC内存分配问题 - 缓冲区溢出攻击

15

gcc是否会智能地进行内存分配以防止缓冲区溢出攻击?

int function(char *str) {
    int a = 0;                 // See the
    char b[16] = "abcd";       // changes here

    if(!strcmp(b, str))
        a = 1;

    return a;
}

int function(char *str) {
    char b[16] = "abcd";       // See the
    int a = 0;                 // changes here

    if(!strcmp(b, str))
        a = 1;

    return a;
}

当我使用gdb调试它时,它总是先将内存分配给整数变量,然后是字符数组;无论变量声明的顺序如何。 例如,在上述两种情况下,编译器首先分配内存给a,然后再分配给b

(higher address)
  Memory
|        |
|        |
+--------+
|        |
|        |
|        |
|        |
+--------+ <----- b (16 bytes)
|        |
+--------+ <----- a (4 bytes)
|        |
(lower address)

所以,即使我们在str中提供了超过16个字符,也不会影响a的值。

有人能帮我吗?


1
“标准”允许为任何目的重新排序,编译器想要实现最常见的优化。这仍然是“智能”的,但不是为了防止“缓冲区溢出攻击”。而且它仍然是“未定义行为”(缓冲区溢出)。 - Kiril Kirov
3个回答

11

是的,如果使用-fstack-protector标志运行。

使用该标志时,GCC会添加栈金丝雀(Stack Canaries),将数组变量排序到堆栈帧的最高部分,使其更难溢出并破坏其他变量,并复制函数参数以与其他本地变量一起存储。

有关缓冲区溢出保护的更多信息,请参见维基百科页面ProPolice主页。


@Ravi:有些发行版默认启用它。尝试使用“-fno-stack-protector”编译并查看是否有所不同。 - Hasturkun
@MM:已添加。为了简洁起见,大部分内容被省略了。 - Hasturkun
1
你是对的。GCC默认启用它。如果我们使用-fno-stack-protector编译,它会按声明顺序顺序分配内存,而不是将数组变量分配到堆栈帧的最高部分。谢谢。 - Ravi

2
即使GCC具有防止缓冲区溢出的功能,但在这里还有许多其他考虑因素可能导致固定变量声明顺序。声明的位置并不重要,编译器将根据变量在运行时何时以及如何使用来做出分配决策。
最重要的是,编译器会希望在堆栈帧中以最佳对齐方式分配变量。这可能以完全不同的方式完成,具体取决于CPU和优化设置。为速度进行优化可能会给出完全不同的分配,相比之下,为内存消耗进行优化可能会产生不同的结果。而且很可能将一些变量放入CPU寄存器中,从而消除整个RAM分配需求。
所以回答您的问题:GCC根据编译器端口以各种方式分配变量。它是如何做到的,程序员不需要过度关注。可能有选项重新排列堆栈以保护免受缓冲区溢出攻击,但这只对某些类型的应用程序有意义。我们甚至不知道特定系统是否有任何输入。因此,使编译器默认启用此安全功能是没有意义的。

-1
gcc是否会智能地进行内存分配以防止缓冲区溢出攻击?
不会。如果没有边界检查,您无法防止攻击或缓冲区溢出,而这并非总是可能的。有时只能在事后检测到溢出。
最好的情况下,编译器可以在堆栈上的返回地址附近包含额外信息(所谓的canary value),并在从函数返回之前检查它是否完整且未被覆盖,以防止缓冲区溢出。

2
这是错误的,GCC可以并且确实重新排列变量以防止缓冲区溢出的影响。 - Hasturkun
@Hasturkun,你无法在不进行边界检查的情况下防止溢出。有时候你只能检测到它。 - Alexey Frunze
没错。但是你可以通过ProPolice等方式来减轻它们的影响。 - Hasturkun
@Hasturkun 预防不等于缓解。 - Alexey Frunze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接