Takashi Iwai 33f605
From 280a8ab81733da8bc442253c700a52c4c0886ffd Mon Sep 17 00:00:00 2001
Takashi Iwai 33f605
From: Hyunwoo Kim <v4bel@theori.io>
Takashi Iwai 33f605
Date: Mon, 21 Nov 2022 06:33:08 +0000
Takashi Iwai 33f605
Subject: [PATCH] media: dvb-core: Fix use-after-free due to race condition at dvb_ca_en50221
Takashi Iwai 33f605
Git-commit: 280a8ab81733da8bc442253c700a52c4c0886ffd
Takashi Iwai 33f605
Patch-mainline: v6.4-rc3
Takashi Iwai 33f605
References: CVE-2022-45919 bsc#1205803
Takashi Iwai 33f605
Takashi Iwai 33f605
If the device node of dvb_ca_en50221 is open() and the
Takashi Iwai 33f605
device is disconnected, a UAF may occur when calling
Takashi Iwai 33f605
close() on the device node.
Takashi Iwai 33f605
Takashi Iwai 33f605
The root cause is that wake_up() and wait_event() for
Takashi Iwai 33f605
dvbdev->wait_queue are not implemented.
Takashi Iwai 33f605
Takashi Iwai 33f605
So implement wait_event() function in dvb_ca_en50221_release()
Takashi Iwai 33f605
and add 'remove_mutex' which prevents race condition
Takashi Iwai 33f605
for 'ca->exit'.
Takashi Iwai 33f605
Takashi Iwai 33f605
[mchehab: fix a checkpatch warning]
Takashi Iwai 33f605
Takashi Iwai 33f605
Link: https://lore.kernel.org/linux-media/20221121063308.GA33821@ubuntu
Takashi Iwai 33f605
Signed-off-by: Hyunwoo Kim <v4bel@theori.io>
Takashi Iwai 33f605
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Takashi Iwai 33f605
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 33f605
Takashi Iwai 33f605
---
Takashi Iwai 33f605
 drivers/media/dvb-core/dvb_ca_en50221.c | 37 ++++++++++++++++++++++++-
Takashi Iwai 33f605
 1 file changed, 36 insertions(+), 1 deletion(-)
Takashi Iwai 33f605
Takashi Iwai 33f605
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
Takashi Iwai 33f605
index b6ca29dfb184..baf64540dc00 100644
Takashi Iwai 33f605
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
Takashi Iwai 33f605
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
Takashi Iwai 33f605
@@ -151,6 +151,12 @@ struct dvb_ca_private {
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 	/* mutex serializing ioctls */
Takashi Iwai 33f605
 	struct mutex ioctl_mutex;
Takashi Iwai 33f605
+
Takashi Iwai 33f605
+	/* A mutex used when a device is disconnected */
Takashi Iwai 33f605
+	struct mutex remove_mutex;
Takashi Iwai 33f605
+
Takashi Iwai 33f605
+	/* Whether the device is disconnected */
Takashi Iwai 33f605
+	int exit;
Takashi Iwai 33f605
 };
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 static void dvb_ca_private_free(struct dvb_ca_private *ca)
Takashi Iwai 33f605
@@ -1711,12 +1717,22 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 	dprintk("%s\n", __func__);
Takashi Iwai 33f605
 
Takashi Iwai 33f605
-	if (!try_module_get(ca->pub->owner))
Takashi Iwai 33f605
+	mutex_lock(&ca->remove_mutex);
Takashi Iwai 33f605
+
Takashi Iwai 33f605
+	if (ca->exit) {
Takashi Iwai 33f605
+		mutex_unlock(&ca->remove_mutex);
Takashi Iwai 33f605
+		return -ENODEV;
Takashi Iwai 33f605
+	}
Takashi Iwai 33f605
+
Takashi Iwai 33f605
+	if (!try_module_get(ca->pub->owner)) {
Takashi Iwai 33f605
+		mutex_unlock(&ca->remove_mutex);
Takashi Iwai 33f605
 		return -EIO;
Takashi Iwai 33f605
+	}
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 	err = dvb_generic_open(inode, file);
Takashi Iwai 33f605
 	if (err < 0) {
Takashi Iwai 33f605
 		module_put(ca->pub->owner);
Takashi Iwai 33f605
+		mutex_unlock(&ca->remove_mutex);
Takashi Iwai 33f605
 		return err;
Takashi Iwai 33f605
 	}
Takashi Iwai 33f605
 
Takashi Iwai 33f605
@@ -1741,6 +1757,7 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 	dvb_ca_private_get(ca);
Takashi Iwai 33f605
 
Takashi Iwai 33f605
+	mutex_unlock(&ca->remove_mutex);
Takashi Iwai 33f605
 	return 0;
Takashi Iwai 33f605
 }
Takashi Iwai 33f605
 
Takashi Iwai 33f605
@@ -1760,6 +1777,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 	dprintk("%s\n", __func__);
Takashi Iwai 33f605
 
Takashi Iwai 33f605
+	mutex_lock(&ca->remove_mutex);
Takashi Iwai 33f605
+
Takashi Iwai 33f605
 	/* mark the CA device as closed */
Takashi Iwai 33f605
 	ca->open = 0;
Takashi Iwai 33f605
 	dvb_ca_en50221_thread_update_delay(ca);
Takashi Iwai 33f605
@@ -1770,6 +1789,13 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 	dvb_ca_private_put(ca);
Takashi Iwai 33f605
 
Takashi Iwai 33f605
+	if (dvbdev->users == 1 && ca->exit == 1) {
Takashi Iwai 33f605
+		mutex_unlock(&ca->remove_mutex);
Takashi Iwai 33f605
+		wake_up(&dvbdev->wait_queue);
Takashi Iwai 33f605
+	} else {
Takashi Iwai 33f605
+		mutex_unlock(&ca->remove_mutex);
Takashi Iwai 33f605
+	}
Takashi Iwai 33f605
+
Takashi Iwai 33f605
 	return err;
Takashi Iwai 33f605
 }
Takashi Iwai 33f605
 
Takashi Iwai 33f605
@@ -1893,6 +1919,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
Takashi Iwai 33f605
 	}
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 	mutex_init(&ca->ioctl_mutex);
Takashi Iwai 33f605
+	mutex_init(&ca->remove_mutex);
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 	if (signal_pending(current)) {
Takashi Iwai 33f605
 		ret = -EINTR;
Takashi Iwai 33f605
@@ -1935,6 +1962,14 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
Takashi Iwai 33f605
 
Takashi Iwai 33f605
 	dprintk("%s\n", __func__);
Takashi Iwai 33f605
 
Takashi Iwai 33f605
+	mutex_lock(&ca->remove_mutex);
Takashi Iwai 33f605
+	ca->exit = 1;
Takashi Iwai 33f605
+	mutex_unlock(&ca->remove_mutex);
Takashi Iwai 33f605
+
Takashi Iwai 33f605
+	if (ca->dvbdev->users < 1)
Takashi Iwai 33f605
+		wait_event(ca->dvbdev->wait_queue,
Takashi Iwai 33f605
+				ca->dvbdev->users == 1);
Takashi Iwai 33f605
+
Takashi Iwai 33f605
 	/* shutdown the thread if there was one */
Takashi Iwai 33f605
 	kthread_stop(ca->thread);
Takashi Iwai 33f605
 
Takashi Iwai 33f605
-- 
Takashi Iwai 33f605
2.35.3
Takashi Iwai 33f605