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

media: dvbdev: prevent the risk of out of memory access

The dvbdev contains a static variable used to store dvb minors.

The behavior of it depends if CONFIG_DVB_DYNAMIC_MINORS is set
or not. When not set, dvb_register_device() won't check for
boundaries, as it will rely that a previous call to
dvb_register_adapter() would already be enforcing it.

On a similar way, dvb_device_open() uses the assumption
that the register functions already did the needed checks.

This can be fragile if some device ends using different
calls. This also generate warnings on static check analysers
like Coverity.

So, add explicit guards to prevent potential risk of OOM issues.

Fixes: 5dd3f3071070 ("V4L/DVB (9361): Dynamic DVB minor allocation")
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

+15 -2
+15 -2
drivers/media/dvb-core/dvbdev.c
··· 86 86 static int dvb_device_open(struct inode *inode, struct file *file) 87 87 { 88 88 struct dvb_device *dvbdev; 89 + unsigned int minor = iminor(inode); 90 + 91 + if (minor >= MAX_DVB_MINORS) 92 + return -ENODEV; 89 93 90 94 mutex_lock(&dvbdev_mutex); 91 95 down_read(&minor_rwsem); 92 - dvbdev = dvb_minors[iminor(inode)]; 96 + 97 + dvbdev = dvb_minors[minor]; 93 98 94 99 if (dvbdev && dvbdev->fops) { 95 100 int err = 0; ··· 530 525 for (minor = 0; minor < MAX_DVB_MINORS; minor++) 531 526 if (!dvb_minors[minor]) 532 527 break; 533 - if (minor == MAX_DVB_MINORS) { 528 + if (minor >= MAX_DVB_MINORS) { 534 529 if (new_node) { 535 530 list_del(&new_node->list_head); 536 531 kfree(dvbdevfops); ··· 545 540 } 546 541 #else 547 542 minor = nums2minor(adap->num, type, id); 543 + if (minor >= MAX_DVB_MINORS) { 544 + dvb_media_device_free(dvbdev); 545 + list_del(&dvbdev->list_head); 546 + kfree(dvbdev); 547 + *pdvbdev = NULL; 548 + mutex_unlock(&dvbdev_register_lock); 549 + return ret; 550 + } 548 551 #endif 549 552 dvbdev->minor = minor; 550 553 dvb_minors[minor] = dvb_device_get(dvbdev);