哪些C / C ++函数最常被错误使用,并可能导致缓冲区溢出?

时间:2020-03-06 15:04:42  来源:igfitidea点击:

我被要求维护一个充满内存泄漏的大型C ++代码库。闲逛时,我发现我们有很多导致缓冲区泄漏的缓冲区溢出(它是如何变坏的,我永远都不想知道)。

我已决定首先从危险功能开始消除缓冲区溢出。哪些C / C ++函数最常被错误使用并会导致缓冲区溢出?

对于用于帮助查找缓冲区溢出的编译器和/或者工具,我创建了另一个解决此问题的方法

解决方案

这是一些我发现很危险的功能:

  • gets()-它不检查变量的长度,如果输入大于变量的缓冲区,则可以覆盖内存。
  • scanf()-我很高兴Visual Studio告诉我该函数已弃用。这是一个简单的解决方法。
  • strcpy()-如果源的存储空间大于目标的存储空间,则目标之后的数据将被覆盖。

以下链接应该使我们全面了解C ++中的安全功能(后缀有'_s'的功能以修复溢出问题):http://msdn.microsoft.com/zh-cn/library/8ef0s5kh( VS.80).aspx

编辑:此链接包含已替换的特定功能:http://msdn.microsoft.com/zh-cn/library/wd3wzwts(VS.80).aspx

编辑:我应该提到这些是Microsoft方法,但是该链接对于识别被认为是危险信号的功能仍然有用。

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

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

在功能方面,sprintf会很高兴地走出缓冲区的尽头。可以用snprintf代替。

通常,任何不检查参数范围的函数。清单将是

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

我们应该使用大小受限制的版本,例如stncpy,strncat,fgets等。考虑以" \ 0"结尾的字符串。

同样,在C或者C ++中不对数组进行绑定检查。以下示例将导致错误。出错一次

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

编辑:@ MrValdez,@ Denton Gentry的复制答案

基本上,任何接受指针并写入它的对象,都无需检查其长度。这样的东西如strcpy(),sprintf()等。

Memcpy()是另一种危险的方法。

任何访问数组的循环都是一个危险点,因为无法停止超出数组末尾的情况。

内存泄漏是由分配内存而不释放内存引起的。构造函数和析构函数应该是另一个强项,后者可以确保释放所有分配的内存。

我们正在使用哪个版本的Visual Studio?在2008年启用了所有警告的情况下,我们提到的所有功能(以及更多功能)会警告我们已弃用它们。

也许我们可以检查所有警告是否都已打开,并让编译器为我们完成艰苦的工作?

附带说明一下,出色的编写安全代码在解释某些较旧功能的不同陷阱方面做得很好。

在我处理的代码库中,我遇到了相同的问题。我的建议:警惕任何看起来像str *()和mem *()的C函数。也要警惕任何需要一个没有长度的缓冲区指针的东西。既然我们似乎有机会使用C ++,那么在最糟糕的情况下,我会尝试使用C ++容器来处理矢量,字符串,映射等内容。这些使生活更加轻松。

此外,自动问题检测工具也很棒。如果可以使用valgrind,我会推荐它。另外,Rational Purify功能非常强大,尽管并不便宜。

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

查找批处理的最佳解决方案是Visual Studio 2005/8中的/ GS。它不会找到所有这些对象,但这是减少所需手工工作量的廉价方法。

瓦尔格隆德(Valgrind)是新好朋友。

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

C语言中的另一个陷阱是" strncpy()"函数。许多人没有意识到可以自由返回非null终止的字符串。