All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Denis Benato" <denis.benato@linux.dev>
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v12 11/17] HID: asus: add support for turbo buttons
Date: Sat, 31 Oct 2026 17:54:47 +0000	[thread overview]
Message-ID: <sashiko-outbox-192079@kernel.org> (raw)
In-Reply-To: <20261031174130.208240-12-denis.benato@linux.dev>

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] HID: asus: ABBA deadlock between ally_prefs.lock and sysfs removal
- [High] HID: asus: UAF of ally_prefs.button_entries on second device removal
- [Low] HID: asus: mix of cleanup helpers and goto in ally_prefs_setup() and helper
- [Low] HID: asus: malformed kernel-doc comment on ally_set_turbo_params()
- [Low] HID: asus: per-call stack table and manual copy in ally_btn_param() and setter
--

commit d9c271786b208c5b04b58afd3cb388dfc9868ef1
Author: Denis Benato <denis.benato@linux.dev>

HID: asus: add support for turbo buttons

This commit adds support for configuring ROG Ally turbo button parameters
by exposing turbo_period and toggle_period sysfs attributes for each
gamepad button and restoring the settings during device initialization.

> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 3e58c5ca0cbd8..d20e433af5740 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -812,6 +812,80 @@ struct ally_joystick_resp_curve {

[ ... ]

> +/* Payload order. Not the same order as enum ally_button_id. */
> +struct ally_turbo_config {
> +	struct ally_btn_turbo_params btn_du;
> +	struct ally_btn_turbo_params btn_dd;
> +	struct ally_btn_turbo_params btn_dl;
> +	struct ally_btn_turbo_params btn_dr;
> +	struct ally_btn_turbo_params btn_j0b;
> +	struct ally_btn_turbo_params btn_j1b;
> +	struct ally_btn_turbo_params btn_lb;
> +	struct ally_btn_turbo_params btn_rb;
> +	struct ally_btn_turbo_params btn_a;
> +	struct ally_btn_turbo_params btn_b;
> +	struct ally_btn_turbo_params btn_x;
> +	struct ally_btn_turbo_params btn_y;
> +	struct ally_btn_turbo_params btn_view;
> +	struct ally_btn_turbo_params btn_menu;
> +	struct ally_btn_turbo_params btn_m2;
> +	struct ally_btn_turbo_params btn_m1;
> +};

[ ... ]

> @@ -2120,9 +2197,297 @@ static const struct attribute_group ally_attr_groups[] = {
>  	},
>  };
>  
> +static struct ally_btn_turbo_params *ally_btn_param(enum ally_button_id btn)
> +{
> +	struct ally_btn_turbo_params *table[ALLY_BTN_MAX] = {
> +		[ALLY_BTN_A] = &ally_prefs.turbo.btn_a,
> +		[ALLY_BTN_B] = &ally_prefs.turbo.btn_b,
> +		[ALLY_BTN_X] = &ally_prefs.turbo.btn_x,
> +		[ALLY_BTN_Y] = &ally_prefs.turbo.btn_y,
> +		[ALLY_BTN_LB] = &ally_prefs.turbo.btn_lb,
> +		[ALLY_BTN_RB] = &ally_prefs.turbo.btn_rb,
> +		[ALLY_BTN_DU] = &ally_prefs.turbo.btn_du,
> +		[ALLY_BTN_DD] = &ally_prefs.turbo.btn_dd,
> +		[ALLY_BTN_DL] = &ally_prefs.turbo.btn_dl,
> +		[ALLY_BTN_DR] = &ally_prefs.turbo.btn_dr,
> +		[ALLY_BTN_J0B] = &ally_prefs.turbo.btn_j0b,
> +		[ALLY_BTN_J1B] = &ally_prefs.turbo.btn_j1b,
> +		[ALLY_BTN_MENU] = &ally_prefs.turbo.btn_menu,
> +		[ALLY_BTN_VIEW] = &ally_prefs.turbo.btn_view,
> +		[ALLY_BTN_M1] = &ally_prefs.turbo.btn_m1,
> +		[ALLY_BTN_M2] = &ally_prefs.turbo.btn_m2,
> +	};
> +
> +	if (btn >= ALLY_BTN_MAX)
> +		return NULL;
> +
> +	return table[btn];
> +}
> +
> +/** ally_set_turbo_params() - Set the turbo parameters
> + *
> + * Caller holds ally_prefs.lock.
> + *
> + * @hdev: The HID device to send the packet to.
> + * Return: 0 on success, negative error code on failure.
> + */

[Severity: Low]
This isn't a bug, but in drivers/hid/hid-asus.c above ally_set_turbo_params(),
the kernel-doc comment has the function identifier on the opening /** line
and places the locking note before the @hdev parameter description.

Could the opening /** be placed on its own line and the description text
moved after the parameter list so scripts/kernel-doc can parse it cleanly?

> +static int ally_set_turbo_params(struct hid_device *hdev)
> +{
> +	struct ally_turbo_config *turbo = &ally_prefs.turbo;
> +	const u8 payload[] = {
> +		turbo->btn_du.turbo, turbo->btn_du.toggle,
> +		turbo->btn_dd.turbo, turbo->btn_dd.toggle,
> +		turbo->btn_dl.turbo, turbo->btn_dl.toggle,
> +		turbo->btn_dr.turbo, turbo->btn_dr.toggle,
> +		turbo->btn_j0b.turbo, turbo->btn_j0b.toggle,
> +		turbo->btn_j1b.turbo, turbo->btn_j1b.toggle,
> +		turbo->btn_lb.turbo, turbo->btn_lb.toggle,
> +		turbo->btn_rb.turbo, turbo->btn_rb.toggle,
> +		turbo->btn_a.turbo, turbo->btn_a.toggle,
> +		turbo->btn_b.turbo, turbo->btn_b.toggle,
> +		turbo->btn_x.turbo, turbo->btn_x.toggle,
> +		turbo->btn_y.turbo, turbo->btn_y.toggle,
> +		turbo->btn_view.turbo, turbo->btn_view.toggle,
> +		turbo->btn_menu.turbo, turbo->btn_menu.toggle,
> +		turbo->btn_m2.turbo, turbo->btn_m2.toggle,
> +		turbo->btn_m1.turbo, turbo->btn_m1.toggle,
> +	};

[Severity: Low]
This isn't a bug, but in drivers/hid/hid-asus.c, ally_btn_param() rebuilds
table[ALLY_BTN_MAX] on the stack on every call instead of marking it static
const (like ally_trigger_limits, ally_anti_deadzones, and ally_resp_curves
earlier in the file), and ally_set_turbo_params() manually copies all 32
bytes into payload[] even though struct ally_turbo_config is already laid
out in hardware payload order.

Could table[] in ally_btn_param() be marked static const (or replaced with
an indexed array), and could struct ally_turbo_config be marked __packed and
passed directly to ally_alloc_cmd()?

[ ... ]

> +static ssize_t btn_turbo_period_show(struct device *dev,
> +				     struct device_attribute *attr, char *buf)
> +{
> +	struct ally_btn_sysfs_entry *entry;
> +	struct ally_btn_turbo_params *params;
> +
> +	entry = container_of(attr, struct ally_btn_sysfs_entry, attr_turbo_period);
> +
> +	guard(mutex)(&ally_prefs.lock);
> +
> +	if (!ally_prefs.turbo_support)
> +		return -EOPNOTSUPP;
> +
> +	params = ally_btn_param(entry->btn);
> +	if (!params)
> +		return -ENODEV;
> +
> +	return sysfs_emit(buf, "%hhu\n", params->turbo);
> +}
> +
> +static ssize_t btn_turbo_period_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf, size_t count)
> +{
> +	struct ally_btn_sysfs_entry *entry;
> +	struct hid_device *hdev = to_hid_device(dev);
> +	struct ally_btn_turbo_params *params;
> +	u8 old_value, value;
> +	int ret;
> +
> +	entry = container_of(attr, struct ally_btn_sysfs_entry, attr_turbo_period);
> +
> +	ret = kstrtou8(buf, 10, &value);
> +	if (ret || value > ALLY_TURBO_PERIOD_MAX)
> +		return ret ? ret : -EINVAL;
> +
> +	scoped_guard(mutex, &ally_prefs.lock) {

[ ... ]

> +static struct ally_btn_sysfs_entry *ally_create_button_attributes(struct hid_device *hdev)
> +{
> +	int i, ret;
> +
> +	if (!ally_prefs.turbo_support)
> +		return NULL;
> +
> +	struct ally_btn_sysfs_entry *entries __free(kfree) = kzalloc_objs(*entries, ALLY_BTN_MAX);
> +	if (!entries)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < ALLY_BTN_MAX; i++) {
> +		entries[i].btn = i;
> +
> +		if (!ally_btn_param(i)) {
> +			hid_err(hdev, "Invalid button id %d for turbo attributes\n", i);
> +			ret = -EINVAL;
> +			goto ally_btn_err_cleanup;
> +		}
> +
> +		ally_btn_turbo_init_attrs(&entries[i]);
> +		entries[i].group.name = ally_button_names[i];
> +		entries[i].group.attrs = entries[i].attrs;
> +
> +		ret = sysfs_create_group(&hdev->dev.kobj, &entries[i].group);
> +		if (ret < 0) {
> +			hid_err(hdev, "Failed to create sysfs group for %s: %d\n",
> +				ally_button_names[i], ret);
> +			goto ally_btn_err_cleanup;
> +		}
> +	}
> +
> +	return no_free_ptr(entries);
> +
> +ally_btn_err_cleanup:
> +	while (i--)
> +		sysfs_remove_group(&hdev->dev.kobj, &entries[i].group);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static void ally_remove_button_attributes(struct hid_device *hdev)
> +{
> +	struct ally_btn_sysfs_entry *entries;
> +	int i;
> +
> +	entries = ally_prefs.button_entries;
> +	if (!entries)
> +		return;
> +
> +	for (i = 0; i < ALLY_BTN_MAX; i++)
> +		sysfs_remove_group(&hdev->dev.kobj, &entries[i].group);
> +
> +	kfree(entries);
> +	ally_prefs.button_entries = NULL;
> +}
> +
>  static int ally_prefs_setup(struct hid_device *hdev)
>  {
> -	int ret, i;
> +	struct ally_btn_sysfs_entry *entries = NULL;
> +	int ret, i, cal_n = 0;
>  
>  	ret = ally_detect_capabilities(hdev);
>  	if (ret)
> @@ -2158,27 +2523,38 @@ static int ally_prefs_setup(struct hid_device *hdev)
>  	/* Skip the calibration groups when the capability is missing. */
>  	if (ally_prefs.user_cal_support || ally_prefs.anti_deadzone_support ||
>  	    ally_prefs.resp_curve_support) {
> -		int cal_i;
> -
> -		for (cal_i = 0; cal_i < ARRAY_SIZE(ally_cal_attr_groups); cal_i++) {
> +		for (; cal_n < ARRAY_SIZE(ally_cal_attr_groups); cal_n++) {
>  			ret = sysfs_create_group(&hdev->dev.kobj,
> -						 ally_cal_attr_groups[cal_i]);
> +						ally_cal_attr_groups[cal_n]);
>  			if (ret) {
>  				hid_err(hdev, "Failed to create Ally sysfs group '%s': %d\n",
> -					ally_cal_attr_groups[cal_i]->name, ret);
> -				while (cal_i--)
> -					sysfs_remove_group(&hdev->dev.kobj,
> -							   ally_cal_attr_groups[cal_i]);
> -				for (i = 0; i < ARRAY_SIZE(ally_attr_groups); i++)
> -					sysfs_remove_group(&hdev->dev.kobj,
> -							   &ally_attr_groups[i]);
> -				return ret;
> +					ally_cal_attr_groups[cal_n]->name, ret);
> +				goto err_cal_groups;
>  			}
>  		}
>  	}
>  
> +	if (ally_prefs.turbo_support) {
> +		entries = ally_create_button_attributes(hdev);
> +		if (IS_ERR(entries)) {
> +			ret = PTR_ERR(entries);
> +			hid_err(hdev, "Failed to create button attributes: %d\n", ret);
> +			goto err_cal_groups;
> +		}
> +	}
> +
> +	ally_prefs.button_entries = entries;
>  	ally_prefs.sysfs_live = true;
>  	return 0;
> +
> +err_cal_groups:
> +	while (cal_n--)
> +		sysfs_remove_group(&hdev->dev.kobj, ally_cal_attr_groups[cal_n]);
> +
> +	for (i = 0; i < ARRAY_SIZE(ally_attr_groups); i++)
> +		sysfs_remove_group(&hdev->dev.kobj, &ally_attr_groups[i]);
> +
> +	return ret;
>  }

[Severity: Low]
This isn't a bug, but in drivers/hid/hid-asus.c,
ally_create_button_attributes() mixes __free(kfree) on entries with goto
ally_btn_err_cleanup, and ally_prefs_setup() mixes
guard(mutex)(&ally_prefs.lock) with goto err_cal_groups.

Since include/linux/cleanup.h advises against mixing scope-based cleanup
helpers and goto error labels within the same function, could these
functions use either pure goto-based cleanup or pure scope-based cleanup?

>  static void ally_sysfs_remove(struct hid_device *hdev)
> @@ -2195,6 +2571,8 @@ static void ally_sysfs_remove(struct hid_device *hdev)
>  
>  	ally_prefs.sysfs_live = false;
>  
> +	ally_remove_button_attributes(hdev);

[Severity: High]
Can this trigger an ABBA deadlock between ally_prefs.lock and kernfs active
node draining?

In drivers/hid/hid-asus.c, when userspace reads or writes any of the turbo
button sysfs attributes (btn_turbo_period_show(), btn_turbo_period_store(),
btn_toggle_period_show(), or btn_toggle_period_store()), kernfs holds an
active reference on the attribute kernfs_node and the callback then acquires
ally_prefs.lock:

  kernfs_fop_read_iter() / kernfs_fop_write_iter()
    kernfs_get_active_of()          /* acquires kn->active */
    btn_turbo_period_show() / btn_turbo_period_store()
      guard(mutex)(&ally_prefs.lock)

Conversely, during device removal in ally_sysfs_remove() (and on the error
unwind paths in ally_prefs_setup() and ally_create_button_attributes()),
ally_prefs.lock is held while calling ally_remove_button_attributes() and
sysfs_remove_group(), which calls kernfs_drain() to wait synchronously for
kn->active to drain:

  ally_sysfs_remove()
    guard(mutex)(&ally_prefs.lock)
    ally_remove_button_attributes()
      sysfs_remove_group()
        kernfs_remove_by_name_ns()
          __kernfs_remove()
            kernfs_drain()          /* waits on kn->active */

If a sysfs read or write is waiting to acquire ally_prefs.lock while
ally_sysfs_remove() or ally_prefs_setup() calls sysfs_remove_group() under
ally_prefs.lock, won't both tasks deadlock waiting on each other?

[Severity: High]
Can removing a second HID_ALLY_INTF_CFG_IN device cause a use-after-free of
ally_prefs.button_entries?

In drivers/hid/hid-asus.c, when the first device (hdev1) probes,
ally_prefs_setup(hdev1) allocates ally_prefs.button_entries via
ally_create_button_attributes(hdev1), registers the attribute groups (which
embed struct device_attribute attr_turbo_period and attr_toggle_period) on
hdev1->dev.kobj, and sets ally_prefs.sysfs_live = true.

If a second matching HID_ALLY_INTF_CFG_IN interface (hdev2) is probed,
ally_prefs_setup(hdev2) first calls ally_detect_capabilities(hdev2)
(overwriting ally_prefs.turbo_support and other global capability flags),
and then sees ally_prefs.sysfs_live == true and returns 0 without
registering sysfs groups on hdev2->dev.kobj:

  ally_prefs_setup(hdev2)
    ally_detect_capabilities(hdev2)
    ...
    guard(mutex)(&ally_prefs.lock);
    ...
    if (ally_prefs.sysfs_live)
        return 0;

When hdev2 is subsequently unbound, asus_remove(hdev2) calls
ally_sysfs_remove(hdev2) -> ally_remove_button_attributes(hdev2):

  ally_remove_button_attributes(hdev2)
    entries = ally_prefs.button_entries;
    for (i = 0; i < ALLY_BTN_MAX; i++)
        sysfs_remove_group(&hdev2->dev.kobj, &entries[i].group);
    kfree(entries);
    ally_prefs.button_entries = NULL;

Because ally_prefs does not track which hid_device owns the live sysfs
groups, sysfs_remove_group() looks for the groups on hdev2->dev.kobj (leaving
them registered on hdev1->dev.kobj) and kfree(entries) frees the backing
struct ally_btn_sysfs_entry memory while hdev1's sysfs attributes are still
active. Wouldn't any subsequent sysfs access on hdev1 (or unbinding hdev1)
dereference freed memory?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261031174130.208240-1-denis.benato@linux.dev?part=11

  reply	other threads:[~2026-10-31 17:54 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-31 17:41 [PATCH v12 00/17] HID: asus: add support for ROG Ally handhelds Denis Benato
2026-10-31 17:41 ` [PATCH v12 01/17] HID: asus: size feature reports from the HID report descriptor Denis Benato
2026-10-31 18:04   ` sashiko-bot
2026-10-31 18:08     ` Denis Benato
2026-10-31 18:30   ` Rebecca Mara Müller
2026-11-01 13:26     ` Denis Benato
2026-10-31 17:41 ` [PATCH v12 02/17] HID: asus: add support for ROG Ally handhelds Denis Benato
2026-10-31 18:11   ` sashiko-bot
2026-10-31 17:41 ` [PATCH v12 03/17] HID: asus: add gamepad configuration Denis Benato
2026-10-31 17:59   ` sashiko-bot
2026-10-31 17:41 ` [PATCH v12 04/17] HID: asus: add vibration strength configuration Denis Benato
2026-10-31 17:41 ` [PATCH v12 05/17] HID: asus: add joysticks inner and outer range configuration Denis Benato
2026-10-31 17:57   ` sashiko-bot
2026-10-31 17:41 ` [PATCH v12 06/17] HID: asus: add triggers " Denis Benato
2026-10-31 17:54   ` sashiko-bot
2026-10-31 17:41 ` [PATCH v12 07/17] HID: asus: add joysticks anti-deadzone configuration Denis Benato
2026-10-31 17:51   ` sashiko-bot
2026-10-31 17:41 ` [PATCH v12 08/17] HID: asus: add support for response curve Denis Benato
2026-10-31 17:41 ` [PATCH v12 09/17] HID: asus: add support to force feedback Denis Benato
2026-10-31 17:41 ` [PATCH v12 10/17] HID: asus: add support for gamepad mode Denis Benato
2026-10-31 17:41 ` [PATCH v12 11/17] HID: asus: add support for turbo buttons Denis Benato
2026-10-31 17:54   ` sashiko-bot [this message]
2026-10-31 17:41 ` [PATCH v12 12/17] HID: asus: add support for btn remapping Denis Benato
2026-10-31 18:00   ` sashiko-bot
2026-10-31 19:06     ` Denis Benato
2026-10-31 17:41 ` [PATCH v12 13/17] HID: asus: identify XG Mobile stations and bind the LED to the reported model Denis Benato
2026-10-31 17:41 ` [PATCH v12 14/17] HID: asus: expose XG Mobile identity and firmware information Denis Benato
2026-10-31 17:41 ` [PATCH v12 15/17] HID: asus: document ROG XG Mobile sysfs attributes Denis Benato
2026-10-31 17:41 ` [PATCH v12 16/17] platform/x86: asus-armoury: pick the eGPU activation from the reported GPU Denis Benato
2026-10-31 17:41 ` [PATCH v12 17/17] MAINTAINERS: add maintainer for the ASUS HID driver Denis Benato

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=sashiko-outbox-192079@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=denis.benato@linux.dev \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.