From: Greg Kroah-Hartman Date: Mon, 19 Jun 2023 07:50:07 +0000 (+0200) Subject: 5.10-stable patches X-Git-Tag: v4.14.319~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=80e2a7cee17ba4129a600fc08a7fb5e28f4cd7dd;p=thirdparty%2Fkernel%2Fstable-queue.git 5.10-stable patches added patches: media-dvb-core-fix-use-after-free-due-to-race-at-dvb_register_device.patch media-dvbdev-fix-error-logic-at-dvb_register_device.patch media-dvbdev-fix-memleak-in-dvb_register_device.patch --- diff --git a/queue-5.10/media-dvb-core-fix-use-after-free-due-to-race-at-dvb_register_device.patch b/queue-5.10/media-dvb-core-fix-use-after-free-due-to-race-at-dvb_register_device.patch new file mode 100644 index 00000000000..c29fceef601 --- /dev/null +++ b/queue-5.10/media-dvb-core-fix-use-after-free-due-to-race-at-dvb_register_device.patch @@ -0,0 +1,251 @@ +From stable-owner@vger.kernel.org Mon Jun 12 15:40:18 2023 +From: ovidiu.panait@windriver.com +Date: Mon, 12 Jun 2023 16:39:07 +0300 +Subject: media: dvb-core: Fix use-after-free due to race at dvb_register_device() +To: stable@vger.kernel.org +Cc: Hyunwoo Kim , kernel test robot , Dan Carpenter , Mauro Carvalho Chehab , Ovidiu Panait +Message-ID: <20230612133907.2999114-3-ovidiu.panait@windriver.com> + +From: Hyunwoo Kim + +commit 627bb528b086b4136315c25d6a447a98ea9448d3 upstream. + +dvb_register_device() dynamically allocates fops with kmemdup() +to set the fops->owner. +And these fops are registered in 'file->f_ops' using replace_fops() +in the dvb_device_open() process, and kfree()d in dvb_free_device(). + +However, it is not common to use dynamically allocated fops instead +of 'static const' fops as an argument of replace_fops(), +and UAF may occur. +These UAFs can occur on any dvb type using dvb_register_device(), +such as dvb_dvr, dvb_demux, dvb_frontend, dvb_net, etc. + +So, instead of kfree() the fops dynamically allocated in +dvb_register_device() in dvb_free_device() called during the +.disconnect() process, kfree() it collectively in exit_dvbdev() +called when the dvbdev.c module is removed. + +Link: https://lore.kernel.org/linux-media/20221117045925.14297-4-imv4bel@gmail.com +Signed-off-by: Hyunwoo Kim +Reported-by: kernel test robot +Reported-by: Dan Carpenter +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Ovidiu Panait +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/dvb-core/dvbdev.c | 84 ++++++++++++++++++++++++++++++---------- + include/media/dvbdev.h | 15 +++++++ + 2 files changed, 78 insertions(+), 21 deletions(-) + +--- a/drivers/media/dvb-core/dvbdev.c ++++ b/drivers/media/dvb-core/dvbdev.c +@@ -37,6 +37,7 @@ + #include + + static DEFINE_MUTEX(dvbdev_mutex); ++static LIST_HEAD(dvbdevfops_list); + static int dvbdev_debug; + + module_param(dvbdev_debug, int, 0644); +@@ -462,14 +463,15 @@ int dvb_register_device(struct dvb_adapt + enum dvb_device_type type, int demux_sink_pads) + { + struct dvb_device *dvbdev; +- struct file_operations *dvbdevfops; ++ struct file_operations *dvbdevfops = NULL; ++ struct dvbdevfops_node *node = NULL, *new_node = NULL; + struct device *clsdev; + int minor; + int id, ret; + + mutex_lock(&dvbdev_register_lock); + +- if ((id = dvbdev_get_free_id (adap, type)) < 0){ ++ if ((id = dvbdev_get_free_id (adap, type)) < 0) { + mutex_unlock(&dvbdev_register_lock); + *pdvbdev = NULL; + pr_err("%s: couldn't find free device id\n", __func__); +@@ -477,18 +479,45 @@ int dvb_register_device(struct dvb_adapt + } + + *pdvbdev = dvbdev = kzalloc(sizeof(*dvbdev), GFP_KERNEL); +- + if (!dvbdev){ + mutex_unlock(&dvbdev_register_lock); + return -ENOMEM; + } + +- dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL); ++ /* ++ * When a device of the same type is probe()d more than once, ++ * the first allocated fops are used. This prevents memory leaks ++ * that can occur when the same device is probe()d repeatedly. ++ */ ++ list_for_each_entry(node, &dvbdevfops_list, list_head) { ++ if (node->fops->owner == adap->module && ++ node->type == type && ++ node->template == template) { ++ dvbdevfops = node->fops; ++ break; ++ } ++ } + +- if (!dvbdevfops){ +- kfree (dvbdev); +- mutex_unlock(&dvbdev_register_lock); +- return -ENOMEM; ++ if (dvbdevfops == NULL) { ++ dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL); ++ if (!dvbdevfops) { ++ kfree(dvbdev); ++ mutex_unlock(&dvbdev_register_lock); ++ return -ENOMEM; ++ } ++ ++ new_node = kzalloc(sizeof(struct dvbdevfops_node), GFP_KERNEL); ++ if (!new_node) { ++ kfree(dvbdevfops); ++ kfree(dvbdev); ++ mutex_unlock(&dvbdev_register_lock); ++ return -ENOMEM; ++ } ++ ++ new_node->fops = dvbdevfops; ++ new_node->type = type; ++ new_node->template = template; ++ list_add_tail (&new_node->list_head, &dvbdevfops_list); + } + + memcpy(dvbdev, template, sizeof(struct dvb_device)); +@@ -499,20 +528,20 @@ int dvb_register_device(struct dvb_adapt + dvbdev->priv = priv; + dvbdev->fops = dvbdevfops; + init_waitqueue_head (&dvbdev->wait_queue); +- + dvbdevfops->owner = adap->module; +- + list_add_tail (&dvbdev->list_head, &adap->device_list); +- + down_write(&minor_rwsem); + #ifdef CONFIG_DVB_DYNAMIC_MINORS + for (minor = 0; minor < MAX_DVB_MINORS; minor++) + if (dvb_minors[minor] == NULL) + break; +- + if (minor == MAX_DVB_MINORS) { ++ if (new_node) { ++ list_del (&new_node->list_head); ++ kfree(dvbdevfops); ++ kfree(new_node); ++ } + list_del (&dvbdev->list_head); +- kfree(dvbdevfops); + kfree(dvbdev); + up_write(&minor_rwsem); + mutex_unlock(&dvbdev_register_lock); +@@ -521,41 +550,47 @@ int dvb_register_device(struct dvb_adapt + #else + minor = nums2minor(adap->num, type, id); + #endif +- + dvbdev->minor = minor; + dvb_minors[minor] = dvb_device_get(dvbdev); + up_write(&minor_rwsem); +- + ret = dvb_register_media_device(dvbdev, type, minor, demux_sink_pads); + if (ret) { + pr_err("%s: dvb_register_media_device failed to create the mediagraph\n", + __func__); +- ++ if (new_node) { ++ list_del (&new_node->list_head); ++ kfree(dvbdevfops); ++ kfree(new_node); ++ } + dvb_media_device_free(dvbdev); + list_del (&dvbdev->list_head); +- kfree(dvbdevfops); + kfree(dvbdev); + mutex_unlock(&dvbdev_register_lock); + return ret; + } + +- mutex_unlock(&dvbdev_register_lock); +- + clsdev = device_create(dvb_class, adap->device, + MKDEV(DVB_MAJOR, minor), + dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id); + if (IS_ERR(clsdev)) { + pr_err("%s: failed to create device dvb%d.%s%d (%ld)\n", + __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); ++ if (new_node) { ++ list_del (&new_node->list_head); ++ kfree(dvbdevfops); ++ kfree(new_node); ++ } + dvb_media_device_free(dvbdev); + list_del (&dvbdev->list_head); +- kfree(dvbdevfops); + kfree(dvbdev); ++ mutex_unlock(&dvbdev_register_lock); + return PTR_ERR(clsdev); + } ++ + dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", + adap->num, dnames[type], id, minor, minor); + ++ mutex_unlock(&dvbdev_register_lock); + return 0; + } + EXPORT_SYMBOL(dvb_register_device); +@@ -584,7 +619,6 @@ static void dvb_free_device(struct kref + { + struct dvb_device *dvbdev = container_of(ref, struct dvb_device, ref); + +- kfree (dvbdev->fops); + kfree (dvbdev); + } + +@@ -1090,9 +1124,17 @@ error: + + static void __exit exit_dvbdev(void) + { ++ struct dvbdevfops_node *node, *next; ++ + class_destroy(dvb_class); + cdev_del(&dvb_device_cdev); + unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS); ++ ++ list_for_each_entry_safe(node, next, &dvbdevfops_list, list_head) { ++ list_del (&node->list_head); ++ kfree(node->fops); ++ kfree(node); ++ } + } + + subsys_initcall(init_dvbdev); +--- a/include/media/dvbdev.h ++++ b/include/media/dvbdev.h +@@ -190,6 +190,21 @@ struct dvb_device { + }; + + /** ++ * struct dvbdevfops_node - fops nodes registered in dvbdevfops_list ++ * ++ * @fops: Dynamically allocated fops for ->owner registration ++ * @type: type of dvb_device ++ * @template: dvb_device used for registration ++ * @list_head: list_head for dvbdevfops_list ++ */ ++struct dvbdevfops_node { ++ struct file_operations *fops; ++ enum dvb_device_type type; ++ const struct dvb_device *template; ++ struct list_head list_head; ++}; ++ ++/** + * dvb_device_get - Increase dvb_device reference + * + * @dvbdev: pointer to struct dvb_device diff --git a/queue-5.10/media-dvbdev-fix-error-logic-at-dvb_register_device.patch b/queue-5.10/media-dvbdev-fix-error-logic-at-dvb_register_device.patch new file mode 100644 index 00000000000..dbf0dc79910 --- /dev/null +++ b/queue-5.10/media-dvbdev-fix-error-logic-at-dvb_register_device.patch @@ -0,0 +1,54 @@ +From stable-owner@vger.kernel.org Mon Jun 12 15:40:14 2023 +From: ovidiu.panait@windriver.com +Date: Mon, 12 Jun 2023 16:39:06 +0300 +Subject: media: dvbdev: fix error logic at dvb_register_device() +To: stable@vger.kernel.org +Cc: Mauro Carvalho Chehab , Ovidiu Panait +Message-ID: <20230612133907.2999114-2-ovidiu.panait@windriver.com> + +From: Mauro Carvalho Chehab + +commit 1fec2ecc252301110e4149e6183fa70460d29674 upstream. + +As reported by smatch: + + drivers/media/dvb-core/dvbdev.c: drivers/media/dvb-core/dvbdev.c:510 dvb_register_device() warn: '&dvbdev->list_head' not removed from list + drivers/media/dvb-core/dvbdev.c: drivers/media/dvb-core/dvbdev.c:530 dvb_register_device() warn: '&dvbdev->list_head' not removed from list + drivers/media/dvb-core/dvbdev.c: drivers/media/dvb-core/dvbdev.c:545 dvb_register_device() warn: '&dvbdev->list_head' not removed from list + +The error logic inside dvb_register_device() doesn't remove +devices from the dvb_adapter_list in case of errors. + +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Ovidiu Panait +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/dvb-core/dvbdev.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/media/dvb-core/dvbdev.c ++++ b/drivers/media/dvb-core/dvbdev.c +@@ -511,6 +511,7 @@ int dvb_register_device(struct dvb_adapt + break; + + if (minor == MAX_DVB_MINORS) { ++ list_del (&dvbdev->list_head); + kfree(dvbdevfops); + kfree(dvbdev); + up_write(&minor_rwsem); +@@ -531,6 +532,7 @@ int dvb_register_device(struct dvb_adapt + __func__); + + dvb_media_device_free(dvbdev); ++ list_del (&dvbdev->list_head); + kfree(dvbdevfops); + kfree(dvbdev); + mutex_unlock(&dvbdev_register_lock); +@@ -546,6 +548,7 @@ int dvb_register_device(struct dvb_adapt + pr_err("%s: failed to create device dvb%d.%s%d (%ld)\n", + __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); + dvb_media_device_free(dvbdev); ++ list_del (&dvbdev->list_head); + kfree(dvbdevfops); + kfree(dvbdev); + return PTR_ERR(clsdev); diff --git a/queue-5.10/media-dvbdev-fix-memleak-in-dvb_register_device.patch b/queue-5.10/media-dvbdev-fix-memleak-in-dvb_register_device.patch new file mode 100644 index 00000000000..68e14a61a3d --- /dev/null +++ b/queue-5.10/media-dvbdev-fix-memleak-in-dvb_register_device.patch @@ -0,0 +1,36 @@ +From stable-owner@vger.kernel.org Mon Jun 12 15:40:18 2023 +From: ovidiu.panait@windriver.com +Date: Mon, 12 Jun 2023 16:39:05 +0300 +Subject: media: dvbdev: Fix memleak in dvb_register_device +To: stable@vger.kernel.org +Cc: Dinghao Liu , Sean Young , Mauro Carvalho Chehab , Ovidiu Panait +Message-ID: <20230612133907.2999114-1-ovidiu.panait@windriver.com> + +From: Dinghao Liu + +commit 167faadfcf9339088910e9e85a1b711fcbbef8e9 upstream. + +When device_create() fails, dvbdev and dvbdevfops should +be freed just like when dvb_register_media_device() fails. + +Signed-off-by: Dinghao Liu +Signed-off-by: Sean Young +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Ovidiu Panait +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/dvb-core/dvbdev.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/media/dvb-core/dvbdev.c ++++ b/drivers/media/dvb-core/dvbdev.c +@@ -545,6 +545,9 @@ int dvb_register_device(struct dvb_adapt + if (IS_ERR(clsdev)) { + pr_err("%s: failed to create device dvb%d.%s%d (%ld)\n", + __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); ++ dvb_media_device_free(dvbdev); ++ kfree(dvbdevfops); ++ kfree(dvbdev); + return PTR_ERR(clsdev); + } + dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", diff --git a/queue-5.10/series b/queue-5.10/series index d207fd8dab7..ee1739914c4 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -82,3 +82,6 @@ batman-adv-switch-to-kstrtox.h-for-kstrtou64.patch mmc-block-ensure-error-propagation-for-non-blk.patch mm-memory_hotplug-extend-offline_and_remove_memory-to-handle-more-than-one-memory-block.patch nilfs2-reject-devices-with-insufficient-block-count.patch +media-dvbdev-fix-memleak-in-dvb_register_device.patch +media-dvbdev-fix-error-logic-at-dvb_register_device.patch +media-dvb-core-fix-use-after-free-due-to-race-at-dvb_register_device.patch