Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH] Input: evdev: drain queued events before reporting device removal
@ 2026-07-29  9:27 zhangliuyang.zly
  2026-07-29  9:39 ` sashiko-bot
  2026-07-29 18:34 ` Dmitry Torokhov
  0 siblings, 2 replies; 3+ messages in thread
From: zhangliuyang.zly @ 2026-07-29  9:27 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, zhangliuyang.zly

When an input device is unregistered, the input core calls
input_dev_release_keys() and sends a SYN_REPORT before closing the input
handles. This is intended to deliver synthetic key-up events for keys that
are still pressed when the device disappears.

However, evdev_read() currently checks evdev->exist before draining the
per-client event queue. If evdev_disconnect() marks the evdev node dead
before userspace reads the queued synthetic release packet, read() returns
-ENODEV immediately and the queued key-up events are lost from userspace's
point of view.

This can happen on Android with USB OTG 2.4G keyboard receivers. The input
core generates the release event during disconnect, but Android EventHub 
may observe the evdev hangup/removal first and then fail to read the 
pending EV_KEY value=0 event. The framework then falls back to device 
reset/cancel semantics instead of dispatching a normal ACTION_UP.

Allow evdev_read() to drain already queued events even after evdev->exist 
is cleared. Return -ENODEV only when the client queue is empty, or when 
the client has been revoked. Keep zero-length reads compatible with the 
previous error-checking behavior.

Signed-off-by: zhangliuyang.zly <zhangliuyang.zly@bytedance.com>
---
 drivers/input/evdev.c | 34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index c7325226cb86..89bdd7a2c763 100644
--- 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;
 
-		if (client->packet_head == client->tail &&
-		    (file->f_flags & O_NONBLOCK))
-			return -EAGAIN;
+		return 0;
+	}
 
+	for (;;) {
 		/*
-		 * count == 0 is special - no IO is done but we check
-		 * for error conditions (see above).
+		 * A revoked client must not consume any more events.
 		 */
-		if (count == 0)
-			break;
+		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;
+
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+		}
 
 		while (read + input_event_size() <= count &&
 		       evdev_fetch_next_event(client, &event)) {
-- 
2.54.0

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-07-29 18:34 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29  9:27 [PATCH] Input: evdev: drain queued events before reporting device removal zhangliuyang.zly
2026-07-29  9:39 ` sashiko-bot
2026-07-29 18:34 ` Dmitry Torokhov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox