mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-25 05:34:00 +08:00
USB: keyspan: fix NULL-pointer dereferences and memory leaks
Fix NULL-pointer dereference at release by moving port data allocation
and deallocation to port_probe and port_remove.
Fix NULL-pointer dereference at disconnect by stopping port urbs at
port_remove.
Since commit 0998d06310
(device-core: Ensure drvdata = NULL when no
driver is bound) the port private data is no longer accessible at
disconnect or release.
Note that this patch also fixes port and interface-data memory leaks in
the error path of attach should port initialisation fail for any port.
Compile-only tested.
Cc: <stable@vger.kernel.org>
Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
5260e458f5
commit
f79b2d0fe8
@ -1374,13 +1374,9 @@ static struct callbacks {
|
|||||||
data in device_details */
|
data in device_details */
|
||||||
static void keyspan_setup_urbs(struct usb_serial *serial)
|
static void keyspan_setup_urbs(struct usb_serial *serial)
|
||||||
{
|
{
|
||||||
int i, j;
|
|
||||||
struct keyspan_serial_private *s_priv;
|
struct keyspan_serial_private *s_priv;
|
||||||
const struct keyspan_device_details *d_details;
|
const struct keyspan_device_details *d_details;
|
||||||
struct usb_serial_port *port;
|
|
||||||
struct keyspan_port_private *p_priv;
|
|
||||||
struct callbacks *cback;
|
struct callbacks *cback;
|
||||||
int endp;
|
|
||||||
|
|
||||||
s_priv = usb_get_serial_data(serial);
|
s_priv = usb_get_serial_data(serial);
|
||||||
d_details = s_priv->device_details;
|
d_details = s_priv->device_details;
|
||||||
@ -1404,45 +1400,6 @@ static void keyspan_setup_urbs(struct usb_serial *serial)
|
|||||||
(serial, d_details->glocont_endpoint, USB_DIR_OUT,
|
(serial, d_details->glocont_endpoint, USB_DIR_OUT,
|
||||||
serial, s_priv->glocont_buf, GLOCONT_BUFLEN,
|
serial, s_priv->glocont_buf, GLOCONT_BUFLEN,
|
||||||
cback->glocont_callback);
|
cback->glocont_callback);
|
||||||
|
|
||||||
/* Setup endpoints for each port specific thing */
|
|
||||||
for (i = 0; i < d_details->num_ports; i++) {
|
|
||||||
port = serial->port[i];
|
|
||||||
p_priv = usb_get_serial_port_data(port);
|
|
||||||
|
|
||||||
/* Do indat endpoints first, once for each flip */
|
|
||||||
endp = d_details->indat_endpoints[i];
|
|
||||||
for (j = 0; j <= d_details->indat_endp_flip; ++j, ++endp) {
|
|
||||||
p_priv->in_urbs[j] = keyspan_setup_urb
|
|
||||||
(serial, endp, USB_DIR_IN, port,
|
|
||||||
p_priv->in_buffer[j], 64,
|
|
||||||
cback->indat_callback);
|
|
||||||
}
|
|
||||||
for (; j < 2; ++j)
|
|
||||||
p_priv->in_urbs[j] = NULL;
|
|
||||||
|
|
||||||
/* outdat endpoints also have flip */
|
|
||||||
endp = d_details->outdat_endpoints[i];
|
|
||||||
for (j = 0; j <= d_details->outdat_endp_flip; ++j, ++endp) {
|
|
||||||
p_priv->out_urbs[j] = keyspan_setup_urb
|
|
||||||
(serial, endp, USB_DIR_OUT, port,
|
|
||||||
p_priv->out_buffer[j], 64,
|
|
||||||
cback->outdat_callback);
|
|
||||||
}
|
|
||||||
for (; j < 2; ++j)
|
|
||||||
p_priv->out_urbs[j] = NULL;
|
|
||||||
|
|
||||||
/* inack endpoint */
|
|
||||||
p_priv->inack_urb = keyspan_setup_urb
|
|
||||||
(serial, d_details->inack_endpoints[i], USB_DIR_IN,
|
|
||||||
port, p_priv->inack_buffer, 1, cback->inack_callback);
|
|
||||||
|
|
||||||
/* outcont endpoint */
|
|
||||||
p_priv->outcont_urb = keyspan_setup_urb
|
|
||||||
(serial, d_details->outcont_endpoints[i], USB_DIR_OUT,
|
|
||||||
port, p_priv->outcont_buffer, 64,
|
|
||||||
cback->outcont_callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* usa19 function doesn't require prescaler */
|
/* usa19 function doesn't require prescaler */
|
||||||
@ -2407,9 +2364,7 @@ static void keyspan_send_setup(struct usb_serial_port *port, int reset_port)
|
|||||||
static int keyspan_startup(struct usb_serial *serial)
|
static int keyspan_startup(struct usb_serial *serial)
|
||||||
{
|
{
|
||||||
int i, err;
|
int i, err;
|
||||||
struct usb_serial_port *port;
|
|
||||||
struct keyspan_serial_private *s_priv;
|
struct keyspan_serial_private *s_priv;
|
||||||
struct keyspan_port_private *p_priv;
|
|
||||||
const struct keyspan_device_details *d_details;
|
const struct keyspan_device_details *d_details;
|
||||||
|
|
||||||
for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i)
|
for (i = 0; (d_details = keyspan_devices[i]) != NULL; ++i)
|
||||||
@ -2432,19 +2387,6 @@ static int keyspan_startup(struct usb_serial *serial)
|
|||||||
s_priv->device_details = d_details;
|
s_priv->device_details = d_details;
|
||||||
usb_set_serial_data(serial, s_priv);
|
usb_set_serial_data(serial, s_priv);
|
||||||
|
|
||||||
/* Now setup per port private data */
|
|
||||||
for (i = 0; i < serial->num_ports; i++) {
|
|
||||||
port = serial->port[i];
|
|
||||||
p_priv = kzalloc(sizeof(struct keyspan_port_private),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!p_priv) {
|
|
||||||
dev_dbg(&port->dev, "%s - kmalloc for keyspan_port_private (%d) failed!.\n", __func__, i);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
p_priv->device_details = d_details;
|
|
||||||
usb_set_serial_port_data(port, p_priv);
|
|
||||||
}
|
|
||||||
|
|
||||||
keyspan_setup_urbs(serial);
|
keyspan_setup_urbs(serial);
|
||||||
|
|
||||||
if (s_priv->instat_urb != NULL) {
|
if (s_priv->instat_urb != NULL) {
|
||||||
@ -2463,59 +2405,112 @@ static int keyspan_startup(struct usb_serial *serial)
|
|||||||
|
|
||||||
static void keyspan_disconnect(struct usb_serial *serial)
|
static void keyspan_disconnect(struct usb_serial *serial)
|
||||||
{
|
{
|
||||||
int i, j;
|
struct keyspan_serial_private *s_priv;
|
||||||
struct usb_serial_port *port;
|
|
||||||
struct keyspan_serial_private *s_priv;
|
|
||||||
struct keyspan_port_private *p_priv;
|
|
||||||
|
|
||||||
s_priv = usb_get_serial_data(serial);
|
s_priv = usb_get_serial_data(serial);
|
||||||
|
|
||||||
/* Stop reading/writing urbs */
|
|
||||||
stop_urb(s_priv->instat_urb);
|
stop_urb(s_priv->instat_urb);
|
||||||
stop_urb(s_priv->glocont_urb);
|
stop_urb(s_priv->glocont_urb);
|
||||||
stop_urb(s_priv->indat_urb);
|
stop_urb(s_priv->indat_urb);
|
||||||
for (i = 0; i < serial->num_ports; ++i) {
|
|
||||||
port = serial->port[i];
|
|
||||||
p_priv = usb_get_serial_port_data(port);
|
|
||||||
stop_urb(p_priv->inack_urb);
|
|
||||||
stop_urb(p_priv->outcont_urb);
|
|
||||||
for (j = 0; j < 2; j++) {
|
|
||||||
stop_urb(p_priv->in_urbs[j]);
|
|
||||||
stop_urb(p_priv->out_urbs[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now free them */
|
|
||||||
usb_free_urb(s_priv->instat_urb);
|
|
||||||
usb_free_urb(s_priv->indat_urb);
|
|
||||||
usb_free_urb(s_priv->glocont_urb);
|
|
||||||
for (i = 0; i < serial->num_ports; ++i) {
|
|
||||||
port = serial->port[i];
|
|
||||||
p_priv = usb_get_serial_port_data(port);
|
|
||||||
usb_free_urb(p_priv->inack_urb);
|
|
||||||
usb_free_urb(p_priv->outcont_urb);
|
|
||||||
for (j = 0; j < 2; j++) {
|
|
||||||
usb_free_urb(p_priv->in_urbs[j]);
|
|
||||||
usb_free_urb(p_priv->out_urbs[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyspan_release(struct usb_serial *serial)
|
static void keyspan_release(struct usb_serial *serial)
|
||||||
{
|
{
|
||||||
int i;
|
struct keyspan_serial_private *s_priv;
|
||||||
struct usb_serial_port *port;
|
|
||||||
struct keyspan_serial_private *s_priv;
|
|
||||||
|
|
||||||
s_priv = usb_get_serial_data(serial);
|
s_priv = usb_get_serial_data(serial);
|
||||||
|
|
||||||
kfree(s_priv);
|
usb_free_urb(s_priv->instat_urb);
|
||||||
|
usb_free_urb(s_priv->indat_urb);
|
||||||
|
usb_free_urb(s_priv->glocont_urb);
|
||||||
|
|
||||||
/* Now free per port private data */
|
kfree(s_priv);
|
||||||
for (i = 0; i < serial->num_ports; i++) {
|
}
|
||||||
port = serial->port[i];
|
|
||||||
kfree(usb_get_serial_port_data(port));
|
static int keyspan_port_probe(struct usb_serial_port *port)
|
||||||
|
{
|
||||||
|
struct usb_serial *serial = port->serial;
|
||||||
|
struct keyspan_port_private *s_priv;
|
||||||
|
struct keyspan_port_private *p_priv;
|
||||||
|
const struct keyspan_device_details *d_details;
|
||||||
|
struct callbacks *cback;
|
||||||
|
int endp;
|
||||||
|
int port_num;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
s_priv = usb_get_serial_data(serial);
|
||||||
|
d_details = s_priv->device_details;
|
||||||
|
|
||||||
|
p_priv = kzalloc(sizeof(*p_priv), GFP_KERNEL);
|
||||||
|
if (!p_priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
s_priv = usb_get_serial_data(port->serial);
|
||||||
|
p_priv->device_details = d_details;
|
||||||
|
|
||||||
|
/* Setup values for the various callback routines */
|
||||||
|
cback = &keyspan_callbacks[d_details->msg_format];
|
||||||
|
|
||||||
|
port_num = port->number - port->serial->minor;
|
||||||
|
|
||||||
|
/* Do indat endpoints first, once for each flip */
|
||||||
|
endp = d_details->indat_endpoints[port_num];
|
||||||
|
for (i = 0; i <= d_details->indat_endp_flip; ++i, ++endp) {
|
||||||
|
p_priv->in_urbs[i] = keyspan_setup_urb(serial, endp,
|
||||||
|
USB_DIR_IN, port,
|
||||||
|
p_priv->in_buffer[i], 64,
|
||||||
|
cback->indat_callback);
|
||||||
}
|
}
|
||||||
|
/* outdat endpoints also have flip */
|
||||||
|
endp = d_details->outdat_endpoints[port_num];
|
||||||
|
for (i = 0; i <= d_details->outdat_endp_flip; ++i, ++endp) {
|
||||||
|
p_priv->out_urbs[i] = keyspan_setup_urb(serial, endp,
|
||||||
|
USB_DIR_OUT, port,
|
||||||
|
p_priv->out_buffer[i], 64,
|
||||||
|
cback->outdat_callback);
|
||||||
|
}
|
||||||
|
/* inack endpoint */
|
||||||
|
p_priv->inack_urb = keyspan_setup_urb(serial,
|
||||||
|
d_details->inack_endpoints[port_num],
|
||||||
|
USB_DIR_IN, port,
|
||||||
|
p_priv->inack_buffer, 1,
|
||||||
|
cback->inack_callback);
|
||||||
|
/* outcont endpoint */
|
||||||
|
p_priv->outcont_urb = keyspan_setup_urb(serial,
|
||||||
|
d_details->outcont_endpoints[port_num],
|
||||||
|
USB_DIR_OUT, port,
|
||||||
|
p_priv->outcont_buffer, 64,
|
||||||
|
cback->outcont_callback);
|
||||||
|
|
||||||
|
usb_set_serial_port_data(port, p_priv);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int keyspan_port_remove(struct usb_serial_port *port)
|
||||||
|
{
|
||||||
|
struct keyspan_port_private *p_priv;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
p_priv = usb_get_serial_port_data(port);
|
||||||
|
|
||||||
|
stop_urb(p_priv->inack_urb);
|
||||||
|
stop_urb(p_priv->outcont_urb);
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
stop_urb(p_priv->in_urbs[i]);
|
||||||
|
stop_urb(p_priv->out_urbs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_free_urb(p_priv->inack_urb);
|
||||||
|
usb_free_urb(p_priv->outcont_urb);
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
usb_free_urb(p_priv->in_urbs[i]);
|
||||||
|
usb_free_urb(p_priv->out_urbs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(p_priv);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||||
|
@ -42,6 +42,8 @@ static void keyspan_dtr_rts (struct usb_serial_port *port, int on);
|
|||||||
static int keyspan_startup (struct usb_serial *serial);
|
static int keyspan_startup (struct usb_serial *serial);
|
||||||
static void keyspan_disconnect (struct usb_serial *serial);
|
static void keyspan_disconnect (struct usb_serial *serial);
|
||||||
static void keyspan_release (struct usb_serial *serial);
|
static void keyspan_release (struct usb_serial *serial);
|
||||||
|
static int keyspan_port_probe(struct usb_serial_port *port);
|
||||||
|
static int keyspan_port_remove(struct usb_serial_port *port);
|
||||||
static int keyspan_write_room (struct tty_struct *tty);
|
static int keyspan_write_room (struct tty_struct *tty);
|
||||||
|
|
||||||
static int keyspan_write (struct tty_struct *tty,
|
static int keyspan_write (struct tty_struct *tty,
|
||||||
@ -567,6 +569,8 @@ static struct usb_serial_driver keyspan_1port_device = {
|
|||||||
.attach = keyspan_startup,
|
.attach = keyspan_startup,
|
||||||
.disconnect = keyspan_disconnect,
|
.disconnect = keyspan_disconnect,
|
||||||
.release = keyspan_release,
|
.release = keyspan_release,
|
||||||
|
.port_probe = keyspan_port_probe,
|
||||||
|
.port_remove = keyspan_port_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_serial_driver keyspan_2port_device = {
|
static struct usb_serial_driver keyspan_2port_device = {
|
||||||
@ -589,6 +593,8 @@ static struct usb_serial_driver keyspan_2port_device = {
|
|||||||
.attach = keyspan_startup,
|
.attach = keyspan_startup,
|
||||||
.disconnect = keyspan_disconnect,
|
.disconnect = keyspan_disconnect,
|
||||||
.release = keyspan_release,
|
.release = keyspan_release,
|
||||||
|
.port_probe = keyspan_port_probe,
|
||||||
|
.port_remove = keyspan_port_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_serial_driver keyspan_4port_device = {
|
static struct usb_serial_driver keyspan_4port_device = {
|
||||||
@ -611,6 +617,8 @@ static struct usb_serial_driver keyspan_4port_device = {
|
|||||||
.attach = keyspan_startup,
|
.attach = keyspan_startup,
|
||||||
.disconnect = keyspan_disconnect,
|
.disconnect = keyspan_disconnect,
|
||||||
.release = keyspan_release,
|
.release = keyspan_release,
|
||||||
|
.port_probe = keyspan_port_probe,
|
||||||
|
.port_remove = keyspan_port_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct usb_serial_driver * const serial_drivers[] = {
|
static struct usb_serial_driver * const serial_drivers[] = {
|
||||||
|
Loading…
Reference in New Issue
Block a user