Re: [PATCH v2 2/6] HID: steam: Initial 2026 Steam Controller support
[email protected] Thu, 06 Aug 2026 02:49:30 +0000
| Newsgroups | org.kernel.vger.linux-input |
|---|---|
| Message-ID | <[email protected]> |
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: New issues: - [Medium] The removal of the explicit query workaround in `steam_write_settings` can cause driver initialization to fail on subsequent driver binds or module reloads due to lingering reports. Pre-existing issues: - [High] Use-After-Free due to HID events triggering work re-queuing during teardown because `hid_hw_stop` is called after `cancel_work_sync`. - [High] Concurrent execution of `steam_register` leads to resource leaks and Use-After-Free/Double-Free on error paths due to missing locking. - [High] Race condition in teardown functions (`steam_battery_unregister`, `steam_input_unregister`, etc.) leads to double-free of device components. -- --- Patch [2]: [PATCH v2 2/6] HID: steam: Initial 2026 Steam Controller support --- commit 4e3ca92dc2f8afcf2d70cdcfce3822923a4ccbfc Author: Vicki Pfau <[email protected]> HID: steam: Initial 2026 Steam Controller support This brings support for the 2026 Steam Controller, comparably featureful to the existing support for the Steam Deck. > diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c > index 967f12c7a599a..6476fbdc58886 100644 > --- a/drivers/hid/hid-steam.c > +++ b/drivers/hid/hid-steam.c [ ... ] > @@ -489,16 +615,7 @@ static int steam_write_settings(struct steam_device *steam, > } > va_end(args); > > - ret = steam_send_report(steam, cmd, 2 + cmd[1]); > - if (ret < 0) > - return ret; > - > - /* > - * Sometimes a lingering report for this command can > - * get read back instead of the last set report if > - * this isn't explicitly queried > - */ > - return steam_recv_report(steam, cmd, 2 + cmd[1]); > + return steam_send_report(steam, cmd, 2 + cmd[1]); [Severity: Medium] By removing the explicit steam_recv_report() call here, could a lingering report be left in the device buffer? If the module is later reloaded or the driver rebound, steam_probe() will call steam_get_conn_status() which expects a wireless state report. > } [ ... ] > -/* > - * This command requests the wireless adaptor to post an event > - * with the connection status. Useful if this driver is loaded when > - * the controller is already connected. > - */ > -static inline int steam_request_conn_status(struct steam_device *steam) > +static int steam_get_conn_status(struct steam_device *steam) > { > + int ret = 0; > + u8 cmd[] = {ID_DONGLE_GET_WIRELESS_STATE}; > + u8 reply[3] = {}; > + u8 report_id; > + > + if (steam->quirks & STEAM_QUIRK_IBEX) > + report_id = REPORT_ID_FEATURES_DONGLE; > + else > + report_id = 0; > + > guard(mutex)(&steam->report_mutex); > - return steam_send_report_byte(steam, ID_DONGLE_GET_WIRELESS_STATE); > + ret = steam_send_report_id(steam, cmd, sizeof(cmd), report_id); > + if (ret < 0) > + return ret; > + ret = steam_recv_report_id(steam, reply, sizeof(reply), report_id); > + if (ret < 0) > + return ret; > + if (reply[0] != ID_DONGLE_GET_WIRELESS_STATE || reply[1] < 1) { > + hid_err(steam->hdev, "%s: invalid reply (%*ph)\n", __func__, > + (int)sizeof(reply), reply); > + return -EIO; > + } > + > + return reply[2]; > } [Severity: Medium] Does this function read the lingering ID_SET_SETTINGS_VALUES report instead of the expected status? Because this lacks a retry mechanism, it appears this would fail the reply[0] != ID_DONGLE_GET_WIRELESS_STATE check and return -EIO, breaking device initialization. [ ... ] > @@ -1149,51 +1377,42 @@ static int steam_register(struct steam_device *steam) > * another client send a get_connection_status command, for example. > * The battery and serial number are set just once per device. > */ > - if (!steam->serial_no[0]) { > - /* > - * Unlikely, but getting the serial could fail, and it is not so > - * important, so make up a serial number and go on. > - */ > - if (steam_get_serial(steam) < 0) > - strscpy(steam->serial_no, "XXXXXXXXXX", > - sizeof(steam->serial_no)); > - > - ret = steam_get_attributes(steam); > - if (ret < 0) > - hid_err(steam->hdev, > - "%s:steam_get_attributes failed with error %d\n", > - __func__, ret); > + if (steam->serial_no[0]) > + return 0; [Severity: High] This is a pre-existing issue, but does this check allow concurrent execution? If a wireless connect event triggers steam_work_connect_cb() simultaneously with steam_probe() invoking steam_get_conn_status(), could both threads evaluate steam->serial_no[0] as empty because there is no locking here? If so, both could proceed to allocate and register input devices and batteries. Could this potentially overwrite the pointers and leak the first set, or cause a use-after-free when one thread fails and runs the error cleanup path on resources the other thread just allocated? [ ... ] > - if (do_add) { > - mutex_lock(&steam_devices_lock); > - if (list_empty(&steam->list)) > - list_add(&steam->list, &steam_devices); > - mutex_unlock(&steam_devices_lock); > - } > + mutex_lock(&steam_devices_lock); > + if (list_empty(&steam->list)) > + list_add(&steam->list, &steam_devices); > + mutex_unlock(&steam_devices_lock); > return 0; > > steam_register_sensors_fail: [Severity: High] This is a pre-existing issue, but does the teardown sequence in steam_battery_unregister() and steam_input_unregister() have a race condition? If these unregister functions are called concurrently (for example, if steam_register() fails on one thread while steam_work_connect_cb() processes a disconnect event on another), both threads could read the same component pointer within the RCU read-side critical section before either thread executes RCU_INIT_POINTER(..., NULL). Would this cause both threads to call power_supply_unregister() and devm_kfree() on the exact same pointer, leading to a double-free? [ ... ] [Severity: High] This is a pre-existing issue, but can the sequence in steam_remove() lead to a use-after-free? drivers/hid/hid-steam.c:steam_remove() { ... cancel_work_sync(&steam->work_connect); ... hid_hw_stop(hdev); } Because hid_hw_stop() is called after cancel_work_sync(), the HID transport can still deliver events during this window. If an event like REPORT_ID_WIRELESS_EVENT arrives, steam_raw_event() will call steam_do_connect_event(), which calls schedule_work(&steam->work_connect), re-queuing the work after it was cancelled. Once steam_remove() returns and devres frees the steam_device structure, will the workqueue run steam_work_connect_cb() on the freed memory? -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=2