2013-07-06 2 views
3

В общем, что может привести к двойному освобождению в программе, которая не содержит динамического распределения памяти?двойной свободный без какого-либо распределения динамической памяти

Чтобы быть более точным, ни один из моего кода использует динамическое распределение. Я использую STL, но гораздо более вероятно, что я сделал что-то неправильно, чем для того, чтобы он был сломанным внедрением G ++/glibc/STL.

Я искал, пытаясь найти ответ на этот вопрос, но я не смог найти какой-либо пример генерируемой ошибки без каких-либо распределений динамической памяти.

Я хотел бы поделиться кодом, который генерировал эту ошибку, но мне не разрешено ее выпускать, и я не знаю, как уменьшить проблему до достаточно маленького, чтобы быть здесь. Я сделаю все возможное, чтобы описать суть моего кода.

Ошибка была брошена при выходе из функции, а трассировка стека показала, что она исходит от деструктора std::vector<std::set<std::string>>. Некоторое количество элементов в векторе инициализировалось emplace_back(). В последней попытке я поменял ее на push_back({{}}), и проблема исчезла. Проблем также можно избежать, установив переменную окружения MALLOC_CHECK_ = 2. По моему мнению, эта переменная среды должна приводить к тому, что glibc прерывается с большей информацией, а не заставляет ошибку уходить.

Этот вопрос только просит служить моему любопытству, поэтому я соглашусь на выстрел в темном ответе. Лучшее, что я смог придумать, это то, что это ошибка компилятора, но it's always my fault.

ответ

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

Я извлек презентабельный пример демонстрации ошибки я сделал, что привело к «двойному свободной или поврежденной ". Обратите внимание, что структура явно не использует распределение динамической памяти, однако внутренне 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 неправильно и увидели то же поведение, возможно, вы этого не сделали. В конце концов, это была моя полностью моя ошибка :).