Linux Input/HID development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: zhangliuyang.zly <zhangliuyang.zly@bytedance.com>
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH] Input: evdev: drain queued events before reporting device removal
Date: Wed, 29 Jul 2026 09:39:08 +0000	[thread overview]
Message-ID: <20260729093909.6624B1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260729092753.1596400-1-zhangliuyang.zly@bytedance.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] The patch breaks zero-length O_NONBLOCK reads by returning 0 instead of -EAGAIN when the queue is empty, altering userspace ABI.
- [High] A TOCTOU race between lockless queue checks and device death drops synthetic key-up events, defeating the patch's purpose.

Pre-existing issues:
- [High] Uninitialized kernel stack memory is leaked to user-space due to missing padding initialization in `struct input_event` on architectures like SPARC64.
--

--- Patch [1]: [PATCH] Input: evdev: drain queued events before reporting device removal ---
commit 0c2e6f64ca8c023851919612ec0b8e230776b440
Author: zhangliuyang.zly <zhangliuyang.zly@bytedance.com>
Subject: Input: evdev: drain queued events before reporting device removal

This commit modifies the evdev read path to drain the per-client event queue
before reporting -ENODEV when a device is removed. It aims to prevent dropping
synthetic key-up events generated during device unregistration.

> diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
> --- a/drivers/input/evdev.c
> +++ b/drivers/input/evdev.c

[ ... ]

> @@ -565,20 +565,38 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
>  	if (count != 0 && count < input_event_size())
>  		return -EINVAL;
>  
> -	for (;;) {
> +	/*
> +	 * count == 0 is special - no IO is done but we still check for
> +	 * error conditions, preserving the historical behavior.
> +	 */
> +	if (count == 0) {
> +		if (!evdev->exist || client->revoked)
> +			return -ENODEV;
> +
> +		return 0;
> +	}

[Severity: High]
Does this change alter the historical behavior for non-blocking reads?

Previously, if count == 0 and the file descriptor was set to O_NONBLOCK with
an empty queue, the code would return -EAGAIN. By moving the count == 0 check
above the loop and unconditionally returning 0, applications checking for queue
status using a zero-length read might now incorrectly assume events are
available.

>  
> +	for (;;) {
> +		/*
> +		 * A revoked client must not consume any more events.
> +		 */
> +		if (client->revoked)
> +			return -ENODEV;
> +
> +		/*
> +		 * The input core may queue synthetic release events during
> +		 * device unregister before evdev_disconnect() marks the evdev
> +		 * node dead. Do not drop those already queued events by
> +		 * returning -ENODEV too early. Drain the client queue first and
> +		 * report -ENODEV only when there is nothing left to read.
> +		 */
> +		if (client->packet_head == client->tail) {
> +			if (!evdev->exist)
> +				return -ENODEV;

[Severity: High]
Might this lockless conditional block still drop synthetic key-up events due
to a race condition?

Because these checks are performed locklessly without memory barriers, a thread
could evaluate packet_head == tail as true. If a context switch occurs and the
input core queues synthetic events and subsequently marks evdev->exist as false,
when this thread resumes it will evaluate !evdev->exist as true.

This would result in returning -ENODEV and missing the newly queued synthetic
events entirely, defeating the original purpose of the patch.

> +
> +			if (file->f_flags & O_NONBLOCK)
> +				return -EAGAIN;
> +		}
>  
>  		while (read + input_event_size() <= count &&
>  		       evdev_fetch_next_event(client, &event)) {
>  
> -			/*
> -			 * count == 0 is special - no IO is done but we check
> -			 * for error conditions (see above).
> -			 */
> -			if (count == 0)
> -				break;
> +			if (input_event_to_user(buffer + read, &event))
> +				return -EFAULT;

[Severity: High]
This is a pre-existing issue, but could this leak uninitialized kernel stack
memory to userspace?

In evdev_pass_values(), the input_event structure is allocated on the stack
but its padding fields (such as the 32-bit padding on SPARC64) are not fully
initialized before being copied into the client buffer.

When evdev_read() fetches these events via evdev_fetch_next_event() and
subsequently copies them to userspace using input_event_to_user():

evdev_pass_values() {
    struct input_event event;
    ...
    event.type = v->type;
    ...
    __pass_event(client, &event);
}

The uninitialized padding bytes are copied out here, potentially leaking
sensitive kernel data.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260729092753.1596400-1-zhangliuyang.zly@bytedance.com?part=1

  reply	other threads:[~2026-07-29  9:39 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-29  9:27 [PATCH] Input: evdev: drain queued events before reporting device removal zhangliuyang.zly
2026-07-29  9:39 ` sashiko-bot [this message]
2026-07-29 18:34 ` Dmitry Torokhov

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=20260729093909.6624B1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=zhangliuyang.zly@bytedance.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox