Skip to content

Commit

Permalink
media: dvb-core: Fix use-after-free due to race condition at dvb_ca_e…
Browse files Browse the repository at this point in the history
…n50221

[ Upstream commit 280a8ab ]

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: Hyunwoo Kim <v4bel@theori.io>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Hyunwoo Kim authored and gregkh committed Jun 9, 2023
1 parent d0088ea commit d5d61f7
Showing 1 changed file with 36 additions and 1 deletion.
37 changes: 36 additions & 1 deletion drivers/media/dvb-core/dvb_ca_en50221.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,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)
Expand Down Expand Up @@ -1711,12 +1717,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;
}

Expand All @@ -1741,6 +1757,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;
}

Expand All @@ -1760,6 +1777,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);
Expand All @@ -1770,6 +1789,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;
}

Expand Down Expand Up @@ -1893,6 +1919,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;
Expand Down Expand Up @@ -1935,6 +1962,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);

Expand Down

0 comments on commit d5d61f7

Please sign in to comment.