All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Zhan Xusheng" <zhanxusheng1024@gmail.com>
Cc: linux-serial@vger.kernel.org
Subject: Re: [PATCH v3 1/2] tty: sysrq: reparse the reset sequence when its length changes
Date: Wed, 21 Oct 2026 02:34:10 +0000	[thread overview]
Message-ID: <sashiko-outbox-179703@kernel.org> (raw)
In-Reply-To: <20261021022007.1717207-2-zhanxusheng1024@gmail.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] tty: sysrq: incomplete race fix in sysrq_handle_keypress() during param_array()
- [Medium] tty: sysrq: unsynchronized version latching in sysrq_parse_reset_sequence()
--

commit 969b9cd1cf7c6541203279c0b287db9c5b090a8b
Author: Zhan Xusheng <zhanxusheng@xiaomi.com>

tty: sysrq: reparse the reset sequence when its length changes

This commit tracks the parsed reset sequence length in struct sysrq_state and
triggers a reparse in sysrq_handle_keypress() when sysrq_reset_seq_len no
longer matches the previously parsed length.

> 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;
>  }

[Severity: Medium]
Could sysrq_parse_reset_sequence() still latch a stale or partially updated
key sequence when racing with param_array() -> sysrq_reset_seq_param_set()?

In sysrq_parse_reset_sequence(), len is read from sysrq_reset_seq_len at
the start of the function and sysrq_reset_seq[i] is read in the loop, while
sysrq_reset_seq_version is read and latched into state->reset_seq_version at
the end without verifying whether sysrq_reset_seq_version changed in the
meantime (and the follow-up commit "tty: sysrq: reset the per-device key
state when reparsing reset_seq" adds timer_delete() and bitmap_zero() between
reading len and latching sysrq_reset_seq_version, widening this window):

drivers/tty/sysrq.c:sysrq_parse_reset_sequence() {
	unsigned int len = READ_ONCE(sysrq_reset_seq_len);
	...
	for (i = 0; i < len; i++) {
		key = sysrq_reset_seq[i];
		...
	}
	...
	state->reset_seq_version = sysrq_reset_seq_version;
	state->reset_seq_parsed_len = len;
}

If a concurrent write to /sys/module/sysrq/parameters/reset_seq updates the
sequence to the same final length and increments sysrq_reset_seq_version
before state->reset_seq_version is assigned, state->reset_seq_version and
state->reset_seq_parsed_len will both match the final published values even
though stale or mixed sysrq_reset_seq[] elements were parsed.

In addition, sysrq_reset_seq_param_set() and param_array() update
sysrq_reset_seq[i], sysrq_reset_seq_version, and sysrq_reset_seq_len without
memory barriers, and sysrq_parse_reset_sequence() reads them without read
barriers:

drivers/tty/sysrq.c:sysrq_reset_seq_param_set() {
	...
	*((unsigned short *)kp->arg) = val;
	sysrq_reset_seq_version++;
}

On weakly-ordered architectures, can sysrq_parse_reset_sequence() observe the
updated sysrq_reset_seq_len and sysrq_reset_seq_version before the stores to
sysrq_reset_seq[] are visible, permanently latching stale keys?

>  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);
>  
>  		/*

[Severity: High]
What happens if the first key of a new multi-key sequence is pressed while
param_array() is still parsing the remaining elements?

When writing a multi-key sequence to /sys/module/sysrq/parameters/reset_seq,
param_array() resets *num (sysrq_reset_seq_len) to 0 and increments it after
each element setter call:

kernel/params.c:param_array() {
	*num = 0;
	do {
		...
		ret = set(val, &kp);
		if (ret != 0)
			return ret;
		kp.arg += elemsize;
		val += len+1;
		(*num)++;
	} while (save == ',');
}

After the first key is stored in sysrq_reset_seq[0] and (*num)++ sets
sysrq_reset_seq_len to 1, param_array() proceeds to parse the second element.
If a press of that first key (value == 1) arrives in sysrq_handle_keypress()
during this window, sysrq_parse_reset_sequence() reads len == 1 and configures
a 1-key reset sequence with state->reset_seq_len == 1, state->reset_seq_cnt ==
0, and state->reset_canceled == false.

Immediately afterwards in the same sysrq_handle_keypress() call,
sysrq_detect_reset_sequence() runs before any subsequent keypress can reparse
the final length:

drivers/tty/sysrq.c:sysrq_handle_keypress() {
		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);

		if (value)
			set_bit(code, sysrq->key_down);
		else
			clear_bit(code, sysrq->key_down);

		if (was_active)
			schedule_work(&sysrq->reinject_work);

		/* Check for reset sequence */
		sysrq_detect_reset_sequence(sysrq, code, value);
}

In sysrq_detect_reset_sequence(), ++state->reset_seq_cnt ==
state->reset_seq_len (1 == 1) is satisfied with !state->reset_canceled, which
calls sysrq_handle_reset_request() -> sysrq_do_reset() -> orderly_reboot()
immediately on that single keypress.

Also, because param_array() sets *num = 0 before calling the setter for the
first element, checking sysrq->reset_seq_parsed_len !=
READ_ONCE(sysrq_reset_seq_len) causes a concurrent keypress to reparse an
empty sequence (len == 0) before the first setter even runs (which, with the
follow-up commit "tty: sysrq: reset the per-device key state when reparsing
reset_seq", also clears reset_keybit and deletes keyreset_timer even if the
parameter write fails on the first element).

Would it be safer to synchronize updates to sysrq_reset_seq[] and
sysrq_reset_seq_len so that sysrq_handle_keypress() never observes intermediate
states while param_array() is in progress?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261021022007.1717207-1-zhanxusheng1024@gmail.com?part=1

  reply	other threads:[~2026-10-21  2:34 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21  2:20 [PATCH v3 0/2] tty: sysrq: fix the reset sequence state kept per input device Zhan Xusheng
2026-10-21  2:20 ` [PATCH v3 1/2] tty: sysrq: reparse the reset sequence when its length changes Zhan Xusheng
2026-10-21  2:34   ` sashiko-bot [this message]
2026-10-21  2:20 ` [PATCH v3 2/2] tty: sysrq: reset the per-device key state when reparsing reset_seq Zhan Xusheng
2026-10-21  2:27   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-179703@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=zhanxusheng1024@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.