Commit a457dcb7 authored by Johan Hovold's avatar Johan Hovold Committed by Sasha Levin

USB: serial: ch341: fix control-message error handling

[ Upstream commit 2d5a9c72 ]

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: 6ce76104 ("USB: Driver for CH341 USB-serial adaptor")
Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarJohan Hovold <johan@kernel.org>
Signed-off-by: default avatarSasha Levin <alexander.levin@verizon.com>
parent 1fe63899
...@@ -99,6 +99,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request, ...@@ -99,6 +99,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request,
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
value, index, NULL, 0, DEFAULT_TIMEOUT); value, index, NULL, 0, DEFAULT_TIMEOUT);
if (r < 0)
dev_err(&dev->dev, "failed to send control message: %d\n", r);
return r; return r;
} }
...@@ -116,7 +118,20 @@ static int ch341_control_in(struct usb_device *dev, ...@@ -116,7 +118,20 @@ static int ch341_control_in(struct usb_device *dev,
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
value, index, buf, bufsize, DEFAULT_TIMEOUT); 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(struct usb_device *dev, static int ch341_set_baudrate(struct usb_device *dev,
...@@ -158,9 +173,9 @@ static int ch341_set_handshake(struct usb_device *dev, u8 control) ...@@ -158,9 +173,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) static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
{ {
const unsigned int size = 2;
char *buffer; char *buffer;
int r; int r;
const unsigned size = 8;
unsigned long flags; unsigned long flags;
buffer = kmalloc(size, GFP_KERNEL); buffer = kmalloc(size, GFP_KERNEL);
...@@ -171,14 +186,9 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) ...@@ -171,14 +186,9 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
if (r < 0) if (r < 0)
goto out; goto out;
/* setup the private status if available */ spin_lock_irqsave(&priv->lock, flags);
if (r == 2) { priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
r = 0; spin_unlock_irqrestore(&priv->lock, flags);
spin_lock_irqsave(&priv->lock, flags);
priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
spin_unlock_irqrestore(&priv->lock, flags);
} else
r = -EPROTO;
out: kfree(buffer); out: kfree(buffer);
return r; return r;
...@@ -188,9 +198,9 @@ out: kfree(buffer); ...@@ -188,9 +198,9 @@ out: kfree(buffer);
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
{ {
const unsigned int size = 2;
char *buffer; char *buffer;
int r; int r;
const unsigned size = 8;
buffer = kmalloc(size, GFP_KERNEL); buffer = kmalloc(size, GFP_KERNEL);
if (!buffer) if (!buffer)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment