[PATCH v4 2/2] tty: sysrq: reset the per-device key state when reparsing reset_seq
From: Zhan Xusheng
Date: Fri Oct 30 2026 - 01:00:10 EST
From: Zhan Xusheng <zhanxusheng@xxxxxxxxxx>
sysrq_parse_reset_sequence() clears reset_seq_cnt, but reset_keybit is
only ever __set_bit() and reset_seq_len only ever incremented. struct
sysrq_state is allocated once per input device in sysrq_connect() and
lives until the device goes away, so both accumulate every time the
sequence is reparsed, which happens whenever the mode 0644 reset_seq
parameter is written.
After writing 56,29,111 and then 59,60 a device keeps all five keys in
reset_keybit with reset_seq_len 5, so the effective combination is the
union of the old and the new sequence: holding just 59 and 60 counts two
of five and does nothing. Releasing one of the three stale keys takes the
key-release path, because the key still tests positive in reset_keybit, so
reset_seq_cnt is decremented for a key that was never counted and goes
negative. From there ++reset_seq_cnt has to climb back through zero
before it can match reset_seq_len, so the emergency reset cannot be
triggered until reset_seq is written again or the device reconnects.
Rebuild both from the sequence that was just read. Also delete
keyreset_timer: with sysrq_downtime_ms set, holding a full sequence arms
it, and it is only deleted on a press outside the sequence or a release
inside it. Dropping the old keys from reset_keybit removes both of those
cancellations, so an already armed reset would otherwise survive into
orderly_reboot().
This depends on the previous patch. Clearing reset_seq_len turns what
used to be a stale superset of the old and the new keys into a real
truncation, so without the sequence being published in one step the
single-keypress reboot described there would reach every device rather
than only those that have never parsed a sequence.
Fixes: 154b7a489a5b ("Input: sysrq - allow specifying alternate reset sequence")
Signed-off-by: Zhan Xusheng <zhanxusheng@xxxxxxxxxx>
---
drivers/tty/sysrq.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index f9d63b45f859..00cea2f315c3 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -724,11 +724,18 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state)
if (read_seqcount_retry(&sysrq_reset_seq_lock.seqcount, seq))
return;
+ /*
+ * An armed reset belongs to the sequence being replaced: once the old
+ * keys are gone from reset_keybit their release no longer cancels it.
+ */
+ timer_delete(&state->keyreset_timer);
+
+ bitmap_zero(state->reset_keybit, KEY_CNT);
+ state->reset_seq_len = len;
state->reset_seq_cnt = 0;
for (i = 0; i < len; i++) {
__set_bit(keys[i], state->reset_keybit);
- state->reset_seq_len++;
if (test_bit(keys[i], state->key_down))
state->reset_seq_cnt++;
--
2.43.0