Skip to content

Commit

Permalink
USB: serial: ch341: fix control-message error handling
Browse files Browse the repository at this point in the history
A short control transfer would currently fail to be detected, something
which could lead to stale buffer data being used as valid input.

Check for short transfers, and make sure to log any transfer errors.

Note that this also avoids leaking heap data to user space (TIOCMGET)
and the remote device (break control).

Fixes: 6ce7610 ("USB: Driver for CH341 USB-serial adaptor")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Johan Hovold <johan@kernel.org>
  • Loading branch information
jhovold committed Jan 11, 2017
1 parent 146cc8a commit 2d5a9c7
Showing 1 changed file with 21 additions and 11 deletions.
32 changes: 21 additions & 11 deletions drivers/usb/serial/ch341.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request,
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
value, index, NULL, 0, DEFAULT_TIMEOUT);
if (r < 0)
dev_err(&dev->dev, "failed to send control message: %d\n", r);

return r;
}
Expand All @@ -130,7 +132,20 @@ static int ch341_control_in(struct usb_device *dev,
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, buf, bufsize, DEFAULT_TIMEOUT);
return r;
if (r < bufsize) {
if (r >= 0) {
dev_err(&dev->dev,
"short control message received (%d < %u)\n",
r, bufsize);
r = -EIO;
}

dev_err(&dev->dev, "failed to receive control message: %d\n",
r);
return r;
}

return 0;
}

static int ch341_set_baudrate_lcr(struct usb_device *dev,
Expand Down Expand Up @@ -181,9 +196,9 @@ static int ch341_set_handshake(struct usb_device *dev, u8 control)

static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
{
const unsigned int size = 2;
char *buffer;
int r;
const unsigned size = 8;
unsigned long flags;

buffer = kmalloc(size, GFP_KERNEL);
Expand All @@ -194,14 +209,9 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
if (r < 0)
goto out;

/* setup the private status if available */
if (r == 2) {
r = 0;
spin_lock_irqsave(&priv->lock, flags);
priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
spin_unlock_irqrestore(&priv->lock, flags);
} else
r = -EPROTO;
spin_lock_irqsave(&priv->lock, flags);
priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
spin_unlock_irqrestore(&priv->lock, flags);

out: kfree(buffer);
return r;
Expand All @@ -211,9 +221,9 @@ out: kfree(buffer);

static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
{
const unsigned int size = 2;
char *buffer;
int r;
const unsigned size = 8;

buffer = kmalloc(size, GFP_KERNEL);
if (!buffer)
Expand Down

0 comments on commit 2d5a9c7

Please sign in to comment.