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

Configure Feed

Select the types of activity you want to include in your feed.

driver: iio: add missing checks on iio_info's callback access

Some callbacks from iio_info structure are accessed without any check, so
if a driver doesn't implement them trying to access the corresponding
sysfs entries produce a kernel oops such as:

[ 2203.527791] Unable to handle kernel NULL pointer dereference at virtual address 00000000 when execute
[...]
[ 2203.783416] Call trace:
[ 2203.783429] iio_read_channel_info_avail from dev_attr_show+0x18/0x48
[ 2203.789807] dev_attr_show from sysfs_kf_seq_show+0x90/0x120
[ 2203.794181] sysfs_kf_seq_show from seq_read_iter+0xd0/0x4e4
[ 2203.798555] seq_read_iter from vfs_read+0x238/0x2a0
[ 2203.802236] vfs_read from ksys_read+0xa4/0xd4
[ 2203.805385] ksys_read from ret_fast_syscall+0x0/0x54
[ 2203.809135] Exception stack(0xe0badfa8 to 0xe0badff0)
[ 2203.812880] dfa0: 00000003 b6f10f80 00000003 b6eab000 00020000 00000000
[ 2203.819746] dfc0: 00000003 b6f10f80 7ff00000 00000003 00000003 00000000 00020000 00000000
[ 2203.826619] dfe0: b6e1bc88 bed80958 b6e1bc94 b6e1bcb0
[ 2203.830363] Code: bad PC value
[ 2203.832695] ---[ end trace 0000000000000000 ]---

Reviewed-by: Nuno Sa <nuno.sa@analog.com>
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
Link: https://lore.kernel.org/r/20240530-iio-core-fix-segfault-v3-1-8b7cd2a03773@baylibre.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Julien Stephan and committed by
Jonathan Cameron
c4ec8ded 9f53b59f

+37 -11
+6 -1
drivers/iio/industrialio-core.c
··· 758 758 INDIO_MAX_RAW_ELEMENTS, 759 759 vals, &val_len, 760 760 this_attr->address); 761 - else 761 + else if (indio_dev->info->read_raw) 762 762 ret = indio_dev->info->read_raw(indio_dev, this_attr->c, 763 763 &vals[0], &vals[1], this_attr->address); 764 + else 765 + return -EINVAL; 764 766 765 767 if (ret < 0) 766 768 return ret; ··· 843 841 int ret; 844 842 int length; 845 843 int type; 844 + 845 + if (!indio_dev->info->read_avail) 846 + return -EINVAL; 846 847 847 848 ret = indio_dev->info->read_avail(indio_dev, this_attr->c, 848 849 &vals, &type, &length,
+9
drivers/iio/industrialio-event.c
··· 285 285 if (ret < 0) 286 286 return ret; 287 287 288 + if (!indio_dev->info->write_event_config) 289 + return -EINVAL; 290 + 288 291 ret = indio_dev->info->write_event_config(indio_dev, 289 292 this_attr->c, iio_ev_attr_type(this_attr), 290 293 iio_ev_attr_dir(this_attr), val); ··· 302 299 struct iio_dev *indio_dev = dev_to_iio_dev(dev); 303 300 struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); 304 301 int val; 302 + 303 + if (!indio_dev->info->read_event_config) 304 + return -EINVAL; 305 305 306 306 val = indio_dev->info->read_event_config(indio_dev, 307 307 this_attr->c, iio_ev_attr_type(this_attr), ··· 323 317 struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); 324 318 int val, val2, val_arr[2]; 325 319 int ret; 320 + 321 + if (!indio_dev->info->read_event_value) 322 + return -EINVAL; 326 323 327 324 ret = indio_dev->info->read_event_value(indio_dev, 328 325 this_attr->c, iio_ev_attr_type(this_attr),
+22 -10
drivers/iio/inkern.c
··· 543 543 static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, 544 544 enum iio_chan_info_enum info) 545 545 { 546 + const struct iio_info *iio_info = chan->indio_dev->info; 546 547 int unused; 547 548 int vals[INDIO_MAX_RAW_ELEMENTS]; 548 549 int ret; ··· 555 554 if (!iio_channel_has_info(chan->channel, info)) 556 555 return -EINVAL; 557 556 558 - if (chan->indio_dev->info->read_raw_multi) { 559 - ret = chan->indio_dev->info->read_raw_multi(chan->indio_dev, 560 - chan->channel, INDIO_MAX_RAW_ELEMENTS, 561 - vals, &val_len, info); 557 + if (iio_info->read_raw_multi) { 558 + ret = iio_info->read_raw_multi(chan->indio_dev, 559 + chan->channel, 560 + INDIO_MAX_RAW_ELEMENTS, 561 + vals, &val_len, info); 562 562 *val = vals[0]; 563 563 *val2 = vals[1]; 564 + } else if (iio_info->read_raw) { 565 + ret = iio_info->read_raw(chan->indio_dev, 566 + chan->channel, val, val2, info); 564 567 } else { 565 - ret = chan->indio_dev->info->read_raw(chan->indio_dev, 566 - chan->channel, val, val2, info); 568 + return -EINVAL; 567 569 } 568 570 569 571 return ret; ··· 754 750 const int **vals, int *type, int *length, 755 751 enum iio_chan_info_enum info) 756 752 { 753 + const struct iio_info *iio_info = chan->indio_dev->info; 754 + 757 755 if (!iio_channel_has_available(chan->channel, info)) 758 756 return -EINVAL; 759 757 760 - return chan->indio_dev->info->read_avail(chan->indio_dev, chan->channel, 761 - vals, type, length, info); 758 + if (iio_info->read_avail) 759 + return iio_info->read_avail(chan->indio_dev, chan->channel, 760 + vals, type, length, info); 761 + return -EINVAL; 762 762 } 763 763 764 764 int iio_read_avail_channel_attribute(struct iio_channel *chan, ··· 925 917 static int iio_channel_write(struct iio_channel *chan, int val, int val2, 926 918 enum iio_chan_info_enum info) 927 919 { 928 - return chan->indio_dev->info->write_raw(chan->indio_dev, 929 - chan->channel, val, val2, info); 920 + const struct iio_info *iio_info = chan->indio_dev->info; 921 + 922 + if (iio_info->write_raw) 923 + return iio_info->write_raw(chan->indio_dev, 924 + chan->channel, val, val2, info); 925 + return -EINVAL; 930 926 } 931 927 932 928 int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2,