]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Jun 2023 07:50:07 +0000 (09:50 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Jun 2023 07:50:07 +0000 (09:50 +0200)
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

queue-5.10/media-dvb-core-fix-use-after-free-due-to-race-at-dvb_register_device.patch [new file with mode: 0644]
queue-5.10/media-dvbdev-fix-error-logic-at-dvb_register_device.patch [new file with mode: 0644]
queue-5.10/media-dvbdev-fix-memleak-in-dvb_register_device.patch [new file with mode: 0644]
queue-5.10/series

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 (file)
index 0000000..c29fcee
--- /dev/null
@@ -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 <imv4bel@gmail.com>, kernel test robot <lkp@intel.com>, Dan Carpenter <error27@gmail.com>, Mauro Carvalho Chehab <mchehab@kernel.org>, Ovidiu Panait <ovidiu.panait@windriver.com>
+Message-ID: <20230612133907.2999114-3-ovidiu.panait@windriver.com>
+
+From: Hyunwoo Kim <imv4bel@gmail.com>
+
+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 <imv4bel@gmail.com>
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <error27@gmail.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
+Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 <media/tuner.h>
+ 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 (file)
index 0000000..dbf0dc7
--- /dev/null
@@ -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 <mchehab+huawei@kernel.org>, Ovidiu Panait <ovidiu.panait@windriver.com>
+Message-ID: <20230612133907.2999114-2-ovidiu.panait@windriver.com>
+
+From: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+
+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 <mchehab+huawei@kernel.org>
+Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..68e14a6
--- /dev/null
@@ -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 <dinghao.liu@zju.edu.cn>, Sean Young <sean@mess.org>, Mauro Carvalho Chehab <mchehab+huawei@kernel.org>, Ovidiu Panait <ovidiu.panait@windriver.com>
+Message-ID: <20230612133907.2999114-1-ovidiu.panait@windriver.com>
+
+From: Dinghao Liu <dinghao.liu@zju.edu.cn>
+
+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 <dinghao.liu@zju.edu.cn>
+Signed-off-by: Sean Young <sean@mess.org>
+Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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",
index d207fd8dab725175322ece544217b21bf353c1c5..ee1739914c4fe6f934a6167e50ce2e6582a11c61 100644 (file)
@@ -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