Skip to content

Commit

Permalink
media: dvb-core: Fix use-after-free due on race condition at dvb_net
Browse files Browse the repository at this point in the history
[ Upstream commit 4172385 ]

A race condition may occur between the .disconnect function, which
is called when the device is disconnected, and the dvb_device_open()
function, which is called when the device node is open()ed.
This results in several types of UAFs.

The root cause of this is that you use the dvb_device_open() function,
which does not implement a conditional statement
that checks 'dvbnet->exit'.

So, add 'remove_mutex` to protect 'dvbnet->exit' and use
locked_dvb_net_open() function to check 'dvbnet->exit'.

[mchehab: fix a checkpatch warning]

Link: https://lore.kernel.org/linux-media/20221117045925.14297-3-imv4bel@gmail.com
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
V4bel authored and gregkh committed Jun 9, 2023
1 parent bf3b6f8 commit 93b5dfe
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 3 deletions.
38 changes: 35 additions & 3 deletions drivers/media/dvb-core/dvb_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -1564,23 +1564,51 @@ static long dvb_net_ioctl(struct file *file,
return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl);
}

static int locked_dvb_net_open(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
struct dvb_net *dvbnet = dvbdev->priv;
int ret;

if (mutex_lock_interruptible(&dvbnet->remove_mutex))
return -ERESTARTSYS;

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

ret = dvb_generic_open(inode, file);

mutex_unlock(&dvbnet->remove_mutex);

return ret;
}

static int dvb_net_close(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
struct dvb_net *dvbnet = dvbdev->priv;

mutex_lock(&dvbnet->remove_mutex);

dvb_generic_release(inode, file);

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

return 0;
}


static const struct file_operations dvb_net_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = dvb_net_ioctl,
.open = dvb_generic_open,
.open = locked_dvb_net_open,
.release = dvb_net_close,
.llseek = noop_llseek,
};
Expand All @@ -1599,10 +1627,13 @@ void dvb_net_release (struct dvb_net *dvbnet)
{
int i;

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

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

dvb_unregister_device(dvbnet->dvbdev);

Expand All @@ -1621,6 +1652,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
int i;

mutex_init(&dvbnet->ioctl_mutex);
mutex_init(&dvbnet->remove_mutex);
dvbnet->demux = dmx;

for (i=0; i<DVB_NET_DEVICES_MAX; i++)
Expand Down
4 changes: 4 additions & 0 deletions include/media/dvb_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
* @exit: flag to indicate when the device is being removed.
* @demux: pointer to &struct dmx_demux.
* @ioctl_mutex: protect access to this struct.
* @remove_mutex: mutex that avoids a race condition between a callback
* called when the hardware is disconnected and the
* file_operations of dvb_net.
*
* Currently, the core supports up to %DVB_NET_DEVICES_MAX (10) network
* devices.
Expand All @@ -53,6 +56,7 @@ struct dvb_net {
unsigned int exit:1;
struct dmx_demux *demux;
struct mutex ioctl_mutex;
struct mutex remove_mutex;
};

/**
Expand Down

0 comments on commit 93b5dfe

Please sign in to comment.