diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-aspeed.c | 5 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-npcm7xx.c | 3 | ||||
-rw-r--r-- | drivers/i2c/i2c-core-smbus.c | 20 | ||||
-rw-r--r-- | drivers/i2c/i2c-dev.c | 93 |
4 files changed, 97 insertions, 24 deletions
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 67e8b97c0c95..7395f3702fae 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -533,7 +533,7 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) msg->buf[bus->buf_index++] = recv_byte; if (msg->flags & I2C_M_RECV_LEN) { - if (unlikely(recv_byte > I2C_SMBUS_BLOCK_MAX)) { + if (unlikely(recv_byte > I2C_SMBUS_V3_BLOCK_MAX)) { bus->cmd_err = -EPROTO; aspeed_i2c_do_stop(bus); goto out_no_complete; @@ -718,7 +718,8 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_V3_BLOCK; } #if IS_ENABLED(CONFIG_I2C_SLAVE) diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index 2ad166355ec9..6d60f65add85 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -1399,7 +1399,7 @@ static void npcm_i2c_irq_master_handler_read(struct npcm_i2c *bus) if (bus->read_block_use) { /* first byte in block protocol is the size: */ data = npcm_i2c_rd_byte(bus); - data = clamp_val(data, 1, I2C_SMBUS_BLOCK_MAX); + data = clamp_val(data, 1, I2C_SMBUS_V3_BLOCK_MAX); bus->rd_size = data + block_extra_bytes_size; bus->rd_buf[bus->rd_ind++] = data; @@ -2187,6 +2187,7 @@ static u32 npcm_i2c_functionality(struct i2c_adapter *adap) I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PEC | + I2C_FUNC_SMBUS_V3_BLOCK | I2C_FUNC_SLAVE; } diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index e5b2d1465e7e..743415584aba 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -303,7 +303,8 @@ static void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val) bool is_read = msg->flags & I2C_M_RD; unsigned char *dma_buf; - dma_buf = kzalloc(I2C_SMBUS_BLOCK_MAX + (is_read ? 2 : 3), GFP_KERNEL); + dma_buf = kzalloc(I2C_SMBUS_V3_BLOCK_MAX + (is_read ? 2 : 3), + GFP_KERNEL); if (!dma_buf) return; @@ -329,9 +330,10 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, * initialize most things with sane defaults, to keep the code below * somewhat simpler. */ - unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; - unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; + unsigned char msgbuf0[I2C_SMBUS_V3_BLOCK_MAX+3]; + unsigned char msgbuf1[I2C_SMBUS_V3_BLOCK_MAX+2]; int nmsgs = read_write == I2C_SMBUS_READ ? 2 : 1; + u16 block_max; u8 partial_pec = 0; int status; struct i2c_msg msg[2] = { @@ -350,6 +352,10 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, bool wants_pec = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA); + /* Drivers must opt in to 255 byte max block size */ + block_max = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_V3_BLOCK) + ? I2C_SMBUS_V3_BLOCK_MAX : I2C_SMBUS_BLOCK_MAX; + msgbuf0[0] = command; switch (size) { case I2C_SMBUS_QUICK: @@ -399,7 +405,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, i2c_smbus_try_get_dmabuf(&msg[1], 0); } else { msg[0].len = data->block[0] + 2; - if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { + if (msg[0].len > block_max + 2) { dev_err(&adapter->dev, "Invalid block write size %d\n", data->block[0]); @@ -413,7 +419,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, case I2C_SMBUS_BLOCK_PROC_CALL: nmsgs = 2; /* Another special case */ read_write = I2C_SMBUS_READ; - if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + if (data->block[0] > block_max) { dev_err(&adapter->dev, "Invalid block write size %d\n", data->block[0]); @@ -430,7 +436,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, i2c_smbus_try_get_dmabuf(&msg[1], 0); break; case I2C_SMBUS_I2C_BLOCK_DATA: - if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { + if (data->block[0] > block_max) { dev_err(&adapter->dev, "Invalid block %s size %d\n", read_write == I2C_SMBUS_READ ? "read" : "write", data->block[0]); @@ -498,7 +504,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr, break; case I2C_SMBUS_BLOCK_DATA: case I2C_SMBUS_BLOCK_PROC_CALL: - if (msg[1].buf[0] > I2C_SMBUS_BLOCK_MAX) { + if (msg[1].buf[0] > block_max) { dev_err(&adapter->dev, "Invalid block size returned: %d\n", msg[1].buf[0]); diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index bce0e8bb7852..5ee9118c0407 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -46,6 +46,24 @@ struct i2c_dev { struct cdev cdev; }; +/* The userspace union i2c_smbus_data for I2C_SMBUS ioctl is limited + * to 32 bytes (I2C_SMBUS_BLOCK_MAX) for compatibility. + */ +union compat_i2c_smbus_data { + __u8 byte; + __u16 word; + __u8 block[I2C_SMBUS_BLOCK_MAX + 2]; /* block[0] is used for length */ + /* and one more for user-space compatibility */ +}; + +/* Must match i2c-dev.h definition with compat .data member */ +struct i2c_smbus_ioctl_data { + __u8 read_write; + __u8 command; + __u32 size; + union compat_i2c_smbus_data __user *data; +}; + #define I2C_MINORS (MINORMASK + 1) static LIST_HEAD(i2c_dev_list); static DEFINE_SPINLOCK(i2c_dev_list_lock); @@ -235,14 +253,17 @@ static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, unsigned nmsgs, struct i2c_msg *msgs) { - u8 __user **data_ptrs; + u8 __user **data_ptrs = NULL; + u16 *orig_lens = NULL; int i, res; + res = -ENOMEM; data_ptrs = kmalloc_array(nmsgs, sizeof(u8 __user *), GFP_KERNEL); - if (data_ptrs == NULL) { - kfree(msgs); - return -ENOMEM; - } + if (data_ptrs == NULL) + goto out; + orig_lens = kmalloc_array(nmsgs, sizeof(u16), GFP_KERNEL); + if (orig_lens == NULL) + goto out; res = 0; for (i = 0; i < nmsgs; i++) { @@ -253,12 +274,30 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, } data_ptrs[i] = (u8 __user *)msgs[i].buf; - msgs[i].buf = memdup_user(data_ptrs[i], msgs[i].len); + msgs[i].buf = NULL; + if (msgs[i].len < 1) { + /* Sanity check */ + res = -EINVAL; + break; + + } + /* Allocate a larger buffer to accommodate possible 255 byte + * blocks. Read results will be dropped later + * if they are too large for the original length. + */ + orig_lens[i] = msgs[i].len; + msgs[i].buf = kmalloc(msgs[i].len + I2C_SMBUS_V3_BLOCK_MAX, + GFP_USER | __GFP_NOWARN); if (IS_ERR(msgs[i].buf)) { res = PTR_ERR(msgs[i].buf); break; } - /* memdup_user allocates with GFP_KERNEL, so DMA is ok */ + if (copy_from_user(msgs[i].buf, data_ptrs[i], msgs[i].len)) { + kfree(msgs[i].buf); + res = -EFAULT; + break; + } + /* Buffer from kmalloc, so DMA is ok */ msgs[i].flags |= I2C_M_DMA_SAFE; /* @@ -274,7 +313,7 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, */ if (msgs[i].flags & I2C_M_RECV_LEN) { if (!(msgs[i].flags & I2C_M_RD) || - msgs[i].len < 1 || msgs[i].buf[0] < 1 || + msgs[i].buf[0] < 1 || msgs[i].len < msgs[i].buf[0] + I2C_SMBUS_BLOCK_MAX) { i++; @@ -297,12 +336,16 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, res = i2c_transfer(client->adapter, msgs, nmsgs); while (i-- > 0) { if (res >= 0 && (msgs[i].flags & I2C_M_RD)) { - if (copy_to_user(data_ptrs[i], msgs[i].buf, - msgs[i].len)) + if (orig_lens[i] < msgs[i].len) + res = -EINVAL; + else if (copy_to_user(data_ptrs[i], msgs[i].buf, + msgs[i].len)) res = -EFAULT; } kfree(msgs[i].buf); } +out: + kfree(orig_lens); kfree(data_ptrs); kfree(msgs); return res; @@ -310,7 +353,7 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, u8 read_write, u8 command, u32 size, - union i2c_smbus_data __user *data) + union compat_i2c_smbus_data __user *data) { union i2c_smbus_data temp = {}; int datasize, res; @@ -371,6 +414,16 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, if (copy_from_user(&temp, data, datasize)) return -EFAULT; } + if ((size == I2C_SMBUS_BLOCK_PROC_CALL || + size == I2C_SMBUS_I2C_BLOCK_DATA || + size == I2C_SMBUS_BLOCK_DATA) && + read_write == I2C_SMBUS_WRITE && + temp.block[0] > I2C_SMBUS_BLOCK_MAX) { + /* Don't accept writes larger than the buffer size */ + dev_dbg(&client->adapter->dev, "block write is too large"); + return -EINVAL; + + } if (size == I2C_SMBUS_I2C_BLOCK_BROKEN) { /* Convert old I2C block commands to the new convention. This preserves binary compatibility. */ @@ -380,9 +433,21 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, } res = i2c_smbus_xfer(client->adapter, client->addr, client->flags, read_write, command, size, &temp); - if (!res && ((size == I2C_SMBUS_PROC_CALL) || - (size == I2C_SMBUS_BLOCK_PROC_CALL) || - (read_write == I2C_SMBUS_READ))) { + if (res) + return res; + if ((size == I2C_SMBUS_BLOCK_PROC_CALL || + size == I2C_SMBUS_I2C_BLOCK_DATA || + size == I2C_SMBUS_BLOCK_DATA) && + read_write == I2C_SMBUS_READ && + temp.block[0] > I2C_SMBUS_BLOCK_MAX) { + /* Don't accept reads larger than the buffer size */ + dev_dbg(&client->adapter->dev, "block read is too large"); + return -EINVAL; + + } + if ((size == I2C_SMBUS_PROC_CALL) || + (size == I2C_SMBUS_BLOCK_PROC_CALL) || + (read_write == I2C_SMBUS_READ)) { if (copy_to_user(data, &temp, datasize)) return -EFAULT; } |