Re: [PATCH 1/5] HID: steam: Initial 2026 Steam Controller support

[email protected] Wed, 05 Aug 2026 02:13:03 +0000
Newsgroups org.kernel.vger.linux-input
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 5 potential issue(=
s) to consider:

New issues:
- [High] Battery registration fails due to thermal zone name length limits =
if the device serial number is long.
- [Medium] Incorrect initialization of battery temperature results in repor=
ting 0.2 =C2=B0C.
- [Low] Duplicate error logging block introduced in steam_recv_report_id.

Pre-existing issues:
- [High] Probe failure leaves a registered power supply with dangling devm =
pointers.
- [High] Unbinding the driver while the device is active can lead to a Use-=
After-Free in steam_work_connect_cb.
--

--- Patch [1]: [PATCH 1/5] HID: steam: Initial 2026 Steam Controller suppor=
t ---
commit 10f5a4093529f8973ab3200723fc61d0c419c6bb
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 6199f67f3c4cd..111e57926d049 100644
> --- a/drivers/hid/hid-steam.c
> +++ b/drivers/hid/hid-steam.c

[ ... ]

> @@ -378,21 +459,38 @@ static int steam_recv_report(struct steam_device *s=
team,
>  		return -ENOMEM;
> =20
>  	/*
> -	 * The report ID is always 0, so strip the first byte from the output.
> +	 * The report ID is consistent, so strip the first byte from the output.
>  	 * hid_report_len() is not counting the report ID, so +1 to the length
>  	 * or else we get a EOVERFLOW. We are safe from a buffer overflow
>  	 * because hid_alloc_report_buf() allocates +7 bytes.
>  	 */
> -	ret =3D hid_hw_raw_request(steam->hdev, 0x00,
> -			buf, hid_report_len(r) + 1,
> -			HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
> +	if (!(steam->quirks & STEAM_QUIRK_IBEX))
> +		len +=3D 1;
> +
> +	/*
> +	 * Sometimes the wireless controller fails with EPIPE
> +	 * when sending a feature report.
> +	 * Doing a HID_REQ_GET_REPORT and waiting for a while
> +	 * seems to fix that.
> +	 */
> +	do {
> +		ret =3D hid_hw_raw_request(steam->hdev, report_id,
> +				buf, len,
> +				HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
> +		if (ret !=3D -EPIPE)
> +			break;
> +		msleep(20);
> +	} while (--retries);
>  	if (ret > 0) {
>  		/* Remove the report ID from the return buffer */
>  		ret--;
>  		size =3D min(size, ret);
>  		memcpy(data, buf + 1, size);
>  	}
> +
>  	kfree(buf);
> +	if (ret < 0)
> +		hid_err(steam->hdev, "%s: error %d\n", __func__, ret);
> =20
>  	if (ret < 0)
>  		hid_err(steam->hdev, "%s: error %d\n", __func__, ret);

[Severity: Low]
Is this error logging block accidentally duplicated? There are two identical
checks and hid_err() calls sequentially in steam_recv_report_id(), which wi=
ll
cause the exact same error message to be printed twice upon a single
hid_hw_raw_request() failure.

[ ... ]

> @@ -854,19 +1051,35 @@ static int steam_battery_register(struct steam_dev=
ice *steam)
>  	int ret;
> =20
>  	steam->battery_desc.type =3D POWER_SUPPLY_TYPE_BATTERY;
> -	steam->battery_desc.properties =3D steam_battery_props;
> -	steam->battery_desc.num_properties =3D ARRAY_SIZE(steam_battery_props);
> +	if (steam->quirks & STEAM_QUIRK_IBEX) {
> +		steam->battery_desc.properties =3D steam_ibex_battery_props;
> +		steam->battery_desc.num_properties =3D ARRAY_SIZE(steam_ibex_battery_p=
rops);
> +		/*
> +		 * Ibex needs a shorter name as it has a temperature and the
> +		 * thermal zone name length limit is 20 characters. It's more
> +		 * ambiguous sounding, so let's only use it when needed.
> +		 */
> +		steam->battery_desc.name =3D devm_kasprintf(&steam->hdev->dev,
> +				GFP_KERNEL, "steam-%s",
> +				steam->serial_no);

[Severity: High]
Will this name exceed the 20-character maximum permitted for a thermal zone
device type if the serial number is long?

Because STEAM_SERIAL_LEN is 21, a serial number of 14 characters or
longer combined with the "steam-" prefix will exceed 20 characters. This
causes thermal_zone_device_register_with_trips() to return -EINVAL,
resulting in power_supply_register() and steam_battery_register() failing
entirely.

> +	} else {
> +		steam->battery_desc.properties =3D steam_battery_props;
> +		steam->battery_desc.num_properties =3D ARRAY_SIZE(steam_battery_props);
> +		steam->battery_desc.name =3D devm_kasprintf(&steam->hdev->dev,
> +				GFP_KERNEL, "steam-controller-%s-battery",
> +				steam->serial_no);
> +	}
>  	steam->battery_desc.get_property =3D steam_battery_get_property;
> -	steam->battery_desc.name =3D devm_kasprintf(&steam->hdev->dev,
> -			GFP_KERNEL, "steam-controller-%s-battery",
> -			steam->serial_no);
>  	if (!steam->battery_desc.name)
>  		return -ENOMEM;
> =20
>  	/* avoid the warning of 0% battery while waiting for the first info */
>  	spin_lock_irqsave(&steam->lock, flags);
> -	steam->voltage =3D 3000;
> +	steam->battery_voltage =3D 3000;
>  	steam->battery_charge =3D 100;
> +	steam->battery_current =3D 0;
> +	steam->battery_temp =3D 200;

[Severity: Medium]
Does this initialize the default temperature to 0.2 degrees Celsius?

In steam_battery_get_property(), this value is divided by 100:
val->intval =3D temp / 100;

Since the power supply subsystem expects POWER_SUPPLY_PROP_TEMP in tenths
of a degree Celsius, returning 2 represents 0.2 C. If the intended default
temperature was 20.0 C, should this be initialized to 20000?

> +	steam->battery_status =3D POWER_SUPPLY_STATUS_UNKNOWN;
>  	spin_unlock_irqrestore(&steam->lock, flags);
> =20
>  	battery =3D power_supply_register(&steam->hdev->dev,

[ ... ]

> @@ -1149,51 +1380,42 @@ static int steam_register(struct steam_device *st=
eam)
> -	spin_lock_irqsave(&steam->lock, flags);
> -	client_opened =3D steam->client_opened;
> -	spin_unlock_irqrestore(&steam->lock, flags);
> +	/* ignore battery errors, we can live without it */
> +	if (steam->quirks & (STEAM_QUIRK_WIRELESS | STEAM_QUIRK_IBEX))
> +		steam_battery_register(steam);
> =20
> -	if (!client_opened) {
> -		steam_set_lizard_mode(steam, lizard_mode);
> -		ret =3D steam_input_register(steam);
> -		if (ret !=3D 0)
> -			goto steam_register_input_fail;
> -		ret =3D steam_sensors_register(steam);
> -		if (ret !=3D 0)
> -			goto steam_register_sensors_fail;
> -	}
> +	steam_set_lizard_mode(steam, lizard_mode);
> +	ret =3D steam_input_register(steam);
> +	if (ret !=3D 0)
> +		goto steam_register_input_fail;
> +	ret =3D steam_sensors_register(steam);
> +	if (ret !=3D 0)
> +		goto steam_register_sensors_fail;

[Severity: High]
This is a pre-existing issue, but does this error path leave the battery
registered if steam_input_register() or steam_sensors_register() fails?

If steam_register() fails, steam_probe() jumps to the err_hw_close label,
which bypasses steam_unregister(). Because steam_probe() returns an error,
devres will free the steam_device structure.

Since the battery was not unregistered, it remains active in sysfs, and any
subsequent userspace read of the battery's properties will dereference the
freed steam_device pointer, causing a kernel use-after-free.

[ ... ]

> @@ -1989,8 +2462,84 @@ static int steam_raw_event(struct hid_device *hdev,
>  		return 0;
> =20
>  	if (steam->client_opened)
> -		hid_input_report(steam->client_hdev, HID_FEATURE_REPORT,
> -				data, size, 0);
> +		hid_input_report(steam->client_hdev, report->type, data, size, 0);
> +
> +	/* Ibex uses a different report format */
> +	if (steam->quirks & STEAM_QUIRK_IBEX) {
> +		if (report->type !=3D HID_INPUT_REPORT)
> +			return 0;
> +
> +		switch (report->id) {
> +		case REPORT_ID_INPUT:
> +			if (size !=3D 54)

[ ... ]

> +		case REPORT_ID_WIRELESS_EVENT:
> +			if (size !=3D 2)
> +				return 0;
> +			switch (data[1]) {
> +			case WIRELESS_EVENT_DISCONNECT:
> +				steam_do_connect_event(steam, false);
> +				break;
> +			case WIRELESS_EVENT_CONNECT:
> +				steam_do_connect_event(steam, true);
> +				break;
> +			}
> +			break;

[Severity: High]
This is a pre-existing issue, but can this lead to a use-after-free during
driver unbind?

When the driver is unbound, steam_remove() cancels the steam->work_connect
work item and calls steam_unregister(), but hid_hw_stop() is not called
until the very end.

In this window, the device can still stream data and trigger
steam_raw_event(), which can call steam_do_connect_event(). This
reschedules steam->work_connect back onto the workqueue. Once
steam_remove() returns, devres frees the steam_device structure, causing
the scheduled work item to execute steam_work_connect_cb() on freed memory.

--=20
Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260805015116.3884=
[email protected]?part=3D1