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