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

            Bug ID: 527269
           Summary: Crash in QPA EGLPlatformContext::swapBuffers() when a
                    script resizes an internal window (e.g. the quick tile
                    outline) from windowAdded
    Classification: Plasma
           Product: kwin
      Version First 6.8.0
       Reported In:
          Platform: CachyOS
                OS: Linux
            Status: REPORTED
          Severity: crash
          Priority: NOR
         Component: wayland-generic
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: ---

SUMMARY
kwin_wayland 6.8.0 crashes with SIGSEGV in
KWin::QPA::EGLPlatformContext::swapBuffers(), at
m_currentSlot->releasePoint() after internalWindow->present()
(eglplatformcontext.cpp:171):
m_currentSlot is null there.

present() re-enters swapBuffers(). The first present() of an internal window
calls
markAsMapped() -> Workspace::addInternalWindow(), which emits windowAdded. If a
script handles
windowAdded by setting frameGeometry, InternalWindow::moveResizeInternal()
sends a synchronous
expose event for the new size, Qt Quick renders the same window again, and the
nested
swapBuffers() presents and resets m_currentSlot. When the outer present()
returns, the outer
swapBuffers() dereferences the null slot.

The quick tile outline hits this because its internal window is seen by scripts
as
normalWindow == true and managed == true, so a script that filters windowAdded
on those still
handles it.

The dereference after present() came with 934eeb315f ("vulkan: make the
swapchain more
generic", first in 6.7.90, so in 6.8.0). Before that, swapBuffers() only reset
m_current
after present(), so a nested frame did not crash it. This matches what I see: I
went from
6.7.5 to 6.8.0 on 2026-10-15; there is no kwin_wayland crash in my journal from
2026-09-07
(oldest entry) to that upgrade, then two crashes on 2026-10-17, with the same
script
installed since July.

STEPS TO REPRODUCE
1. Run a KWin script that changes the geometry of new normal windows, for
example:

   workspace.windowAdded.connect(function (w) {
       if (!w.normalWindow || !w.managed || w.move || w.resize) return;
       var g = w.frameGeometry;
       w.frameGeometry = {x: g.x + 10, y: g.y + 10, width: g.width - 20,
height: g.height - 20};
   });

2. Drag a window to the top screen edge, so that the maximize outline is shown.

I reproduced it with exactly this script in the attached integration test
(virtual backend),
not in a live session. My live session runs a personal script that does this
(on windowAdded
and outputChanged) for normal windows that appear on one particular output, my
TV, moving
them to the other output. I believe that is what triggered both crashes, but I
cannot
confirm on which output the outline was at the time.

OBSERVED RESULT
kwin_wayland crashes ("segfault at 10", the offset of m_releasePoint in
SwapchainSlot). Right
before the crash the journal has:
  Attempted to call beginFrame() within a still active frame; ignored

EXPECTED RESULT
No crash.

SOFTWARE/OS VERSIONS
Linux: CachyOS, kernel 7.2.9
KDE Plasma Version: 6.8.0 (kwin 6.8.0-1.1)
KDE Frameworks Version: 6.31.0
Qt Version: 6.12.0
GPU: NVIDIA RTX 5090, driver 615.78.08 (the test reproduces it with the virtual
backend)

ADDITIONAL INFORMATION
backtraces.txt has:
- the backtrace of the 11:30 crash from the distro package (the QPA plugin is
linked into
  kwin_wayland without symbols; I identified frames #1 and #12 by disassembly
as
  EGLPlatformContext::swapBuffers and QPA::Window::map). The 02:41 crash has
identical frames
  #0-#19; there setElectricBorderMaximizing() was called from the 250 ms
  m_electricMaximizingDelay timer (an edge shared by two outputs) instead of
from pointer motion;
- from a debug build of v6.8.0 running the test under gdb: the nested
swapBuffers() call,
  which goes swapBuffers:164 -> InternalWindow::present -> markAsMapped ->
  Workspace::addInternalWindow -> WorkspaceWrapper::windowAdded -> (script)
frameGeometry ->
  Window::moveResize -> InternalWindow::moveResizeInternal:261 ->
handleExposeEvent ->
  Qt Quick render -> swapBuffers; then the crash at eglplatformcontext.cpp:171.

The attached patch takes m_currentSlot and m_current out of the context before
calling
present(). The nested frame then finds no current slot and is dropped, and a
later frame
presents the new geometry. It adds autotests/integration/outline_drag_test.cpp
(testOutlineDrag): drag a window to the top edge 20 times without and with such
a script, and
check that the outline is mapped each time and ends up with the geometry the
script asked for.

Results on master c08459bfa8 (and the same on v6.8.0):
- testOutlineDrag without the fix: SIGSEGV at address 0x10 in the script case;
- with the fix: both test functions pass (5 runs on v6.8.0, 3 on master), and
  testInternalWindow and testQuickTiling still pass (Totals: 18 and 144
passed).

Not needed for this crash, but I noticed it while reading the code: since
4adcd2db1a the fence
fd is moved into InternalWindowFrame::syncFd by the first
fence.takeFileDescriptor(), so the
second one, passed to releasePoint()->addReleaseFence(), is invalid and
addReleaseFence()
returns early. The patch keeps that line as it is. Mentioning it in case it is
not intended.

Another option would be to keep helper windows like the outline away from
scripts' windowAdded
handlers, or to emit windowAdded after present() returns. I went for the
smallest change, in
swapBuffers().

Workaround for script authors until this is fixed: skip windows with `outline
== true` in the
windowAdded handler. With that check added to the script above, the outline is
left alone and
the test shows no nested frame.

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

Reply via email to