哪些C/C++函数最容易被错误使用并导致缓冲区溢出?

18

我被要求维护一个充满内存泄漏的大型C++代码库。在研究时,我发现我们有很多导致内存泄漏的缓冲区溢出(我不想知道它是如何变得如此糟糕的)。

我已经决定首先消除缓冲区溢出,从危险函数开始。哪些C/C++函数最容易被不正确使用并可能导致缓冲区溢出?

对于编译器和/或用于帮助查找缓冲区溢出的工具,我创建了另一个问题来处理这个问题


我不明白这个问题的意思。只要小心处理,这里的任何函数都不会导致溢出。 - unexist
2
@unexist:如果正确使用,没有任何函数会出错。问题在于哪些函数最常被错误使用,从而可能导致缓冲区溢出。 - Martin York
你使用的编译器/平台是什么?有很多工具可以自动查找这种东西。 - twk
@unexist:完全错误。gets()无法“小心处理”。我不在乎你malloc()多少内存或声明多大的数组,我可以超过它并导致缓冲区溢出。所有其他未限定char*函数都存在类似问题。 - Zathrus
@Zathrus:所以malloc()总是导致溢出吗?我只是说问题的标题有误,而MrValdez已经修复了它。;) - unexist
显示剩余2条评论
11个回答

13

一般来说,任何不检查参数边界的函数都有风险。以下是其中一些

  • gets()
  • scanf()
  • strcpy()
  • strcat()

应该使用带有大小限制版本的函数,例如stncpy、strncat、fgets等。在给出大小限制时要小心,考虑字符串末尾的'\0'。

C或C++中也没有对数组进行边界检查。下面这个例子会导致错误。请参见off by one error

int foo[3];
foo[3] = WALKED_OFF_END_OF_ARRAY;

编辑:复制了@MrValdez和@Denton Gentry的答案。


2
我不同意:检查参数边界的函数仍然是危险的。如果你可以用strcpy越界,那么如果边界错误,你也可以用strncpy越界(例如,增加指针并忘记减少边界)。为了保护自己,请使用std::string或等效的C字符串库。 - Steve Jessop
1
一个更简短的总结可能是:“C函数存在溢出风险,而C++函数通常不会出现这种情况。” - Aaron
5
使用strnxxx版本时要注意,它们可能会生成一个未以空字符结尾的字符串。 - user3458

6

Valgrind是你的新朋友。

valgrind --tool=memcheck --leak-check=full ./a.out

(注:该命令用于检查内存泄漏和其他内存错误)

3

恐怕问题的开始就错了。它假设缓冲区溢出发生在其他函数中。根据我的经验,最常见的原因是operator ++,或者缺少operator !=。

找到一批这样的问题的最佳解决方案是在Visual Studio 2005/8中使用/GS。它不会找到所有问题,但这是一种减少手动工作量的廉价方法。


2

下面是一些我发现的危险函数:

  • gets() - 它不检查变量的长度,如果输入比变量缓冲区大,它可以覆盖内存。
  • scanf() - 我很高兴 Visual Studio 告诉我这个函数已被弃用。这很容易修复。
  • strcpy() - 如果源内存空间比目标内存空间大,目标之后的数据将被覆盖。

2

这些是非可移植的Windows特定函数。 - Alex B
是的,但它们识别出有问题的函数。 - Jordan Parmer

2

不幸的是,任何数组都可能导致缓冲区溢出:

uint32_t foo[3];
foo[3] = WALKED_OFF_END_OF_ARRAY;

就功能而言,sprintf 可能会超出缓冲区的末尾。可以用 snprintf 替代。


2

Memcpy()是另一个危险的函数。

任何访问数组的循环都是一个危险点,因为没有停止越过数组末端的机制。

内存泄漏是由于分配内存后未释放造成的。构造函数和析构函数应该是另一个强烈审核点,后者确保任何分配的内存都被释放。


memcpy并不是那么危险的,因为它接受一个长度参数,只要目标缓冲区与长度保持同步(这两者都应该在开发人员的控制下),源缓冲区是否受损就无关紧要。 - James Curran

2
你正在使用哪个版本的Visual Studio?在启用所有警告的情况下,2008年的所有函数(以及更多)都会警告你它们已被弃用。也许你可以检查一下是否已经打开了所有警告,并让编译器为你完成艰苦的工作?
另外,优秀的编写安全代码一书非常好地解释了一些旧函数的不同陷阱。

我已经开了另一个问题(https://dev59.com/SHVC5IYBdhLWcg3w1E_w),涉及帮助我查找可能导致溢出的函数的工具。 - MrValdez

2
我在我工作的代码库中遇到了类似的问题。我的建议是:对任何看起来像str*()和mem*()的C函数要保持警惕。同样,对于任何接受缓冲区指针但没有长度的函数也要保持警惕。由于你有机会使用C ++,在最严重的情况下,尝试使用C ++容器:vector、string、map等。这些将使你的生活更加轻松。
此外,自动问题检测工具非常好用。如果可以使用valgrind,我会推荐使用它。Rational Purify也非常强大,但不便宜。

1
在C语言中,另一个需要注意的问题是 "strncpy()" 函数。许多人没有意识到它可以返回一个没有以空字符结尾的字符串。

为什么strncpy是不安全的?此外,strtok也是线程不安全和有风险的。+1 - legends2k

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