[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20250928130819.383808-1-pengyu@kylinos.cn>
Date: Sun, 28 Sep 2025 21:08:19 +0800
From: pengyu <pengyu@...inos.cn>
To: gregkh@...uxfoundation.org,
jirislaby@...nel.org
Cc: legion@...nel.org,
mingo@...nel.org,
myrrhperiwinkle@...labs.xyz,
tglx@...utronix.de,
dmitry.torokhov@...il.com,
changlianzhi@...ontech.com,
linux-kernel@...r.kernel.org,
linux-serial@...r.kernel.org,
pengyu <pengyu@...inos.cn>,
syzbot+79c403850e6816dc39cf@...kaller.appspotmail.com
Subject: [PATCH] tty/vt: Fix possible deadlock in input_inject_event
syzkaller testing revealed a potential deadlock involving keyboard
handling:
CPU0 CPU1 CPU2
---- ---- ----
read_lock(tasklist_lock); evdev_write
input_inject_event write_lock(tasklist_lock);
lock(&dev->event_lock);
read_lock(tasklist_lock);
<Interrupt>
kbd_bh() / kd_sound_helper()
input_inject_event
lock(&dev->event_lock); // Deadlock risk
The deadlock occurs because:
1. Both kbd_bh and kd_sound_helper run in interrupt context
2. tasklist_lock is interrupt-unsafe
3. When evdev_write holds both dev->event_lock and tasklist_lock,
interrupt context attempts to acquire dev->event_lock create deadlock
risks
Convert both kbd_bh and kd_sound_helper to use workqueues. This moves
input_inject_event execution to process context, where it's safe to
acquire locks that may be held by code using interrupt-unsafe locks.
Reported-by: syzbot+79c403850e6816dc39cf@...kaller.appspotmail.com
Closes: https://lore.kernel.org/all/66f6c8ce.050a0220.46d20.001c.GAE@google.com/T/#u
Fixes: fb09d0ac0772 ("tty: Fix the keyboard led light display problem")
Signed-off-by: pengyu <pengyu@...inos.cn>
---
drivers/tty/vt/keyboard.c | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index ee1d9c448c7e..eb2afc86b502 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -131,8 +131,8 @@ static const unsigned char max_vals[] = {
static const int NR_TYPES = ARRAY_SIZE(max_vals);
-static void kbd_bh(struct tasklet_struct *unused);
-static DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh);
+static void kbd_bh(struct work_struct *unused);
+static DECLARE_WORK(keyboard_work, kbd_bh);
static struct input_handler kbd_handler;
static DEFINE_SPINLOCK(kbd_event_lock);
@@ -264,23 +264,23 @@ static int kd_sound_helper(struct input_handle *handle, void *data)
return 0;
}
-static void kd_nosound(struct timer_list *unused)
+static void kd_nosound(struct work_struct *unused)
{
static unsigned int zero;
input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper);
}
-static DEFINE_TIMER(kd_mksound_timer, kd_nosound);
+static DECLARE_DELAYED_WORK(kd_mksound_worker, kd_nosound);
void kd_mksound(unsigned int hz, unsigned int ticks)
{
- timer_delete_sync(&kd_mksound_timer);
+ cancel_delayed_work_sync(&kd_mksound_worker);
input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper);
if (hz && ticks)
- mod_timer(&kd_mksound_timer, jiffies + ticks);
+ schedule_delayed_work(&kd_mksound_worker, ticks);
}
EXPORT_SYMBOL(kd_mksound);
@@ -390,7 +390,7 @@ static void put_queue_utf8(struct vc_data *vc, u32 value)
/* FIXME: review locking for vt.c callers */
static void set_leds(void)
{
- tasklet_schedule(&keyboard_tasklet);
+ schedule_work(&keyboard_work);
}
/*
@@ -1024,10 +1024,10 @@ static int kbd_led_trigger_activate(struct led_classdev *cdev)
struct kbd_led_trigger *trigger =
container_of(cdev->trigger, struct kbd_led_trigger, trigger);
- tasklet_disable(&keyboard_tasklet);
+ cancel_work_sync(&keyboard_work);
if (ledstate != -1U)
led_set_brightness(cdev, ledstate & trigger->mask ? LED_FULL : LED_OFF);
- tasklet_enable(&keyboard_tasklet);
+ enable_work(&keyboard_work);
return 0;
}
@@ -1243,7 +1243,7 @@ void vt_kbd_con_stop(unsigned int console)
* handle the scenario when keyboard handler is not registered yet
* but we already getting updates from the VT to update led state.
*/
-static void kbd_bh(struct tasklet_struct *unused)
+static void kbd_bh(struct work_struct *unused)
{
unsigned int leds;
unsigned long flags;
@@ -1535,7 +1535,7 @@ static void kbd_event(struct input_handle *handle, unsigned int event_type,
spin_unlock(&kbd_event_lock);
- tasklet_schedule(&keyboard_tasklet);
+ schedule_work(&keyboard_work);
do_poke_blanked_console = 1;
schedule_console_callback();
}
@@ -1607,12 +1607,12 @@ static void kbd_disconnect(struct input_handle *handle)
*/
static void kbd_start(struct input_handle *handle)
{
- tasklet_disable(&keyboard_tasklet);
+ cancel_work_sync(&keyboard_work);
if (ledstate != -1U)
kbd_update_leds_helper(handle, &ledstate);
- tasklet_enable(&keyboard_tasklet);
+ enable_work(&keyboard_work);
}
static const struct input_device_id kbd_ids[] = {
@@ -1662,8 +1662,8 @@ int __init kbd_init(void)
if (error)
return error;
- tasklet_enable(&keyboard_tasklet);
- tasklet_schedule(&keyboard_tasklet);
+ enable_work(&keyboard_work);
+ schedule_work(&keyboard_work);
return 0;
}
--
2.25.1
Powered by blists - more mailing lists