GUI: fix use-after-free crash in Qt accessibility cache after host work-area changes - #793
GUI: fix use-after-free crash in Qt accessibility cache after host work-area changes#793baizhenyu wants to merge 1 commit into
Conversation
…og 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 VirtualBox#696 Signed-off-by: Tim Bai <tim.baizhenyu@gmail.com>
|
Thank you for your pull request and welcome to our community! To contribute, please sign the Oracle Contributor Agreement (OCA).
To sign the OCA, please create an Oracle account and sign the OCA in Oracle's Contributor Agreement Application. When signing the OCA, please provide your GitHub username. After signing the OCA and getting an OCA approval from Oracle, this PR will be automatically updated. If you are an Oracle employee, please make sure that you are a member of the main Oracle GitHub organization, and your membership in this organization is public. |
|
So disconnecting all connections of a soon-to-be deleted object causes QAccessibleCache to refer to a dangling pointer? Is this a documented behavior. I am not convinced that this is the main cause of the crash. Unfortunately I cannot produce it myself. |
Summary
Fixes the host-side GUI segfault tracked in #696 (also reported downstream as Ubuntu bug #2127740 and on Fedora discussion): both
VirtualBoxandVirtualBoxVMcrash inlibQt6Widgetsafter host display/work-area transitions (screen lock/unlock on GNOME Wayland, external monitor power-off, suspend/resume), leaving running VMs in an "Aborted" state.Root cause
UIDesktopWidgetWatchdogrecreates its per-screenUIInvisibleWindowworkers on every host work-area change, dropping the old worker with a wildcardQObject::disconnect()followed bydeleteLater()(three sites inUIDesktopWidgetWatchdog.cpp).A wildcard disconnect severs all of the widget's signal connections — including the
destroyed()connection that Qt's internalQAccessibleCacheuses to invalidate cached accessible interfaces (the VirtualBox GUI installsQAccessiblefactories unconditionally, so interfaces get created and cached even when no screen reader is active). Qt 6.10 added a diagnostic for exactly this hazard, which appears in the journal immediately before every crash:With that connection severed, the cache keeps a stale
QAccessibleWidgetfor the deleted worker. Because workers are recreated repeatedly, a subsequent allocation reuses the freed heap address, the cache lookup returns the stale interface, andQAccessibleWidget::text(QAccessible::Name)invokesQWidget::accessibleName()on a null widget pointer.accessibleName()reads the object'sd_ptrat offset 8, producing the reported signature:Offset
0x1f8024in Qt 6.10.2'slibQt6Widgetsis the first dereferencing instruction ofQWidget::accessibleName()(mov 0x8(%rsi),%rcx), confirming the null-thiscall. The mechanism is version-independent (QAccessibleCachehas relied ondestroyed()since early Qt 6), matching reports against Qt 6.9 as well; Qt 6.10 merely made the hazard visible via the warning.Fix
Scope the three disconnects to the watchdog itself (
pWorker->disconnect(this)). The only connection the watchdog makes on these workers issigHostScreenAvailableGeometryCalculated→ watchdog, so the change is behavior-preserving for VirtualBox while Qt-internal connections (accessibility cache, and any otherdestroyed()listeners) survive until the object is actually deleted and the cache invalidates normally.Validation
Reproduction (Ubuntu 26.04 GNOME/Wayland, VirtualBox 7.2.10, Qt 6.10.2): run a VM and cycle session lock/unlock (or power an external monitor off/on, or shut the VM down). Each work-area transition prints the wildcard-disconnect warning above, and after a few cycles
VirtualBoxand/orVirtualBoxVMsegfault with theipoffset resolving toQWidget::accessibleName().The root-cause chain was verified at the binary level (crash offset disassembly,
QAccessibleCache::insert'sdestroyed()connection, the Qt 6.10 warning source). I have not yet rebuilt the GUI to exercise the patched path; expected observable results are that theUIInvisibleWindowwildcard-disconnect warnings disappear and repeated display transitions no longer crash, while work-area recalculation keeps functioning (GUI: UIDesktopWidgetWatchdog::sltHandleHostScreenAvailableGeometryCalculatedlog lines still appear).Fixes #696