iio: health: afe4404: Fix oob read in afe4404_[read|write]_raw
authorWei Yongjun <weiyongjun1@huawei.com>
Mon, 7 Nov 2022 15:20:10 +0000 (15:20 +0000)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sat, 12 Nov 2022 16:30:23 +0000 (16:30 +0000)
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: b36e8257641a ("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>
drivers/iio/health/afe4404.c

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