Повреждение кучи: что может быть причиной?

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

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

Я был бы признателен, если бы вы предложили сценарии, которые могут привести к куче коррупции.

Платформа: Windows XP

Язык: С++

Компилятор: VC6

Заранее спасибо

SSS

Ответы

Ответ 1

Общие сценарии включают:

  • Запись вне выделенного пространства массива (char *stuff = new char[10]; stuff[10] = 3;)
  • Кастинг с неправильным типом
  • Неинициализированные указатели
  • Ошибка Typo для → и.
  • Ошибка при использовании * и и (или нескольких).

[EDIT] Из комментариев еще несколько:

  • Смешивание нового [] и нового с удалением [] и удаление
  • Отсутствующие или неправильные конструкторы копирования
  • Указатель, указывающий на мусор
  • Вызов несколько раз удаляться по тем же данным
  • Полиморфные базовые классы без виртуальных деструкторов

Ответ 2

Добро пожаловать в ад. Нет простого решения, поэтому я буду предлагать только некоторые указатели.

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

В этом случае вы можете попробовать инструмент анализатора, например Purify. Они обнаружат почти все, что противно, что ваш код делает, но также будет работать ОЧЕНЬ медленно. Такой инструмент обнаруживает доступ к связанной памяти, освобождает доступ к памяти, пытается освободить дважды тот же блок, используя неправильный распределитель/дезактиваторы и т.д. Это все виды условий, которые могут оставаться скрытыми в течение очень долгого времени и только сбой в самый неподходящий момент.

Ответ 3

Вы можете посмотреть примерную главу из Расширенная книга отладки Windows, которая содержит различные примеры повреждения кучи.

EDIT: если вы используете stl-контейнеры, которые могут перемещать элементы во время изменений (т.е. vector, deque), убедитесь, что вы не храните ссылки на такие элементы в разных операциях, которые могут его изменить.

Ответ 4

Есть продукты, которые будут наблюдать за распределением памяти и освобождением памяти, а также сообщать об аномалиях. Последнее, что я использовал, они были не дешевы, но я не знаю, какова ситуация сейчас. Однако найти материал для VС++ 6 может быть проблемой.

Помните, что вы можете получать кучу коррупции намного чаще, чем вы собираетесь разбиться, поэтому будьте внимательны к отчетам о проблемах и исправьте все повреждения кучи.

Я предлагаю использовать std::tr1::smart_ptr<> вместо необработанных указателей везде, но я не уверен, что VС++ 6 будет поддерживать это.

Почему вы все еще на VС++ 6? Было бы целесообразно обновить? Инструменты лучше с более поздними версиями, и они полностью поддерживают интеллектуальные указатели.

Ответ 5

Проверьте ответы на этот связанный с этим вопрос.

Ответ . Я предложил метод, который может вернуть вас к коду, который фактически вызывает повреждение кучи. Мой ответ описывает технику с помощью gdb, но я уверен, что вы должны иметь возможность делать что-то подобное в окнах.

Принцип, по крайней мере, должен быть одним и тем же.

Ответ 6

Каждый раз, когда вы делаете что-то, что не определено в стандарте языка, это поведение undefined, и одним из способов его проявления может быть повреждение кучи. В С++ существует около трех миллионов способов сделать это, поэтому на самом деле невозможно сказать.

Несколько распространенных случаев - это двойное освобождение динамически распределенной памяти или запись за пределами массива. Или записать неинициализированный указатель.

Недавние версии компилятора Microsoft добавляют компилятор/анализатор компилятора, который выполняет кучу статического анализа для обнаружения таких ошибок. В Linux, valgrind - очевидный выбор.

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

Если возможно, вам следует перейти на правильный компилятор.

Ответ 7

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

Также всегда устанавливайте указатели на нуль после удаления их даже в деструкторах. Иногда в цепочке деструкторов в базовом классе могут запускаться неожиданные вещи, а не вызвать доступ к частично удаленному подклассу.

Ответ 8

Его распространенная ошибка - освободить() или удалить выделенную память более чем на одну. Это может помочь вставить что-то вроде * var = NULL после таких вызовов и проверить!= NULL при вызове бесплатного. Хотя в С++ его законно вызывать удалить с помощью переменной NULL, вызов C - free() завершится с ошибкой.

Также распространенная проблема заключается в том, чтобы путать удаление и удаление [].

Переменные, выделенные с помощью new, должны быть освобождены с помощью удалить.

Массив, выделенный с помощью new [], должен быть освобожден с помощью delete [].

Также не следует смешивать управление памятью в стиле C (malloc, calloc, free) с управлением памятью стиля С++ (новый/удалить). В устаревшем коде часто оба смешаны, но вещи, выделенные с помощью одного, не могут быть освобождены вместе с другим.

Все эти ошибки обычно не распознаются компилятором.

Ответ 9

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

constQ= new double* [num_equations];
for(int i=0;i<num_equations;i++)
{
constQ[i]=new double[num_equations];
for(int j=0;j<num_equations;j++)
{
constQ[i][j]=0.0;
}
.
.
.

//Deleting/Freeing memory block 
//Here the below only parent memory block is deleted, the child memory block is leaked.

if(constQ!=NULL)
{
delete[] constQ;
constQ=NULL
} 
//Correct way of deleting heap memory..First delet child block memory and then Parent block

if(constQ!=NULL)
{
for(int i=0; i <num_equations;i++)
{
delete[] constQ[i];
delete[] constQ;
constQ=NULL
}

Ответ 10

Если у вас есть доступ к машине * nix, вы можете использовать Valgrind.

Ответ 11

Самая сложная ошибка с повреждением памяти, с которой я столкнулся, (1) вызывает функцию в DLL, которая возвращает std::vector, а затем (2), позволяя этому std::vector выпасть из области действия (что в основном является целым точка std::vector). К сожалению, оказалось, что DLL была связана с одной версией среды выполнения С++, и программа была связана с другой; что библиотека вызывала одну версию new[], и я вызывал совершенно другую версию delete[].

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

Недавно я использовал оценочные версии двух продуктов, которые могут вам помочь: IBM Rational Purify и Intel Parallel Inspector. Я уверен, что есть другие (Insure ++ упоминается много). В Linux вы бы использовали Valgrind.

Ответ 12

Вы считали, что изолировать источник коррупции с помощью gflags? После того, как у вас есть дамп (или разрыв отладчика → WinDBG), вы можете увидеть, где коррупция вызвана более точно.

Вот некоторые примеры gflag: http://blogs.msdn.com/b/webdav_101/archive/2010/06/22/detecting-heap-corruption-using-gflags-and-dumps.aspx

Приветствия, Seb

Ответ 13

Это синтаксис HeapAlloc fuction.

LPVOID WINAPI HeapAlloc(
  _In_ HANDLE hHeap,
  _In_ DWORD  dwFlags,
  _In_ SIZE_T dwBytes
);

Здесь dwFlags параметр может иметь либо HEAP_GENERATE_EXCEPTIONS, либо HEAP_NO_SERIALIZE или HEAP_ZERO_MEMORY.

В нашем файле мы должны проверить флаги, которые мы установили. Если мы установили значение флага как HEAP_NO_SERIALIZE, тогда не будет сериализации, которая означает, что несколько потоков будут обращаться к ресурсам, которые могут вызвать повреждение памяти.

"Установка значения HEAP_NO_SERIALIZE исключает взаимное исключение из кучи. Без сериализации, два или более потока, которые используют один и тот же дескриптор кучи, могут попытаться выделить или освободить память одновременно, вероятно, вызывая коррупцию в куче ".

поэтому я думаю, что из-за повреждения памяти в куче, node получил сбой.