10

由于 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

4 回答 4

8

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

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

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

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

所以解决方案可能是在和之间添加一个间隙ab用一个模式填充它。但是,好吧,有些人实际上编写了上面的代码。所以你的编译器需要检测到这一点。

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

看看我要去哪里?

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

snprintf()如果可以选择,请始终使用函数版本。如果旧代码使用不安全的版本,请更改它。

于 2013-09-30T11:49:11.893 回答
2

You can use a tool called Valgrind

http://valgrind.org/

于 2013-09-30T11:48:28.903 回答
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 使用高风险函数的“更安全”变体,memcpy例如strcpygets。当编译器可以推断目标缓冲区大小时,它会使用更安全的变体。如果副本超出目标缓冲区大小,则程序调用abort(). 如果编译器无法推断目标缓冲区大小,则不使用“更安全”的变体。

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


C 标准通过ISO/IEC TR 24731-1,边界检查接口具有“更安全”的功能。在符合标准的平台上,您可以简单地调用gets_sand sprintf_s。它们提供一致的行为(例如始终确保NULL终止字符串)和一致的返回值(例如成功时为 0 或errno_t)。

不幸的是,gcc 和 glibc 不符合 C 标准。Ulrich Drepper(glibc 维护者之一)称边界检查接口是“非常低效的 BSD 废话”,并且从未添加过它们。希望将来会有所改变。

于 2014-09-12T15:14:15.833 回答
0

首先不要使用gets。到目前为止,几乎每个人都知道可能发生的所有安全性和可靠性问题gets。但是由于历史原因,它也被包括在这里,因为它是糟糕编程的一个很好的例子。

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

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

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

gets功能非常糟糕,以至于 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(buffer, sizeof(buffer), "%s", "abcdefghpapeas")一些工具,如 valgrind 或 GDB。

希望这可以帮助你..

于 2014-09-12T15:55:37.487 回答