Повреждение памяти с помощью GetProcessHeap/HeapAlloc

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

Вот код, который я использую:

//POD_base.h: contains the base template POD (plain old data) class. Also contains memory allocation and deallocation functions.
namespace pod_helpers
{
  void* pod_malloc(size_t size)
  {
    HANDLE heapHandle = GetProcessHeap();
    HANDLE storageHandle = nullptr;

    if (heapHandle == nullptr)
    {
      return nullptr;
    }

    storageHandle = HeapAlloc(heapHandle, 0, size);

    return storageHandle;
  }

  void pod_free(void* ptr)
  {
    HANDLE heapHandle = GetProcessHeap();
    if (heapHandle == nullptr)
    {
      return;
    }

    if (ptr == nullptr)
    {
      return;
    }

    HeapFree(heapHandle, 0, ptr);
  }
}

template<typename T>
class pod
{
protected:
  pod();
  pod(const T& value);
  pod(const pod& copy);                   // no copy ctor in any pod
  ~pod();

  pod<T>& operator=(pod<T> value);
  operator T() const;

  T get() const;
  void swap(pod<T>& first, pod<T>& second);
};

pod специализация для int:

//POD_basic_types.h: contains pod specializations for basic datatypes
template<>
class pod<int>
{
  typedef int original_type; //these typedefs are meant to make the specializations easier to write and understand, since they're all the same except for the underlying datatypes.
  typedef std::int32_t safe_type;

public:
  pod() : data(nullptr) {}

  pod(const original_type& value)
  {
    set_from(value);
  }

  pod(const pod<original_type>& copyVal)
  {
    original_type copyData = copyVal.get();
    set_from(copyData);
  }

  ~pod()
  {
    release();
  }

  pod<original_type>& operator=(pod<original_type> value)
  {
    swap(*this, value);

    return *this;
  }

  operator original_type() const
  {
    return get();
  }

protected:
  safe_type* data;

  original_type get() const
  {
    original_type result;

    result = static_cast<original_type>(*data);

    return result;
  }

  void set_from(const original_type& value)
  {
    data = reinterpret_cast<safe_type*>(pod_helpers::pod_malloc(sizeof(safe_type)));

    if (data == nullptr)
    {
      return;
    }

    new(data) safe_type (value);
  }

  void release()
  {
    if (data)
    {
      pod_helpers::pod_free(data);
      data = nullptr;
    }
  }

  void swap(pod<original_type>& first, pod<original_type>& second)
  {
    using std::swap;

    swap(first.data, second.data);
  }
};

Моя DLL использует преимущества такого скрытого преобразования типов, как это:

virtual pod<int> Add(const pod<int> number1, const pod<int> number2);

pod<int> CCDLL_v1_implementation::Add(const pod<int> number1, const pod<int> number2)
{
  int workingNum1, workingNum2;

  workingNum1 = number1;
  workingNum2 = number2;

  return workingNum1 + workingNum2;
}

Затем моя тестовая программа загружает DLL через LoadLibrary/GetProcAddress, Все идет нормально; Я подтвердил, что DLL на самом деле загружена и Add функция называется. Я также подтвердил, что pod<int>"s set_from вызывается с правильными значениями. Тем не менее, это то, где вещи ломаются.

Я жду set_from выделить достаточно места для одного safe_type (в этом случае, std::int32_t), затем сохраните переданное значение в выделенной памяти. Когда я проверяю значение *data в set_fromэто похоже на случай. Тем не менее, когда я получаю pod<int>значение через getстручок data кажется мусором. Это больше не указывает на значение pod был принят во время set_from,

я знаю set_from вызывается на стороне EXE и get вызывается на стороне DLL. Мое понимание процесса заключается в следующем:

EXE -> creates pod<int> (allocating memory via GetProcessHeap/HeapAlloc) -> constructs the pod with a given value, which is passed to set_from.
The DLL's Add function is called, with the given pod passed to it.
DLL -> accesses pod<int> -> gets the pod's stored value via get -> accesses the memory the EXE allocated
DLL does its calculations (here, a simple addition) with the pod's value
DLL -> creates pod<int> (allocating memory via GetProcessHeap/HeapAlloc) -> constructs the pod with a given value, which is passed to set_from.
The DLL's newly-constructed pod is returned to the EXE.
EXE -> accesses the pod's internal value via get -> accesses the memory the DLL allocated

Это, кажется, ломает, где или DLL или EXE должен получить доступ к памяти, выделенной другому. Я видел в другом месте на SO, что с помощью этой комбинации GetProcessHeap/HeapAlloc должен работать через границы DLL. HeapCreate Документация также наводит на мысль о той же идее:

Память частного объекта кучи доступна только процессу, который его создал. Если динамически подключаемая библиотека (DLL) создает частную кучу, куча создается в адресном пространстве процесса, который вызывает DLL, и она доступна только этому процессу.

как это документация дляGetProcessHeap (выделение мое):

Функция GetProcessHeap получает дескриптор кучи по умолчаниюдля вызывающего процесса.

Как ни странно, однако,GlobalAlloc страдал такими же проблемами GetProcessHeap/HeapAlloc сделал, заставляя меня еще раз спросить, что здесь происходит не так. Еще более странно, когда я компилирую и EXE, и DLL одним и тем же компилятором, все работает как положено.

Я делаю неправильные предположения о том, как этот процесс распределения / освобождения работает? Должен ли я использовать что-то кроме GetProcessHeap/HeapAlloc? Или я просто пытаюсь сделать невозможное?


Обновление с информацией, полученной из комментариев:

Проходя pod по ссылке (CCDLL_v1_implementation::Add(const pod<int>& number1, const pod<int>& number2) работает правильно. Только передача по значению не делает.

Кажется, не имеет значения, передаю ли я "развернутые" аргументы podфункция или я обернуть аргументы в podсначала:

pod<int> a = 9;
pod<int> b = 2;

CCDLL_lib->Add(a, b);

выдает тот же испорченный data как

CCDLL_lib->Add(9, 2);

Единственная разница, кажется, заключается в том, что pods сначала вызовет копию c'or, когда Add вызывается, а оставленные без аргументов аргументы просто вызывают обычный c'or.

Это также не кажется проблемой макета класса:

if (reinterpret_cast<const void*>(&data) == reinterpret_cast<const void*>(this))
{
  //simple debug messagebox here
}

оценивается как true с обеих сторон границы EXE/DLL.


Мне удалось доказать, что проблема лежит на границе EXE/DLL, хотя я до сих пор не знаю, почему.

Я добавил новый метод в DLL, чтобы сосредоточиться на одном аргументе вместо двух:

void Square(pod<int> number);

Тогда я назвал это так:

pod<int> a = 9;
CCDLL_lib->Square(a);

Из серии отладочных сообщений появляется следующая последовательность:

Regular c'tor called (EXE)
set_from(9) called (EXE)
copy c'tor called (EXE)
get returns 9 to the copy c'tor (EXE)
set_from(9) called (EXE)
Square called (DLL) with an invalid pod (Visual Studio shows the data pointer pointing to garbage at this point)

Если я изменю Square вместо этого принять ссылку (virtual void Square(pod<int>& number) = 0;), следующая последовательность происходит:

Regular c'tor called (EXE)
set_from(9) called (EXE)
Square called (DLL) with a valid pod (Visual Studio shows the data pointer holding correct data)

0 ответов

Другие вопросы по тегам