Как найти запись в освобожденную память (повреждение кучи)? - PullRequest
2 голосов
/ 04 апреля 2019

Я отлаживаю многопоточное приложение C ++ в Visual Studio 2017. Тип проблемы может быть воспроизведен с помощью следующего примера кода

  int* i = new int();
  *i = 4;
  int* j = i;
  delete i;
  //perhaps some code or time passes
  *j = 5;//write to freed momory = possible heap corruption!!

Я использовал встроенную проверку кучи , чтобы найти тип проблемы с флагами:

 _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_DELAY_FREE_MEM_DF )

Затем использовал _ASSERTE(_CrtCheckMemory());, чтобы попытаться сузить его - но только для того, чтобы сделать вывод, что он был в другом потоке. Глядя на другие потоки к тому времени, когда было обнаружено повреждение, кажется, что он просто делает «нормальные» вещи и не находится в коде приложения в то время. Отчеты выглядят так:

HEAP CORRUPTION DETECTED: on top of Free block at 0x00000214938B88D0.
CRT detected that the application wrote to a heap buffer that was freed.
DAMAGED located at 0x00000214938B88D0 is 120 bytes long.
Debug Assertion Failed!

Каждый раз 120 байтов - но адрес меняется. (способ, которым это обнаружено, - то, что образец 0xdddddddd был перезаписан, когда куча проверена) Либо найти распределение, либо найти оскорбительную запись было бы полезно. Я пытался использовать 'gflags.exe' , но мне не удалось обнаружить этот тип повреждения (так как я понимаю, что он в основном предназначен для поиска переполнения буфера), и у меня нет предыдущего опыта работы с этим инструментом , Если бы я мог найти «уникальный номер распределения» по адресу, это также могло бы помочь.

1 Ответ

0 голосов
/ 10 апреля 2019

Вот код, который я использовал, чтобы отследить его. Краткое содержание: Оставляет страницы в виртуальной памяти незафиксированными вместо полного освобождения.Это означает, что попытка использовать такую ​​страницу не удастся.

DWORD PageSize = 0;

namespace {
  inline void SetPageSize()
  {
    if (!PageSize)
    {
      SYSTEM_INFO sysInfo;
      GetSystemInfo(&sysInfo);
      PageSize = sysInfo.dwPageSize;
    }
  }

  void* alloc_impl(size_t nSize) {
    SetPageSize();
    size_t Extra = nSize % PageSize;
    if (Extra != 0 || nSize == 0) {
      nSize = nSize + (PageSize - Extra);
    }
    void* res = VirtualAlloc(0, nSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!res) {
      DWORD errorMessageID = ::GetLastError();
      LPSTR messageBuffer = nullptr;
      size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);

      throw std::bad_alloc{};
    }
    if (reinterpret_cast<size_t>(res) % PageSize != 0) {
      throw std::bad_alloc{};
    }

    return res;
  }

  void dealloc_impl(void* pPtr) {
    if (pPtr == nullptr) {
      return;
    }


    VirtualFree(pPtr, 0, MEM_DECOMMIT);


  }
}


void* operator new (size_t nSize)
{
  return alloc_impl(nSize);
}

void operator delete (void* pPtr)
{
  dealloc_impl(pPtr);
}
void *operator new[](std::size_t nSize) throw(std::bad_alloc)
{
  return alloc_impl(nSize);
}
void operator delete[](void *pPtr) throw()
{
  dealloc_impl(pPtr);
}
...