[PATCH v3 1/2] tty: sysrq: reparse the reset sequence when its length changes
From: Zhan Xusheng
Date: Tue Oct 20 2026 - 22:23:21 EST
From: Zhan Xusheng <zhanxusheng@xxxxxxxxxx>
reset_seq is a module parameter array, so a write to it is handled by
param_array(), which publishes the new length only after the element
setter has already bumped the version:
kernel/params.c:
*num = 0; /* sysrq_reset_seq_len */
do {
ret = set(val, &kp); /* bumps the version */
...
(*num)++;
} while (save == ',');
sysrq_handle_keypress() decides to reparse purely on the version, so a
keypress landing between the last setter and the matching (*num)++ parses
a sequence that is one element short and then latches the final version.
Nothing makes it reparse afterwards, so the device keeps the truncated
sequence until the parameter is written again.
On a device that has not parsed before, a two-key sequence truncated this
way leaves reset_seq_len at 1. The parse runs before the keypress is
recorded in key_down, so reset_seq_cnt is 0 and reset_canceled stays
false. A single press of the remaining key then satisfies
if (++state->reset_seq_cnt == state->reset_seq_len &&
!state->reset_canceled)
and sysrq_handle_reset_request() goes straight to orderly_reboot(),
because sysrq_downtime_ms defaults to 0.
Record the length the parse used and treat a change in it as staleness
too. The truncated parse then no longer sticks: once param_array() has
published the final length, the next keypress reparses.
Fixes: 154b7a489a5b ("Input: sysrq - allow specifying alternate reset sequence")
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Closes: https://lore.kernel.org/all/sashiko-outbox-173915@xxxxxxxxxx/
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Zhan Xusheng <zhanxusheng@xxxxxxxxxx>
---
drivers/tty/sysrq.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index c2e4b31b699aa..4fd5b3ace9c40 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -676,6 +676,7 @@ struct sysrq_state {
int reset_seq_len;
int reset_seq_cnt;
int reset_seq_version;
+ unsigned int reset_seq_parsed_len;
struct timer_list keyreset_timer;
};
@@ -686,12 +687,13 @@ static unsigned int sysrq_reset_seq_version = 1;
static void sysrq_parse_reset_sequence(struct sysrq_state *state)
{
+ unsigned int len = READ_ONCE(sysrq_reset_seq_len);
int i;
unsigned short key;
state->reset_seq_cnt = 0;
- for (i = 0; i < sysrq_reset_seq_len; i++) {
+ for (i = 0; i < len; i++) {
key = sysrq_reset_seq[i];
if (key == KEY_RESERVED || key > KEY_MAX)
@@ -708,6 +710,7 @@ static void sysrq_parse_reset_sequence(struct sysrq_state *state)
state->reset_canceled = state->reset_seq_cnt != 0;
state->reset_seq_version = sysrq_reset_seq_version;
+ state->reset_seq_parsed_len = len;
}
static void sysrq_do_reset(struct timer_list *t)
@@ -903,7 +906,9 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
/*
* See if reset sequence has changed since the last time.
*/
- if (sysrq->reset_seq_version != sysrq_reset_seq_version)
+ if (sysrq->reset_seq_version != sysrq_reset_seq_version ||
+ sysrq->reset_seq_parsed_len !=
+ READ_ONCE(sysrq_reset_seq_len))
sysrq_parse_reset_sequence(sysrq);
/*
--
2.43.0