【问题标题】:how to check consequence of using free() with new []如何检查将 free() 与 new [] 一起使用的后果
【发布时间】:2011-11-30 16:03:44
【问题描述】:

一个进程因 SIGSEV 而崩溃,并且回溯表明内存损坏。 Valgrind 报告表明存在一些“不匹配的空闲空间”,即使用 new[] 分配的内存正在使用 free() 释放。

我找到了以下information from cert.org

另一个潜在的问题是,当将指针传递给使用 operator new 分配给 operator delete() 的数组时,可能会在 operator delete() 调用 free() 时(这在许多实现中很常见,尽管不是C++ 标准要求),传递给 free() 的指针不是调用 malloc() 返回的指针。这是因为 malloc() 在 operator new 调用时返回的前四个字节通常用于存储数组的大小(同样,标准未指定,但常见),因此 operator new 返回的指针实际上指向到距离 malloc() 返回的指针四个字节的地址。因此,free() 将读取错误的大小以进行释放,这可能会导致堆内存损坏问题。

问题是,我如何证明 free() 释放了错误的大小?任何人都可以建议如何验证这是否发生在我的环境中,也许使用 gdb 或任何其他工具?

此外,该数组是基本类型 char,因此不存在不调用单独的析构函数的问题。

【问题讨论】:

  • 没什么意义,你已经知道程序在 UB 上崩溃了。无需证明。
  • 不要试图证明 free() 正在释放错误的大小。如果您使用new[] 进行分配并使用delete[] 以外的其他内容进行分配,则会导致未定义的行为。事后不要试图分析你的程序。在操作中捕获错误的释放(您可能可以使用 Valgrind)并修复它。
  • valgrind 绝对是非常准确方便地指出此类问题的首选工具之一。
  • 我不确定如何验证它的实际作用,但你能确认你是在调用delete[] 而不是delete吗?
  • 你是为了学术兴趣还是在实际的实际程序中尝试这个?如果是后者,这没有什么意义,因为一旦您在分配有new 的任何东西上使用free,它就是一个保证的未定义行为,标准不保证在这种未定义行为场景中实现应该做什么。

标签: c++ memory-management


【解决方案1】:

问题是,我如何证明 free() 释放了错误的大小?

谁能建议如何验证这是否发生在我的环境中,可能使用 gdb 或任何其他工具?

您已经运行了一个工具,它告诉您这就是正在发生的事情。你为什么要运行另一个工具来验证“这确实是正在发生的事情”?

根据 Valgrind 的说法,您与一些 free 调用不匹配。这就是问题,这就是你需要解决的问题。

只要存在该问题,它可能会产生一百万种不同的后果,其中一个就是您引用的文本中所描述的。但是您不需要知道在您的程序中实际发生的百万后果中哪些,因为这些只是症状,而且你已经知道原因是什么 .

当您不匹配free/delete 时,唯一可以保证的效果是“某事发生”。那“东西”是什么?没有人可以说。

除非您有意尝试编写糟糕的软件,否则您应该从源头上解决问题,而您所要求的对您没有帮助。

【讨论】:

    【解决方案2】:

    这是实现定义的。尽管您可能能够使用特定的编译器/工具链/libc,但您不能笼统地“证明”这一点。

    我建议您使用 valgrind 来发现此类错误。

    可以在 SO 上找到更全面的此类工具列表

    编辑这是一个推荐的帖子,其中包含这样的列表:Problem with memory leak check tools in Linux

    【讨论】:

      【解决方案3】:

      您可以尝试使用 Duma (http://duma.sourceforge.net/)。它检测 malloc/free new/delete 不一致。它还会在调用 free/delete 后检查双重释放和修改的内存

      【讨论】:

        【解决方案4】:

        free() 的调用不会释放错误的大小,要么你传入的指针是用malloc()(或其任何变体)分配的,要么不是。在第一种情况下,它会释放与最初分配一样多的资源,而在第二种情况下,它可能会崩溃。

        根据实现的不同,SIGSEGV 附带的错误消息可能会告诉您传入的指针无效(或未分配或类似情况)。

        【讨论】:

          【解决方案5】:

          如果这是向他人解释和“演示”的情况,我建议您解释为什么不演示就很危险。在这种情况下,解释应该足以清楚地向每个人展示陷阱。如果您坚持演示,您可以使用malloc 创建一个缓冲区,使用new [] 创建一个缓冲区,并显示内存位置的内容,这些内存位置包含malloc 使用的分配大小和new 使用的内容。只是观察差异。但是,正如sehe解释的那样,行为是特定于实现的。

          【讨论】:

            【解决方案6】:

            这是一个演示该问题的代码示例:

            #include <stdlib.h>
            
            struct A
            { int i; };
            
            struct B
            {
              ~B(){ }
              int i;
            };
            
            int main()
            {
              A * a = new A[10];
              free(a);
            
              B * b = new B[10];
              free(b);
            }
            

            如果你在 Linux 下使用 ltrace 运行它,你会看到如下内容:

            _Znaj(40, 0x293324, 0x292ff4, 0xbfeaca28, 0x165d35) = 0x88f7008
            free(0x088f7008)                                 = <void>
            _Znaj(44, 0x293324, 0x292ff4, 0xbfeaca28, 0x165d35) = 0x88f7008
            free(0x088f700c
            

            所以在第二种情况下,被释放的指针与被分配的指针不匹配(因为用户在 B 中声明了析构函数)。

            当然,new[] 和 free 之间的不匹配是未定义的行为,但在实践中并不总是会导致 SIGSEGV。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2018-02-09
              • 2014-10-08
              • 1970-01-01
              • 1970-01-01
              • 2013-03-07
              • 2013-07-03
              • 1970-01-01
              相关资源
              最近更新 更多