GCC如何检测堆栈缓冲区溢出

时间:2013-09-30 10:12:35

标签: c gcc buffer-overflow fortify-source

由于gcc中有一个选项-fstack-protector-strong来检测堆栈粉碎。但是,它无法始终检测到堆栈缓冲区溢出。对于第一个函数func,当我输入10个字符串更多字符串时,程序并不总是崩溃。我的问题是有一种方法可以检测堆栈缓冲区溢出。

void func()
{
    char array[10];
    gets(array);
}

void func2()
{
    char buffer[10];
    int n = sprintf(buffer, "%s", "abcdefghpapeas");
    printf("aaaa [%d], [%s]\n", n, buffer);
}

int main ()
{
   func();
   func2();
}

4 个答案:

答案 0 :(得分:8)

堆栈上的溢出要么难以检测,要么检测起来非常昂贵 - 选择了你的毒药。

简而言之,当你有这个时:

 char a,b;
 char *ptr=&a;
 ptr[1] = 0;

那么这在技术上是合法的:在堆栈上分配了属于该函数的空间。这非常危险。

因此,解决方案可能是在ab之间添加间隙,并用模式填充。但是,有些人实际上是按照上面的方式编写代码。所以你的编译器需要检测它。

或者,我们可以创建代码实际分配的所有字节的位图,然后检测所有代码以检查此映射。非常安全,非常慢,使您的内存使用量增加。从积极的方面来说,有一些工具可以帮助解决这个问题(比如Valgrind)。

看看我要去哪里?

结论:在C语言中,没有很好的方法可以自动检测许多内存问题,因为语言和API通常太过于草率。解决方案是将代码移动到辅助函数中,这些函数严格检查它们的参数,始终是正确的,并且具有良好的单元测试覆盖率。

如果您有选择,请始终使用snprintf()版本的功能。如果旧代码使用不安全版本,请更改它。

答案 1 :(得分:2)

您可以使用名为Valgrind的工具

http://valgrind.org/

答案 2 :(得分:1)

  

我的问题是有哪种方法可以检测堆栈缓冲区溢出...

void func()
{
    char array[10];
    gets(array);
}

void func2()
{
    char buffer[10];
    int n = sprintf(buffer, "%s", "abcdefghpapeas");
    printf("aaaa [%d], [%s]\n", n, buffer);
}

因为您使用的是GCC,所以可以使用FORTIFY_SOURCES。

FORTIFY_SOURCE使用"更安全"高风险函数的变体,如memcpystrcpygets。当编译器可以推导出目标缓冲区大小时,编译器会使用更安全的变体。如果副本超过目标缓冲区大小,则程序将调用abort()。如果编译器无法推断出目标缓冲区大小,那么"更安全"不使用变体。

要禁用FORTIFY_SOURCE进行测试,您应该使用-U_FORTIFY_SOURCE-D_FORTIFY_SOURCE=0编译该程序。


C标准更安全"功能通过ISO/IEC TR 24731-1, Bounds Checking Interfaces。在符合要求的平台上,您只需拨打gets_ssprintf_s即可。它们提供一致的行为(如始终确保字符串NULL已终止)和一致的返回值(如成功时为0或errno_t)。

不幸的是,gcc和glibc不符合C标准。 Ulrich Drepper(其中一个glibc维护者)称为边界检查接口 "horribly inefficient BSD crap" ,但它们从未添加过。希望将来会改变。

答案 3 :(得分:0)

首先不要使用gets.By现在几乎每个人都知道get可能出现的所有安全性和可靠性问题。但由于历史原因,它也包含在这里,因为它是糟糕编程的一个很好的例子。

让我们看一下代码的所有问题:

// Really bad code
char line[100];
gets(line);

因为gets不执行边界检查超过100个字符的字符串将覆盖内存。如果你很幸运,程序将崩溃或者它可能表现出奇怪的行为。

获取函数非常糟糕,GNU gcc链接器在使用它时会发出警告。

/tmp/ccI5WJ5m.o(.text+0x24): In function `main':
: warning: the `gets' function is dangerous and should not be used.

使用断言保护数组访问

C / C ++不进行绑定检查。

例如:

int data[10]

i = 20
data[20] = 100 //Memory Corruption

上述代码使用Assert函数

#include<assert.h>


int data[10];
i=20

assert((i >= 0) && (i < sizeof(data) / sizeof(data[0]))); // throws 

data[i] = 100

数组溢出是最常见的编程错误之一,并且尝试定位非常令人沮丧。这段代码并没有消除它们,但它确实会导致错误的代码提前中止,从而使问题更容易找到。

使用Snprintf(缓冲区,sizeof(缓冲区),“%s”,“abcdefghpapeas”)和valgrind,GDB等工具。

希望这可以帮助你..