我在PyCXX中发现了一些可能是错误的代码。
它真的是一个bug吗?如果是的话,有什么正确的方法来解决它呢?
以下是问题所在:
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的表示)。
异种为存储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硝酸盐“,并且:
如果类型的tp_itemsize为非零,则应将对象的ob_size字段初始化为nitems,而分配的内存块的长度应为tp_basicsize + nitemstp_itemsize,舍入为相当大的倍数(无效);否则,不使用nitems,块的长度应为tp_basicsize。
所以看起来我应该设置:
table->tp_itemsize = sizeof(void*);
:
PyObject* tmp = subtype->tp_alloc(subtype,1);编辑:刚试过这个,它会导致崩溃。
但文件接着又说:
不要使用此函数进行任何其他实例初始化,甚至不能分配额外的内存;这应该由tp_new来完成。
现在我不确定这段代码是属于tp_new还是tp_init。
相关信息:
发布于 2015-01-18 15:37:58
实际上,这是一个(小/无害的) PyCXX中的bug
所以想把这个答案转换成一个评论,这是没有意义的,我不能授予绿色滴答的完成,所以我的评论。所以我得漫步才能符合条件。布莱。
发布于 2014-11-19 01:24:39
密码是正确的。
只要正确初始化扩展对象的PyTypeObject,它就可以工作。
基类tp_alloc接收subtype,因此它应该通过检查tp_basicsize成员来知道要分配多少内存。
这是一个常见的Python /API模式,如在教程中演示的那样。
https://stackoverflow.com/questions/26961000
复制相似问题