|
Takashi Iwai |
042e8e |
From 4172385b0c9ac366dcab78eda48c26814b87ed1a Mon Sep 17 00:00:00 2001
|
|
Takashi Iwai |
042e8e |
From: Hyunwoo Kim <imv4bel@gmail.com>
|
|
Takashi Iwai |
042e8e |
Date: Thu, 17 Nov 2022 04:59:23 +0000
|
|
Takashi Iwai |
042e8e |
Subject: [PATCH] media: dvb-core: Fix use-after-free due on race condition at dvb_net
|
|
Takashi Iwai |
042e8e |
Git-commit: 4172385b0c9ac366dcab78eda48c26814b87ed1a
|
|
Takashi Iwai |
042e8e |
Patch-mainline: v6.4-rc3
|
|
Takashi Iwai |
042e8e |
References: CVE-2022-45886 bsc#1205760
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
A race condition may occur between the .disconnect function, which
|
|
Takashi Iwai |
042e8e |
is called when the device is disconnected, and the dvb_device_open()
|
|
Takashi Iwai |
042e8e |
function, which is called when the device node is open()ed.
|
|
Takashi Iwai |
042e8e |
This results in several types of UAFs.
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
The root cause of this is that you use the dvb_device_open() function,
|
|
Takashi Iwai |
042e8e |
which does not implement a conditional statement
|
|
Takashi Iwai |
042e8e |
that checks 'dvbnet->exit'.
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
So, add 'remove_mutex` to protect 'dvbnet->exit' and use
|
|
Takashi Iwai |
042e8e |
locked_dvb_net_open() function to check 'dvbnet->exit'.
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
[mchehab: fix a checkpatch warning]
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
Link: https://lore.kernel.org/linux-media/20221117045925.14297-3-imv4bel@gmail.com
|
|
Takashi Iwai |
042e8e |
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
|
|
Takashi Iwai |
042e8e |
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
|
|
Takashi Iwai |
042e8e |
Acked-by: Takashi Iwai <tiwai@suse.de>
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
---
|
|
Takashi Iwai |
042e8e |
drivers/media/dvb-core/dvb_net.c | 38 +++++++++++++++++++++++++++++---
|
|
Takashi Iwai |
042e8e |
include/media/dvb_net.h | 4 ++++
|
|
Takashi Iwai |
042e8e |
2 files changed, 39 insertions(+), 3 deletions(-)
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
|
|
Takashi Iwai |
042e8e |
index 8a2febf33ce2..8bb8dd34c223 100644
|
|
Takashi Iwai |
042e8e |
--- a/drivers/media/dvb-core/dvb_net.c
|
|
Takashi Iwai |
042e8e |
+++ b/drivers/media/dvb-core/dvb_net.c
|
|
Takashi Iwai |
042e8e |
@@ -1564,15 +1564,43 @@ static long dvb_net_ioctl(struct file *file,
|
|
Takashi Iwai |
042e8e |
return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl);
|
|
Takashi Iwai |
042e8e |
}
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
+static int locked_dvb_net_open(struct inode *inode, struct file *file)
|
|
Takashi Iwai |
042e8e |
+{
|
|
Takashi Iwai |
042e8e |
+ struct dvb_device *dvbdev = file->private_data;
|
|
Takashi Iwai |
042e8e |
+ struct dvb_net *dvbnet = dvbdev->priv;
|
|
Takashi Iwai |
042e8e |
+ int ret;
|
|
Takashi Iwai |
042e8e |
+
|
|
Takashi Iwai |
042e8e |
+ if (mutex_lock_interruptible(&dvbnet->remove_mutex))
|
|
Takashi Iwai |
042e8e |
+ return -ERESTARTSYS;
|
|
Takashi Iwai |
042e8e |
+
|
|
Takashi Iwai |
042e8e |
+ if (dvbnet->exit) {
|
|
Takashi Iwai |
042e8e |
+ mutex_unlock(&dvbnet->remove_mutex);
|
|
Takashi Iwai |
042e8e |
+ return -ENODEV;
|
|
Takashi Iwai |
042e8e |
+ }
|
|
Takashi Iwai |
042e8e |
+
|
|
Takashi Iwai |
042e8e |
+ ret = dvb_generic_open(inode, file);
|
|
Takashi Iwai |
042e8e |
+
|
|
Takashi Iwai |
042e8e |
+ mutex_unlock(&dvbnet->remove_mutex);
|
|
Takashi Iwai |
042e8e |
+
|
|
Takashi Iwai |
042e8e |
+ return ret;
|
|
Takashi Iwai |
042e8e |
+}
|
|
Takashi Iwai |
042e8e |
+
|
|
Takashi Iwai |
042e8e |
static int dvb_net_close(struct inode *inode, struct file *file)
|
|
Takashi Iwai |
042e8e |
{
|
|
Takashi Iwai |
042e8e |
struct dvb_device *dvbdev = file->private_data;
|
|
Takashi Iwai |
042e8e |
struct dvb_net *dvbnet = dvbdev->priv;
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
+ mutex_lock(&dvbnet->remove_mutex);
|
|
Takashi Iwai |
042e8e |
+
|
|
Takashi Iwai |
042e8e |
dvb_generic_release(inode, file);
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
- if(dvbdev->users == 1 && dvbnet->exit == 1)
|
|
Takashi Iwai |
042e8e |
+ if (dvbdev->users == 1 && dvbnet->exit == 1) {
|
|
Takashi Iwai |
042e8e |
+ mutex_unlock(&dvbnet->remove_mutex);
|
|
Takashi Iwai |
042e8e |
wake_up(&dvbdev->wait_queue);
|
|
Takashi Iwai |
042e8e |
+ } else {
|
|
Takashi Iwai |
042e8e |
+ mutex_unlock(&dvbnet->remove_mutex);
|
|
Takashi Iwai |
042e8e |
+ }
|
|
Takashi Iwai |
042e8e |
+
|
|
Takashi Iwai |
042e8e |
return 0;
|
|
Takashi Iwai |
042e8e |
}
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
@@ -1580,7 +1608,7 @@ static int dvb_net_close(struct inode *inode, struct file *file)
|
|
Takashi Iwai |
042e8e |
static const struct file_operations dvb_net_fops = {
|
|
Takashi Iwai |
042e8e |
.owner = THIS_MODULE,
|
|
Takashi Iwai |
042e8e |
.unlocked_ioctl = dvb_net_ioctl,
|
|
Takashi Iwai |
042e8e |
- .open = dvb_generic_open,
|
|
Takashi Iwai |
042e8e |
+ .open = locked_dvb_net_open,
|
|
Takashi Iwai |
042e8e |
.release = dvb_net_close,
|
|
Takashi Iwai |
042e8e |
.llseek = noop_llseek,
|
|
Takashi Iwai |
042e8e |
};
|
|
Takashi Iwai |
042e8e |
@@ -1599,10 +1627,13 @@ void dvb_net_release (struct dvb_net *dvbnet)
|
|
Takashi Iwai |
042e8e |
{
|
|
Takashi Iwai |
042e8e |
int i;
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
+ mutex_lock(&dvbnet->remove_mutex);
|
|
Takashi Iwai |
042e8e |
dvbnet->exit = 1;
|
|
Takashi Iwai |
042e8e |
+ mutex_unlock(&dvbnet->remove_mutex);
|
|
Takashi Iwai |
042e8e |
+
|
|
Takashi Iwai |
042e8e |
if (dvbnet->dvbdev->users < 1)
|
|
Takashi Iwai |
042e8e |
wait_event(dvbnet->dvbdev->wait_queue,
|
|
Takashi Iwai |
042e8e |
- dvbnet->dvbdev->users==1);
|
|
Takashi Iwai |
042e8e |
+ dvbnet->dvbdev->users == 1);
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
dvb_unregister_device(dvbnet->dvbdev);
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
@@ -1621,6 +1652,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
|
|
Takashi Iwai |
042e8e |
int i;
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
mutex_init(&dvbnet->ioctl_mutex);
|
|
Takashi Iwai |
042e8e |
+ mutex_init(&dvbnet->remove_mutex);
|
|
Takashi Iwai |
042e8e |
dvbnet->demux = dmx;
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
for (i=0; i
|
|
Takashi Iwai |
042e8e |
diff --git a/include/media/dvb_net.h b/include/media/dvb_net.h
|
|
Takashi Iwai |
042e8e |
index 9980b1dd750b..4a921ea96091 100644
|
|
Takashi Iwai |
042e8e |
--- a/include/media/dvb_net.h
|
|
Takashi Iwai |
042e8e |
+++ b/include/media/dvb_net.h
|
|
Takashi Iwai |
042e8e |
@@ -39,6 +39,9 @@ struct net_device;
|
|
Takashi Iwai |
042e8e |
* @exit: flag to indicate when the device is being removed.
|
|
Takashi Iwai |
042e8e |
* @demux: pointer to &struct dmx_demux.
|
|
Takashi Iwai |
042e8e |
* @ioctl_mutex: protect access to this struct.
|
|
Takashi Iwai |
042e8e |
+ * @remove_mutex: mutex that avoids a race condition between a callback
|
|
Takashi Iwai |
042e8e |
+ * called when the hardware is disconnected and the
|
|
Takashi Iwai |
042e8e |
+ * file_operations of dvb_net.
|
|
Takashi Iwai |
042e8e |
*
|
|
Takashi Iwai |
042e8e |
* Currently, the core supports up to %DVB_NET_DEVICES_MAX (10) network
|
|
Takashi Iwai |
042e8e |
* devices.
|
|
Takashi Iwai |
042e8e |
@@ -51,6 +54,7 @@ struct dvb_net {
|
|
Takashi Iwai |
042e8e |
unsigned int exit:1;
|
|
Takashi Iwai |
042e8e |
struct dmx_demux *demux;
|
|
Takashi Iwai |
042e8e |
struct mutex ioctl_mutex;
|
|
Takashi Iwai |
042e8e |
+ struct mutex remove_mutex;
|
|
Takashi Iwai |
042e8e |
};
|
|
Takashi Iwai |
042e8e |
|
|
Takashi Iwai |
042e8e |
/**
|
|
Takashi Iwai |
042e8e |
--
|
|
Takashi Iwai |
042e8e |
2.35.3
|
|
Takashi Iwai |
042e8e |
|