diff options
Diffstat (limited to 'drivers/i2c/i2c-dev.c')
-rw-r--r-- | drivers/i2c/i2c-dev.c | 93 |
1 files changed, 14 insertions, 79 deletions
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 5ee9118c0407..bce0e8bb7852 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -46,24 +46,6 @@ 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); @@ -253,17 +235,14 @@ 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 = NULL; - u16 *orig_lens = NULL; + u8 __user **data_ptrs; int i, res; - res = -ENOMEM; data_ptrs = kmalloc_array(nmsgs, sizeof(u8 __user *), GFP_KERNEL); - if (data_ptrs == NULL) - goto out; - orig_lens = kmalloc_array(nmsgs, sizeof(u16), GFP_KERNEL); - if (orig_lens == NULL) - goto out; + if (data_ptrs == NULL) { + kfree(msgs); + return -ENOMEM; + } res = 0; for (i = 0; i < nmsgs; i++) { @@ -274,30 +253,12 @@ static noinline int i2cdev_ioctl_rdwr(struct i2c_client *client, } data_ptrs[i] = (u8 __user *)msgs[i].buf; - 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); + msgs[i].buf = memdup_user(data_ptrs[i], msgs[i].len); if (IS_ERR(msgs[i].buf)) { res = PTR_ERR(msgs[i].buf); break; } - 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 */ + /* memdup_user allocates with GFP_KERNEL, so DMA is ok */ msgs[i].flags |= I2C_M_DMA_SAFE; /* @@ -313,7 +274,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].buf[0] < 1 || + msgs[i].len < 1 || msgs[i].buf[0] < 1 || msgs[i].len < msgs[i].buf[0] + I2C_SMBUS_BLOCK_MAX) { i++; @@ -336,16 +297,12 @@ 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 (orig_lens[i] < msgs[i].len) - res = -EINVAL; - else if (copy_to_user(data_ptrs[i], msgs[i].buf, - msgs[i].len)) + 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; @@ -353,7 +310,7 @@ out: static noinline int i2cdev_ioctl_smbus(struct i2c_client *client, u8 read_write, u8 command, u32 size, - union compat_i2c_smbus_data __user *data) + union i2c_smbus_data __user *data) { union i2c_smbus_data temp = {}; int datasize, res; @@ -414,16 +371,6 @@ 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. */ @@ -433,21 +380,9 @@ 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) - 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 (!res && ((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; } |