diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 6b84c3413e83..a1bf488af4fb 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2153,13 +2153,17 @@ static int em28xx_v4l2_open(struct file *filp) return -EINVAL; } + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + if (!dev->v4l2) { + mutex_unlock(&dev->lock); + return -ENOMEM; + } + em28xx_videodbg("open dev=%s type=%s users=%d\n", video_device_node_name(vdev), v4l2_type_names[fh_type], v4l2->users); - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - ret = v4l2_fh_open(filp); if (ret) { dev_err(&dev->intf->dev, diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index a593ea0598b5..4687a5761845 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -424,6 +424,15 @@ static int v4l2_open(struct inode *inode, struct file *filp) else ret = -ENODEV; } + /* + * when vdev is a nested struct in some driver's struct, video_get() has no effect and + * referencing vdev might cause UAF, therefore, after returning from open() we need to + * check whether vdev has already been freed as part of a parent struct, after returning + * from open() + */ + mutex_lock(&videodev_lock); + if (!video_devdata(filp) && ret) + goto out; if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) dprintk("%s: open (%d)\n", @@ -431,6 +440,8 @@ static int v4l2_open(struct inode *inode, struct file *filp) /* decrease the refcount in case of an error */ if (ret) video_put(vdev); +out: + mutex_unlock(&videodev_lock); return ret; }