Bug 523515 - InputCapture session not torn down when the portal frontend crashes -> all keyboard/pointer input stays diverted (desktop input freeze)
Summary: InputCapture session not torn down when the portal frontend crashes -> all ke...
Status: RESOLVED FIXED
Alias: None
Product: xdg-desktop-portal-kde
Classification: Plasma
Component: general (other bugs)
Version First Reported In: 6.6.4
Platform: Other Linux
: NOR major
Target Milestone: ---
Assignee: Plasma Bugs List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2026-07-26 02:27 UTC by Marcus Renheim
Modified: 2026-08-06 21:03 UTC (History)
3 users (show)

See Also:
Latest Commit:
Version Fixed/Implemented In: 6.6.7
Sentry Crash Report:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Marcus Renheim 2026-07-26 02:27:08 UTC
If the xdg-desktop-portal *frontend* process dies while an InputCapture session
is active (an app such as Deskflow / Input Leap holding a screen-edge barrier),
the input-capture session is never torn down. KWin keeps diverting ALL pointer
and keyboard input into the now-ownerless EIS channel: the desktop keeps
rendering and the cursor still moves, but nothing accepts input -- no clicks, no
keyboard, no Alt+Tab, no taskbar. Recovery requires an out-of-band action (SSH,
or the built-in Meta+Shift+Escape disable shortcut if it still reaches KWin).

This is a fail-safe problem, not merely a crash follow-on: when the owner of an
InputCapture session goes away, the compositor must release capture rather than
divert all input into a dead session. It is a denial-of-input hazard.

STEPS TO REPRODUCE
1. Have an InputCapture consumer running with a screen-edge barrier (e.g.
   Deskflow linking this machine to another; the pointer crossing the shared
   edge engages capture).
2. Kill the portal frontend:
      systemctl --user kill -s ABRT xdg-desktop-portal.service
   (In the wild this happened via an unrelated portal crash during a PipeWire
   audio-stack restart.)
3. Move the pointer across the barrier to engage capture.

OBSERVED
All input is diverted; the desktop accepts no keyboard/pointer input while
continuing to render. Journal shows "Invalid session type for signal" and a
flood of "Error disabling capture on zone change".

EXPECTED
When the session's owner (the portal frontend / the app) is gone, the capture is
released and local input is restored.

ROOT CAUSE (source analysis of current master: xdg-desktop-portal-kde 449b1ae,
kwin 9ec0d6f)
The teardown chain that would release capture is only driven by an explicit
session Close(), which a crashed frontend can never send:

* xdg-desktop-portal-kde -- src/session.cpp: Session::close() (line 56) emits
  closed() and is only reached via the D-Bus method Session::Close() (line 77).
  The class registers NO QDBusServiceWatcher and does not react to its owning
  peer disconnecting.
* The KWin-side teardown is wired to that signal -- src/inputcapture.cpp:90-94:
  connect(session, &Session::closed, ... removeInputCapture ...). Without
  closed(), removeInputCapture is never sent to KWin.
* KWin -- src/plugins/eis/eisinputcapturemanager.cpp: it does have a fail-safe
  (QDBusServiceWatcher, lines 125-135) but it watches its immediate caller, the
  xdg-desktop-portal-kde backend (message().service(), line 193), which stays
  alive when the frontend dies. The input filter installed at barrierHit()
  (line 210) therefore stays installed.

So a frontend crash leaves the backend Session alive (no caller watcher), KWin is
never told to remove the capture, and input stays diverted.

The same codebase already uses the missing pattern elsewhere:
src/appchooser.cpp:124-125 creates a QDBusServiceWatcher(WatchForUnregistration)
on its caller and cleans up on serviceUnregistered.

SUGGESTED FIX DIRECTION (not a patch)
* Primary (xdg-desktop-portal-kde): tie the Session -- at least InputCapture
  sessions -- to the lifetime of the D-Bus peer that owns it (the frontend, i.e.
  the sender of CreateSession/Start). Watch it with QDBusServiceWatcher and call
  close() on unregistration, mirroring appchooser.cpp. This drives the existing
  correct teardown: closed() -> removeInputCapture -> KWin deactivate().
* Secondary (KWin, defense-in-depth): independently release an active capture if
  the session it serves is gone, rather than relying on the backend to always
  call removeInputCapture.

ENVIRONMENT
* KDE Plasma 6.6.4 / kwin_wayland 6.6.4 (Wayland)
* xdg-desktop-portal 1.21.1; xdg-desktop-portal-kde from Plasma 6.6.4
* Reproduced on the running system; root cause re-checked against current master
  (xdg-desktop-portal-kde 449b1ae, kwin 9ec0d6f).

NOTES
Related but distinct: the frontend crash that first triggered this on my machine
is a separate use-after-free in xdg-desktop-portal
(flatpak/xdg-desktop-portal PR #2074). This report is about the compositor /
portal backend NOT releasing capture once the owner is gone -- an independent
fail-safe defect that would strand input on any frontend death.
Comment 1 Marcus Renheim 2026-07-26 02:40:05 UTC Comment hidden (off-topic)
Comment 2 Marcus Renheim 2026-07-27 22:18:32 UTC Comment hidden (off-topic)
Comment 3 Marcus Renheim 2026-07-28 01:03:53 UTC
Update: I have now reproduced this and verified a fix at runtime on Plasma/KWin 6.6.4 (Wayland), with an InputCapture consumer (Deskflow) using a screen-edge barrier.

Reproduction (unpatched, stock 6.6.4): with an active InputCapture session, killing the xdg-desktop-portal frontend (systemctl --user kill -s ABRT xdg-desktop-portal.service) and then crossing the barrier freezes all input - the compositor keeps rendering (mouse-shake still triggers the shake-cursor effect) but no pointer or keyboard events get through, because KWin's capture filter is never removed. Recovery required restarting the backend/frontend out of band.

Fix: tie the InputCapture session to the lifetime of the requesting client. In InputCapturePortal, when the capture is set up, install a QDBusServiceWatcher(WatchForUnregistration) on the caller (the frontend, i.e. the sender of Start), parented to the Session so its lifetime matches the capture, and call session->close() on serviceUnregistered. That drives the existing Session::closed -> removeInputCapture teardown, which already works - it just never fired on the frontend-death path. (This mirrors the intent of the watcher in appchooser.cpp, but with the watcher owned by the session so it actually outlives the call.)

Result (patched): the same frontend kill now logs "Input capture requester .../portal... disappeared, releasing capture" and input remains fully responsive across the barrier - no freeze.

This was verified with the machine's own out-of-band auto-recovery disabled, so the patched watcher was the only thing that could release the capture. I'll open a merge request against xdg-desktop-portal-kde with this change and link it here.
Comment 4 TraceyC 2026-07-28 16:05:00 UTC
Thanks for the report, and the incoming patch!
Comment 5 Bug Janitor Service 2026-07-29 06:36:47 UTC
A possibly relevant merge request was started @ https://invent.kde.org/plasma/xdg-desktop-portal-kde/-/merge_requests/607
Comment 6 Marcus Renheim 2026-07-29 06:43:30 UTC
Merge request opened: https://invent.kde.org/plasma/xdg-desktop-portal-kde/-/merge_requests/607

It implements the fix described above: a QDBusServiceWatcher(WatchForUnregistration) on the requesting frontend (the sender of Start), parented to the InputCapture session so it outlives the call, calling session->close() on serviceUnregistered. That drives the existing Session::closed -> removeInputCapture teardown, so KWin releases the capture instead of diverting all input into an ownerless EIS channel. Rebased on current master; single commit.
Comment 7 Marcus Renheim 2026-08-04 11:20:09 UTC
Git commit e0cf8bf311a7baad8933797ec2fca0b94eb7d3d5 by Marcus Renheim.
Committed on 04/08/2026 at 10:08.
Pushed by davidre into branch 'master'.

session: release all sessions when the portal frontend disappears

Every session in this backend is created on behalf of the single portal
frontend (org.freedesktop.portal.Desktop). If it drops off the bus without
cleanly closing its sessions - for example if it crashes - nothing tears them
down. For an InputCapture session that means KWin keeps diverting all pointer
and keyboard input into the now ownerless EIS channel, so the desktop renders
normally but accepts no input until KWin is killed.

Watch the frontend centrally and close every session if it disappears, which
drives the existing Session::closed teardown for each. This covers all session
types rather than only input capture.

M  +10   -0    src/desktopportal.cpp
M  +2    -0    src/desktopportal.h
M  +13   -0    src/session.cpp
M  +1    -0    src/session.h

https://invent.kde.org/plasma/xdg-desktop-portal-kde/-/commit/e0cf8bf311a7baad8933797ec2fca0b94eb7d3d5
Comment 8 David Redondo 2026-08-04 13:48:34 UTC
Git commit b4d3f6cd01edd5302030f2b8292ed82315069f27 by David Redondo.
Committed on 04/08/2026 at 13:41.
Pushed by davidre into branch 'Plasma/6.7'.

session: release all sessions when the portal frontend disappears

Every session in this backend is created on behalf of the single portal
frontend (org.freedesktop.portal.Desktop). If it drops off the bus without
cleanly closing its sessions - for example if it crashes - nothing tears them
down. For an InputCapture session that means KWin keeps diverting all pointer
and keyboard input into the now ownerless EIS channel, so the desktop renders
normally but accepts no input until KWin is killed.

Watch the frontend centrally and close every session if it disappears, which
drives the existing Session::closed teardown for each. This covers all session
types rather than only input capture.


(cherry picked from commit e0cf8bf311a7baad8933797ec2fca0b94eb7d3d5)

Co-authored-by: Marcus Renheim <marcus@renheim.com>

M  +10   -0    src/desktopportal.cpp
M  +2    -0    src/desktopportal.h
M  +13   -0    src/session.cpp
M  +1    -0    src/session.h

https://invent.kde.org/plasma/xdg-desktop-portal-kde/-/commit/b4d3f6cd01edd5302030f2b8292ed82315069f27
Comment 9 David Redondo 2026-08-04 14:25:29 UTC
Git commit 867386a6e43d9b63fd23fca27d6f3f0b43a740b9 by David Redondo.
Committed on 04/08/2026 at 14:22.
Pushed by davidre into branch 'Plasma/6.6'.

session: release all sessions when the portal frontend disappears

Every session in this backend is created on behalf of the single portal
frontend (org.freedesktop.portal.Desktop). If it drops off the bus without
cleanly closing its sessions - for example if it crashes - nothing tears them
down. For an InputCapture session that means KWin keeps diverting all pointer
and keyboard input into the now ownerless EIS channel, so the desktop renders
normally but accepts no input until KWin is killed.

Watch the frontend centrally and close every session if it disappears, which
drives the existing Session::closed teardown for each. This covers all session
types rather than only input capture.


(cherry picked from commit e0cf8bf311a7baad8933797ec2fca0b94eb7d3d5)

Co-authored-by: Marcus Renheim <marcus@renheim.com>

(cherry picked from commit b4d3f6cd01edd5302030f2b8292ed82315069f27)

Co-authored-by: David Redondo <kde@david-redondo.de>

M  +10   -0    src/desktopportal.cpp
M  +2    -0    src/desktopportal.h
M  +13   -0    src/session.cpp
M  +1    -0    src/session.h

https://invent.kde.org/plasma/xdg-desktop-portal-kde/-/commit/867386a6e43d9b63fd23fca27d6f3f0b43a740b9