Skip to content

Commit

Permalink
USB: digi_acceleport: fix port-data memory leak
Browse files Browse the repository at this point in the history
Fix port-data memory leak by moving port data allocation and
deallocation to port_probe and port_remove.

Since commit 0998d06 (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: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Johan Hovold authored and Greg Kroah-Hartman committed Oct 25, 2012
1 parent 456c5be commit fb44ff8
Showing 1 changed file with 67 additions and 50 deletions.
117 changes: 67 additions & 50 deletions drivers/usb/serial/digi_acceleport.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 = {
Expand All @@ -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[] = {
Expand Down Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down

0 comments on commit fb44ff8

Please sign in to comment.