【问题标题】:double free without any dynamic memory allocation没有任何动态内存分配的双重释放
【发布时间】:2013-07-04 08:37:36
【问题描述】:

一般来说,在不包含任何动态内存分配的程序中,什么会导致双重释放?

更准确地说,my 代码都没有使用动态分配。我正在使用 STL,但这更有可能是我做错了什么,而不是 G++/glibc/STL 的错误实现。

我四处寻找试图找到这个问题的答案,但我找不到任何在没有任何动态内存分配的情况下生成此错误的示例。

我很乐意分享产生此错误的代码,但我无权发布它,而且我不知道如何将问题减少到足以在此处给出的小问题。我会尽力描述我的代码在做什么的要点。

离开函数时抛出错误,堆栈跟踪显示它来自std::vector<std::set<std::string>> 的析构函数。向量中的一些元素被emplace_back() 初始化。在最后一次尝试中,我将其更改为push_back({{}}),问题就消失了。通过设置环境变量 MALLOC_CHECK_=2 也可以避免该问题。据我了解,该环境变量应该导致 glibc 中止并提供更多信息,而不是导致错误消失。

问这个问题只是为了满足我的好奇心,所以我会在黑暗中找到答案。我能想到的最好的结果是它是一个编译器错误,但是it's always my fault

【问题讨论】:

    标签: c++ memory-management double-free


    【解决方案1】:

    一般来说,什么会导致不包含任何动态内存分配的程序出现双重释放?

    通常当您复制动态分配内存但不遵循 rule of three

    的类型时
    struct Type
    {
       Type() : ptr = new int(3) { }
       ~Type() { delete ptr; }
       // no copy constructor is defined
       // no copy assign operator is defined
    
    private:
       int * ptr;
    };
    
    void func()
    {       
       { 
         std::vector<Type> objs;
         Type t; // allocates ptr
         objs.push_back(t); // make a copy of t, now t->ptr and objs[0]->ptr point to same memory location
         // when this scope finishes, t will be destroyed, its destructor will be called and it will try to delete ptr;
         // objs go out of scope, elements in objs will be destroyed, their destructors are called, and delete ptr; will be executed again. That's double free on same pointer.
       }    
    }
    

    【讨论】:

    • 我偶然发现了很多关于三法则的问题和文章。但是,所有这些,包括您的答案,都涉及对 new 和 delete 的调用。我愿意相信添加复制构造函数和赋值运算符会解决我的问题,但我的问题是为什么?我了解动态分配成员的类可能会发生什么,但当一切都在堆栈上时不会发生。
    • 这不能回答问题。 OP 询问了没有动态分配的双释放(这与我遇到的情况相同)
    【解决方案2】:

    我提取了一个可展示的示例,展示了我所犯的导致“双重释放或损坏”运行时错误的错误。请注意,该结构没有显式使用任何动态内存分配,但内部 std::vector 使用(因为它的内容可以增长以容纳更多项目)。因此,这个问题有点难以诊断,因为它不违反“3 规则”原则。

    #include <vector>
    #include <string.h>
    
    typedef struct message {
      std::vector<int> options;
      void push(int o) { this->options.push_back(o); }
    } message;
    
    int main( int argc, const char* argv[] )
    {
      message m;
      m.push(1);
      m.push(2);
    
      message m_copy;
      memcpy(&m_copy, &m, sizeof(m));
      //m_copy = m; // This is the correct method for copying object instances, it calls the default assignment operator generated 'behind the scenes' by the compiler
    }
    

    当 main() 返回时 m_copy 被销毁,它调用 std::vector 析构函数。这会尝试删除在 m 对象被销毁时已经释放的内存。

    具有讽刺意味的是,我实际上是在使用 memcpy 来尝试实现“深度复制”。这就是我的问题所在。我的猜测是,通过使用赋值运算符,message.options 的所有成员实际上都被复制到“新分配的内存”,而 memcpy 只会复制那些在编译时分配的成员(例如 uint32_t 大小的成员)。见Will memcpy or memmove cause problems copying classes?。显然,这也适用于具有非基本类型成员的结构(就像这里的情况一样)。

    也许您还错误地复制了 std::vector 并看到了相同的行为,也许您没有。最后,这完全是我的错:)。

    【讨论】:

      猜你喜欢
      • 2016-07-16
      • 2011-03-17
      • 1970-01-01
      • 2013-11-22
      • 2017-06-13
      • 2013-04-14
      • 1970-01-01
      • 2023-03-03
      相关资源
      最近更新 更多