* [PATCH 5.10 1/3] media: dvbdev: Fix memleak in dvb_register_device
@ 2023-06-12 13:39 ovidiu.panait
2023-06-12 13:39 ` [PATCH 5.10 2/3] media: dvbdev: fix error logic at dvb_register_device() ovidiu.panait
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: ovidiu.panait @ 2023-06-12 13:39 UTC (permalink / raw)
To: stable; +Cc: Dinghao Liu, Sean Young, Mauro Carvalho Chehab, Ovidiu Panait
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>
---
drivers/media/dvb-core/dvbdev.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index fea2d82300b0..52315434e0a9 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -545,6 +545,9 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
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",
--
2.39.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 5.10 2/3] media: dvbdev: fix error logic at dvb_register_device()
2023-06-12 13:39 [PATCH 5.10 1/3] media: dvbdev: Fix memleak in dvb_register_device ovidiu.panait
@ 2023-06-12 13:39 ` ovidiu.panait
2023-06-12 13:39 ` [PATCH 5.10 3/3] media: dvb-core: Fix use-after-free due to race " ovidiu.panait
2023-06-19 7:50 ` [PATCH 5.10 1/3] media: dvbdev: Fix memleak in dvb_register_device Greg KH
2 siblings, 0 replies; 4+ messages in thread
From: ovidiu.panait @ 2023-06-12 13:39 UTC (permalink / raw)
To: stable; +Cc: Mauro Carvalho Chehab, Ovidiu Panait
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>
---
drivers/media/dvb-core/dvbdev.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index 52315434e0a9..6e2b7e97da17 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -511,6 +511,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
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_adapter *adap, struct dvb_device **pdvbdev,
__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_adapter *adap, struct dvb_device **pdvbdev,
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);
--
2.39.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 5.10 3/3] media: dvb-core: Fix use-after-free due to race at dvb_register_device()
2023-06-12 13:39 [PATCH 5.10 1/3] media: dvbdev: Fix memleak in dvb_register_device ovidiu.panait
2023-06-12 13:39 ` [PATCH 5.10 2/3] media: dvbdev: fix error logic at dvb_register_device() ovidiu.panait
@ 2023-06-12 13:39 ` ovidiu.panait
2023-06-19 7:50 ` [PATCH 5.10 1/3] media: dvbdev: Fix memleak in dvb_register_device Greg KH
2 siblings, 0 replies; 4+ messages in thread
From: ovidiu.panait @ 2023-06-12 13:39 UTC (permalink / raw)
To: stable
Cc: Hyunwoo Kim, kernel test robot, Dan Carpenter,
Mauro Carvalho Chehab, Ovidiu Panait
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>
---
drivers/media/dvb-core/dvbdev.c | 84 ++++++++++++++++++++++++---------
include/media/dvbdev.h | 15 ++++++
2 files changed, 78 insertions(+), 21 deletions(-)
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index 6e2b7e97da17..2ff8a1b776fb 100644
--- 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_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 = 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_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));
@@ -499,20 +528,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);
@@ -521,41 +550,47 @@ 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] = 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 *ref)
{
struct dvb_device *dvbdev = container_of(ref, struct dvb_device, ref);
- kfree (dvbdev->fops);
kfree (dvbdev);
}
@@ -1090,9 +1124,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 c493a3ffe45c..91ff2d5fcd08 100644
--- a/include/media/dvbdev.h
+++ b/include/media/dvbdev.h
@@ -189,6 +189,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_device_get - Increase dvb_device reference
*
--
2.39.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 5.10 1/3] media: dvbdev: Fix memleak in dvb_register_device
2023-06-12 13:39 [PATCH 5.10 1/3] media: dvbdev: Fix memleak in dvb_register_device ovidiu.panait
2023-06-12 13:39 ` [PATCH 5.10 2/3] media: dvbdev: fix error logic at dvb_register_device() ovidiu.panait
2023-06-12 13:39 ` [PATCH 5.10 3/3] media: dvb-core: Fix use-after-free due to race " ovidiu.panait
@ 2023-06-19 7:50 ` Greg KH
2 siblings, 0 replies; 4+ messages in thread
From: Greg KH @ 2023-06-19 7:50 UTC (permalink / raw)
To: ovidiu.panait; +Cc: stable, Dinghao Liu, Sean Young, Mauro Carvalho Chehab
On Mon, Jun 12, 2023 at 04:39:05PM +0300, ovidiu.panait@windriver.com wrote:
> 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>
> ---
> drivers/media/dvb-core/dvbdev.c | 3 +++
> 1 file changed, 3 insertions(+)
All queued up, thanks.
greg k-h
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2023-06-19 7:50 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-12 13:39 [PATCH 5.10 1/3] media: dvbdev: Fix memleak in dvb_register_device ovidiu.panait
2023-06-12 13:39 ` [PATCH 5.10 2/3] media: dvbdev: fix error logic at dvb_register_device() ovidiu.panait
2023-06-12 13:39 ` [PATCH 5.10 3/3] media: dvb-core: Fix use-after-free due to race " ovidiu.panait
2023-06-19 7:50 ` [PATCH 5.10 1/3] media: dvbdev: Fix memleak in dvb_register_device Greg KH
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox