由于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();
}
答案 0 :(得分:8)
堆栈上的溢出要么难以检测,要么检测起来非常昂贵 - 选择了你的毒药。
简而言之,当你有这个时:
char a,b;
char *ptr=&a;
ptr[1] = 0;
那么这在技术上是合法的:在堆栈上分配了属于该函数的空间。这非常危险。
因此,解决方案可能是在a
和b
之间添加间隙,并用模式填充。但是,有些人实际上是按照上面的方式编写代码。所以你的编译器需要检测它。
或者,我们可以创建代码实际分配的所有字节的位图,然后检测所有代码以检查此映射。非常安全,非常慢,使您的内存使用量增加。从积极的方面来说,有一些工具可以帮助解决这个问题(比如Valgrind)。
看看我要去哪里?
结论:在C语言中,没有很好的方法可以自动检测许多内存问题,因为语言和API通常太过于草率。解决方案是将代码移动到辅助函数中,这些函数严格检查它们的参数,始终是正确的,并且具有良好的单元测试覆盖率。
如果您有选择,请始终使用snprintf()
版本的功能。如果旧代码使用不安全版本,请更改它。
答案 1 :(得分:2)
您可以使用名为Valgrind的工具
答案 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使用"更安全"高风险函数的变体,如memcpy
,strcpy
和gets
。当编译器可以推导出目标缓冲区大小时,编译器会使用更安全的变体。如果副本超过目标缓冲区大小,则程序将调用abort()
。如果编译器无法推断出目标缓冲区大小,那么"更安全"不使用变体。
要禁用FORTIFY_SOURCE进行测试,您应该使用-U_FORTIFY_SOURCE
或-D_FORTIFY_SOURCE=0
编译该程序。
C标准更安全"功能通过ISO/IEC TR 24731-1, Bounds Checking Interfaces。在符合要求的平台上,您只需拨打gets_s
和sprintf_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等工具。
希望这可以帮助你..