docs: state the per-frame reauthz and the wake's one-shot bound

Two things the security doc left implicit, both measured on 2026-07-31.

The `_drm` authorization is described as per-connection, which undersells it.
DRM/KMS capture is not session-scoped - it grabs the physical scanout of a CRTC
no matter which session owns the display - so the check is re-run on every
frame, and when a user logs in at a greeter the greeter's stream is closed
rather than continued. That is the property that stops an outgoing greeter
process from capturing the screen of the user who just logged in, and it is
worth stating where a reader is looking for exactly that confinement.

And the wake section never said what happens after the wake. It resets the
compositor's idle timer; it does not hold the display on. Left alone, the
connector idles off again one full idle period later: 30.3 s at a GDM greeter,
70.3 s in a user session with idle-delay=60. Saying so makes the existing
"useless as a way to keep a screen lit" clause concrete, and points at the
component whose job that actually is.
This commit is contained in:
Mariano Abad
2026-07-31 09:43:32 -03:00
parent 417028f9db
commit ab69a612a3

View File

@@ -113,7 +113,17 @@ presents) but reuses RustDesk's own hardened IPC.
`grab`), and non-32bpp scanouts are rejected before the copy. The device is
realpath-gated to `/dev/dri/` on both paths.
- **`_drm` is a screen-content channel.** It is authorized per connection (see
above); without that authz any local process could read the screen. On the
above); without that authz any local process could read the screen. Authorization
is also **re-checked on every frame**, not only at accept, because DRM/KMS
capture is not session-scoped: it grabs the physical scanout of a CRTC no matter
which session owns the display. So when the active session changes -- a user
logging in at a greeter -- the greeter's `_drm` stream is CLOSED rather than
continued (`drm: _drm peer no longer matches the active session`; observed with
peer_uid=60578 against active_uid=1000, and the greeter's uinput channel goes
with it). That is what stops an outgoing greeter process from capturing the
logged-in user's screen. The cost is a reconnect, not the session: the client
re-establishes itself against the new session's `--server` on its own in about
2.5 s (~3.6 s of dark screen, measured 2026-07-31). On the
**default (split) path** the channel carries the scanout DMA-BUF fd, passed to
the unprivileged `--server` over `SCM_RIGHTS` as a **read-only** descriptor
(the `--server` holds an import-once EGLImage cache, so a given scanout buffer
@@ -167,6 +177,13 @@ presents) but reuses RustDesk's own hardened IPC.
concurrent winner (compare-exchange claim), so a reconnect storm cannot
become an input-injection storm, and it is useless as a way to keep a
screen lit;
- the wake is **one-shot: it resets the compositor's idle timer, it does not
hold the display on**. If nothing else keeps the session awake, the connector
idles off again one full idle period later -- measured 2026-07-31: 30.3 s at
a GDM greeter, 70.3 s in a user session with `idle-delay=60`. Keeping a
screen lit for the length of a session is the job of RustDesk's existing
keep-awake inhibitor, not of this wake, which only recovers a connector that
is *already* dark;
- the uinput device is created and destroyed around the emit — nothing
persists in the input stack between wakes;
- without `/dev/uinput` the wake is skipped and latched off. Such a session