Re: [PATCH v2] input: xpad: Prevent corruption of urb request.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Is the patch acceptable, or do I need to make any major changes?  If additional justification is required,
I can post kernel log files with the patch not applied, and a test program to cause a kernel panic.

On Fri, 16 May 2014 02:11:57 -0700
Sarah Bessmer <aotos@xxxxxxxxxxx> wrote:

> The xpad_send_led_command() and xpad_play_effect() functions submit urb requests, but do not
> wait for the previous urb request to complete before using the same resources to
> submit a new request.  This can lead to unpredictable undesirable effects, including
> memory corruption and dereferencing of NULL pointers(and needless to say, kernel
> panics).
> 
> Fix the issue by introducing a busy flag set on submission of the urb request, and
> cleared on urb request completion.  If this flag is set while in xpad_send_led_command()
> or xpad_play_effect(), the led/rumble packet data is buffered, and will be sent from
> the urb request completion routine when the current urb request finishes.
> 
> 
> Patch tested with rumble against a Logitech F510 and an X-Box v1 pad.
> 
> 
> Signed-off-by: Sarah Bessmer <aotos@xxxxxxxxxxx>
> ---
> v2:
> -fixed introduced race in xpad_irq_out()
> 
> --- linux-3.14.4/drivers/input/joystick/xpad.c.orig	2014-05-15 13:13:39.000000000 -0700
> +++ linux-3.14.4/drivers/input/joystick/xpad.c	2014-05-16 02:00:56.000000000 -0700
> @@ -78,6 +78,7 @@
>  #include <linux/stat.h>
>  #include <linux/module.h>
>  #include <linux/usb/input.h>
> +#include <linux/spinlock.h>
>  
>  #define DRIVER_AUTHOR "Marko Friedemann <mfr@xxxxxxxxxxxxxxx>"
>  #define DRIVER_DESC "X-Box pad driver"
> @@ -282,7 +283,15 @@ struct usb_xpad {
>  	struct urb *irq_out;		/* urb for interrupt out report */
>  	unsigned char *odata;		/* output data */
>  	dma_addr_t odata_dma;
> -	struct mutex odata_mutex;
> +	int odata_busy;
> +
> +	spinlock_t pend_lock;
> +
> +	unsigned pend_rum;
> +	unsigned char rum_data[XPAD_PKT_LEN];
> +
> +	unsigned pend_led;
> +	unsigned char led_data[XPAD_PKT_LEN];
>  #endif
>  
>  #if defined(CONFIG_JOYSTICK_XPAD_LEDS)
> @@ -541,12 +550,37 @@ static void xpad_irq_out(struct urb *urb
>  	struct usb_xpad *xpad = urb->context;
>  	struct device *dev = &xpad->intf->dev;
>  	int retval, status;
> +	unsigned long flags;
>  
>  	status = urb->status;
>  
>  	switch (status) {
>  	case 0:
>  		/* success */
> +		spin_lock_irqsave(&xpad->pend_lock, flags);
> +		xpad->irq_out->transfer_buffer_length = 0;
> +
> +		if (xpad->pend_rum != 0) {
> +			memcpy(xpad->odata, xpad->rum_data, xpad->pend_rum);
> +			xpad->irq_out->transfer_buffer_length = xpad->pend_rum;
> +			xpad->pend_rum = 0;
> +		} else if (xpad->pend_led != 0) {
> +			memcpy(xpad->odata, xpad->led_data, xpad->pend_led);
> +			xpad->irq_out->transfer_buffer_length = xpad->pend_led;
> +			xpad->pend_led = 0;
> +		}
> +
> +		if (xpad->irq_out->transfer_buffer_length != 0) {
> +			spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +			if (usb_submit_urb(xpad->irq_out, GFP_ATOMIC) != 0) {
> +				spin_lock_irqsave(&xpad->pend_lock, flags);
> +				xpad->odata_busy = 0;
> +				spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +			}
> +		} else {
> +			xpad->odata_busy = 0;
> +			spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +		}
>  		return;
>  
>  	case -ECONNRESET:
> @@ -555,19 +589,27 @@ static void xpad_irq_out(struct urb *urb
>  		/* this urb is terminated, clean up */
>  		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
>  			__func__, status);
> +
> +		spin_lock_irqsave(&xpad->pend_lock, flags);
> +		xpad->odata_busy = 0;
> +		spin_unlock_irqrestore(&xpad->pend_lock, flags);
>  		return;
>  
>  	default:
>  		dev_dbg(dev, "%s - nonzero urb status received: %d\n",
>  			__func__, status);
> -		goto exit;
> -	}
>  
> -exit:
> -	retval = usb_submit_urb(urb, GFP_ATOMIC);
> -	if (retval)
> -		dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
> -			__func__, retval);
> +		retval = usb_submit_urb(urb, GFP_ATOMIC);
> +		if (retval) {
> +			dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
> +				__func__, retval);
> +			spin_lock_irqsave(&xpad->pend_lock, flags);
> +			xpad->odata_busy = 0;
> +			spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +		}
> +
> +		return;
> +	}
>  }
>  
>  static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
> @@ -585,7 +627,12 @@ static int xpad_init_output(struct usb_i
>  		goto fail1;
>  	}
>  
> -	mutex_init(&xpad->odata_mutex);
> +	xpad->odata_busy = 0;
> +
> +	spin_lock_init(&xpad->pend_lock);
> +
> +	xpad->pend_rum = 0;
> +	xpad->pend_led = 0;
>  
>  	xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL);
>  	if (!xpad->irq_out) {
> @@ -628,61 +675,91 @@ static void xpad_stop_output(struct usb_
>  #endif
>  
>  #ifdef CONFIG_JOYSTICK_XPAD_FF
> +static int xpad_make_rum_data(struct usb_xpad *xpad, __u16 strong, __u16 weak)
> +{
> +	switch (xpad->xtype) {
> +
> +	case XTYPE_XBOX:
> +		xpad->rum_data[0] = 0x00;
> +		xpad->rum_data[1] = 0x06;
> +		xpad->rum_data[2] = 0x00;
> +		xpad->rum_data[3] = strong / 256;	/* left actuator */
> +		xpad->rum_data[4] = 0x00;
> +		xpad->rum_data[5] = weak / 256;	/* right actuator */
> +		xpad->pend_rum = 6;
> +		return 0;
> +
> +
> +	case XTYPE_XBOX360:
> +		xpad->rum_data[0] = 0x00;
> +		xpad->rum_data[1] = 0x08;
> +		xpad->rum_data[2] = 0x00;
> +		xpad->rum_data[3] = strong / 256;  /* left actuator? */
> +		xpad->rum_data[4] = weak / 256;	/* right actuator? */
> +		xpad->rum_data[5] = 0x00;
> +		xpad->rum_data[6] = 0x00;
> +		xpad->rum_data[7] = 0x00;
> +		xpad->pend_rum = 8;
> +		return 0;
> +
> +	case XTYPE_XBOX360W:
> +		xpad->rum_data[0] = 0x00;
> +		xpad->rum_data[1] = 0x01;
> +		xpad->rum_data[2] = 0x0F;
> +		xpad->rum_data[3] = 0xC0;
> +		xpad->rum_data[4] = 0x00;
> +		xpad->rum_data[5] = strong / 256;
> +		xpad->rum_data[6] = weak / 256;
> +		xpad->rum_data[7] = 0x00;
> +		xpad->rum_data[8] = 0x00;
> +		xpad->rum_data[9] = 0x00;
> +		xpad->rum_data[10] = 0x00;
> +		xpad->rum_data[11] = 0x00;
> +		xpad->pend_rum = 12;
> +		return 0;
> +
> +	}
> +	return -1;
> +}
> +
>  static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
>  {
>  	struct usb_xpad *xpad = input_get_drvdata(dev);
>  
>  	if (effect->type == FF_RUMBLE) {
> -		__u16 strong = effect->u.rumble.strong_magnitude;
> -		__u16 weak = effect->u.rumble.weak_magnitude;
> -
> -		switch (xpad->xtype) {
> +		unsigned long flags;
> +		int mrdrv;
>  
> -		case XTYPE_XBOX:
> -			xpad->odata[0] = 0x00;
> -			xpad->odata[1] = 0x06;
> -			xpad->odata[2] = 0x00;
> -			xpad->odata[3] = strong / 256;	/* left actuator */
> -			xpad->odata[4] = 0x00;
> -			xpad->odata[5] = weak / 256;	/* right actuator */
> -			xpad->irq_out->transfer_buffer_length = 6;
> -
> -			return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
> -
> -		case XTYPE_XBOX360:
> -			xpad->odata[0] = 0x00;
> -			xpad->odata[1] = 0x08;
> -			xpad->odata[2] = 0x00;
> -			xpad->odata[3] = strong / 256;  /* left actuator? */
> -			xpad->odata[4] = weak / 256;	/* right actuator? */
> -			xpad->odata[5] = 0x00;
> -			xpad->odata[6] = 0x00;
> -			xpad->odata[7] = 0x00;
> -			xpad->irq_out->transfer_buffer_length = 8;
> -
> -			return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
> -
> -		case XTYPE_XBOX360W:
> -			xpad->odata[0] = 0x00;
> -			xpad->odata[1] = 0x01;
> -			xpad->odata[2] = 0x0F;
> -			xpad->odata[3] = 0xC0;
> -			xpad->odata[4] = 0x00;
> -			xpad->odata[5] = strong / 256;
> -			xpad->odata[6] = weak / 256;
> -			xpad->odata[7] = 0x00;
> -			xpad->odata[8] = 0x00;
> -			xpad->odata[9] = 0x00;
> -			xpad->odata[10] = 0x00;
> -			xpad->odata[11] = 0x00;
> -			xpad->irq_out->transfer_buffer_length = 12;
> -
> -			return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
> +		spin_lock_irqsave(&xpad->pend_lock, flags);
> +		mrdrv = xpad_make_rum_data(xpad, effect->u.rumble.strong_magnitude,
> +						effect->u.rumble.weak_magnitude);
> +
> +		if (mrdrv == 0 && !xpad->odata_busy) {
> +			memcpy(xpad->odata, xpad->rum_data, xpad->pend_rum);
> +			xpad->irq_out->transfer_buffer_length = xpad->pend_rum;
> +			xpad->pend_rum = 0;
> +			xpad->odata_busy = 1;
> +			spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +
> +			if (usb_submit_urb(xpad->irq_out, GFP_ATOMIC) != 0) {
> +				spin_lock_irqsave(&xpad->pend_lock, flags);
> +				xpad->odata_busy = 0;
> +				spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +			}
> +		} else {
> +			spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +
> +			if (mrdrv == 0) {
> +				dev_dbg(&xpad->dev->dev,
> +					"%s - rumble while urb busy\n", __func__);
> +			}
> +		}
>  
> -		default:
> +		if (mrdrv != 0) {
>  			dev_dbg(&xpad->dev->dev,
>  				"%s - rumble command sent to unsupported xpad type: %d\n",
>  				__func__, xpad->xtype);
> +
>  			return -1;
>  		}
>  	}
> @@ -716,13 +793,30 @@ struct xpad_led {
>  static void xpad_send_led_command(struct usb_xpad *xpad, int command)
>  {
>  	if (command >= 0 && command < 14) {
> -		mutex_lock(&xpad->odata_mutex);
> -		xpad->odata[0] = 0x01;
> -		xpad->odata[1] = 0x03;
> -		xpad->odata[2] = command;
> -		xpad->irq_out->transfer_buffer_length = 3;
> -		usb_submit_urb(xpad->irq_out, GFP_KERNEL);
> -		mutex_unlock(&xpad->odata_mutex);
> +		unsigned long flags;
> +
> +		spin_lock_irqsave(&xpad->pend_lock, flags);
> +		xpad->led_data[0] = 0x01;
> +		xpad->led_data[1] = 0x03;
> +		xpad->led_data[2] = command;
> +		xpad->pend_led = 3;
> +
> +		if (!xpad->odata_busy) {
> +			memcpy(xpad->odata, xpad->led_data, xpad->pend_led);
> +			xpad->irq_out->transfer_buffer_length = xpad->pend_led;
> +			xpad->pend_led = 0;
> +			xpad->odata_busy = 1;
> +			spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +
> +			if (usb_submit_urb(xpad->irq_out, GFP_KERNEL) != 0) {
> +				spin_lock_irqsave(&xpad->pend_lock, flags);
> +				xpad->odata_busy = 0;
> +				spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +			}
> +		} else {
> +			spin_unlock_irqrestore(&xpad->pend_lock, flags);
> +			dev_dbg(&xpad->dev->dev, "%s - led while urb busy\n", __func__);
> +		}
>  	}
>  }
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux