* [PATCH v5 0/5] HID: asus: security fixes and more hardware support
@ 2026-06-19 0:10 Denis Benato
2026-06-19 0:10 ` [PATCH v5 1/5] HID: asus: refactor the two workqueues and init sequence Denis Benato
` (4 more replies)
0 siblings, 5 replies; 8+ messages in thread
From: Denis Benato @ 2026-06-19 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli
Hi all,
I have added support for controlling the (way too bright) XG mobile
LEDs in hid-asus and added the i2c version of already supported
hardware that was probed only when it's a USB: these are two separate
features changes and are the only two that are not fixes for
pre-existing issue (see below).
Auto-review bot has spotted a bunch of pre-existing problems alongside
problems in my own code therefore at this point I am going to fix the
more problems I can and including those fixes and improvements in this
patchset.
For v4 I decided to follow Antheas' suggestion of reusing the existing
workqueue and by making it more generic I solved a good bunch of issues.
The v5 iteration is simply me fixing a bunch of bugs in new code spotted
by the bot. Thanks for providing such a useful tool!
On a side node this patchset has a few more warnings: specifically
"WARNING: Prefer kzalloc_obj over kzalloc with sizeof" but it's a false
positive as that would introduce sleeping calls in atomic contexts.
Link: https://lore.kernel.org/all/20260612124830.984592-1-denis.benato@linux.dev/
Link: https://lore.kernel.org/all/30258f28-4036-4e99-9494-031212544dc2@linux.dev/
Best regards,
Denis Benato
Link v2: https://lore.kernel.org/all/20260612142326.1704858-1-denis.benato@linux.dev/
Link v3: https://lore.kernel.org/all/20260613153029.2559774-1-denis.benato@linux.dev/
Link v4: https://lore.kernel.org/all/20260615165058.3845-1-denis.benato@linux.dev/
Changelog:
- v1
- Initial submission (as separate patches)
- v2
- HID: asus: add i2c entry for FA808UM and other TUFs
- Joined to the patchset
- HID: asus: add support for xgm led
- Use the correct callback set_brightness_blocking
- Stop hardcoding the cled name
- HID: asus: prevent wrong pointer cast
- Added
- HID: asus: mitigate possible use-after-free
- Added
- v3
- HID: asus: add support for xgm led
- check devm_kasprintf result
- HID: asus: prevent a late KEY_FN_ESC to trigger a use-after-free
- Added
- HID: asus: avoid sleeping calls in atomic context
- Added
- HID: asus: cleanup keyboard listener on failure: avoid use-after-free
- Added
- v4
- HID: asus: refactor the two workqueues and init sequence
- Added to replace previous patches in a clearer way
- HID: asus: add support for xgm led
- Simplify cled interface allocation and registration
- v5
- HID: asus: refactor the two workqueues and init sequence
- replace kzalloc_obj in atomix context with kzalloc(..., GFP_ATOMIC/NOWAIT)
- better destroy the queue on _probe in case of errors
- Fix a data race for deferred asus_kbd_wmi_fan()
- HID: asus: add support for xgm led
- improve teardown of the cled interface
Denis Benato (5):
HID: asus: refactor the two workqueues and init sequence
HID: asus: remove extraneous OOM error
HID: asus: fix a off-by-one in mcu_parse_version_string() validation
HID: asus: add support for xgm led
HID: asus: add i2c entry for FA808UM and other TUFs
drivers/hid/hid-asus.c | 491 ++++++++++++++++++++++++++++++++---------
1 file changed, 382 insertions(+), 109 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v5 1/5] HID: asus: refactor the two workqueues and init sequence
2026-06-19 0:10 [PATCH v5 0/5] HID: asus: security fixes and more hardware support Denis Benato
@ 2026-06-19 0:10 ` Denis Benato
2026-06-19 0:23 ` sashiko-bot
2026-06-19 0:11 ` [PATCH v5 2/5] HID: asus: remove extraneous OOM error Denis Benato
` (3 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Denis Benato @ 2026-06-19 0:10 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli, sahiko-bot
Multiple issues have been found within the hid-asus driver:
- unchecked size in asus_raw_event()
- unclean teardown of asus_probe on failure
- possible use-after-free in asus_probe
- multiple workqueue used for jobs where one was enough
- sleeping calls in atomic context
- packets of incorrect size being sent to the keyboard controller
Join the two workqueues into one reusing the stopping mechanism
of the brightness workqueue, use the joined workqueue to also
move the asus_wmi_send_event() sleeping call away from atomic
context and add a size check in asus_raw_event().
Fixes: f631011e36b8 ("HID: hid-asus: Implement fn lock for Asus ProArt P16")
Fixes: 1489a34e97ef ("HID: asus: Implement Fn+F5 fan control key handler")
Fixes: b34b5945a769 ("HID: asus: listen to the asus-wmi brightness device instead of creating one")
Reported-by: sahiko-bot@kernel.org
Closes: https://lore.kernel.org/all/20260613154732.60A4B1F000E9@smtp.kernel.org/
Signed-off-by: Denis Benato <denis.benato@linux.dev>
---
drivers/hid/hid-asus.c | 400 +++++++++++++++++++++++++++++------------
1 file changed, 289 insertions(+), 111 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index d34d74df3dc0..40191eb9e2e8 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -109,11 +109,36 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
-struct asus_kbd_leds {
- struct asus_hid_listener listener;
+enum asus_work_action_type {
+ FN_LOCK_SYNC,
+ BRIGHTNESS_SET,
+ WMI_FAN,
+};
+
+struct hid_raw_event_data {
+ u8 report_data[FEATURE_KBD_REPORT_SIZE];
+ size_t report_size;
+};
+
+struct asus_work_action {
+ struct list_head node;
+ enum asus_work_action_type type;
+ union {
+ /* Data for BRIGHTNESS_SET */
+ unsigned int brightness;
+
+ /* Data for FN_LOCK_SYNC */
+ bool fn_lock;
+
+ /* Data for WMI_FAN */
+ struct hid_raw_event_data fan_hid_data;
+ } data;
+};
+
+struct asus_worker {
struct hid_device *hdev;
struct work_struct work;
- unsigned int brightness;
+ struct list_head actions;
spinlock_t lock;
bool removed;
};
@@ -133,7 +158,8 @@ struct asus_drvdata {
struct hid_device *hdev;
struct input_dev *input;
struct input_dev *tp_kbd_input;
- struct asus_kbd_leds *kbd_backlight;
+ struct asus_worker *worker;
+ unsigned int kbd_backlight_brightness;
const struct asus_touchpad_info *tp;
struct power_supply *battery;
struct power_supply_desc battery_desc;
@@ -141,7 +167,7 @@ struct asus_drvdata {
int battery_stat;
bool battery_in_query;
unsigned long battery_next_query;
- struct work_struct fn_lock_sync_work;
+ struct asus_hid_listener listener;
bool fn_lock;
};
@@ -211,6 +237,29 @@ static const u8 asus_report_id_init[] = {
FEATURE_KBD_LED_REPORT_ID2
};
+/*
+ * Send events to asus-wmi driver for handling special keys
+ */
+static int asus_wmi_send_event(struct asus_drvdata *drvdata, u8 code)
+{
+ int err;
+ u32 retval;
+
+ err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS,
+ ASUS_WMI_METHODID_NOTIF, code, &retval);
+ if (err) {
+ pr_warn("Failed to notify asus-wmi: %d\n", err);
+ return err;
+ }
+
+ if (retval != 0) {
+ pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static void asus_report_contact_down(struct asus_drvdata *drvdat,
int toolType, u8 *data)
{
@@ -331,25 +380,71 @@ static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size)
}
/*
- * Send events to asus-wmi driver for handling special keys
+ * Used in atomic contexts to schedule work involving sleeps operations or
+ * asus-wmi interactions.
+ *
+ * Caller is responsible to store relevant data in the structure to carry out
+ * the required action.
+ *
+ * This function must be called while the spin lock protecting the workqueue
+ * is already being held.
*/
-static int asus_wmi_send_event(struct asus_drvdata *drvdata, u8 code)
+static void asus_worker_schedule(struct asus_worker *worker, struct asus_work_action *action)
{
- int err;
- u32 retval;
-
- err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS,
- ASUS_WMI_METHODID_NOTIF, code, &retval);
- if (err) {
- pr_warn("Failed to notify asus-wmi: %d\n", err);
- return err;
+ if (worker->removed) {
+ kfree(action);
+ return;
}
- if (retval != 0) {
- pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval);
- return -EIO;
+ list_add_tail(&action->node, &worker->actions);
+ schedule_work(&worker->work);
+}
+
+static int asus_kbd_fn_lock_set(struct asus_drvdata *drvdata, bool enabled)
+{
+ struct asus_work_action *action;
+ unsigned long flags;
+
+ action = kzalloc(sizeof(struct asus_work_action), GFP_ATOMIC);
+ if (!action)
+ return -ENOMEM;
+
+ drvdata->fn_lock = enabled;
+ action->type = FN_LOCK_SYNC;
+ action->data.fn_lock = drvdata->fn_lock;
+ INIT_LIST_HEAD(&action->node);
+
+ spin_lock_irqsave(&drvdata->worker->lock, flags);
+ asus_worker_schedule(drvdata->worker, action);
+ spin_unlock_irqrestore(&drvdata->worker->lock, flags);
+
+ return 0;
+}
+
+static int asus_kbd_wmi_fan_send(struct asus_drvdata *drvdata, u8 *report_data,
+ size_t report_size)
+{
+ struct asus_work_action *action;
+ unsigned long flags;
+
+ if (report_size > FEATURE_KBD_REPORT_SIZE) {
+ hid_err(drvdata->hdev, "Invalid report size for fan event: %zu\n", report_size);
+ return -EINVAL;
}
+ action = kzalloc(sizeof(struct asus_work_action), GFP_NOWAIT);
+ if (!action)
+ return -ENOMEM;
+
+ action->type = WMI_FAN;
+ action->data.fan_hid_data.report_size = report_size;
+ memcpy(action->data.fan_hid_data.report_data, report_data, report_size);
+ INIT_LIST_HEAD(&action->node);
+
+ spin_lock_irqsave(&drvdata->worker->lock, flags);
+ asus_worker_schedule(drvdata->worker, action);
+ spin_unlock_irqrestore(&drvdata->worker->lock, flags);
+
return 0;
}
@@ -357,6 +452,7 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ int ret;
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR &&
(usage->hid & HID_USAGE) != 0x00 &&
@@ -375,8 +471,11 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
return !asus_hid_event(ASUS_EV_BRTTOGGLE);
case KEY_FN_ESC:
if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
- drvdata->fn_lock = !drvdata->fn_lock;
- schedule_work(&drvdata->fn_lock_sync_work);
+ ret = asus_kbd_fn_lock_set(drvdata, !drvdata->fn_lock);
+ if (ret) {
+ hid_err(hdev, "Error while toggling FN lock: %d\n", ret);
+ return ret;
+ }
}
break;
}
@@ -389,6 +488,12 @@ static int asus_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *data, int size)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ int ret;
+
+ if (size < 2) {
+ hid_dbg(hdev, "Unexpected keyboard report size %d\n", size);
+ return 0;
+ }
if (drvdata->battery && data[0] == BATTERY_REPORT_ID)
return asus_report_battery(drvdata, data, size);
@@ -414,19 +519,13 @@ static int asus_raw_event(struct hid_device *hdev,
* pass to userspace so it can implement its own fan control.
*/
if (data[1] == ASUS_FAN_CTRL_KEY_CODE) {
- int ret = asus_wmi_send_event(drvdata, ASUS_FAN_CTRL_KEY_CODE);
+ ret = asus_kbd_wmi_fan_send(drvdata, data, size);
- if (ret == 0) {
- /* Successfully handled by asus-wmi, block event */
+ /* if execution deferred successfully block event */
+ if (ret == 0)
return -1;
- }
- /*
- * Warn if asus-wmi failed (but not if it's unavailable).
- * Let the event reach userspace in all failure cases.
- */
- if (ret != -ENODEV)
- hid_warn(hdev, "Failed to notify asus-wmi: %d\n", ret);
+ return ret;
}
/*
@@ -569,59 +668,157 @@ static int asus_kbd_disable_oobe(struct hid_device *hdev)
return 0;
}
-static int asus_kbd_set_fn_lock(struct hid_device *hdev, bool enabled)
+static void asus_kbd_set_fn_lock(struct hid_device *hdev, bool enabled)
{
- u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xd0, 0x4e, !!enabled };
+ const u8 buf[FEATURE_KBD_REPORT_SIZE] = { FEATURE_KBD_REPORT_ID, 0xd0, 0x4e, !!enabled };
+ int ret;
- return asus_kbd_set_report(hdev, buf, sizeof(buf));
+ ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
+ if (ret < 0)
+ hid_err(hdev, "Asus failed to set fn lock: %d\n", ret);
}
-static void asus_sync_fn_lock(struct work_struct *work)
+static void asus_kbd_set_brightness(struct hid_device *hdev, u8 brightness)
{
- struct asus_drvdata *drvdata =
- container_of(work, struct asus_drvdata, fn_lock_sync_work);
+ const u8 buf[FEATURE_KBD_REPORT_SIZE] = {
+ FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, brightness
+ };
+ int ret;
- asus_kbd_set_fn_lock(drvdata->hdev, drvdata->fn_lock);
+ ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
+ if (ret < 0)
+ hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret);
}
-static void asus_schedule_work(struct asus_kbd_leds *led)
+static void asus_kbd_wmi_fan(struct hid_device *hdev, struct hid_raw_event_data *data)
{
+ struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ int ret;
+
+ ret = asus_wmi_send_event(drvdata, ASUS_FAN_CTRL_KEY_CODE);
+
+ /*
+ * Warn if asus-wmi failed (but not if it's unavailable).
+ * Let the event reach userspace in all failure cases.
+ */
+ switch (ret) {
+ case -ENODEV:
+ break;
+ case 0:
+ return;
+ default:
+ hid_warn(hdev, "Failed to notify asus-wmi: %d\n", ret);
+ break;
+ }
+
+ /*
+ * Fallback: pass the raw event to the HID core; to avoid
+ * racing against the hid_report_raw_event() that generated
+ * this event use the same locking mechanism and wait for
+ * that function to terminate and signal the deferred execution
+ * before raising the stored event.
+ */
+ down(&hdev->driver_input_lock);
+ hid_report_raw_event(hdev, HID_INPUT_REPORT,
+ data->report_data, data->report_size,
+ data->report_size, 1);
+ up(&hdev->driver_input_lock);
+}
+
+static void asus_kbd_backlight_set(struct asus_hid_listener *listener, int brightness)
+{
+ struct asus_drvdata *drvdata = container_of(listener, struct asus_drvdata, listener);
+ struct asus_worker *worker = drvdata->worker;
+ struct asus_work_action *action;
unsigned long flags;
- spin_lock_irqsave(&led->lock, flags);
- if (!led->removed)
- schedule_work(&led->work);
- spin_unlock_irqrestore(&led->lock, flags);
+ drvdata->kbd_backlight_brightness = brightness;
+
+ action = kzalloc(sizeof(struct asus_work_action), GFP_NOWAIT);
+ if (!action)
+ return;
+
+ action->type = BRIGHTNESS_SET;
+ action->data.brightness = brightness;
+ INIT_LIST_HEAD(&action->node);
+
+ spin_lock_irqsave(&worker->lock, flags);
+ asus_worker_schedule(worker, action);
+ spin_unlock_irqrestore(&worker->lock, flags);
}
-static void asus_kbd_backlight_set(struct asus_hid_listener *listener,
- int brightness)
+static void asus_work(struct work_struct *work)
{
- struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds,
- listener);
+ struct asus_worker *worker = container_of(work, struct asus_worker, work);
+ struct asus_work_action *action = NULL;
unsigned long flags;
- spin_lock_irqsave(&led->lock, flags);
- led->brightness = brightness;
- spin_unlock_irqrestore(&led->lock, flags);
+ /* Save the action to be performed and clear the flag */
+ spin_lock_irqsave(&worker->lock, flags);
+ if (!list_empty(&worker->actions)) {
+ action = list_first_entry(&worker->actions,
+ struct asus_work_action, node);
+ list_del(&action->node);
+ }
+ spin_unlock_irqrestore(&worker->lock, flags);
+
+ if (!action)
+ return;
+
+ switch (action->type) {
+ case BRIGHTNESS_SET:
+ asus_kbd_set_brightness(worker->hdev, action->data.brightness);
+ break;
+ case FN_LOCK_SYNC:
+ asus_kbd_set_fn_lock(worker->hdev, action->data.fn_lock);
+ break;
+ case WMI_FAN:
+ asus_kbd_wmi_fan(worker->hdev, &action->data.fan_hid_data);
+ break;
+ default:
+ hid_err(worker->hdev, "Invalid action type: %d\n", action->type);
+ break;
+ }
+
+ kfree(action);
- asus_schedule_work(led);
+ /* Re-schedule if there are more pending actions */
+ spin_lock_irqsave(&worker->lock, flags);
+ if (!list_empty(&worker->actions))
+ schedule_work(&worker->work);
+ spin_unlock_irqrestore(&worker->lock, flags);
}
-static void asus_kbd_backlight_work(struct work_struct *work)
+static int asus_worker_create(struct hid_device *hdev, struct asus_drvdata *drvdata)
{
- struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
- u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
- int ret;
+ drvdata->worker = devm_kzalloc(&hdev->dev, sizeof(struct asus_worker), GFP_KERNEL);
+ if (!drvdata->worker)
+ return -ENOMEM;
+
+ drvdata->worker->removed = false;
+ drvdata->worker->hdev = hdev;
+ INIT_LIST_HEAD(&drvdata->worker->actions);
+
+ INIT_WORK(&drvdata->worker->work, asus_work);
+ spin_lock_init(&drvdata->worker->lock);
+
+ return 0;
+}
+
+static void asus_worker_stop(struct asus_worker *worker)
+{
+ struct asus_work_action *action, *tmp;
unsigned long flags;
- spin_lock_irqsave(&led->lock, flags);
- buf[4] = led->brightness;
- spin_unlock_irqrestore(&led->lock, flags);
+ spin_lock_irqsave(&worker->lock, flags);
+ worker->removed = true;
+ list_for_each_entry_safe(action, tmp, &worker->actions, node) {
+ list_del(&action->node);
+ kfree(action);
+ }
+ spin_unlock_irqrestore(&worker->lock, flags);
- ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf));
- if (ret < 0)
- hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
+ cancel_work_sync(&worker->work);
}
/*
@@ -760,23 +957,11 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
le16_to_cpu(udev->descriptor.idProduct));
}
- drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
- sizeof(struct asus_kbd_leds),
- GFP_KERNEL);
- if (!drvdata->kbd_backlight)
- return -ENOMEM;
-
- drvdata->kbd_backlight->removed = false;
- drvdata->kbd_backlight->brightness = 0;
- drvdata->kbd_backlight->hdev = hdev;
- drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set;
- INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
- spin_lock_init(&drvdata->kbd_backlight->lock);
-
- ret = asus_hid_register_listener(&drvdata->kbd_backlight->listener);
+ drvdata->listener.brightness_set = asus_kbd_backlight_set;
+ ret = asus_hid_register_listener(&drvdata->listener);
if (ret < 0) {
- /* No need to have this still around */
- devm_kfree(&hdev->dev, drvdata->kbd_backlight);
+ hid_err(hdev, "Unable to register kbd brightness listener: %d\n", ret);
+ drvdata->listener.brightness_set = NULL;
}
return ret;
@@ -965,11 +1150,9 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
}
}
- if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
- drvdata->fn_lock = true;
- INIT_WORK(&drvdata->fn_lock_sync_work, asus_sync_fn_lock);
- asus_kbd_set_fn_lock(hdev, true);
- }
+ if ((drvdata->quirks & QUIRK_HID_FN_LOCK) &&
+ (asus_kbd_fn_lock_set(drvdata, true)))
+ hid_warn(hdev, "Error while setting FN lock to ON\n");
if (drvdata->tp) {
int ret;
@@ -1004,11 +1187,9 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
drvdata->input = input;
- if (drvdata->quirks & QUIRK_HID_FN_LOCK) {
- drvdata->fn_lock = true;
- INIT_WORK(&drvdata->fn_lock_sync_work, asus_sync_fn_lock);
- asus_kbd_set_fn_lock(hdev, true);
- }
+ if ((drvdata->quirks & QUIRK_HID_FN_LOCK) &&
+ (asus_kbd_fn_lock_set(drvdata, true)))
+ hid_warn(hdev, "Error while setting FN lock to ON\n");
return 0;
}
@@ -1171,20 +1352,16 @@ static int asus_start_multitouch(struct hid_device *hdev)
static int __maybe_unused asus_resume(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
- int ret = 0;
- if (drvdata->kbd_backlight) {
- const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4,
- drvdata->kbd_backlight->brightness };
- ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
- if (ret < 0) {
- hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret);
- goto asus_resume_err;
- }
- }
+ /*
+ * If we have a backlight listener registered, restore the previous state,
+ * in case of error do not fail: most models restore the backlight
+ * automatically, and the error is non-fatal.
+ */
+ if (drvdata->listener.brightness_set)
+ asus_kbd_backlight_set(&drvdata->listener, drvdata->kbd_backlight_brightness);
-asus_resume_err:
- return ret;
+ return 0;
}
static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
@@ -1294,8 +1471,15 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
is_vendor = true;
}
+ ret = asus_worker_create(hdev, drvdata);
+ if (ret) {
+ hid_warn(hdev, "Failed to initialize worker: %d\n", ret);
+ return ret;
+ }
+
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
+ asus_worker_stop(drvdata->worker);
hid_err(hdev, "Asus hw start failed: %d\n", ret);
return ret;
}
@@ -1343,6 +1527,10 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
return 0;
err_stop_hw:
+ if (drvdata->listener.brightness_set)
+ asus_hid_unregister_listener(&drvdata->listener);
+
+ asus_worker_stop(drvdata->worker);
hid_hw_stop(hdev);
return ret;
}
@@ -1350,21 +1538,11 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
static void asus_remove(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
- unsigned long flags;
-
- if (drvdata->kbd_backlight) {
- asus_hid_unregister_listener(&drvdata->kbd_backlight->listener);
-
- spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags);
- drvdata->kbd_backlight->removed = true;
- spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags);
-
- cancel_work_sync(&drvdata->kbd_backlight->work);
- }
- if (drvdata->quirks & QUIRK_HID_FN_LOCK)
- cancel_work_sync(&drvdata->fn_lock_sync_work);
+ if (drvdata->listener.brightness_set)
+ asus_hid_unregister_listener(&drvdata->listener);
+ asus_worker_stop(drvdata->worker);
hid_hw_stop(hdev);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 2/5] HID: asus: remove extraneous OOM error
2026-06-19 0:10 [PATCH v5 0/5] HID: asus: security fixes and more hardware support Denis Benato
2026-06-19 0:10 ` [PATCH v5 1/5] HID: asus: refactor the two workqueues and init sequence Denis Benato
@ 2026-06-19 0:11 ` Denis Benato
2026-06-19 0:11 ` [PATCH v5 3/5] HID: asus: fix a off-by-one in mcu_parse_version_string() validation Denis Benato
` (2 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Denis Benato @ 2026-06-19 0:11 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli
If devm_kzalloc fails an allocation error is already being reported:
no need to repeat it. For new code this behavior is disincentivized
and checkpatch.pl reports a warning.
Reviewed-by: Antheas Kapenekakis <lkml@antheas.dev>
Signed-off-by: Denis Benato <denis.benato@linux.dev>
---
drivers/hid/hid-asus.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 40191eb9e2e8..d31e71ce3d19 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -1383,10 +1383,8 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
int ret;
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
- if (drvdata == NULL) {
- hid_err(hdev, "Can't alloc Asus descriptor\n");
+ if (drvdata == NULL)
return -ENOMEM;
- }
hid_set_drvdata(hdev, drvdata);
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 3/5] HID: asus: fix a off-by-one in mcu_parse_version_string() validation
2026-06-19 0:10 [PATCH v5 0/5] HID: asus: security fixes and more hardware support Denis Benato
2026-06-19 0:10 ` [PATCH v5 1/5] HID: asus: refactor the two workqueues and init sequence Denis Benato
2026-06-19 0:11 ` [PATCH v5 2/5] HID: asus: remove extraneous OOM error Denis Benato
@ 2026-06-19 0:11 ` Denis Benato
2026-06-19 0:11 ` [PATCH v5 4/5] HID: asus: add support for xgm led Denis Benato
2026-06-19 0:11 ` [PATCH v5 5/5] HID: asus: add i2c entry for FA808UM and other TUFs Denis Benato
4 siblings, 0 replies; 8+ messages in thread
From: Denis Benato @ 2026-06-19 0:11 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli
In mcu_parse_version_string() a size validation for response is stricter
that it needs to be: relax the check by one byte.
The device always answer with a greater byte count so this does
not introduce visible changes.
Fixes: ("hid-asus: check ROG Ally MCU version and warn")
Signed-off-by: Denis Benato <denis.benato@linux.dev>
---
drivers/hid/hid-asus.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index d31e71ce3d19..0fb8cd6437b7 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -841,7 +841,7 @@ static int mcu_parse_version_string(const u8 *response, size_t response_size)
dots++;
}
- if (dots != 2 || p >= end || (p + 3) >= end)
+ if (dots != 2 || end - p < 3)
return -EINVAL;
memcpy(buf, p, 3);
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 4/5] HID: asus: add support for xgm led
2026-06-19 0:10 [PATCH v5 0/5] HID: asus: security fixes and more hardware support Denis Benato
` (2 preceding siblings ...)
2026-06-19 0:11 ` [PATCH v5 3/5] HID: asus: fix a off-by-one in mcu_parse_version_string() validation Denis Benato
@ 2026-06-19 0:11 ` Denis Benato
2026-06-19 0:23 ` sashiko-bot
2026-06-19 0:11 ` [PATCH v5 5/5] HID: asus: add i2c entry for FA808UM and other TUFs Denis Benato
4 siblings, 1 reply; 8+ messages in thread
From: Denis Benato @ 2026-06-19 0:11 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli
XG mobile stations have very bright leds behind the fan that can be
turned either ON or OFF: add a cled interface to allow controlling the
brightness of those red leds.
Reviewed-by: Antheas Kapenekakis <lkml@antheas.dev>
Signed-off-by: Denis Benato <denis.benato@linux.dev>
---
drivers/hid/hid-asus.c | 94 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 0fb8cd6437b7..06e4888f090c 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -51,6 +51,8 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
#define FEATURE_KBD_LED_REPORT_ID2 0x5e
+#define ROG_XGM_REPORT_SIZE 300
+
#define ROG_ALLY_REPORT_SIZE 64
#define ROG_ALLY_X_MIN_MCU 313
#define ROG_ALLY_MIN_MCU 319
@@ -143,6 +145,11 @@ struct asus_worker {
bool removed;
};
+struct asus_xgm_led {
+ struct led_classdev cdev;
+ struct hid_device *hdev;
+};
+
struct asus_touchpad_info {
int max_x;
int max_y;
@@ -169,6 +176,7 @@ struct asus_drvdata {
unsigned long battery_next_query;
struct asus_hid_listener listener;
bool fn_lock;
+ struct asus_xgm_led *xgm_led;
};
static int asus_report_battery(struct asus_drvdata *, u8 *, int);
@@ -1125,6 +1133,26 @@ static int asus_battery_probe(struct hid_device *hdev)
return ret;
}
+static int asus_xgm_led_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ const u8 buf[ROG_XGM_REPORT_SIZE] = {
+ FEATURE_KBD_LED_REPORT_ID2, 0xC5, (value) ? 0x50 : 0x00
+ };
+ struct asus_xgm_led *xgm = container_of(led_cdev, struct asus_xgm_led, cdev);
+ int ret;
+
+ ret = asus_kbd_set_report(xgm->hdev, buf, ROG_XGM_REPORT_SIZE);
+ if (ret < 0) {
+ hid_err(xgm->hdev, "Unable to set XG mobile led state: %d\n", ret);
+ return ret;
+ } else if (ret != ROG_XGM_REPORT_SIZE) {
+ hid_err(xgm->hdev, "Unexpected partial transfer to XG mobile: %d\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct input_dev *input = hi->input;
@@ -1349,9 +1377,52 @@ static int asus_start_multitouch(struct hid_device *hdev)
return 0;
}
+static int asus_xgm_init(struct hid_device *hdev, struct asus_drvdata *drvdata)
+{
+ const char *name;
+ int ret;
+
+ drvdata->xgm_led = devm_kzalloc(&hdev->dev, sizeof(*drvdata->xgm_led), GFP_KERNEL);
+ if (drvdata->xgm_led == NULL)
+ return -ENOMEM;
+
+ name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "asus:xgm-%s:led",
+ strlen(hdev->uniq) ? hdev->uniq : dev_name(&hdev->dev));
+
+ if (name == NULL) {
+ ret = -ENOMEM;
+ goto asus_xgm_init_err;
+ }
+
+ drvdata->xgm_led->hdev = hdev;
+ drvdata->xgm_led->cdev.name = name;
+ drvdata->xgm_led->cdev.brightness = 1;
+ drvdata->xgm_led->cdev.max_brightness = 1;
+ drvdata->xgm_led->cdev.brightness_set_blocking = asus_xgm_led_set;
+
+ /* LED state is arbitrary on boot, set a default */
+ ret = asus_xgm_led_set(&drvdata->xgm_led->cdev, drvdata->xgm_led->cdev.brightness);
+ if (ret) {
+ hid_err(hdev, "Asus failed to set xgm led: %d\n", ret);
+ goto asus_xgm_init_err;
+ }
+
+ ret = devm_led_classdev_register(&hdev->dev, &drvdata->xgm_led->cdev);
+ if (ret) {
+ hid_err(hdev, "Asus failed to register xgm led: %d\n", ret);
+ goto asus_xgm_init_err;
+ }
+
+ return 0;
+asus_xgm_init_err:
+ drvdata->xgm_led = NULL;
+ return ret;
+}
+
static int __maybe_unused asus_resume(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ int ret;
/*
* If we have a backlight listener registered, restore the previous state,
@@ -1361,7 +1432,17 @@ static int __maybe_unused asus_resume(struct hid_device *hdev)
if (drvdata->listener.brightness_set)
asus_kbd_backlight_set(&drvdata->listener, drvdata->kbd_backlight_brightness);
+ if (drvdata->xgm_led) {
+ ret = asus_xgm_led_set(&drvdata->xgm_led->cdev, drvdata->xgm_led->cdev.brightness);
+ if (ret) {
+ hid_err(hdev, "Asus failed to restore xgm brightness: %d\n", ret);
+ goto asus_resume_err;
+ }
+ }
+
return 0;
+asus_resume_err:
+ return ret;
}
static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
@@ -1491,6 +1572,16 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
+ if (asus_has_report_id(hdev, FEATURE_KBD_REPORT_ID) &&
+ ((hdev->product == USB_DEVICE_ID_ASUSTEK_XGM_2022) ||
+ (hdev->product == USB_DEVICE_ID_ASUSTEK_XGM_2023))) {
+ ret = asus_xgm_init(hdev, drvdata);
+ if (ret) {
+ hid_err(hdev, "Failed to initialize xg mobile: %d\n", ret);
+ goto err_stop_hw;
+ }
+ }
+
/* Laptops keyboard backlight is always at 0x5a */
if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) &&
(asus_has_report_id(hdev, FEATURE_KBD_REPORT_ID)) &&
@@ -1540,6 +1631,9 @@ static void asus_remove(struct hid_device *hdev)
if (drvdata->listener.brightness_set)
asus_hid_unregister_listener(&drvdata->listener);
+ if (drvdata->xgm_led)
+ devm_led_classdev_unregister(&hdev->dev, &drvdata->xgm_led->cdev);
+
asus_worker_stop(drvdata->worker);
hid_hw_stop(hdev);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v5 5/5] HID: asus: add i2c entry for FA808UM and other TUFs
2026-06-19 0:10 [PATCH v5 0/5] HID: asus: security fixes and more hardware support Denis Benato
` (3 preceding siblings ...)
2026-06-19 0:11 ` [PATCH v5 4/5] HID: asus: add support for xgm led Denis Benato
@ 2026-06-19 0:11 ` Denis Benato
4 siblings, 0 replies; 8+ messages in thread
From: Denis Benato @ 2026-06-19 0:11 UTC (permalink / raw)
To: linux-kernel
Cc: linux-input, Benjamin Tissoires, Jiri Kosina, Luke D . Jones,
Mateusz Schyboll, Denis Benato, Denis Benato, Antheas Kapenekakis,
Connor Belli
On newer TUF laptops the keyboard HID device uses the same PID/VID of a
USB device that was found in ROG laptops: add it to hid-asus as i2c too.
Signed-off-by: Denis Benato <denis.benato@linux.dev>
---
drivers/hid/hid-asus.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 06e4888f090c..b155eaf106a5 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -1770,6 +1770,9 @@ static const struct hid_device_id asus_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_HID_FN_LOCK },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK,
+ USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_HID_FN_LOCK },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v5 1/5] HID: asus: refactor the two workqueues and init sequence
2026-06-19 0:10 ` [PATCH v5 1/5] HID: asus: refactor the two workqueues and init sequence Denis Benato
@ 2026-06-19 0:23 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-06-19 0:23 UTC (permalink / raw)
To: Denis Benato; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] ABBA deadlock between driver removal/probe failure and the deferred WMI_FAN action in the workqueue due to inverted lock acquisition order of `hdev->driver_input_lock`.
- [High] Unbounded dynamic allocation of workqueue actions allows an untrusted USB device or unprivileged user to exhaust kernel atomic memory pools.
- [Medium] Passing the raw packet size as `bufsize` to `hid_report_raw_event()` bypasses the HID core's automatic padding, leading to valid short packets being incorrectly rejected.
--
commit 656f5a6cc923dccccd418cd0aefa2239f221bf47
Author: Denis Benato <denis.benato@linux.dev>
HID: asus: refactor the two workqueues and init sequence
Multiple issues have been found within the hid-asus driver. This patch
joins the two workqueues into one and reuses the stopping mechanism
of the brightness workqueue. It also moves the asus_wmi_send_event()
sleeping call away from atomic context.
Link: https://lore.kernel.org/all/20260613154732.60A4B1F000E9@smtp.kernel.org/
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index d34d74df3dc0b..40191eb9e2e86 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -331,25 +380,71 @@ static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size)
> }
[ ... ]
> +static int asus_kbd_wmi_fan_send(struct asus_drvdata *drvdata, u8 *report_data,
> + size_t report_size)
> +{
> + struct asus_work_action *action;
> + unsigned long flags;
> +
> + if (report_size > FEATURE_KBD_REPORT_SIZE) {
> + hid_err(drvdata->hdev, "Invalid report size for fan event: %zu\n", report_size);
> + return -EINVAL;
> }
>
> + action = kzalloc(sizeof(struct asus_work_action), GFP_NOWAIT);
[Severity: High]
Does this code allow an unbounded number of allocations?
If a device sends a flood of fan events, or if a local user rapidly changes
the brightness, it seems this could queue an unlimited number of actions
using GFP_NOWAIT or GFP_ATOMIC.
Could this exhaust kernel atomic memory pools and lead to a local denial of
service?
> + if (!action)
> + return -ENOMEM;
> +
> + action->type = WMI_FAN;
> + action->data.fan_hid_data.report_size = report_size;
> + memcpy(action->data.fan_hid_data.report_data, report_data, report_size);
> + INIT_LIST_HEAD(&action->node);
> +
> + spin_lock_irqsave(&drvdata->worker->lock, flags);
> + asus_worker_schedule(drvdata->worker, action);
> + spin_unlock_irqrestore(&drvdata->worker->lock, flags);
>
> return 0;
> }
[ ... ]
> @@ -569,59 +668,157 @@ static int asus_kbd_disable_oobe(struct hid_device *hdev)
[ ... ]
> +static void asus_kbd_wmi_fan(struct hid_device *hdev, struct hid_raw_event_data *data)
> {
[ ... ]
> + /*
> + * Fallback: pass the raw event to the HID core; to avoid
> + * racing against the hid_report_raw_event() that generated
> + * this event use the same locking mechanism and wait for
> + * that function to terminate and signal the deferred execution
> + * before raising the stored event.
> + */
> + down(&hdev->driver_input_lock);
[Severity: High]
Can this code cause an ABBA deadlock during device probe failure or removal?
The HID core holds hdev->driver_input_lock during hid_device_probe() and
hid_device_remove().
If asus_remove() or the error path in asus_probe() calls asus_worker_stop()
and blocks on cancel_work_sync(&worker->work) while driver_input_lock is
held:
HID core thread:
hid_device_remove() (acquires driver_input_lock)
-> asus_remove()
-> asus_worker_stop()
-> cancel_work_sync() (waits for worker)
Worker thread:
asus_work()
-> asus_kbd_wmi_fan()
-> down(&hdev->driver_input_lock) (waits for HID core thread)
Would this lead to an unrecoverable deadlock?
> + hid_report_raw_event(hdev, HID_INPUT_REPORT,
> + data->report_data, data->report_size,
> + data->report_size, 1);
[Severity: Medium]
Does passing data->report_size as the bufsize argument drop valid short
packets?
The HID core natively handles short packets by padding the buffer up to the
expected report descriptor size (rsize), provided the allocated buffer size
(bufsize) is large enough.
If bufsize is artificially restricted to data->report_size, the safety check
"if (bsize < rsize)" inside hid_report_raw_event() might falsely trigger and
reject the valid short event with -EINVAL.
Should FEATURE_KBD_REPORT_SIZE be used as the bufsize parameter instead?
> + up(&hdev->driver_input_lock);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260619001103.1189200-1-denis.benato@linux.dev?part=1
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v5 4/5] HID: asus: add support for xgm led
2026-06-19 0:11 ` [PATCH v5 4/5] HID: asus: add support for xgm led Denis Benato
@ 2026-06-19 0:23 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-06-19 0:23 UTC (permalink / raw)
To: Denis Benato; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] A failure to initialize the cosmetic XG Mobile LED aborts the entire HID device probe.
- [High] Failing to restore the XGM LED brightness causes the entire HID device resume process to fail.
Pre-existing issues:
- [High] The devm-managed battery is not explicitly unregistered before stopping the hardware, leading to use-after-stop during teardown.
--
commit 7aa41eaa0bd3e715bef4c850703c7d47445cad14
Author: Denis Benato <denis.benato@linux.dev>
HID: asus: add support for xgm led
XG mobile stations have very bright leds behind the fan that can be
turned either ON or OFF: add a cled interface to allow controlling the
brightness of those red leds.
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 0fb8cd6437b77..06e4888f090ca 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
[ ... ]
> @@ -1361,7 +1432,17 @@ static int __maybe_unused asus_resume(struct hid_device *hdev)
> if (drvdata->listener.brightness_set)
> asus_kbd_backlight_set(&drvdata->listener, drvdata->kbd_backlight_brightness);
>
> + if (drvdata->xgm_led) {
> + ret = asus_xgm_led_set(&drvdata->xgm_led->cdev, drvdata->xgm_led->cdev.brightness);
> + if (ret) {
> + hid_err(hdev, "Asus failed to restore xgm brightness: %d\n", ret);
> + goto asus_resume_err;
> + }
> + }
> +
> return 0;
> +asus_resume_err:
> + return ret;
[Severity: High]
In asus_resume(), if asus_xgm_led_set() fails to restore the LED state,
returning an error causes the entire HID device resume process to fail.
Will this leave the device in an inconsistent state and break input
functionality?
As documented in the comments for the backlight, non-fatal LED state
restorations shouldn't fail the system resume. Could this just ignore the error
here as well?
[ ... ]
> @@ -1491,6 +1572,16 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
> }
> }
>
> + if (asus_has_report_id(hdev, FEATURE_KBD_REPORT_ID) &&
> + ((hdev->product == USB_DEVICE_ID_ASUSTEK_XGM_2022) ||
> + (hdev->product == USB_DEVICE_ID_ASUSTEK_XGM_2023))) {
> + ret = asus_xgm_init(hdev, drvdata);
> + if (ret) {
> + hid_err(hdev, "Failed to initialize xg mobile: %d\n", ret);
> + goto err_stop_hw;
> + }
> + }
> +
[Severity: High]
If asus_xgm_init() fails due to a transient USB error during the initial
asus_xgm_led_set() call, will this completely abort the HID device probe and
disable the keyboard?
Since similar features like the keyboard backlight only emit a warning on
failure, should a non-fatal cosmetic LED initialization failure also just warn
and allow the driver to continue normal operation?
[ ... ]
> @@ -1540,6 +1631,9 @@ static void asus_remove(struct hid_device *hdev)
> if (drvdata->listener.brightness_set)
> asus_hid_unregister_listener(&drvdata->listener);
>
> + if (drvdata->xgm_led)
> + devm_led_classdev_unregister(&hdev->dev, &drvdata->xgm_led->cdev);
> +
> asus_worker_stop(drvdata->worker);
> hid_hw_stop(hdev);
> }
[Severity: High]
This is a pre-existing issue, but the devm-managed battery is not explicitly
unregistered before stopping the hardware.
Because the battery is registered via devm_power_supply_register() before
hid_hw_start(), and hid_hw_stop() is called here before the devres cleanup,
could this lead to a use-after-stop?
If a concurrent read of the battery sysfs properties happens during teardown, it
triggers asus_battery_get_property() which calls hid_hw_raw_request() on a
stopped HID device. Can this cause bus hangs or undefined behavior?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260619001103.1189200-1-denis.benato@linux.dev?part=4
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-19 0:23 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-19 0:10 [PATCH v5 0/5] HID: asus: security fixes and more hardware support Denis Benato
2026-06-19 0:10 ` [PATCH v5 1/5] HID: asus: refactor the two workqueues and init sequence Denis Benato
2026-06-19 0:23 ` sashiko-bot
2026-06-19 0:11 ` [PATCH v5 2/5] HID: asus: remove extraneous OOM error Denis Benato
2026-06-19 0:11 ` [PATCH v5 3/5] HID: asus: fix a off-by-one in mcu_parse_version_string() validation Denis Benato
2026-06-19 0:11 ` [PATCH v5 4/5] HID: asus: add support for xgm led Denis Benato
2026-06-19 0:23 ` sashiko-bot
2026-06-19 0:11 ` [PATCH v5 5/5] HID: asus: add i2c entry for FA808UM and other TUFs Denis Benato
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox