Linux Input/HID development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Sriman Achanta" <srimanachanta@gmail.com>
Cc: dmitry.torokhov@gmail.com, linux-input@vger.kernel.org
Subject: Re: [PATCH v4 09/10] HID: steelseries: Add async status interface support
Date: Tue, 23 Jun 2026 17:36:12 +0000	[thread overview]
Message-ID: <20260623173612.D0D131F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260623172310.272708-10-srimanachanta@gmail.com>

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

New issues:
- [Critical] The driver blindly casts the sibling interface's driver data to a `struct hid_device` without verifying the bound driver, leading to type confusion and memory corruption.
- [Critical] The sync interface publishes `sd` to `drvdata` before initialization is complete, allowing the async interface to crash the kernel by scheduling an uninitialized work struct.
- [High] Lockless retrieval and access of the sibling interface's driver data leads to a Use-After-Free if the sync interface is unbound concurrently.
- [High] If the sync interface probe fails after the async interface binds to it, the async interface will access a freed `sd->hdev` pointer.

Pre-existing issues:
- [High] An ERR_PTR temporarily assigned to `sd->battery` during initialization failure can be concurrently dereferenced by the raw event handler.
--

commit 002dd61306f7255151ab901886b17a9e612226ad
Author: Sriman Achanta <srimanachanta@gmail.com>
Subject: HID: steelseries: Add async status interface support

This commit adds support for a second HID interface that sends battery
and connection updates asynchronously. This allows the driver to stop
polling the sync interface by watching the async interface instead.

> diff --git a/drivers/hid/hid-steelseries-arctis.c b/drivers/hid/hid-steelseries-arctis.c
> index 734cf1eb87894..2208d0e4cd2ad 100644
> --- a/drivers/hid/hid-steelseries-arctis.c
> +++ b/drivers/hid/hid-steelseries-arctis.c
[ ... ]
> @@ -316,6 +318,23 @@ static int steelseries_battery_register(struct steelseries_device *sd)
>  	return 0;
>  }
>  
> +static struct hid_device *steelseries_get_sibling_hdev(struct hid_device *hdev,
> +						       int interface_num)
> +{
> +	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
> +	struct usb_device *usb_dev = interface_to_usbdev(intf);
> +	struct usb_interface *sibling_intf;
> +	struct hid_device *sibling_hdev;
> +
> +	sibling_intf = usb_ifnum_to_if(usb_dev, interface_num);
> +	if (!sibling_intf)
> +		return NULL;
> +
> +	sibling_hdev = usb_get_intfdata(sibling_intf);

[Severity: High]
Does this code safely retrieve the sibling interface's driver data? Because
no reference is taken on the USB interface and usb_driver_claim_interface()
is not used, could a concurrent sysfs unbind destroy the interface and the
retrieved data while we are accessing it?

[Severity: Critical]
Is it safe to assume usb_get_intfdata() returns a struct hid_device pointer
here? If the sync interface is bound to a different driver (such as
snd-usb-audio), its driver data would be a completely different structure.
Could this lead to type confusion and memory corruption when the pointer is
dereferenced later in the probe function?

> +
> +	return sibling_hdev;
> +}
> +
>  static int steelseries_arctis_probe(struct hid_device *hdev,
>  				    const struct hid_device_id *id)
>  {
> @@ -337,39 +356,76 @@ static int steelseries_arctis_probe(struct hid_device *hdev,
>  	if (ret)
>  		return ret;
>  
> -	/* Let hid-generic handle non-sync interfaces */
> -	if (interface_num != info->sync_interface)
> +	/* Let hid-generic handle non-vendor or unknown interfaces */
> +	if (interface_num != info->sync_interface &&
> +	    (!info->async_interface || interface_num != info->async_interface))
>  		return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
>  
> -	sd = kzalloc_obj(*sd, GFP_KERNEL);
> -	if (!sd)
> -		return -ENOMEM;
> +	if (interface_num == info->sync_interface) {
> +		sd = kzalloc_obj(*sd, GFP_KERNEL);
> +		if (!sd)
> +			return -ENOMEM;
>  
> -	kref_init(&sd->refcnt);
> -	sd->hdev = hdev;
> -	sd->info = info;
> -	spin_lock_init(&sd->lock);
> +		kref_init(&sd->refcnt);
> +		sd->hdev = hdev;
> +		sd->info = info;
> +		spin_lock_init(&sd->lock);
>  
> -	hid_set_drvdata(hdev, sd);
> +		hid_set_drvdata(hdev, sd);

[Severity: Critical]
By calling hid_set_drvdata() here, sd is globally published before
INIT_DELAYED_WORK() is called below. If the async interface probes concurrently
and immediately receives an event, could it call schedule_delayed_work() on
an uninitialized work struct?

>  
> -	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
> -	if (ret)
> -		goto err_put;
> +		ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
> +		if (ret)
> +			goto err_put;
>  
> -	ret = hid_hw_open(hdev);
> -	if (ret)
> -		goto err_stop;
> +		ret = hid_hw_open(hdev);
> +		if (ret)
> +			goto err_stop;

[Severity: High]
If the sync interface probe fails after this point (for example, if hid_hw_open()
or steelseries_battery_register() fails) and returns an error, but the async
interface has already bound and incremented sd->refcnt:

Since steelseries_arctis_remove() is never called for the failed sync probe,
sd->removed remains false. If the driver core clears the sync interface's driver
data and the sibling interface is unbound, sd->hdev is freed.

Could the still-active async interface then process raw events, bypass the
sd->removed check, and access the freed sd->hdev pointer via
steelseries_headset_set_wireless_status()?

> +
> +		if (info->capabilities & SS_CAP_BATTERY) {
> +			ret = steelseries_battery_register(sd);
> +			if (ret < 0)
> +				hid_warn(hdev, "Failed to register battery: %d\n", ret);
> +		}
>  
> -	if (info->capabilities & SS_CAP_BATTERY) {
> -		ret = steelseries_battery_register(sd);
> -		if (ret < 0)
> -			hid_warn(hdev, "Failed to register battery: %d\n", ret);
> +		INIT_DELAYED_WORK(&sd->status_work, steelseries_status_timer_work_handler);
> +		schedule_delayed_work(&sd->status_work, msecs_to_jiffies(100));
> +
> +		return 0;
>  	}
[ ... ]
> @@ -442,6 +509,10 @@ static int steelseries_arctis_raw_event(struct hid_device *hdev,
>  			old_connected ? "" : "not ",
>  			sd->headset_connected ? "" : "not ");
>  
> +		if (sd->headset_connected && !old_connected &&
> +		    sd->info->async_interface && is_async_interface)
> +			schedule_delayed_work(&sd->status_work, 0);
> +
>  		if (sd->battery) {
>  			steelseries_headset_set_wireless_status(sd->hdev,
>  							       sd->headset_connected);
>  			power_supply_changed(sd->battery);

[Severity: High]
This is a pre-existing issue, but is there a data race with sd->battery here?

During initialization in steelseries_battery_register():

	sd->battery = power_supply_register(&sd->hdev->dev,
			&sd->battery_desc, &battery_cfg);
	if (IS_ERR(sd->battery)) {
		ret = PTR_ERR(sd->battery);
		sd->battery = NULL;

Since hardware has already been enabled via hid_hw_start(), interrupts can
arrive concurrently. If an event arrives during the window where sd->battery
holds an ERR_PTR, if (sd->battery) will evaluate to true.

Could this cause power_supply_changed(sd->battery) to dereference the error
pointer?

>  		}
>  
> @@ -465,6 +536,8 @@ static int steelseries_arctis_raw_event(struct hid_device *hdev,
>  			power_supply_changed(sd->battery);
>  	}
>  
> +	spin_unlock_irqrestore(&sd->lock, flags);
> +
>  	return 0;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260623172310.272708-1-srimanachanta@gmail.com?part=9

  reply	other threads:[~2026-06-23 17:36 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-23 17:23 [PATCH v4 00/10] HID: steelseries: Refactor Arctis driver and add Arctis Nova 7 Gen2 support Sriman Achanta
2026-06-23 17:23 ` [PATCH v4 01/10] HID: steelseries: Fix ARCTIS_1_X device mislabeling Sriman Achanta
2026-06-23 17:23 ` [PATCH v4 02/10] HID: steelseries: Fix whitespace in srws1 report descriptor Sriman Achanta
2026-06-23 17:23 ` [PATCH v4 03/10] HID: steelseries: Split Arctis headset driver into separate module Sriman Achanta
2026-06-23 17:23 ` [PATCH v4 04/10] HID: steelseries: Inline and simplify SRWS1 wheel driver Sriman Achanta
2026-06-23 17:23 ` [PATCH v4 05/10] HID: steelseries: Refactor Arctis driver to use device_info framework Sriman Achanta
2026-06-23 17:33   ` sashiko-bot
2026-06-23 17:23 ` [PATCH v4 06/10] HID: steelseries: Report POWER_SUPPLY_STATUS_FULL when full Sriman Achanta
2026-06-23 17:23 ` [PATCH v4 07/10] HID: steelseries: Correct Arctis 9 battery calibration range Sriman Achanta
2026-06-23 17:23 ` [PATCH v4 08/10] HID: steelseries: Manage battery lifetime with refcounting Sriman Achanta
2026-06-23 17:36   ` sashiko-bot
2026-06-23 17:23 ` [PATCH v4 09/10] HID: steelseries: Add async status interface support Sriman Achanta
2026-06-23 17:36   ` sashiko-bot [this message]
2026-06-23 17:23 ` [PATCH v4 10/10] HID: steelseries: Add support for Arctis Nova 7 Gen2 family Sriman Achanta
2026-06-23 17:35   ` sashiko-bot

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=20260623173612.D0D131F000E9@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=srimanachanta@gmail.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