Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

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>

authored by

Wei Yongjun and committed by
Jonathan Cameron
fc92d9e3 58143c1e

+7 -5
+7 -5
drivers/iio/health/afe4404.c
··· 250 250 int *val, int *val2, long mask) 251 251 { 252 252 struct afe4404_data *afe = iio_priv(indio_dev); 253 - unsigned int value_reg = afe4404_channel_values[chan->address]; 254 - unsigned int led_field = afe4404_channel_leds[chan->address]; 255 - unsigned int offdac_field = afe4404_channel_offdacs[chan->address]; 253 + unsigned int value_reg, led_field, offdac_field; 256 254 int ret; 257 255 258 256 switch (chan->type) { 259 257 case IIO_INTENSITY: 260 258 switch (mask) { 261 259 case IIO_CHAN_INFO_RAW: 260 + value_reg = afe4404_channel_values[chan->address]; 262 261 ret = regmap_read(afe->regmap, value_reg, val); 263 262 if (ret) 264 263 return ret; 265 264 return IIO_VAL_INT; 266 265 case IIO_CHAN_INFO_OFFSET: 266 + offdac_field = afe4404_channel_offdacs[chan->address]; 267 267 ret = regmap_field_read(afe->fields[offdac_field], val); 268 268 if (ret) 269 269 return ret; ··· 273 273 case IIO_CURRENT: 274 274 switch (mask) { 275 275 case IIO_CHAN_INFO_RAW: 276 + led_field = afe4404_channel_leds[chan->address]; 276 277 ret = regmap_field_read(afe->fields[led_field], val); 277 278 if (ret) 278 279 return ret; ··· 296 295 int val, int val2, long mask) 297 296 { 298 297 struct afe4404_data *afe = iio_priv(indio_dev); 299 - unsigned int led_field = afe4404_channel_leds[chan->address]; 300 - unsigned int offdac_field = afe4404_channel_offdacs[chan->address]; 298 + unsigned int led_field, offdac_field; 301 299 302 300 switch (chan->type) { 303 301 case IIO_INTENSITY: 304 302 switch (mask) { 305 303 case IIO_CHAN_INFO_OFFSET: 304 + offdac_field = afe4404_channel_offdacs[chan->address]; 306 305 return regmap_field_write(afe->fields[offdac_field], val); 307 306 } 308 307 break; 309 308 case IIO_CURRENT: 310 309 switch (mask) { 311 310 case IIO_CHAN_INFO_RAW: 311 + led_field = afe4404_channel_leds[chan->address]; 312 312 return regmap_field_write(afe->fields[led_field], val); 313 313 } 314 314 break;