Linux Input/HID development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Vicki Pfau" <vi@endrift.com>
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v4 08/11] HID: steam: Rearrange teardown sequence
Date: Wed, 29 Jul 2026 02:15:32 +0000	[thread overview]
Message-ID: <20260729021533.370011F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260729015243.1170573-9-vi@endrift.com>

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

New issues:
- [High] Calling `hid_hw_start()` before initializing driver data structures exposes the device to events early, leading to crashes from uninitialized locks and work_structs.
- [High] The modified error path in `steam_probe()` skips the cancellation of `work_connect` and `mode_switch`, leaving them dangling on probe failure.

Pre-existing issues:
- [High] Teardown ordering in `steam_remove()` allows `work_connect` to be re-scheduled by raw events after it has been explicitly cancelled.
- [High] `steam->client_hdev` is destroyed before the `client_opened` flag is cleared, allowing asynchronous events to dereference the freed device.
- [High] If `steam_register()` fails during initialization, the `steam` object is freed but remains linked in the global `steam_devices` list.
--

--- Patch [8]: [PATCH v4 08/11] HID: steam: Rearrange teardown sequence ---
commit 4778053b21e62ca367e7570ac85b4132006adade
Author: Vicki Pfau <vi@endrift.com>

HID: steam: Rearrange teardown sequence

This fixes a narrow window during the teardown sequence where callbacks
could still be scheduled during cleanup that would then have a dangling
pointer to the now-freed steam struct. This also puts work canceling
for rumble and mode switch in steam_unregister, as that shouldn't persist
while the client hdev is open.

> diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
> index ff112aa1b0e91..5ab4396a241e0 100644
> --- a/drivers/hid/hid-steam.c
> +++ b/drivers/hid/hid-steam.c
[ ... ]
> @@ -1352,6 +1356,14 @@ static int steam_probe(struct hid_device *hdev,
>  	if (!steam)
>  		return -ENOMEM;
>  
> +	/*
> +	 * With the real steam controller interface, do not connect hidraw.
> +	 * Instead, create the client_hid and connect that.
> +	 */
> +	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDRAW);
> +	if (ret)
> +		return ret;
> +
>  	steam->hdev = hdev;
>  	hid_set_drvdata(hdev, steam);
>  	spin_lock_init(&steam->lock);

[Severity: High]
Does calling hid_hw_start() before initializing driver data structures expose
the device to events early?

Moving hid_hw_start() here makes the input devices visible to userspace.
If a userspace process opens the input node, hid_hw_open() is called, starting
URB polling. An incoming hardware interrupt can then invoke steam_raw_event().

Because hid_set_drvdata() is called right after hid_hw_start(),
steam_raw_event() could access the non-NULL steam pointer and call
steam_do_connect_event(), which might attempt to acquire the uninitialized
spinlock and schedule the uninitialized work_connect, potentially leading to
a crash.

[ ... ]
> @@ -1397,14 +1393,22 @@ static int steam_probe(struct hid_device *hdev,
>  			hid_err(hdev,
>  				"%s:steam_register failed with error %d\n",
>  				__func__, ret);
> -			goto err_hw_close;
> +			goto err_hw_stop;

[Severity: High]
This is a pre-existing issue, but if steam_register() fails during
initialization, does the steam object remain linked in the global
steam_devices list after being freed?

During steam_register(), the device is added to the global steam_devices list.
If a subsequent step like steam_input_register() fails, the function returns
an error without removing the device from the list.

When this failure jumps to err_hw_stop in steam_probe(), it bypasses
steam_unregister(), which is responsible for removing it from the list.
If devres then frees the object, could a dangling pointer be left in the
global list?

[ ... ]
>  err_destroy:
>  	hid_destroy_device(steam->client_hdev);
> -err_steam_unregister:
> -	if (steam->connected)
> -		steam_unregister(steam);
>  err_hw_close:
>  	hid_hw_close(hdev);
> -err_hw_stop:
> -	hid_hw_stop(hdev);
> -err_cancel_work:
> +err_steam_unregister:
> +	spin_lock_irqsave(&steam->lock, flags);
> +	steam->client_opened = 0;
> +	spin_unlock_irqrestore(&steam->lock, flags);
>  	cancel_work_sync(&steam->work_connect);
> +	if (steam->connected)
> +		steam_unregister(steam);
>  	cancel_delayed_work_sync(&steam->mode_switch);
> +err_hw_stop:

[Severity: High]
Does the modified error path in steam_probe() skip the cancellation of
work_connect and mode_switch when a probe failure jumps to err_hw_stop?

Because hid_hw_start() was moved earlier in the probe, raw events might already
queue work_connect. If the probe fails and jumps directly to err_hw_stop,
bypassing err_steam_unregister, could it leave these work items dangling when
devres frees the steam object?

>  	cancel_work_sync(&steam->rumble_work);
>  	cancel_delayed_work_sync(&steam->coalesce_rumble_work);
> +	hid_hw_stop(hdev);
>  
>  	return ret;
>  }
> @@ -1435,25 +1441,24 @@ static int steam_probe(struct hid_device *hdev,
>  static void steam_remove(struct hid_device *hdev)
>  {
>  	struct steam_device *steam = hid_get_drvdata(hdev);
> +	unsigned long flags;
>  
>  	if (!steam || hdev->group == HID_GROUP_STEAM) {
>  		hid_hw_stop(hdev);
>  		return;
>  	}
>  
> +	hid_hw_close(hdev);
>  	hid_destroy_device(steam->client_hdev);
> -	cancel_delayed_work_sync(&steam->mode_switch);
> -	cancel_work_sync(&steam->work_connect);
> -	cancel_work_sync(&steam->rumble_work);
> -	cancel_delayed_work_sync(&steam->coalesce_rumble_work);
> -	steam->client_hdev = NULL;
> +	spin_lock_irqsave(&steam->lock, flags);
>  	steam->client_opened = 0;
> +	spin_unlock_irqrestore(&steam->lock, flags);

[Severity: High]
This is a pre-existing issue, but is steam->client_hdev destroyed before
the client_opened flag is cleared?

Because steam_raw_event() runs in softirq context without taking a lock around
the client_opened check, there could be a race condition.

If an interrupt sees client_opened > 0 right before hid_destroy_device()
completes, could it pass the freed client_hdev pointer to hid_input_report()?

> +	cancel_work_sync(&steam->work_connect);
>  	if (steam->quirks & STEAM_QUIRK_WIRELESS) {
>  		hid_info(hdev, "Steam wireless receiver disconnected");
>  	}
> -	hid_hw_close(hdev);
> -	hid_hw_stop(hdev);
>  	steam_unregister(steam);
> +	hid_hw_stop(hdev);

[Severity: High]
This is a pre-existing issue, but does the teardown ordering in steam_remove()
allow work_connect to be re-scheduled by raw events after it has been explicitly
cancelled?

cancel_work_sync(&steam->work_connect) is called above, but URB polling is only
guaranteed to stop at the end of the function when hid_hw_stop() is called.

If a hardware event arrives after the cancellation but before hid_hw_stop(),
could it re-queue work_connect via steam_raw_event(), leaving the work pending
after the driver finishes removal and frees the device?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260729015243.1170573-1-vi@endrift.com?part=8

  reply	other threads:[~2026-07-29  2:15 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-29  1:52 [PATCH v4 00/10] HID: steam: General cleanup and improvements Vicki Pfau
2026-07-29  1:52 ` [PATCH v4 01/11] HID: steam: Update documentation Vicki Pfau
2026-07-29  1:52 ` [PATCH v4 02/11] HID: steam: Refactor and clean up report parsing Vicki Pfau
2026-07-29  1:52 ` [PATCH v4 03/11] HID: steam: Rename some constants that got renamed upstream Vicki Pfau
2026-07-29  1:52 ` [PATCH v4 04/11] HID: steam: Zero-initialize reply in serial lookup Vicki Pfau
2026-07-29  2:10   ` sashiko-bot
2026-07-29  1:52 ` [PATCH v4 05/11] HID: steam: Add support for sensor events on the Steam Controller (2015) Vicki Pfau
2026-07-29  2:09   ` sashiko-bot
2026-07-29  1:52 ` [PATCH v4 06/11] HID: steam: Coalesce rumble packets Vicki Pfau
2026-07-29  2:13   ` sashiko-bot
2026-07-29  1:52 ` [PATCH v4 07/11] HID: steam: Fully unregister controller when hidraw is opened Vicki Pfau
2026-07-29  2:12   ` sashiko-bot
2026-07-29  1:52 ` [PATCH v4 08/11] HID: steam: Rearrange teardown sequence Vicki Pfau
2026-07-29  2:15   ` sashiko-bot [this message]
2026-07-29  1:52 ` [PATCH v4 09/11] HID: steam: Improve logging and other cleanup Vicki Pfau
2026-07-29  2:11   ` sashiko-bot
2026-07-29  1:52 ` [PATCH v4 10/11] HID: steam: Reject short reads Vicki Pfau
2026-07-29  1:52 ` [PATCH v4 11/11] HID: steam: Retry send/recv reports if stale Vicki Pfau

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260729021533.370011F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=vi@endrift.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox