Commit df4a3e7f authored by Pi-Hsun Shih's avatar Pi-Hsun Shih Committed by Mauro Carvalho Chehab

media: v4l2-ctrl: Lock main_hdl on operations of requests_queued.

There's a race condition between the list_del_init in the
v4l2_ctrl_request_complete, and the list_add_tail in the
v4l2_ctrl_request_queue, since they can be called in different thread
and the requests_queued list is not protected by a lock. This can lead
to that the v4l2_ctrl_handler is still in the requests_queued list while
the request_is_queued is already set to false, which would cause
use-after-free if the v4l2_ctrl_handler is later released.

Fix this by locking the ->lock of main_hdl (which is the owner of the
requests_queued list) when doing list operations on the
->requests_queued list.
Signed-off-by: default avatarPi-Hsun Shih <pihsun@chromium.org>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent 2df200ab
...@@ -3302,6 +3302,7 @@ static void v4l2_ctrl_request_queue(struct media_request_object *obj) ...@@ -3302,6 +3302,7 @@ static void v4l2_ctrl_request_queue(struct media_request_object *obj)
struct v4l2_ctrl_handler *prev_hdl = NULL; struct v4l2_ctrl_handler *prev_hdl = NULL;
struct v4l2_ctrl_ref *ref_ctrl, *ref_ctrl_prev = NULL; struct v4l2_ctrl_ref *ref_ctrl, *ref_ctrl_prev = NULL;
mutex_lock(main_hdl->lock);
if (list_empty(&main_hdl->requests_queued)) if (list_empty(&main_hdl->requests_queued))
goto queue; goto queue;
...@@ -3333,18 +3334,22 @@ static void v4l2_ctrl_request_queue(struct media_request_object *obj) ...@@ -3333,18 +3334,22 @@ static void v4l2_ctrl_request_queue(struct media_request_object *obj)
queue: queue:
list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued); list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued);
hdl->request_is_queued = true; hdl->request_is_queued = true;
mutex_unlock(main_hdl->lock);
} }
static void v4l2_ctrl_request_unbind(struct media_request_object *obj) static void v4l2_ctrl_request_unbind(struct media_request_object *obj)
{ {
struct v4l2_ctrl_handler *hdl = struct v4l2_ctrl_handler *hdl =
container_of(obj, struct v4l2_ctrl_handler, req_obj); container_of(obj, struct v4l2_ctrl_handler, req_obj);
struct v4l2_ctrl_handler *main_hdl = obj->priv;
list_del_init(&hdl->requests); list_del_init(&hdl->requests);
mutex_lock(main_hdl->lock);
if (hdl->request_is_queued) { if (hdl->request_is_queued) {
list_del_init(&hdl->requests_queued); list_del_init(&hdl->requests_queued);
hdl->request_is_queued = false; hdl->request_is_queued = false;
} }
mutex_unlock(main_hdl->lock);
} }
static void v4l2_ctrl_request_release(struct media_request_object *obj) static void v4l2_ctrl_request_release(struct media_request_object *obj)
...@@ -4298,9 +4303,11 @@ void v4l2_ctrl_request_complete(struct media_request *req, ...@@ -4298,9 +4303,11 @@ void v4l2_ctrl_request_complete(struct media_request *req,
v4l2_ctrl_unlock(ctrl); v4l2_ctrl_unlock(ctrl);
} }
mutex_lock(main_hdl->lock);
WARN_ON(!hdl->request_is_queued); WARN_ON(!hdl->request_is_queued);
list_del_init(&hdl->requests_queued); list_del_init(&hdl->requests_queued);
hdl->request_is_queued = false; hdl->request_is_queued = false;
mutex_unlock(main_hdl->lock);
media_request_object_complete(obj); media_request_object_complete(obj);
media_request_object_put(obj); media_request_object_put(obj);
} }
......
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