[PyQt] segfault with multiple inheritance and QGraphicsItems

Zach Pincus zpincus at gmail.com
Thu May 2 04:47:16 BST 2019


Just to follow up a little on the below:

(1) Since the order of the superclasses often matters for weird bugs
like this, I wanted to clarify that in this case it actually doesn't
matter. That is, the segfault occurs regardless of whether it's:
class MyItem(Mixin, Qt.QGraphicsRectItem):
or
class MyItem(Qt.QGraphicsRectItem, Mixin):

(2) I have found that (using the PyQt5 builds available on PyPI) that
the segfault does not appear when my test case is run against PyQt5
versions 5.8.x or earlier. The segfault appears in the 5.9 series and
is present in every release thereafter, including the latest on PyPI,
version 5.12.1. This is true on both mac and linux. In contrast, the
version of python doesn't seem to matter much here.

So my guess (based on the backtraces and this last finding) is that
this is the result of a super-weird corner-case bug introduced in SIP
a few releases back. It is a little odd that the exact location of the
segfault differs between mac and linux. Maybe this suggests an
unitialized memory error rather than dereferencing a null pointer? But
what do I know? :)

Happy to run more tests and report back if it helps.

Thanks again,
Zach

On Wed, May 1, 2019 at 10:34 AM Zach Pincus <zpincus at gmail.com> wrote:
>
> Hello,
>
> I've traced a segfault in PyQt5 to multiple inheritance with
> QGraphicsItems. Below is a minimal example to reproduce this issue:
>
> #######
> from PyQt5 import Qt
>
> class Mixin:
>    pass
>
> class MyItem(Mixin, Qt.QGraphicsRectItem):
>    def itemChange(self, change, value):
>        return value
>
> myitem = MyItem()
> rect = Qt.QGraphicsRectItem(parent=myitem) # segfault here
> #######
>
> If MyItem doesn't inherit from Mixin, there's no problem. (The order
> of the superclasses doesn't matter, BTW.) If MyItem doesn't override
> itemChange, there's again no problem. (It can override other methods
> from QGraphicsItem just fine, as far as I can tell.) And if the parent
> of rect on the last line is None, there's no problem.
>
> This is tested with the latest PyQt5 from pip (5.12.1), but the issue
> reproduces across many versions of PyQt5. It happens on macOS and
> linux; I have not tested on windows. Below is an LLDB backtrace of the
> segfault on a mac, and a GDB backtrace from a Ubuntu install.
>
> Note also that the following works fine:
> myitem = MyItem()
> rect = Qt.QGraphicsRectItem(parent=None)
> rect.setParentItem(myItem)
>
> However, that workaround doesn't seem to apply to the larger Qt
> application that I'm working on, from which this example is extracted.
> In particular, the workaround creates some kind race condition that
> freezes the application. I don't fully understand that race, but I
> suspect it may well be related to the segfault here. So I'm hoping if
> I can fix the segfault in this simple example, I can get my
> application working properly.
>
> And obviously in the full application, there's a good reason to need
> to use a mixin class, even though it's just a placeholder here. (I'm
> trying to make it possible to add a set of behaviors to an arbitrary
> QGraphicsItem subclass, and inheritance of those behaviors via mixin
> seems like the most straightforward way to do this.)
>
> Any suggestions / workarounds would be welcome!
>
> Thanks,
> Zach
>
>
>
> *** GDB backtrace on up-to-date Ubuntu ***
>
> Program received signal SIGSEGV, Segmentation fault.
> 0x00007ffff5da743d in QObject::connect(QObject const*, char const*,
> QObject const*, char const*, Qt::ConnectionType) ()
>   from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Core.so.5
> (gdb) bt
> #0  0x00007ffff5da743d in QObject::connect(QObject const*, char
> const*, QObject const*, char const*, Qt::ConnectionType) ()
>   from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Core.so.5
> #1  0x00007ffff64429dc in PyQtMonitor::monitor(QObject*) () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/QtCore.so
> #2  0x00007ffff2d27c45 in sipSimpleWrapper_init () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/sip.so
> #3  0x00005555556f0f57 in type_call () at
> /tmp/build/80754af9/python_1540319457073/work/Objects/typeobject.c:915
> #4  0x00005555556671de in PyObject_Call () at
> /tmp/build/80754af9/python_1540319457073/work/Objects/abstract.c:2261
> #5  0x00007ffff2d296b6 in sipWrapInstance () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/sip.so
> #6  0x00007ffff2d1cafb in sip_api_convert_from_type () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/sip.so
> #7  0x00007ffff643e10b in Chimera::toPyObject(void*) const () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/QtCore.so
> #8  0x00007ffff643d8e3 in Chimera::toPyObject(QVariant const&) const
> () from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/QtCore.so
> #9  0x00007ffff643dba8 in Chimera::toAnyPyObject(QVariant const&) ()
> from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/QtCore.so
> #10 0x00007ffff2d25aa9 in sip_api_convert_from_new_type () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/sip.so
> #11 0x00007ffff2d260e8 in buildObject () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/sip.so
> #12 0x00007ffff2d26a09 in call_method () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/sip.so
> #13 0x00007ffff2d26c0c in sip_api_call_method () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/sip.so
> #14 0x00007fffeb73d10c in sipVH_QtWidgets_154(PyGILState_STATE, void
> (*)(_sipSimpleWrapper*, PyGILState_STATE), _sipSimpleWrapper*,
> _object*, QGraphicsItem::GraphicsItemChange, QVariant const&) () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/QtWidgets.so
> #15 0x00007fffeb848ddc in
> sipQGraphicsRectItem::itemChange(QGraphicsItem::GraphicsItemChange,
> QVariant const&) ()
>   from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/QtWidgets.so
> #16 0x00007fffeb0f3034 in
> QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem*, QVariant
> const*, QVariant const*) ()
>   from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
> #17 0x00007fffeb0f3717 in QGraphicsItem::setParentItem(QGraphicsItem*) ()
>   from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
> #18 0x00007fffeb0f3ca9 in
> QAbstractGraphicsShapeItem::QAbstractGraphicsShapeItem(QAbstractGraphicsShapeItemPrivate&,
> QGraphicsItem*) ()
>   from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
> #19 0x00007fffeb0f40bd in
> QGraphicsRectItem::QGraphicsRectItem(QGraphicsItem*) ()
>   from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/Qt/lib/libQt5Widgets.so.5
> #20 0x00007fffeb752409 in
> sipQGraphicsRectItem::sipQGraphicsRectItem(QGraphicsItem*) ()
>   from /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/QtWidgets.so
> #21 0x00007fffeb75270a in init_type_QGraphicsRectItem () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/QtWidgets.so
> #22 0x00007ffff2d27c94 in sipSimpleWrapper_init () from
> /usr/local/miniconda3/lib/python3.6/site-packages/PyQt5/sip.so
> #23 0x00005555556f0f57 in type_call () at
> /tmp/build/80754af9/python_1540319457073/work/Objects/typeobject.c:915
> #24 0x00005555556675bb in _PyObject_FastCallDict () at
> /tmp/build/80754af9/python_1540319457073/work/Objects/abstract.c:2331
> #25 0x00005555556eb2aa in _PyObject_FastCallKeywords () at
> /tmp/build/80754af9/python_1540319457073/work/Objects/abstract.c:2496
> #26 0x00005555556f0d6e in call_function () at
> /tmp/build/80754af9/python_1540319457073/work/Python/ceval.c:4861
> #27 0x00005555557144d8 in _PyEval_EvalFrameDefault () at
> /tmp/build/80754af9/python_1540319457073/work/Python/ceval.c:3351
> #28 0x00005555556ebad9 in _PyEval_EvalCodeWithName (qualname=0x0,
> name=<optimized out>, closure=0x0, kwdefs=0x0, defcount=0, defs=0x0,
>    kwstep=2, kwcount=<optimized out>, kwargs=0x0, kwnames=0x0,
> argcount=0, args=0x0, locals=0x7ffff7f422d0, globals=0x7ffff7f422d0,
>    _co=0x7ffff6ae25d0) at
> /tmp/build/80754af9/python_1540319457073/work/Python/ceval.c:4166
> #29 PyEval_EvalCodeEx () at
> /tmp/build/80754af9/python_1540319457073/work/Python/ceval.c:4187
> #30 0x00005555556ec87c in PyEval_EvalCode (co=co at entry=0x7ffff6ae25d0,
> globals=globals at entry=0x7ffff7f422d0,
> locals=locals at entry=0x7ffff7f422d0)
>    at /tmp/build/80754af9/python_1540319457073/work/Python/ceval.c:731
> #31 0x000055555576d074 in run_mod () at
> /tmp/build/80754af9/python_1540319457073/work/Python/pythonrun.c:1025
> #32 0x000055555576d471 in PyRun_FileExFlags () at
> /tmp/build/80754af9/python_1540319457073/work/Python/pythonrun.c:978
> #33 0x000055555576d673 in PyRun_SimpleFileExFlags () at
> /tmp/build/80754af9/python_1540319457073/work/Python/pythonrun.c:419
> #34 0x000055555576d77d in PyRun_AnyFileExFlags () at
> /tmp/build/80754af9/python_1540319457073/work/Python/pythonrun.c:81
> #35 0x00005555557711b0 in run_file (p_cf=0x7fffffffe8cc,
> filename=0x5555558ac690 L"foo.py", fp=0x55555593c900)
>    at /tmp/build/80754af9/python_1540319457073/work/Modules/main.c:340
> #36 Py_Main () at
> /tmp/build/80754af9/python_1540319457073/work/Modules/main.c:811
> #37 0x0000555555638b4e in main () at
> /tmp/build/80754af9/python_1540319457073/work/Programs/python.c:69
> #38 0x00007ffff77e6b97 in __libc_start_main (main=0x555555638a60
> <main>, argc=2, argv=0x7fffffffead8, init=<optimized out>,
>    fini=<optimized out>, rtld_fini=<optimized out>,
> stack_end=0x7fffffffeac8) at ../csu/libc-start.c:310
> #39 0x000055555571a1a8 in _start () at ../sysdeps/x86_64/elf/start.S:103
>
>
> *** LLDB backtrace on an up-to-date Mac ***
>
> * thread #1, queue = 'com.apple.main-thread', stop reason =
> EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
>  * frame #0: 0x0000000109908c41
> QtRemoteObjects.so`sipSubClass_QRemoteObjectNode(void**) + 49
>    frame #1: 0x00000001022ea61d sip.so`convertSubClass + 152
>    frame #2: 0x00000001022ea4dd sip.so`sip_api_convert_from_type + 225
>    frame #3: 0x0000000101b60ef8 QtCore.so`Chimera::toPyObject(void*) const + 760
>    frame #4: 0x0000000101b60b92 QtCore.so`Chimera::toPyObject(QVariant
> const&) const + 338
>    frame #5: 0x0000000101b61139
> QtCore.so`Chimera::toAnyPyObject(QVariant const&) + 217
>    frame #6: 0x00000001022ecec8 sip.so`sip_api_convert_from_new_type + 160
>    frame #7: 0x00000001022f0b8f sip.so`buildObject + 2598
>    frame #8: 0x00000001022f0edc sip.so`call_method + 58
>    frame #9: 0x00000001022ecc1c sip.so`sip_api_call_method + 136
>    frame #10: 0x00000001078e2c3a
> QtWidgets.so`sipQGraphicsRectItem::itemChange(QGraphicsItem::GraphicsItemChange,
> QVariant const&) + 202
>    frame #11: 0x000000010806e540
> QtWidgets`QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem*,
> QVariant const*, QVariant const*) + 1456
>    frame #12: 0x0000000108071691
> QtWidgets`QGraphicsItem::setParentItem(QGraphicsItem*) + 513
>    frame #13: 0x0000000108081f2d
> QtWidgets`QGraphicsRectItem::QGraphicsRectItem(QGraphicsItem*) + 541
>    frame #14: 0x00000001078e3cbb
> QtWidgets.so`init_type_QGraphicsRectItem(_sipSimpleWrapper*, _object*,
> _object*, _object**, _object**, _object**) + 139
>    frame #15: 0x00000001022eaaa9 sip.so`sipSimpleWrapper_init + 175
>    frame #16: 0x00000001000b5af1 python`type_call + 241
>    frame #17: 0x0000000100008ef1 python`_PyObject_FastCallDict + 177
>    frame #18: 0x0000000100011137 python`_PyObject_FastCallKeywords + 327
>    frame #19: 0x000000010015f718 python`call_function + 392
>    frame #20: 0x000000010015d225 python`_PyEval_EvalFrameDefault + 47013
>    frame #21: 0x00000001001508c9 python`_PyEval_EvalCodeWithName + 425
>    frame #22: 0x00000001001a955c python`PyRun_FileExFlags + 252
>    frame #23: 0x00000001001a8a34 python`PyRun_SimpleFileExFlags + 372
>    frame #24: 0x00000001001cf7c6 python`Py_Main + 3734
>    frame #25: 0x0000000100000f59 python`main + 313
>    frame #26: 0x00007fff79e603d5 libdyld.dylib`start + 1
>    frame #27: 0x00007fff79e603d5 libdyld.dylib`start + 1


More information about the PyQt mailing list