Add design considerations for #4

This commit is contained in:
Johannes Leupolz 2026-01-08 21:13:07 +00:00
parent 0062a1741e
commit 11144bd694
2 changed files with 79 additions and 21 deletions

View file

@ -499,10 +499,54 @@ As a result:
* input devices may interact with the VT layer in unintended ways
When a graphical session is active, these issues do not occur:
the compositor, via `systemd-logind`, owns a VT, switches it to `KD_GRAPHICS`, and the VT keyboard handler is **suppressed**.
the compositor, via `systemd-logind`, owns a VT, switches it to `KD_GRAPHICS` and `K_OFF`, and the VT keyboard handler is **suppressed**.
The missing piece is a **well-defined fallback** for the “no graphical session” case.
### **Effect of K_OFF in Linux VT subsystem**
- ioctl KDSKBMODE on /dev/ttyX leads to call of vt_do_kdskbmode
- kb->kbdmode = VC_OFF
This suppresses the following chains in [keyboard.c](https://github.com/torvalds/linux/blob/master/drivers/tty/vt/keyboard.c)
Lets take Console_1 via ALT+F2 as an example:
- kbd_event (Entry Point)
- kbd_keycode (Translation)
- Job: looks up the Keysym in the keymap based on the current modifier state (ALT+F2 is 0xf501 in [defkeymap.c_shipped](https://github.com/torvalds/linux/blob/master/drivers/tty/vt/defkeymap.c_shipped))
- type = KTYP(keysym) takes the first 16 bits (which is 0xf5).
- type -= 0xf0. (which is 0x05). Note hat the kernel uses the ** offset** `0xf0` to differentiate between characters and special handlers in the keymap. When `type -= 0xf0` is called, it "normalizes" the keysym into an index for the `k_handler` array.
- index = KVAL(keysym) takes the last 16 bits (which is 0x01)
- return if ((raw_mode || kbd->kbdmode == VC_OFF) && type != KT_SPEC && type != KT_SHIFT), so suppression happens here.
- Note that K_HANDLERS[type] == K_HANDLERS[0x05] == k_cons.
- if no suppression: call (*k_handler[type])(vc, KVAL(keysym), !down), which is k_cons(vc,0x01)
Lets take Decr_Console via ALT+Left as second example:
- kbd_event (Entry Point)
- kbd_keycode (Translation)
- Job: looks up the Keysym in the keymap based on the current modifier state (ALT+Left is 0xf210 in [defkeymap.c_shipped](https://github.com/torvalds/linux/blob/master/drivers/tty/vt/defkeymap.c_shipped))
- type = KTYP(keysym) takes the first 16 bits (which is 0xf2).
- type -= 0xf0. (which is 0x02)
- index = KVAL(keysym) takes the last 16 bits (which is 0x10)
- return if ((raw_mode || kbd->kbdmode == VC_OFF) && type != KT_SPEC && type != KT_SHIFT), so suppression does *not* happen here.
- Note that K_HANDLERS[type] == K_HANDLERS[0x02] == k_spec.
- call (*k_handler[type])(vc, KVAL(keysym), !down), which is k_spec(vc,0x10)
- k_spec (Handling)
- Condition if ((... || kbd->kbdmode == VC_OFF) && value != KVAL(K_SAK)) evaluates to false, so suppression happens here
- if no suppression: call fn_handler[value](...) which is fn_dec_console(...)
In the KT_SHIFT-case of "return if ((raw_mode || kbd->kbdmode == VC_OFF) && type != KT_SPEC && type != KT_SHIFT", nothing interesting happens in our case: it might enable and disable caps lock. This means uinput can still enable disable caps, which is a bit odd, but nothing tragic in our use cases.
sysrq has an own handler. It is not affected by K_OFF.
https://github.com/torvalds/linux/blob/master/drivers/tty/sysrq.c#L1048
Raw mode is not relevant.
### The Risk of Non-Standard or User-Loaded Keymaps
While `vuinputd` relies on the default kernel keymap logic for its internal filtering, it is important to note that the host's active keymap can be modified at runtime (e.g., via `loadkeys` or `systemd-vconsole-setup`). Because the kernel's `K_OFF` logic (triggered by `KDSKBMODE`) explicitly whitelists the `K_SAK` (Secure Attention Key) keysym, any user-defined key combination mapped to `SAK` will bypass the kernel's own input suppression.
To further mitigate these risks, `vuinputd` could be extended to parse the host's active keymap during startup, allowing the sanitizer to dynamically identify and filter any physical keycode mapped to a sensitive keysym like `K_SAK`. This is currently not planned. Alternatively, on systems dedicated to containerization where full host TTY access is not required, administrators can load a "hardened" keymap stripped of all `Console_N`, `Boot`, and `SAK` assignments. By combining a minimized host keymap with `vuinputd`'s CUSE-level filtering, the system achieves a robust "Defense in Depth" that protects against both accidental triggers and intentional container escapes.
### **Decision**
`fallbackdm` is implemented as a **logind-managed fallback graphical session**.

View file

@ -93,13 +93,20 @@ fn is_allowed_in_sanitized_mode(keytracker: &mut KeyTracker, event: &input_event
return false;
}
// 3. Block CAD (Ctrl + Alt + Del)
// 3. Block CAD (Ctrl + Alt + Del).
// Block basically all Boot from defkeymap.map
if alt_down && ctrl_down && (code == KEY_DELETE || code == KEY_KPDOT) {
return false;
}
// 4. Block standalone dangerous keys
// 4. Block SAK (CTRL+ALT+PAUSE). seems not to be mapped
// This one is crucial, because it is allowed even in K_OFF mode.
// https://github.com/torvalds/linux/blob/master/drivers/tty/vt/keyboard.c
// It seems not to be mapped by default, but close the potential hole if a
// distro follows the ancient https://www.kernel.org/doc/Documentation/SAK.txt
// 5. Block standalone dangerous keys
match code {
KEY_POWER | KEY_SLEEP | KEY_WAKEUP => return false,
_ => {}
@ -108,26 +115,33 @@ fn is_allowed_in_sanitized_mode(keytracker: &mut KeyTracker, event: &input_event
true
}
fn is_allowed_in_strict_gamepad_mode(keytracker: &mut KeyTracker, event: &input_event) -> bool {
let type_ = event.type_;
let code = event.code;
fn is_allowed_in_strict_gamepad_mode(
_keytracker: &mut KeyTracker,
event: &input_event,
) -> bool {
match event.type_ {
EV_SYN => true,
if type_ == EV_SYN {
return true;
}
if type_ == EV_ABS {
return true;
}
if type_ == EV_FF {
return true;
}
// Analog sticks, triggers
EV_ABS => true,
if type_ == EV_KEY {
return match code {
BTN_SOUTH..BTN_THUMBR => true,
BTN_DPAD_UP..BTN_GRIPR2 => true,
// Force feedback
EV_FF => true,
// Digital buttons only
EV_KEY => match event.code {
// Standard gamepad face + shoulder + stick buttons
BTN_SOUTH..=BTN_THUMBR => true,
// D-Pad + extended gamepad buttons (triggers, paddles)
BTN_DPAD_UP..=BTN_GRIPR2 => true,
// Everything else is rejected (KEY_*, mouse buttons, etc.)
_ => false,
};
},
// Explicitly reject everything else (EV_REL, EV_MSC, etc.)
_ => false,
}
false
}