i2c: xlp9xx: Make sure the transfer size is not more than I2C_SMBUS_BLOCK_SIZE

For SMBus transactions the max permissible transfer size is
I2C_SMBUS_BLOCK_SIZE. It is possible that some clients might
not follow it strictly occasionally.
This would lead to stack corruption if the driver copies more than
I2C_SMBUS_BLOCK_SIZE bytes. Add a check to avoid such conditions.

Signed-off-by: Jayachandran C <jnair@caviumnetworks.com>
Signed-off-by: George Cherian <george.cherian@cavium.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
George Cherian 2018-05-16 00:00:18 -07:00 committed by Wolfram Sang
parent 8d504d804a
commit 88b4116e7e
1 changed files with 24 additions and 13 deletions

View File

@ -172,6 +172,8 @@ static void xlp9xx_i2c_update_rlen(struct xlp9xx_i2c_dev *priv)
len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) &
XLP9XX_I2C_FIFO_WCNT_MASK;
len = max_t(u32, priv->msg_len, len + 4);
if (len >= I2C_SMBUS_BLOCK_MAX + 2)
return;
val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
(len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val);
@ -189,14 +191,20 @@ static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
if (priv->len_recv) {
/* read length byte */
rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
*buf++ = rlen;
if (priv->client_pec)
++rlen;
/* update remaining bytes and message length */
priv->msg_buf_remaining = rlen;
priv->msg_len = rlen + 1;
priv->len_recv = false;
if (rlen > I2C_SMBUS_BLOCK_MAX || rlen == 0) {
rlen = 0; /*abort transfer */
priv->msg_buf_remaining = 0;
priv->msg_len = 0;
} else {
*buf++ = rlen;
if (priv->client_pec)
++rlen; /* account for error check byte */
/* update remaining bytes and message length */
priv->msg_buf_remaining = rlen;
priv->msg_len = rlen + 1;
}
xlp9xx_i2c_update_rlen(priv);
priv->len_recv = false;
} else {
len = min(priv->msg_buf_remaining, len);
for (i = 0; i < len; i++, buf++)
@ -315,10 +323,6 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL,
XLP9XX_I2C_MFIFOCTRL_RST);
/* set FIFO threshold if reading */
if (priv->msg_read)
xlp9xx_i2c_update_rx_fifo_thres(priv);
/* set slave addr */
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_SLAVEADDR,
(msg->addr << XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT) |
@ -337,9 +341,13 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
val &= ~XLP9XX_I2C_CTRL_ADDMODE;
priv->len_recv = msg->flags & I2C_M_RECV_LEN;
len = priv->len_recv ? XLP9XX_I2C_FIFO_SIZE : msg->len;
len = priv->len_recv ? I2C_SMBUS_BLOCK_MAX + 2 : msg->len;
priv->client_pec = msg->flags & I2C_CLIENT_PEC;
/* set FIFO threshold if reading */
if (priv->msg_read)
xlp9xx_i2c_update_rx_fifo_thres(priv);
/* set data length to be transferred */
val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
(len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
@ -393,8 +401,11 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
}
/* update msg->len with actual received length */
if (msg->flags & I2C_M_RECV_LEN)
if (msg->flags & I2C_M_RECV_LEN) {
if (!priv->msg_len)
return -EPROTO;
msg->len = priv->msg_len;
}
return 0;
}