aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses/i2c-at91.c
diff options
context:
space:
mode:
authorMarek Roszko <mark.roszko@gmail.com>2014-08-20 21:39:41 -0400
committerWolfram Sang <wsa@the-dreams.de>2014-09-02 08:29:33 -0400
commit75b81f339c6af43f6f4a1b3eabe0603321dade65 (patch)
treec3fe33ee41b403ca194c82a14c76652e19038145 /drivers/i2c/busses/i2c-at91.c
parent5da4309f9e1b4de9c2b69e917912fbb84006d44e (diff)
i2c: at91: add bound checking on SMBus block length bytes
The driver was not bound checking the received length byte to ensure it was within the the buffer size that is allocated for SMBus blocks. This resulted in buffer overflows whenever an invalid length byte was received. It also failed to ensure the length byte was not zero. If it received zero, it would end up in an infinite loop as the at91_twi_read_next_byte function returned immediately without allowing RHR to be read to clear the RXRDY interrupt. Tested agaisnt a SMBus compliant battery. Signed-off-by: Marek Roszko <mark.roszko@gmail.com> Acked-by: Ludovic Desroches <ludovic.desroches@atmel.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de> Cc: stable@kernel.org
Diffstat (limited to 'drivers/i2c/busses/i2c-at91.c')
-rw-r--r--drivers/i2c/busses/i2c-at91.c28
1 files changed, 24 insertions, 4 deletions
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index ec299ae17a5a..917d54588d95 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -101,6 +101,7 @@ struct at91_twi_dev {
101 unsigned twi_cwgr_reg; 101 unsigned twi_cwgr_reg;
102 struct at91_twi_pdata *pdata; 102 struct at91_twi_pdata *pdata;
103 bool use_dma; 103 bool use_dma;
104 bool recv_len_abort;
104 struct at91_twi_dma dma; 105 struct at91_twi_dma dma;
105}; 106};
106 107
@@ -267,12 +268,24 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
267 *dev->buf = at91_twi_read(dev, AT91_TWI_RHR) & 0xff; 268 *dev->buf = at91_twi_read(dev, AT91_TWI_RHR) & 0xff;
268 --dev->buf_len; 269 --dev->buf_len;
269 270
271 /* return if aborting, we only needed to read RHR to clear RXRDY*/
272 if (dev->recv_len_abort)
273 return;
274
270 /* handle I2C_SMBUS_BLOCK_DATA */ 275 /* handle I2C_SMBUS_BLOCK_DATA */
271 if (unlikely(dev->msg->flags & I2C_M_RECV_LEN)) { 276 if (unlikely(dev->msg->flags & I2C_M_RECV_LEN)) {
272 dev->msg->flags &= ~I2C_M_RECV_LEN; 277 /* ensure length byte is a valid value */
273 dev->buf_len += *dev->buf; 278 if (*dev->buf <= I2C_SMBUS_BLOCK_MAX && *dev->buf > 0) {
274 dev->msg->len = dev->buf_len + 1; 279 dev->msg->flags &= ~I2C_M_RECV_LEN;
275 dev_dbg(dev->dev, "received block length %d\n", dev->buf_len); 280 dev->buf_len += *dev->buf;
281 dev->msg->len = dev->buf_len + 1;
282 dev_dbg(dev->dev, "received block length %d\n",
283 dev->buf_len);
284 } else {
285 /* abort and send the stop by reading one more byte */
286 dev->recv_len_abort = true;
287 dev->buf_len = 1;
288 }
276 } 289 }
277 290
278 /* send stop if second but last byte has been read */ 291 /* send stop if second but last byte has been read */
@@ -444,6 +457,12 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
444 ret = -EIO; 457 ret = -EIO;
445 goto error; 458 goto error;
446 } 459 }
460 if (dev->recv_len_abort) {
461 dev_err(dev->dev, "invalid smbus block length recvd\n");
462 ret = -EPROTO;
463 goto error;
464 }
465
447 dev_dbg(dev->dev, "transfer complete\n"); 466 dev_dbg(dev->dev, "transfer complete\n");
448 467
449 return 0; 468 return 0;
@@ -500,6 +519,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
500 dev->buf_len = m_start->len; 519 dev->buf_len = m_start->len;
501 dev->buf = m_start->buf; 520 dev->buf = m_start->buf;
502 dev->msg = m_start; 521 dev->msg = m_start;
522 dev->recv_len_abort = false;
503 523
504 ret = at91_do_twi_transfer(dev); 524 ret = at91_do_twi_transfer(dev);
505 525