summaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorGeorge Cherian <george.cherian@cavium.com>2018-05-16 00:00:18 -0700
committerWolfram Sang <wsa@the-dreams.de>2018-05-22 14:06:34 +0200
commit88b4116e7e98454c2131094336e4f8861eebbd85 (patch)
tree639eed4ee889c536ec063ae06bc76ca78948d6f9 /drivers/i2c
parent8d504d804ab657779254bdd37079d2442d75cbe8 (diff)
downloadlinux-88b4116e7e98454c2131094336e4f8861eebbd85.tar.gz
linux-88b4116e7e98454c2131094336e4f8861eebbd85.tar.bz2
linux-88b4116e7e98454c2131094336e4f8861eebbd85.zip
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>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-xlp9xx.c37
1 files changed, 24 insertions, 13 deletions
diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c
index c268fde5bbd9..1f41a4f89c08 100644
--- a/drivers/i2c/busses/i2c-xlp9xx.c
+++ b/drivers/i2c/busses/i2c-xlp9xx.c
@@ -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;
}