Commit 13bfac7c authored by Zhipeng Lu's avatar Zhipeng Lu Committed by liwei
Browse files

media: edia: dvbdev: fix a use-after-free

mainline inclusion
from mainline-v6.9-rc1
commit 8c64f4cdf4e6cc5682c52523713af8c39c94e6d5
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I9L9MA
CVE: CVE-2024-27043

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8c64f4cdf4e6cc5682c52523713af8c39c94e6d5

------------------------------------------------------

In dvb_register_device, *pdvbdev is set equal to dvbdev, which is freed
in several error-handling paths. However, *pdvbdev is not set to NULL
after dvbdev's deallocation, causing use-after-frees in many places,
for example, in the following call chain:

budget_register
  |-> dvb_dmxdev_init
        |-> dvb_register_device
  |-> dvb_dmxdev_release
        |-> dvb_unregister_device
              |-> dvb_remove_device
                    |-> dvb_device_put
                          |-> kref_put

When calling dvb_unregister_device, dmxdev->dvbdev (i.e. *pdvbdev in
dvb_register_device) could point to memory that had been freed in
dvb_register_device. Thereafter, this pointer is transferred to
kref_put and triggering a use-after-free.

Link: https://lore.kernel.org/linux-media/20240203134046.3120099-1-alexious@zju.edu.cn


Fixes: b6190102 ("V4L/DVB (5244): Dvbdev: fix illegal re-usage of fileoperations struct")
Signed-off-by: default avatarZhipeng Lu <alexious@zju.edu.cn>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>

Conflicts:
	drivers/media/dvb-core/dvbdev.c
Signed-off-by: default avatarliwei <liwei728@huawei.com>
parent b9f13a64
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -498,6 +498,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
		dvbdevfops = kmemdup(template->fops, sizeof(*dvbdevfops), GFP_KERNEL);
		if (!dvbdevfops) {
			kfree(dvbdev);
			*pdvbdev = NULL;
			mutex_unlock(&dvbdev_register_lock);
			return -ENOMEM;
		}
@@ -506,6 +507,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
		if (!new_node) {
			kfree(dvbdevfops);
			kfree(dvbdev);
			*pdvbdev = NULL;
			mutex_unlock(&dvbdev_register_lock);
			return -ENOMEM;
		}
@@ -537,6 +539,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
			kfree(new_node);
		}
		kfree(dvbdev);
		*pdvbdev = NULL;
		up_write(&minor_rwsem);
		mutex_unlock(&dvbdev_register_lock);
		return -EINVAL;
@@ -558,6 +561,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
		}
		dvb_media_device_free(dvbdev);
		kfree(dvbdev);
		*pdvbdev = NULL;
		up_write(&minor_rwsem);
		mutex_unlock(&dvbdev_register_lock);
		return ret;
@@ -576,6 +580,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
		}
		dvb_media_device_free(dvbdev);
		kfree(dvbdev);
		*pdvbdev = NULL;
		mutex_unlock(&dvbdev_register_lock);
		return PTR_ERR(clsdev);
	}