Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH v9 6/9] input: keyboard: mtk-pmic-keys: Add MT6392 support
From: sashiko-bot @ 2026-06-21  8:28 UTC (permalink / raw)
  To: Luca Leonardo Scorcia
  Cc: robh, dmitry.torokhov, devicetree, conor+dt, linux-input
In-Reply-To: <20260621081634.467858-7-l.scorcia@gmail.com>

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

Pre-existing issues:
- [High] Potential array overflow when iterating over device tree child nodes if disabled nodes are present.
--

commit 59fc7db0ad7a68e49a6e83f5d6e6861165fb6460
Author: Val Packett <val@packett.cool>
Subject: input: keyboard: mtk-pmic-keys: Add MT6392 support

This commit adds support for the MT6392 PMIC to the MediaTek PMIC keys driver
by introducing its specific register definitions.

> diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c
> index c78d9f6d97c4f..8b4a89fce4fb1 100644
> --- a/drivers/input/keyboard/mtk-pmic-keys.c
> +++ b/drivers/input/keyboard/mtk-pmic-keys.c

[ ... ]

[Severity: High]
This isn't a bug introduced by this patch, but there is a pre-existing issue
in mtk_pmic_keys_probe().

Is it possible for the loop iteration to cause an out-of-bounds array access?

The code starts by validating the available child node count against the
maximum allowed keys:

	keycount = of_get_available_child_count(node);
	if (keycount > MTK_PMIC_MAX_KEY_COUNT ||
	    keycount > ARRAY_SIZE(irqnames)) {
		dev_err(keys->dev, "too many keys defined (%d)\n", keycount);
		return -EINVAL;
	}

However, the loop below it iterates over all child nodes, including those
with status="disabled":

	for_each_child_of_node_scoped(node, child) {
		keys->keys[index].regs = &mtk_pmic_regs->keys_regs[index];

		keys->keys[index].irq =
			platform_get_irq_byname(pdev, irqnames[index]);

If a device tree happens to contain more than two child nodes in total but
only two or fewer are marked as available, the initial keycount check passes.

The loop will then execute more than twice, and when the index reaches 2,
it overflows the keys->keys array and the irqnames array.

Would it be better to use for_each_available_child_of_node_scoped() here
instead to safely skip the disabled nodes?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260621081634.467858-1-l.scorcia@gmail.com?part=6

^ permalink raw reply

* Re: [PATCH v9 4/9] mfd: mt6397: Use MFD_CELL_* to describe sub-devices
From: sashiko-bot @ 2026-06-21  8:29 UTC (permalink / raw)
  To: Luca Leonardo Scorcia
  Cc: devicetree, linux-input, robh, conor+dt, dmitry.torokhov
In-Reply-To: <20260621081634.467858-5-l.scorcia@gmail.com>

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

Pre-existing issues:
- [High] Missing unregister_pm_notifier() on probe error path and driver unbind.
--

commit ad0ff6ef61b51852f825a12aebf3d2e548ceb3dd
Author: Luca Leonardo Scorcia <l.scorcia@gmail.com>

mfd: mt6397: Use MFD_CELL_* to describe sub-devices

This commit updates the sub-device descriptions in mt6397-core.c to use the
MFD_CELL_* macros. There are no functional changes intended.

> diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
> index 1bdacda9a933f..ccd97d66d7f1c 100644
> --- a/drivers/mfd/mt6397-core.c
> +++ b/drivers/mfd/mt6397-core.c

[ ... ]

[Severity: High]
This is a pre-existing issue, but while reviewing this driver I noticed
a potential problem in the probe error path and teardown lifecycle.

In mt6397_irq_init() (drivers/mfd/mt6397-irq.c), a PM notifier is
registered:

mt6397_irq_init() {
    ...
	register_pm_notifier(&chip->pm_nb);
	return 0;
}

However, if devm_mfd_add_devices() fails later in mt6397_probe(), the
function returns an error without unregistering this notifier:

drivers/mfd/mt6397-core.c:mt6397_probe() {
    ...
	ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
				   pmic_core->cells, pmic_core->cell_size,
				   NULL, 0, pmic->irq_domain);
	if (ret) {
		irq_domain_remove(pmic->irq_domain);
		dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
	}

	return ret;
}

Since the pmic structure is managed by devres and will be freed on probe
failure, does this leave a dangling pointer in the global PM notifier list
that could cause a use-after-free on the next suspend/resume transition?

Additionally, would this same situation occur on driver unbind via sysfs,
since there appears to be no .remove callback to unregister the notifier?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260621081634.467858-1-l.scorcia@gmail.com?part=4

^ permalink raw reply

* Re: [PATCH v9 3/9] regulator: dt-bindings: Add MediaTek MT6392 PMIC
From: sashiko-bot @ 2026-06-21  8:30 UTC (permalink / raw)
  To: Luca Leonardo Scorcia
  Cc: dmitry.torokhov, robh, linux-input, devicetree, conor+dt
In-Reply-To: <20260621081634.467858-4-l.scorcia@gmail.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Hardware Description Anti-Pattern (`avddldo-supply`)
- [Low] Missing DT Binding Examples
--

commit 0677db1e9f95451166ee6a30c2d046ffeee3f450
Author: Luca Leonardo Scorcia <l.scorcia@gmail.com>

regulator: dt-bindings: Add MediaTek MT6392 PMIC

Add bindings for the regulators found in the MediaTek MT6392 PMIC,
usually found in board designs using the MediaTek MT8516/MT8167 SoCs.

> diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6392-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6392-regulator.yaml
> new file mode 100644
> index 0000000000000..d74721d8f2ffc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6392-regulator.yaml
> @@ -0,0 +1,112 @@

[ ... ]

> +properties:
> +  compatible:
> +    items:
> +      - const: mediatek,mt6392-regulator
> +
> +  vproc-supply:
> +    description: Supply for buck regulator vproc
> +  vcore-supply:
> +    description: Supply for buck regulator vcore
> +  vsys-supply:
> +    description: Supply for buck regulator vsys
> +  avddldo-supply:
> +    description:
> +      Supply for AVDD LDOs (vm, vio18, vcn18, vcamd, vcamio). According to the data sheet
> +      this is an internal supply derived from vsys.

[Severity: Medium]
Since this supply is an internal silicon connection derived from vsys, should it
be exposed as a configurable board-level devicetree property?

Devicetree typically describes board-level wiring. Would it be better for the
regulator driver to statically resolve this dependency internally to avoid
establishing an incorrect ABI?

> +  ldo1-supply:
> +    description: Supply for LDOs group 1 (vaud28, vxo22, vaud22, vadc18, vcama, vrtc)

[ ... ]

> +required:
> +  - compatible
> +
> +additionalProperties: false

[Severity: Low]
Does this new binding schema miss an examples section?

According to devicetree binding guidelines, new schemas should include an
examples section at the end of the file. Without it, validation tools like
dt_binding_check might emit errors or skip full validation.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260621081634.467858-1-l.scorcia@gmail.com?part=3

^ permalink raw reply

* Re: [PATCH v9 8/9] regulator: Add MediaTek MT6392 regulator
From: sashiko-bot @ 2026-06-21  8:30 UTC (permalink / raw)
  To: Luca Leonardo Scorcia
  Cc: dmitry.torokhov, linux-input, conor+dt, devicetree, robh
In-Reply-To: <20260621081634.467858-9-l.scorcia@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Data race and global state corruption due to dynamic mutation of static global `regulator_desc` array during asynchronous probe.
--

commit 986be7d1279b96c61669e064178dad0c9c317fcd
Author: Fabien Parent <parent.f@gmail.com>

regulator: Add MediaTek MT6392 regulator

This commit introduces the regulator driver for the MediaTek MT6392 PMIC.
The PMIC is used on boards based on MT8167, MT8516, and other SoCs,
connecting via SPI wrapped inside PWRAP.

> diff --git a/drivers/regulator/mt6392-regulator.c b/drivers/regulator/mt6392-regulator.c
> new file mode 100644
> index 0000000000000..93fe887709c3b
> --- /dev/null
> +++ b/drivers/regulator/mt6392-regulator.c

[ ... ]

> +static int mt6392_set_buck_vsel_reg(struct platform_device *pdev)
> +{
> +	struct mt6397_chip *mt6392 = dev_get_drvdata(pdev->dev.parent);
> +	int i;
> +	u32 regval;
> +
> +	for (i = 0; i < MT6392_MAX_REGULATOR; i++) {
> +		if (mt6392_regulators[i].vselctrl_reg) {
> +			// Read the vselctrl_reg register
> +			if (regmap_read(mt6392->regmap,
> +					mt6392_regulators[i].vselctrl_reg,
> +					&regval) < 0) {
> +				dev_err(&pdev->dev,
> +					"Failed to read buck ctrl\n");
> +				return -EIO;
> +			}
> +
> +			// vselctrl_reg[vselctrl_mask] defines the mode
> +			if (regval & mt6392_regulators[i].vselctrl_mask) {
> +				// Regulator in Normal mode
> +				mt6392_regulators[i].desc.vsel_reg =
> +					mt6392_regulators[i].vsel_normal_mode_reg;
> +				mt6392_regulators[i].desc.vsel_mask =
> +					mt6392_regulators[i].vsel_normal_mode_mask;

[Severity: High]
Does this code safely handle concurrent probes or multiple device instances?

The mt6392_regulators array is a static global variable. Modifying
desc.vsel_reg and desc.vsel_mask directly during probe alters shared global
state.

The regulator core API expects regulator_desc to be a constant immutable
structure, as devm_regulator_register() takes a const pointer. Mutating
this static structure can corrupt the active configuration of previously
registered instances, or persist corrupted state across unbind/rebind cycles.

> +			} else {
> +				// Regulator in Register mode
> +				mt6392_regulators[i].desc.vsel_reg =
> +					mt6392_regulators[i].vsel_reg_mode_reg;
> +				mt6392_regulators[i].desc.vsel_mask =
> +					mt6392_regulators[i].vsel_reg_mode_mask;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}

[ ... ]

> +static struct platform_driver mt6392_regulator_driver = {
> +	.driver = {
> +		.name = "mt6392-regulator",
> +		.probe_type = PROBE_PREFER_ASYNCHRONOUS,

[Severity: High]
Could this asynchronous probe type lead to a data race?

Since multiple device instances could be probed concurrently, they would
race to modify the shared static mt6392_regulators array during
mt6392_set_buck_vsel_reg().

> +	},
> +	.probe = mt6392_regulator_probe,
> +	.id_table = mt6392_platform_ids,
> +};

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260621081634.467858-1-l.scorcia@gmail.com?part=8

^ permalink raw reply

* Re: [PATCH v9 7/9] pinctrl: mediatek: mt6397: Add MediaTek MT6392
From: sashiko-bot @ 2026-06-21  8:30 UTC (permalink / raw)
  To: Luca Leonardo Scorcia
  Cc: linux-input, conor+dt, robh, dmitry.torokhov, devicetree
In-Reply-To: <20260621081634.467858-8-l.scorcia@gmail.com>

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

Pre-existing issues:
- [High] Use-After-Free due to missing `.remove` callback while using unmanaged registration with devm-allocated memory.
--

commit 120aff926d5691bb4a5ec3b6058e13323e67c325
Author: Luca Leonardo Scorcia <l.scorcia@gmail.com>

pinctrl: mediatek: mt6397: Add MediaTek MT6392
    
Add support for the MT6392 pinctrl device, which is very similar to
MT6397 with a handful of different property values and its own pins
definition.

Update the MT6397 driver to retrieve device data from the match table and
use it for driver init.

> diff --git a/drivers/pinctrl/mediatek/pinctrl-mt6397.c b/drivers/pinctrl/mediatek/pinctrl-mt6397.c
> index 03d0f65d7bcc1..8ba02e70595ce 100644
> --- a/drivers/pinctrl/mediatek/pinctrl-mt6397.c
> +++ b/drivers/pinctrl/mediatek/pinctrl-mt6397.c
[ ... ]
> @@ -40,13 +62,24 @@ static const struct mtk_pinctrl_devdata mt6397_pinctrl_data = {
>  static int mt6397_pinctrl_probe(struct platform_device *pdev)
>  {
>  	struct mt6397_chip *mt6397;
> +	const struct mtk_pinctrl_devdata *data;
> +
> +	data = device_get_match_data(&pdev->dev);
> +	if (!data)
> +		return -ENOENT;
>  
>  	mt6397 = dev_get_drvdata(pdev->dev.parent);
> -	return mtk_pctrl_init(pdev, &mt6397_pinctrl_data, mt6397->regmap);
> +	return mtk_pctrl_init(pdev, data, mt6397->regmap);
>  }
>  
>  static const struct of_device_id mt6397_pctrl_match[] = {
> -	{ .compatible = "mediatek,mt6397-pinctrl", },
> +	{
> +		.compatible = "mediatek,mt6392-pinctrl",
> +		.data = &mt6392_pinctrl_data
> +	}, {
> +		.compatible = "mediatek,mt6397-pinctrl",
> +		.data = &mt6397_pinctrl_data
> +	},
>  	{ }
>  };

[Severity: High]
This is a pre-existing issue, but does mtk_pinctrl_driver in
drivers/pinctrl/mediatek/pinctrl-mt6397.c need a .remove callback or
suppress_bind_attrs set to true to prevent unbinding?

In drivers/pinctrl/mediatek/pinctrl-mtk-common.c:mtk_pctrl_init(), the driver
allocates the pctl and pctl->chip structures using devm_kzalloc() but
registers the GPIO chip using the unmanaged gpiochip_add_data() API:

mtk_pctrl_init() {
    ...
    pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL);
    ...
    ret = gpiochip_add_data(pctl->chip, pctl);
    ...
}

If the driver is unbound via sysfs, the devm_ subsystem will free the memory
backing the GPIO chip while it remains registered with the core subsystem.
Could this lead to a use-after-free if the GPIOs are accessed after the
driver is unbound?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260621081634.467858-1-l.scorcia@gmail.com?part=7

^ permalink raw reply

* Re: [PATCH v9 5/9] mfd: mt6397: Add support for MT6392 PMIC
From: sashiko-bot @ 2026-06-21  8:32 UTC (permalink / raw)
  To: Luca Leonardo Scorcia
  Cc: conor+dt, dmitry.torokhov, devicetree, linux-input, robh
In-Reply-To: <20260621081634.467858-6-l.scorcia@gmail.com>

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

Pre-existing issues:
- [High] Resource leaks and Use-After-Free due to missing PM notifier teardown
- [High] Race condition in PM notifier causing lost IRQ hardware state
--

commit 4936b4ce67a0024573fa33154f81b91a5d2261b2
Author: Fabien Parent <parent.f@gmail.com>

mfd: mt6397: Add support for MT6392 PMIC

Align the MT6397 PMIC driver to other MFD drivers by passing only an
identifier through mt6397_of_match[*].data and add support for the MT6392
PMIC and its regulator, RTC, keys and pinctrl devices.

> diff --git a/drivers/mfd/mt6397-irq.c b/drivers/mfd/mt6397-irq.c
> index 5d2e5459f7444..80ea5b92d232f 100644
> --- a/drivers/mfd/mt6397-irq.c
> +++ b/drivers/mfd/mt6397-irq.c
[ ... ]
> @@ -203,6 +205,12 @@ int mt6397_irq_init(struct mt6397_chip *chip)
>  		chip->int_status[0] = MT6397_INT_STATUS0;
>  		chip->int_status[1] = MT6397_INT_STATUS1;
>  		break;
> +	case MT6392_CHIP_ID:
> +		chip->int_con[0] = MT6392_INT_CON0;
> +		chip->int_con[1] = MT6392_INT_CON1;
> +		chip->int_status[0] = MT6392_INT_STATUS0;
> +		chip->int_status[1] = MT6392_INT_STATUS1;
> +		break;
>  
>  	default:
>  		dev_err(chip->dev, "unsupported chip: 0x%x\n", chip->chip_id);

[Severity: High]
This isn't a bug introduced by this patch, but does the global PM notifier
leak if probe fails?

The PM notifier is registered globally at the end of mt6397_irq_init() without
using a devm_action wrapper:

	register_pm_notifier(&chip->pm_nb);
	return 0;

If probe fails after IRQ initialization (for example, if
devm_mfd_add_devices() fails in mt6397_probe()), or if the driver is unbound
via sysfs, devres frees the pmic structure while the PM notifier remains
active in the global kernel chain.

Could the next system suspend/resume cycle then call the notifier callback
on freed memory, leading to a crash?

Additionally, does the irq domain leak on unbind since mt6397_driver lacks a
.remove callback entirely?

[Severity: High]
This is a pre-existing issue, but can the PM notifier corrupt hardware state?

The PM notifier callback (mt6397_irq_pm_notifier) writes to the int_con
hardware registers using cached masks without acquiring chip->irqlock:

	case PM_SUSPEND_PREPARE:
		regmap_write(chip->regmap,
			     chip->int_con[0], chip->wake_mask[0]);

However, core IRQ functions like mt6397_irq_sync_unlock() modify the exact
same int_con registers safely under the protection of irqlock:

	regmap_write(mt6397->regmap, mt6397->int_con[0],
		     mt6397->irq_masks_cur[0]);

Since user tasks are still running during PM_SUSPEND_PREPARE and are already
thawed prior to PM_POST_SUSPEND, concurrent calls to enable_irq(),
disable_irq(), or irq_set_wake() can interleave register reads and writes.

Could this cause the hardware to be programmed with stale interrupt masks or
overwrite the suspend wake mask, leading to dropped device interrupts or a
failure to wake the system?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260621081634.467858-1-l.scorcia@gmail.com?part=5

^ permalink raw reply

* Re: [PATCH v2 2/2] arm64: dts: qcom: sdm845-oneplus: Update compatible to include model
From: David Heidelberg @ 2026-06-21 17:11 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jason A. Donenfeld, Matthias Schiffer, Vincent Huang,
	Bjorn Andersson, Konrad Dybcio, linux-input, devicetree,
	linux-kernel, linux-arm-msm, phone-devel, Krzysztof Kozlowski,
	Konrad Dybcio
In-Reply-To: <1d0e7e31-f808-4347-955a-7246dea208f5@ixit.cz>

On 28/05/2026 00:13, David Heidelberg wrote:
> On 27/05/2026 23:56, Dmitry Torokhov wrote:
>> Hi David,
>>
>> On Sat, May 23, 2026 at 11:45:35AM +0200, David Heidelberg via B4 Relay wrote:
>>> From: David Heidelberg <david@ixit.cz>
>>>
>>> We know the driver is reporting s3706b, introduce the compatible so we
>>> can more easily introduce quirks for weird touchscreen replacements in
>>> followup series.
>>>
>>> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>>> Signed-off-by: David Heidelberg <david@ixit.cz>
>>> ---
>>>   arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi | 2 +-
>>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>>
>>> diff --git a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi b/arch/ 
>>> arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi
>>> index 6b7378cf4d493..148164d456a5a 100644
>>> --- a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi
>>> +++ b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi
>>> @@ -475,17 +475,17 @@ bq27441_fg: bq27441-battery@55 {
>>>       };
>>>   };
>>>   &i2c12 {
>>>       status = "okay";
>>>       clock-frequency = <400000>;
>>>       synaptics-rmi4-i2c@20 {
>>> -        compatible = "syna,rmi4-i2c";
>>> +        compatible = "syna,rmi4-s3706b", "syna,rmi4-i2c";
>>
>> So I believe we established that this device (s3706b) does not in fact
>> implement rmi4 protocol properly. Why do we have "syna,rmi4-i2c" as a
>> fallback? Shouldn't it be just "syna,rmi4-s3706b"?
> 
> The vendor supplies s3706b which does implement the RMI4 properly.
> 
> The 3rd party replacement impersonating original parts may not implement it 
> properly, but I don't address this issue in this initial submission.
> 
> With this compatible we know which original part is used by the vendor and 
> installed in the phones, so later we can deduct specific sequences for the 
> replacement aftermarket parts to keep phone touchscreen working same as they do 
> on Android without affecting other devices.

Hello Dmitry.

May I ask what is currently preventing this series from moving forward?

The first version was posted in 2023 [1]. I picked it up again in 2025 [2] and 
am now on the 9th iteration (this patchset). At this point, the series has been 
under discussion for well over a year, with relatively little feedback and 
increasingly long gaps between review rounds.

The current approach is based on the guidance I have received so far, including 
suggestions from the device-tree maintainers. When concerns were raised, I tried 
to address them and rework the series accordingly.

What I am struggling with is understanding what specific issue still needs to be 
resolved before these patches can be accepted. If there are remaining 
requirements, objections to the approach, or technical concerns that I have not 
addressed, I would appreciate having them stated explicitly so I can work on them.

I also split out the straightforward, self-contained changes in the hope that at 
least those could progress independently while I continued working on any 
follow-up requirements. However, even those patches do not appear to be moving 
forward.

Could you please clarify what outcome you would like to see from this series, 
and what concrete changes would be required to get it accepted?

Thank you,
David


[1] 
https://lore.kernel.org/all/20230929-caleb-rmi4-quirks-v1-0-cc3c703f022d@linaro.org/
[2] https://lore.kernel.org/all/20250402-synaptics-rmi4-v4-0-1bb95959e564@ixit.cz/

> 
> David
> 
>>
>> Thanks.
>>
> 

-- 
David Heidelberg


^ permalink raw reply

* [PATCH] HID: hyperx-headset: Add support for HyperX headset devices
From: Sofia Schneider @ 2026-06-22  2:17 UTC (permalink / raw)
  To: jikos, bentiss; +Cc: linux-input, linux-kernel, Sofia Schneider

Introduce a HID driver for HyperX Cloud III Wireless headsets,
supporting battery reporting and connection status.

Tested with a HyperX Cloud III Wireless only, for lack of
other testable devices.

Signed-off-by: Sofia Schneider <sofia@schn.dev>
---
 MAINTAINERS                      |   6 +
 drivers/hid/Kconfig              |  11 +
 drivers/hid/Makefile             |   1 +
 drivers/hid/hid-hyperx-headset.c | 374 +++++++++++++++++++++++++++++++
 drivers/hid/hid-ids.h            |   1 +
 5 files changed, 393 insertions(+)
 create mode 100644 drivers/hid/hid-hyperx-headset.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d8252026bbd4..fa49655255f6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11419,6 +11419,12 @@ F:	include/uapi/linux/hid*
 F:	samples/hid/
 F:	tools/testing/selftests/hid/
 
+HID HYPERX HEADSET DRIVER
+M:	Sofia Schneider <sofia@schn.dev>
+L:	linux-input@vger.kernel.org
+S:	Maintained
+F:	drivers/hid/hid-hyperx-headset.c
+
 HID LOGITECH DRIVERS
 R:	Filipe Laíns <lains@riseup.net>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index f9bcaeb66385..e9f5f1f982c9 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1215,6 +1215,17 @@ config HID_HYPERV_MOUSE
 	help
 	Select this option to enable the Hyper-V mouse driver.
 
+config HID_HYPERX_HEADSET
+	tristate "HyperX headset devices"
+	depends on USB_HID
+	select POWER_SUPPLY
+	help
+	Support for HyperX headset devices.
+
+	Say Y here if you would like to enable support for HyperX headset devices.
+	To compile this driver as a module, choose M here: the module will be called
+	hid-hyperx-headset.
+
 config HID_SMARTJOYPLUS
 	tristate "SmartJoy PLUS PS2/USB adapter support"
 	help
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 23e6e3dd0c56..9f3fd2c21837 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_HID_HOLTEK)	+= hid-holtek-kbd.o
 obj-$(CONFIG_HID_HOLTEK)	+= hid-holtek-mouse.o
 obj-$(CONFIG_HID_HOLTEK)	+= hid-holtekff.o
 obj-$(CONFIG_HID_HYPERV_MOUSE)	+= hid-hyperv.o
+obj-$(CONFIG_HID_HYPERX_HEADSET)	+= hid-hyperx-headset.o
 obj-$(CONFIG_HID_ICADE)		+= hid-icade.o
 obj-$(CONFIG_HID_ITE)		+= hid-ite.o
 obj-$(CONFIG_HID_JABRA)		+= hid-jabra.o
diff --git a/drivers/hid/hid-hyperx-headset.c b/drivers/hid/hid-hyperx-headset.c
new file mode 100644
index 000000000000..18dc3e4f7e85
--- /dev/null
+++ b/drivers/hid/hid-hyperx-headset.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  HID driver for HyperX headsets
+ *
+ *  Supports HyperX Cloud III Wireless headsets.
+ *
+ *  Copyright (c) 2026 Sofia Schneider
+ */
+
+#include <linux/usb.h>
+#include <linux/hid.h>
+
+#include "hid-ids.h"
+
+#define HYPERX_POLL_INTERVAL_MS (2 * 60 * 1000)
+
+#define HYPERX_REPORT_ID 0x66
+#define HYPERX_PACKET_SIZE 62
+
+#define HYPERX_CMD_GET_CONNECTED 0x82
+#define HYPERX_CMD_GET_BATTERY 0x89
+#define HYPERX_CMD_GET_CHARGING 0x8A
+
+#define HYPERX_RESP_CONNECTED 0x0B
+#define HYPERX_RESP_CHARGING 0x0C
+#define HYPERX_RESP_BATTERY 0x0D
+
+#define HYPERX_PREFIX "HP, Inc "
+#define HYPERX_PREFIX_LEN strlen(HYPERX_PREFIX)
+
+struct hyperx_headset_device {
+	struct hid_device *hdev;
+	struct power_supply *battery;
+
+	spinlock_t lock;
+	u8 battery_level;
+	bool is_charging;
+	bool is_connected;
+
+	struct delayed_work poll_work;
+	struct work_struct battery_work;
+};
+
+static const enum power_supply_property hyperx_headset_battery_props[] = {
+	POWER_SUPPLY_PROP_PRESENT,	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_STATUS,	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_SCOPE,	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static int hyperx_headset_battery_get_property(struct power_supply *psy,
+					       enum power_supply_property psp,
+					       union power_supply_propval *val)
+{
+	struct hyperx_headset_device *drvdata = power_supply_get_drvdata(psy);
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&drvdata->lock, flags);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = drvdata->is_connected ? 1 : 0;
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = drvdata->battery_level;
+		break;
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+		break;
+	case POWER_SUPPLY_PROP_STATUS:
+		if (!drvdata->is_connected)
+			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+		else if (drvdata->is_charging)
+			val->intval = POWER_SUPPLY_STATUS_CHARGING;
+		else if (drvdata->battery_level == 100)
+			val->intval = POWER_SUPPLY_STATUS_FULL;
+		else
+			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+		break;
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = drvdata->hdev->name;
+		while (!strncmp(val->strval, HYPERX_PREFIX, HYPERX_PREFIX_LEN))
+			val->strval += HYPERX_PREFIX_LEN;
+		break;
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "HyperX";
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	spin_unlock_irqrestore(&drvdata->lock, flags);
+	return ret;
+}
+
+static const struct power_supply_desc hyperx_headset_battery_desc = {
+	.name = "hyperx_headset_battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = hyperx_headset_battery_props,
+	.num_properties = ARRAY_SIZE(hyperx_headset_battery_props),
+	.get_property = hyperx_headset_battery_get_property,
+};
+
+static int hyperx_headset_send_command(struct hyperx_headset_device *drvdata,
+				       u8 command)
+{
+	struct hid_device *hdev = drvdata->hdev;
+	u8 *buf;
+	int ret;
+
+	buf = kzalloc(HYPERX_PACKET_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = HYPERX_REPORT_ID;
+	buf[1] = command;
+
+	ret = hid_hw_raw_request(hdev, HYPERX_REPORT_ID, buf,
+				 HYPERX_PACKET_SIZE, HID_OUTPUT_REPORT,
+				 HID_REQ_SET_REPORT);
+
+	if (ret < 0)
+		hid_err(hdev, "hw_raw_request failed (command 0x%02x)\n",
+			command);
+
+	kfree(buf);
+	return ret;
+}
+
+static void hyperx_headset_poll_work(struct work_struct *work)
+{
+	struct hyperx_headset_device *drvdata = container_of(
+		work, struct hyperx_headset_device, poll_work.work);
+
+	hyperx_headset_send_command(drvdata, HYPERX_CMD_GET_CONNECTED);
+	hyperx_headset_send_command(drvdata, HYPERX_CMD_GET_BATTERY);
+	hyperx_headset_send_command(drvdata, HYPERX_CMD_GET_CHARGING);
+
+	schedule_delayed_work(&drvdata->poll_work,
+			      msecs_to_jiffies(HYPERX_POLL_INTERVAL_MS));
+}
+
+static void hyperx_headset_set_wireless_status(struct hid_device *hdev,
+					       bool connected)
+{
+	struct usb_interface *intf;
+
+	if (!hid_is_usb(hdev))
+		return;
+
+	intf = to_usb_interface(hdev->dev.parent);
+	usb_set_wireless_status(intf, connected ?
+					      USB_WIRELESS_STATUS_CONNECTED :
+					      USB_WIRELESS_STATUS_DISCONNECTED);
+}
+
+static void hyperx_headset_battery_work(struct work_struct *work)
+{
+	struct hyperx_headset_device *drvdata =
+		container_of(work, struct hyperx_headset_device, battery_work);
+	struct power_supply_config battery_cfg = { .drv_data = drvdata };
+	unsigned long flags;
+	bool connected;
+
+	spin_lock_irqsave(&drvdata->lock, flags);
+	connected = drvdata->is_connected;
+	spin_unlock_irqrestore(&drvdata->lock, flags);
+
+	hyperx_headset_set_wireless_status(drvdata->hdev, connected);
+
+	if (connected && !drvdata->battery) {
+		struct power_supply *ps;
+
+		ps = power_supply_register(&drvdata->hdev->dev,
+					   &hyperx_headset_battery_desc,
+					   &battery_cfg);
+		if (IS_ERR(ps)) {
+			hid_err(drvdata->hdev,
+				"power_supply_register failed\n");
+			return;
+		}
+
+		power_supply_powers(ps, &drvdata->hdev->dev);
+
+		spin_lock_irqsave(&drvdata->lock, flags);
+		drvdata->battery = ps;
+		spin_unlock_irqrestore(&drvdata->lock, flags);
+	} else if (!connected && drvdata->battery) {
+		struct power_supply *ps;
+
+		spin_lock_irqsave(&drvdata->lock, flags);
+		ps = drvdata->battery;
+		drvdata->battery = NULL;
+		spin_unlock_irqrestore(&drvdata->lock, flags);
+
+		power_supply_unregister(ps);
+	}
+}
+
+static void
+hyperx_headset_parse_battery_event(struct hyperx_headset_device *drvdata,
+				   u8 *data)
+{
+	unsigned long flags;
+	u8 state1 = data[2];
+	u8 state2 = data[3];
+	u8 level = data[4];
+
+	// Battery event is invalid if both states are 0
+	if (state1 == 0 && state2 == 0)
+		return;
+
+	spin_lock_irqsave(&drvdata->lock, flags);
+
+	if (drvdata->battery_level != level) {
+		drvdata->battery_level = level;
+
+		if (drvdata->battery)
+			power_supply_changed(drvdata->battery);
+	}
+
+	spin_unlock_irqrestore(&drvdata->lock, flags);
+}
+
+static void
+hyperx_headset_parse_charging_event(struct hyperx_headset_device *drvdata,
+				    u8 *data)
+{
+	unsigned long flags;
+	bool charging = (data[2] == 1);
+
+	spin_lock_irqsave(&drvdata->lock, flags);
+
+	if (drvdata->is_charging != charging) {
+		drvdata->is_charging = charging;
+
+		if (drvdata->battery)
+			power_supply_changed(drvdata->battery);
+	}
+
+	spin_unlock_irqrestore(&drvdata->lock, flags);
+}
+
+static void
+hyperx_headset_parse_connected_event(struct hyperx_headset_device *drvdata,
+				     u8 *data)
+{
+	unsigned long flags;
+	bool state_changed = false;
+	bool connected = (data[2] == 1);
+
+	spin_lock_irqsave(&drvdata->lock, flags);
+
+	if (drvdata->is_connected != connected) {
+		drvdata->is_connected = connected;
+		state_changed = true;
+	}
+
+	spin_unlock_irqrestore(&drvdata->lock, flags);
+
+	if (state_changed)
+		schedule_work(&drvdata->battery_work);
+}
+
+static int hyperx_headset_probe(struct hid_device *hdev,
+				const struct hid_device_id *id)
+{
+	int ret;
+	struct hyperx_headset_device *drvdata;
+
+	drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
+	if (drvdata == NULL)
+		return -ENOMEM;
+	drvdata->hdev = hdev;
+	drvdata->is_connected = false;
+	drvdata->is_charging = false;
+	drvdata->battery_level = 100;
+	spin_lock_init(&drvdata->lock);
+	hid_set_drvdata(hdev, drvdata);
+
+	INIT_DELAYED_WORK(&drvdata->poll_work, hyperx_headset_poll_work);
+	INIT_WORK(&drvdata->battery_work, hyperx_headset_battery_work);
+
+	ret = hid_parse(hdev);
+	if (ret != 0) {
+		hid_err(hdev, "parse failed\n");
+		return ret;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+	if (ret != 0) {
+		hid_err(hdev, "hw_start failed\n");
+		return ret;
+	}
+
+	schedule_delayed_work(&drvdata->poll_work, 0);
+
+	return 0;
+}
+
+static int hyperx_headset_raw_event(struct hid_device *hdev,
+				    struct hid_report *report, u8 *data,
+				    int size)
+{
+	struct hyperx_headset_device *drvdata = hid_get_drvdata(hdev);
+
+	if (size < 5 || data[0] != HYPERX_REPORT_ID)
+		return 0;
+
+	switch (data[1]) {
+	case HYPERX_CMD_GET_CONNECTED:
+	case HYPERX_RESP_CONNECTED:
+		hyperx_headset_parse_connected_event(drvdata, data);
+		break;
+
+	case HYPERX_CMD_GET_BATTERY:
+	case HYPERX_RESP_BATTERY:
+		hyperx_headset_parse_battery_event(drvdata, data);
+		break;
+
+	case HYPERX_CMD_GET_CHARGING:
+	case HYPERX_RESP_CHARGING:
+		hyperx_headset_parse_charging_event(drvdata, data);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void hyperx_headset_remove(struct hid_device *hdev)
+{
+	struct hyperx_headset_device *drvdata = hid_get_drvdata(hdev);
+
+	if (drvdata) {
+		cancel_delayed_work_sync(&drvdata->poll_work);
+		cancel_work_sync(&drvdata->battery_work);
+
+		if (drvdata->battery) {
+			power_supply_unregister(drvdata->battery);
+			drvdata->battery = NULL;
+		}
+	}
+
+	hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id hyperx_headset_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_HP,
+			 USB_DEVICE_ID_HP_HYPERX_CLOUD_III_WIRELESS) },
+	{}
+};
+MODULE_DEVICE_TABLE(hid, hyperx_headset_devices);
+
+static struct hid_driver hyperx_headset_driver = {
+	.name = "hyperx-headset",
+	.id_table = hyperx_headset_devices,
+	.probe = hyperx_headset_probe,
+	.raw_event = hyperx_headset_raw_event,
+	.remove = hyperx_headset_remove,
+};
+module_hid_driver(hyperx_headset_driver);
+
+MODULE_AUTHOR("Sofia Schneider <sofia@schn.dev>");
+MODULE_DESCRIPTION("HID driver for HyperX headsets");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 1059922baaac..aa2c3a71315b 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -696,6 +696,7 @@
 #define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0941	0x0941
 #define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0641	0x0641
 #define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_1f4a	0x1f4a
+#define USB_DEVICE_ID_HP_HYPERX_CLOUD_III_WIRELESS 0x05b7
 
 #define USB_VENDOR_ID_HUION		0x256c
 #define USB_DEVICE_ID_HUION_TABLET	0x006e
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH] HID: hyperx-headset: Add support for HyperX headset devices
From: sashiko-bot @ 2026-06-22  2:28 UTC (permalink / raw)
  To: Sofia Schneider; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <20260622021744.145340-1-sofia@schn.dev>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Inverted teardown order in `hyperx_headset_remove` leads to Use-After-Free.
- [High] Missing lock during power supply unregistration causes a data race and Use-After-Free.
- [High] Missing work cancellation on `hid_hw_start` error path leads to Use-After-Free.
- [Medium] Hardcoded power supply name causes sysfs collisions for multiple devices or interfaces.
--

commit 435d0aab64fdcdfdde5a060312bb493c98996f6a
Author: Sofia Schneider <sofia@schn.dev>

HID: hyperx-headset: Add support for HyperX headset devices

Introduce a HID driver for HyperX Cloud III Wireless headsets,
supporting battery reporting and connection status.

> diff --git a/drivers/hid/hid-hyperx-headset.c b/drivers/hid/hid-hyperx-headset.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/hid/hid-hyperx-headset.c

[ ... ]

> +static const struct power_supply_desc hyperx_headset_battery_desc = {
> +	.name = "hyperx_headset_battery",
> +	.type = POWER_SUPPLY_TYPE_BATTERY,

[Severity: Medium]
Does this static power supply name cause sysfs collisions?

If a user connects multiple identical headsets, or if the headset
exposes multiple HID interfaces that bind to this driver, the second
instance will fail to register with a -EEXIST error.

Could this be dynamically allocated, perhaps using devm_kasprintf with
hdev->uniq or a counter, to ensure each device gets a unique name?

> +	.properties = hyperx_headset_battery_props,
> +	.num_properties = ARRAY_SIZE(hyperx_headset_battery_props),
> +	.get_property = hyperx_headset_battery_get_property,
> +};

[ ... ]

> +static int hyperx_headset_probe(struct hid_device *hdev,
> +				const struct hid_device_id *id)
> +{

[ ... ]

> +	ret = hid_parse(hdev);
> +	if (ret != 0) {
> +		hid_err(hdev, "parse failed\n");
> +		return ret;
> +	}
> +
> +	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
> +	if (ret != 0) {
> +		hid_err(hdev, "hw_start failed\n");
> +		return ret;
> +	}

[Severity: High]
Can this error path leak pending work items?

The hid_hw_start() call initializes the low-level driver, which can
immediately start receiving hardware interrupts before completing the
logical HID connection.

If a packet arrives during this window, hyperx_headset_raw_event() can
schedule drvdata->battery_work.

If hid_hw_start() fails and returns an error, the function exits without
calling cancel_work_sync(&drvdata->battery_work). The drvdata structure is
then freed by devres, leaving a pending work item that will dereference
freed memory when it executes.

Should the error path ensure background work is cancelled before returning?

> +
> +	schedule_delayed_work(&drvdata->poll_work, 0);
> +
> +	return 0;
> +}

[ ... ]

> +static void hyperx_headset_remove(struct hid_device *hdev)
> +{
> +	struct hyperx_headset_device *drvdata = hid_get_drvdata(hdev);
> +
> +	if (drvdata) {
> +		cancel_delayed_work_sync(&drvdata->poll_work);
> +		cancel_work_sync(&drvdata->battery_work);

[Severity: High]
Could this teardown sequence result in re-queued work?

The driver calls cancel_work_sync() before calling hid_hw_stop().
This leaves a window where the hardware is still active and can generate
interrupts after the work queues have been canceled.

If a device state change event arrives during this window,
hyperx_headset_raw_event() will call schedule_work(&drvdata->battery_work).

When hyperx_headset_remove() completes, devres frees the drvdata structure.
The incorrectly re-queued work item will execute later, dereferencing the
freed drvdata memory.

Should hid_hw_stop() be called before cancelling the work queues?

> +
> +		if (drvdata->battery) {
> +			power_supply_unregister(drvdata->battery);
> +			drvdata->battery = NULL;
> +		}

[Severity: High]
Is it safe to access and clear drvdata->battery here without holding
drvdata->lock?

If an interrupt arrives concurrently, hyperx_headset_raw_event() will
acquire the lock, observe a non-null drvdata->battery, and call
power_supply_changed(drvdata->battery).

Since power_supply_unregister() can concurrently drop the device reference
and free the structure, power_supply_changed() might access freed memory.

Should the pointer be cleared to null under the spinlock before
unregistering, similar to how it is handled in
hyperx_headset_battery_work()?

> +	}
> +
> +	hid_hw_stop(hdev);
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260622021744.145340-1-sofia@schn.dev?part=1

^ permalink raw reply

* Re: [PATCH 1/4] dt-bindings: input: Add binding for Qualcomm SPMI PMIC haptics
From: Fenglin Wu @ 2026-06-22  2:28 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: linux-arm-msm, Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Lee Jones, Stephen Boyd, Bjorn Andersson,
	Konrad Dybcio, David Collins, Subbaraman Narayanamurthy,
	Kamal Wadhwa, kernel, linux-input, devicetree, linux-kernel
In-Reply-To: <44778bec-68c5-426d-bc05-b4013630b3ee@kernel.org>


On 6/19/2026 12:18 PM, Krzysztof Kozlowski wrote:
> On 17/06/2026 13:02, Fenglin Wu wrote:
>> On 6/17/2026 6:35 PM, Krzysztof Kozlowski wrote:
>>> On Tue, Jun 16, 2026 at 03:08:24AM -0700, Fenglin Wu wrote:
>>>> ....
>>>> +
>>>> +  qcom,lra-period-us:
>>>> +    description:
>>>> +      LRA actuator initial resonance period in microseconds
>>>> +      (1,000,000 / resonant_freq_hz).  Used to configure T_LRA-based play
>>>> +      rates and the auto-resonance zero-crossing window.
>>> This does not feel like static characteristic. Isn't period depending on
>>> intensity of vibration you want to have? Why would that be fixed per
>>> board?
>> This period is specifically used for playbacks that require
>> auto-resonance to be enabled, which I referred to as "T_LRA-based" and
>> "auto-resonance zero-crossing window." It plays a key role in the
>> "DIRECT_PLAY" mode, which produces a constant vibration effect. To
>> adjust the vibration intensity during this constant effect, the hardware
>> does it by scaling the peak voltage of the driver signals, rather than
>> changing the frequency.
> But maybe changing frequency runtime still would be useful?
It could be, but the LRA F0 (resonant frequency) still needs to be the 
starting point. You can control vibration intensity by driving the LRA 
slightly off resonance by a given percentage—for example, to reach 50% 
vibration, you could probably drive it 10% off resonant frequency, and 
that mapping also depends on the LRA characteristic. Keep in mind that 
LRA is a spring-mass resonant system, so its output is not linear with 
driving frequency; it is a High_Q system, and its output actually shows 
a sharp peak at the resonance point. By contrast, the relationship 
between driving voltage and its output is much more linear, so scaling 
vibration intensity by adjusting the driving voltage is easier to 
control. Qcom haptics HW scales vibration intensity in DIRECT_PLAY mode 
(for constant vibration effect) by scaling the driving voltage instead. 
That said, the HW can also change the driving waveform frequency by 
updating the T-LRA registers, and this property has to be specified as 
an initial value; otherwise, you won't have a baseline to achieve that.

>
> What does the frequency depend on? IOW, how do you choose the value here
> - based on some motor-part characteristics?
It's the resonant frequency of the LRA actuator that's mounted on the board.

>
> Best regards,
> Krzysztof

^ permalink raw reply

* [PATCH v1] iio: temperature: hid-sensor-temperature: switch to non-devm iio_device_register()
From: Sanjay Chitroda @ 2026-06-22  5:21 UTC (permalink / raw)
  To: jikos, jic23, srinivas.pandruvada
  Cc: dlechner, nuno.sa, andy, hongyan.song, linux-input, linux-iio,
	linux-kernel

From: Sanjay Chitroda <sanjayembeddedse@gmail.com>

Avoid using devm_iio_device_register(), as this driver requires explicit
error handling and teardown ordering.

Mixing devm_* APIs with goto-based error unwinding breaks the expected
LIFO resource release model and can introduce race windows during device
removal. In particular, the IIO device may remain visible to userspace
while dependent resources are already being freed, potentially leading
to use-after-free issues.

Add explicit iio_device_unregister() call in the teardown path to ensure
deterministic cleanup and follow kernel resource management conventions.

Fixes: 59d0f2da3569 ("iio: hid: Add temperature sensor support")
Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
 drivers/iio/temperature/hid-sensor-temperature.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c
index 9f628a8e5cfb..34bff7e9f3a3 100644
--- a/drivers/iio/temperature/hid-sensor-temperature.c
+++ b/drivers/iio/temperature/hid-sensor-temperature.c
@@ -244,7 +244,7 @@ static int hid_temperature_probe(struct platform_device *pdev)
 	if (ret)
 		goto error_remove_trigger;
 
-	ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
+	ret = iio_device_register(indio_dev);
 	if (ret)
 		goto error_remove_callback;
 
@@ -264,6 +264,7 @@ static void hid_temperature_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct temperature_state *temp_st = iio_priv(indio_dev);
 
+	iio_device_unregister(indio_dev);
 	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE);
 	hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes);
 }
-- 
--


^ permalink raw reply related

* [PATCH v2 0/8] HID: iio: Avoid race between callback setup and device exposure
From: Sanjay Chitroda @ 2026-06-22  5:29 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, David Lechner,
	Nuno Sá, Andy Shevchenko, Archana Patni, Song Hongyan
  Cc: linux-input, linux-iio, linux-kernel, srinivas pandruvada,
	Sanjay Chitroda

Hi all,

This series avoid a race condition in HID IIO drivers related to the
ordering between callback registration and device exposure.

Currently, several HID IIO drivers register the IIO device (making it
visible to userspace and other kernel consumers) before all required
callbacks and resources are fully initialized, or rely on devm-based
cleanup in a way that does not guarantee correct teardown ordering.
This creates a window where the device can be accessed while it is
not fully initialized or is being torn down, potentially leading to
sample drop or stale/no data.

To handle this, the series ensures that:
  - All required callbacks and resources are set up before the device
    is registered with the IIO core
  - Resource cleanup is performed explicitly where ordering matters

PS: This is prepratory series to convert all HID IIO driver to devm.

Testing:
  - Compiled with W=1 for each patch in series

---
Changes in v2:
- Drop fixes tag and rectify commit message with reference to that
- Link to v1: https://patch.msgid.link/20260606-5-june-hid-iio-race-fixes-v1-0-27a848c5758f@gmail.com

To: Jiri Kosina <jikos@kernel.org>
To: Jonathan Cameron <jic23@kernel.org>
To: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
To: David Lechner <dlechner@baylibre.com>
To: Nuno Sá <nuno.sa@analog.com>
To: Andy Shevchenko <andy@kernel.org>
Cc: linux-input@vger.kernel.org
Cc: linux-iio@vger.kernel.org
Cc: linux-kernel@vger.kernel.org

---
Sanjay Chitroda (8):
      iio: orientation: hid-sensor-rotation: Avoid race between callback setup and device exposure
      iio: orientation: hid-sensor-incl-3d: Avoid race between callback setup and device exposure
      iio: gyro: hid-sensor-gyro-3d: Avoid race between callback setup and device exposure
      iio: pressure: hid-sensor-press: Avoid race between callback setup and device exposure
      iio: light: hid-sensor-prox: Avoid race between callback setup and device exposure
      iio: light: hid-sensor-als: Avoid race between callback setup and device exposure
      iio: magnetometer: hid-sensor-magn-3d: Avoid race between callback setup and device exposure
      iio: accel: hid-sensor-accel-3d: Avoid race between callback setup and device exposure

 drivers/iio/accel/hid-sensor-accel-3d.c       | 20 ++++++++++----------
 drivers/iio/gyro/hid-sensor-gyro-3d.c         | 20 ++++++++++----------
 drivers/iio/light/hid-sensor-als.c            | 20 ++++++++++----------
 drivers/iio/light/hid-sensor-prox.c           | 20 ++++++++++----------
 drivers/iio/magnetometer/hid-sensor-magn-3d.c | 20 ++++++++++----------
 drivers/iio/orientation/hid-sensor-incl-3d.c  | 20 ++++++++++----------
 drivers/iio/orientation/hid-sensor-rotation.c | 20 ++++++++++----------
 drivers/iio/pressure/hid-sensor-press.c       | 20 ++++++++++----------
 8 files changed, 80 insertions(+), 80 deletions(-)
---
base-commit: cc746297b23e89bd5df9f91f3a0ca209e8991763
change-id: 20260605-5-june-hid-iio-race-fixes-f8b981f82b80

Best regards,
--  
Sanjay Chitroda <sanjayembeddedse@gmail.com>


^ permalink raw reply

* [PATCH v2 1/8] iio: orientation: hid-sensor-rotation: Avoid race between callback setup and device exposure
From: Sanjay Chitroda @ 2026-06-22  5:29 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, David Lechner,
	Nuno Sá, Andy Shevchenko, Archana Patni, Song Hongyan
  Cc: linux-input, linux-iio, linux-kernel, srinivas pandruvada,
	Sanjay Chitroda
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com>

From: Sanjay Chitroda <sanjayembeddedse@gmail.com>

The driver currently exposes the IIO device to userspace before
completing sensor hub callback registration, and similarly removes
callbacks while the device can still be accessed during teardown.

This creates a timing window where userspace may enable the buffer
before callbacks are available. In such cases:
- samples can be dropped,
- buffered reads may observe stale or no data.

Reorder probe and remove paths to ensure callbacks are active before
device exposure and are removed after device is no longer accessible.

This avoids a race window leading to data loss.

Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
 drivers/iio/orientation/hid-sensor-rotation.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index 20563d8efaf6..6773bb0ec204 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -332,12 +332,6 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = iio_device_register(indio_dev);
-	if (ret) {
-		dev_err(&pdev->dev, "device register failed\n");
-		goto error_remove_trigger;
-	}
-
 	rot_state->callbacks.send_event = dev_rot_proc_event;
 	rot_state->callbacks.capture_sample = dev_rot_capture_sample;
 	rot_state->callbacks.pdev = pdev;
@@ -345,13 +339,19 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
 					&rot_state->callbacks);
 	if (ret) {
 		dev_err(&pdev->dev, "callback reg failed\n");
-		goto error_iio_unreg;
+		goto error_remove_trigger;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_callback;
 	}
 
 	return 0;
 
-error_iio_unreg:
-	iio_device_unregister(indio_dev);
+error_remove_callback:
+	sensor_hub_remove_callback(hsdev, hsdev->usage);
 error_remove_trigger:
 	hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
 	return ret;
@@ -364,8 +364,8 @@ static void hid_dev_rot_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct dev_rot_state *rot_state = iio_priv(indio_dev);
 
-	sensor_hub_remove_callback(hsdev, hsdev->usage);
 	iio_device_unregister(indio_dev);
+	sensor_hub_remove_callback(hsdev, hsdev->usage);
 	hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
 }
 

-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 2/8] iio: orientation: hid-sensor-incl-3d: Avoid race between callback setup and device exposure
From: Sanjay Chitroda @ 2026-06-22  5:29 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, David Lechner,
	Nuno Sá, Andy Shevchenko, Archana Patni, Song Hongyan
  Cc: linux-input, linux-iio, linux-kernel, srinivas pandruvada,
	Sanjay Chitroda
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com>

From: Sanjay Chitroda <sanjayembeddedse@gmail.com>

The driver currently exposes the IIO device to userspace before
completing sensor hub callback registration, and similarly removes
callbacks while the device can still be accessed during teardown.

This creates a timing window where userspace may enable the buffer
before callbacks are available. In such cases:
- samples can be dropped,
- buffered reads may observe stale or no data.

Reorder probe and remove paths to ensure callbacks are active before
device exposure and are removed after device is no longer accessible.

This avoids a race window leading to data loss.

Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
 drivers/iio/orientation/hid-sensor-incl-3d.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c
index c7fbff498be7..5696e4ef3633 100644
--- a/drivers/iio/orientation/hid-sensor-incl-3d.c
+++ b/drivers/iio/orientation/hid-sensor-incl-3d.c
@@ -356,12 +356,6 @@ static int hid_incl_3d_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = iio_device_register(indio_dev);
-	if (ret) {
-		dev_err(&pdev->dev, "device register failed\n");
-		goto error_remove_trigger;
-	}
-
 	incl_state->callbacks.send_event = incl_3d_proc_event;
 	incl_state->callbacks.capture_sample = incl_3d_capture_sample;
 	incl_state->callbacks.pdev = pdev;
@@ -370,13 +364,19 @@ static int hid_incl_3d_probe(struct platform_device *pdev)
 					&incl_state->callbacks);
 	if (ret) {
 		dev_err(&pdev->dev, "callback reg failed\n");
-		goto error_iio_unreg;
+		goto error_remove_trigger;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_callback;
 	}
 
 	return 0;
 
-error_iio_unreg:
-	iio_device_unregister(indio_dev);
+error_remove_callback:
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D);
 error_remove_trigger:
 	hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes);
 	return ret;
@@ -389,8 +389,8 @@ static void hid_incl_3d_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct incl_3d_state *incl_state = iio_priv(indio_dev);
 
-	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D);
 	iio_device_unregister(indio_dev);
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D);
 	hid_sensor_remove_trigger(indio_dev, &incl_state->common_attributes);
 }
 

-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 3/8] iio: gyro: hid-sensor-gyro-3d: Avoid race between callback setup and device exposure
From: Sanjay Chitroda @ 2026-06-22  5:29 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, David Lechner,
	Nuno Sá, Andy Shevchenko, Archana Patni, Song Hongyan
  Cc: linux-input, linux-iio, linux-kernel, srinivas pandruvada,
	Sanjay Chitroda
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com>

From: Sanjay Chitroda <sanjayembeddedse@gmail.com>

The driver currently exposes the IIO device to userspace before
completing sensor hub callback registration, and similarly removes
callbacks while the device can still be accessed during teardown.

This creates a timing window where userspace may enable the buffer
before callbacks are available. In such cases:
- samples can be dropped,
- buffered reads may observe stale or no data.

Reorder probe and remove paths to ensure callbacks are active before
device exposure and are removed after device is no longer accessible.

This avoids a race window leading to data loss.

Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
 drivers/iio/gyro/hid-sensor-gyro-3d.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
index bbca2111e79b..c8130b488f10 100644
--- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -324,12 +324,6 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = iio_device_register(indio_dev);
-	if (ret) {
-		dev_err(&pdev->dev, "device register failed\n");
-		goto error_remove_trigger;
-	}
-
 	gyro_state->callbacks.send_event = gyro_3d_proc_event;
 	gyro_state->callbacks.capture_sample = gyro_3d_capture_sample;
 	gyro_state->callbacks.pdev = pdev;
@@ -337,13 +331,19 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
 					&gyro_state->callbacks);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "callback reg failed\n");
-		goto error_iio_unreg;
+		goto error_remove_trigger;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_callback;
 	}
 
 	return ret;
 
-error_iio_unreg:
-	iio_device_unregister(indio_dev);
+error_remove_callback:
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
 error_remove_trigger:
 	hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes);
 	return ret;
@@ -356,8 +356,8 @@ static void hid_gyro_3d_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
 
-	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
 	iio_device_unregister(indio_dev);
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
 	hid_sensor_remove_trigger(indio_dev, &gyro_state->common_attributes);
 }
 

-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 4/8] iio: pressure: hid-sensor-press: Avoid race between callback setup and device exposure
From: Sanjay Chitroda @ 2026-06-22  5:30 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, David Lechner,
	Nuno Sá, Andy Shevchenko, Archana Patni, Song Hongyan
  Cc: linux-input, linux-iio, linux-kernel, srinivas pandruvada,
	Sanjay Chitroda
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com>

From: Sanjay Chitroda <sanjayembeddedse@gmail.com>

The driver currently exposes the IIO device to userspace before
completing sensor hub callback registration, and similarly removes
callbacks while the device can still be accessed during teardown.

This creates a timing window where userspace may enable the buffer
before callbacks are available. In such cases:
- samples can be dropped,
- buffered reads may observe stale or no data.

Reorder probe and remove paths to ensure callbacks are active before
device exposure and are removed after device is no longer accessible.

This avoids a race window leading to data loss.

Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
 drivers/iio/pressure/hid-sensor-press.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c
index 3e47a10d72a8..8f81a6d65b9f 100644
--- a/drivers/iio/pressure/hid-sensor-press.c
+++ b/drivers/iio/pressure/hid-sensor-press.c
@@ -291,12 +291,6 @@ static int hid_press_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = iio_device_register(indio_dev);
-	if (ret) {
-		dev_err(&pdev->dev, "device register failed\n");
-		goto error_remove_trigger;
-	}
-
 	press_state->callbacks.send_event = press_proc_event;
 	press_state->callbacks.capture_sample = press_capture_sample;
 	press_state->callbacks.pdev = pdev;
@@ -304,13 +298,19 @@ static int hid_press_probe(struct platform_device *pdev)
 					&press_state->callbacks);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "callback reg failed\n");
-		goto error_iio_unreg;
+		goto error_remove_trigger;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_callback;
 	}
 
 	return ret;
 
-error_iio_unreg:
-	iio_device_unregister(indio_dev);
+error_remove_callback:
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
 error_remove_trigger:
 	hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
 	return ret;
@@ -323,8 +323,8 @@ static void hid_press_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct press_state *press_state = iio_priv(indio_dev);
 
-	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
 	iio_device_unregister(indio_dev);
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
 	hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
 }
 

-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 5/8] iio: light: hid-sensor-prox: Avoid race between callback setup and device exposure
From: Sanjay Chitroda @ 2026-06-22  5:30 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, David Lechner,
	Nuno Sá, Andy Shevchenko, Archana Patni, Song Hongyan
  Cc: linux-input, linux-iio, linux-kernel, srinivas pandruvada,
	Sanjay Chitroda
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com>

From: Sanjay Chitroda <sanjayembeddedse@gmail.com>

The driver currently exposes the IIO device to userspace before
completing sensor hub callback registration, and similarly removes
callbacks while the device can still be accessed during teardown.

This creates a timing window where userspace may enable the buffer
before callbacks are available. In such cases:
- samples can be dropped,
- buffered reads may observe stale or no data.

Reorder probe and remove paths to ensure callbacks are active before
device exposure and are removed after device is no longer accessible.

This avoids a race window leading to data loss.

Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
 drivers/iio/light/hid-sensor-prox.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index 9059f00f0ced..11609dc4c5dc 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -312,12 +312,6 @@ static int hid_prox_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = iio_device_register(indio_dev);
-	if (ret) {
-		dev_err(&pdev->dev, "device register failed\n");
-		goto error_remove_trigger;
-	}
-
 	prox_state->callbacks.send_event = prox_proc_event;
 	prox_state->callbacks.capture_sample = prox_capture_sample;
 	prox_state->callbacks.pdev = pdev;
@@ -325,13 +319,19 @@ static int hid_prox_probe(struct platform_device *pdev)
 					   &prox_state->callbacks);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "callback reg failed\n");
-		goto error_iio_unreg;
+		goto error_remove_trigger;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_callback;
 	}
 
 	return ret;
 
-error_iio_unreg:
-	iio_device_unregister(indio_dev);
+error_remove_callback:
+	sensor_hub_remove_callback(hsdev, hsdev->usage);
 error_remove_trigger:
 	hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
 	return ret;
@@ -344,8 +344,8 @@ static void hid_prox_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct prox_state *prox_state = iio_priv(indio_dev);
 
-	sensor_hub_remove_callback(hsdev, hsdev->usage);
 	iio_device_unregister(indio_dev);
+	sensor_hub_remove_callback(hsdev, hsdev->usage);
 	hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
 }
 

-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 6/8] iio: light: hid-sensor-als: Avoid race between callback setup and device exposure
From: Sanjay Chitroda @ 2026-06-22  5:30 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, David Lechner,
	Nuno Sá, Andy Shevchenko, Archana Patni, Song Hongyan
  Cc: linux-input, linux-iio, linux-kernel, srinivas pandruvada,
	Sanjay Chitroda
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com>

From: Sanjay Chitroda <sanjayembeddedse@gmail.com>

The driver currently exposes the IIO device to userspace before
completing sensor hub callback registration, and similarly removes
callbacks while the device can still be accessed during teardown.

This creates a timing window where userspace may enable the buffer
before callbacks are available. In such cases:
- samples can be dropped,
- buffered reads may observe stale or no data.

Reorder probe and remove paths to ensure callbacks are active before
device exposure and are removed after device is no longer accessible.

This avoids a race window leading to data loss.

Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
 drivers/iio/light/hid-sensor-als.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index ae2fda8dc500..823bb56b9873 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -406,25 +406,25 @@ static int hid_als_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = iio_device_register(indio_dev);
-	if (ret) {
-		dev_err(&pdev->dev, "device register failed\n");
-		goto error_remove_trigger;
-	}
-
 	als_state->callbacks.send_event = als_proc_event;
 	als_state->callbacks.capture_sample = als_capture_sample;
 	als_state->callbacks.pdev = pdev;
 	ret = sensor_hub_register_callback(hsdev, hsdev->usage, &als_state->callbacks);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "callback reg failed\n");
-		goto error_iio_unreg;
+		goto error_remove_trigger;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_callback;
 	}
 
 	return ret;
 
-error_iio_unreg:
-	iio_device_unregister(indio_dev);
+error_remove_callback:
+	sensor_hub_remove_callback(hsdev, hsdev->usage);
 error_remove_trigger:
 	hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
 	return ret;
@@ -437,8 +437,8 @@ static void hid_als_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct als_state *als_state = iio_priv(indio_dev);
 
-	sensor_hub_remove_callback(hsdev, hsdev->usage);
 	iio_device_unregister(indio_dev);
+	sensor_hub_remove_callback(hsdev, hsdev->usage);
 	hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
 }
 

-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 7/8] iio: magnetometer: hid-sensor-magn-3d: Avoid race between callback setup and device exposure
From: Sanjay Chitroda @ 2026-06-22  5:30 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, David Lechner,
	Nuno Sá, Andy Shevchenko, Archana Patni, Song Hongyan
  Cc: linux-input, linux-iio, linux-kernel, srinivas pandruvada,
	Sanjay Chitroda
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com>

From: Sanjay Chitroda <sanjayembeddedse@gmail.com>

The driver currently exposes the IIO device to userspace before
completing sensor hub callback registration, and similarly removes
callbacks while the device can still be accessed during teardown.

This creates a timing window where userspace may enable the buffer
before callbacks are available. In such cases:
- samples can be dropped,
- buffered reads may observe stale or no data.

Reorder probe and remove paths to ensure callbacks are active before
device exposure and are removed after device is no longer accessible.

This avoids a race window leading to data loss.

Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
 drivers/iio/magnetometer/hid-sensor-magn-3d.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
index 23884825eb00..acae77273bd5 100644
--- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -513,12 +513,6 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = iio_device_register(indio_dev);
-	if (ret) {
-		dev_err(&pdev->dev, "device register failed\n");
-		goto error_remove_trigger;
-	}
-
 	magn_state->callbacks.send_event = magn_3d_proc_event;
 	magn_state->callbacks.capture_sample = magn_3d_capture_sample;
 	magn_state->callbacks.pdev = pdev;
@@ -526,13 +520,19 @@ static int hid_magn_3d_probe(struct platform_device *pdev)
 					&magn_state->callbacks);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "callback reg failed\n");
-		goto error_iio_unreg;
+		goto error_remove_trigger;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_callback;
 	}
 
 	return ret;
 
-error_iio_unreg:
-	iio_device_unregister(indio_dev);
+error_remove_callback:
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
 error_remove_trigger:
 	hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);
 	return ret;
@@ -545,8 +545,8 @@ static void hid_magn_3d_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct magn_3d_state *magn_state = iio_priv(indio_dev);
 
-	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
 	iio_device_unregister(indio_dev);
+	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
 	hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);
 }
 

-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 8/8] iio: accel: hid-sensor-accel-3d: Avoid race between callback setup and device exposure
From: Sanjay Chitroda @ 2026-06-22  5:30 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, David Lechner,
	Nuno Sá, Andy Shevchenko, Archana Patni, Song Hongyan
  Cc: linux-input, linux-iio, linux-kernel, srinivas pandruvada,
	Sanjay Chitroda
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com>

From: Sanjay Chitroda <sanjayembeddedse@gmail.com>

The driver currently exposes the IIO device to userspace before
completing sensor hub callback registration, and similarly removes
callbacks while the device can still be accessed during teardown.

This creates a timing window where userspace may enable the buffer
before callbacks are available. In such cases:
- samples can be dropped,
- buffered reads may observe stale or no data.

Reorder probe and remove paths to ensure callbacks are active before
device exposure and are removed after device is no longer accessible.

This avoids a race window leading to data loss.

Signed-off-by: Sanjay Chitroda <sanjayembeddedse@gmail.com>
---
 drivers/iio/accel/hid-sensor-accel-3d.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
index 42c4259bf209..12481cfe9800 100644
--- a/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -386,12 +386,6 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = iio_device_register(indio_dev);
-	if (ret) {
-		dev_err(&pdev->dev, "device register failed\n");
-		goto error_remove_trigger;
-	}
-
 	accel_state->callbacks.send_event = accel_3d_proc_event;
 	accel_state->callbacks.capture_sample = accel_3d_capture_sample;
 	accel_state->callbacks.pdev = pdev;
@@ -399,13 +393,19 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
 					&accel_state->callbacks);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "callback reg failed\n");
-		goto error_iio_unreg;
+		goto error_remove_trigger;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "device register failed\n");
+		goto error_remove_callback;
 	}
 
 	return ret;
 
-error_iio_unreg:
-	iio_device_unregister(indio_dev);
+error_remove_callback:
+	sensor_hub_remove_callback(hsdev, hsdev->usage);
 error_remove_trigger:
 	hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
 	return ret;
@@ -418,8 +418,8 @@ static void hid_accel_3d_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct accel_3d_state *accel_state = iio_priv(indio_dev);
 
-	sensor_hub_remove_callback(hsdev, hsdev->usage);
 	iio_device_unregister(indio_dev);
+	sensor_hub_remove_callback(hsdev, hsdev->usage);
 	hid_sensor_remove_trigger(indio_dev, &accel_state->common_attributes);
 }
 

-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH v1] iio: temperature: hid-sensor-temperature: switch to non-devm iio_device_register()
From: sashiko-bot @ 2026-06-22  5:39 UTC (permalink / raw)
  To: Sanjay Chitroda; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <20260622052135.1804135-1-sanjayembedded@gmail.com>

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

Pre-existing issues:
- [Critical] Concurrent execution of HID sensor hub callbacks and device unbind leads to a Use-After-Free of `indio_dev`.
- [High] The static global `temperature_callbacks` struct is mutated during probe, causing data corruption and cross-device routing bugs when multiple sensors are present.
--

commit 829c19b8a370c5d8b09345d4e07b865deb47a88f
Author: Sanjay Chitroda <sanjayembeddedse@gmail.com>

iio: temperature: hid-sensor-temperature: switch to non-devm iio_device_register()

This commit replaces devm_iio_device_register() with iio_device_register() 
to ensure deterministic cleanup. It adds an explicit iio_device_unregister() 
call in the remove path to fix potential use-after-free race conditions 
during device removal.

> diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c
> index 9f628a8e5cfbe..34bff7e9f3a33 100644
> --- a/drivers/iio/temperature/hid-sensor-temperature.c
> +++ b/drivers/iio/temperature/hid-sensor-temperature.c
> @@ -244,7 +244,7 @@ static int hid_temperature_probe(struct platform_device *pdev)
>  	if (ret)
>  		goto error_remove_trigger;
>  
> -	ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
> +	ret = iio_device_register(indio_dev);

[Severity: High]
This isn't a bug introduced by this patch, but looking at the probe function
just prior to this registration, is there a risk of corrupting device routing
when multiple sensors are present?

The driver defines temperature_callbacks as a static global struct:

static struct hid_sensor_hub_callbacks temperature_callbacks = {
        .send_event = &temperature_proc_event,
        .capture_sample = &temperature_capture_sample,
};

During hid_temperature_probe(), the current device's pdev is directly
assigned to temperature_callbacks.pdev.

If a system has multiple HID temperature sensors, does the last probed sensor
overwrite this global pointer? It seems like when sensor_hub_raw_event()
processes hardware events, it might fetch the shared struct and pass the
single overwritten pdev to temperature_capture_sample(). Would this cause the
data from all temperature sensors to be written exclusively into the IIO state
of the last probed sensor?

[ ... ]

> @@ -264,6 +264,7 @@ static void hid_temperature_remove(struct platform_device *pdev)
>  	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>  	struct temperature_state *temp_st = iio_priv(indio_dev);
>  
> +	iio_device_unregister(indio_dev);
>  	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TEMPERATURE);

[Severity: Critical]
This is a pre-existing issue, but I wanted to ask about the teardown order
here.

In hid_temperature_remove(), we call sensor_hub_remove_callback(), which
removes the callback from the sensor hub's list under dyn_callback_lock.
However, it doesn't appear to synchronize with or wait for concurrently
executing callback handlers like sensor_hub_raw_event() to finish before
returning.

Since indio_dev is allocated with devm_iio_device_alloc() during probe, it
will be freed by devres immediately after hid_temperature_remove() returns.

If sensor_hub_raw_event() is executing concurrently and has already retrieved
the callback pointer, could it pass the pdev to temperature_capture_sample()
and perform a use-after-free write on the freed private state?

For example:
temp_st->scan.temperature_data = *(s32 *)raw_data;

Should we be flushing or waiting for running callbacks to complete before
letting the device context be freed?

>  	hid_sensor_remove_trigger(indio_dev, &temp_st->common_attributes);
>  }

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

^ permalink raw reply

* Re: [PATCH v2 5/8] iio: light: hid-sensor-prox: Avoid race between callback setup and device exposure
From: sashiko-bot @ 2026-06-22  5:42 UTC (permalink / raw)
  To: Sanjay Chitroda; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-5-1cfabcd1881e@gmail.com>

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

New issues:
- [Critical] The reordering introduces a Use-After-Free race condition in the probe error path due to a lack of synchronization when removing the callback.
- [Critical] Reordering `sensor_hub_remove_callback()` after `iio_device_unregister()` creates a severe race condition where lockless list traversal races with IIO buffer teardown, potentially causing an infinite loop.

Pre-existing issues:
- [Critical] Preexisting Use-After-Free in the device remove path due to lack of callback synchronization.
--

commit a4fd8cfbe15835905bbb5054997cb2a8715dcdd2
Author: Sanjay Chitroda <sanjayembeddedse@gmail.com>

iio: light: hid-sensor-prox: Avoid race between callback setup and device exposure

The driver currently exposes the IIO device to userspace before completing
sensor hub callback registration, and similarly removes callbacks while the
device can still be accessed during teardown. This reorders probe and remove
paths to avoid a race window.

> diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
> index 9059f00f0cedf..11609dc4c5dc6 100644
> --- a/drivers/iio/light/hid-sensor-prox.c
> +++ b/drivers/iio/light/hid-sensor-prox.c

[ ... ]

> @@ -325,13 +319,19 @@ static int hid_prox_probe(struct platform_device *pdev)
>  					   &prox_state->callbacks);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "callback reg failed\n");
> -		goto error_iio_unreg;
> +		goto error_remove_trigger;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "device register failed\n");
> +		goto error_remove_callback;
>  	}
>  
>  	return ret;
>  
> -error_iio_unreg:
> -	iio_device_unregister(indio_dev);
> +error_remove_callback:
> +	sensor_hub_remove_callback(hsdev, hsdev->usage);

[Severity: Critical]
Could this introduce a use-after-free if the callback fires during error
handling?

Looking at sensor_hub_remove_callback(), it unlinks the callback from the
list but does not appear to wait for actively executing handlers to finish.
If a sensor event triggers concurrently, hid_prox_probe() could return,
allowing the devres framework to free the indio_dev and prox_state memory.

Would an actively executing callback then dereference this freed memory?

>  error_remove_trigger:
>  	hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
>  	return ret;
> @@ -344,8 +344,8 @@ static void hid_prox_remove(struct platform_device *pdev)
>  	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>  	struct prox_state *prox_state = iio_priv(indio_dev);
>  
> -	sensor_hub_remove_callback(hsdev, hsdev->usage);
>  	iio_device_unregister(indio_dev);
> +	sensor_hub_remove_callback(hsdev, hsdev->usage);

[Severity: Critical]
Does reordering these calls create a race condition with IIO buffer teardown?

By unregistering the IIO device before removing the sensor hub callback, a
concurrently firing callback could invoke iio_push_to_buffers() while
iio_device_unregister() is tearing down the buffers.

iio_push_to_buffers() traverses the buffer list locklessly:

    list_for_each_entry(buf, &iio_dev_opaque->buffer_list, buffer_list) {

At the same time, iio_device_unregister() modifies this list using
list_del_init() via iio_buffer_deactivate(). If the lockless iterator
evaluates a buffer node exactly when list_del_init() resets its pointers to
point to itself, could this cause an endless loop and a hard CPU stall?

[Severity: Critical]
This is a pre-existing issue, but does sensor_hub_remove_callback() properly
synchronize with active callbacks before returning?

Since sensor_hub_remove_callback() unlinks the callback but does not wait
for executions of sensor_hub_raw_event() to finish, hid_prox_remove() can
return while callbacks are still running.

Once the function returns, the devres framework frees indio_dev and
prox_state. Can this lead to a use-after-free when the still-running
callback attempts to access the freed memory?

>  	hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com?part=5

^ permalink raw reply

* Re: [PATCH v2 1/8] iio: orientation: hid-sensor-rotation: Avoid race between callback setup and device exposure
From: sashiko-bot @ 2026-06-22  5:42 UTC (permalink / raw)
  To: Sanjay Chitroda; +Cc: linux-input, dmitry.torokhov
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-1-1cfabcd1881e@gmail.com>

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

New issues:
- [High] Reordering `iio_device_unregister()` before `sensor_hub_remove_callback()` creates a TOCTOU race condition that can lead to IIO buffer list corruption and infinite loops.

Pre-existing issues:
- [High] Concurrent execution of sensor hub callbacks can cause use-after-free on probe failure or device removal due to lack of synchronization.
- [High] Malicious or malformed HID devices can trigger an out-of-bounds read of kernel memory due to a lack of bounds checking on the raw data buffer.
--

commit 3524bf526c21d398ff01cdcd77d3cff858988d72
Author: Sanjay Chitroda <sanjayembeddedse@gmail.com>

iio: orientation: hid-sensor-rotation: Avoid race between callback setup and device exposure

This patch reorders the probe and remove paths in the hid-sensor-rotation
driver to ensure sensor hub callbacks are registered before the IIO device
is exposed, and removed after the device is unregistered. This aims to
prevent userspace from accessing the device before callbacks are ready.

> diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
> --- a/drivers/iio/orientation/hid-sensor-rotation.c
> +++ b/drivers/iio/orientation/hid-sensor-rotation.c

[ ... ]

> @@ -345,13 +339,19 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
>  	rot_state->callbacks.send_event = dev_rot_proc_event;
>  	rot_state->callbacks.capture_sample = dev_rot_capture_sample;

[Severity: High]
This isn't a bug introduced by this patch, but does dev_rot_capture_sample()
lack bounds checking on the raw data buffer?

If a maliciously crafted HID descriptor specifies a small size (e.g., 4 bytes)
for the quaternion field, the HID core will allocate a buffer matching that
small size.

In dev_rot_capture_sample(), if the size doesn't match the expected 16 bytes,
it falls back to a blind copy:

        } else {
                memcpy(&rot_state->scan.sampled_vals, raw_data,
                       sizeof(rot_state->scan.sampled_vals));
        }

Since sizeof(rot_state->scan.sampled_vals) is 16 bytes, this reads past the
end of a smaller raw_data buffer and copies adjacent kernel heap memory into
the IIO buffer. Userspace can then read this IIO buffer, resulting in an
information leak.

>  	rot_state->callbacks.pdev = pdev;
>  	ret = sensor_hub_register_callback(hsdev, hsdev->usage,
>  					&rot_state->callbacks);
>  	if (ret) {
>  		dev_err(&pdev->dev, "callback reg failed\n");
> -		goto error_iio_unreg;
> +		goto error_remove_trigger;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
>  		dev_err(&pdev->dev, "device register failed\n");
> -		goto error_remove_trigger;
> -	}
>  
>  	return 0;
>  
> -error_iio_unreg:
> -	iio_device_unregister(indio_dev);
> +error_remove_callback:
> +	sensor_hub_remove_callback(hsdev, hsdev->usage);

[Severity: High]
This is a pre-existing issue, but does removing the callback here leave a
window for a use-after-free?

sensor_hub_raw_event() looks up a callback and executes it under pdata->lock.
However, sensor_hub_remove_callback() removes the callback from
pdata->dyn_callback_list without waiting for any currently executing
callbacks in sensor_hub_raw_event() to complete.

If a callback is executing concurrently on another CPU when probe fails or
the device is removed, the driver will exit and devres will free the
indio_dev and rot_state memory structures. The still-executing callback
will then continue reading and writing to the freed rot_state fields.

>  error_remove_trigger:
>  	hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
>  	return ret;
> @@ -364,8 +364,8 @@ static void hid_dev_rot_remove(struct platform_device *pdev)
>  	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>  	struct dev_rot_state *rot_state = iio_priv(indio_dev);
>  
> -	sensor_hub_remove_callback(hsdev, hsdev->usage);
>  	iio_device_unregister(indio_dev);
> +	sensor_hub_remove_callback(hsdev, hsdev->usage);

[Severity: High]
Does reordering iio_device_unregister() before sensor_hub_remove_callback()
create a race condition that can lead to an infinite loop?

iio_device_unregister() disables all buffers, which calls list_del_init() on
the buffer list under the IIO core info_exist_lock. If the sensor hub
callback has not been removed yet, a hardware interrupt can still trigger
the callback, which calls dev_rot_proc_event() and subsequently
iio_push_to_buffers().
        
iio_push_to_buffers() iterates over the buffer list locklessly using
list_for_each_entry(). If list_del_init() executes concurrently with this
lockless iteration, the 'next' pointer of the removed list element points
to itself. This causes the lockless iterator to enter an infinite loop and
hang the kernel.

Should the data producer (the callback) be stopped before destroying the
data consumer (unregistering the IIO device)?

>  	hid_sensor_remove_trigger(indio_dev, &rot_state->common_attributes);
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com?part=1

^ permalink raw reply

* Re: [PATCH v2 4/8] iio: pressure: hid-sensor-press: Avoid race between callback setup and device exposure
From: sashiko-bot @ 2026-06-22  5:44 UTC (permalink / raw)
  To: Sanjay Chitroda; +Cc: linux-input, dmitry.torokhov
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-4-1cfabcd1881e@gmail.com>

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

New issues:
- [High] Reordering `iio_device_unregister()` before `sensor_hub_remove_callback()` introduces a race condition leading to list corruption or Use-After-Free.

Pre-existing issues:
- [High] Lack of synchronization in `sensor_hub_remove_callback()` allows concurrent events to cause a Use-After-Free in the probe error path and device removal.
- [High] Unaligned and out-of-bounds memory access in `press_capture_sample()`.
--

commit 9b0142efc7c55a69dc83b67a2c132e1f174fd7cc
Author: Sanjay Chitroda <sanjayembeddedse@gmail.com>
Subject: iio: pressure: hid-sensor-press: Avoid race between callback setup and device exposure

This patch reorders the probe and remove paths in the hid-sensor-press
driver. It registers the sensor hub callbacks before exposing the IIO
device to userspace, and in reverse during teardown, intending to fix
timing windows where buffers might be accessed before callbacks are ready.

> diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c
> index 3e47a10d72a89..8f81a6d65b9f7 100644
> --- a/drivers/iio/pressure/hid-sensor-press.c
> +++ b/drivers/iio/pressure/hid-sensor-press.c

[ ... ]

> @@ -304,13 +298,19 @@ static int hid_press_probe(struct platform_device *pdev)
>  					&press_state->callbacks);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "callback reg failed\n");
> -		goto error_iio_unreg;
> +		goto error_remove_trigger;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "device register failed\n");
> +		goto error_remove_callback;
>  	}
>  
>  	return ret;
>  
> -error_iio_unreg:
> -	iio_device_unregister(indio_dev);
> +error_remove_callback:
> +	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);

[Severity: High]
This is a pre-existing issue, but does sensor_hub_remove_callback() wait
for any executing callbacks to complete?

If iio_device_register() fails, the new error path calls
sensor_hub_remove_callback(). If a callback like press_proc_event() is
already running concurrently, it appears the memory for press_state will be
freed by devres while the callback is still dereferencing it, potentially
leading to a use-after-free.

>  error_remove_trigger:
>  	hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
>  	return ret;
> @@ -323,8 +323,8 @@ static void hid_press_remove(struct platform_device *pdev)
>  	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>  	struct press_state *press_state = iio_priv(indio_dev);
>  
> -	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
>  	iio_device_unregister(indio_dev);
> +	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);

[Severity: High]
Does unregistering the IIO device before removing the sensor hub callback
introduce a regression during teardown?

By explicitly reversing the teardown order, iio_device_unregister() will
dismantle the IIO buffers via iio_disable_all_buffers(). If a hardware
event arrives concurrently, the still-active press_proc_event() callback can
call iio_push_to_buffers_with_ts() and attempt to push data to buffers being
locklessly destroyed, which could lead to list corruption or a
use-after-free.

>  	hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
>  }

[Severity: High]
This isn't a bug introduced by this patch, but is press_capture_sample()
safely handling the raw_data buffer?

Looking at the callback handling in press_capture_sample():

drivers/iio/pressure/hid-sensor-press.c:press_capture_sample() {
	...
	switch (usage_id) {
	case HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE:
		press_state->scan.press_data = *(u32 *)raw_data;
        ...
}

The pointer raw_data is directly cast to u32 * and dereferenced without
verifying raw_len or checking for proper alignment.

Could a maliciously crafted HID descriptor with a shorter report length
cause an out-of-bounds read that leaks heap memory to userspace, or trigger
unaligned access faults on architectures that strictly require alignment?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com?part=4

^ permalink raw reply

* Re: [PATCH v2 3/8] iio: gyro: hid-sensor-gyro-3d: Avoid race between callback setup and device exposure
From: sashiko-bot @ 2026-06-22  5:45 UTC (permalink / raw)
  To: Sanjay Chitroda; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <20260622-5-june-hid-iio-race-fixes-v2-3-1cfabcd1881e@gmail.com>

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

New issues:
- [Critical] Reordering `iio_device_unregister()` to execute before `sensor_hub_remove_callback()` introduces a critical race condition leading to list corruption or an infinite loop.
- [High] The patch introduces a use-after-free vulnerability on the probe error path if `iio_device_register()` fails.

Pre-existing issues:
- [Critical] A preexisting architectural flaw in the `hid-sensor-hub` framework allows a use-after-free if a raw event is processed during driver removal.
--

commit 081315902ee73f264b09ca59da9c7688bdfeefd0
Author: Sanjay Chitroda <sanjayembeddedse@gmail.com>

iio: gyro: hid-sensor-gyro-3d: Avoid race between callback setup and device exposure

The driver currently exposes the IIO device to userspace before
completing sensor hub callback registration, and similarly removes
callbacks while the device can still be accessed during teardown.

> diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
> index bbca2111e79bb..c8130b488f103 100644
> --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
> +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c

[ ... ]

> @@ -337,13 +331,19 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
>  					&gyro_state->callbacks);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "callback reg failed\n");
> -		goto error_iio_unreg;
> +		goto error_remove_trigger;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "device register failed\n");
> +		goto error_remove_callback;
>  	}
>  
>  	return ret;
>  
> -error_iio_unreg:
> -	iio_device_unregister(indio_dev);
> +error_remove_callback:
> +	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);

[Severity: High]
Does this error path introduce a use-after-free vulnerability?

By moving sensor_hub_register_callback() to execute before
iio_device_register(), if device registration fails, the new error path
removes the callback and returns.

However, sensor_hub_remove_callback() does not wait for concurrently
executing callbacks to finish. If an incoming hardware event triggers
gyro_3d_proc_event() on another CPU right after registration, and then
probe returns, devres will automatically free indio_dev (which contains
gyro_state). The still-executing callback would then dereference the freed
memory.

Could this be prevented by ensuring the callback is only registered when it
is safe to process events?

[Severity: Critical]
This isn't a bug introduced by this patch, but there is a pre-existing
architectural flaw in the hid-sensor-hub framework that exacerbates this
teardown race.

Looking at sensor_hub_remove_callback(), it removes a sensor's callback from
dyn_callback_list under a lock, then immediately returns without waiting for
sensor_hub_raw_event() handlers executing on other CPUs to complete.

Because sensor_hub_raw_event() temporarily drops the list lock and invokes the
driver's function pointers based on a cached pointer, if driver removal or
probe failure completes while this is executing, devres frees the driver
memory. The framework then continues to dereference the cached callback
pointer into freed memory.

Would it be possible to add proper synchronization, such as a completion or
RCU sync, in the sensor_hub_remove_callback() framework to prevent this?

> @@ -356,8 +356,8 @@ static void hid_gyro_3d_remove(struct platform_device *pdev)
>  	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>  	struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
>  
> -	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
>  	iio_device_unregister(indio_dev);
> +	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);

[Severity: Critical]
Does reordering these functions introduce a race condition that leads to list
corruption or an infinite loop?

iio_device_unregister() disables IIO buffers and dismantles the buffer list
locklessly using list_del_init(). If we call iio_device_unregister() while
the sensor hub callback is still active, incoming hardware events can still
trigger gyro_3d_proc_event().

gyro_3d_proc_event() calls iio_push_to_buffers_with_timestamp(), which
iterates over the buffer list locklessly:

gyro_3d_proc_event()
   iio_push_to_buffers_with_timestamp()
      list_for_each_entry()

Because list_del_init() points the next pointer to itself, traversing the list
concurrently while it is being dismantled could cause the loop to never
terminate, resulting in a hard CPU hang.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-5-june-hid-iio-race-fixes-v2-0-1cfabcd1881e@gmail.com?part=3

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox