media: dvb-core: Fix use-after-free due to race condition at dvb_ca_en50221
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>
This commit is contained in:
parent
b8c75e4a1b
commit
280a8ab817
@ -151,6 +151,12 @@ struct dvb_ca_private {
|
|||||||
|
|
||||||
/* mutex serializing ioctls */
|
/* mutex serializing ioctls */
|
||||||
struct mutex ioctl_mutex;
|
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)
|
static void dvb_ca_private_free(struct dvb_ca_private *ca)
|
||||||
@ -1711,12 +1717,22 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
|
|||||||
|
|
||||||
dprintk("%s\n", __func__);
|
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;
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
err = dvb_generic_open(inode, file);
|
err = dvb_generic_open(inode, file);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
module_put(ca->pub->owner);
|
module_put(ca->pub->owner);
|
||||||
|
mutex_unlock(&ca->remove_mutex);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1741,6 +1757,7 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
|
|||||||
|
|
||||||
dvb_ca_private_get(ca);
|
dvb_ca_private_get(ca);
|
||||||
|
|
||||||
|
mutex_unlock(&ca->remove_mutex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1760,6 +1777,8 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
|
|||||||
|
|
||||||
dprintk("%s\n", __func__);
|
dprintk("%s\n", __func__);
|
||||||
|
|
||||||
|
mutex_lock(&ca->remove_mutex);
|
||||||
|
|
||||||
/* mark the CA device as closed */
|
/* mark the CA device as closed */
|
||||||
ca->open = 0;
|
ca->open = 0;
|
||||||
dvb_ca_en50221_thread_update_delay(ca);
|
dvb_ca_en50221_thread_update_delay(ca);
|
||||||
@ -1770,6 +1789,13 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
|
|||||||
|
|
||||||
dvb_ca_private_put(ca);
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1893,6 +1919,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
mutex_init(&ca->ioctl_mutex);
|
mutex_init(&ca->ioctl_mutex);
|
||||||
|
mutex_init(&ca->remove_mutex);
|
||||||
|
|
||||||
if (signal_pending(current)) {
|
if (signal_pending(current)) {
|
||||||
ret = -EINTR;
|
ret = -EINTR;
|
||||||
@ -1935,6 +1962,14 @@ void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
|
|||||||
|
|
||||||
dprintk("%s\n", __func__);
|
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 */
|
/* shutdown the thread if there was one */
|
||||||
kthread_stop(ca->thread);
|
kthread_stop(ca->thread);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user