C-API:分配“PyTypeObject-extension”

时间:2014-11-16 19:18:17

标签: memory-management python-c-api pycxx

我在PyCXX中发现了一些可能有问题的代码。

它确实是一个错误,如果是这样,解决它的正确方法是什么?

问题在于:

struct PythonClassInstance
{
    PyObject_HEAD
    ExtObjBase* m_pycxx_object;
}
:
{
    :
    table->tp_new = extension_object_new; // PyTypeObject
    :
}
:
static PyObject* extension_object_new( 
                 PyTypeObject* subtype, PyObject* args, PyObject* kwds )
{
    PythonClassInstance* o = reinterpret_cast<PythonClassInstance *>
                                       ( subtype->tp_alloc(subtype,0) );
    if( ! o )
        return nullptr;

    o->m_pycxx_object = nullptr;

    PyObject* self = reinterpret_cast<PyObject* >( o );

    return self;
}

现在PyObject_HEAD扩展为“PyObject ob_base;”,所以很明显PythonClassInstance简单地扩展PyObject以包含一个额外的指针(它将指向PyCXX对这个PyObject的表示)

tp_alloc分配用于存储PyObject的内存

然后代码将此指针强制转换为PythonClassInstance,声称它不拥有额外的4(或8?)字节!

然后它将这个额外的内存设置为0。

这看起来非常危险,我很惊讶这个bug已经被忽视了。风险在于某些未来的对象将被放置在这个位置(这意味着存储ExtObjBase *)。

如何解决?

PythonClassInstance foo{};

PyObject* tmp = subtype->tp_alloc(subtype,0);

// !!! memcpy sizeof(PyObject) bytes starting from location tmp into location (void*)foo

但是我想现在也许我需要释放tmp,我不认为我应该像这样直接玩内存。我觉得它可能会危害Python内置管理/内置机器的垃圾收集。

另一种选择是,我可以说服tp_alloc分配4个额外字节(或现在是8;足够指针)绕过1而不是0。

文档说第二个参数是“Py_ssize_t nitems”和:

  

如果类型的tp_itemsize不为零,则为对象的ob_size字段   应该初始化为nitems和分配的内存的长度   block应为tp_basicsize + nitems tp_itemsize,向上舍入为a   sizeof的多个(void );否则,不使用nitems和   块的长度应为tp_basicsize。

所以看起来我应该设置:

table->tp_itemsize = sizeof(void*);
:
PyObject* tmp = subtype->tp_alloc(subtype,1);

编辑:刚刚试过这个并导致崩溃

然后文件继续说:

  

不要使用此函数进行任何其他实例初始化,而不是   甚至分配额外的内存;这应该由tp_new完成。

现在我不确定这段代码是属于tp_new还是tp_init。

相关:

Passing arguments to tp_new and tp_init from subtypes in Python C API

Python C-API Object Allocation‏

2 个答案:

答案 0 :(得分:1)

代码是正确的。

只要扩展对象的PyTypeObject被正确初始化,它就应该有效。

基类tp_alloc收到subtype因此它应该通过检查tp_basicsize成员知道要分配多少内存。

这是tutorial中所示的常见Python C / API模式。

答案 1 :(得分:0)

实际上这是一个(轻微的/无害的)bug in PyCXX

所以我希望将这个答案转换为评论,这没有任何意义,我无法授予完成的绿色标记,所以我发表评论。所以我必须絮絮叨叨才能获得资格。 blerh。