Pickle segfaults with custom type
Marco Sulla
Marco.Sulla.Python at gmail.com
Sat Jan 15 10:49:26 EST 2022
Found. I simply forgot:
if (PyType_Ready(&PyFrozenDictIterKey_Type) < 0) {
goto fail;
}
in the frozendict_exec function for the module.
On Fri, 7 Jan 2022 at 20:27, Marco Sulla <Marco.Sulla.Python at gmail.com>
wrote:
> I have a custom implementation of dict using a C extension. All works but
> the pickling of views and iter types. Python segfaults if I try to pickle
> them.
>
> For example, I have:
>
>
> static PyTypeObject PyFrozenDictIterKey_Type = {
> PyVarObject_HEAD_INIT(NULL, 0)
> "frozendict.keyiterator", /* tp_name */
> sizeof(dictiterobject), /* tp_basicsize */
> 0, /* tp_itemsize */
> /* methods */
> (destructor)dictiter_dealloc, /* tp_dealloc */
> 0, /* tp_vectorcall_offset */
> 0, /* tp_getattr */
> 0, /* tp_setattr */
> 0, /* tp_as_async */
> 0, /* tp_repr */
> 0, /* tp_as_number */
> 0, /* tp_as_sequence */
> 0, /* tp_as_mapping */
> PyObject_HashNotImplemented, /* tp_hash */
> 0, /* tp_call */
> 0, /* tp_str */
> PyObject_GenericGetAttr, /* tp_getattro */
> 0, /* tp_setattro */
> 0, /* tp_as_buffer */
> Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
> 0, /* tp_doc */
> (traverseproc)dictiter_traverse, /* tp_traverse */
> 0, /* tp_clear */
> 0, /* tp_richcompare */
> 0, /* tp_weaklistoffset */
> PyObject_SelfIter, /* tp_iter */
> (iternextfunc)frozendictiter_iternextkey, /* tp_iternext */
> dictiter_methods, /* tp_methods */
> 0,
> };
>
> This is the backtrace I get with gdb:
>
> #0 PyObject_Hash (v=0x7f043ce15540 <PyFrozenDictIterKey_Type>) at
> ../cpython_3_10/Objects/object.c:788
> #1 0x000000000048611c in PyDict_GetItemWithError (op=0x7f043e1f4900,
> key=key at entry=0x7f043ce15540 <PyFrozenDictIterKey_Type>)
> at ../cpython_3_10/Objects/dictobject.c:1520
> #2 0x00007f043ce227f6 in save (self=self at entry=0x7f043d8507d0,
> obj=obj at entry=0x7f043e1fb0b0, pers_save=pers_save at entry=0)
> at /home/marco/sources/cpython_3_10/Modules/_pickle.c:4381
> #3 0x00007f043ce2534d in dump (self=self at entry=0x7f043d8507d0,
> obj=obj at entry=0x7f043e1fb0b0) at
> /home/marco/sources/cpython_3_10/Modules/_pickle.c:4515
> #4 0x00007f043ce2567f in _pickle_dumps_impl (module=<optimized out>,
> buffer_callback=<optimized out>, fix_imports=<optimized out>,
> protocol=<optimized out>,
> obj=0x7f043e1fb0b0) at
> /home/marco/sources/cpython_3_10/Modules/_pickle.c:1203
> #5 _pickle_dumps (module=<optimized out>, args=<optimized out>,
> nargs=<optimized out>, kwnames=<optimized out>)
> at /home/marco/sources/cpython_3_10/Modules/clinic/_pickle.c.h:619
>
> and so on. The problematic part is in the second frame. Indeed the code of
> _pickle.c here is:
>
>
> reduce_func = PyDict_GetItemWithError(st->dispatch_table,
> (PyObject *)type);
>
> The problem is that type is NULL. It tries to get the attribute tp_hash
> and it segfaults.
>
> I tried to change the header of the type to:
>
> PyVarObject_HEAD_INIT(&PyType_Type, 0)
>
> This way it works but, as known, it does not compile on Windows.
>
> The strange fact is that pickling the main type works, even if the type is
> NULL, as suggested for a custom type. This is the main type:
>
> PyTypeObject PyFrozenDict_Type = {
> PyVarObject_HEAD_INIT(NULL, 0)
> "frozendict." FROZENDICT_CLASS_NAME, /* tp_name */
> sizeof(PyFrozenDictObject), /* tp_basicsize */
> 0, /* tp_itemsize */
> (destructor)dict_dealloc, /* tp_dealloc */
> 0, /* tp_vectorcall_offset */
> 0, /* tp_getattr */
> 0, /* tp_setattr */
> 0, /* tp_as_async */
> (reprfunc)frozendict_repr, /* tp_repr */
> &frozendict_as_number, /* tp_as_number */
> &dict_as_sequence, /* tp_as_sequence */
> &frozendict_as_mapping, /* tp_as_mapping */
> (hashfunc)frozendict_hash, /* tp_hash */
> 0, /* tp_call */
> 0, /* tp_str */
> PyObject_GenericGetAttr, /* tp_getattro */
> 0, /* tp_setattro */
> 0, /* tp_as_buffer */
> Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC
> | Py_TPFLAGS_BASETYPE
> | _Py_TPFLAGS_MATCH_SELF
> | Py_TPFLAGS_MAPPING, /* tp_flags */
> frozendict_doc, /* tp_doc */
> dict_traverse, /* tp_traverse */
> 0, /* tp_clear */
> dict_richcompare, /* tp_richcompare */
> 0, /* tp_weaklistoffset */
> (getiterfunc)frozendict_iter, /* tp_iter */
> 0, /* tp_iternext */
> frozendict_mapp_methods, /* tp_methods */
> 0, /* tp_members */
> 0, /* tp_getset */
> 0, /* tp_base */
> 0, /* tp_dict */
> 0, /* tp_descr_get */
> 0, /* tp_descr_set */
> 0, /* tp_dictoffset */
> 0, /* tp_init */
> PyType_GenericAlloc, /* tp_alloc */
> frozendict_new, /* tp_new */
> PyObject_GC_Del, /* tp_free */
> .tp_vectorcall = frozendict_vectorcall,
> };
>
More information about the Python-list
mailing list