PySide6 crash: NULL deref in SignalManager::retrieveMetaObject via stale QTableWidgetItem wrapper (address reuse) — 6.10.2 and 6.11.1
-
Environment: Windows 11, Python 3.13.11, PySide6 6.10.2 and 6.11.1 (identical behavior on both), QtWebEngine in use.
Summary: A large PySide6 application hits a recurring, sporadic access violation (0xC0000005). Several independent first-chance full dumps (WinDbg/cdb) show an identical signature.
Crash signature:
0xC0000005, read at address 0x0 pyside6_abi3!PySide::SignalManager::retrieveMetaObject+0x24 mov rcx, qword ptr [rax] ; rax = 0PySide::retrieveMetaObject(obj) returns NULL and the result is dereferenced without a null check at +0x24.
Root cause (from the dumps): the object passed in is a STALE QTableWidgetItem Python wrapper living at a reused address. QTableWidgetItem is not a QObject, so retrieveMetaObject legitimately returns NULL — but it is dereferenced unconditionally, so the process crashes. The wrapper has refcnt 1 and an empty instance dict, and a full-heap scan of the dump finds NO Python reference to it — i.e. the only holder is shiboken's wrapper cache (BindingManager). The C++ QTableWidgetItem was freed and its address later reused by a fresh QObject/QTimer allocation; the pointer map then returns the dead wrapper for the new object. This looks like the same family as shiboken issue #74 / PYSIDE-38 (unreleased association -> address reuse -> wrong-type wrapper).
Trigger: it only reproduces on-screen, during re-entrant widget/item destruction while a QtWebEngine compositor is active (heavy page/table rebuilds right after opening a view). It is NOT reproducible headless — every normal destruction path (clear / clearContents / setRowCount / removeRow / takeItem / deleteLater / shiboken.delete) invalidates the item wrappers correctly.
Second, separate finding: shiboken6.invalidate() appears to be a NO-OP on 6.11.1 (and 6.10.2). Calling it on a QTableWidgetItem (or even a QObject) leaves shiboken6.isValid() True and does not remove the binding-map entry:
from PySide6 import QtWidgets import shiboken6 app = QtWidgets.QApplication([]) t = QtWidgets.QTableWidget(1, 1) t.setItem(0, 0, QtWidgets.QTableWidgetItem("x")) it = t.item(0, 0) shiboken6.invalidate(it) print(shiboken6.isValid(it), it.text()) # -> True x (expected: invalidated)This matters because the natural workaround — explicitly invalidating item wrappers before destroying the table/view — is unavailable if invalidate() does nothing.
Questions:
- Is this a known BindingManager wrapper-invalidation gap under re-entrant destruction with QtWebEngine active?
- Is shiboken6.invalidate() expected to be a no-op in 6.11.x, or is that itself a bug? If it is a no-op, what is the supported way to force-invalidate a QTableWidgetItem wrapper before its C++ object is destroyed?
- Should this be filed on bugreports.qt.io? I have several independent first-chance full dumps (~1.8 GB each) plus the cdb analyses available on request.
Thanks!
-
You can create a bug report ( see https://wiki.qt.io/Qt_for_Python/Reporting_Bugs ), but we would need a minimal example to reproduce. At the very least, a stack trace with function calls/source files in text form is needed to see why retrieveMetaObject() is called for a non-QObject. Generally, no guarantees can be given as to how WebEngine interacts with CPython, though.
invalidate() is an internal function that should not be used; it invalidates only for classes for which no generated "wrapper class" exists (in shiboken terms), that is, classes with no virtual functions.
-
Hi and welcome to devnet,
That's intriguing !
Would it be possible for you to provide a reproducer script ?
All in all, I think it's worth bringing it to the big tracker since you have proof of trigger. -
Thanks for the clarification.
I understand that
shiboken6.invalidate()is internal and that its behavior for classes with generated wrapper classes should not be treated as a separate public-API bug.I will prepare a standalone reproducer without application-specific code. The target is to demonstrate the stale
QTableWidgetItemwrapper and native-address reuse independently of the full application. I will also include the complete native call stack in text form, showing the callers leading toPySide::SignalManager::retrieveMetaObject().Once the reproducer and sanitized stack information are ready, I will file the issue in the Qt bug tracker and link the ticket here.