Commit a4745635 authored by Jean Delvare's avatar Jean Delvare Committed by Tom Rini

[PATCH] i2c: user/kernel bug and memory leak in i2c-dev

parent e7a4f368
...@@ -181,6 +181,7 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, ...@@ -181,6 +181,7 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
struct i2c_smbus_ioctl_data data_arg; struct i2c_smbus_ioctl_data data_arg;
union i2c_smbus_data temp; union i2c_smbus_data temp;
struct i2c_msg *rdwr_pa; struct i2c_msg *rdwr_pa;
u8 **data_ptrs;
int i,datasize,res; int i,datasize,res;
unsigned long funcs; unsigned long funcs;
...@@ -214,7 +215,7 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, ...@@ -214,7 +215,7 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
return (copy_to_user((unsigned long __user *)arg, &funcs, return (copy_to_user((unsigned long __user *)arg, &funcs,
sizeof(unsigned long)))?-EFAULT:0; sizeof(unsigned long)))?-EFAULT:0;
case I2C_RDWR: case I2C_RDWR:
if (copy_from_user(&rdwr_arg, if (copy_from_user(&rdwr_arg,
(struct i2c_rdwr_ioctl_data __user *)arg, (struct i2c_rdwr_ioctl_data __user *)arg,
sizeof(rdwr_arg))) sizeof(rdwr_arg)))
...@@ -231,28 +232,37 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, ...@@ -231,28 +232,37 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
if (rdwr_pa == NULL) return -ENOMEM; if (rdwr_pa == NULL) return -ENOMEM;
if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
kfree(rdwr_pa);
return -EFAULT;
}
data_ptrs = (u8 **) kmalloc(rdwr_arg.nmsgs * sizeof(u8 *),
GFP_KERNEL);
if (data_ptrs == NULL) {
kfree(rdwr_pa);
return -ENOMEM;
}
res = 0; res = 0;
for( i=0; i<rdwr_arg.nmsgs; i++ ) { for( i=0; i<rdwr_arg.nmsgs; i++ ) {
if(copy_from_user(&(rdwr_pa[i]),
&(rdwr_arg.msgs[i]),
sizeof(rdwr_pa[i]))) {
res = -EFAULT;
break;
}
/* Limit the size of the message to a sane amount */ /* Limit the size of the message to a sane amount */
if (rdwr_pa[i].len > 8192) { if (rdwr_pa[i].len > 8192) {
res = -EINVAL; res = -EINVAL;
break; break;
} }
data_ptrs[i] = rdwr_pa[i].buf;
rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL); rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
if(rdwr_pa[i].buf == NULL) { if(rdwr_pa[i].buf == NULL) {
res = -ENOMEM; res = -ENOMEM;
break; break;
} }
if(copy_from_user(rdwr_pa[i].buf, if(copy_from_user(rdwr_pa[i].buf,
rdwr_arg.msgs[i].buf, data_ptrs[i],
rdwr_pa[i].len)) { rdwr_pa[i].len)) {
res = -EFAULT; ++i; /* Needs to be kfreed too */
res = -EFAULT;
break; break;
} }
} }
...@@ -260,18 +270,18 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, ...@@ -260,18 +270,18 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
int j; int j;
for (j = 0; j < i; ++j) for (j = 0; j < i; ++j)
kfree(rdwr_pa[j].buf); kfree(rdwr_pa[j].buf);
kfree(data_ptrs);
kfree(rdwr_pa); kfree(rdwr_pa);
return res; return res;
} }
if (!res) {
res = i2c_transfer(client->adapter, res = i2c_transfer(client->adapter,
rdwr_pa, rdwr_pa,
rdwr_arg.nmsgs); rdwr_arg.nmsgs);
}
while(i-- > 0) { while(i-- > 0) {
if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) { if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) {
if(copy_to_user( if(copy_to_user(
rdwr_arg.msgs[i].buf, data_ptrs[i],
rdwr_pa[i].buf, rdwr_pa[i].buf,
rdwr_pa[i].len)) { rdwr_pa[i].len)) {
res = -EFAULT; res = -EFAULT;
...@@ -279,6 +289,7 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, ...@@ -279,6 +289,7 @@ int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd,
} }
kfree(rdwr_pa[i].buf); kfree(rdwr_pa[i].buf);
} }
kfree(data_ptrs);
kfree(rdwr_pa); kfree(rdwr_pa);
return res; return res;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment