Patch "media: v4l2-ctrls.c: fix race condition in hdl->requests list" has been added to the 5.10-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    media: v4l2-ctrls.c: fix race condition in hdl->requests list

to the 5.10-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     media-v4l2-ctrls.c-fix-race-condition-in-hdl-request.patch
and it can be found in the queue-5.10 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 5542335ef68c02420a348d150acc47ce47cc09c9
Author: Hans Verkuil <hverkuil-cisco@xxxxxxxxx>
Date:   Sat Mar 27 12:27:40 2021 +0100

    media: v4l2-ctrls.c: fix race condition in hdl->requests list
    
    [ Upstream commit be7e8af98f3af729aa9f08b1053f9533a5cceb91 ]
    
    When a request is re-inited it will release all control handler
    objects that are still in the request. It does that by unbinding
    and putting all those objects. When the object is unbound the
    obj->req pointer is set to NULL, and the object's unbind op is
    called. When the object it put the object's release op is called
    to free the memory.
    
    For a request object that contains a control handler that means
    that v4l2_ctrl_handler_free() is called in the release op.
    
    A control handler used in a request has a pointer to the main
    control handler that is created by the driver and contains the
    current state of all controls. If the device is unbound (due to
    rmmod or a forced unbind), then that main handler is freed, again
    by calling v4l2_ctrl_handler_free(), and any outstanding request
    objects that refer to that main handler have to be unbound and put
    as well.
    
    It does that by this test:
    
            if (!hdl->req_obj.req && !list_empty(&hdl->requests)) {
    
    I.e. the handler has no pointer to a request, so is the main
    handler, and one or more request objects refer to this main
    handler.
    
    However, this test is wrong since hdl->req_obj.req is actually
    NULL when re-initing a request (the object unbind will set req to
    NULL), and the only reason this seemingly worked is that the
    requests list is typically empty since the request's unbind op
    will remove the handler from the requests list.
    
    But if another thread is at the same time adding a new control
    to a request, then there is a race condition where one thread
    is removing a control handler object from the requests list and
    another thread is adding one. The result is that hdl->requests
    is no longer empty and the code thinks that a main handler is
    being freed instead of a control handler that is part of a request.
    
    There are two bugs here: first the test for hdl->req_obj.req: this
    should be hdl->req_obj.ops since only the main control handler will
    have a NULL pointer there.
    
    The second is that adding or deleting request objects from the
    requests list of the main handler isn't protected by taking the
    main handler's lock.
    
    Signed-off-by: Hans Verkuil <hverkuil-cisco@xxxxxxxxx>
    Reported-by: John Cox <jc@xxxxxxxxxxxxx>
    Fixes: 6fa6f831f095 ("media: v4l2-ctrls: add core request support")
    Tested-by: John Cox <jc@xxxxxxxxxxxxx>
    Reported-by: John Cox <jc@xxxxxxxxxxxxx>
    Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
index 3d8c54b826e9..41f8410d08d6 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -2356,7 +2356,15 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
 	if (hdl == NULL || hdl->buckets == NULL)
 		return;
 
-	if (!hdl->req_obj.req && !list_empty(&hdl->requests)) {
+	/*
+	 * If the main handler is freed and it is used by handler objects in
+	 * outstanding requests, then unbind and put those objects before
+	 * freeing the main handler.
+	 *
+	 * The main handler can be identified by having a NULL ops pointer in
+	 * the request object.
+	 */
+	if (!hdl->req_obj.ops && !list_empty(&hdl->requests)) {
 		struct v4l2_ctrl_handler *req, *next_req;
 
 		list_for_each_entry_safe(req, next_req, &hdl->requests, requests) {
@@ -3402,8 +3410,8 @@ static void v4l2_ctrl_request_unbind(struct media_request_object *obj)
 		container_of(obj, struct v4l2_ctrl_handler, req_obj);
 	struct v4l2_ctrl_handler *main_hdl = obj->priv;
 
-	list_del_init(&hdl->requests);
 	mutex_lock(main_hdl->lock);
+	list_del_init(&hdl->requests);
 	if (hdl->request_is_queued) {
 		list_del_init(&hdl->requests_queued);
 		hdl->request_is_queued = false;
@@ -3462,8 +3470,11 @@ static int v4l2_ctrl_request_bind(struct media_request *req,
 	if (!ret) {
 		ret = media_request_object_bind(req, &req_ops,
 						from, false, &hdl->req_obj);
-		if (!ret)
+		if (!ret) {
+			mutex_lock(from->lock);
 			list_add_tail(&hdl->requests, &from->requests);
+			mutex_unlock(from->lock);
+		}
 	}
 	return ret;
 }



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux