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.
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 - 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 - 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 - 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.
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.