Re: [PATCH 3/3] Input: xpad - add sdata_check() to xpad360 branches

[email protected] Mon, 03 Aug 2026 16:05:11 +0000
Newsgroups org.kernel.vger.linux-input
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [Medium] Zero-length packets incorrectly pass validation and are processed as valid empty states, spuriously releasing all controller buttons.

Pre-existing issues:
- [High] Blocking USB APIs and GFP_KERNEL allocations are unsafely invoked from the atomic URB completion handler.
- [High] Error paths during initialization destroy the input device while leaving a dangling pointer for presence_work to dereference.
- [Medium] 16-bit multi-byte reads and direct array modifications completely bypass the new length validation, leading to stale data leakage.
--

--- Patch [3]: [PATCH 3/3] Input: xpad - add sdata_check() to xpad360 branches ---
commit 0a84609d4ff81bead2c2c2c26f83c5c8f9cc506a
Author: Griffin Kroah-Hartman <[email protected]>

Input: xpad - add sdata_check() to xpad360 branches

This commit adds the sdata_check() safety wrapper to the xpad360_process_packet()
and xpad360w_process_packet() functions. It bounds-checks array accesses to prevent
out-of-bounds reads on the controller data buffer.

> diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
> index 319a4c4a695fe..304229782e455 100644
> --- a/drivers/input/joystick/xpad.c
> +++ b/drivers/input/joystick/xpad.c
> @@ -891,19 +891,24 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
>   */
>  
>  static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
> -				   u16 cmd, unsigned char *data)
> +				   u16 cmd, unsigned char *data, u32 len)
>  {
> +	struct safe_data sdata;
> +
> +	sdata.data = data;
> +	sdata.len = len;
> +
>  	/* valid pad data */
> -	if (data[0] != 0x00)
> +	if (sdata_check(&sdata, 0) != 0x00)
>  		return;

[Severity: Medium]
Does this inadvertently allow zero-length packets to be treated as valid
pad data? Because sdata_check() safely returns 0 when the index is out of
bounds, a zero-length packet will successfully pass this != 0x00 check
and spuriously release all controller buttons.

>  
>  	/* digital pad */
>  	if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {

[ ... ]

> @@ -915,27 +920,29 @@ static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev,
>  	if (!(xpad->mapping & MAP_DPAD_TO_BUTTONS) ||
>  	    xpad->xtype == XTYPE_XBOX360W) {

[ ... ]

>  	/* buttons A,B,X,Y,TL,TR and MODE */
> -	input_report_key(dev, BTN_A,	data[3] & BIT(4));
> -	input_report_key(dev, BTN_B,	data[3] & BIT(5));
> -	input_report_key(dev, BTN_X,	data[3] & BIT(6));
> -	input_report_key(dev, BTN_Y,	data[3] & BIT(7));
> -	input_report_key(dev, BTN_TL,	data[3] & BIT(0));
> -	input_report_key(dev, BTN_TR,	data[3] & BIT(1));
> -	input_report_key(dev, BTN_MODE,	data[3] & BIT(2));
> +	input_report_key(dev, BTN_A,	sdata_check(&sdata, 3) & BIT(4));
> +	input_report_key(dev, BTN_B,	sdata_check(&sdata, 3) & BIT(5));
> +	input_report_key(dev, BTN_X,	sdata_check(&sdata, 3) & BIT(6));
> +	input_report_key(dev, BTN_Y,	sdata_check(&sdata, 3) & BIT(7));
> +	input_report_key(dev, BTN_TL,	sdata_check(&sdata, 3) & BIT(0));
> +	input_report_key(dev, BTN_TR,	sdata_check(&sdata, 3) & BIT(1));
> +	input_report_key(dev, BTN_MODE,	sdata_check(&sdata, 3) & BIT(2));
>  
>  	if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
>  		/* left stick */

[Severity: Medium]
This isn't a bug introduced by this patch, but will the 16-bit stick axis 
reads immediately following this code bypass the new length checks?

xpad_process_packet(), xpad360_process_packet(), and xpadone_process_packet()
read multi-byte values using le16_to_cpup() directly against the raw data
buffer:

    input_report_abs(dev, ABS_X,
                     (__s16) le16_to_cpup((__le16 *)(data + 6)));

A short packet would still cause out-of-bounds reads here since these
accesses completely bypass the sdata_check() wrapper, potentially leaking
uninitialized DMA memory into input events.

[ ... ]

> @@ -1019,14 +1026,18 @@ static void xpad_presence_work(struct work_struct *work)
>   * 01.1 - Pad state (Bytes 4+) valid
>   *
>   */
> -static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
> +static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data, u32 len)
>  {
>  	struct input_dev *dev;
> +	struct safe_data sdata;
>  	bool present;
>  
> +	sdata.data = data;
> +	sdata.len = len;
> +
>  	/* Presence change */
> -	if (data[0] & 0x08) {
> -		present = (data[1] & 0x80) != 0;
> +	if (sdata_check(&sdata, 0) & 0x08) {
> +		present = (sdata_check(&sdata, 1) & 0x80) != 0;
>  
>  		if (xpad->pad_present != present) {
>  			xpad->pad_present = present;
>  			schedule_work(&xpad->work);

[Severity: High]
This is a pre-existing issue, but can the xpad_presence_work thread
dereference a freed pointer if initialization fails?

If xpad_init_input() encounters an error (like during device registration),
it takes the err_free_input path:

xpad_init_input() {
    ...
err_free_input:
    input_free_device(input_dev);
    return error;
}

This frees the device but leaves xpad->dev pointing to the freed memory.
When xpad_presence_work() processes the failure, it logs the error:

xpad_presence_work() {
    ...
    dev_err(&xpad->dev->dev, "unable to init device: %d\n", error);
}

Could this use-after-free lead to memory corruption or a crash?

[ ... ]

> @@ -1262,10 +1273,10 @@ static void xpad_irq_in(struct urb *urb)
>  
>  	switch (xpad->xtype) {
>  	case XTYPE_XBOX360:
> -		xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata);
> +		xpad360_process_packet(xpad, xpad->dev, 0, xpad->idata, urb->actual_length);
>  		break;
>  	case XTYPE_XBOX360W:
> -		xpad360w_process_packet(xpad, 0, xpad->idata);
> +		xpad360w_process_packet(xpad, 0, xpad->idata, urb->actual_length);
>  		break;
>  	case XTYPE_XBOXONE:
>  		xpadone_process_packet(xpad, 0, xpad->idata, urb->actual_length);

[Severity: High]
This is a pre-existing issue, but does xpadone_process_packet() execute
blocking calls and GFP_KERNEL allocations from an atomic context?

When xpad_irq_in() processes an Xbox One GIP_CMD_ANNOUNCE packet from
interrupt context, it follows this call chain:

xpad_irq_in()
  xpadone_process_packet()
    xpad_start_input()
      usb_submit_urb(xpad->irq_in, GFP_KERNEL)

xpad_start_input() also calls xpad_start_xbox_one(), which invokes
usb_set_interface(), a synchronous and blocking USB API. Calling these
from the atomic URB completion handler could lead to scheduling while
atomic warnings and potential deadlocks.

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=3