From a765537c133227616ce60dd7b59a14b406f2542c Mon Sep 17 00:00:00 2001 From: Tim Bai Date: Wed, 29 Jul 2026 00:54:48 -0400 Subject: [PATCH] GUI: don't sever accessibility-cache connections when dropping watchdog workers UIDesktopWidgetWatchdog used wildcard QObject::disconnect() on its UIInvisibleWindow workers before deleteLater(). A wildcard disconnect also severs the destroyed() connection that Qt's QAccessibleCache uses to invalidate cached interfaces, so the cache keeps a stale QAccessibleWidget for the deleted worker. Workers are recreated on every host work-area change (screen lock/unlock, monitor power-off, suspend/resume), so a later cache hit on a recycled heap address returns the stale interface and QAccessibleWidget::text() calls QWidget::accessibleName() on a null widget pointer: segfault at 8 ip ... error 4 in libQt6Widgets.so.6.10.2[1f8024,...] Qt 6.10 warns about exactly this hazard right before each crash: QObject::disconnect: wildcard call disconnects from destroyed signal of UIInvisibleWindow::unnamed Scope the disconnects to the watchdog itself. The only connection made on these workers is signal-to-watchdog, so behavior is otherwise unchanged while Qt-internal connections survive until actual deletion. Fixes #696 Signed-off-by: Tim Bai --- .../src/globals/UIDesktopWidgetWatchdog.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.cpp b/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.cpp index a6f41f0a279b..4ec8fea1896b 100644 --- a/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.cpp +++ b/src/VBox/Frontends/VirtualBox/src/globals/UIDesktopWidgetWatchdog.cpp @@ -919,8 +919,11 @@ void UIDesktopWidgetWatchdog::sltHandleHostScreenAvailableGeometryCalculated(int iHostScreenIndex, availableGeometry.x(), availableGeometry.y(), availableGeometry.width(), availableGeometry.height())); - /* Forget finished worker: */ - pWorker->disconnect(); + /* Forget finished worker; disconnect only our own connections, a wildcard + * disconnect would also sever the destroyed() connection Qt's accessibility + * cache relies upon to invalidate cached interfaces, causing a use-after-free + * in QAccessibleWidget::text later on: */ + pWorker->disconnect(this); pWorker->deleteLater(); m_availableGeometryWorkers[iHostScreenIndex] = 0; @@ -1072,7 +1075,9 @@ void UIDesktopWidgetWatchdog::updateHostScreenAvailableGeometry(int iHostScreenI QWidget *pOldWorker = m_availableGeometryWorkers.value(iHostScreenIndex); if (pOldWorker) { - pOldWorker->disconnect(); + /* Disconnect only our own connections, see + * sltHandleHostScreenAvailableGeometryCalculated for details: */ + pOldWorker->disconnect(this); pOldWorker->deleteLater(); } m_availableGeometryWorkers[iHostScreenIndex] = pWorker; @@ -1102,7 +1107,9 @@ void UIDesktopWidgetWatchdog::cleanupExistingWorkers() { if (pWorker) { - pWorker->disconnect(); + /* Disconnect only our own connections, see + * sltHandleHostScreenAvailableGeometryCalculated for details: */ + pWorker->disconnect(this); pWorker->deleteLater(); } }