mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-03 11:13:56 +08:00
USB: serial: fix null-pointer dereferences on disconnect
Make sure serial-driver dtr_rts is called with disc_mutex held after
checking the disconnected flag.
Due to a bug in the tty layer, dtr_rts may get called after a device has
been disconnected and the tty-device unregistered. Some drivers have had
individual checks for disconnect to make sure the disconnected interface
was not accessed, but this should really be handled in usb-serial core
(at least until the long-standing tty-bug has been fixed).
Note that the problem has been made more acute with commit 0998d06310
("device-core: Ensure drvdata = NULL when no driver is bound") as the
port data is now also NULL when dtr_rts is called resulting in further
oopses.
Reported-by: Chris Ruehl <chris.ruehl@gtsys.com.hk>
Cc: stable <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
cd565279e5
commit
b2ca699076
@ -1886,24 +1886,22 @@ static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
|
|||||||
{
|
{
|
||||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||||
|
|
||||||
mutex_lock(&port->serial->disc_mutex);
|
/* Disable flow control */
|
||||||
if (!port->serial->disconnected) {
|
if (!on) {
|
||||||
/* Disable flow control */
|
if (usb_control_msg(port->serial->dev,
|
||||||
if (!on && usb_control_msg(port->serial->dev,
|
|
||||||
usb_sndctrlpipe(port->serial->dev, 0),
|
usb_sndctrlpipe(port->serial->dev, 0),
|
||||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
||||||
0, priv->interface, NULL, 0,
|
0, priv->interface, NULL, 0,
|
||||||
WDR_TIMEOUT) < 0) {
|
WDR_TIMEOUT) < 0) {
|
||||||
dev_err(&port->dev, "error from flowcontrol urb\n");
|
dev_err(&port->dev, "error from flowcontrol urb\n");
|
||||||
}
|
}
|
||||||
/* drop RTS and DTR */
|
|
||||||
if (on)
|
|
||||||
set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
|
||||||
else
|
|
||||||
clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
|
||||||
}
|
}
|
||||||
mutex_unlock(&port->serial->disc_mutex);
|
/* drop RTS and DTR */
|
||||||
|
if (on)
|
||||||
|
set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
||||||
|
else
|
||||||
|
clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -499,19 +499,15 @@ static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
|
|||||||
unsigned int control_state;
|
unsigned int control_state;
|
||||||
struct mct_u232_private *priv = usb_get_serial_port_data(port);
|
struct mct_u232_private *priv = usb_get_serial_port_data(port);
|
||||||
|
|
||||||
mutex_lock(&port->serial->disc_mutex);
|
spin_lock_irq(&priv->lock);
|
||||||
if (!port->serial->disconnected) {
|
if (on)
|
||||||
/* drop DTR and RTS */
|
priv->control_state |= TIOCM_DTR | TIOCM_RTS;
|
||||||
spin_lock_irq(&priv->lock);
|
else
|
||||||
if (on)
|
priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
|
||||||
priv->control_state |= TIOCM_DTR | TIOCM_RTS;
|
control_state = priv->control_state;
|
||||||
else
|
spin_unlock_irq(&priv->lock);
|
||||||
priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
|
|
||||||
control_state = priv->control_state;
|
mct_u232_set_modem_ctrl(port, control_state);
|
||||||
spin_unlock_irq(&priv->lock);
|
|
||||||
mct_u232_set_modem_ctrl(port, control_state);
|
|
||||||
}
|
|
||||||
mutex_unlock(&port->serial->disc_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mct_u232_close(struct usb_serial_port *port)
|
static void mct_u232_close(struct usb_serial_port *port)
|
||||||
|
@ -945,19 +945,17 @@ static void qt2_dtr_rts(struct usb_serial_port *port, int on)
|
|||||||
struct usb_device *dev = port->serial->dev;
|
struct usb_device *dev = port->serial->dev;
|
||||||
struct qt2_port_private *port_priv = usb_get_serial_port_data(port);
|
struct qt2_port_private *port_priv = usb_get_serial_port_data(port);
|
||||||
|
|
||||||
mutex_lock(&port->serial->disc_mutex);
|
/* Disable flow control */
|
||||||
if (!port->serial->disconnected) {
|
if (!on) {
|
||||||
/* Disable flow control */
|
if (qt2_setregister(dev, port_priv->device_port,
|
||||||
if (!on && qt2_setregister(dev, port_priv->device_port,
|
|
||||||
UART_MCR, 0) < 0)
|
UART_MCR, 0) < 0)
|
||||||
dev_warn(&port->dev, "error from flowcontrol urb\n");
|
dev_warn(&port->dev, "error from flowcontrol urb\n");
|
||||||
/* drop RTS and DTR */
|
|
||||||
if (on)
|
|
||||||
update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0);
|
|
||||||
else
|
|
||||||
update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS);
|
|
||||||
}
|
}
|
||||||
mutex_unlock(&port->serial->disc_mutex);
|
/* drop RTS and DTR */
|
||||||
|
if (on)
|
||||||
|
update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0);
|
||||||
|
else
|
||||||
|
update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch)
|
static void qt2_update_msr(struct usb_serial_port *port, unsigned char *ch)
|
||||||
|
@ -861,19 +861,13 @@ static int sierra_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|||||||
|
|
||||||
static void sierra_dtr_rts(struct usb_serial_port *port, int on)
|
static void sierra_dtr_rts(struct usb_serial_port *port, int on)
|
||||||
{
|
{
|
||||||
struct usb_serial *serial = port->serial;
|
|
||||||
struct sierra_port_private *portdata;
|
struct sierra_port_private *portdata;
|
||||||
|
|
||||||
portdata = usb_get_serial_port_data(port);
|
portdata = usb_get_serial_port_data(port);
|
||||||
portdata->rts_state = on;
|
portdata->rts_state = on;
|
||||||
portdata->dtr_state = on;
|
portdata->dtr_state = on;
|
||||||
|
|
||||||
if (serial->dev) {
|
sierra_send_setup(port);
|
||||||
mutex_lock(&serial->disc_mutex);
|
|
||||||
if (!serial->disconnected)
|
|
||||||
sierra_send_setup(port);
|
|
||||||
mutex_unlock(&serial->disc_mutex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sierra_startup(struct usb_serial *serial)
|
static int sierra_startup(struct usb_serial *serial)
|
||||||
|
@ -506,19 +506,16 @@ static void ssu100_dtr_rts(struct usb_serial_port *port, int on)
|
|||||||
{
|
{
|
||||||
struct usb_device *dev = port->serial->dev;
|
struct usb_device *dev = port->serial->dev;
|
||||||
|
|
||||||
mutex_lock(&port->serial->disc_mutex);
|
/* Disable flow control */
|
||||||
if (!port->serial->disconnected) {
|
if (!on) {
|
||||||
/* Disable flow control */
|
if (ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
|
||||||
if (!on &&
|
|
||||||
ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
|
|
||||||
dev_err(&port->dev, "error from flowcontrol urb\n");
|
dev_err(&port->dev, "error from flowcontrol urb\n");
|
||||||
/* drop RTS and DTR */
|
|
||||||
if (on)
|
|
||||||
set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
|
|
||||||
else
|
|
||||||
clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
|
|
||||||
}
|
}
|
||||||
mutex_unlock(&port->serial->disc_mutex);
|
/* drop RTS and DTR */
|
||||||
|
if (on)
|
||||||
|
set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
|
||||||
|
else
|
||||||
|
clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
|
static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
|
||||||
|
@ -694,10 +694,20 @@ static int serial_carrier_raised(struct tty_port *port)
|
|||||||
static void serial_dtr_rts(struct tty_port *port, int on)
|
static void serial_dtr_rts(struct tty_port *port, int on)
|
||||||
{
|
{
|
||||||
struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
|
struct usb_serial_port *p = container_of(port, struct usb_serial_port, port);
|
||||||
struct usb_serial_driver *drv = p->serial->type;
|
struct usb_serial *serial = p->serial;
|
||||||
|
struct usb_serial_driver *drv = serial->type;
|
||||||
|
|
||||||
if (drv->dtr_rts)
|
if (!drv->dtr_rts)
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
* Work-around bug in the tty-layer which can result in dtr_rts
|
||||||
|
* being called after a disconnect (and tty_unregister_device
|
||||||
|
* has returned). Remove once bug has been squashed.
|
||||||
|
*/
|
||||||
|
mutex_lock(&serial->disc_mutex);
|
||||||
|
if (!serial->disconnected)
|
||||||
drv->dtr_rts(p, on);
|
drv->dtr_rts(p, on);
|
||||||
|
mutex_unlock(&serial->disc_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct tty_port_operations serial_port_ops = {
|
static const struct tty_port_operations serial_port_ops = {
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
|
|
||||||
void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
|
void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
|
||||||
{
|
{
|
||||||
struct usb_serial *serial = port->serial;
|
|
||||||
struct usb_wwan_port_private *portdata;
|
struct usb_wwan_port_private *portdata;
|
||||||
struct usb_wwan_intf_private *intfdata;
|
struct usb_wwan_intf_private *intfdata;
|
||||||
|
|
||||||
@ -48,12 +47,11 @@ void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
portdata = usb_get_serial_port_data(port);
|
portdata = usb_get_serial_port_data(port);
|
||||||
mutex_lock(&serial->disc_mutex);
|
/* FIXME: locking */
|
||||||
portdata->rts_state = on;
|
portdata->rts_state = on;
|
||||||
portdata->dtr_state = on;
|
portdata->dtr_state = on;
|
||||||
if (serial->dev)
|
|
||||||
intfdata->send_setup(port);
|
intfdata->send_setup(port);
|
||||||
mutex_unlock(&serial->disc_mutex);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(usb_wwan_dtr_rts);
|
EXPORT_SYMBOL(usb_wwan_dtr_rts);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user