C-API:分配“PyTypeObject-extension”

Posted

技术标签:

【中文标题】C-API:分配“PyTypeObject-extension”【英文标题】:C-API: Allocating "PyTypeObject-extension" 【发布时间】:2014-11-16 19:18:16 【问题描述】:

我在 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。

这看起来很危险,我很惊讶这个错误竟然被忽视了。风险是一些未来的对象将被放置在这个位置(即存储 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 和分配内存的长度 块应为 tp_basicsize + nitemstp_itemsize,四舍五入为 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‏

【问题讨论】:

【参考方案1】:

代码是正确的。

只要扩展对象的 PyTypeObject 正确初始化,它就可以工作。

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

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

【讨论】:

【参考方案2】:

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

所以想将此答案转换为评论,这毫无意义我不能授予完成的绿色勾号,所以我发表评论。所以我必须漫步才能获得资格。呵呵。

【讨论】:

以上是关于C-API:分配“PyTypeObject-extension”的主要内容,如果未能解决你的问题,请参考以下文章

使用 Python C-API 强制类型转换

来自 Python 的 C-API - 如何获取字符串?

如何在 Python C-API 中动态创建派生类型

在 ubuntu 20.04 LTS 上使用 python Xlib 或 C-API 操作第二个(辅助)鼠标输入

Python C-API:PyDict_GetItem 上的分段错误,可能的参考问题?

Python C-API 和 Numpy:import_array 上的核心转储