3

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

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

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

我很想分享产生这个错误的代码,但我不允许发布它,我不知道如何将问题减少到足够小,可以在这里给出。我会尽力描述我的代码在做什么的要点。

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

这个问题只是为了满足我的好奇心,所以我会在黑暗中回答。我能想到的最好的结果是它是一个编译器错误,但这总是我的错

4

2 回答 2

3

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

通常,当您复制动态分配内存但不遵循三规则的类型时

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.
   }    
}
于 2013-07-06T11:36:50.813 回答
1

我提取了一个可展示的示例,展示了我所犯的导致“双重释放或损坏”运行时错误的错误。请注意,该结构没有显式使用任何动态内存分配,但内部 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 大小的成员)。请参阅memcpy 或 memmove 会导致复制类的问题吗?. 显然,这也适用于具有非基本类型成员的结构(就像这里的情况一样)。

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

于 2014-02-04T15:13:56.913 回答