Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8624c7e0 authored by Hyunwoo Kim's avatar Hyunwoo Kim Committed by Greg Kroah-Hartman
Browse files

media: dvb-core: Fix use-after-free due to race condition at dvb_ca_en50221

[ Upstream commit 280a8ab81733da8bc442253c700a52c4c0886ffd ]

If the device node of dvb_ca_en50221 is open() and the
device is disconnected, a UAF may occur when calling
close() on the device node.

The root cause is that wake_up() and wait_event() for
dvbdev->wait_queue are not implemented.

So implement wait_event() function in dvb_ca_en50221_release()
and add 'remove_mutex' which prevents race condition
for 'ca->exit'.

[mchehab: fix a checkpatch warning]

Link: https://lore.kernel.org/linux-media/20221121063308.GA33821@ubuntu


Signed-off-by: default avatarHyunwoo Kim <v4bel@theori.io>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent f3b54421
Loading
Loading
Loading
Loading
+36 −1
Original line number Diff line number Diff line
@@ -162,6 +162,12 @@ struct dvb_ca_private {

	/* mutex serializing ioctls */
	struct mutex ioctl_mutex;

	/* A mutex used when a device is disconnected */
	struct mutex remove_mutex;

	/* Whether the device is disconnected */
	int exit;
};

static void dvb_ca_private_free(struct dvb_ca_private *ca)
@@ -1719,12 +1725,22 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)

	dprintk("%s\n", __func__);

	if (!try_module_get(ca->pub->owner))
	mutex_lock(&ca->remove_mutex);

	if (ca->exit) {
		mutex_unlock(&ca->remove_mutex);
		return -ENODEV;
	}

	if (!try_module_get(ca->pub->owner)) {
		mutex_unlock(&ca->remove_mutex);
		return -EIO;
	}

	err = dvb_generic_open(inode, file);
	if (err < 0) {
		module_put(ca->pub->owner);
		mutex_unlock(&ca->remove_mutex);
		return err;
	}

@@ -1749,6 +1765,7 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)

	dvb_ca_private_get(ca);

	mutex_unlock(&ca->remove_mutex);
	return 0;
}

@@ -1768,6 +1785,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)

	dprintk("%s\n", __func__);

	mutex_lock(&ca->remove_mutex);

	/* mark the CA device as closed */
	ca->open = 0;
	dvb_ca_en50221_thread_update_delay(ca);
@@ -1778,6 +1797,13 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)

	dvb_ca_private_put(ca);

	if (dvbdev->users == 1 && ca->exit == 1) {
		mutex_unlock(&ca->remove_mutex);
		wake_up(&dvbdev->wait_queue);
	} else {
		mutex_unlock(&ca->remove_mutex);
	}

	return err;
}

@@ -1902,6 +1928,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
	}

	mutex_init(&ca->ioctl_mutex);
	mutex_init(&ca->remove_mutex);

	if (signal_pending(current)) {
		ret = -EINTR;
@@ -1944,6 +1971,14 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)

	dprintk("%s\n", __func__);

	mutex_lock(&ca->remove_mutex);
	ca->exit = 1;
	mutex_unlock(&ca->remove_mutex);

	if (ca->dvbdev->users < 1)
		wait_event(ca->dvbdev->wait_queue,
				ca->dvbdev->users == 1);

	/* shutdown the thread if there was one */
	kthread_stop(ca->thread);