diff options
Diffstat (limited to 'drivers/i2c/i2c-dev.c')
| -rw-r--r-- | drivers/i2c/i2c-dev.c | 30 | 
1 files changed, 26 insertions, 4 deletions
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 45048323b75..5ec2261574e 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -265,19 +265,41 @@ static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,  	res = 0;  	for (i = 0; i < rdwr_arg.nmsgs; i++) { -		/* Limit the size of the message to a sane amount; -		 * and don't let length change either. */ -		if ((rdwr_pa[i].len > 8192) || -		    (rdwr_pa[i].flags & I2C_M_RECV_LEN)) { +		/* Limit the size of the message to a sane amount */ +		if (rdwr_pa[i].len > 8192) {  			res = -EINVAL;  			break;  		} +  		data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;  		rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);  		if (IS_ERR(rdwr_pa[i].buf)) {  			res = PTR_ERR(rdwr_pa[i].buf);  			break;  		} + +		/* +		 * If the message length is received from the slave (similar +		 * to SMBus block read), we must ensure that the buffer will +		 * be large enough to cope with a message length of +		 * I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus +		 * drivers allow. The first byte in the buffer must be +		 * pre-filled with the number of extra bytes, which must be +		 * at least one to hold the message length, but can be +		 * greater (for example to account for a checksum byte at +		 * the end of the message.) +		 */ +		if (rdwr_pa[i].flags & I2C_M_RECV_LEN) { +			if (!(rdwr_pa[i].flags & I2C_M_RD) || +			    rdwr_pa[i].buf[0] < 1 || +			    rdwr_pa[i].len < rdwr_pa[i].buf[0] + +					     I2C_SMBUS_BLOCK_MAX) { +				res = -EINVAL; +				break; +			} + +			rdwr_pa[i].len = rdwr_pa[i].buf[0]; +		}  	}  	if (res < 0) {  		int j;  |