Created attachment 190251 [details] Patch attached. plasmashell crashes at startup on multi-monitor setups. The crash is 100% reproducible on cold login and affects at least 6.6.0 and 6.6.1. == Crash trace (Thread 1) == #4 Plasma::Containment::lastScreen() const [libPlasma.so.7] #5 ShellCorona::isScreenUiReady(int) #6 ShellCorona::checkAllDesktopsUiReady() #7 void doActivate<false>(QObject*, int, void**) [libQt6Core] #8 Plasma::Containment::uiReadyChanged(bool) #9 Plasma::AppletPrivate::setUiReady() #10 Plasma::Applet::flushPendingConstraintsEvents() #11 QObject::event(QEvent*) #12 ... QTimerInfoList::activateTimers() ... == Root cause == m_waitingPanels holds raw Plasma::Containment* pointers. Between load() populating the list and createWaitingPanels() running (after a 250 ms timer), those pointers have no lifetime guard. checkAllDesktopsUiReady() can fire before the timer via uiReadyChanged from a desktop applet. It calls isScreenUiReady(), which iterates m_waitingPanels and calls cont->lastScreen() on each entry. If any containment was freed in the meantime, the qobject_cast<Containment*> (parent()) inside lastScreen() dereferences a corrupted vtable → SIGSEGV (observed PC = 0x7). The old early guard (connecting QObject::destroyed in the constructor for panel-type containments) was removed in commit 086ff5d8, which created this regression. That commit reasoned that createWaitingPanels() already connects destroyed — but it does so only after successfully creating a PanelView, which is too late. Commit 00bd19ec introduced the vulnerable window; 086ff5d8 removed the guard that covered it. == Fix == Change m_waitingPanels from QList<Plasma::Containment*> to QList<QPointer<Plasma::Containment>>. QPointer auto-nulls when the object is destroyed, eliminating the dangling-pointer window entirely: - createWaitingPanels() calls removeAll(nullptr) at entry to purge any freed containments, and no longer needs to connect destroyed. - panelContainmentDestroyed() no longer needs a m_waitingPanels branch; it returns early if the containment has no panel view. - isScreenUiReady() null-checks each QPointer before dereferencing. Patch attached. == Environment == - KDE Plasma 6.6.1, NixOS, dual-monitor (Wayland) - Reproducible on every cold login - GPU: irrelevant (crash is in Qt signal dispatch, not rendering)
Created attachment 190252 [details] v2: add checkAllDesktopsUiReady() hunk and fix panelContainmentDestroyed() guard
Updated patch attached (v2). After further review, the fix has one additional change vs. the original description: - checkAllDesktopsUiReady() also calls m_waitingPanels.removeAll(nullptr) before the isEmpty() check, preventing a spurious timer start if all remaining entries were already freed. - panelContainmentDestroyed() replaces the old m_waitingPanels branch with if (!m_panelViews.contains(cont)) return; — keeping the early-return structure but guarding on m_panelViews instead. This correctly handles both the startup case (no PanelView yet → QPointer handles cleanup) and the runtime case (PanelView exists → fall through to cleanup it). The original description's wording of the second bullet was slightly misleading. The "destroyed" connect in createWaitingPanels() is also kept (not removed) — it is still needed for runtime m_panelViews cleanup. All four changed sites: shellcorona.h, createWaitingPanels(), checkAllDesktopsUiReady(), panelContainmentDestroyed(), isScreenUiReady().
A possibly relevant merge request was started @ https://invent.kde.org/plasma/plasma-workspace/-/merge_requests/6361
Created attachment 190253 [details] v2.1: fix hunk headers — correct panelContainmentDestroyed() line counts and add missing checkAllDesktopsUiReady() hunk
Nice, is this the same as Bug 477941?
(In reply to Nate Graham from comment #6) > Nice, is this the same as Bug 477941? I'm not sure, but by the description and backtrace of Bug 477941 it looks like a different issue. The backtrace shows a Qt assert failure on qobject_cast<PanelView*>(panel) inside a lambda in shellcorona.cpp, triggered when a PanelView is destroyed while that lambda is still pending. This bug (516937) crashes in isScreenUiReady() on a freed Plasma::Containment* in m_waitingPanels, before any PanelView is created.
Git commit 0cc554a33cd496569f0fb1c9ba3a3f9261e627b7 by Dobry Nikolov. Committed on 03/03/2026 at 15:05. Pushed by ngraham into branch 'master'. shell: restore early destroyed guard for panel containments Commit 086ff5d8139eed36c25b3a7417f0eed8ec223739 removed a containmentAdded lambda from the constructor that connected QObject::destroyed -> panelContainmentDestroyed for every panel containment as soon as it was created. It reasoned that createWaitingPanels() already connects this signal, making it redundant. However, createWaitingPanels() only connects destroyed *after* successfully creating a PanelView for the containment. If a containment is freed before that point (e.g. its plugin is missing and libplasma issues deleteLater()), panelContainmentDestroyed is never connected, m_waitingPanels retains a dangling pointer, and the next dereference (e.g. in isScreenUiReady()) crashes with SIGSEGV. Restore the constructor connect so the guard is in place from the moment any panel containment is added, with no window of exposure. The connect inside createWaitingPanels() is now truly redundant and is removed. M +6 -2 shell/shellcorona.cpp https://invent.kde.org/plasma/plasma-workspace/-/commit/0cc554a33cd496569f0fb1c9ba3a3f9261e627b7
Git commit 7308d3153f5e9ee8607f89005aa05c2fd4f07dd6 by Dobry Nikolov. Committed on 03/03/2026 at 15:05. Pushed by ngraham into branch 'master'. shellcorona: use QPointer for m_waitingPanels to futureproof against dangling pointers Change m_waitingPanels to QList<QPointer<Plasma::Containment>> so that any freed containment pointer auto-nulls in place rather than dangling. - createWaitingPanels(): call removeAll(nullptr) at entry to discard any auto-nulled entries before iterating. - checkAllDesktopsUiReady(): same removeAll(nullptr) before isEmpty() to avoid spuriously starting the timer for nulled entries. - panelContainmentDestroyed(): the m_waitingPanels branch is now redundant since QPointer auto-nulls it; guard on m_panelViews instead to keep the PanelView cleanup path intact. - isScreenUiReady(): null-check each QPointer before dereferencing. M +7 -6 shell/shellcorona.cpp M +1 -1 shell/shellcorona.h https://invent.kde.org/plasma/plasma-workspace/-/commit/7308d3153f5e9ee8607f89005aa05c2fd4f07dd6
Git commit 8b4500d7acc699ec72bbac4aeed30db3e168a76d by Nate Graham, on behalf of Dobry Nikolov. Committed on 03/03/2026 at 20:04. Pushed by ngraham into branch 'Plasma/6.6'. shellcorona: use QPointer for m_waitingPanels to futureproof against dangling pointers Change m_waitingPanels to QList<QPointer<Plasma::Containment>> so that any freed containment pointer auto-nulls in place rather than dangling. - createWaitingPanels(): call removeAll(nullptr) at entry to discard any auto-nulled entries before iterating. - checkAllDesktopsUiReady(): same removeAll(nullptr) before isEmpty() to avoid spuriously starting the timer for nulled entries. - panelContainmentDestroyed(): the m_waitingPanels branch is now redundant since QPointer auto-nulls it; guard on m_panelViews instead to keep the PanelView cleanup path intact. - isScreenUiReady(): null-check each QPointer before dereferencing. (cherry picked from commit 7308d3153f5e9ee8607f89005aa05c2fd4f07dd6) M +7 -6 shell/shellcorona.cpp M +1 -1 shell/shellcorona.h https://invent.kde.org/plasma/plasma-workspace/-/commit/8b4500d7acc699ec72bbac4aeed30db3e168a76d
Git commit db08d2f00951ad7a0ad604fb03cf5d0b6e2c057b by Nate Graham, on behalf of Dobry Nikolov. Committed on 03/03/2026 at 20:03. Pushed by ngraham into branch 'Plasma/6.6'. shell: restore early destroyed guard for panel containments Commit 086ff5d8139eed36c25b3a7417f0eed8ec223739 removed a containmentAdded lambda from the constructor that connected QObject::destroyed -> panelContainmentDestroyed for every panel containment as soon as it was created. It reasoned that createWaitingPanels() already connects this signal, making it redundant. However, createWaitingPanels() only connects destroyed *after* successfully creating a PanelView for the containment. If a containment is freed before that point (e.g. its plugin is missing and libplasma issues deleteLater()), panelContainmentDestroyed is never connected, m_waitingPanels retains a dangling pointer, and the next dereference (e.g. in isScreenUiReady()) crashes with SIGSEGV. Restore the constructor connect so the guard is in place from the moment any panel containment is added, with no window of exposure. The connect inside createWaitingPanels() is now truly redundant and is removed. (cherry picked from commit 0cc554a33cd496569f0fb1c9ba3a3f9261e627b7) M +6 -2 shell/shellcorona.cpp https://invent.kde.org/plasma/plasma-workspace/-/commit/db08d2f00951ad7a0ad604fb03cf5d0b6e2c057b