Commit c45d6320 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

usb-serial: ftdi_sio: fix reference counting of ftdi_private

This patch (as1238) adds proper reference counting for ftdi_sio's
private data structure.  Without it, the driver will free the
structure while it is still in use if the user unplugs the serial
device before closing the device file.

The patch also replaces a slightly dangerous
cancel_delayed_work/flush_scheduled_work pair with
cancel_delayed_work_sync, which is always safer.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Reported-by: default avatarDaniel Mack <daniel@caiaq.de>
Tested-by: default avatarDaniel Mack <daniel@caiaq.de>
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 031defd1
...@@ -56,6 +56,7 @@ static __u16 vendor = FTDI_VID; ...@@ -56,6 +56,7 @@ static __u16 vendor = FTDI_VID;
static __u16 product; static __u16 product;
struct ftdi_private { struct ftdi_private {
struct kref kref;
ftdi_chip_type_t chip_type; ftdi_chip_type_t chip_type;
/* type of device, either SIO or FT8U232AM */ /* type of device, either SIO or FT8U232AM */
int baud_base; /* baud base clock for divisor setting */ int baud_base; /* baud base clock for divisor setting */
...@@ -1354,6 +1355,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) ...@@ -1354,6 +1355,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
return -ENOMEM; return -ENOMEM;
} }
kref_init(&priv->kref);
spin_lock_init(&priv->rx_lock); spin_lock_init(&priv->rx_lock);
spin_lock_init(&priv->tx_lock); spin_lock_init(&priv->tx_lock);
init_waitqueue_head(&priv->delta_msr_wait); init_waitqueue_head(&priv->delta_msr_wait);
...@@ -1470,6 +1472,13 @@ static void ftdi_shutdown(struct usb_serial *serial) ...@@ -1470,6 +1472,13 @@ static void ftdi_shutdown(struct usb_serial *serial)
dbg("%s", __func__); dbg("%s", __func__);
} }
static void ftdi_sio_priv_release(struct kref *k)
{
struct ftdi_private *priv = container_of(k, struct ftdi_private, kref);
kfree(priv);
}
static int ftdi_sio_port_remove(struct usb_serial_port *port) static int ftdi_sio_port_remove(struct usb_serial_port *port)
{ {
struct ftdi_private *priv = usb_get_serial_port_data(port); struct ftdi_private *priv = usb_get_serial_port_data(port);
...@@ -1484,7 +1493,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) ...@@ -1484,7 +1493,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
if (priv) { if (priv) {
usb_set_serial_port_data(port, NULL); usb_set_serial_port_data(port, NULL);
kfree(priv); kref_put(&priv->kref, ftdi_sio_priv_release);
} }
return 0; return 0;
...@@ -1549,7 +1558,8 @@ static int ftdi_open(struct tty_struct *tty, ...@@ -1549,7 +1558,8 @@ static int ftdi_open(struct tty_struct *tty,
dev_err(&port->dev, dev_err(&port->dev,
"%s - failed submitting read urb, error %d\n", "%s - failed submitting read urb, error %d\n",
__func__, result); __func__, result);
else
kref_get(&priv->kref);
return result; return result;
} /* ftdi_open */ } /* ftdi_open */
...@@ -1591,11 +1601,11 @@ static void ftdi_close(struct tty_struct *tty, ...@@ -1591,11 +1601,11 @@ static void ftdi_close(struct tty_struct *tty,
mutex_unlock(&port->serial->disc_mutex); mutex_unlock(&port->serial->disc_mutex);
/* cancel any scheduled reading */ /* cancel any scheduled reading */
cancel_delayed_work(&priv->rx_work); cancel_delayed_work_sync(&priv->rx_work);
flush_scheduled_work();
/* shutdown our bulk read */ /* shutdown our bulk read */
usb_kill_urb(port->read_urb); usb_kill_urb(port->read_urb);
kref_put(&priv->kref, ftdi_sio_priv_release);
} /* ftdi_close */ } /* ftdi_close */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment