[PATCH v2] i2c:at91: add bound checking on SMBus block length bytes
Ludovic Desroches
ludovic.desroches at atmel.com
Tue Oct 21 00:14:34 PDT 2014
Hi Wolfram,
Could you take this patch into your tree?
Thanks
Regards
Ludovic
On Wed, Aug 20, 2014 at 09:39:41PM -0400, Marek Roszko wrote:
> 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 at gmail.com>
> Acked-by: Ludovic Desroches <ludovic.desroches at atmel.com>
> ---
> Change from v1:
> fixed typo in commit message
> reworded message slightly to be specifically say length byte
>
> drivers/i2c/busses/i2c-at91.c | 28 ++++++++++++++++++++++++----
> 1 file changed, 24 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
> index e95f9ba..ec4ff33 100644
> --- a/drivers/i2c/busses/i2c-at91.c
> +++ b/drivers/i2c/busses/i2c-at91.c
> @@ -101,6 +101,7 @@ struct at91_twi_dev {
> unsigned twi_cwgr_reg;
> struct at91_twi_pdata *pdata;
> bool use_dma;
> + bool recv_len_abort;
> struct at91_twi_dma dma;
> };
>
> @@ -267,12 +268,24 @@ static void at91_twi_read_next_byte(struct at91_twi_dev *dev)
> *dev->buf = at91_twi_read(dev, AT91_TWI_RHR) & 0xff;
> --dev->buf_len;
>
> + /* return if aborting, we only needed to read RHR to clear RXRDY*/
> + if (dev->recv_len_abort)
> + return;
> +
> /* handle I2C_SMBUS_BLOCK_DATA */
> if (unlikely(dev->msg->flags & I2C_M_RECV_LEN)) {
> - dev->msg->flags &= ~I2C_M_RECV_LEN;
> - dev->buf_len += *dev->buf;
> - dev->msg->len = dev->buf_len + 1;
> - dev_dbg(dev->dev, "received block length %d\n", dev->buf_len);
> + /* ensure length byte is a valid value */
> + if (*dev->buf <= I2C_SMBUS_BLOCK_MAX && *dev->buf > 0) {
> + dev->msg->flags &= ~I2C_M_RECV_LEN;
> + dev->buf_len += *dev->buf;
> + dev->msg->len = dev->buf_len + 1;
> + dev_dbg(dev->dev, "received block length %d\n",
> + dev->buf_len);
> + } else {
> + /* abort and send the stop by reading one more byte */
> + dev->recv_len_abort = true;
> + dev->buf_len = 1;
> + }
> }
>
> /* 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)
> ret = -EIO;
> goto error;
> }
> + if (dev->recv_len_abort) {
> + dev_err(dev->dev, "invalid smbus block length recvd\n");
> + ret = -EPROTO;
> + goto error;
> + }
> +
> dev_dbg(dev->dev, "transfer complete\n");
>
> return 0;
> @@ -500,6 +519,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num)
> dev->buf_len = m_start->len;
> dev->buf = m_start->buf;
> dev->msg = m_start;
> + dev->recv_len_abort = false;
>
> ret = at91_do_twi_transfer(dev);
>
> --
> 1.7.10.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
More information about the linux-arm-kernel
mailing list