Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b2ca6990 authored by Johan Hovold's avatar Johan Hovold Committed by Greg Kroah-Hartman
Browse files

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 0998d063
("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: default avatarChris Ruehl <chris.ruehl@gtsys.com.hk>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarJohan Hovold <jhovold@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent cd565279
Loading
Loading
Loading
Loading
+9 −11
Original line number Original line Diff line number Diff line
@@ -1886,10 +1886,9 @@ 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);
	if (!port->serial->disconnected) {
	/* Disable flow control */
	/* Disable flow control */
		if (!on && usb_control_msg(port->serial->dev,
	if (!on) {
		if (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,
@@ -1897,14 +1896,13 @@ static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
			    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 */
	/* drop RTS and DTR */
	if (on)
	if (on)
		set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
		set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
	else
	else
		clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
		clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
}
}
	mutex_unlock(&port->serial->disc_mutex);
}


/*
/*
 * usbserial:__serial_close  only calls ftdi_close if the point is open
 * usbserial:__serial_close  only calls ftdi_close if the point is open
+9 −13
Original line number Original line Diff line number Diff line
@@ -499,9 +499,6 @@ 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);
	if (!port->serial->disconnected) {
		/* drop DTR and RTS */
	spin_lock_irq(&priv->lock);
	spin_lock_irq(&priv->lock);
	if (on)
	if (on)
		priv->control_state |= TIOCM_DTR | TIOCM_RTS;
		priv->control_state |= TIOCM_DTR | TIOCM_RTS;
@@ -509,10 +506,9 @@ static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
		priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
		priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
	control_state = priv->control_state;
	control_state = priv->control_state;
	spin_unlock_irq(&priv->lock);
	spin_unlock_irq(&priv->lock);

	mct_u232_set_modem_ctrl(port, control_state);
	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)
{
{
+8 −10
Original line number Original line Diff line number Diff line
@@ -945,20 +945,18 @@ 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);
	if (!port->serial->disconnected) {
	/* Disable flow control */
	/* Disable flow control */
		if (!on && qt2_setregister(dev, port_priv->device_port,
	if (!on) {
		if (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 */
	/* drop RTS and DTR */
	if (on)
	if (on)
		update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0);
		update_mctrl(port_priv, TIOCM_DTR | TIOCM_RTS, 0);
	else
	else
		update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS);
		update_mctrl(port_priv, 0, TIOCM_DTR | TIOCM_RTS);
}
}
	mutex_unlock(&port->serial->disc_mutex);
}


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)
{
{
+1 −7
Original line number Original line Diff line number Diff line
@@ -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) {
		mutex_lock(&serial->disc_mutex);
		if (!serial->disconnected)
	sierra_send_setup(port);
	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)
+8 −11
Original line number Original line Diff line number Diff line
@@ -506,20 +506,17 @@ 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);
	if (!port->serial->disconnected) {
	/* Disable flow control */
	/* Disable flow control */
		if (!on &&
	if (!on) {
		    ssu100_setregister(dev, 0, UART_MCR, 0) < 0)
		if (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 */
	/* drop RTS and DTR */
	if (on)
	if (on)
		set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
		set_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
	else
	else
		clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
		clear_mctrl(dev, TIOCM_DTR | TIOCM_RTS);
}
}
	mutex_unlock(&port->serial->disc_mutex);
}


static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
static void ssu100_update_msr(struct usb_serial_port *port, u8 msr)
{
{
Loading