summaryrefslogtreecommitdiffstats
path: root/drivers/media/usb/au0828
diff options
context:
space:
mode:
authorShuah Khan <shuahkh@osg.samsung.com>2016-11-29 21:59:54 -0200
committerMauro Carvalho Chehab <mchehab@s-opensource.com>2017-02-03 07:39:35 -0200
commit90cd366bc61cd539c797b7ad957a9d749d97200f (patch)
treec26c3643a20164385c9916c200df7244c769f24c /drivers/media/usb/au0828
parent92fbeb40b132f5b2ec335f644ba563a1a85ffd8b (diff)
downloadlinux-90cd366bc61cd539c797b7ad957a9d749d97200f.tar.gz
linux-90cd366bc61cd539c797b7ad957a9d749d97200f.tar.bz2
linux-90cd366bc61cd539c797b7ad957a9d749d97200f.zip
[media] media: Protect enable_source and disable_source handler code paths
Drivers might try to access and run enable_source and disable_source handlers when the driver that implements these handlers is clearing the handlers during its unregister. Fix the following race condition: process 1 process 2 request video streaming unbind au0828 v4l2 checks if tuner is free ... ... au0828_unregister_media_device() ... ... (doesn't hold graph_mutex) mdev->enable_source = NULL; if (mdev && mdev->enable_source) mdev->disable_source = NULL; mdev->enable_source() (enable_source holds graph_mutex) As shown above enable_source check is done without holding the graph_mutex. If unbind happens to be in progress, au0828 could clear enable_source and disable_source handlers leading to null pointer de-reference. Fix it by protecting enable_source and disable_source set and clear and protecting enable_source and disable_source handler access and the call itself. process 1 process 2 request video streaming unbind au0828 v4l2 checks if tuner is free ... ... au0828_unregister_media_device() ... ... (hold graph_mutex while clearing) mdev->enable_source = NULL; if (mdev) mdev->disable_source = NULL; (hold graph_mutex to check and call enable_source) if (mdev->enable_source) mdev->enable_source() If graph_mutex is held to just heck for handler being null and needs to be released before calling the handler, there will be another window for the handlers to be cleared. Hence, enable_source and disable_source handlers no longer hold the graph_mutex and expect callers to hold it to avoid forcing them release the graph_mutex before calling the handlers. Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'drivers/media/usb/au0828')
-rw-r--r--drivers/media/usb/au0828/au0828-core.c21
1 files changed, 9 insertions, 12 deletions
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 8c0494042d06..739df61cec4f 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -149,9 +149,11 @@ static void au0828_unregister_media_device(struct au0828_dev *dev)
}
/* clear enable_source, disable_source */
+ mutex_lock(&mdev->graph_mutex);
dev->media_dev->source_priv = NULL;
dev->media_dev->enable_source = NULL;
dev->media_dev->disable_source = NULL;
+ mutex_unlock(&mdev->graph_mutex);
media_device_unregister(dev->media_dev);
media_device_cleanup(dev->media_dev);
@@ -274,6 +276,7 @@ create_link:
}
}
+/* Callers should hold graph_mutex */
static int au0828_enable_source(struct media_entity *entity,
struct media_pipeline *pipe)
{
@@ -287,8 +290,6 @@ static int au0828_enable_source(struct media_entity *entity,
if (!mdev)
return -ENODEV;
- mutex_lock(&mdev->graph_mutex);
-
dev = mdev->source_priv;
/*
@@ -415,12 +416,12 @@ static int au0828_enable_source(struct media_entity *entity,
dev->active_source->name, dev->active_sink->name,
dev->active_link_owner->name, ret);
end:
- mutex_unlock(&mdev->graph_mutex);
pr_debug("au0828_enable_source() end %s %d %d\n",
entity->name, entity->function, ret);
return ret;
}
+/* Callers should hold graph_mutex */
static void au0828_disable_source(struct media_entity *entity)
{
int ret = 0;
@@ -430,13 +431,10 @@ static void au0828_disable_source(struct media_entity *entity)
if (!mdev)
return;
- mutex_lock(&mdev->graph_mutex);
dev = mdev->source_priv;
- if (!dev->active_link) {
- ret = -ENODEV;
- goto end;
- }
+ if (!dev->active_link)
+ return;
/* link is active - stop pipeline from source (tuner) */
if (dev->active_link->sink->entity == dev->active_sink &&
@@ -446,7 +444,7 @@ static void au0828_disable_source(struct media_entity *entity)
* has active pipeline
*/
if (dev->active_link_owner != entity)
- goto end;
+ return;
__media_pipeline_stop(entity);
ret = __media_entity_setup_link(dev->active_link, 0);
if (ret)
@@ -461,9 +459,6 @@ static void au0828_disable_source(struct media_entity *entity)
dev->active_source = NULL;
dev->active_sink = NULL;
}
-
-end:
- mutex_unlock(&mdev->graph_mutex);
}
#endif
@@ -545,9 +540,11 @@ static int au0828_media_device_register(struct au0828_dev *dev,
return ret;
}
/* set enable_source */
+ mutex_lock(&dev->media_dev->graph_mutex);
dev->media_dev->source_priv = (void *) dev;
dev->media_dev->enable_source = au0828_enable_source;
dev->media_dev->disable_source = au0828_disable_source;
+ mutex_unlock(&dev->media_dev->graph_mutex);
#endif
return 0;
}