双重免费,没有任何动态内存分配

时间:2013-07-06 11:27:16

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

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

更准确地说,我的代码都没有使用动态分配。我正在使用STL,但它更有可能是我做错了,而不是G ++ / glibc / STL的破坏。

我一直在寻找答案,但是我没有找到任何没有任何动态内存分配的错误生成示例。

我很乐意分享产生此错误的代码,但我不允许发布它,我不知道如何将问题减少到足够小到可以在这里给出的东西。我会尽力描述我的代码所做的事情。

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

这个问题只是被要求服务于我的好奇心,所以我会在黑暗的答案中找到答案。我能想到的最好的是它是一个编译器错误,但是it's always my fault

2 个答案:

答案 0 :(得分:3)

  

通常,什么可能导致程序中的双重释放不包含任何动态内存分配?

通常,当您制作动态分配内存但不遵循 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.
   }    
}

答案 1 :(得分:1)

我提取了一个可呈现的示例,展示了我所做的导致“双重释放或损坏”运行时错误的错误。请注意,struct不会显式使用任何动态内存分配,但内部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 size成员)。见Will memcpy or memmove cause problems copying classes?。显然,这也适用于具有非基本类型成员的结构(如此处的情况)。

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