Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2022-45884: [PATCH 3/4] media: dvb-core: Fix use-after-free due to race condition occurring in dvb_register_device()

An issue was discovered in the Linux kernel through 6.0.9. drivers/media/dvb-core/dvbdev.c has a use-after-free, related to dvb_register_device dynamically allocating fops.

CVE
#vulnerability#linux#git

From: [email protected] To: [email protected] Cc: Hyunwoo Kim [email protected], [email protected], [email protected], [email protected], [email protected], [email protected] Subject: [PATCH 3/4] media: dvb-core: Fix use-after-free due to race condition occurring in dvb_register_device() Date: Tue, 15 Nov 2022 05:18:21 -0800 [thread overview] Message-ID: [email protected] (raw) In-Reply-To: <[email protected]>

From: Hyunwoo Kim [email protected]

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.

Signed-off-by: Hyunwoo Kim [email protected]

drivers/media/dvb-core/dvbdev.c | 83 +++++++++++++++++++++++±-------- include/media/dvbdev.h | 15 ++++++ 2 files changed, 77 insertions(+), 21 deletions(-)

diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c index 675d877a67b2…424cf92c068e 100644 — a/drivers/media/dvb-core/dvbdev.c +++ b/drivers/media/dvb-core/dvbdev.c @@ -27,6 +27,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); @@ -448,14 +449,15 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, 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, *new_node; 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__); @@ -463,18 +465,45 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, }

    *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)); @@ -484,20 +513,20 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, 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); @@ -506,41 +535,46 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, #else minor = nums2minor(adap->num, type, id); #endif - dvbdev->minor = minor; dvb_minors[minor] = 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); 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); @@ -569,7 +603,6 @@ void dvb_free_device(struct dvb_device *dvbdev) if (!dvbdev) return;

- kfree (dvbdev->fops); kfree (dvbdev); } EXPORT_SYMBOL(dvb_free_device); @@ -1061,9 +1094,17 @@ static int __init init_dvbdev(void)

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); diff --git a/include/media/dvbdev.h b/include/media/dvbdev.h index 2f6b0861322a…1e5413303705 100644 — a/include/media/dvbdev.h +++ b/include/media/dvbdev.h @@ -187,6 +187,21 @@ struct dvb_device { void *priv; };

+/**

  • * 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_register_adapter - Registers a new DVB adapter * – 2.25.1

next prev parent reply other threads:[~2022-11-15 13:19 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top 2022-11-15 13:18 [PATCH 0/4] Fix multiple race condition vulnerabilities in dvb-core and device driver imv4bel 2022-11-15 13:18 ` [PATCH 1/4] media: dvb-core: Fix use-after-free due to race condition occurring in dvb_frontend imv4bel 2022-11-15 13:18 ` [PATCH 2/4] media: dvb-core: Fix use-after-free due to race condition occurring in dvb_net imv4bel 2022-11-15 13:18 ` imv4bel [this message] 2022-11-17 4:16 ` [PATCH 3/4] media: dvb-core: Fix use-after-free due to race condition occurring in dvb_register_device() Dan Carpenter 2022-11-15 13:18 ` [PATCH 4/4] media: ttusb-dec: Fix memory leak in ttusb_dec_exit_dvb() imv4bel

Reply instructions:

You may reply publicly to this message via plain-text email using any one of the following methods:

* Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox

Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the –to, –cc, and –in-reply-to switches of git-send-email(1):

git send-email \ –[email protected] \ –[email protected] \ –[email protected] \ –[email protected] \ –[email protected] \ –[email protected] \ –[email protected] \ –[email protected] \ /path/to/YOUR_REPLY

https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link

Be sure your reply has a Subject: header at the top and a blank line before the message body.

This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).

Related news

Red Hat Security Advisory 2023-7549-01

Red Hat Security Advisory 2023-7549-01 - An update for kernel is now available for Red Hat Enterprise Linux 8. Issues addressed include a use-after-free vulnerability.

Red Hat Security Advisory 2023-7548-01

Red Hat Security Advisory 2023-7548-01 - An update for kernel-rt is now available for Red Hat Enterprise Linux 8. Issues addressed include a use-after-free vulnerability.

Red Hat Security Advisory 2023-7539-01

Red Hat Security Advisory 2023-7539-01 - An update for kernel is now available for Red Hat Enterprise Linux 8.8 Extended Update Support. Issues addressed include a use-after-free vulnerability.

CVE-2022-45886: November 2022 Linux Kernel 6.0.9 Vulnerabilities in NetApp Products

An issue was discovered in the Linux kernel through 6.0.9. drivers/media/dvb-core/dvb_net.c has a .disconnect versus dvb_device_open race condition that leads to a use-after-free.

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907