]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Input: evdev - fix information leak in evdev_pass_values()
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 29 Jul 2026 18:30:45 +0000 (11:30 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Tue, 4 Aug 2026 00:51:29 +0000 (17:51 -0700)
In evdev_pass_values(), the input_event structure is allocated on the
kernel stack and populated field-by-field. However, it is never fully
initialized. On architectures where struct input_event contains explicit
or implicit padding (such as the 32-bit __pad field on SPARC64), these
padding bytes are left uninitialized.

When this event structure is subsequently passed to the client buffer
and later copied to userspace, the uninitialized padding bytes leak
kernel stack memory, potentially exposing sensitive information.

Similar issues exist in __evdev_queue_syn_dropped and __pass_event.

Fix this by explicitly zeroing the entire event structure with memset()
before populating its fields. This ensures all padding bytes are cleared
before the data crosses the security boundary.

Reported-by: sashiko-bot@kernel.org
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/ampGGKo4UMKru6f5@google.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/evdev.c

index c7325226cb8663a3fd2a3224815403630c7d2ce7..531db70aa9f923ca86f76e6aa1d3e3e74c96a5aa 100644 (file)
@@ -146,11 +146,11 @@ static void __evdev_queue_syn_dropped(struct evdev_client *client)
        struct timespec64 ts = ktime_to_timespec64(ev_time[client->clk_type]);
        struct input_event ev;
 
+       memset(&ev, 0, sizeof(ev));
        ev.input_event_sec = ts.tv_sec;
        ev.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
        ev.type = EV_SYN;
        ev.code = SYN_DROPPED;
-       ev.value = 0;
 
        client->buffer[client->head++] = ev;
        client->head &= client->bufsize - 1;
@@ -218,20 +218,20 @@ static void __pass_event(struct evdev_client *client,
        client->head &= client->bufsize - 1;
 
        if (unlikely(client->head == client->tail)) {
+               struct input_event ev;
+
+               memset(&ev, 0, sizeof(ev));
+               ev.input_event_sec = event->input_event_sec;
+               ev.input_event_usec = event->input_event_usec;
+               ev.type = EV_SYN;
+               ev.code = SYN_DROPPED;
+
                /*
                 * This effectively "drops" all unconsumed events, leaving
                 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
                 */
                client->tail = (client->head - 2) & (client->bufsize - 1);
-
-               client->buffer[client->tail] = (struct input_event) {
-                       .input_event_sec = event->input_event_sec,
-                       .input_event_usec = event->input_event_usec,
-                       .type = EV_SYN,
-                       .code = SYN_DROPPED,
-                       .value = 0,
-               };
-
+               client->buffer[client->tail] = ev;
                client->packet_head = client->tail;
        }
 
@@ -253,6 +253,8 @@ static void evdev_pass_values(struct evdev_client *client,
        if (client->revoked)
                return;
 
+       memset(&event, 0, sizeof(event));
+
        ts = ktime_to_timespec64(ev_time[client->clk_type]);
        event.input_event_sec = ts.tv_sec;
        event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;