Commit fb44ff85 authored by Johan Hovold's avatar Johan Hovold Committed by Greg Kroah-Hartman

USB: digi_acceleport: fix port-data memory leak

Fix port-data memory leak by moving port data allocation and
deallocation to port_probe and port_remove.

Since commit 0998d063 (device-core: Ensure drvdata = NULL when no
driver is bound) the port private data is no longer freed at release as
it is no longer accessible.

Note that the oob port is never registered as a port device and should
thus be handled in attach and release.

Compile-only tested.

Cc: Peter Berger <pberger@brimson.com>
Cc: Al Borchers <alborchers@steinerpoint.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarJohan Hovold <jhovold@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 456c5be5
......@@ -244,6 +244,8 @@ static int digi_startup_device(struct usb_serial *serial);
static int digi_startup(struct usb_serial *serial);
static void digi_disconnect(struct usb_serial *serial);
static void digi_release(struct usb_serial *serial);
static int digi_port_probe(struct usb_serial_port *port);
static int digi_port_remove(struct usb_serial_port *port);
static void digi_read_bulk_callback(struct urb *urb);
static int digi_read_inb_callback(struct urb *urb);
static int digi_read_oob_callback(struct urb *urb);
......@@ -294,6 +296,8 @@ static struct usb_serial_driver digi_acceleport_2_device = {
.attach = digi_startup,
.disconnect = digi_disconnect,
.release = digi_release,
.port_probe = digi_port_probe,
.port_remove = digi_port_remove,
};
static struct usb_serial_driver digi_acceleport_4_device = {
......@@ -320,6 +324,8 @@ static struct usb_serial_driver digi_acceleport_4_device = {
.attach = digi_startup,
.disconnect = digi_disconnect,
.release = digi_release,
.port_probe = digi_port_probe,
.port_remove = digi_port_remove,
};
static struct usb_serial_driver * const serial_drivers[] = {
......@@ -1240,59 +1246,50 @@ static int digi_startup_device(struct usb_serial *serial)
return ret;
}
static int digi_startup(struct usb_serial *serial)
static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
{
int i;
struct digi_port *priv;
struct digi_serial *serial_priv;
/* allocate the private data structures for all ports */
/* number of regular ports + 1 for the out-of-band port */
for (i = 0; i < serial->type->num_ports + 1; i++) {
/* allocate port private structure */
priv = kmalloc(sizeof(struct digi_port), GFP_KERNEL);
if (priv == NULL) {
while (--i >= 0)
kfree(usb_get_serial_port_data(serial->port[i]));
return 1; /* error */
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* initialize port private structure */
spin_lock_init(&priv->dp_port_lock);
priv->dp_port_num = i;
priv->dp_out_buf_len = 0;
priv->dp_write_urb_in_use = 0;
priv->dp_modem_signals = 0;
init_waitqueue_head(&priv->dp_modem_change_wait);
priv->dp_transmit_idle = 0;
init_waitqueue_head(&priv->dp_transmit_idle_wait);
priv->dp_throttled = 0;
priv->dp_throttle_restart = 0;
init_waitqueue_head(&priv->dp_flush_wait);
init_waitqueue_head(&priv->dp_close_wait);
INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
priv->dp_port = serial->port[i];
/* initialize write wait queue for this port */
init_waitqueue_head(&serial->port[i]->write_wait);
usb_set_serial_port_data(serial->port[i], priv);
}
spin_lock_init(&priv->dp_port_lock);
priv->dp_port_num = port_num;
init_waitqueue_head(&priv->dp_modem_change_wait);
init_waitqueue_head(&priv->dp_transmit_idle_wait);
init_waitqueue_head(&priv->dp_flush_wait);
init_waitqueue_head(&priv->dp_close_wait);
INIT_WORK(&priv->dp_wakeup_work, digi_wakeup_write_lock);
priv->dp_port = port;
/* allocate serial private structure */
serial_priv = kmalloc(sizeof(struct digi_serial), GFP_KERNEL);
if (serial_priv == NULL) {
for (i = 0; i < serial->type->num_ports + 1; i++)
kfree(usb_get_serial_port_data(serial->port[i]));
return 1; /* error */
}
init_waitqueue_head(&port->write_wait);
usb_set_serial_port_data(port, priv);
return 0;
}
static int digi_startup(struct usb_serial *serial)
{
struct digi_serial *serial_priv;
int ret;
serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
if (!serial_priv)
return -ENOMEM;
/* initialize serial private structure */
spin_lock_init(&serial_priv->ds_serial_lock);
serial_priv->ds_oob_port_num = serial->type->num_ports;
serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num];
serial_priv->ds_device_started = 0;
ret = digi_port_init(serial_priv->ds_oob_port,
serial_priv->ds_oob_port_num);
if (ret) {
kfree(serial_priv);
return ret;
}
usb_set_serial_data(serial, serial_priv);
return 0;
......@@ -1313,15 +1310,35 @@ static void digi_disconnect(struct usb_serial *serial)
static void digi_release(struct usb_serial *serial)
{
int i;
struct digi_serial *serial_priv;
struct digi_port *priv;
serial_priv = usb_get_serial_data(serial);
priv = usb_get_serial_port_data(serial_priv->ds_oob_port);
kfree(priv);
/* free the private data structures for all ports */
/* number of regular ports + 1 for the out-of-band port */
for (i = 0; i < serial->type->num_ports + 1; i++)
kfree(usb_get_serial_port_data(serial->port[i]));
kfree(usb_get_serial_data(serial));
kfree(serial_priv);
}
static int digi_port_probe(struct usb_serial_port *port)
{
unsigned port_num;
port_num = port->number - port->serial->minor;
return digi_port_init(port, port_num);
}
static int digi_port_remove(struct usb_serial_port *port)
{
struct digi_port *priv;
priv = usb_get_serial_port_data(port);
kfree(priv);
return 0;
}
static void digi_read_bulk_callback(struct urb *urb)
{
......
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