Linux Input/HID development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Sanjay Govind" <sanjay.govind9@gmail.com>
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v11] Input: xpad - overhaul device data for wireless devices
Date: Tue, 03 Nov 2026 02:36:54 +0000	[thread overview]
Message-ID: <sashiko-outbox-194465@kernel.org> (raw)
In-Reply-To: <20261103021550.1054584-3-sanjay.govind9@gmail.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] Input: xpad - double read of volatile jiffies in xpad_presence_work()
- [Medium] Input: xpad - unlocked mod_delayed_work() race in xpad_presence_work()
- [Medium] Input: xpad - stale wireless pad_info across xpad_suspend() and xpad_resume()
--

commit 1369f7a52af03b86ab596cabb5b216cf2f7e0a71
Author: Sanjay Govind <sanjay.govind9@gmail.com>

Input: xpad - overhaul device data for wireless devices

This commit updates the Xbox 360 wireless controller driver to populate input
device identification from link and capabilities reports rather than using
static receiver-derived defaults. It defers input registration until
capabilities discovery completes or a 500ms timeout expires, protecting the
discovery state with a spinlock and tracking controller generations across
disconnect and suspend.

> diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
> index 810e52782ae89..82408e1c0e044 100644
> --- a/drivers/input/joystick/xpad.c
> +++ b/drivers/input/joystick/xpad.c

[ ... ]

> @@ -993,6 +1037,54 @@ static void xpad_presence_work(struct work_struct *work)
>  		 */
>  		xpad_deinit_input(xpad);
>  	}
> +
> +	if (!info.present || xpad->input_created)
> +		return;
> +
> +	/*
> +	 * The work may already be running when a new pad connects, so its
> +	 * original delayed execution cannot be relied on to finish discovery.
> +	 */
> +	scoped_guard(spinlock_irqsave, &xpad->x360w_lock) {
> +		if (!xpad->pad_info.present || xpad->pad_info.gen != info.gen)
> +			return;
> +
> +		if (!xpad->pad_info.discovery_ready &&
> +		    time_before(jiffies, xpad->pad_info.discovery_deadline)) {
> +			delay = xpad->pad_info.discovery_deadline - jiffies;
> +		} else {
> +			xpad->pad_info.discovery_ready = true;
> +			info = xpad->pad_info;
> +		}
> +	}
> +
> +	if (delay) {
> +		mod_delayed_work(system_percpu_wq, &xpad->work, delay);
> +		return;
> +	}
> +
> +	if (info.discovery_ready) {

[Severity: Medium]
In xpad_presence_work() in drivers/input/joystick/xpad.c, jiffies is read
twice when checking the discovery deadline:

	if (!xpad->pad_info.discovery_ready &&
	    time_before(jiffies, xpad->pad_info.discovery_deadline)) {
		delay = xpad->pad_info.discovery_deadline - jiffies;
	}

Because jiffies is volatile, the compiler emits two separate loads. Even with
local interrupts disabled under xpad->x360w_lock, another CPU can advance
jiffies between the time_before() check and the subtraction.

If jiffies is discovery_deadline - 1 during time_before() and ticks to
discovery_deadline before the subtraction, time_before() succeeds so
info.discovery_ready remains false, while delay evaluates to 0. Both
if (delay) and if (info.discovery_ready) then evaluate to false, causing
xpad_presence_work() to return without rescheduling xpad->work or calling
xpad_init_input(). If the controller does not send a capabilities report,
will it ever be registered?

Similarly, if jiffies advances by two or more ticks between the two reads,
could discovery_deadline - jiffies underflow unsigned long and schedule
xpad->work far in the future? Snapshotting jiffies into a local variable
before time_before() and the subtraction would avoid both issues.

[ ... ]

> @@ -1009,19 +1101,102 @@ static void xpad_presence_work(struct work_struct *work)

[ ... ]

> +	/* Capabilities report */
> +	if (len >= 21 && data[0] == 0x00 && data[1] == 0x05 && data[5] == 0x12) {
> +		discovery_complete = false;
> +
> +		scoped_guard(spinlock_irqsave, &xpad->x360w_lock) {
> +			if (info->present && info->link_report_received &&
> +			    !info->discovery_ready) {
> +				info->flags |= data[20];
> +				/*
> +				 * Some vendors encode identification in the stick
> +				 * capabilities. Only trust it when the VID matches
> +				 * the link report.
> +				 */
> +				if (info->vid &&
> +				    get_unaligned_le16(data + 10) == info->vid &&
> +				    get_unaligned_le16(data + 12)) {
> +					info->pid = get_unaligned_le16(data + 12);
> +					info->version = get_unaligned_le16(data + 14);
> +				}
> +
> +				info->discovery_ready = true;
> +				discovery_complete = true;
> +			}
> +		}
> +
> +		if (discovery_complete)
> +			mod_delayed_work(system_percpu_wq, &xpad->work, 0);
>  	}

[Severity: Medium]
Can mod_delayed_work() in xpad_presence_work() race with
xpad360w_process_packet() and overwrite an immediate 0-delay schedule with a
stale timeout delay?

Consider the following sequence in drivers/input/joystick/xpad.c:

CPU0 (xpad_presence_work)
  scoped_guard(spinlock_irqsave, &xpad->x360w_lock) {
      /* sees !discovery_ready and time_before(jiffies, discovery_deadline) */
      delay = xpad->pad_info.discovery_deadline - jiffies;
  }
  /* lock is released */

CPU1 (xpad_irq_in -> xpad360w_process_packet)
  /* capabilities report arrives (or pad disconnects) */
  info->discovery_ready = true;
  mod_delayed_work(system_percpu_wq, &xpad->work, 0);

CPU0 (xpad_presence_work)
  if (delay) {
      mod_delayed_work(system_percpu_wq, &xpad->work, delay);
      return;
  }

Because xpad_presence_work() is still running when CPU1 queues xpad->work
with delay 0, the work item is pending when CPU0 calls mod_delayed_work()
outside xpad->x360w_lock. CPU0 then replaces the 0-delay timer with the stale
non-zero delay, stalling controller registration or disconnect teardown until
the discovery timeout expires.

Should mod_delayed_work() be called while holding xpad->x360w_lock in
xpad_presence_work() and in the capabilities completion path of
xpad360w_process_packet(), matching the presence-change path?

[ ... ]

> @@ -2271,14 +2533,38 @@ static int xpad_suspend(struct usb_interface *intf, pm_message_t message)
>  		 */
>  		xpad360w_stop_input(xpad);
>  
> +		scoped_guard(spinlock_irqsave, &xpad->x360w_lock) {
> +			info = xpad->pad_info;
> +		}
> +
>  		/*
>  		 * The wireless adapter is going off now, so the
>  		 * gamepads are going to become disconnected.
>  		 * Unless explicitly disabled, power them down
>  		 * so they don't just sit there flashing.
>  		 */
> -		if (auto_poweroff && xpad->pad_present)
> +		if (auto_poweroff && info.present)
>  			xpad360w_poweroff_controller(xpad);
> +
> +		/* Finish teardown if suspend cancelled work for an old pad. */
> +		if (xpad->input_created &&
> +		    (!info.present || xpad->input_gen != info.gen)) {
> +			RCU_INIT_POINTER(xpad->x360w_dev, NULL);
> +			synchronize_rcu();
> +			xpad_deinit_input(xpad);
> +		}
> +
> +		/*
> +		 * A cancelled discovery must be restarted by the resume inquiry.
> +		 * Clear its metadata so late reports cannot seed the next pad.
> +		 */
> +		if (!xpad->input_created) {
> +			scoped_guard(spinlock_irqsave, &xpad->x360w_lock) {
> +				xpad->pad_info = (struct xpad360w_pad_info) {
> +					.gen = xpad->pad_info.gen,
> +				};
> +			}
> +		}
>  	} else {

[Severity: Medium]
As reported by Sashiko on v10 [1], what happens across xpad_suspend() and
xpad_resume() in drivers/input/joystick/xpad.c when a wireless controller is
already registered before suspend (xpad->input_created is true and
xpad->input_gen == info.gen) and is replaced while xpad->irq_in is stopped?

In xpad_suspend(), when xpad->input_created is true and info.present is true
with matching generation, the function stops xpad->irq_in and powers off the
controller via xpad360w_poweroff_controller(), but skips both the teardown
block and the !xpad->input_created reset block. As a result,
xpad->pad_info.present and xpad->pad_info.discovery_ready remain true.

If xpad_resume() subsequently runs without an interface unbind/rebind (for
example during hibernation freeze/thaw or if port suspend fails during system
suspend) and calls xpad360w_start_input() -> xpad_inquiry_pad_presence()
while a different controller is connected to that slot,
xpad360w_process_packet() sees info->present == true and
info->discovery_ready == true:

	if (info->present != present) {
		...
	}

	/* Link report */
	if (len >= 26 && data[0] == 0x00 && data[1] == 0x0F) {
		...
		scoped_guard(spinlock_irqsave, &xpad->x360w_lock) {
			if (info->present && !info->discovery_ready) {

Wouldn't this cause the replacement controller's presence, link, and
capabilities reports to be ignored so it continues using the previous
controller's input_dev and identity?

[1] https://lore.kernel.org/r/20261021221009.361992-3-sanjay.govind9@gmail.com

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261103021550.1054584-3-sanjay.govind9@gmail.com?part=1

      reply	other threads:[~2026-11-03  2:36 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15 22:01 [PATCH v9] xpad: Overhaul device data for wireless devices Sanjay Govind
2026-10-15 22:12 ` sashiko-bot
2026-10-21 21:07 ` Dmitry Torokhov
2026-10-21 22:10 ` [PATCH v10] Input: xpad - overhaul " Sanjay Govind
2026-10-21 22:50   ` sashiko-bot
2026-11-03  2:15   ` [PATCH v11] " Sanjay Govind
2026-11-03  2:36     ` sashiko-bot [this message]

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-194465@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=sanjay.govind9@gmail.com \
    --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