为什么在 pybind11 中使用犰狳矩阵会导致堆损坏?

Why might I get heap corruption using Armadillo matrices with pybind11?

我已经为此工作了几个星期,无法在我的代码库之外制作可重现的示例。这就是为什么我需要帮助!我不确定这是否是 pybind11 或 Armadillo 的问题。 Carma 这不是问题,因为它发生在没有进行转换的情况下。

编辑This actually does appear to be a bug in Carma and my MRE is working.

我一直在尝试将其归结为 MRE and have been unsuccessful。我将在这里解释我所知道的,但由于这不足以重现错误,我最需要的是一些关于在哪里查看的想法。这基本上是一个非常难以重现的内存损坏错误(堆损坏,Windows fatal exception: code 0xc0000374)。当我有一个未初始化的矩阵 A 并为其分配一个足够大的矩阵以使犰狳获得内存时,这似乎会发生。释放内存时会发生崩溃。

我在 Windows 10,使用 Armadillo 10.6.2 和 pybind11 v2.7.1,使用 Clang 11 编译。

// mat_container.cpp
#include "mat_container.h"

MatrixContainer::MatrixContainer(size_t d) {
    A = arma::Mat<double>(d, d, arma::fill::eye);
    std::cerr << "filled arma matrix\n";
}

绑定码

#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <armadillo>
#include <carma>
#include "mat_container.h"

PYBIND11_MODULE(example, m) {
  py::class_<MatrixContainer>(m, "MC").def(py::init<size_t, bool>())
       .def_readwrite("A", &MatrixContainer::A);
}

我需要做的就是从 Python 调用 example.MC(11) 并在释放成员变量(不是分配给它的临时变量)的矩阵析构函数中的内存时崩溃.崩溃前的犰狳调试消息:

@ __cdecl arma::Mat<double>::~Mat(void) [eT = double] [this = 000001569470DBA0]
Mat::destructor: releasing memory

在我的 attempted MRE 中,我尝试重现相同的结构,使用在单独的库中编译的 MatrixContainer class 绑定代码。我不知道可能缺少什么,所以我的 MRE 没有重现错误。

怪事

当A正在使用获取的内存并被使用本地内存的矩阵替换时,发生崩溃

mc = MC(11)
mc.A = np.eye(3)

Arma 崩溃前的调试消息,因为正在分配 mc.A:

@ void __cdecl arma::Mat<double>::init_warm(arma::uword, arma::uword) [eT = double] [in_n_rows = 3, in_n_cols = 3]
Mat::init(): releasing memory

但不是反过来;这运行成功。它不会崩溃,因为获取的内存在 A 被销毁时被释放:

mc = MC(3)
mc.A = np.eye(11)

当 11x11 A 被 numpy 中的另一个 11x11 矩阵替换时,崩溃又是 Mat::destructor: releasing memory,就像第一个没有从 Python 赋值的例子一样。 我从这些例子中得出的结论是,当将 Carma(不是 Armadillo 直接)准备的获取内存矩阵分配给本地内存 A 时,可以避免崩溃。

更新: 仍在处理此问题,但看起来代码失败了,因为 arma_extra_code 已打开(它不在 MRE 中)。罪魁祸首似乎是 ARMA_ALIEN_MEM 函数,设置 here

感谢 carma 开发者,@RUrlus, for the answer

问题是由于绑定模块 linked 到 carma,被 linked 到一个没有被 linked 到 carma 的库。外部库(MRE 中的 mc)使用标准 malloc 分配内存,而 pybind11 在销毁时使用 Carma 的 free。不匹配导致 Windows.

崩溃

Perhaps in the future there could be a more elegant solution,但目前的解决方法是在编译时将 link 外部库添加到 carma,例如 target_link_libraries(mc PUBLIC armadillo carma) 即使该外部库不包含或使用Carma 以任何明显的方式。