[PATCH v3 6/8] HID: asus: prevent a late KEY_FN_ESC to trigger a use-after-free

From: Denis Benato

Date: Sat Jun 13 2026 - 11:31:34 EST


A late KEY_FN_ESC event can trigger asus_event() and unconditionally call
schedule_work() after the work was supposedly canceled.

Once the driver is removed and drvdata is freed by devres, the workqueue
could execute asus_sync_fn_lock() and dereference the freed drvdata.

Fixes: f631011e36b8 ("HID: hid-asus: Implement fn lock for Asus ProArt P16")
Reported-by: sahiko-bot@xxxxxxxxxx
Signed-off-by: Denis Benato <denis.benato@xxxxxxxxx>
---
drivers/hid/hid-asus.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index a6467172c455..a65a28137f0d 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -148,6 +148,7 @@ struct asus_drvdata {
int battery_stat;
bool battery_in_query;
unsigned long battery_next_query;
+ bool fn_lock_sync_work_disabled;
struct work_struct fn_lock_sync_work;
bool fn_lock;
#if IS_REACHABLE(CONFIG_ASUS_WMI)
@@ -416,7 +417,7 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
case KEY_KBDILLUMTOGGLE:
return !asus_hid_event(ASUS_EV_BRTTOGGLE);
case KEY_FN_ESC:
- if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
+ if (!drvdata->fn_lock_sync_work_disabled && (drvdata->quirks & QUIRK_HID_FN_LOCK)) {
drvdata->fn_lock = !drvdata->fn_lock;
schedule_work(&drvdata->fn_lock_sync_work);
}
@@ -1497,8 +1498,10 @@ static void asus_remove(struct hid_device *hdev)
cancel_work_sync(&drvdata->kbd_backlight->work);
}

- if (drvdata->quirks & QUIRK_HID_FN_LOCK)
+ if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
+ drvdata->fn_lock_sync_work_disabled = true;
cancel_work_sync(&drvdata->fn_lock_sync_work);
+ }

#if IS_REACHABLE(CONFIG_ASUS_WMI)
drvdata->wmi_work_disabled = true;
--
2.47.3