mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-06 06:00:09 +02:00
Survive a malformed control datagram
One bad datagram on the control socket ended the whole relay. Its
dispatch in handle_control ran unguarded in the main loop, so any
exception propagated out of main() and the process exited. The simplest
trigger is "watch abc": float(words[1]) raises ValueError, and the relay
died with
ValueError: could not convert string to float: 'abc'
at handle_control, via the bare handle_control(now) call in the
select loop
The control socket is an abstract socket bound to @frametop_relay.
Abstract sockets carry no permissions, so every local process can send
to it; nothing authenticates the sender. Dying from a datagram was bad
in three ways:
- Every grab is lost. Physical devices go back to gamescope and SteamVR,
which read them themselves, so the mouse types into Steam and the
desktop at once, and Meta+Shift shortcuts fire on both sides.
- The volume-key takeover is lost. gamescope aborts on a volume key when
no window has keyboard focus (wlr_seat_keyboard_notify_enter asserts on
a null focus surface), which ends the whole VR session - the exact
failure the relay exists to prevent.
- systemd restarts the service, but Type=notify with READY only after
the virtual devices exist makes the restart a visible hiccup, and a
crash loop from repeated bad datagrams would flap SteamVR's input.
Reproduced by running the relay with stubbed uinput devices on a
non-Linux host and sending "watch abc" to the control socket: it exited
on the first datagram after answering "devices" correctly. After the
fix, the same exchange gets a log line ("bad control datagram ...") and
the relay keeps answering.
The fix wraps each datagram's dispatch in try/except inside
handle_control's loop, so one malformed message is logged and skipped
while the rest of the queue is still processed. Parsing of the common
helper commands (vrbtn, vrhello, gazeawake, keyboard) keeps its own
guards; the catch-all is only a backstop for anything the guards miss,
including float() on a non-numeric argument to "watch" and "vrcapture".
This commit is contained in:
1 file changed
+6
@@ -778,6 +778,10 @@ def main():
|
|||||||
return
|
return
|
||||||
words = data.decode(errors="replace").split()
|
words = data.decode(errors="replace").split()
|
||||||
cmd = words[0] if words else ""
|
cmd = words[0] if words else ""
|
||||||
|
# One malformed datagram must not end the relay: it would drop every grab,
|
||||||
|
# including the volume keys that keep gamescope from aborting. The control
|
||||||
|
# socket is an abstract socket, so any local process can send to it.
|
||||||
|
try:
|
||||||
if cmd == "keyboard":
|
if cmd == "keyboard":
|
||||||
# From ft-screens (unbound, no reply): where typing goes, repeated every second.
|
# From ft-screens (unbound, no reply): where typing goes, repeated every second.
|
||||||
desktop = len(words) > 1 and words[1] == "desktop"
|
desktop = len(words) > 1 and words[1] == "desktop"
|
||||||
@@ -817,6 +821,8 @@ def main():
|
|||||||
reply(addr, {"t": "vrcapture", "seconds": seconds})
|
reply(addr, {"t": "vrcapture", "seconds": seconds})
|
||||||
else:
|
else:
|
||||||
reply(addr, {"t": "error", "error": f"unknown command {cmd!r}"})
|
reply(addr, {"t": "error", "error": f"unknown command {cmd!r}"})
|
||||||
|
except Exception as e:
|
||||||
|
log(f"bad control datagram {data!r}: {e!r}")
|
||||||
|
|
||||||
def broadcast(node, etype, code, value, now):
|
def broadcast(node, etype, code, value, now):
|
||||||
if not watchers or not node.candidate:
|
if not watchers or not node.candidate:
|
||||||
|
|||||||
Reference in new issue
Block a user