Linux Input/HID development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Aaron Erhardt" <aer@tuxedocomputers.com>
Cc: dmitry.torokhov@gmail.com, linux-input@vger.kernel.org
Subject: Re: [PATCH v6 1/3] HID: lamparray: add new LampArray helper module
Date: Wed, 16 Sep 2026 15:00:28 +0000	[thread overview]
Message-ID: <20260916150028.A750F1F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20260916144838.456239-2-aer@tuxedocomputers.com>

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] hid: lamparray: out-of-bounds access on field->value array
- [Medium] hid: lamparray: mixing goto and scope-based cleanup
- [Medium] hid: lamparray: silent state update failures due to unverified shared reports
- [Medium] hid: lamparray: TOCTOU race in unregister leaves hardware in wrong state
- [Medium] hid: lamparray: error path fails to revert autonomous mode
- [Medium] hid: lamparray: data race on LED class device state
- [Medium] hid: lamparray: initialization race due to early sysfs exposure
- [Low] leds: lamparray: prints success log messages
--

commit dcca692c120b5bda2e0a125483a058dc7b60dad2
Author: Aaron Erhardt <aer@tuxedocomputers.com>

HID: lamparray: add new LampArray helper module

Add a new hid-lamparray helper module that provides basic support for
devices exposing a Lighting/LampArray application collection. It registers a
single-zone RGB LED representation via the LED subsystem.

> diff --git a/drivers/hid/hid-lamparray.c b/drivers/hid/hid-lamparray.c
> --- /dev/null
> +++ b/drivers/hid/hid-lamparray.c

[ ... ]

> +static int lamparray_read_attributes_report(struct lamparray_device *ldev)
> +{
> +	struct hid_device *hdev = ldev->hdev;
> +	struct hid_report *report;
> +	int ret;
> +	size_t len;
> +
> +	if (!ldev->lamp_count.field) {
> +		hid_warn(hdev, "No LampCount field found\n");
> +		return -ENODEV;
> +	}
> +
> +	if (!ldev->lamparray_kind.field) {
> +		hid_warn(hdev, "No LampArrayKind field found\n");
> +		return -ENODEV;
> +	}
> +
> +	report = ldev->lamp_count.field->report;
> +
> +	if (!report) {

[Severity: Medium]
Can lamparray_kind read stale or zeroed memory here? By only fetching the
report associated with lamp_count, the lamparray_kind field may not be
updated if it resides in a different HID report than lamp_count.

> +		hid_warn(hdev, "LampCount field has no report\n");
> +		return -ENODEV;
> +	}

[ ... ]

> +static int lamparray_parse_update_report(struct lamparray_device *ldev)
> +{
> +	struct hid_device *hdev = ldev->hdev;
> +	struct hid_report_enum *re;
> +	struct hid_report *report;
> +	struct hid_field *field;
> +	int i, j;
> +	int ret = 0;
> +
> +	re = &hdev->report_enum[HID_FEATURE_REPORT];
> +
> +	list_for_each_entry(report, &re->report_list, list) {
> +		for (i = 0; i < report->maxfield; i++) {
> +			field = report->field[i];
> +			if (!field)
> +				continue;
> +
> +			if (!field->usage || !field->maxusage)
> +				continue;
> +
> +			for (j = 0; j < field->maxusage; j++) {
> +				u32 usage = field->usage[j].hid;

[Severity: High]
Could this loop overflow the field->value array? The loop iterates up to
field->maxusage, storing the loop index in the field_value structs.
However, the field->value array is allocated by the HID core based on
field->report_count. If a crafted HID descriptor specifies a maxusage
larger than report_count, the subsequent reads and writes in
get_field_value() and set_field_value() can overflow the array.

> +				u32 collection_idx = field->usage[j].collection_index;
> +				u32 collection_usage = hdev->collection[collection_idx].usage;

[ ... ]

> +static int lamparray_hw_set_state(struct lamparray_device *ldev, u8 r, u8 g,
> +				  u8 b, u8 intensity)
> +{
> +	struct hid_device *hdev = ldev->hdev;
> +	struct hid_report *report;
> +
> +	if (!lamparray_color_fields_is_complete(&ldev->color_update))
> +		return -ENODEV;
> +
> +	if (ldev->range_start.field && ldev->range_end.field) {
> +		set_field_value(&ldev->range_start, 0);
> +		set_field_value(&ldev->range_end, ldev->lamp_count_value - 1);
> +	}
> +
> +	set_field_value(&ldev->color_update.red, r);
> +	set_field_value(&ldev->color_update.green, g);
> +	set_field_value(&ldev->color_update.blue, b);
> +	set_field_value(&ldev->color_update.intensity, intensity);
> +
> +	report = ldev->color_update.red.field->report;
> +	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
> +
> +	return 0;

[Severity: Medium]
Does this drop updates for the other color channels and range fields? If
green, blue, intensity, or the range fields reside in a different report
than the red field, sending only color_update.red.field->report will skip
their updates.

> +}

[ ... ]

> +static int lamparray_restore_state(struct lamparray_device *ldev)
> +{
> +	u8 r, g, b;
> +	int ret;
> +	enum led_brightness brightness;
> +
> +	if (!ldev->use_leds_uapi)
> +		return 0;
> +
> +	guard(mutex)(&ldev->dev_lock);
> +
> +	r = ldev->last_r;
> +	g = ldev->last_g;
> +	b = ldev->last_b;
> +	brightness = ldev->last_brightness;
> +
> +	ldev->mc_cdev.subled_info[0].intensity = r;
> +	ldev->mc_cdev.subled_info[1].intensity = g;
> +	ldev->mc_cdev.subled_info[2].intensity = b;
> +	ldev->mc_cdev.led_cdev.brightness = brightness;

[Severity: Medium]
Can these updates race with concurrent sysfs writes? Modifying these LED
class device fields without holding the led_cdev->led_access mutex while
the sysfs interface is active can cause inconsistent brightness or color
states if userspace writes to them simultaneously.

> +
> +	/*
> +	 * Quirk for devices ignoring intensity values, see
> +	 * lamparray_led_brightness_set for more information.
> +	 */

[ ... ]

> +static ssize_t use_leds_uapi_store(struct device *dev,
> +				   struct device_attribute *attr,
> +				   const char *buf, size_t count)
> +{
> +	struct lamparray_device *ldev = lamparray_ldev_from_sysfs_dev(dev);
> +	bool val, old_val;
> +	int ret;
> +
> +	if (!ldev)
> +		return -ENODEV;
> +
> +	ret = kstrtobool(buf, &val);
> +	if (ret)
> +		return ret;
> +
> +	/* Make sure only one concurrent write is possible */
> +	guard(mutex)(&ldev->sysfs_lock);
> +	old_val = ldev->use_leds_uapi;
> +
> +	if (val == old_val)
> +		return count;
> +
> +	ldev->use_leds_uapi = val;
> +
> +	/*
> +	 * Take the device out of autonomous mode before exposing the LED
> +	 * class device, and hand it back afterwards, so that control is
> +	 * transferred in both directions rather than only at probe.
> +	 */
> +	if (val == 1) {
> +		ret = lamparray_hw_set_autonomous(ldev, false);
> +		if (ret)
> +			goto err_revert;

[Severity: Medium]
Does this function mix goto-based error handling with scope-based cleanup?
The cleanup subsystem guidelines request converting all resources to
scope-based cleanup if using __free() or guard(). Mixing the paradigms in
functions like lamparray_register() and use_leds_uapi_store() can create
ownership semantics confusion.

> +		ret = lamparray_register_led(ldev);
> +		if (ret) {
> +			lamparray_hw_set_autonomous(ldev, true);
> +			goto err_revert;

[ ... ]

> +struct lamparray *
> +lamparray_register(struct hid_device *hdev,
> +		   const struct lamparray_init_state *led_init_state)
> +{
> +	int ret;
> +	struct lamparray_device *ldev;
> +
> +	if (!hdev)
> +		return ERR_PTR(-ENODEV);
> +
> +	struct lamparray *la __free(kfree) = kzalloc_obj(*la, GFP_KERNEL);
> +	if (!la)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ldev = &la->ldev;

[ ... ]

> +	ret = xa_err(xa_store(&lamparray_by_hdev, (unsigned long)hdev, ldev,
> +			      GFP_KERNEL));
> +	if (ret)
> +		goto err_unregister_led;
> +
> +	ret = lamparray_register_sysfs(ldev);
> +	if (ret)
> +		goto err_xa_erase;
> +
> +	ret = lamparray_hw_set_autonomous(ldev, false);

[Severity: Medium]
Could exposing the sysfs file before hardware initialization completes
create a race condition? If userspace writes 0 to use_leds_uapi right
after this call, the store handler will unregister the LED and set
autonomous to true. This probe thread then resumes, unconditionally setting
autonomous to false and skipping lamparray_restore_state(), leaving the
device in a broken state.

> +	if (ret) {
> +		hid_err(hdev, "Could not disable autonomous mode: %d", ret);
> +		goto err_remove_sysfs;
> +	}
> +
> +	hid_info(hdev, "LampArray device registered\n");

[Severity: Low]
Should this log message be removed? The LED subsystem guidelines specify
that drivers should only log errors or warnings, avoiding success messages
on probe.

> +
> +	ret = lamparray_restore_state(ldev);
> +	if (ret) {
> +		hid_err(hdev, "Failed to set default state: %d", ret);
> +		goto err_remove_sysfs;
> +	}

[Severity: Medium]
Does this error path leave the hardware in a non-autonomous state? If
lamparray_restore_state() fails, the code jumps to err_remove_sysfs and
cleans up software state, but does not revert the device back to autonomous
mode.

> +
> +	return_ptr(la);
> +
> +err_remove_sysfs:
> +	lamparray_remove_sysfs(ldev);
> +err_xa_erase:
> +	xa_erase(&lamparray_by_hdev, (unsigned long)hdev);

[ ... ]

> +void lamparray_unregister(struct lamparray *la)
> +{
> +	struct lamparray_device *ldev;
> +
> +	if (!la)
> +		return;
> +
> +	ldev = &la->ldev;
> +
> +	lamparray_hw_set_autonomous(ldev, true);
> +
> +	lamparray_remove_sysfs(ldev);

[Severity: Medium]
Can this sequence permanently leave the hardware in a non-autonomous state?
Calling lamparray_hw_set_autonomous(..., true) before removing the sysfs
interface creates a window where a concurrent sysfs write to use_leds_uapi
can switch the device back to non-autonomous mode right before the device
is completely unregistered.

> +	xa_erase(&lamparray_by_hdev, (unsigned long)ldev->hdev);
> +
> +	mutex_lock(&ldev->sysfs_lock);
> +	lamparray_unregister_led(ldev);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260916144838.456239-1-aer@tuxedocomputers.com?part=1

  reply	other threads:[~2026-09-16 15:00 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-16 14:48 [PATCH v6 0/3] HID: generic: add LampArray support via hid-lamparray helper Aaron Erhardt
2026-09-16 14:48 ` [PATCH v6 1/3] HID: lamparray: add new LampArray helper module Aaron Erhardt
2026-09-16 15:00   ` sashiko-bot [this message]
2026-09-16 14:48 ` [PATCH v6 2/3] HID: generic: add LampArray support via hid-lamparray helper Aaron Erhardt
2026-09-16 14:48 ` [PATCH v6 3/3] HID: lamparray: blank lamps across suspend and restore on resume Aaron Erhardt
2026-09-16 15:00   ` sashiko-bot
2026-09-16 16:15 ` [PATCH v6 0/3] HID: generic: add LampArray support via hid-lamparray helper Cristian Mazzotta
2026-09-17  7:29   ` Aaron Erhardt

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=20260916150028.A750F1F000FF@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=aer@tuxedocomputers.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

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

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