Re: [Fwd: Re: [Fwd: Re: [Bug 20703] HAL sometimes doesn't emit udi-removed signals]]

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

 



On Mon, 2009-04-06 at 17:08 -0400, Alan Stern wrote:
> On Tue, 24 Mar 2009, Dan Williams wrote:
> 
> > > Dan, you can try this one out in place of the earlier patch.  Hopefully 
> > > it will resolve the use-after-free BUG.
> > 
> > This patch (slightly modified to apply to 2.6.29-rc8 sources; the
> > bailout_serial_put stuff didn't apply exactly) appears to work correctly
> > over a few plug/unplug sessions while PPP is up and running on the tty.
> > Modified patch below.
> 
> Dan:
> 
> I've got a revised version of the patch that needs testing.  Will you
> be so kind as to oblige?  This is against 2.6.29.

Yes, this appears to work correctly.

I do get BUG (sleeping in invalid context) [1] right after insertion.
Doesn't seem related to me, but who knows?

Thanks!
Dan

[1] http://www.kerneloops.org/submitresult.php?number=341383

> Thanks,
> 
> Alan Stern
> 
> 
> Index: 2.6.29/drivers/usb/serial/usb-serial.c
> ===================================================================
> --- 2.6.29.orig/drivers/usb/serial/usb-serial.c
> +++ 2.6.29/drivers/usb/serial/usb-serial.c
> @@ -142,16 +142,6 @@ static void destroy_serial(struct kref *
>  	if (serial->minor != SERIAL_TTY_NO_MINOR)
>  		return_serial(serial);
>  
> -	for (i = 0; i < serial->num_ports; ++i)
> -		serial->port[i]->port.count = 0;
> -
> -	/* the ports are cleaned up and released in port_release() */
> -	for (i = 0; i < serial->num_ports; ++i)
> -		if (serial->port[i]->dev.parent != NULL) {
> -			device_unregister(&serial->port[i]->dev);
> -			serial->port[i] = NULL;
> -		}
> -
>  	/* If this is a "fake" port, we have to clean it up here, as it will
>  	 * not get cleaned up in port_release() as it was never registered with
>  	 * the driver core */
> @@ -186,7 +176,7 @@ static int serial_open (struct tty_struc
>  	struct usb_serial *serial;
>  	struct usb_serial_port *port;
>  	unsigned int portNumber;
> -	int retval;
> +	int retval = 0;
>  
>  	dbg("%s", __func__);
>  
> @@ -197,16 +187,24 @@ static int serial_open (struct tty_struc
>  		return -ENODEV;
>  	}
>  
> +	mutex_lock(&serial->disc_mutex);
>  	portNumber = tty->index - serial->minor;
>  	port = serial->port[portNumber];
> -	if (!port) {
> +	if (!port || serial->disconnected)
>  		retval = -ENODEV;
> -		goto bailout_kref_put;
> -	}
> +	else
> +		get_device(&port->dev);
> +	/*
> +	 * Note: Our locking order requirement does not allow port->mutex
> +	 * to be acquired while serial->disc_mutex is held.
> +	 */
> +	mutex_unlock(&serial->disc_mutex);
> +	if (retval)
> +		goto bailout_serial_put;
>  
>  	if (mutex_lock_interruptible(&port->mutex)) {
>  		retval = -ERESTARTSYS;
> -		goto bailout_kref_put;
> +		goto bailout_port_put;
>  	}
>  
>  	++port->port.count;
> @@ -226,9 +224,15 @@ static int serial_open (struct tty_struc
>  			goto bailout_mutex_unlock;
>  		}
>  
> -		retval = usb_autopm_get_interface(serial->interface);
> +		mutex_lock(&serial->disc_mutex);
> +		if (serial->disconnected)
> +			retval = -ENODEV;
> +		else
> +			retval = usb_autopm_get_interface(serial->interface);
> +		mutex_unlock(&serial->disc_mutex);
>  		if (retval)
>  			goto bailout_module_put;
> +
>  		/* only call the device specific open if this
>  		 * is the first time the port is opened */
>  		retval = serial->type->open(tty, port, filp);
> @@ -248,7 +252,9 @@ bailout_mutex_unlock:
>  	tty->driver_data = NULL;
>  	tty_port_tty_set(&port->port, NULL);
>  	mutex_unlock(&port->mutex);
> -bailout_kref_put:
> +bailout_port_put:
> +	put_device(&port->dev);
> +bailout_serial_put:
>  	usb_serial_put(serial);
>  	return retval;
>  }
> @@ -256,6 +262,9 @@ bailout_kref_put:
>  static void serial_close(struct tty_struct *tty, struct file *filp)
>  {
>  	struct usb_serial_port *port = tty->driver_data;
> +	struct usb_serial *serial;
> +	struct module *owner;
> +	int count;
>  
>  	if (!port)
>  		return;
> @@ -263,6 +272,8 @@ static void serial_close(struct tty_stru
>  	dbg("%s - port %d", __func__, port->number);
>  
>  	mutex_lock(&port->mutex);
> +	serial = port->serial;
> +	owner = serial->type->driver.owner;
>  
>  	if (port->port.count == 0) {
>  		mutex_unlock(&port->mutex);
> @@ -275,7 +286,7 @@ static void serial_close(struct tty_stru
>  		 * this before we drop the port count. The call is protected
>  		 * by the port mutex
>  		 */
> -		port->serial->type->close(tty, port, filp);
> +		serial->type->close(tty, port, filp);
>  
>  	if (port->port.count == (port->console ? 2 : 1)) {
>  		struct tty_struct *tty = tty_port_tty_get(&port->port);
> @@ -289,17 +300,23 @@ static void serial_close(struct tty_stru
>  		}
>  	}
>  
> -	if (port->port.count == 1) {
> -		mutex_lock(&port->serial->disc_mutex);
> -		if (!port->serial->disconnected)
> -			usb_autopm_put_interface(port->serial->interface);
> -		mutex_unlock(&port->serial->disc_mutex);
> -		module_put(port->serial->type->driver.owner);
> -	}
>  	--port->port.count;
> -
> +	count = port->port.count;
>  	mutex_unlock(&port->mutex);
> -	usb_serial_put(port->serial);
> +	put_device(&port->dev);
> +
> +	/* Mustn't dereference port any more */
> +	if (count == 0) {
> +		mutex_lock(&serial->disc_mutex);
> +		if (!serial->disconnected)
> +			usb_autopm_put_interface(serial->interface);
> +		mutex_unlock(&serial->disc_mutex);
> +	}
> +	usb_serial_put(serial);
> +
> +	/* Mustn't dereference serial any more */
> +	if (count == 0)
> +		module_put(owner);
>  }
>  
>  static int serial_write(struct tty_struct *tty, const unsigned char *buf,
> @@ -548,7 +565,13 @@ static void kill_traffic(struct usb_seri
>  
>  static void port_free(struct usb_serial_port *port)
>  {
> +	/*
> +	 * Stop all the traffic before cancelling the work, so that
> +	 * nobody will restart it by calling usb_serial_port_softint.
> +	 */
>  	kill_traffic(port);
> +	cancel_work_sync(&port->work);
> +
>  	usb_free_urb(port->read_urb);
>  	usb_free_urb(port->write_urb);
>  	usb_free_urb(port->interrupt_in_urb);
> @@ -557,7 +580,6 @@ static void port_free(struct usb_serial_
>  	kfree(port->bulk_out_buffer);
>  	kfree(port->interrupt_in_buffer);
>  	kfree(port->interrupt_out_buffer);
> -	flush_scheduled_work();		/* port->work */
>  	kfree(port);
>  }
>  
> @@ -1042,6 +1064,8 @@ void usb_serial_disconnect(struct usb_in
>  	usb_set_intfdata(interface, NULL);
>  	/* must set a flag, to signal subdrivers */
>  	serial->disconnected = 1;
> +	mutex_unlock(&serial->disc_mutex);
> +
>  	for (i = 0; i < serial->num_ports; ++i) {
>  		port = serial->port[i];
>  		if (port) {
> @@ -1051,11 +1075,13 @@ void usb_serial_disconnect(struct usb_in
>  				tty_kref_put(tty);
>  			}
>  			kill_traffic(port);
> +			cancel_work_sync(&port->work);
> +			device_unregister(&port->dev);
> +			serial->port[i] = NULL;
>  		}
>  	}
>  	/* let the last holder of this object
>  	 * cause it to be cleaned up */
> -	mutex_unlock(&serial->disc_mutex);
>  	usb_serial_put(serial);
>  	dev_info(dev, "device disconnected\n");
>  }
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux