Commit 4172385b authored by Hyunwoo Kim's avatar Hyunwoo Kim Committed by Mauro Carvalho Chehab

media: dvb-core: Fix use-after-free due on race condition at dvb_net

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.comSigned-off-by: default avatarHyunwoo Kim <imv4bel@gmail.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent 6769a0b7
...@@ -1564,15 +1564,43 @@ static long dvb_net_ioctl(struct file *file, ...@@ -1564,15 +1564,43 @@ static long dvb_net_ioctl(struct file *file,
return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl); 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) static int dvb_net_close(struct inode *inode, struct file *file)
{ {
struct dvb_device *dvbdev = file->private_data; struct dvb_device *dvbdev = file->private_data;
struct dvb_net *dvbnet = dvbdev->priv; struct dvb_net *dvbnet = dvbdev->priv;
mutex_lock(&dvbnet->remove_mutex);
dvb_generic_release(inode, file); 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); wake_up(&dvbdev->wait_queue);
} else {
mutex_unlock(&dvbnet->remove_mutex);
}
return 0; return 0;
} }
...@@ -1580,7 +1608,7 @@ static int dvb_net_close(struct inode *inode, struct file *file) ...@@ -1580,7 +1608,7 @@ static int dvb_net_close(struct inode *inode, struct file *file)
static const struct file_operations dvb_net_fops = { static const struct file_operations dvb_net_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.unlocked_ioctl = dvb_net_ioctl, .unlocked_ioctl = dvb_net_ioctl,
.open = dvb_generic_open, .open = locked_dvb_net_open,
.release = dvb_net_close, .release = dvb_net_close,
.llseek = noop_llseek, .llseek = noop_llseek,
}; };
...@@ -1599,10 +1627,13 @@ void dvb_net_release (struct dvb_net *dvbnet) ...@@ -1599,10 +1627,13 @@ void dvb_net_release (struct dvb_net *dvbnet)
{ {
int i; int i;
mutex_lock(&dvbnet->remove_mutex);
dvbnet->exit = 1; dvbnet->exit = 1;
mutex_unlock(&dvbnet->remove_mutex);
if (dvbnet->dvbdev->users < 1) if (dvbnet->dvbdev->users < 1)
wait_event(dvbnet->dvbdev->wait_queue, wait_event(dvbnet->dvbdev->wait_queue,
dvbnet->dvbdev->users==1); dvbnet->dvbdev->users == 1);
dvb_unregister_device(dvbnet->dvbdev); dvb_unregister_device(dvbnet->dvbdev);
...@@ -1621,6 +1652,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, ...@@ -1621,6 +1652,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
int i; int i;
mutex_init(&dvbnet->ioctl_mutex); mutex_init(&dvbnet->ioctl_mutex);
mutex_init(&dvbnet->remove_mutex);
dvbnet->demux = dmx; dvbnet->demux = dmx;
for (i=0; i<DVB_NET_DEVICES_MAX; i++) for (i=0; i<DVB_NET_DEVICES_MAX; i++)
......
...@@ -39,6 +39,9 @@ struct net_device; ...@@ -39,6 +39,9 @@ struct net_device;
* @exit: flag to indicate when the device is being removed. * @exit: flag to indicate when the device is being removed.
* @demux: pointer to &struct dmx_demux. * @demux: pointer to &struct dmx_demux.
* @ioctl_mutex: protect access to this struct. * @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 * Currently, the core supports up to %DVB_NET_DEVICES_MAX (10) network
* devices. * devices.
...@@ -51,6 +54,7 @@ struct dvb_net { ...@@ -51,6 +54,7 @@ struct dvb_net {
unsigned int exit:1; unsigned int exit:1;
struct dmx_demux *demux; struct dmx_demux *demux;
struct mutex ioctl_mutex; struct mutex ioctl_mutex;
struct mutex remove_mutex;
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment