https://bugs.kde.org/show_bug.cgi?id=527846

--- Comment #1 from [email protected] ---
Source-level analysis (from reading libplasma master:
src/plasmaquick/appletquickitem.cpp).

Note first that frames #4 and #5 of the backtrace are inside Qt itself:

  #4  QQuickWindow::maybeUpdate()          libQt6Quick.so.6
  #5  QQuickItemPrivate::derefWindow()     libQt6Quick.so.6

So libplasma is only the trigger; the use-after-free happens in Qt Quick. But
the teardown
order in libplasma is what makes it reachable, and I think it is worth
recording here.

The AppletQuickItem is destroyed from inside applet teardown:

  QObject::connect(applet, &Plasma::Applet::appletDeleted, item, [item,
thisApplet = applet](Plasma::Applet *applet) {
      if (thisApplet == applet) {
          delete item;
          AppletQuickItemPrivate::s_itemsForApplet.remove(applet);
      }
  });

and its destructor deletes the representation items:

  AppletQuickItem::~AppletQuickItem()
  {
      AppletQuickItemPrivate::s_itemsForApplet.remove(d->applet);

      // Here the order is important
      delete d->compactRepresentationItem;
      delete d->fullRepresentationItem;
      delete d->compactRepresentationExpanderItem;
      delete d;
  }

The crash trace shows this exact nesting:

  #31 Plasma::Containment::~Containment()
  #30 QObject::~QObject() / QObjectPrivate::deleteChildren()
  #27 Plasma::Applet::~Applet()
  #26 Plasma::Applet::appletDeleted(...)
  #21 PlasmaQuick::AppletQuickItem::~AppletQuickItem()
  #7  QQuickItem::~QQuickItem()
  #6  QQuickItem::setParentItem(nullptr)
  #5  QQuickItemPrivate::derefWindow()
  #4  QQuickWindow::maybeUpdate()

i.e. ~Containment() deletes its child applets, each one deletes its
AppletQuickItem, and
that in turn deletes representation items whose QQuickWindow has already been
destroyed.
~QQuickItem() calls setParentItem(nullptr) -> derefWindow() -> maybeUpdate() on
the dead
window, hence SIGSEGV.

Why opening the system tray first matters: compactRepresentationExpanderItem is
the item
that lives inside the applet's popup window. That popup window is torn down
before
~Containment() runs, so the expander item is left holding a dangling window
reference. If
the popup was never opened, that item was never created and the crash does not
happen.
This is consistent with bug 417552 ("crashes on teardown when system tray was
expanded",
FIXED in 5.19 by D27401), so this may be a regression of that class rather than
a new bug.

Relationship to 449981: that report is the setEffectiveVisibleRecur variant and
is marked
"Version Fixed In: 6.0". This one reproduces on 6.6.6 and is the destructor
variant
(derefWindow from ~QQuickItem), so I reported it separately.

Possible directions, for whoever triages this:
  1. Qt side (probably the correct fix): make QQuickItemPrivate::derefWindow()
/
     QQuickWindow::maybeUpdate() tolerate an item whose window is already
destroyed, rather
     than dereferencing a stale pointer.
  2. libplasma side: destroy the representation items while the view/window is
still alive,
     or avoid emitting appletDeleted for applets being deleted as part of a
Containment that
     no longer has a view. A purely defensive KDE-side fix is awkward because
clearing the
     item's private windowRef is not possible through public Qt API.

Reproduction is 100% here, twice back-to-back with identical frame offsets, so
I am happy
to test a patch and report back if someone wants to try one.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to