Skip to content

GUI: fix use-after-free crash in Qt accessibility cache after host work-area changes - #793

Open
baizhenyu wants to merge 1 commit into
VirtualBox:mainfrom
baizhenyu:696-a11y-cache-use-after-free
Open

GUI: fix use-after-free crash in Qt accessibility cache after host work-area changes#793
baizhenyu wants to merge 1 commit into
VirtualBox:mainfrom
baizhenyu:696-a11y-cache-use-after-free

Conversation

@baizhenyu

Copy link
Copy Markdown

Summary

Fixes the host-side GUI segfault tracked in #696 (also reported downstream as Ubuntu bug #2127740 and on Fedora discussion): both VirtualBox and VirtualBoxVM crash in libQt6Widgets after 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

UIDesktopWidgetWatchdog recreates its per-screen UIInvisibleWindow workers on every host work-area change, dropping the old worker with a wildcard QObject::disconnect() followed by deleteLater() (three sites in UIDesktopWidgetWatchdog.cpp).

A wildcard disconnect severs all of the widget's signal connections — including the destroyed() connection that Qt's internal QAccessibleCache uses to invalidate cached accessible interfaces (the VirtualBox GUI installs QAccessible factories 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:

QObject::disconnect: wildcard call disconnects from destroyed signal of UIInvisibleWindow::unnamed

With that connection severed, the cache keeps a stale QAccessibleWidget for the deleted worker. Because workers are recreated repeatedly, a subsequent allocation reuses the freed heap address, the cache lookup returns the stale interface, and QAccessibleWidget::text(QAccessible::Name) invokes QWidget::accessibleName() on a null widget pointer. accessibleName() reads the object's d_ptr at offset 8, producing the reported signature:

VirtualBox[774940]: segfault at 8 ip ... error 4 in libQt6Widgets.so.6.10.2[1f8024,...+4c8000]

Offset 0x1f8024 in Qt 6.10.2's libQt6Widgets is the first dereferencing instruction of QWidget::accessibleName() (mov 0x8(%rsi),%rcx), confirming the null-this call. The mechanism is version-independent (QAccessibleCache has relied on destroyed() 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 is sigHostScreenAvailableGeometryCalculated → watchdog, so the change is behavior-preserving for VirtualBox while Qt-internal connections (accessibility cache, and any other destroyed() 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 VirtualBox and/or VirtualBoxVM segfault with the ip offset resolving to QWidget::accessibleName().

The root-cause chain was verified at the binary level (crash offset disassembly, QAccessibleCache::insert's destroyed() connection, the Qt 6.10 warning source). I have not yet rebuilt the GUI to exercise the patched path; expected observable results are that the UIInvisibleWindow wildcard-disconnect warnings disappear and repeated display transitions no longer crash, while work-area recalculation keeps functioning (GUI: UIDesktopWidgetWatchdog::sltHandleHostScreenAvailableGeometryCalculated log lines still appear).

Fixes #696

…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>
@oracle-contributor-agreement

Copy link
Copy Markdown

Thank you for your pull request and welcome to our community! To contribute, please sign the Oracle Contributor Agreement (OCA).
The following contributors of this PR have not signed the 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.

@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Required At least one contributor does not have an approved Oracle Contributor Agreement. label Jul 29, 2026
@baserkan

Copy link
Copy Markdown
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Required At least one contributor does not have an approved Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: VirtualBoxVM crashes in libQt6Widgets.so.6.10.2 after GNOME Wayland lock/unlock, causing VM to become "Aborted"

2 participants