iio: health: afe4404: Fix oob read in afe4404_[read|write]_raw

[ Upstream commit fc92d9e3de ]

KASAN report out-of-bounds read as follows:

BUG: KASAN: global-out-of-bounds in afe4404_read_raw+0x2ce/0x380
Read of size 4 at addr ffffffffc00e4658 by task cat/278

Call Trace:
 afe4404_read_raw
 iio_read_channel_info
 dev_attr_show

The buggy address belongs to the variable:
 afe4404_channel_leds+0x18/0xffffffffffffe9c0

This issue can be reproduce by singe command:

 $ cat /sys/bus/i2c/devices/0-0058/iio\:device0/in_intensity6_raw

The array size of afe4404_channel_leds and afe4404_channel_offdacs
are less than channels, so access with chan->address cause OOB read
in afe4404_[read|write]_raw. Fix it by moving access before use them.

Fixes: b36e825764 ("iio: health/afe440x: Use regmap fields")
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Acked-by: Andrew Davis <afd@ti.com>
Link: https://lore.kernel.org/r/20221107152010.95937-1-weiyongjun@huaweicloud.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Wei Yongjun 2022-11-07 15:20:10 +00:00 committed by Greg Kroah-Hartman
parent b1756af172
commit 5eb114f55b

View File

@ -250,20 +250,20 @@ static int afe4404_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask) int *val, int *val2, long mask)
{ {
struct afe4404_data *afe = iio_priv(indio_dev); struct afe4404_data *afe = iio_priv(indio_dev);
unsigned int value_reg = afe4404_channel_values[chan->address]; unsigned int value_reg, led_field, offdac_field;
unsigned int led_field = afe4404_channel_leds[chan->address];
unsigned int offdac_field = afe4404_channel_offdacs[chan->address];
int ret; int ret;
switch (chan->type) { switch (chan->type) {
case IIO_INTENSITY: case IIO_INTENSITY:
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
value_reg = afe4404_channel_values[chan->address];
ret = regmap_read(afe->regmap, value_reg, val); ret = regmap_read(afe->regmap, value_reg, val);
if (ret) if (ret)
return ret; return ret;
return IIO_VAL_INT; return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET: case IIO_CHAN_INFO_OFFSET:
offdac_field = afe4404_channel_offdacs[chan->address];
ret = regmap_field_read(afe->fields[offdac_field], val); ret = regmap_field_read(afe->fields[offdac_field], val);
if (ret) if (ret)
return ret; return ret;
@ -273,6 +273,7 @@ static int afe4404_read_raw(struct iio_dev *indio_dev,
case IIO_CURRENT: case IIO_CURRENT:
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
led_field = afe4404_channel_leds[chan->address];
ret = regmap_field_read(afe->fields[led_field], val); ret = regmap_field_read(afe->fields[led_field], val);
if (ret) if (ret)
return ret; return ret;
@ -295,19 +296,20 @@ static int afe4404_write_raw(struct iio_dev *indio_dev,
int val, int val2, long mask) int val, int val2, long mask)
{ {
struct afe4404_data *afe = iio_priv(indio_dev); struct afe4404_data *afe = iio_priv(indio_dev);
unsigned int led_field = afe4404_channel_leds[chan->address]; unsigned int led_field, offdac_field;
unsigned int offdac_field = afe4404_channel_offdacs[chan->address];
switch (chan->type) { switch (chan->type) {
case IIO_INTENSITY: case IIO_INTENSITY:
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_OFFSET: case IIO_CHAN_INFO_OFFSET:
offdac_field = afe4404_channel_offdacs[chan->address];
return regmap_field_write(afe->fields[offdac_field], val); return regmap_field_write(afe->fields[offdac_field], val);
} }
break; break;
case IIO_CURRENT: case IIO_CURRENT:
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
led_field = afe4404_channel_leds[chan->address];
return regmap_field_write(afe->fields[led_field], val); return regmap_field_write(afe->fields[led_field], val);
} }
break; break;