From: sashiko-bot@kernel.org
To: "Matías Martínez" <hello@matias.me>
Cc: dmitry.torokhov@gmail.com, linux-input@vger.kernel.org
Subject: Re: [PATCH] HID: ayaneo: Add AYANEO 3 detachable controller driver
Date: Mon, 24 Aug 2026 22:01:24 +0000 [thread overview]
Message-ID: <20260824220125.297EE1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260824215041.79892-1-hello@matias.me>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Out-of-Bounds/NULL Pointer Dereference when `hdev->maxcollection == 0`
- [High] Use-After-Free due to Devres LIFO Violation on `hid_hw_stop`
- [Medium] Resource Lifecycle Stall in `eject_store` on hardware errors
- [Medium] Data Race allowing new commands to read a stale payload
--
--- Patch [1]: [PATCH] HID: ayaneo: Add AYANEO 3 detachable controller driver ---
commit fb4fc36a684a0e10b09f75f11cd5139de0102be2
Author: Matías Martínez <hello@matias.me>
HID: ayaneo: Add AYANEO 3 detachable controller driver
The AYANEO 3 handheld has a detachable controller with swappable
modules ("Magic Modules"). The controller exposes three USB HID
interfaces behind 1c4f:0002 (a generic SigmaMicro VID/PID, hence the
DMI gate): a gamepad, a keyboard for the extra buttons, and a vendor
interface accepting 65-byte commands.
> diff --git a/drivers/hid/hid-ayaneo.c b/drivers/hid/hid-ayaneo.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/hid/hid-ayaneo.c
[ ... ]
> +static int aya3_cmd(struct aya3 *aya, u8 *resp)
> +{
> + int attempt, ret;
> +
> + lockdep_assert_held(&aya->lock);
> +
> + for (attempt = 0; attempt < AYA3_CMD_ATTEMPTS; attempt++) {
> + reinit_completion(&aya->resp_done);
> + aya->resp_expect = aya->xfer[4];
> + WRITE_ONCE(aya->resp_pending, true);
> +
> + ret = aya3_send(aya);
> + if (ret) {
> + WRITE_ONCE(aya->resp_pending, false);
> + return ret;
> + }
> +
> + if (wait_for_completion_timeout(&aya->resp_done,
> + msecs_to_jiffies(AYA3_CMD_TIMEOUT_MS))) {
> + if (resp)
> + memcpy(resp, aya->resp, AYA3_RESP_SIZE);
> + return 0;
> + }
> + }
> + WRITE_ONCE(aya->resp_pending, false);
> + return -ETIMEDOUT;
> +}
[ ... ]
> +static int aya3_raw_event(struct hid_device *hdev, struct hid_report *report,
> + u8 *data, int size)
> +{
> + struct aya3 *aya = hid_get_drvdata(hdev);
> +
> + if (!READ_ONCE(aya->resp_pending) || size < AYA3_RESP_SIZE)
> + return 0;
> + if (data[AYA3_RESP_CMD] != aya->resp_expect)
> + return 0;
> +
> + memcpy(aya->resp, data, AYA3_RESP_SIZE);
> + WRITE_ONCE(aya->resp_pending, false);
> + complete(&aya->resp_done);
> + return 0;
> +}
[Severity: Medium]
Is there a race condition here if a command times out just as the reply
arrives?
If aya3_cmd() times out, it clears aya->resp_pending and returns. However,
a concurrent aya3_raw_event() might have already read aya->resp_pending as
true.
If a new command is issued immediately, could this delayed event copy stale
data and trigger complete() for the new command?
[ ... ]
> +static ssize_t eject_store(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
[ ... ]
> + /*
> + * Wait for the firmware to report the eject as done. Userspace
> + * must then cut power through ayaneo-ec's controller_power for
> + * the module to be physically released.
> + */
> + ret = -ETIMEDOUT;
> + for (i = 0; i < 20; i++) {
> + msleep(400);
> + if (aya3_check(aya, resp))
> + continue;
> + if (!(resp[AYA3_RESP_EJECT_STATUS] & ~AYA3_EJECT_DONE_MASK)) {
> + ret = 0;
> + break;
> + }
> + }
[Severity: Medium]
Could this loop stall for up to 8 seconds if the hardware is abruptly
unplugged?
If the device is physically removed, aya3_check() will immediately return
a fatal error, such as -ENODEV.
Since this continue statement ignores the error unconditionally, won't it keep
sleeping for all 20 iterations even though the hardware is permanently gone?
[ ... ]
> +static int aya3_probe(struct hid_device *hdev, const struct hid_device_id *id)
> +{
> + struct aya3 *aya;
> + int ret;
> +
> + /* The VID/PID is a generic SigmaMicro ID; bind on AYANEO 3 only */
> + if (!dmi_check_system(aya3_dmi_table))
> + return -ENODEV;
> +
> + if (!hid_is_usb(hdev))
> + return -ENODEV;
> +
> + ret = hid_parse(hdev);
> + if (ret)
> + return ret;
> +
> + /* Bind only the vendor interface, not the gamepad/keyboard ones */
> + if (hdev->collection->usage != (HID_UP_MSVENDOR | 0x0001))
> + return -ENODEV;
[Severity: High]
Can this dereference a NULL or uninitialized pointer if the device presents a
report descriptor with zero collections?
A malformed USB HID device could pass hid_parse() but leave
hdev->maxcollection as 0.
Should this code check that hdev->maxcollection is greater than 0 before
accessing hdev->collection->usage?
[ ... ]
> +static void aya3_remove(struct hid_device *hdev)
> +{
> + hid_hw_close(hdev);
> + hid_hw_stop(hdev);
> +}
[Severity: High]
Does this manual hid_hw_stop() call violate the devres release order and
create a use-after-free window?
The driver registers the LED class device earlier in aya3_probe() using
the managed devm_led_classdev_multicolor_register().
Because devres actions run after the aya3_remove() callback finishes, calling
hid_hw_stop() here tears down the HID transport while the LED sysfs attributes
are still active.
If user space writes to the LED sysfs attribute during this window,
could aya3_led_set() attempt to send commands over the stopped transport?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260824215041.79892-1-hello@matias.me?part=1
next prev parent reply other threads:[~2026-08-24 22:01 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-24 21:50 [PATCH] HID: ayaneo: Add AYANEO 3 detachable controller driver Matías Martínez
2026-08-24 22:00 ` Antheas Kapenekakis
2026-08-24 22:25 ` Antheas Kapenekakis
2026-08-24 22:47 ` Matías Martínez
2026-08-24 23:23 ` Antheas Kapenekakis
2026-08-25 17:21 ` Matías Martínez
2026-08-24 22:01 ` sashiko-bot [this message]
2026-08-24 22:47 ` Matías Martínez
2026-08-24 22:31 ` [PATCH v2] " Matías Martínez
2026-08-24 22:41 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260824220125.297EE1F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=dmitry.torokhov@gmail.com \
--cc=hello@matias.me \
--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